2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_handler (GtkAction *action,
170 static void mark_all_read_cb (GtkAction *action,
172 static void mark_all_read_recursive_cb (GtkAction *action,
175 static void folderview_empty_trash_cb (GtkAction *action,
178 static void folderview_send_queue_cb (GtkAction *action,
181 static void folderview_search_cb (GtkAction *action,
183 static void folderview_run_processing_cb(GtkAction *action,
186 static void folderview_property_cb (GtkAction *action,
189 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
190 GdkDragContext *context,
194 FolderView *folderview);
195 static void folderview_drag_leave_cb (GtkWidget *widget,
196 GdkDragContext *context,
198 FolderView *folderview);
199 static void folderview_drag_received_cb (GtkWidget *widget,
200 GdkDragContext *drag_context,
203 GtkSelectionData *data,
206 FolderView *folderview);
208 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
209 FolderView *folderview);
211 static void folderview_drag_data_get (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 GtkSelectionData *selection_data,
216 FolderView *folderview);
217 static void folderview_drag_end_cb (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 FolderView *folderview);
221 static void folderview_create_folder_node (FolderView *folderview,
223 static gboolean folderview_update_folder (gpointer source,
225 static gboolean folderview_update_item_claws (gpointer source,
227 static void folderview_processing_cb(GtkAction *action, gpointer data);
228 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
229 GdkEventButton *event);
231 GHashTable *folderview_popups;
233 static GtkActionEntry folderview_common_popup_entries[] =
235 {"FolderViewPopup", NULL, "FolderViewPopup" },
236 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
237 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
238 {"FolderViewPopup/---", NULL, "---" },
239 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
240 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
241 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
242 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
243 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
244 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
248 GtkTargetEntry folderview_drag_types[] =
250 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
251 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
254 void folderview_initialize(void)
256 FolderViewPopup *fpopup;
258 fpopup = g_new0(FolderViewPopup, 1);
260 fpopup->klass = "common";
261 fpopup->path = "<CommonFolder>";
262 fpopup->entries = folderview_common_popup_entries;
263 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
264 fpopup->set_sensitivity = NULL;
266 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
267 g_hash_table_insert(folderview_popups, "common", fpopup);
270 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 FolderViewPopup *fpopup_common;
273 GtkActionGroup *action_group;
275 action_group = cm_menu_create_action_group(
277 fpopup->entries, fpopup->n_entries,
278 (gpointer)folderview);
280 if (fpopup->toggle_entries)
281 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
282 fpopup->n_toggle_entries,
283 (gpointer)folderview);
284 if (fpopup->radio_entries)
285 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
286 fpopup->n_radio_entries, fpopup->radio_default,
287 G_CALLBACK(fpopup->radio_callback),
288 (gpointer)folderview);
290 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
291 if (fpopup_common != fpopup) {
292 gtk_action_group_add_actions(action_group, fpopup_common->entries,
293 fpopup_common->n_entries,
294 (gpointer)folderview);
295 if (fpopup_common->toggle_entries)
296 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
297 fpopup_common->n_toggle_entries,
298 (gpointer)folderview);
299 if (fpopup_common->radio_entries)
300 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
301 fpopup_common->n_radio_entries, fpopup_common->radio_default,
302 G_CALLBACK(fpopup_common->radio_callback),
303 (gpointer)folderview);
309 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 FolderView *folderview = data;
312 FolderViewPopup *fpopup = value;
313 GtkActionGroup *group;
315 group = create_action_group(folderview, fpopup);
316 g_hash_table_insert(folderview->popups, fpopup->klass, group);
319 static void folderview_column_set_titles(FolderView *folderview)
321 GtkWidget *ctree = folderview->ctree;
322 GtkWidget *label_folder;
323 GtkWidget *label_new;
324 GtkWidget *label_unread;
325 GtkWidget *label_total;
326 GtkWidget *hbox_folder;
328 GtkWidget *hbox_unread;
329 GtkWidget *hbox_total;
330 gint *col_pos = folderview->col_pos;
332 debug_print("setting titles...\n");
333 gtk_widget_realize(folderview->ctree);
334 gtk_widget_show_all(folderview->scrolledwin);
336 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
337 * instead text (text overflows making them unreadable and ugly) */
338 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
340 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
342 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
345 label_folder = gtk_label_new(_("Folder"));
346 label_new = gtk_image_new_from_pixbuf(newxpm);
347 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
348 label_total = gtk_image_new_from_pixbuf(readxpm);
350 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
352 hbox_folder = gtk_hbox_new(FALSE, 4);
353 hbox_new = gtk_hbox_new(FALSE, 4);
354 hbox_unread = gtk_hbox_new(FALSE, 4);
355 hbox_total = gtk_hbox_new(FALSE, 4);
358 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
359 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
360 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
364 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
365 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
367 gtk_widget_show_all(hbox_folder);
368 gtk_widget_show_all(hbox_new);
369 gtk_widget_show_all(hbox_unread);
370 gtk_widget_show_all(hbox_total);
373 gtk_widget_set_size_request(hbox_new, -1, 20);
374 gtk_widget_set_size_request(hbox_unread, -1, 20);
375 gtk_widget_set_size_request(hbox_total, -1, 20);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
379 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
380 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
381 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
387 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
388 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
389 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
392 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
394 FolderView *folderview = (FolderView *)data;
395 GdkEventButton event;
396 if (folderview_get_selected_item(folderview) == NULL)
400 event.time = gtk_get_current_event_time();
402 folderview_set_sens_and_popup_menu(folderview, -1,
409 static GtkWidget *folderview_ctree_create(FolderView *folderview)
413 FolderColumnState *col_state;
414 FolderColumnType type;
415 gchar *titles[N_FOLDER_COLS];
417 GtkWidget *scrolledwin = folderview->scrolledwin;
419 debug_print("creating tree...\n");
420 memset(titles, 0, sizeof(titles));
422 col_state = prefs_folder_column_get_config();
423 memset(titles, 0, sizeof(titles));
425 col_pos = folderview->col_pos;
427 for (i = 0; i < N_FOLDER_COLS; i++) {
428 folderview->col_state[i] = col_state[i];
429 type = col_state[i].type;
433 titles[col_pos[F_COL_FOLDER]] = _("Folder");
434 titles[col_pos[F_COL_NEW]] = _("New");
435 titles[col_pos[F_COL_UNREAD]] = _("Unread");
436 /* TRANSLATORS: This in Number sign in American style */
437 titles[col_pos[F_COL_TOTAL]] = _("#");
439 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
442 if (prefs_common.show_col_headers == FALSE)
443 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
446 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
447 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
450 col_pos[F_COL_UNREAD],
452 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
453 col_pos[F_COL_TOTAL],
455 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
456 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
457 GTK_CMCTREE_EXPANDER_TRIANGLE);
459 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
460 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
462 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
463 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
465 /* don't let title buttons take key focus */
466 for (i = 0; i < N_FOLDER_COLS; i++) {
467 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
480 g_signal_connect(G_OBJECT(ctree), "popup-menu",
481 G_CALLBACK(folderview_popup_menu), folderview);
482 g_signal_connect(G_OBJECT(ctree), "button_release_event",
483 G_CALLBACK(folderview_button_released),
485 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
486 G_CALLBACK(folderview_selected), folderview);
488 /* drag-n-dropping folders on maemo is impractical as this
489 * opens the folder almost everytime */
490 g_signal_connect(G_OBJECT(ctree), "start_drag",
491 G_CALLBACK(folderview_start_drag), folderview);
493 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
494 G_CALLBACK(folderview_drag_data_get),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
498 G_CALLBACK(folderview_tree_expanded),
500 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
501 G_CALLBACK(folderview_tree_collapsed),
504 g_signal_connect(G_OBJECT(ctree), "resize_column",
505 G_CALLBACK(folderview_col_resized),
509 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
510 folderview_drag_types, 2,
511 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
512 g_signal_connect(G_OBJECT(ctree), "drag_motion",
513 G_CALLBACK(folderview_drag_motion_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_leave",
516 G_CALLBACK(folderview_drag_leave_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
519 G_CALLBACK(folderview_drag_received_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_end",
522 G_CALLBACK(folderview_drag_end_cb),
525 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
530 void folderview_set_column_order(FolderView *folderview)
532 GtkWidget *ctree = folderview->ctree;
533 FolderItem *item = folderview_get_selected_item(folderview);
534 FolderItem *sel_item = NULL, *op_item = NULL;
535 GtkWidget *scrolledwin = folderview->scrolledwin;
537 if (folderview->drag_timer_id != 0) {
538 g_source_remove(folderview->drag_timer_id);
539 folderview->drag_timer_id = 0;
541 if (folderview->deferred_refresh_id != 0) {
542 g_source_remove(folderview->deferred_refresh_id);
543 folderview->deferred_refresh_id = 0;
545 if (folderview->scroll_timeout_id != 0) {
546 g_source_remove(folderview->scroll_timeout_id);
547 folderview->scroll_timeout_id = 0;
549 if (folderview->postpone_select_id != 0) {
550 g_source_remove(folderview->postpone_select_id);
551 folderview->postpone_select_id = 0;
554 if (folderview->selected)
555 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
556 if (folderview->opened)
557 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
559 debug_print("recreating tree...\n");
560 gtk_widget_destroy(folderview->ctree);
563 folderview->ctree = ctree = folderview_ctree_create(folderview);
564 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565 GTK_CMCLIST(ctree)->hadjustment);
566 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
567 GTK_CMCLIST(ctree)->vadjustment);
568 gtk_widget_show(ctree);
571 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
573 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
575 folderview_set(folderview);
576 folderview_column_set_titles(folderview);
578 folderview_select(folderview,item);
581 FolderView *folderview_create(void)
583 FolderView *folderview;
584 GtkWidget *scrolledwin;
587 debug_print("Creating folder view...\n");
588 folderview = g_new0(FolderView, 1);
590 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
591 gtk_scrolled_window_set_policy
592 (GTK_SCROLLED_WINDOW(scrolledwin),
593 GTK_POLICY_AUTOMATIC,
594 prefs_common.folderview_vscrollbar_policy);
595 gtk_widget_set_size_request(scrolledwin,
596 prefs_common.folderview_width,
597 prefs_common.folderview_height);
599 folderview->scrolledwin = scrolledwin;
600 ctree = folderview_ctree_create(folderview);
602 /* create popup factories */
603 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
604 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
606 folderview->ctree = ctree;
608 folderview->folder_update_callback_id =
609 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
610 folderview->folder_item_update_callback_id =
611 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
613 gtk_widget_show_all(scrolledwin);
615 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
616 folderview_list = g_list_append(folderview_list, folderview);
618 folderview->drag_timer_id = 0;
619 folderview->deferred_refresh_id = 0;
620 folderview->scroll_timeout_id = 0;
621 folderview->postpone_select_id = 0;
626 void folderview_init(FolderView *folderview)
628 GtkWidget *ctree = folderview->ctree;
630 PangoFontDescription *normal_font;
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
671 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
672 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
673 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
674 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
675 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
676 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
677 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
679 normal_font = pango_font_description_from_string(NORMAL_FONT);
681 gtk_widget_modify_font(ctree, normal_font);
682 pango_font_description_free(normal_font);
684 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
687 PangoFontDescription *font_desc;
688 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
689 font_desc = pango_font_description_from_string(NORMAL_FONT);
691 if (normal_style->font_desc)
692 pango_font_description_free
693 (normal_style->font_desc);
694 normal_style->font_desc = font_desc;
696 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
697 normal_color_style = gtk_style_copy(normal_style);
698 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
702 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
703 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
704 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
705 PangoFontDescription *font_desc;
706 font_desc = pango_font_description_from_string(NORMAL_FONT);
708 pango_font_description_free(bold_style->font_desc);
709 bold_style->font_desc = font_desc;
711 pango_font_description_set_weight
712 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
714 PangoFontDescription *font_desc;
715 font_desc = pango_font_description_from_string(BOLD_FONT);
717 if (bold_style->font_desc)
718 pango_font_description_free
719 (bold_style->font_desc);
720 bold_style->font_desc = font_desc;
723 bold_color_style = gtk_style_copy(bold_style);
724 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
726 bold_tgtfold_style = gtk_style_copy(bold_style);
727 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
731 static gboolean folderview_defer_set(gpointer data)
733 FolderView *folderview = (FolderView *)data;
734 MainWindow *mainwin = folderview->mainwin;
738 if (mainwin->lock_count)
741 debug_print("doing deferred folderview_set now\n");
742 folderview_set(folderview);
744 folderview->deferred_refresh_id = 0;
748 void folderview_set(FolderView *folderview)
750 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
751 MainWindow *mainwin = folderview->mainwin;
752 FolderItem *sel_item = NULL, *op_item = NULL;
757 if (mainwin->lock_count) {
758 if (folderview->deferred_refresh_id == 0)
759 folderview->deferred_refresh_id =
760 g_timeout_add(500, folderview_defer_set, folderview);
761 debug_print("deferred folderview_set\n");
766 debug_print("Setting folder info...\n");
767 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
769 main_window_cursor_wait(mainwin);
771 if (folderview->selected)
772 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
773 if (folderview->opened)
774 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
776 folderview->selected = NULL;
777 folderview->opened = NULL;
779 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
780 gtk_cmclist_clear(GTK_CMCLIST(ctree));
782 folderview_set_folders(folderview);
785 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
787 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
789 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
790 main_window_cursor_normal(mainwin);
791 STATUSBAR_POP(mainwin);
795 void folderview_set_all(void)
799 for (list = folderview_list; list != NULL; list = list->next)
800 folderview_set((FolderView *)list->data);
803 void folderview_select(FolderView *folderview, FolderItem *item)
805 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
806 GtkCMCTreeNode *node;
807 GtkCMCTreeNode *old_selected = folderview->selected;
811 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
812 if (node) folderview_select_node(folderview, node);
814 if (old_selected != node)
815 folder_update_op_count();
818 static void mark_all_read_cb(GtkAction *action, gpointer data)
820 mark_all_read_handler(action, data, FALSE);
823 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
825 mark_all_read_handler(action, data, TRUE);
828 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
830 FolderView *folderview = (FolderView *)data;
834 item = folderview_get_selected_item(folderview);
838 if (folderview->summaryview->folder_item != item
839 && prefs_common.ask_mark_all_read) {
840 val = alertpanel_full(_("Mark all as read"),
841 _("Do you really want to mark all mails in this "
842 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
843 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
845 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
847 else if (val & G_ALERTDISABLE)
848 prefs_common.ask_mark_all_read = FALSE;
852 folder_item_update_freeze();
853 if (folderview->summaryview->folder_item != item && !recursive)
854 summary_lock(folderview->summaryview);
856 summary_freeze(folderview->summaryview);
859 folderutils_mark_all_read_recursive(item);
861 folderutils_mark_all_read(item);
863 if (folderview->summaryview->folder_item != item && !recursive)
864 summary_unlock(folderview->summaryview);
866 summary_thaw(folderview->summaryview);
867 folder_item_update_thaw();
870 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
872 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
874 cm_return_if_fail(node != NULL);
876 if (folderview->open_folder) {
880 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
881 gtkut_ctree_expand_parent_all(ctree, node);
883 folderview->open_folder = TRUE;
884 gtkut_ctree_set_focus_row(ctree, node);
885 gtk_cmctree_select(ctree, node);
886 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
887 if ((folderview->summaryview->folder_item &&
888 folderview->summaryview->folder_item->total_msgs > 0) ||
889 prefs_common.layout_mode == SMALL_LAYOUT)
890 summary_grab_focus(folderview->summaryview);
892 gtk_widget_grab_focus(folderview->ctree);
895 void folderview_unselect(FolderView *folderview)
897 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
899 (GTK_CMCTREE(folderview->ctree), folderview->opened);
901 folderview->selected = folderview->opened = NULL;
904 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
905 GtkCMCTreeNode *node,
911 node = gtkut_ctree_node_next(ctree, node);
913 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
915 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
916 item = gtk_cmctree_node_get_row_data(ctree, node);
919 if (item->stype == F_TRASH || item->stype == F_DRAFT)
923 if(item->unread_msgs > 0)
927 if(item->new_msgs > 0)
931 if(item->marked_msgs > 0)
940 void folderview_select_next_with_flag(FolderView *folderview,
944 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
945 GtkCMCTreeNode *node = NULL;
946 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
947 gboolean last_open = prefs_common.always_show_msg;
951 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
954 prefs_common.summary_select_prio[0] = ACTION_NEW;
957 prefs_common.summary_select_prio[0] = ACTION_MARKED;
960 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
962 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
964 folderview_select_node(folderview, node);
968 if (!folderview->opened ||
969 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
973 /* search again from the first node */
974 node = folderview_find_next_with_flag(ctree, NULL, flag);
976 folderview_select_node(folderview, node);
979 prefs_common.summary_select_prio[0] = last_summary_select_prio;
980 prefs_common.always_show_msg = last_open;
983 FolderItem *folderview_get_selected_item(FolderView *folderview)
985 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
987 if (!folderview->selected) return NULL;
988 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
991 static void folderview_set_folders(FolderView *folderview)
994 list = folder_get_list();
996 for (; list != NULL; list = list->next) {
997 folderview_append_folder(folderview, FOLDER(list->data));
1001 static gchar *get_scan_str(FolderItem *item)
1004 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1005 item->folder->name, G_DIR_SEPARATOR,
1008 return g_strdup_printf(_("Scanning folder %s..."),
1009 item->folder->name);
1011 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1015 for (list = folderview_list; list != NULL; list = list->next) {
1016 FolderView *folderview = (FolderView *)list->data;
1017 MainWindow *mainwin = folderview->mainwin;
1018 gchar *str = get_scan_str(item);
1020 STATUSBAR_PUSH(mainwin, str);
1021 STATUSBAR_POP(mainwin);
1026 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1029 MainWindow *mainwin = mainwindow_get_mainwindow();
1030 FolderView *folderview = NULL;
1031 GtkAdjustment *pos = NULL;
1034 cm_return_if_fail(folder != NULL);
1036 if (!folder->klass->scan_tree) return;
1039 alertpanel_full(_("Rebuild folder tree"),
1040 _("Rebuilding the folder tree will remove "
1041 "local caches. Do you want to continue?"),
1042 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1043 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1044 != G_ALERTALTERNATE) {
1050 window = label_window_create(_("Rebuilding folder tree..."));
1052 window = label_window_create(_("Scanning folder tree..."));
1055 folderview = mainwin->folderview;
1058 pos = gtk_scrolled_window_get_vadjustment(
1059 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1060 height = gtk_adjustment_get_value(pos);
1063 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1064 folder_scan_tree(folder, rebuild);
1065 folder_set_ui_func(folder, NULL, NULL);
1067 folderview_set_all();
1070 pos = gtk_scrolled_window_get_vadjustment(
1071 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1072 gtk_adjustment_set_value(pos, height);
1073 gtk_adjustment_changed(pos);
1075 label_window_destroy(window);
1079 /** folderview_check_new()
1080 * Scan and update the folder and return the
1081 * count the number of new messages since last check.
1082 * \param folder the folder to check for new messages
1083 * \return the number of new messages since last check
1085 gint folderview_check_new(Folder *folder)
1089 FolderView *folderview;
1091 GtkCMCTreeNode *node;
1093 gint former_new_msgs = 0;
1094 gint former_new = 0, former_unread = 0, former_total;
1096 for (list = folderview_list; list != NULL; list = list->next) {
1097 folderview = (FolderView *)list->data;
1098 ctree = GTK_CMCTREE(folderview->ctree);
1099 folderview->scanning_folder = folder;
1101 main_window_lock(folderview->mainwin);
1103 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1104 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1106 item = gtk_cmctree_node_get_row_data(ctree, node);
1107 if (!item || !item->path || !item->folder) continue;
1108 if (item->no_select) continue;
1109 if (folder && folder != item->folder) continue;
1110 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1111 if (!item->prefs->newmailcheck) continue;
1112 if (item->processing_pending == TRUE) {
1113 debug_print("skipping %s, processing pending\n",
1114 item->path ? item->path : item->name);
1117 if (item->scanning != ITEM_NOT_SCANNING) {
1118 debug_print("skipping %s, scanning\n",
1119 item->path ? item->path : item->name);
1123 str = get_scan_str(item);
1125 STATUSBAR_PUSH(folderview->mainwin, str);
1129 folderview_scan_tree_func(item->folder, item, NULL);
1130 former_new = item->new_msgs;
1131 former_unread = item->unread_msgs;
1132 former_total = item->total_msgs;
1134 if (item->folder->klass->scan_required &&
1135 (item->folder->klass->scan_required(item->folder, item) ||
1136 item->folder->inbox == item ||
1137 item->opened == TRUE ||
1138 item->processing_pending == TRUE)) {
1139 if (folder_item_scan(item) < 0) {
1141 summaryview_unlock(folderview->summaryview, item);
1142 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1143 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1144 item->path ? item->path:item->name);
1145 STATUSBAR_POP(folderview->mainwin);
1147 } else if (!FOLDER_IS_LOCAL(folder)) {
1148 STATUSBAR_POP(folderview->mainwin);
1153 } else if (!item->folder->klass->scan_required) {
1154 if (folder_item_scan(item) < 0) {
1155 summaryview_unlock(folderview->summaryview, item);
1156 if (folder && !FOLDER_IS_LOCAL(folder)) {
1157 STATUSBAR_POP(folderview->mainwin);
1162 if (former_new != item->new_msgs ||
1163 former_unread != item->unread_msgs ||
1164 former_total != item->total_msgs)
1165 folderview_update_node(folderview, node);
1167 new_msgs += item->new_msgs;
1168 former_new_msgs += former_new;
1169 STATUSBAR_POP(folderview->mainwin);
1171 folderview->scanning_folder = NULL;
1172 main_window_unlock(folderview->mainwin);
1176 folder_write_list();
1177 /* Number of new messages since last check is the just the difference
1178 * between former_new_msgs and new_msgs. If new_msgs is less than
1179 * former_new_msgs, that would mean another session accessed the folder
1180 * and the result is not well defined.
1182 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1186 void folderview_check_new_all(void)
1190 FolderView *folderview;
1192 folderview = (FolderView *)folderview_list->data;
1195 main_window_lock(folderview->mainwin);
1196 window = label_window_create
1197 (_("Checking for new messages in all folders..."));
1199 list = folder_get_list();
1200 for (; list != NULL; list = list->next) {
1201 Folder *folder = list->data;
1203 folderview_check_new(folder);
1206 folder_write_list();
1207 folderview_set_all();
1209 label_window_destroy(window);
1210 main_window_unlock(folderview->mainwin);
1214 static gboolean folderview_have_children_sub(FolderView *folderview,
1220 if (!item || !item->folder || !item->folder->node)
1223 node = item->folder->node;
1225 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1226 node = node->children;
1228 if (in_sub && item->total_msgs > 0) {
1232 while (node != NULL) {
1233 if (node && node->data) {
1234 FolderItem *next_item = (FolderItem*) node->data;
1236 if (folderview_have_children_sub(folderview,
1245 static gboolean folderview_have_children(FolderView *folderview,
1248 return folderview_have_children_sub(folderview, item, FALSE);
1251 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1257 if (!item || !item->folder || !item->folder->node)
1260 node = item->folder->node;
1262 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1263 node = node->children;
1266 (item->new_msgs > 0 ||
1267 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1271 while (node != NULL) {
1272 if (node && node->data) {
1273 FolderItem *next_item = (FolderItem*) node->data;
1275 if (folderview_have_new_children_sub(folderview,
1284 static gboolean folderview_have_new_children(FolderView *folderview,
1287 return folderview_have_new_children_sub(folderview, item, FALSE);
1290 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1296 if (!item || !item->folder || !item->folder->node)
1299 node = item->folder->node;
1301 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1302 node = node->children;
1305 (item->unread_msgs > 0 ||
1306 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1310 while (node != NULL) {
1311 if (node && node->data) {
1312 FolderItem *next_item = (FolderItem*) node->data;
1314 if (folderview_have_unread_children_sub(folderview,
1324 static gboolean folderview_have_unread_children(FolderView *folderview,
1327 return folderview_have_unread_children_sub(folderview, item, FALSE);
1330 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1336 if (!item || !item->folder || !item->folder->node)
1339 node = item->folder->node;
1341 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1342 node = node->children;
1344 if (in_sub && item->search_match){
1348 while (node != NULL) {
1349 if (node && node->data) {
1350 FolderItem *next_item = (FolderItem*) node->data;
1352 if (folderview_have_matching_children_sub(folderview,
1362 static gboolean folderview_have_matching_children(FolderView *folderview,
1365 return folderview_have_matching_children_sub(folderview, item, FALSE);
1368 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1374 if (!item || !item->folder || !item->folder->node)
1377 node = item->folder->node;
1379 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1380 node = node->children;
1382 if (item->marked_msgs != 0) {
1386 while (node != NULL) {
1387 if (node && node->data) {
1388 FolderItem *next_item = (FolderItem*) node->data;
1390 if (folderview_have_marked_children_sub(folderview,
1399 static gboolean folderview_have_marked_children(FolderView *folderview,
1402 return folderview_have_marked_children_sub(folderview, item, FALSE);
1405 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1407 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1408 GtkStyle *style = NULL;
1409 GtkStyle *color_style = NULL;
1411 GdkPixbuf *xpm, *openxpm;
1412 static GdkPixbuf *searchicon;
1413 gboolean mark = FALSE;
1416 gboolean add_unread_mark;
1417 gboolean add_sub_match_mark;
1418 gboolean use_bold, use_color;
1419 gint *col_pos = folderview->col_pos;
1420 SpecialFolderItemType stype;
1422 item = gtk_cmctree_node_get_row_data(ctree, node);
1423 cm_return_if_fail(item != NULL);
1425 if (!GTK_CMCTREE_ROW(node)->expanded)
1426 mark = folderview_have_marked_children(folderview, item);
1428 mark = (item->marked_msgs != 0);
1430 stype = item->stype;
1431 if (stype == F_NORMAL) {
1432 if (folder_has_parent_of_type(item, F_TRASH))
1434 else if (folder_has_parent_of_type(item, F_DRAFT))
1436 else if (folder_has_parent_of_type(item, F_OUTBOX))
1438 else if (folder_has_parent_of_type(item, F_QUEUE))
1443 if (item->hide_read_msgs || item->hide_read_threads) {
1444 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1445 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1447 xpm = mark?m_inboxxpm:inboxxpm;
1448 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1452 if (item->hide_read_msgs || item->hide_read_threads) {
1453 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1454 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1456 xpm = mark?m_outboxxpm:outboxxpm;
1457 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1461 if (item->hide_read_msgs || item->hide_read_threads) {
1462 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1463 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1465 xpm = mark?m_queuexpm:queuexpm;
1466 openxpm = mark?m_queueopenxpm:queueopenxpm;
1470 if (item->hide_read_msgs || item->hide_read_threads) {
1471 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1472 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1474 xpm = mark?m_trashxpm:trashxpm;
1475 openxpm = mark?m_trashopenxpm:trashopenxpm;
1479 xpm = mark?m_draftsxpm:draftsxpm;
1480 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1483 if (item->hide_read_msgs || item->hide_read_threads) {
1484 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1485 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1487 xpm = mark?m_folderxpm:folderxpm;
1488 openxpm = mark?m_folderopenxpm:folderopenxpm;
1492 if (item->no_select) {
1493 xpm = openxpm = noselectxpm;
1496 name = folder_item_get_name(item);
1498 if (!GTK_CMCTREE_ROW(node)->expanded) {
1499 add_unread_mark = folderview_have_unread_children(
1501 add_sub_match_mark = folderview_have_matching_children(
1504 add_unread_mark = FALSE;
1505 add_sub_match_mark = FALSE;
1508 if (item->search_match) {
1510 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1513 xpm = openxpm = searchicon;
1517 if (prefs_common.display_folder_unread) {
1518 if (folder_has_parent_of_type(item, F_QUEUE)) {
1519 /* only total_msgs matters here */
1520 if (item->total_msgs > 0) {
1521 /* show total number (should be equal to the unread number)
1523 str = g_strdup_printf("%s (%d%s%s)",
1524 name, item->total_msgs,
1525 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1526 (item->unreadmarked_msgs > 0) ? "!" : "");
1529 if (prefs_common.display_folder_unread == 1) {
1530 if (item->unread_msgs > 0) {
1531 /* show unread number and signs */
1532 str = g_strdup_printf("%s (%d%s%s)",
1533 name, item->unread_msgs,
1534 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1535 (item->unreadmarked_msgs > 0) ? "!" : "");
1538 if (item->total_msgs > 0) {
1539 /* show unread number, total number and signs if any */
1540 str = g_strdup_printf("%s (%d/%d%s%s)",
1541 name, item->unread_msgs, item->total_msgs,
1542 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1543 (item->unreadmarked_msgs > 0) ? "!" : "");
1547 if ((str == NULL) &&
1548 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1549 /* no unread/total numbers, but at least one sign */
1550 str = g_strdup_printf("%s (%s%s)",
1552 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1553 (item->unreadmarked_msgs > 0) ? "!" : "");
1557 /* last fallback, folder name only or with +! sign */
1558 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1559 str = g_strdup_printf("%s%s",
1561 } else if (item->unreadmarked_msgs > 0) {
1562 str = g_strdup_printf("%s%s",
1564 } else if (add_sub_match_mark) {
1565 str = g_strdup_printf("%s%s",
1568 str = g_strdup_printf("%s", name);
1571 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1573 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1577 if (!folder_item_parent(item)) {
1578 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1579 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1580 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1582 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1583 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1584 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1587 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1588 folder_has_parent_of_type(item, F_TRASH)) {
1589 use_bold = use_color = FALSE;
1590 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1591 GSList *list = folder_item_get_msg_list(item);
1593 use_bold = use_color = FALSE;
1594 for (cur = list; cur; cur = cur->next) {
1595 MsgInfo *msginfo = (MsgInfo *)cur->data;
1596 if (!MSG_IS_DELETED(msginfo->flags)) {
1597 /* highlight queue folder if there are any messages */
1598 use_bold = use_color = TRUE;
1602 if (!GTK_CMCTREE_ROW(node)->expanded &&
1603 use_bold == FALSE &&
1604 folderview_have_children(folderview, item))
1605 use_bold = use_color = TRUE;
1606 procmsg_msg_list_free(list);
1608 /* if unread messages exist, print with bold font */
1609 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1611 /* if new messages exist, print with colored letter */
1613 (item->new_msgs > 0) ||
1615 folderview_have_new_children(folderview, item));
1618 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1623 if (item->prefs->color > 0 && !use_color) {
1624 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1625 color_style = gtk_style_copy(bold_style);
1626 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1627 style = color_style;
1628 } else if (use_color) {
1629 style = bold_color_style;
1632 if (item->op_count > 0) {
1633 style = bold_tgtfold_style;
1635 } else if (use_color) {
1636 style = normal_color_style;
1637 gtk_cmctree_node_set_foreground(ctree, node,
1638 &folderview->color_new);
1639 } else if (item->op_count > 0) {
1640 style = bold_tgtfold_style;
1641 } else if (item->prefs->color > 0) {
1643 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1644 color_style = gtk_style_copy(normal_style);
1645 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1646 style = color_style;
1648 style = normal_style;
1651 gtk_cmctree_node_set_row_style(ctree, node, style);
1653 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1654 folderview_update_node(folderview, node);
1657 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1660 FolderView *folderview;
1662 GtkCMCTreeNode *node;
1664 cm_return_if_fail(item != NULL);
1666 for (list = folderview_list; list != NULL; list = list->next) {
1667 folderview = (FolderView *)list->data;
1668 ctree = GTK_CMCTREE(folderview->ctree);
1670 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1671 if (node && item->search_match != matches) {
1672 item->search_match = matches;
1673 folderview_update_node(folderview, node);
1678 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1680 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1681 FolderView *folderview = (FolderView *)data;
1683 GtkCMCTreeNode *node;
1684 cm_return_val_if_fail(update_info != NULL, TRUE);
1685 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1686 cm_return_val_if_fail(folderview != NULL, FALSE);
1688 ctree = GTK_CMCTREE(folderview->ctree);
1690 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1693 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1694 folderview_update_node(folderview, node);
1696 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1697 update_info->item == folderview->summaryview->folder_item &&
1698 update_info->item != NULL)
1699 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1700 summary_show(folderview->summaryview, update_info->item);
1706 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1707 GNode *gnode, GtkCMCTreeNode *cnode,
1710 FolderView *folderview = (FolderView *)data;
1711 FolderItem *item = FOLDER_ITEM(gnode->data);
1713 cm_return_val_if_fail(item != NULL, FALSE);
1715 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1716 folderview_update_node(folderview, cnode);
1721 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1724 FolderView *folderview = (FolderView *)data;
1727 if (GTK_CMCTREE_ROW(node)->children) {
1728 item = gtk_cmctree_node_get_row_data(ctree, node);
1729 cm_return_if_fail(item != NULL);
1731 if (!item->collapsed)
1732 gtk_cmctree_expand(ctree, node);
1734 folderview_update_node(folderview, node);
1738 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1739 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1742 GtkCMCTreeNode *node, *parent, *sibling;
1744 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1746 g_warning("%s not found.\n", item->path);
1748 parent = GTK_CMCTREE_ROW(node)->parent;
1749 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1750 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1752 sibling = GTK_CMCTREE_ROW(parent)->children;
1756 tmp = gtk_cmctree_node_get_row_data
1758 if (tmp && tmp->stype != F_NORMAL)
1759 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1763 if (node != sibling)
1764 gtk_cmctree_move(ctree, node, parent, sibling);
1771 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1774 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1775 GtkCMCTreeNode *prev = NULL;
1777 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1778 gtk_sctree_sort_recursive(ctree, root);
1779 if (root && GTK_CMCTREE_ROW(root)->parent) {
1780 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1783 set_special_folder(ctree, folder->inbox, root, &prev);
1784 set_special_folder(ctree, folder->outbox, root, &prev);
1785 set_special_folder(ctree, folder->draft, root, &prev);
1786 set_special_folder(ctree, folder->queue, root, &prev);
1787 set_special_folder(ctree, folder->trash, root, &prev);
1788 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1791 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1793 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1794 GtkCMCTreeNode *root;
1796 cm_return_if_fail(folder != NULL);
1798 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1799 folderview_gnode_func, folderview);
1800 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1802 folderview_sort_folders(folderview, root, folder);
1805 /* callback functions */
1806 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1807 GdkEventButton *event)
1811 FolderViewPopup *fpopup;
1812 GtkActionGroup *action_group;
1814 FolderItem *special_trash = NULL, *special_queue = NULL;
1816 GtkUIManager *ui_manager = gtk_ui_manager_new();
1818 if (folderview->ui_manager)
1819 g_object_unref(folderview->ui_manager);
1821 folderview->ui_manager = ui_manager;
1822 item = folderview_get_selected_item(folderview);
1824 cm_return_if_fail(item != NULL);
1825 cm_return_if_fail(item->folder != NULL);
1826 folder = item->folder;
1828 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1831 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1833 fpopup = g_hash_table_lookup(folderview_popups, "common");
1834 action_group = g_hash_table_lookup(folderview->popups, "common");
1837 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1838 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1839 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1841 if (fpopup->add_menuitems)
1842 fpopup->add_menuitems(ui_manager, item);
1844 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1845 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1846 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1847 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1850 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1852 if (fpopup->set_sensitivity != NULL)
1853 fpopup->set_sensitivity(ui_manager, item);
1855 if (NULL != (ac = account_find_from_item(item))) {
1856 special_trash = account_get_special_folder(ac, F_TRASH);
1857 special_queue = account_get_special_folder(ac, F_QUEUE);
1860 if ((item == folder->trash || item == special_trash
1861 || folder_has_parent_of_type(item, F_TRASH))) {
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1866 if ((item == folder->queue || item == special_queue
1867 || folder_has_parent_of_type(item, F_QUEUE))) {
1868 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1869 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1872 #define SET_SENS(name, sens) \
1873 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1875 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1876 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1877 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1878 folderview->selected == folderview->opened);
1879 SET_SENS("FolderViewPopup/Properties", TRUE);
1881 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1882 item->total_msgs >= 1 && !item->processing_pending);
1883 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1884 !item->no_select && !item->processing_pending);
1886 if (item == folder->trash || item == special_trash
1887 || folder_has_parent_of_type(item, F_TRASH)) {
1888 GSList *msglist = folder_item_get_msg_list(item);
1889 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1890 procmsg_msg_list_free(msglist);
1892 if (item == folder->queue || item == special_queue
1893 || folder_has_parent_of_type(item, F_QUEUE)) {
1894 GSList *msglist = folder_item_get_msg_list(item);
1895 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1896 procmsg_msg_list_free(msglist);
1900 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1901 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1902 g_signal_connect(G_OBJECT(popup), "selection_done",
1903 G_CALLBACK(folderview_popup_close),
1905 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1906 event->button, event->time);
1909 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1910 FolderView *folderview)
1912 GtkCMCList *clist = GTK_CMCLIST(ctree);
1913 gint prev_row = -1, row = -1, column = -1;
1915 if (!event) return FALSE;
1917 if (event->button == 1 || event->button == 2) {
1918 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1919 folderview->open_folder = TRUE;
1921 if (event->type == GDK_2BUTTON_PRESS) {
1922 if (clist->selection) {
1923 GtkCMCTreeNode *node;
1925 node = GTK_CMCTREE_NODE(clist->selection->data);
1927 gtk_cmctree_toggle_expansion(
1930 folderview->open_folder = FALSE;
1937 if (event->button == 2 || event->button == 3) {
1939 if (clist->selection) {
1940 GtkCMCTreeNode *node;
1942 node = GTK_CMCTREE_NODE(clist->selection->data);
1944 prev_row = gtkut_ctree_get_nth_from_node
1945 (GTK_CMCTREE(ctree), node);
1948 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1951 if (prev_row != row) {
1952 gtk_cmclist_unselect_all(clist);
1953 if (event->button == 2)
1954 folderview_select_node
1956 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1959 gtk_cmclist_select_row(clist, row, column);
1963 if (event->button != 3) return FALSE;
1965 folderview_set_sens_and_popup_menu(folderview, row, event);
1969 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1970 FolderView *folderview)
1972 int row = -1, column = -1;
1974 if (!event) return FALSE;
1976 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1979 if (event->button == 1 && folderview->open_folder == FALSE &&
1980 folderview->opened != NULL) {
1981 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1982 folderview->opened);
1983 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1989 #define BREAK_ON_MODIFIER_KEY() \
1990 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1992 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1993 FolderView *folderview)
1995 if (!event) return FALSE;
1997 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2000 switch (event->keyval) {
2002 #ifndef GENERIC_UMPC
2003 case GDK_KEY_Return:
2004 case GDK_KEY_KP_Enter:
2006 if (folderview->selected) {
2007 folderview_select_node(folderview,
2008 folderview->selected);
2012 case GDK_KEY_Return:
2013 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2014 gtk_cmctree_toggle_expansion(
2015 GTK_CMCTREE(folderview->ctree),
2016 folderview->selected);
2021 BREAK_ON_MODIFIER_KEY();
2022 if (folderview->selected) {
2023 if (folderview->opened == folderview->selected &&
2024 (!folderview->summaryview->folder_item ||
2025 folderview->summaryview->folder_item->total_msgs == 0))
2026 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2028 folderview_select_node(folderview,
2029 folderview->selected);
2039 typedef struct _PostponedSelectData
2042 GtkCMCTreeNode *row;
2044 FolderView *folderview;
2045 } PostponedSelectData;
2047 static gboolean postpone_select(void *data)
2049 PostponedSelectData *psdata = (PostponedSelectData *)data;
2050 debug_print("trying again\n");
2052 psdata->folderview->postpone_select_id = 0;
2053 psdata->folderview->open_folder = TRUE;
2054 main_window_cursor_normal(psdata->folderview->mainwin);
2055 STATUSBAR_POP(psdata->folderview->mainwin);
2056 folderview_selected(psdata->ctree, psdata->row,
2057 psdata->column, psdata->folderview);
2062 void folderview_close_opened(FolderView *folderview)
2064 if (folderview->opened) {
2065 FolderItem *olditem;
2067 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2068 folderview->opened);
2070 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2071 olditem->path ? olditem->path:olditem->name);
2072 /* will be null if we just moved the previously opened folder */
2073 STATUSBAR_PUSH(folderview->mainwin, buf);
2074 main_window_cursor_wait(folderview->mainwin);
2076 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2077 summary_show(folderview->summaryview, NULL);
2078 folder_item_close(olditem);
2079 main_window_cursor_normal(folderview->mainwin);
2080 STATUSBAR_POP(folderview->mainwin);
2081 if (olditem->folder->klass->item_closed)
2082 olditem->folder->klass->item_closed(olditem);
2087 if (folderview->opened &&
2088 !GTK_CMCTREE_ROW(folderview->opened)->children)
2089 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2091 folderview->opened = NULL;
2093 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2094 gint column, FolderView *folderview)
2096 static gboolean can_select = TRUE; /* exclusive lock */
2101 GtkCMCTreeNode *old_opened = folderview->opened;
2103 folderview->selected = row;
2105 debug_print("newly selected %p, opened %p\n", folderview->selected,
2106 folderview->opened);
2107 if (folderview->opened == row) {
2108 folderview->open_folder = FALSE;
2113 item = gtk_cmctree_node_get_row_data(ctree, row);
2116 folderview->open_folder = FALSE;
2120 if (!can_select || summary_is_locked(folderview->summaryview)) {
2121 if (folderview->opened) {
2122 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2123 gtk_cmctree_select(ctree, folderview->opened);
2125 folderview->open_folder = FALSE;
2130 if (!folderview->open_folder) {
2137 /* Save cache for old folder */
2138 /* We don't want to lose all caches if sylpheed crashed */
2139 /* resets folderview->opened to NULL */
2140 folderview_close_opened(folderview);
2142 /* CLAWS: set compose button type: news folder items
2143 * always have a news folder as parent */
2145 toolbar_set_compose_button
2146 (folderview->mainwin->toolbar,
2147 FOLDER_TYPE(item->folder) == F_NEWS ?
2148 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2151 debug_print("Folder %s is selected\n", item->path);
2153 if (!GTK_CMCTREE_ROW(row)->children)
2154 gtk_cmctree_expand(ctree, row);
2156 /* ungrab the mouse event */
2157 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2158 gtk_grab_remove(GTK_WIDGET(ctree));
2159 if (gdk_pointer_is_grabbed())
2160 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2164 /* TODO: wwp: avoid displaying (null) in the status bar */
2165 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2166 item->path : "(null)");
2167 debug_print("%s\n", buf);
2168 STATUSBAR_PUSH(folderview->mainwin, buf);
2171 main_window_cursor_wait(folderview->mainwin);
2173 if (folderview->scanning_folder == item->folder) {
2176 res = folder_item_open(item);
2179 if (res == -1 && item->no_select == FALSE) {
2180 main_window_cursor_normal(folderview->mainwin);
2181 STATUSBAR_POP(folderview->mainwin);
2183 alertpanel_error(_("Folder could not be opened."));
2185 folderview->open_folder = FALSE;
2189 } else if (res == -2 && item->no_select == FALSE) {
2190 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2191 data->ctree = ctree;
2193 data->column = column;
2194 data->folderview = folderview;
2195 debug_print("postponing open of %s till end of scan\n",
2196 item->path ? item->path:item->name);
2197 folderview->open_folder = FALSE;
2199 if (folderview->postpone_select_id != 0)
2200 g_source_remove(folderview->postpone_select_id);
2201 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2206 main_window_cursor_normal(folderview->mainwin);
2209 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2210 opened = summary_show(folderview->summaryview, item);
2212 folder_clean_cache_memory(item);
2215 gtkut_ctree_set_focus_row(ctree, old_opened);
2216 gtk_cmctree_select(ctree, old_opened);
2217 folderview->opened = old_opened;
2219 folderview->opened = row;
2220 if (gtk_cmctree_node_is_visible(ctree, row)
2221 != GTK_VISIBILITY_FULL)
2222 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2225 STATUSBAR_POP(folderview->mainwin);
2227 folderview->open_folder = FALSE;
2232 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2233 FolderView *folderview)
2237 item = gtk_cmctree_node_get_row_data(ctree, node);
2238 cm_return_if_fail(item != NULL);
2239 item->collapsed = FALSE;
2240 folderview_update_node(folderview, node);
2243 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2244 FolderView *folderview)
2248 item = gtk_cmctree_node_get_row_data(ctree, node);
2249 cm_return_if_fail(item != NULL);
2250 item->collapsed = TRUE;
2251 folderview_update_node(folderview, node);
2254 static void folderview_popup_close(GtkMenuShell *menu_shell,
2255 FolderView *folderview)
2257 if (!folderview->opened) return;
2259 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2262 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2263 FolderView *folderview)
2265 FolderColumnType type = folderview->col_state[column].type;
2267 prefs_common.folder_col_size[type] = width;
2270 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2272 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2273 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2274 GtkCMCTreeNode *node, *parent_node;
2275 gint *col_pos = folderview->col_pos;
2276 FolderItemUpdateData hookdata;
2278 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2279 if (parent_node == NULL)
2282 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2284 text[col_pos[F_COL_FOLDER]] = item->name;
2285 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2290 gtk_cmctree_expand(ctree, parent_node);
2291 gtk_cmctree_node_set_row_data(ctree, node, item);
2293 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2294 folderview_sort_folders(folderview, parent_node, item->folder);
2296 hookdata.item = item;
2297 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2298 hookdata.msg = NULL;
2299 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2301 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2304 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2306 FolderView *folderview = (FolderView *)data;
2307 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2309 GSList *mlist = NULL;
2311 FolderItem *special_trash = NULL;
2314 if (!folderview->selected) return;
2315 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2316 cm_return_if_fail(item != NULL);
2317 cm_return_if_fail(item->folder != NULL);
2319 if (NULL != (ac = account_find_from_item(item)))
2320 special_trash = account_get_special_folder(ac, F_TRASH);
2322 if (item != item->folder->trash && item != special_trash
2323 && !folder_has_parent_of_type(item, F_TRASH)) return;
2325 if (prefs_common.ask_on_clean) {
2326 if (alertpanel(_("Empty trash"),
2327 _("Delete all messages in trash?"),
2328 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2332 mlist = folder_item_get_msg_list(item);
2334 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2335 MsgInfo * msginfo = (MsgInfo *) cur->data;
2336 if (MSG_IS_LOCKED(msginfo->flags))
2338 /* is it partially received? (partial_recv isn't cached) */
2339 if (msginfo->total_size != 0 &&
2340 msginfo->size != (off_t)msginfo->total_size)
2341 partial_mark_for_delete(msginfo);
2343 procmsg_msg_list_free(mlist);
2345 folder_item_remove_all_msg(item);
2348 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2350 FolderView *folderview = (FolderView *)data;
2351 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2353 FolderItem *special_queue = NULL;
2355 gchar *errstr = NULL;
2357 if (!folderview->selected) return;
2358 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2359 cm_return_if_fail(item != NULL);
2360 cm_return_if_fail(item->folder != NULL);
2362 if (NULL != (ac = account_find_from_item(item)))
2363 special_queue = account_get_special_folder(ac, F_QUEUE);
2365 if (item != item->folder->queue && item != special_queue
2366 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2368 if (procmsg_queue_is_empty(item))
2371 if (prefs_common.work_offline)
2372 if (alertpanel(_("Offline warning"),
2373 _("You're working offline. Override?"),
2374 GTK_STOCK_NO, GTK_STOCK_YES,
2375 NULL) != G_ALERTALTERNATE)
2378 /* ask for confirmation before sending queued messages only
2379 in online mode and if there is at least one message queued
2380 in any of the folder queue
2382 if (prefs_common.confirm_send_queued_messages) {
2383 if (!prefs_common.work_offline) {
2384 if (alertpanel(_("Send queued messages"),
2385 _("Send all queued messages?"),
2386 GTK_STOCK_CANCEL, _("_Send"),
2387 NULL) != G_ALERTALTERNATE)
2392 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2394 alertpanel_error_log(_("Some errors occurred while "
2395 "sending queued messages."));
2397 alertpanel_error_log(_("Some errors occurred "
2398 "while sending queued messages:\n%s"), errstr);
2404 static void folderview_search_cb(GtkAction *action, gpointer data)
2406 FolderView *folderview = (FolderView *)data;
2407 summary_search(folderview->summaryview);
2410 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2412 FolderView *folderview = (FolderView *)data;
2413 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2416 if (!folderview->selected) return;
2418 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2419 cm_return_if_fail(item != NULL);
2420 cm_return_if_fail(item->folder != NULL);
2422 item->processing_pending = TRUE;
2423 folder_item_apply_processing(item);
2424 item->processing_pending = FALSE;
2427 static void folderview_property_cb(GtkAction *action, gpointer data)
2429 FolderView *folderview = (FolderView *)data;
2430 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2433 if (!folderview->selected) return;
2435 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2436 cm_return_if_fail(item != NULL);
2437 cm_return_if_fail(item->folder != NULL);
2439 prefs_folder_item_open(item);
2442 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2444 GSList *list = NULL;
2445 GSList *done = NULL;
2446 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2448 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2449 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2450 && list->data != node) {
2451 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2452 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2455 for (list = done; list != NULL; list = g_slist_next(list)) {
2456 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2462 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2463 FolderItem *to_folder, gboolean copy)
2465 FolderItem *new_folder = NULL;
2469 cm_return_if_fail(folderview != NULL);
2470 cm_return_if_fail(from_folder != NULL);
2471 cm_return_if_fail(to_folder != NULL);
2473 if (prefs_common.warn_dnd) {
2474 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2475 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2476 from_folder->name, to_folder->name);
2477 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2478 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2479 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2482 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2484 else if (status & G_ALERTDISABLE)
2485 prefs_common.warn_dnd = FALSE;
2488 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2489 from_folder->name, to_folder->name);
2490 STATUSBAR_PUSH(folderview->mainwin, buf);
2492 summary_clear_all(folderview->summaryview);
2493 folderview->opened = NULL;
2494 folderview->selected = NULL;
2495 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2497 main_window_cursor_wait(folderview->mainwin);
2499 statusbar_verbosity_set(FALSE);
2500 folder_item_update_freeze();
2501 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2502 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2503 statusbar_verbosity_set(FALSE);
2504 main_window_cursor_normal(folderview->mainwin);
2505 STATUSBAR_POP(folderview->mainwin);
2506 folder_item_update_thaw();
2507 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2509 folderview_sort_folders(folderview,
2510 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2511 NULL, to_folder), new_folder->folder);
2512 folderview_select(folderview, new_folder);
2513 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2515 statusbar_verbosity_set(FALSE);
2516 main_window_cursor_normal(folderview->mainwin);
2517 STATUSBAR_POP(folderview->mainwin);
2518 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2519 folder_item_update_thaw();
2521 case F_MOVE_FAILED_DEST_IS_PARENT:
2522 alertpanel_error(_("Source and destination are the same."));
2524 case F_MOVE_FAILED_DEST_IS_CHILD:
2525 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2526 _("Can't move a folder to one of its children."));
2528 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2529 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2532 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2537 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2540 static gint folderview_clist_compare(GtkCMCList *clist,
2541 gconstpointer ptr1, gconstpointer ptr2)
2543 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2544 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2546 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2548 return item1->order - item2->order;
2551 // if only one folder has an order it comes first
2552 if (item1->order > 0)
2556 if (item2->order > 0)
2562 return (item2->name != NULL);
2566 return g_utf8_collate(item1->name, item2->name);
2569 static void folderview_processing_cb(GtkAction *action, gpointer data)
2571 FolderView *folderview = (FolderView *)data;
2572 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2576 if (!folderview->selected) return;
2578 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2579 cm_return_if_fail(item != NULL);
2580 cm_return_if_fail(item->folder != NULL);
2582 id = folder_item_get_identifier(item);
2583 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2586 prefs_filtering_open(&item->prefs->processing, title,
2587 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2591 void folderview_set_target_folder_color(gint color_op)
2595 FolderView *folderview;
2597 for (list = folderview_list; list != NULL; list = list->next) {
2598 folderview = (FolderView *)list->data;
2599 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2601 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2602 folderview->color_op;
2608 static gchar *last_smallfont = NULL;
2609 static gchar *last_normalfont = NULL;
2610 static gchar *last_boldfont = NULL;
2611 static gboolean last_derive = 0;
2613 void folderview_reinit_fonts(FolderView *folderview)
2616 g_free(last_smallfont);
2617 last_smallfont = NULL;
2618 g_free(last_normalfont);
2619 last_normalfont = NULL;
2620 g_free(last_boldfont);
2621 last_boldfont = NULL;
2624 void folderview_reflect_prefs(void)
2626 gboolean update_font = FALSE;
2627 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2628 FolderItem *item = folderview_get_selected_item(folderview);
2629 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2630 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2631 gint height = gtk_adjustment_get_value(pos);
2633 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2634 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2635 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2636 last_derive != prefs_common.derive_from_normal_font)
2639 g_free(last_smallfont);
2640 last_smallfont = g_strdup(SMALL_FONT);
2641 g_free(last_normalfont);
2642 last_normalfont = g_strdup(NORMAL_FONT);
2643 g_free(last_boldfont);
2644 last_boldfont = g_strdup(BOLD_FONT);
2645 last_derive = prefs_common.derive_from_normal_font;
2648 normal_style = normal_color_style = bold_style =
2649 bold_color_style = bold_tgtfold_style = NULL;
2651 folderview_init(folderview);
2653 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2654 folderview_column_set_titles(folderview);
2655 folderview_set_all();
2657 g_signal_handlers_block_by_func
2658 (G_OBJECT(folderview->ctree),
2659 G_CALLBACK(folderview_selected), folderview);
2662 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2663 GTK_CMCTREE(folderview->ctree), NULL, item);
2665 folderview_select(folderview, item);
2666 folderview->open_folder = FALSE;
2667 folderview->selected = node;
2670 g_signal_handlers_unblock_by_func
2671 (G_OBJECT(folderview->ctree),
2672 G_CALLBACK(folderview_selected), folderview);
2674 pos = gtk_scrolled_window_get_vadjustment(
2675 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2676 gtk_adjustment_set_value(pos, height);
2677 gtk_adjustment_changed(pos);
2678 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2681 static void drag_state_stop(FolderView *folderview)
2683 if (folderview->drag_timer_id)
2684 g_source_remove(folderview->drag_timer_id);
2685 folderview->drag_timer_id = 0;
2686 folderview->drag_node = NULL;
2689 static gboolean folderview_defer_expand(FolderView *folderview)
2691 if (folderview->drag_node) {
2692 folderview_recollapse_nodes(folderview, folderview->drag_node);
2693 if (folderview->drag_item->collapsed) {
2694 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2695 folderview->nodes_to_recollapse = g_slist_append
2696 (folderview->nodes_to_recollapse, folderview->drag_node);
2699 folderview->drag_item = NULL;
2700 folderview->drag_timer_id = 0;
2704 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2706 /* the idea is that we call drag_state_start() whenever we want expansion to
2707 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2708 * we need to call drag_state_stop() */
2709 drag_state_stop(folderview);
2710 /* request expansion */
2711 if (0 != (folderview->drag_timer_id = g_timeout_add
2712 (prefs_common.hover_timeout,
2713 (GSourceFunc)folderview_defer_expand,
2715 folderview->drag_node = node;
2716 folderview->drag_item = item;
2719 #ifndef GENERIC_UMPC
2720 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2721 FolderView *folderview)
2723 GdkDragContext *context;
2725 cm_return_if_fail(folderview != NULL);
2726 if (folderview->selected == NULL) return;
2727 if (folderview->nodes_to_recollapse)
2728 g_slist_free(folderview->nodes_to_recollapse);
2729 folderview->nodes_to_recollapse = NULL;
2730 context = gtk_drag_begin(widget, folderview->target_list,
2731 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2732 gtk_drag_set_icon_default(context);
2735 static void folderview_drag_data_get(GtkWidget *widget,
2736 GdkDragContext *drag_context,
2737 GtkSelectionData *selection_data,
2740 FolderView *folderview)
2744 gchar *source = NULL;
2745 if (info == TARGET_DUMMY) {
2746 sel = GTK_CMCLIST(folderview->ctree)->selection;
2750 item = gtk_cmctree_node_get_row_data
2751 (GTK_CMCTREE(folderview->ctree),
2752 GTK_CMCTREE_NODE(sel->data));
2754 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2755 gtk_selection_data_set(selection_data,
2756 gtk_selection_data_get_target(selection_data), 8,
2757 source, strlen(source));
2760 g_warning("unknown info %d\n", info);
2764 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2766 FolderUpdateData *hookdata;
2767 FolderView *folderview;
2771 folderview = (FolderView *) userdata;
2772 cm_return_val_if_fail(hookdata != NULL, FALSE);
2773 cm_return_val_if_fail(folderview != NULL, FALSE);
2775 ctree = folderview->ctree;
2776 cm_return_val_if_fail(ctree != NULL, FALSE);
2778 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2779 folderview_create_folder_node(folderview, hookdata->item);
2780 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2781 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2782 NULL, folder_item_parent(hookdata->item));
2783 folderview_sort_folders(folderview, node, hookdata->folder);
2784 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2785 GtkCMCTreeNode *node;
2787 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2789 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2790 if (folderview->selected == node)
2791 folderview->selected = NULL;
2792 if (folderview->opened == node)
2793 folderview->opened = NULL;
2795 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2796 /* do nothing, it's done by the ADD and REMOVE) */
2797 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2798 folderview_set(folderview);
2803 static gboolean folderview_dnd_scroll_cb(gpointer data)
2805 FolderView *folderview = (FolderView *)data;
2806 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2807 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2808 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2809 gint max = (int)gtk_adjustment_get_upper(pos) -
2810 (int)gtk_adjustment_get_page_size(pos);
2812 if (folderview->scroll_value == 0) {
2813 folderview->scroll_timeout_id = 0;
2817 if (folderview->scroll_value > 0 && new_val > max) {
2819 } else if (folderview->scroll_value < 0 && new_val < 0) {
2822 gtk_adjustment_set_value(pos, new_val);
2827 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2828 GdkDragContext *context,
2832 FolderView *folderview)
2835 FolderItem *item = NULL, *src_item = NULL;
2836 GtkCMCTreeNode *node = NULL;
2837 gboolean acceptable = FALSE;
2838 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2839 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2840 int height = (int)gtk_adjustment_get_page_size(pos);
2841 int total_height = (int)gtk_adjustment_get_upper(pos);
2842 int vpos = (int)gtk_adjustment_get_value(pos);
2843 int offset = prefs_common.show_col_headers ? 24:0;
2846 if (gtk_cmclist_get_selection_info
2847 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2848 GtkWidget *srcwidget;
2850 if (y > height - (48 - offset) && height + vpos < total_height) {
2851 dist = -(height - (48 - offset) - y);
2852 folderview->scroll_value = 1.41f * (1+(dist / 6));
2853 } else if (y < 72 - (24 - offset) && y >= 0) {
2854 dist = 72 - (24 - offset) - y;
2855 folderview->scroll_value = -1.41f * (1+(dist / 6));
2857 folderview->scroll_value = 0;
2859 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2860 folderview->scroll_timeout_id =
2861 g_timeout_add(30, folderview_dnd_scroll_cb,
2865 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2866 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2867 src_item = folderview->summaryview->folder_item;
2869 srcwidget = gtk_drag_get_source_widget(context);
2870 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2871 /* comes from summaryview */
2872 /* we are copying messages, so only accept folder items that are not
2873 the source item, are no root items and can copy messages */
2874 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2875 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2876 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2878 } else if (srcwidget == folderview->ctree) {
2879 /* comes from folderview */
2880 /* we are moving folder items, only accept folders that are not
2881 the source items and can copy messages and create folder items */
2882 if (item && item->folder && src_item && src_item != item &&
2883 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2884 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2885 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2886 || item->folder == src_item->folder))
2889 /* comes from another app */
2890 /* we are adding messages, so only accept folder items that are
2891 no root items and can copy messages */
2892 if (item && item->folder && folder_item_parent(item) != NULL
2893 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2894 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2899 if (acceptable || (src_item && src_item == item))
2900 drag_state_start(folderview, node, item);
2903 g_signal_handlers_block_by_func
2905 G_CALLBACK(folderview_selected), folderview);
2906 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2907 g_signal_handlers_unblock_by_func
2909 G_CALLBACK(folderview_selected), folderview);
2910 gdk_drag_status(context,
2911 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2912 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2914 if (folderview->opened)
2915 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2916 gdk_drag_status(context, 0, time);
2922 static void folderview_drag_leave_cb(GtkWidget *widget,
2923 GdkDragContext *context,
2925 FolderView *folderview)
2927 drag_state_stop(folderview);
2928 folderview->scroll_value = 0;
2929 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2932 static void free_info (gpointer stuff, gpointer data)
2937 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2938 guint time, FolderItem *item)
2941 GSList *msglist = NULL;
2942 list = uri_list_extract_filenames(data);
2943 if (!(item && item->folder && folder_item_parent(item) != NULL
2944 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2946 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2947 debug_print("item doesn't fit\n");
2951 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2952 debug_print("list is empty\n");
2955 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2956 MsgFileInfo *info = NULL;
2958 if (file_is_email((gchar *)tmp->data)) {
2959 info = g_new0(MsgFileInfo, 1);
2960 info->msginfo = NULL;
2961 info->file = (gchar *)tmp->data;
2962 msglist = g_slist_prepend(msglist, info);
2963 debug_print("file is a mail\n");
2965 debug_print("file isn't a mail\n");
2969 msglist = g_slist_reverse(msglist);
2970 folder_item_add_msgs(item, msglist, FALSE);
2971 g_slist_foreach(msglist, free_info, NULL);
2972 g_slist_free(msglist);
2973 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2975 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2977 list_free_strings(list);
2981 static void folderview_drag_received_cb(GtkWidget *widget,
2982 GdkDragContext *drag_context,
2985 GtkSelectionData *data,
2988 FolderView *folderview)
2991 FolderItem *item = NULL, *src_item;
2992 GtkCMCTreeNode *node;
2993 int offset = prefs_common.show_col_headers ? 24:0;
2995 folderview->scroll_value = 0;
2997 if (info == TARGET_DUMMY) {
2998 drag_state_stop(folderview);
2999 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3000 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3001 /* comes from summaryview */
3002 if (gtk_cmclist_get_selection_info
3003 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3006 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3007 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3008 src_item = folderview->summaryview->folder_item;
3010 if (item->no_select) {
3011 alertpanel_error(_("The destination folder can only be used to "
3012 "store subfolders."));
3015 /* re-check (due to acceptable possibly set for folder moves */
3016 if (!(item && item->folder && item->path && !item->no_select &&
3017 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3020 if (item && src_item) {
3021 switch (gdk_drag_context_get_selected_action(drag_context)) {
3022 case GDK_ACTION_COPY:
3023 summary_copy_selected_to(folderview->summaryview, item);
3024 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3026 case GDK_ACTION_MOVE:
3027 case GDK_ACTION_DEFAULT:
3029 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3030 summary_copy_selected_to(folderview->summaryview, item);
3032 summary_move_selected_to(folderview->summaryview, item);
3033 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3036 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3038 /* comes from folderview */
3040 gboolean folder_is_normal = TRUE;
3041 gboolean copy = (GDK_ACTION_COPY ==
3042 gdk_drag_context_get_selected_action(drag_context));
3044 source = (char *)gtk_selection_data_get_data(data) + 17;
3045 if (gtk_cmclist_get_selection_info
3046 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3048 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3051 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3052 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3053 src_item = folder_find_item_from_identifier(source);
3057 src_item->stype == F_NORMAL &&
3058 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3059 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3060 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3061 !folder_has_parent_of_type(src_item, F_TRASH);
3062 if (!item || !src_item || !folder_is_normal) {
3063 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3067 folderview_move_folder(folderview, src_item, item, copy);
3068 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3070 folderview->nodes_to_recollapse = NULL;
3071 } else if (info == TARGET_MAIL_URI_LIST) {
3072 if (gtk_cmclist_get_selection_info
3073 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3076 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3078 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3079 debug_print("no node\n");
3082 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3084 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3085 debug_print("no item\n");
3088 folderview_finish_dnd(gtk_selection_data_get_data(data),
3089 drag_context, time, item);
3093 static void folderview_drag_end_cb(GtkWidget *widget,
3094 GdkDragContext *drag_context,
3095 FolderView *folderview)
3097 drag_state_stop(folderview);
3098 folderview->scroll_value = 0;
3099 g_slist_free(folderview->nodes_to_recollapse);
3100 folderview->nodes_to_recollapse = NULL;
3103 void folderview_register_popup(FolderViewPopup *fpopup)
3107 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3108 FolderView *folderview = folderviews->data;
3109 GtkActionGroup *factory;
3111 factory = create_action_group(folderview, fpopup);
3112 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3114 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3117 void folderview_unregister_popup(FolderViewPopup *fpopup)
3122 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3123 FolderView *folderview = folderviews->data;
3125 g_hash_table_remove(folderview->popups, fpopup->klass);
3127 g_hash_table_remove(folderview_popups, fpopup->klass);