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_cb (GtkAction *action,
169 static void folderview_empty_trash_cb (GtkAction *action,
172 static void folderview_send_queue_cb (GtkAction *action,
175 static void folderview_search_cb (GtkAction *action,
177 static void folderview_run_processing_cb(GtkAction *action,
180 static void folderview_property_cb (GtkAction *action,
183 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
184 GdkDragContext *context,
188 FolderView *folderview);
189 static void folderview_drag_leave_cb (GtkWidget *widget,
190 GdkDragContext *context,
192 FolderView *folderview);
193 static void folderview_drag_received_cb (GtkWidget *widget,
194 GdkDragContext *drag_context,
197 GtkSelectionData *data,
200 FolderView *folderview);
202 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
203 FolderView *folderview);
205 static void folderview_drag_data_get (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 GtkSelectionData *selection_data,
210 FolderView *folderview);
211 static void folderview_drag_end_cb (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 FolderView *folderview);
215 static void folderview_create_folder_node (FolderView *folderview,
217 static gboolean folderview_update_folder (gpointer source,
219 static gboolean folderview_update_item_claws (gpointer source,
221 static void folderview_processing_cb(GtkAction *action, gpointer data);
222 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
223 GdkEventButton *event);
225 GHashTable *folderview_popups;
227 static GtkActionEntry folderview_common_popup_entries[] =
229 {"FolderViewPopup", NULL, "FolderViewPopup" },
230 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
231 {"FolderViewPopup/---", NULL, "---" },
232 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
233 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
234 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
235 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
236 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
237 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
241 GtkTargetEntry folderview_drag_types[] =
243 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
244 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
247 void folderview_initialize(void)
249 FolderViewPopup *fpopup;
251 fpopup = g_new0(FolderViewPopup, 1);
253 fpopup->klass = "common";
254 fpopup->path = "<CommonFolder>";
255 fpopup->entries = folderview_common_popup_entries;
256 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
257 fpopup->set_sensitivity = NULL;
259 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
260 g_hash_table_insert(folderview_popups, "common", fpopup);
263 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
265 FolderViewPopup *fpopup_common;
266 GtkActionGroup *action_group;
268 action_group = cm_menu_create_action_group(
270 fpopup->entries, fpopup->n_entries,
271 (gpointer)folderview);
273 if (fpopup->toggle_entries)
274 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
275 fpopup->n_toggle_entries,
276 (gpointer)folderview);
277 if (fpopup->radio_entries)
278 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
279 fpopup->n_radio_entries, fpopup->radio_default,
280 G_CALLBACK(fpopup->radio_callback),
281 (gpointer)folderview);
283 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
284 if (fpopup_common != fpopup) {
285 gtk_action_group_add_actions(action_group, fpopup_common->entries,
286 fpopup_common->n_entries,
287 (gpointer)folderview);
288 if (fpopup_common->toggle_entries)
289 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
290 fpopup_common->n_toggle_entries,
291 (gpointer)folderview);
292 if (fpopup_common->radio_entries)
293 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
294 fpopup_common->n_radio_entries, fpopup_common->radio_default,
295 G_CALLBACK(fpopup_common->radio_callback),
296 (gpointer)folderview);
302 static void create_action_groups(gpointer key, gpointer value, gpointer data)
304 FolderView *folderview = data;
305 FolderViewPopup *fpopup = value;
306 GtkActionGroup *group;
308 group = create_action_group(folderview, fpopup);
309 g_hash_table_insert(folderview->popups, fpopup->klass, group);
312 static void folderview_column_set_titles(FolderView *folderview)
314 GtkWidget *ctree = folderview->ctree;
315 GtkWidget *label_folder;
316 GtkWidget *label_new;
317 GtkWidget *label_unread;
318 GtkWidget *label_total;
319 GtkWidget *hbox_folder;
321 GtkWidget *hbox_unread;
322 GtkWidget *hbox_total;
323 gint *col_pos = folderview->col_pos;
325 debug_print("setting titles...\n");
326 gtk_widget_realize(folderview->ctree);
327 gtk_widget_show_all(folderview->scrolledwin);
329 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
330 * instead text (text overflows making them unreadable and ugly) */
331 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
333 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
335 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
338 label_folder = gtk_label_new(_("Folder"));
339 label_new = gtk_image_new_from_pixbuf(newxpm);
340 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
341 label_total = gtk_image_new_from_pixbuf(readxpm);
343 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
345 hbox_folder = gtk_hbox_new(FALSE, 4);
346 hbox_new = gtk_hbox_new(FALSE, 4);
347 hbox_unread = gtk_hbox_new(FALSE, 4);
348 hbox_total = gtk_hbox_new(FALSE, 4);
351 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
352 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
353 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
354 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
355 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
360 gtk_widget_show_all(hbox_folder);
361 gtk_widget_show_all(hbox_new);
362 gtk_widget_show_all(hbox_unread);
363 gtk_widget_show_all(hbox_total);
366 gtk_widget_set_size_request(hbox_new, -1, 20);
367 gtk_widget_set_size_request(hbox_unread, -1, 20);
368 gtk_widget_set_size_request(hbox_total, -1, 20);
371 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
372 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
373 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
374 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
381 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
382 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
385 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
387 FolderView *folderview = (FolderView *)data;
388 GdkEventButton event;
389 if (folderview_get_selected_item(folderview) == NULL)
393 event.time = gtk_get_current_event_time();
395 folderview_set_sens_and_popup_menu(folderview, -1,
402 static GtkWidget *folderview_ctree_create(FolderView *folderview)
406 FolderColumnState *col_state;
407 FolderColumnType type;
408 gchar *titles[N_FOLDER_COLS];
410 GtkWidget *scrolledwin = folderview->scrolledwin;
412 debug_print("creating tree...\n");
413 memset(titles, 0, sizeof(titles));
415 col_state = prefs_folder_column_get_config();
416 memset(titles, 0, sizeof(titles));
418 col_pos = folderview->col_pos;
420 for (i = 0; i < N_FOLDER_COLS; i++) {
421 folderview->col_state[i] = col_state[i];
422 type = col_state[i].type;
426 titles[col_pos[F_COL_FOLDER]] = _("Folder");
427 titles[col_pos[F_COL_NEW]] = _("New");
428 titles[col_pos[F_COL_UNREAD]] = _("Unread");
429 /* TRANSLATORS: This in Number sign in American style */
430 titles[col_pos[F_COL_TOTAL]] = _("#");
432 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
435 if (prefs_common.show_col_headers == FALSE)
436 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
439 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
440 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
442 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
443 col_pos[F_COL_UNREAD],
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
446 col_pos[F_COL_TOTAL],
448 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
449 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
450 GTK_CMCTREE_EXPANDER_TRIANGLE);
452 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
453 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
455 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
456 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
458 /* don't let title buttons take key focus */
459 for (i = 0; i < N_FOLDER_COLS; i++) {
460 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
461 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
462 prefs_common.folder_col_size[i]);
463 gtk_cmclist_set_column_visibility
464 (GTK_CMCLIST(ctree), i, col_state[i].visible);
467 g_signal_connect(G_OBJECT(ctree), "key_press_event",
468 G_CALLBACK(folderview_key_pressed),
470 g_signal_connect(G_OBJECT(ctree), "button_press_event",
471 G_CALLBACK(folderview_button_pressed),
473 g_signal_connect(G_OBJECT(ctree), "popup-menu",
474 G_CALLBACK(folderview_popup_menu), folderview);
475 g_signal_connect(G_OBJECT(ctree), "button_release_event",
476 G_CALLBACK(folderview_button_released),
478 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
479 G_CALLBACK(folderview_selected), folderview);
481 /* drag-n-dropping folders on maemo is impractical as this
482 * opens the folder almost everytime */
483 g_signal_connect(G_OBJECT(ctree), "start_drag",
484 G_CALLBACK(folderview_start_drag), folderview);
486 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
487 G_CALLBACK(folderview_drag_data_get),
490 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
491 G_CALLBACK(folderview_tree_expanded),
493 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
494 G_CALLBACK(folderview_tree_collapsed),
497 g_signal_connect(G_OBJECT(ctree), "resize_column",
498 G_CALLBACK(folderview_col_resized),
502 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
503 folderview_drag_types, 2,
504 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
505 g_signal_connect(G_OBJECT(ctree), "drag_motion",
506 G_CALLBACK(folderview_drag_motion_cb),
508 g_signal_connect(G_OBJECT(ctree), "drag_leave",
509 G_CALLBACK(folderview_drag_leave_cb),
511 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
512 G_CALLBACK(folderview_drag_received_cb),
514 g_signal_connect(G_OBJECT(ctree), "drag_end",
515 G_CALLBACK(folderview_drag_end_cb),
518 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
523 void folderview_set_column_order(FolderView *folderview)
525 GtkWidget *ctree = folderview->ctree;
526 FolderItem *item = folderview_get_selected_item(folderview);
527 FolderItem *sel_item = NULL, *op_item = NULL;
528 GtkWidget *scrolledwin = folderview->scrolledwin;
530 if (folderview->drag_timer_id != 0) {
531 g_source_remove(folderview->drag_timer_id);
532 folderview->drag_timer_id = 0;
534 if (folderview->deferred_refresh_id != 0) {
535 g_source_remove(folderview->deferred_refresh_id);
536 folderview->deferred_refresh_id = 0;
538 if (folderview->scroll_timeout_id != 0) {
539 g_source_remove(folderview->scroll_timeout_id);
540 folderview->scroll_timeout_id = 0;
542 if (folderview->postpone_select_id != 0) {
543 g_source_remove(folderview->postpone_select_id);
544 folderview->postpone_select_id = 0;
547 if (folderview->selected)
548 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
549 if (folderview->opened)
550 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
552 debug_print("recreating tree...\n");
553 gtk_widget_destroy(folderview->ctree);
556 folderview->ctree = ctree = folderview_ctree_create(folderview);
557 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
558 GTK_CMCLIST(ctree)->hadjustment);
559 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
560 GTK_CMCLIST(ctree)->vadjustment);
561 gtk_widget_show(ctree);
564 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
566 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
568 folderview_set(folderview);
569 folderview_column_set_titles(folderview);
571 folderview_select(folderview,item);
574 FolderView *folderview_create(void)
576 FolderView *folderview;
577 GtkWidget *scrolledwin;
580 debug_print("Creating folder view...\n");
581 folderview = g_new0(FolderView, 1);
583 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
584 gtk_scrolled_window_set_policy
585 (GTK_SCROLLED_WINDOW(scrolledwin),
586 GTK_POLICY_AUTOMATIC,
587 prefs_common.folderview_vscrollbar_policy);
588 gtk_widget_set_size_request(scrolledwin,
589 prefs_common.folderview_width,
590 prefs_common.folderview_height);
592 folderview->scrolledwin = scrolledwin;
593 ctree = folderview_ctree_create(folderview);
595 /* create popup factories */
596 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
597 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
599 folderview->ctree = ctree;
601 folderview->folder_update_callback_id =
602 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
603 folderview->folder_item_update_callback_id =
604 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
606 gtk_widget_show_all(scrolledwin);
608 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
609 folderview_list = g_list_append(folderview_list, folderview);
611 folderview->drag_timer_id = 0;
612 folderview->deferred_refresh_id = 0;
613 folderview->scroll_timeout_id = 0;
614 folderview->postpone_select_id = 0;
619 void folderview_init(FolderView *folderview)
621 GtkWidget *ctree = folderview->ctree;
623 PangoFontDescription *normal_font;
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
672 normal_font = pango_font_description_from_string(NORMAL_FONT);
674 gtk_widget_modify_font(ctree, normal_font);
675 pango_font_description_free(normal_font);
677 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
680 PangoFontDescription *font_desc;
681 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
682 font_desc = pango_font_description_from_string(NORMAL_FONT);
684 if (normal_style->font_desc)
685 pango_font_description_free
686 (normal_style->font_desc);
687 normal_style->font_desc = font_desc;
689 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
690 normal_color_style = gtk_style_copy(normal_style);
691 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
695 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
696 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
697 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
698 PangoFontDescription *font_desc;
699 font_desc = pango_font_description_from_string(NORMAL_FONT);
701 pango_font_description_free(bold_style->font_desc);
702 bold_style->font_desc = font_desc;
704 pango_font_description_set_weight
705 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
707 PangoFontDescription *font_desc;
708 font_desc = pango_font_description_from_string(BOLD_FONT);
710 if (bold_style->font_desc)
711 pango_font_description_free
712 (bold_style->font_desc);
713 bold_style->font_desc = font_desc;
716 bold_color_style = gtk_style_copy(bold_style);
717 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
719 bold_tgtfold_style = gtk_style_copy(bold_style);
720 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
724 static gboolean folderview_defer_set(gpointer data)
726 FolderView *folderview = (FolderView *)data;
727 MainWindow *mainwin = folderview->mainwin;
731 if (mainwin->lock_count)
734 debug_print("doing deferred folderview_set now\n");
735 folderview_set(folderview);
737 folderview->deferred_refresh_id = 0;
741 void folderview_set(FolderView *folderview)
743 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
744 MainWindow *mainwin = folderview->mainwin;
745 FolderItem *sel_item = NULL, *op_item = NULL;
750 if (mainwin->lock_count) {
751 if (folderview->deferred_refresh_id == 0)
752 folderview->deferred_refresh_id =
753 g_timeout_add(500, folderview_defer_set, folderview);
754 debug_print("deferred folderview_set\n");
759 debug_print("Setting folder info...\n");
760 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
762 main_window_cursor_wait(mainwin);
764 if (folderview->selected)
765 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
766 if (folderview->opened)
767 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
769 folderview->selected = NULL;
770 folderview->opened = NULL;
772 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
773 gtk_cmclist_clear(GTK_CMCLIST(ctree));
775 folderview_set_folders(folderview);
778 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
780 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
782 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
783 main_window_cursor_normal(mainwin);
784 STATUSBAR_POP(mainwin);
788 void folderview_set_all(void)
792 for (list = folderview_list; list != NULL; list = list->next)
793 folderview_set((FolderView *)list->data);
796 void folderview_select(FolderView *folderview, FolderItem *item)
798 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
799 GtkCMCTreeNode *node;
800 GtkCMCTreeNode *old_selected = folderview->selected;
804 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
805 if (node) folderview_select_node(folderview, node);
807 if (old_selected != node)
808 folder_update_op_count();
811 static void mark_all_read_cb(GtkAction *action, gpointer data)
813 FolderView *folderview = (FolderView *)data;
817 item = folderview_get_selected_item(folderview);
821 if (folderview->summaryview->folder_item != item
822 && prefs_common.ask_mark_all_read) {
823 val = alertpanel_full(_("Mark all as read"),
824 _("Do you really want to mark all mails in this "
825 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
826 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
828 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
830 else if (val & G_ALERTDISABLE)
831 prefs_common.ask_mark_all_read = FALSE;
835 folder_item_update_freeze();
836 if (folderview->summaryview->folder_item != item)
837 summary_lock(folderview->summaryview);
839 summary_freeze(folderview->summaryview);
841 folderutils_mark_all_read(item);
843 if (folderview->summaryview->folder_item != item)
844 summary_unlock(folderview->summaryview);
846 summary_thaw(folderview->summaryview);
847 folder_item_update_thaw();
850 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
852 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
854 cm_return_if_fail(node != NULL);
856 if (folderview->open_folder) {
860 folderview->open_folder = TRUE;
861 gtkut_ctree_set_focus_row(ctree, node);
862 gtk_cmctree_select(ctree, node);
863 if ((folderview->summaryview->folder_item &&
864 folderview->summaryview->folder_item->total_msgs > 0) ||
865 prefs_common.layout_mode == SMALL_LAYOUT)
866 summary_grab_focus(folderview->summaryview);
868 gtk_widget_grab_focus(folderview->ctree);
870 gtkut_ctree_expand_parent_all(ctree, node);
873 void folderview_unselect(FolderView *folderview)
875 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
877 (GTK_CMCTREE(folderview->ctree), folderview->opened);
879 folderview->selected = folderview->opened = NULL;
882 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
883 GtkCMCTreeNode *node,
889 node = gtkut_ctree_node_next(ctree, node);
891 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
893 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
894 item = gtk_cmctree_node_get_row_data(ctree, node);
895 if (item->stype == F_TRASH || item->stype == F_DRAFT)
899 if(item->unread_msgs > 0)
903 if(item->new_msgs > 0)
907 if(item->marked_msgs > 0)
916 void folderview_select_next_with_flag(FolderView *folderview,
920 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
921 GtkCMCTreeNode *node = NULL;
922 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
923 gboolean last_open = prefs_common.always_show_msg;
927 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
930 prefs_common.summary_select_prio[0] = ACTION_NEW;
933 prefs_common.summary_select_prio[0] = ACTION_MARKED;
936 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
938 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
940 folderview_select_node(folderview, node);
944 if (!folderview->opened ||
945 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
949 /* search again from the first node */
950 node = folderview_find_next_with_flag(ctree, NULL, flag);
952 folderview_select_node(folderview, node);
955 prefs_common.summary_select_prio[0] = last_summary_select_prio;
956 prefs_common.always_show_msg = last_open;
959 FolderItem *folderview_get_selected_item(FolderView *folderview)
961 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
963 if (!folderview->selected) return NULL;
964 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
967 static void folderview_set_folders(FolderView *folderview)
970 list = folder_get_list();
972 for (; list != NULL; list = list->next) {
973 folderview_append_folder(folderview, FOLDER(list->data));
977 static gchar *get_scan_str(FolderItem *item)
980 return g_strdup_printf(_("Scanning folder %s%c%s..."),
981 item->folder->name, G_DIR_SEPARATOR,
984 return g_strdup_printf(_("Scanning folder %s..."),
987 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
991 for (list = folderview_list; list != NULL; list = list->next) {
992 FolderView *folderview = (FolderView *)list->data;
993 MainWindow *mainwin = folderview->mainwin;
994 gchar *str = get_scan_str(item);
996 STATUSBAR_PUSH(mainwin, str);
997 STATUSBAR_POP(mainwin);
1002 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1005 MainWindow *mainwin = mainwindow_get_mainwindow();
1006 FolderView *folderview = NULL;
1007 GtkAdjustment *pos = NULL;
1010 cm_return_if_fail(folder != NULL);
1012 if (!folder->klass->scan_tree) return;
1015 alertpanel_full(_("Rebuild folder tree"),
1016 _("Rebuilding the folder tree will remove "
1017 "local caches. Do you want to continue?"),
1018 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1019 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1020 != G_ALERTALTERNATE) {
1026 window = label_window_create(_("Rebuilding folder tree..."));
1028 window = label_window_create(_("Scanning folder tree..."));
1031 folderview = mainwin->folderview;
1034 pos = gtk_scrolled_window_get_vadjustment(
1035 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1036 height = gtk_adjustment_get_value(pos);
1039 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1040 folder_scan_tree(folder, rebuild);
1041 folder_set_ui_func(folder, NULL, NULL);
1043 folderview_set_all();
1046 pos = gtk_scrolled_window_get_vadjustment(
1047 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1048 gtk_adjustment_set_value(pos, height);
1049 gtk_adjustment_changed(pos);
1051 label_window_destroy(window);
1055 /** folderview_check_new()
1056 * Scan and update the folder and return the
1057 * count the number of new messages since last check.
1058 * \param folder the folder to check for new messages
1059 * \return the number of new messages since last check
1061 gint folderview_check_new(Folder *folder)
1065 FolderView *folderview;
1067 GtkCMCTreeNode *node;
1069 gint former_new_msgs = 0;
1070 gint former_new = 0, former_unread = 0, former_total;
1072 for (list = folderview_list; list != NULL; list = list->next) {
1073 folderview = (FolderView *)list->data;
1074 ctree = GTK_CMCTREE(folderview->ctree);
1075 folderview->scanning_folder = folder;
1077 main_window_lock(folderview->mainwin);
1079 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1080 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1082 item = gtk_cmctree_node_get_row_data(ctree, node);
1083 if (!item || !item->path || !item->folder) continue;
1084 if (item->no_select) continue;
1085 if (folder && folder != item->folder) continue;
1086 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1087 if (!item->prefs->newmailcheck) continue;
1088 if (item->processing_pending == TRUE) {
1089 debug_print("skipping %s, processing pending\n",
1090 item->path ? item->path : item->name);
1093 if (item->scanning != ITEM_NOT_SCANNING) {
1094 debug_print("skipping %s, scanning\n",
1095 item->path ? item->path : item->name);
1099 str = get_scan_str(item);
1101 STATUSBAR_PUSH(folderview->mainwin, str);
1105 folderview_scan_tree_func(item->folder, item, NULL);
1106 former_new = item->new_msgs;
1107 former_unread = item->unread_msgs;
1108 former_total = item->total_msgs;
1110 if (item->folder->klass->scan_required &&
1111 (item->folder->klass->scan_required(item->folder, item) ||
1112 item->folder->inbox == item ||
1113 item->opened == TRUE ||
1114 item->processing_pending == TRUE)) {
1115 if (folder_item_scan(item) < 0) {
1117 summaryview_unlock(folderview->summaryview, item);
1118 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1119 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1120 item->path ? item->path:item->name);
1121 STATUSBAR_POP(folderview->mainwin);
1123 } else if (!FOLDER_IS_LOCAL(folder)) {
1124 STATUSBAR_POP(folderview->mainwin);
1129 } else if (!item->folder->klass->scan_required) {
1130 if (folder_item_scan(item) < 0) {
1131 summaryview_unlock(folderview->summaryview, item);
1132 if (folder && !FOLDER_IS_LOCAL(folder)) {
1133 STATUSBAR_POP(folderview->mainwin);
1138 if (former_new != item->new_msgs ||
1139 former_unread != item->unread_msgs ||
1140 former_total != item->total_msgs)
1141 folderview_update_node(folderview, node);
1143 new_msgs += item->new_msgs;
1144 former_new_msgs += former_new;
1145 STATUSBAR_POP(folderview->mainwin);
1147 folderview->scanning_folder = NULL;
1148 main_window_unlock(folderview->mainwin);
1152 folder_write_list();
1153 /* Number of new messages since last check is the just the difference
1154 * between former_new_msgs and new_msgs. If new_msgs is less than
1155 * former_new_msgs, that would mean another session accessed the folder
1156 * and the result is not well defined.
1158 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1162 void folderview_check_new_all(void)
1166 FolderView *folderview;
1168 folderview = (FolderView *)folderview_list->data;
1171 main_window_lock(folderview->mainwin);
1172 window = label_window_create
1173 (_("Checking for new messages in all folders..."));
1175 list = folder_get_list();
1176 for (; list != NULL; list = list->next) {
1177 Folder *folder = list->data;
1179 folderview_check_new(folder);
1182 folder_write_list();
1183 folderview_set_all();
1185 label_window_destroy(window);
1186 main_window_unlock(folderview->mainwin);
1190 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1196 if (!item || !item->folder || !item->folder->node)
1199 node = item->folder->node;
1201 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1202 node = node->children;
1205 (item->new_msgs > 0 ||
1206 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1210 while (node != NULL) {
1211 if (node && node->data) {
1212 FolderItem *next_item = (FolderItem*) node->data;
1214 if (folderview_have_new_children_sub(folderview,
1223 static gboolean folderview_have_new_children(FolderView *folderview,
1226 return folderview_have_new_children_sub(folderview, item, FALSE);
1229 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1235 if (!item || !item->folder || !item->folder->node)
1238 node = item->folder->node;
1240 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1241 node = node->children;
1244 (item->unread_msgs > 0 ||
1245 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1249 while (node != NULL) {
1250 if (node && node->data) {
1251 FolderItem *next_item = (FolderItem*) node->data;
1253 if (folderview_have_unread_children_sub(folderview,
1263 static gboolean folderview_have_unread_children(FolderView *folderview,
1266 return folderview_have_unread_children_sub(folderview, item, FALSE);
1269 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1275 if (!item || !item->folder || !item->folder->node)
1278 node = item->folder->node;
1280 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1281 node = node->children;
1283 if (in_sub && item->search_match){
1287 while (node != NULL) {
1288 if (node && node->data) {
1289 FolderItem *next_item = (FolderItem*) node->data;
1291 if (folderview_have_matching_children_sub(folderview,
1301 static gboolean folderview_have_matching_children(FolderView *folderview,
1304 return folderview_have_matching_children_sub(folderview, item, FALSE);
1307 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1313 if (!item || !item->folder || !item->folder->node)
1316 node = item->folder->node;
1318 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1319 node = node->children;
1321 if (item->marked_msgs != 0) {
1325 while (node != NULL) {
1326 if (node && node->data) {
1327 FolderItem *next_item = (FolderItem*) node->data;
1329 if (folderview_have_marked_children_sub(folderview,
1338 static gboolean folderview_have_marked_children(FolderView *folderview,
1341 return folderview_have_marked_children_sub(folderview, item, FALSE);
1344 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1346 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1347 GtkStyle *style = NULL;
1348 GtkStyle *color_style = NULL;
1350 GdkPixbuf *xpm, *openxpm;
1351 static GdkPixbuf *searchicon;
1352 gboolean mark = FALSE;
1355 gboolean add_unread_mark;
1356 gboolean add_sub_match_mark;
1357 gboolean use_bold, use_color;
1358 gint *col_pos = folderview->col_pos;
1359 SpecialFolderItemType stype;
1361 item = gtk_cmctree_node_get_row_data(ctree, node);
1362 cm_return_if_fail(item != NULL);
1364 if (!GTK_CMCTREE_ROW(node)->expanded)
1365 mark = folderview_have_marked_children(folderview, item);
1367 mark = (item->marked_msgs != 0);
1369 stype = item->stype;
1370 if (stype == F_NORMAL) {
1371 if (folder_has_parent_of_type(item, F_TRASH))
1373 else if (folder_has_parent_of_type(item, F_DRAFT))
1375 else if (folder_has_parent_of_type(item, F_OUTBOX))
1377 else if (folder_has_parent_of_type(item, F_QUEUE))
1382 if (item->hide_read_msgs || item->hide_read_threads) {
1383 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1384 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1386 xpm = mark?m_inboxxpm:inboxxpm;
1387 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1391 if (item->hide_read_msgs || item->hide_read_threads) {
1392 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1393 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1395 xpm = mark?m_outboxxpm:outboxxpm;
1396 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1400 if (item->hide_read_msgs || item->hide_read_threads) {
1401 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1402 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1404 xpm = mark?m_queuexpm:queuexpm;
1405 openxpm = mark?m_queueopenxpm:queueopenxpm;
1409 if (item->hide_read_msgs || item->hide_read_threads) {
1410 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1411 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1413 xpm = mark?m_trashxpm:trashxpm;
1414 openxpm = mark?m_trashopenxpm:trashopenxpm;
1418 xpm = mark?m_draftsxpm:draftsxpm;
1419 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1422 if (item->hide_read_msgs || item->hide_read_threads) {
1423 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1424 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1426 xpm = mark?m_folderxpm:folderxpm;
1427 openxpm = mark?m_folderopenxpm:folderopenxpm;
1431 if (item->no_select) {
1432 xpm = openxpm = noselectxpm;
1435 name = folder_item_get_name(item);
1437 if (!GTK_CMCTREE_ROW(node)->expanded) {
1438 add_unread_mark = folderview_have_unread_children(
1440 add_sub_match_mark = folderview_have_matching_children(
1443 add_unread_mark = FALSE;
1444 add_sub_match_mark = FALSE;
1447 if (item->search_match) {
1449 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1452 xpm = openxpm = searchicon;
1456 if (prefs_common.display_folder_unread) {
1457 if (folder_has_parent_of_type(item, F_QUEUE)) {
1458 /* only total_msgs matters here */
1459 if (item->total_msgs > 0) {
1460 /* show total number (should be equal to the unread number)
1462 str = g_strdup_printf("%s (%d%s%s)",
1463 name, item->total_msgs,
1464 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1465 (item->unreadmarked_msgs > 0) ? "!" : "");
1468 if (prefs_common.display_folder_unread == 1) {
1469 if (item->unread_msgs > 0) {
1470 /* show unread number and signs */
1471 str = g_strdup_printf("%s (%d%s%s)",
1472 name, item->unread_msgs,
1473 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1474 (item->unreadmarked_msgs > 0) ? "!" : "");
1477 if (item->total_msgs > 0) {
1478 /* show unread number, total number and signs if any */
1479 str = g_strdup_printf("%s (%d/%d%s%s)",
1480 name, item->unread_msgs, item->total_msgs,
1481 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1482 (item->unreadmarked_msgs > 0) ? "!" : "");
1486 if ((str == NULL) &&
1487 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1488 /* no unread/total numbers, but at least one sign */
1489 str = g_strdup_printf("%s (%s%s)",
1491 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1492 (item->unreadmarked_msgs > 0) ? "!" : "");
1496 /* last fallback, folder name only or with +! sign */
1497 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1498 str = g_strdup_printf("%s%s",
1500 } else if (item->unreadmarked_msgs > 0) {
1501 str = g_strdup_printf("%s%s",
1503 } else if (add_sub_match_mark) {
1504 str = g_strdup_printf("%s%s",
1507 str = g_strdup_printf("%s", name);
1510 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1512 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1516 if (!folder_item_parent(item)) {
1517 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1518 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1519 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1521 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1522 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1523 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1526 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1527 folder_has_parent_of_type(item, F_TRASH)) {
1528 use_bold = use_color = FALSE;
1529 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1530 GSList *list = folder_item_get_msg_list(item);
1532 use_bold = use_color = FALSE;
1533 for (cur = list; cur; cur = cur->next) {
1534 MsgInfo *msginfo = (MsgInfo *)cur->data;
1535 if (!MSG_IS_DELETED(msginfo->flags)) {
1536 /* highlight queue folder if there are any messages */
1537 use_bold = use_color = TRUE;
1541 procmsg_msg_list_free(list);
1543 /* if unread messages exist, print with bold font */
1544 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1546 /* if new messages exist, print with colored letter */
1548 (item->new_msgs > 0) ||
1550 folderview_have_new_children(folderview, item));
1553 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1558 if (item->prefs->color > 0 && !use_color) {
1559 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1560 color_style = gtk_style_copy(bold_style);
1561 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1562 style = color_style;
1563 } else if (use_color) {
1564 style = bold_color_style;
1567 if (item->op_count > 0) {
1568 style = bold_tgtfold_style;
1570 } else if (use_color) {
1571 style = normal_color_style;
1572 gtk_cmctree_node_set_foreground(ctree, node,
1573 &folderview->color_new);
1574 } else if (item->op_count > 0) {
1575 style = bold_tgtfold_style;
1576 } else if (item->prefs->color > 0) {
1578 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1579 color_style = gtk_style_copy(normal_style);
1580 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1581 style = color_style;
1583 style = normal_style;
1586 gtk_cmctree_node_set_row_style(ctree, node, style);
1588 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1589 folderview_update_node(folderview, node);
1592 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1595 FolderView *folderview;
1597 GtkCMCTreeNode *node;
1599 cm_return_if_fail(item != NULL);
1601 for (list = folderview_list; list != NULL; list = list->next) {
1602 folderview = (FolderView *)list->data;
1603 ctree = GTK_CMCTREE(folderview->ctree);
1605 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1606 if (node && item->search_match != matches) {
1607 item->search_match = matches;
1608 folderview_update_node(folderview, node);
1613 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1615 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1616 FolderView *folderview = (FolderView *)data;
1618 GtkCMCTreeNode *node;
1619 cm_return_val_if_fail(update_info != NULL, TRUE);
1620 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1621 cm_return_val_if_fail(folderview != NULL, FALSE);
1623 ctree = GTK_CMCTREE(folderview->ctree);
1625 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1628 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1629 folderview_update_node(folderview, node);
1631 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1632 update_info->item == folderview->summaryview->folder_item &&
1633 update_info->item != NULL)
1634 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1635 summary_show(folderview->summaryview, update_info->item);
1641 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1642 GNode *gnode, GtkCMCTreeNode *cnode,
1645 FolderView *folderview = (FolderView *)data;
1646 FolderItem *item = FOLDER_ITEM(gnode->data);
1648 cm_return_val_if_fail(item != NULL, FALSE);
1650 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1651 folderview_update_node(folderview, cnode);
1656 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1659 FolderView *folderview = (FolderView *)data;
1662 if (GTK_CMCTREE_ROW(node)->children) {
1663 item = gtk_cmctree_node_get_row_data(ctree, node);
1664 cm_return_if_fail(item != NULL);
1666 if (!item->collapsed)
1667 gtk_cmctree_expand(ctree, node);
1669 folderview_update_node(folderview, node);
1673 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1674 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1677 GtkCMCTreeNode *node, *parent, *sibling;
1679 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1681 g_warning("%s not found.\n", item->path);
1683 parent = GTK_CMCTREE_ROW(node)->parent;
1684 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1685 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1687 sibling = GTK_CMCTREE_ROW(parent)->children;
1691 tmp = gtk_cmctree_node_get_row_data
1693 if (tmp->stype != F_NORMAL)
1694 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1698 if (node != sibling)
1699 gtk_cmctree_move(ctree, node, parent, sibling);
1706 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1709 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1710 GtkCMCTreeNode *prev = NULL;
1712 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1713 gtk_sctree_sort_recursive(ctree, root);
1714 if (root && GTK_CMCTREE_ROW(root)->parent) {
1715 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1718 set_special_folder(ctree, folder->inbox, root, &prev);
1719 set_special_folder(ctree, folder->outbox, root, &prev);
1720 set_special_folder(ctree, folder->draft, root, &prev);
1721 set_special_folder(ctree, folder->queue, root, &prev);
1722 set_special_folder(ctree, folder->trash, root, &prev);
1723 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1726 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1728 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1729 GtkCMCTreeNode *root;
1731 cm_return_if_fail(folder != NULL);
1733 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1734 folderview_gnode_func, folderview);
1735 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1737 folderview_sort_folders(folderview, root, folder);
1740 /* callback functions */
1741 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1742 GdkEventButton *event)
1746 FolderViewPopup *fpopup;
1747 GtkActionGroup *action_group;
1749 FolderItem *special_trash = NULL, *special_queue = NULL;
1751 GtkUIManager *ui_manager = gtk_ui_manager_new();
1753 if (folderview->ui_manager)
1754 g_object_unref(folderview->ui_manager);
1756 folderview->ui_manager = ui_manager;
1757 item = folderview_get_selected_item(folderview);
1759 cm_return_if_fail(item != NULL);
1760 cm_return_if_fail(item->folder != NULL);
1761 folder = item->folder;
1763 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1766 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1768 fpopup = g_hash_table_lookup(folderview_popups, "common");
1769 action_group = g_hash_table_lookup(folderview->popups, "common");
1772 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1773 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1774 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1776 if (fpopup->add_menuitems)
1777 fpopup->add_menuitems(ui_manager, item);
1779 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1780 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1781 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1782 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1783 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1784 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1786 if (fpopup->set_sensitivity != NULL)
1787 fpopup->set_sensitivity(ui_manager, item);
1789 if (NULL != (ac = account_find_from_item(item))) {
1790 special_trash = account_get_special_folder(ac, F_TRASH);
1791 special_queue = account_get_special_folder(ac, F_QUEUE);
1794 if ((item == folder->trash || item == special_trash
1795 || folder_has_parent_of_type(item, F_TRASH))) {
1796 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1797 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1800 if ((item == folder->queue || item == special_queue
1801 || folder_has_parent_of_type(item, F_QUEUE))) {
1802 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1803 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1806 #define SET_SENS(name, sens) \
1807 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1809 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1810 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1811 folderview->selected == folderview->opened);
1812 SET_SENS("FolderViewPopup/Properties", TRUE);
1814 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1815 item->total_msgs >= 1 && !item->processing_pending);
1816 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1817 !item->no_select && !item->processing_pending);
1819 if (item == folder->trash || item == special_trash
1820 || folder_has_parent_of_type(item, F_TRASH)) {
1821 GSList *msglist = folder_item_get_msg_list(item);
1822 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1823 procmsg_msg_list_free(msglist);
1825 if (item == folder->queue || item == special_queue
1826 || folder_has_parent_of_type(item, F_QUEUE)) {
1827 GSList *msglist = folder_item_get_msg_list(item);
1828 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1829 procmsg_msg_list_free(msglist);
1833 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1834 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1835 g_signal_connect(G_OBJECT(popup), "selection_done",
1836 G_CALLBACK(folderview_popup_close),
1838 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1839 event->button, event->time);
1842 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1843 FolderView *folderview)
1845 GtkCMCList *clist = GTK_CMCLIST(ctree);
1846 gint prev_row = -1, row = -1, column = -1;
1848 if (!event) return FALSE;
1850 if (event->button == 1 || event->button == 2) {
1851 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1852 folderview->open_folder = TRUE;
1854 if (event->type == GDK_2BUTTON_PRESS) {
1855 if (clist->selection) {
1856 GtkCMCTreeNode *node;
1858 node = GTK_CMCTREE_NODE(clist->selection->data);
1860 gtk_cmctree_toggle_expansion(
1863 folderview->open_folder = FALSE;
1870 if (event->button == 2 || event->button == 3) {
1872 if (clist->selection) {
1873 GtkCMCTreeNode *node;
1875 node = GTK_CMCTREE_NODE(clist->selection->data);
1877 prev_row = gtkut_ctree_get_nth_from_node
1878 (GTK_CMCTREE(ctree), node);
1881 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1884 if (prev_row != row) {
1885 gtk_cmclist_unselect_all(clist);
1886 if (event->button == 2)
1887 folderview_select_node
1889 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1892 gtk_cmclist_select_row(clist, row, column);
1896 if (event->button != 3) return FALSE;
1898 folderview_set_sens_and_popup_menu(folderview, row, event);
1902 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1903 FolderView *folderview)
1905 int row = -1, column = -1;
1907 if (!event) return FALSE;
1909 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1912 if (event->button == 1 && folderview->open_folder == FALSE &&
1913 folderview->opened != NULL) {
1914 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1915 folderview->opened);
1916 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1922 #define BREAK_ON_MODIFIER_KEY() \
1923 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1925 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1926 FolderView *folderview)
1928 if (!event) return FALSE;
1930 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1933 switch (event->keyval) {
1935 #ifndef GENERIC_UMPC
1936 case GDK_KEY_Return:
1937 case GDK_KEY_KP_Enter:
1939 if (folderview->selected) {
1940 folderview_select_node(folderview,
1941 folderview->selected);
1945 case GDK_KEY_Return:
1946 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
1947 gtk_cmctree_toggle_expansion(
1948 GTK_CMCTREE(folderview->ctree),
1949 folderview->selected);
1954 BREAK_ON_MODIFIER_KEY();
1955 if (folderview->selected) {
1956 if (folderview->opened == folderview->selected &&
1957 (!folderview->summaryview->folder_item ||
1958 folderview->summaryview->folder_item->total_msgs == 0))
1959 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
1961 folderview_select_node(folderview,
1962 folderview->selected);
1972 typedef struct _PostponedSelectData
1975 GtkCMCTreeNode *row;
1977 FolderView *folderview;
1978 } PostponedSelectData;
1980 static gboolean postpone_select(void *data)
1982 PostponedSelectData *psdata = (PostponedSelectData *)data;
1983 debug_print("trying again\n");
1985 psdata->folderview->postpone_select_id = 0;
1986 psdata->folderview->open_folder = TRUE;
1987 main_window_cursor_normal(psdata->folderview->mainwin);
1988 STATUSBAR_POP(psdata->folderview->mainwin);
1989 folderview_selected(psdata->ctree, psdata->row,
1990 psdata->column, psdata->folderview);
1995 void folderview_close_opened(FolderView *folderview)
1997 if (folderview->opened) {
1998 FolderItem *olditem;
2000 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2001 folderview->opened);
2003 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2004 olditem->path ? olditem->path:olditem->name);
2005 /* will be null if we just moved the previously opened folder */
2006 STATUSBAR_PUSH(folderview->mainwin, buf);
2007 main_window_cursor_wait(folderview->mainwin);
2009 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2010 summary_show(folderview->summaryview, NULL);
2011 folder_item_close(olditem);
2012 main_window_cursor_normal(folderview->mainwin);
2013 STATUSBAR_POP(folderview->mainwin);
2014 if (olditem->folder->klass->item_closed)
2015 olditem->folder->klass->item_closed(olditem);
2020 if (folderview->opened &&
2021 !GTK_CMCTREE_ROW(folderview->opened)->children)
2022 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2024 folderview->opened = NULL;
2026 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2027 gint column, FolderView *folderview)
2029 static gboolean can_select = TRUE; /* exclusive lock */
2034 GtkCMCTreeNode *old_opened = folderview->opened;
2036 folderview->selected = row;
2038 debug_print("newly selected %p, opened %p\n", folderview->selected,
2039 folderview->opened);
2040 if (folderview->opened == row) {
2041 folderview->open_folder = FALSE;
2046 item = gtk_cmctree_node_get_row_data(ctree, row);
2049 folderview->open_folder = FALSE;
2053 if (!can_select || summary_is_locked(folderview->summaryview)) {
2054 if (folderview->opened) {
2055 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2056 gtk_cmctree_select(ctree, folderview->opened);
2058 folderview->open_folder = FALSE;
2063 if (!folderview->open_folder) {
2070 /* Save cache for old folder */
2071 /* We don't want to lose all caches if sylpheed crashed */
2072 /* resets folderview->opened to NULL */
2073 folderview_close_opened(folderview);
2075 /* CLAWS: set compose button type: news folder items
2076 * always have a news folder as parent */
2078 toolbar_set_compose_button
2079 (folderview->mainwin->toolbar,
2080 FOLDER_TYPE(item->folder) == F_NEWS ?
2081 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2084 debug_print("Folder %s is selected\n", item->path);
2086 if (!GTK_CMCTREE_ROW(row)->children)
2087 gtk_cmctree_expand(ctree, row);
2089 /* ungrab the mouse event */
2090 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2091 gtk_grab_remove(GTK_WIDGET(ctree));
2092 if (gdk_pointer_is_grabbed())
2093 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2097 /* TODO: wwp: avoid displaying (null) in the status bar */
2098 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2099 item->path : "(null)");
2100 debug_print("%s\n", buf);
2101 STATUSBAR_PUSH(folderview->mainwin, buf);
2104 main_window_cursor_wait(folderview->mainwin);
2106 if (folderview->scanning_folder == item->folder) {
2109 res = folder_item_open(item);
2112 if (res == -1 && item->no_select == FALSE) {
2113 main_window_cursor_normal(folderview->mainwin);
2114 STATUSBAR_POP(folderview->mainwin);
2116 alertpanel_error(_("Folder could not be opened."));
2118 folderview->open_folder = FALSE;
2122 } else if (res == -2 && item->no_select == FALSE) {
2123 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2124 data->ctree = ctree;
2126 data->column = column;
2127 data->folderview = folderview;
2128 debug_print("postponing open of %s till end of scan\n",
2129 item->path ? item->path:item->name);
2130 folderview->open_folder = FALSE;
2132 if (folderview->postpone_select_id != 0)
2133 g_source_remove(folderview->postpone_select_id);
2134 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2139 main_window_cursor_normal(folderview->mainwin);
2142 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2143 opened = summary_show(folderview->summaryview, item);
2145 folder_clean_cache_memory(item);
2148 gtkut_ctree_set_focus_row(ctree, old_opened);
2149 gtk_cmctree_select(ctree, old_opened);
2150 folderview->opened = old_opened;
2152 folderview->opened = row;
2153 if (gtk_cmctree_node_is_visible(ctree, row)
2154 != GTK_VISIBILITY_FULL)
2155 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2158 STATUSBAR_POP(folderview->mainwin);
2160 folderview->open_folder = FALSE;
2165 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2166 FolderView *folderview)
2170 item = gtk_cmctree_node_get_row_data(ctree, node);
2171 cm_return_if_fail(item != NULL);
2172 item->collapsed = FALSE;
2173 folderview_update_node(folderview, node);
2176 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2177 FolderView *folderview)
2181 item = gtk_cmctree_node_get_row_data(ctree, node);
2182 cm_return_if_fail(item != NULL);
2183 item->collapsed = TRUE;
2184 folderview_update_node(folderview, node);
2187 static void folderview_popup_close(GtkMenuShell *menu_shell,
2188 FolderView *folderview)
2190 if (!folderview->opened) return;
2192 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2195 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2196 FolderView *folderview)
2198 FolderColumnType type = folderview->col_state[column].type;
2200 prefs_common.folder_col_size[type] = width;
2203 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2205 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2206 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2207 GtkCMCTreeNode *node, *parent_node;
2208 gint *col_pos = folderview->col_pos;
2209 FolderItemUpdateData hookdata;
2211 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2212 if (parent_node == NULL)
2215 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2217 text[col_pos[F_COL_FOLDER]] = item->name;
2218 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2223 gtk_cmctree_expand(ctree, parent_node);
2224 gtk_cmctree_node_set_row_data(ctree, node, item);
2226 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2227 folderview_sort_folders(folderview, parent_node, item->folder);
2229 hookdata.item = item;
2230 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2231 hookdata.msg = NULL;
2232 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2234 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2237 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2239 FolderView *folderview = (FolderView *)data;
2240 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2242 GSList *mlist = NULL;
2244 FolderItem *special_trash = NULL;
2247 if (!folderview->selected) return;
2248 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2249 cm_return_if_fail(item != NULL);
2250 cm_return_if_fail(item->folder != NULL);
2252 if (NULL != (ac = account_find_from_item(item)))
2253 special_trash = account_get_special_folder(ac, F_TRASH);
2255 if (item != item->folder->trash && item != special_trash
2256 && !folder_has_parent_of_type(item, F_TRASH)) return;
2258 if (prefs_common.ask_on_clean) {
2259 if (alertpanel(_("Empty trash"),
2260 _("Delete all messages in trash?"),
2261 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2265 mlist = folder_item_get_msg_list(item);
2267 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2268 MsgInfo * msginfo = (MsgInfo *) cur->data;
2269 if (MSG_IS_LOCKED(msginfo->flags))
2271 /* is it partially received? (partial_recv isn't cached) */
2272 if (msginfo->total_size != 0 &&
2273 msginfo->size != (off_t)msginfo->total_size)
2274 partial_mark_for_delete(msginfo);
2276 procmsg_msg_list_free(mlist);
2278 folder_item_remove_all_msg(item);
2281 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2283 FolderView *folderview = (FolderView *)data;
2284 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2286 FolderItem *special_queue = NULL;
2288 gchar *errstr = NULL;
2290 if (!folderview->selected) return;
2291 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2292 cm_return_if_fail(item != NULL);
2293 cm_return_if_fail(item->folder != NULL);
2295 if (NULL != (ac = account_find_from_item(item)))
2296 special_queue = account_get_special_folder(ac, F_QUEUE);
2298 if (item != item->folder->queue && item != special_queue
2299 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2301 if (procmsg_queue_is_empty(item))
2304 if (prefs_common.work_offline)
2305 if (alertpanel(_("Offline warning"),
2306 _("You're working offline. Override?"),
2307 GTK_STOCK_NO, GTK_STOCK_YES,
2308 NULL) != G_ALERTALTERNATE)
2311 /* ask for confirmation before sending queued messages only
2312 in online mode and if there is at least one message queued
2313 in any of the folder queue
2315 if (prefs_common.confirm_send_queued_messages) {
2316 if (!prefs_common.work_offline) {
2317 if (alertpanel(_("Send queued messages"),
2318 _("Send all queued messages?"),
2319 GTK_STOCK_CANCEL, _("_Send"),
2320 NULL) != G_ALERTALTERNATE)
2325 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2327 alertpanel_error_log(_("Some errors occurred while "
2328 "sending queued messages."));
2330 alertpanel_error_log(_("Some errors occurred "
2331 "while sending queued messages:\n%s"), errstr);
2337 static void folderview_search_cb(GtkAction *action, gpointer data)
2339 FolderView *folderview = (FolderView *)data;
2340 summary_search(folderview->summaryview);
2343 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2345 FolderView *folderview = (FolderView *)data;
2346 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2349 if (!folderview->selected) return;
2351 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2352 cm_return_if_fail(item != NULL);
2353 cm_return_if_fail(item->folder != NULL);
2355 item->processing_pending = TRUE;
2356 folder_item_apply_processing(item);
2357 item->processing_pending = FALSE;
2360 static void folderview_property_cb(GtkAction *action, gpointer data)
2362 FolderView *folderview = (FolderView *)data;
2363 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2366 if (!folderview->selected) return;
2368 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2369 cm_return_if_fail(item != NULL);
2370 cm_return_if_fail(item->folder != NULL);
2372 prefs_folder_item_open(item);
2375 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2377 GSList *list = NULL;
2378 GSList *done = NULL;
2379 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2381 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2382 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2383 && list->data != node) {
2384 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2385 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2388 for (list = done; list != NULL; list = g_slist_next(list)) {
2389 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2395 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2396 FolderItem *to_folder, gboolean copy)
2398 FolderItem *new_folder = NULL;
2402 cm_return_if_fail(folderview != NULL);
2403 cm_return_if_fail(from_folder != NULL);
2404 cm_return_if_fail(to_folder != NULL);
2406 if (prefs_common.warn_dnd) {
2407 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2408 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2409 from_folder->name, to_folder->name);
2410 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2411 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2412 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2415 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2417 else if (status & G_ALERTDISABLE)
2418 prefs_common.warn_dnd = FALSE;
2421 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2422 from_folder->name, to_folder->name);
2423 STATUSBAR_PUSH(folderview->mainwin, buf);
2425 summary_clear_all(folderview->summaryview);
2426 folderview->opened = NULL;
2427 folderview->selected = NULL;
2428 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2430 main_window_cursor_wait(folderview->mainwin);
2432 statusbar_verbosity_set(FALSE);
2433 folder_item_update_freeze();
2434 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2435 statusbar_verbosity_set(FALSE);
2436 main_window_cursor_normal(folderview->mainwin);
2437 STATUSBAR_POP(folderview->mainwin);
2438 folder_item_update_thaw();
2439 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2441 folderview_sort_folders(folderview,
2442 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2443 NULL, to_folder), new_folder->folder);
2444 folderview_select(folderview, new_folder);
2446 statusbar_verbosity_set(FALSE);
2447 main_window_cursor_normal(folderview->mainwin);
2448 STATUSBAR_POP(folderview->mainwin);
2449 folder_item_update_thaw();
2451 case F_MOVE_FAILED_DEST_IS_PARENT:
2452 alertpanel_error(_("Source and destination are the same."));
2454 case F_MOVE_FAILED_DEST_IS_CHILD:
2455 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2456 _("Can't move a folder to one of its children."));
2458 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2459 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2462 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2467 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2470 static gint folderview_clist_compare(GtkCMCList *clist,
2471 gconstpointer ptr1, gconstpointer ptr2)
2473 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2474 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2476 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2478 return item1->order - item2->order;
2481 // if only one folder has an order it comes first
2482 if (item1->order > 0)
2486 if (item2->order > 0)
2492 return (item2->name != NULL);
2496 return g_utf8_collate(item1->name, item2->name);
2499 static void folderview_processing_cb(GtkAction *action, gpointer data)
2501 FolderView *folderview = (FolderView *)data;
2502 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2506 if (!folderview->selected) return;
2508 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2509 cm_return_if_fail(item != NULL);
2510 cm_return_if_fail(item->folder != NULL);
2512 id = folder_item_get_identifier(item);
2513 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2516 prefs_filtering_open(&item->prefs->processing, title,
2517 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2521 void folderview_set_target_folder_color(gint color_op)
2525 FolderView *folderview;
2527 for (list = folderview_list; list != NULL; list = list->next) {
2528 folderview = (FolderView *)list->data;
2529 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2531 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2532 folderview->color_op;
2538 static gchar *last_smallfont = NULL;
2539 static gchar *last_normalfont = NULL;
2540 static gchar *last_boldfont = NULL;
2541 static gboolean last_derive = 0;
2543 void folderview_reinit_fonts(FolderView *folderview)
2546 g_free(last_smallfont);
2547 last_smallfont = NULL;
2548 g_free(last_normalfont);
2549 last_normalfont = NULL;
2550 g_free(last_boldfont);
2551 last_boldfont = NULL;
2554 void folderview_reflect_prefs(void)
2556 gboolean update_font = FALSE;
2557 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2558 FolderItem *item = folderview_get_selected_item(folderview);
2559 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2560 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2561 gint height = gtk_adjustment_get_value(pos);
2563 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2564 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2565 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2566 last_derive != prefs_common.derive_from_normal_font)
2569 g_free(last_smallfont);
2570 last_smallfont = g_strdup(SMALL_FONT);
2571 g_free(last_normalfont);
2572 last_normalfont = g_strdup(NORMAL_FONT);
2573 g_free(last_boldfont);
2574 last_boldfont = g_strdup(BOLD_FONT);
2575 last_derive = prefs_common.derive_from_normal_font;
2578 normal_style = normal_color_style = bold_style =
2579 bold_color_style = bold_tgtfold_style = NULL;
2581 folderview_init(folderview);
2583 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2584 folderview_column_set_titles(folderview);
2585 folderview_set_all();
2587 g_signal_handlers_block_by_func
2588 (G_OBJECT(folderview->ctree),
2589 G_CALLBACK(folderview_selected), folderview);
2592 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2593 GTK_CMCTREE(folderview->ctree), NULL, item);
2595 folderview_select(folderview, item);
2596 folderview->open_folder = FALSE;
2597 folderview->selected = node;
2600 g_signal_handlers_unblock_by_func
2601 (G_OBJECT(folderview->ctree),
2602 G_CALLBACK(folderview_selected), folderview);
2604 pos = gtk_scrolled_window_get_vadjustment(
2605 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2606 gtk_adjustment_set_value(pos, height);
2607 gtk_adjustment_changed(pos);
2608 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2611 static void drag_state_stop(FolderView *folderview)
2613 if (folderview->drag_timer_id)
2614 g_source_remove(folderview->drag_timer_id);
2615 folderview->drag_timer_id = 0;
2616 folderview->drag_node = NULL;
2619 static gboolean folderview_defer_expand(FolderView *folderview)
2621 if (folderview->drag_node) {
2622 folderview_recollapse_nodes(folderview, folderview->drag_node);
2623 if (folderview->drag_item->collapsed) {
2624 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2625 folderview->nodes_to_recollapse = g_slist_append
2626 (folderview->nodes_to_recollapse, folderview->drag_node);
2629 folderview->drag_item = NULL;
2630 folderview->drag_timer_id = 0;
2634 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2636 /* the idea is that we call drag_state_start() whenever we want expansion to
2637 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2638 * we need to call drag_state_stop() */
2639 drag_state_stop(folderview);
2640 /* request expansion */
2641 if (0 != (folderview->drag_timer_id = g_timeout_add
2642 (prefs_common.hover_timeout,
2643 (GSourceFunc)folderview_defer_expand,
2645 folderview->drag_node = node;
2646 folderview->drag_item = item;
2649 #ifndef GENERIC_UMPC
2650 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2651 FolderView *folderview)
2653 GdkDragContext *context;
2655 cm_return_if_fail(folderview != NULL);
2656 if (folderview->selected == NULL) return;
2657 if (folderview->nodes_to_recollapse)
2658 g_slist_free(folderview->nodes_to_recollapse);
2659 folderview->nodes_to_recollapse = NULL;
2660 context = gtk_drag_begin(widget, folderview->target_list,
2661 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2662 gtk_drag_set_icon_default(context);
2665 static void folderview_drag_data_get(GtkWidget *widget,
2666 GdkDragContext *drag_context,
2667 GtkSelectionData *selection_data,
2670 FolderView *folderview)
2674 gchar *source = NULL;
2675 if (info == TARGET_DUMMY) {
2676 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2677 cur != NULL; cur = cur->next) {
2678 item = gtk_cmctree_node_get_row_data
2679 (GTK_CMCTREE(folderview->ctree),
2680 GTK_CMCTREE_NODE(cur->data));
2682 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2683 gtk_selection_data_set(selection_data,
2684 gtk_selection_data_get_target(selection_data), 8,
2685 source, strlen(source));
2691 g_warning("unknown info %d\n", info);
2695 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2697 FolderUpdateData *hookdata;
2698 FolderView *folderview;
2702 folderview = (FolderView *) userdata;
2703 cm_return_val_if_fail(hookdata != NULL, FALSE);
2704 cm_return_val_if_fail(folderview != NULL, FALSE);
2706 ctree = folderview->ctree;
2707 cm_return_val_if_fail(ctree != NULL, FALSE);
2709 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2710 folderview_create_folder_node(folderview, hookdata->item);
2711 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2712 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2713 NULL, folder_item_parent(hookdata->item));
2714 folderview_sort_folders(folderview, node, hookdata->folder);
2715 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2716 GtkCMCTreeNode *node;
2718 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2720 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2721 if (folderview->selected == node)
2722 folderview->selected = NULL;
2723 if (folderview->opened == node)
2724 folderview->opened = NULL;
2726 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2727 /* do nothing, it's done by the ADD and REMOVE) */
2728 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2729 folderview_set(folderview);
2734 static gboolean folderview_dnd_scroll_cb(gpointer data)
2736 FolderView *folderview = (FolderView *)data;
2737 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2738 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2739 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2740 gint max = (int)gtk_adjustment_get_upper(pos) -
2741 (int)gtk_adjustment_get_page_size(pos);
2743 if (folderview->scroll_value == 0) {
2744 folderview->scroll_timeout_id = 0;
2748 if (folderview->scroll_value > 0 && new_val > max) {
2750 } else if (folderview->scroll_value < 0 && new_val < 0) {
2753 gtk_adjustment_set_value(pos, new_val);
2758 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2759 GdkDragContext *context,
2763 FolderView *folderview)
2766 FolderItem *item = NULL, *src_item = NULL;
2767 GtkCMCTreeNode *node = NULL;
2768 gboolean acceptable = FALSE;
2769 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2770 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2771 int height = (int)gtk_adjustment_get_page_size(pos);
2772 int total_height = (int)gtk_adjustment_get_upper(pos);
2773 int vpos = (int)gtk_adjustment_get_value(pos);
2774 int offset = prefs_common.show_col_headers ? 24:0;
2777 if (gtk_cmclist_get_selection_info
2778 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2779 GtkWidget *srcwidget;
2781 if (y > height - (48 - offset) && height + vpos < total_height) {
2782 dist = -(height - (48 - offset) - y);
2783 folderview->scroll_value = 1.41f * (1+(dist / 6));
2784 } else if (y < 72 - (24 - offset) && y >= 0) {
2785 dist = 72 - (24 - offset) - y;
2786 folderview->scroll_value = -1.41f * (1+(dist / 6));
2788 folderview->scroll_value = 0;
2790 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2791 folderview->scroll_timeout_id =
2792 g_timeout_add(30, folderview_dnd_scroll_cb,
2796 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2797 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2798 src_item = folderview->summaryview->folder_item;
2800 srcwidget = gtk_drag_get_source_widget(context);
2801 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2802 /* comes from summaryview */
2803 /* we are copying messages, so only accept folder items that are not
2804 the source item, are no root items and can copy messages */
2805 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2806 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2807 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2809 } else if (srcwidget == folderview->ctree) {
2810 /* comes from folderview */
2811 /* we are moving folder items, only accept folders that are not
2812 the source items and can copy messages and create folder items */
2813 if (item && item->folder && src_item && src_item != item &&
2814 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2815 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2816 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2817 || item->folder == src_item->folder))
2820 /* comes from another app */
2821 /* we are adding messages, so only accept folder items that are
2822 no root items and can copy messages */
2823 if (item && item->folder && folder_item_parent(item) != NULL
2824 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2825 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2830 if (acceptable || (src_item && src_item == item))
2831 drag_state_start(folderview, node, item);
2834 g_signal_handlers_block_by_func
2836 G_CALLBACK(folderview_selected), folderview);
2837 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2838 g_signal_handlers_unblock_by_func
2840 G_CALLBACK(folderview_selected), folderview);
2841 gdk_drag_status(context,
2842 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2843 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2845 if (folderview->opened)
2846 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2847 gdk_drag_status(context, 0, time);
2853 static void folderview_drag_leave_cb(GtkWidget *widget,
2854 GdkDragContext *context,
2856 FolderView *folderview)
2858 drag_state_stop(folderview);
2859 folderview->scroll_value = 0;
2860 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2863 static void free_info (gpointer stuff, gpointer data)
2868 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2869 guint time, FolderItem *item)
2872 GSList *msglist = NULL;
2873 list = uri_list_extract_filenames(data);
2874 if (!(item && item->folder && folder_item_parent(item) != NULL
2875 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2877 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2878 debug_print("item doesn't fit\n");
2882 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2883 debug_print("list is empty\n");
2886 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2887 MsgFileInfo *info = NULL;
2889 if (file_is_email((gchar *)tmp->data)) {
2890 info = g_new0(MsgFileInfo, 1);
2891 info->msginfo = NULL;
2892 info->file = (gchar *)tmp->data;
2893 msglist = g_slist_prepend(msglist, info);
2894 debug_print("file is a mail\n");
2896 debug_print("file isn't a mail\n");
2900 msglist = g_slist_reverse(msglist);
2901 folder_item_add_msgs(item, msglist, FALSE);
2902 g_slist_foreach(msglist, free_info, NULL);
2903 g_slist_free(msglist);
2904 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2906 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2908 list_free_strings(list);
2912 static void folderview_drag_received_cb(GtkWidget *widget,
2913 GdkDragContext *drag_context,
2916 GtkSelectionData *data,
2919 FolderView *folderview)
2922 FolderItem *item = NULL, *src_item;
2923 GtkCMCTreeNode *node;
2924 int offset = prefs_common.show_col_headers ? 24:0;
2926 folderview->scroll_value = 0;
2928 if (info == TARGET_DUMMY) {
2929 drag_state_stop(folderview);
2930 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2931 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2932 /* comes from summaryview */
2933 if (gtk_cmclist_get_selection_info
2934 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2937 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2938 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2939 src_item = folderview->summaryview->folder_item;
2941 if (item->no_select) {
2942 alertpanel_error(_("The destination folder can only be used to "
2943 "store subfolders."));
2946 /* re-check (due to acceptable possibly set for folder moves */
2947 if (!(item && item->folder && item->path && !item->no_select &&
2948 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2951 if (item && src_item) {
2952 switch (gdk_drag_context_get_selected_action(drag_context)) {
2953 case GDK_ACTION_COPY:
2954 summary_copy_selected_to(folderview->summaryview, item);
2955 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2957 case GDK_ACTION_MOVE:
2958 case GDK_ACTION_DEFAULT:
2960 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2961 summary_copy_selected_to(folderview->summaryview, item);
2963 summary_move_selected_to(folderview->summaryview, item);
2964 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2967 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2969 /* comes from folderview */
2971 gboolean folder_is_normal = TRUE;
2972 gboolean copy = (GDK_ACTION_COPY ==
2973 gdk_drag_context_get_selected_action(drag_context));
2975 source = (char *)gtk_selection_data_get_data(data) + 17;
2976 if (gtk_cmclist_get_selection_info
2977 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
2979 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2982 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2983 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2984 src_item = folder_find_item_from_identifier(source);
2988 src_item->stype == F_NORMAL &&
2989 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
2990 !folder_has_parent_of_type(src_item, F_DRAFT) &&
2991 !folder_has_parent_of_type(src_item, F_QUEUE) &&
2992 !folder_has_parent_of_type(src_item, F_TRASH);
2993 if (!item || !src_item || !folder_is_normal) {
2994 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2998 folderview_move_folder(folderview, src_item, item, copy);
2999 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3001 folderview->nodes_to_recollapse = NULL;
3002 } else if (info == TARGET_MAIL_URI_LIST) {
3003 if (gtk_cmclist_get_selection_info
3004 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3007 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3009 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3010 debug_print("no node\n");
3013 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3015 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3016 debug_print("no item\n");
3019 folderview_finish_dnd(gtk_selection_data_get_data(data),
3020 drag_context, time, item);
3024 static void folderview_drag_end_cb(GtkWidget *widget,
3025 GdkDragContext *drag_context,
3026 FolderView *folderview)
3028 drag_state_stop(folderview);
3029 folderview->scroll_value = 0;
3030 g_slist_free(folderview->nodes_to_recollapse);
3031 folderview->nodes_to_recollapse = NULL;
3034 void folderview_register_popup(FolderViewPopup *fpopup)
3038 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3039 FolderView *folderview = folderviews->data;
3040 GtkActionGroup *factory;
3042 factory = create_action_group(folderview, fpopup);
3043 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3045 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3048 void folderview_unregister_popup(FolderViewPopup *fpopup)
3053 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3054 FolderView *folderview = folderviews->data;
3056 g_hash_table_remove(folderview->popups, fpopup->klass);
3058 g_hash_table_remove(folderview_popups, fpopup->klass);