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);
897 if (item->stype == F_TRASH || item->stype == F_DRAFT)
901 if(item->unread_msgs > 0)
905 if(item->new_msgs > 0)
909 if(item->marked_msgs > 0)
918 void folderview_select_next_with_flag(FolderView *folderview,
922 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
923 GtkCMCTreeNode *node = NULL;
924 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
925 gboolean last_open = prefs_common.always_show_msg;
929 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
932 prefs_common.summary_select_prio[0] = ACTION_NEW;
935 prefs_common.summary_select_prio[0] = ACTION_MARKED;
938 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
940 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
942 folderview_select_node(folderview, node);
946 if (!folderview->opened ||
947 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
951 /* search again from the first node */
952 node = folderview_find_next_with_flag(ctree, NULL, flag);
954 folderview_select_node(folderview, node);
957 prefs_common.summary_select_prio[0] = last_summary_select_prio;
958 prefs_common.always_show_msg = last_open;
961 FolderItem *folderview_get_selected_item(FolderView *folderview)
963 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
965 if (!folderview->selected) return NULL;
966 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
969 static void folderview_set_folders(FolderView *folderview)
972 list = folder_get_list();
974 for (; list != NULL; list = list->next) {
975 folderview_append_folder(folderview, FOLDER(list->data));
979 static gchar *get_scan_str(FolderItem *item)
982 return g_strdup_printf(_("Scanning folder %s%c%s..."),
983 item->folder->name, G_DIR_SEPARATOR,
986 return g_strdup_printf(_("Scanning folder %s..."),
989 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
993 for (list = folderview_list; list != NULL; list = list->next) {
994 FolderView *folderview = (FolderView *)list->data;
995 MainWindow *mainwin = folderview->mainwin;
996 gchar *str = get_scan_str(item);
998 STATUSBAR_PUSH(mainwin, str);
999 STATUSBAR_POP(mainwin);
1004 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1007 MainWindow *mainwin = mainwindow_get_mainwindow();
1008 FolderView *folderview = NULL;
1009 GtkAdjustment *pos = NULL;
1012 cm_return_if_fail(folder != NULL);
1014 if (!folder->klass->scan_tree) return;
1017 alertpanel_full(_("Rebuild folder tree"),
1018 _("Rebuilding the folder tree will remove "
1019 "local caches. Do you want to continue?"),
1020 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1021 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1022 != G_ALERTALTERNATE) {
1028 window = label_window_create(_("Rebuilding folder tree..."));
1030 window = label_window_create(_("Scanning folder tree..."));
1033 folderview = mainwin->folderview;
1036 pos = gtk_scrolled_window_get_vadjustment(
1037 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1038 height = gtk_adjustment_get_value(pos);
1041 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1042 folder_scan_tree(folder, rebuild);
1043 folder_set_ui_func(folder, NULL, NULL);
1045 folderview_set_all();
1048 pos = gtk_scrolled_window_get_vadjustment(
1049 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1050 gtk_adjustment_set_value(pos, height);
1051 gtk_adjustment_changed(pos);
1053 label_window_destroy(window);
1057 /** folderview_check_new()
1058 * Scan and update the folder and return the
1059 * count the number of new messages since last check.
1060 * \param folder the folder to check for new messages
1061 * \return the number of new messages since last check
1063 gint folderview_check_new(Folder *folder)
1067 FolderView *folderview;
1069 GtkCMCTreeNode *node;
1071 gint former_new_msgs = 0;
1072 gint former_new = 0, former_unread = 0, former_total;
1074 for (list = folderview_list; list != NULL; list = list->next) {
1075 folderview = (FolderView *)list->data;
1076 ctree = GTK_CMCTREE(folderview->ctree);
1077 folderview->scanning_folder = folder;
1079 main_window_lock(folderview->mainwin);
1081 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1082 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1084 item = gtk_cmctree_node_get_row_data(ctree, node);
1085 if (!item || !item->path || !item->folder) continue;
1086 if (item->no_select) continue;
1087 if (folder && folder != item->folder) continue;
1088 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1089 if (!item->prefs->newmailcheck) continue;
1090 if (item->processing_pending == TRUE) {
1091 debug_print("skipping %s, processing pending\n",
1092 item->path ? item->path : item->name);
1095 if (item->scanning != ITEM_NOT_SCANNING) {
1096 debug_print("skipping %s, scanning\n",
1097 item->path ? item->path : item->name);
1101 str = get_scan_str(item);
1103 STATUSBAR_PUSH(folderview->mainwin, str);
1107 folderview_scan_tree_func(item->folder, item, NULL);
1108 former_new = item->new_msgs;
1109 former_unread = item->unread_msgs;
1110 former_total = item->total_msgs;
1112 if (item->folder->klass->scan_required &&
1113 (item->folder->klass->scan_required(item->folder, item) ||
1114 item->folder->inbox == item ||
1115 item->opened == TRUE ||
1116 item->processing_pending == TRUE)) {
1117 if (folder_item_scan(item) < 0) {
1119 summaryview_unlock(folderview->summaryview, item);
1120 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1121 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1122 item->path ? item->path:item->name);
1123 STATUSBAR_POP(folderview->mainwin);
1125 } else if (!FOLDER_IS_LOCAL(folder)) {
1126 STATUSBAR_POP(folderview->mainwin);
1131 } else if (!item->folder->klass->scan_required) {
1132 if (folder_item_scan(item) < 0) {
1133 summaryview_unlock(folderview->summaryview, item);
1134 if (folder && !FOLDER_IS_LOCAL(folder)) {
1135 STATUSBAR_POP(folderview->mainwin);
1140 if (former_new != item->new_msgs ||
1141 former_unread != item->unread_msgs ||
1142 former_total != item->total_msgs)
1143 folderview_update_node(folderview, node);
1145 new_msgs += item->new_msgs;
1146 former_new_msgs += former_new;
1147 STATUSBAR_POP(folderview->mainwin);
1149 folderview->scanning_folder = NULL;
1150 main_window_unlock(folderview->mainwin);
1154 folder_write_list();
1155 /* Number of new messages since last check is the just the difference
1156 * between former_new_msgs and new_msgs. If new_msgs is less than
1157 * former_new_msgs, that would mean another session accessed the folder
1158 * and the result is not well defined.
1160 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1164 void folderview_check_new_all(void)
1168 FolderView *folderview;
1170 folderview = (FolderView *)folderview_list->data;
1173 main_window_lock(folderview->mainwin);
1174 window = label_window_create
1175 (_("Checking for new messages in all folders..."));
1177 list = folder_get_list();
1178 for (; list != NULL; list = list->next) {
1179 Folder *folder = list->data;
1181 folderview_check_new(folder);
1184 folder_write_list();
1185 folderview_set_all();
1187 label_window_destroy(window);
1188 main_window_unlock(folderview->mainwin);
1192 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1198 if (!item || !item->folder || !item->folder->node)
1201 node = item->folder->node;
1203 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1204 node = node->children;
1207 (item->new_msgs > 0 ||
1208 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1212 while (node != NULL) {
1213 if (node && node->data) {
1214 FolderItem *next_item = (FolderItem*) node->data;
1216 if (folderview_have_new_children_sub(folderview,
1225 static gboolean folderview_have_new_children(FolderView *folderview,
1228 return folderview_have_new_children_sub(folderview, item, FALSE);
1231 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1237 if (!item || !item->folder || !item->folder->node)
1240 node = item->folder->node;
1242 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1243 node = node->children;
1246 (item->unread_msgs > 0 ||
1247 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1251 while (node != NULL) {
1252 if (node && node->data) {
1253 FolderItem *next_item = (FolderItem*) node->data;
1255 if (folderview_have_unread_children_sub(folderview,
1265 static gboolean folderview_have_unread_children(FolderView *folderview,
1268 return folderview_have_unread_children_sub(folderview, item, FALSE);
1271 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1277 if (!item || !item->folder || !item->folder->node)
1280 node = item->folder->node;
1282 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1283 node = node->children;
1285 if (in_sub && item->search_match){
1289 while (node != NULL) {
1290 if (node && node->data) {
1291 FolderItem *next_item = (FolderItem*) node->data;
1293 if (folderview_have_matching_children_sub(folderview,
1303 static gboolean folderview_have_matching_children(FolderView *folderview,
1306 return folderview_have_matching_children_sub(folderview, item, FALSE);
1309 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1315 if (!item || !item->folder || !item->folder->node)
1318 node = item->folder->node;
1320 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1321 node = node->children;
1323 if (item->marked_msgs != 0) {
1327 while (node != NULL) {
1328 if (node && node->data) {
1329 FolderItem *next_item = (FolderItem*) node->data;
1331 if (folderview_have_marked_children_sub(folderview,
1340 static gboolean folderview_have_marked_children(FolderView *folderview,
1343 return folderview_have_marked_children_sub(folderview, item, FALSE);
1346 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1348 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1349 GtkStyle *style = NULL;
1350 GtkStyle *color_style = NULL;
1352 GdkPixbuf *xpm, *openxpm;
1353 static GdkPixbuf *searchicon;
1354 gboolean mark = FALSE;
1357 gboolean add_unread_mark;
1358 gboolean add_sub_match_mark;
1359 gboolean use_bold, use_color;
1360 gint *col_pos = folderview->col_pos;
1361 SpecialFolderItemType stype;
1363 item = gtk_cmctree_node_get_row_data(ctree, node);
1364 cm_return_if_fail(item != NULL);
1366 if (!GTK_CMCTREE_ROW(node)->expanded)
1367 mark = folderview_have_marked_children(folderview, item);
1369 mark = (item->marked_msgs != 0);
1371 stype = item->stype;
1372 if (stype == F_NORMAL) {
1373 if (folder_has_parent_of_type(item, F_TRASH))
1375 else if (folder_has_parent_of_type(item, F_DRAFT))
1377 else if (folder_has_parent_of_type(item, F_OUTBOX))
1379 else if (folder_has_parent_of_type(item, F_QUEUE))
1384 if (item->hide_read_msgs || item->hide_read_threads) {
1385 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1386 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1388 xpm = mark?m_inboxxpm:inboxxpm;
1389 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1393 if (item->hide_read_msgs || item->hide_read_threads) {
1394 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1395 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1397 xpm = mark?m_outboxxpm:outboxxpm;
1398 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1402 if (item->hide_read_msgs || item->hide_read_threads) {
1403 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1404 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1406 xpm = mark?m_queuexpm:queuexpm;
1407 openxpm = mark?m_queueopenxpm:queueopenxpm;
1411 if (item->hide_read_msgs || item->hide_read_threads) {
1412 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1413 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1415 xpm = mark?m_trashxpm:trashxpm;
1416 openxpm = mark?m_trashopenxpm:trashopenxpm;
1420 xpm = mark?m_draftsxpm:draftsxpm;
1421 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1424 if (item->hide_read_msgs || item->hide_read_threads) {
1425 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1426 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1428 xpm = mark?m_folderxpm:folderxpm;
1429 openxpm = mark?m_folderopenxpm:folderopenxpm;
1433 if (item->no_select) {
1434 xpm = openxpm = noselectxpm;
1437 name = folder_item_get_name(item);
1439 if (!GTK_CMCTREE_ROW(node)->expanded) {
1440 add_unread_mark = folderview_have_unread_children(
1442 add_sub_match_mark = folderview_have_matching_children(
1445 add_unread_mark = FALSE;
1446 add_sub_match_mark = FALSE;
1449 if (item->search_match) {
1451 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1454 xpm = openxpm = searchicon;
1458 if (prefs_common.display_folder_unread) {
1459 if (folder_has_parent_of_type(item, F_QUEUE)) {
1460 /* only total_msgs matters here */
1461 if (item->total_msgs > 0) {
1462 /* show total number (should be equal to the unread number)
1464 str = g_strdup_printf("%s (%d%s%s)",
1465 name, item->total_msgs,
1466 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1467 (item->unreadmarked_msgs > 0) ? "!" : "");
1470 if (prefs_common.display_folder_unread == 1) {
1471 if (item->unread_msgs > 0) {
1472 /* show unread number and signs */
1473 str = g_strdup_printf("%s (%d%s%s)",
1474 name, item->unread_msgs,
1475 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1476 (item->unreadmarked_msgs > 0) ? "!" : "");
1479 if (item->total_msgs > 0) {
1480 /* show unread number, total number and signs if any */
1481 str = g_strdup_printf("%s (%d/%d%s%s)",
1482 name, item->unread_msgs, item->total_msgs,
1483 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1484 (item->unreadmarked_msgs > 0) ? "!" : "");
1488 if ((str == NULL) &&
1489 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1490 /* no unread/total numbers, but at least one sign */
1491 str = g_strdup_printf("%s (%s%s)",
1493 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1494 (item->unreadmarked_msgs > 0) ? "!" : "");
1498 /* last fallback, folder name only or with +! sign */
1499 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1500 str = g_strdup_printf("%s%s",
1502 } else if (item->unreadmarked_msgs > 0) {
1503 str = g_strdup_printf("%s%s",
1505 } else if (add_sub_match_mark) {
1506 str = g_strdup_printf("%s%s",
1509 str = g_strdup_printf("%s", name);
1512 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1514 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1518 if (!folder_item_parent(item)) {
1519 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1520 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1521 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1523 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1524 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1525 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1528 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1529 folder_has_parent_of_type(item, F_TRASH)) {
1530 use_bold = use_color = FALSE;
1531 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1532 GSList *list = folder_item_get_msg_list(item);
1534 use_bold = use_color = FALSE;
1535 for (cur = list; cur; cur = cur->next) {
1536 MsgInfo *msginfo = (MsgInfo *)cur->data;
1537 if (!MSG_IS_DELETED(msginfo->flags)) {
1538 /* highlight queue folder if there are any messages */
1539 use_bold = use_color = TRUE;
1543 procmsg_msg_list_free(list);
1545 /* if unread messages exist, print with bold font */
1546 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1548 /* if new messages exist, print with colored letter */
1550 (item->new_msgs > 0) ||
1552 folderview_have_new_children(folderview, item));
1555 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1560 if (item->prefs->color > 0 && !use_color) {
1561 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1562 color_style = gtk_style_copy(bold_style);
1563 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1564 style = color_style;
1565 } else if (use_color) {
1566 style = bold_color_style;
1569 if (item->op_count > 0) {
1570 style = bold_tgtfold_style;
1572 } else if (use_color) {
1573 style = normal_color_style;
1574 gtk_cmctree_node_set_foreground(ctree, node,
1575 &folderview->color_new);
1576 } else if (item->op_count > 0) {
1577 style = bold_tgtfold_style;
1578 } else if (item->prefs->color > 0) {
1580 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1581 color_style = gtk_style_copy(normal_style);
1582 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1583 style = color_style;
1585 style = normal_style;
1588 gtk_cmctree_node_set_row_style(ctree, node, style);
1590 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1591 folderview_update_node(folderview, node);
1594 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1597 FolderView *folderview;
1599 GtkCMCTreeNode *node;
1601 cm_return_if_fail(item != NULL);
1603 for (list = folderview_list; list != NULL; list = list->next) {
1604 folderview = (FolderView *)list->data;
1605 ctree = GTK_CMCTREE(folderview->ctree);
1607 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1608 if (node && item->search_match != matches) {
1609 item->search_match = matches;
1610 folderview_update_node(folderview, node);
1615 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1617 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1618 FolderView *folderview = (FolderView *)data;
1620 GtkCMCTreeNode *node;
1621 cm_return_val_if_fail(update_info != NULL, TRUE);
1622 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1623 cm_return_val_if_fail(folderview != NULL, FALSE);
1625 ctree = GTK_CMCTREE(folderview->ctree);
1627 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1630 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1631 folderview_update_node(folderview, node);
1633 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1634 update_info->item == folderview->summaryview->folder_item &&
1635 update_info->item != NULL)
1636 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1637 summary_show(folderview->summaryview, update_info->item);
1643 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1644 GNode *gnode, GtkCMCTreeNode *cnode,
1647 FolderView *folderview = (FolderView *)data;
1648 FolderItem *item = FOLDER_ITEM(gnode->data);
1650 cm_return_val_if_fail(item != NULL, FALSE);
1652 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1653 folderview_update_node(folderview, cnode);
1658 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1661 FolderView *folderview = (FolderView *)data;
1664 if (GTK_CMCTREE_ROW(node)->children) {
1665 item = gtk_cmctree_node_get_row_data(ctree, node);
1666 cm_return_if_fail(item != NULL);
1668 if (!item->collapsed)
1669 gtk_cmctree_expand(ctree, node);
1671 folderview_update_node(folderview, node);
1675 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1676 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1679 GtkCMCTreeNode *node, *parent, *sibling;
1681 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1683 g_warning("%s not found.\n", item->path);
1685 parent = GTK_CMCTREE_ROW(node)->parent;
1686 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1687 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1689 sibling = GTK_CMCTREE_ROW(parent)->children;
1693 tmp = gtk_cmctree_node_get_row_data
1695 if (tmp && tmp->stype != F_NORMAL)
1696 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1700 if (node != sibling)
1701 gtk_cmctree_move(ctree, node, parent, sibling);
1708 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1711 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1712 GtkCMCTreeNode *prev = NULL;
1714 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1715 gtk_sctree_sort_recursive(ctree, root);
1716 if (root && GTK_CMCTREE_ROW(root)->parent) {
1717 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1720 set_special_folder(ctree, folder->inbox, root, &prev);
1721 set_special_folder(ctree, folder->outbox, root, &prev);
1722 set_special_folder(ctree, folder->draft, root, &prev);
1723 set_special_folder(ctree, folder->queue, root, &prev);
1724 set_special_folder(ctree, folder->trash, root, &prev);
1725 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1728 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1730 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1731 GtkCMCTreeNode *root;
1733 cm_return_if_fail(folder != NULL);
1735 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1736 folderview_gnode_func, folderview);
1737 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1739 folderview_sort_folders(folderview, root, folder);
1742 /* callback functions */
1743 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1744 GdkEventButton *event)
1748 FolderViewPopup *fpopup;
1749 GtkActionGroup *action_group;
1751 FolderItem *special_trash = NULL, *special_queue = NULL;
1753 GtkUIManager *ui_manager = gtk_ui_manager_new();
1755 if (folderview->ui_manager)
1756 g_object_unref(folderview->ui_manager);
1758 folderview->ui_manager = ui_manager;
1759 item = folderview_get_selected_item(folderview);
1761 cm_return_if_fail(item != NULL);
1762 cm_return_if_fail(item->folder != NULL);
1763 folder = item->folder;
1765 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1768 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1770 fpopup = g_hash_table_lookup(folderview_popups, "common");
1771 action_group = g_hash_table_lookup(folderview->popups, "common");
1774 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1775 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1776 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1778 if (fpopup->add_menuitems)
1779 fpopup->add_menuitems(ui_manager, item);
1781 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1782 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1783 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1784 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1785 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1786 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1788 if (fpopup->set_sensitivity != NULL)
1789 fpopup->set_sensitivity(ui_manager, item);
1791 if (NULL != (ac = account_find_from_item(item))) {
1792 special_trash = account_get_special_folder(ac, F_TRASH);
1793 special_queue = account_get_special_folder(ac, F_QUEUE);
1796 if ((item == folder->trash || item == special_trash
1797 || folder_has_parent_of_type(item, F_TRASH))) {
1798 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1799 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1802 if ((item == folder->queue || item == special_queue
1803 || folder_has_parent_of_type(item, F_QUEUE))) {
1804 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1805 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1808 #define SET_SENS(name, sens) \
1809 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1811 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1812 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1813 folderview->selected == folderview->opened);
1814 SET_SENS("FolderViewPopup/Properties", TRUE);
1816 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1817 item->total_msgs >= 1 && !item->processing_pending);
1818 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1819 !item->no_select && !item->processing_pending);
1821 if (item == folder->trash || item == special_trash
1822 || folder_has_parent_of_type(item, F_TRASH)) {
1823 GSList *msglist = folder_item_get_msg_list(item);
1824 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1825 procmsg_msg_list_free(msglist);
1827 if (item == folder->queue || item == special_queue
1828 || folder_has_parent_of_type(item, F_QUEUE)) {
1829 GSList *msglist = folder_item_get_msg_list(item);
1830 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1831 procmsg_msg_list_free(msglist);
1835 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1836 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1837 g_signal_connect(G_OBJECT(popup), "selection_done",
1838 G_CALLBACK(folderview_popup_close),
1840 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1841 event->button, event->time);
1844 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1845 FolderView *folderview)
1847 GtkCMCList *clist = GTK_CMCLIST(ctree);
1848 gint prev_row = -1, row = -1, column = -1;
1850 if (!event) return FALSE;
1852 if (event->button == 1 || event->button == 2) {
1853 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1854 folderview->open_folder = TRUE;
1856 if (event->type == GDK_2BUTTON_PRESS) {
1857 if (clist->selection) {
1858 GtkCMCTreeNode *node;
1860 node = GTK_CMCTREE_NODE(clist->selection->data);
1862 gtk_cmctree_toggle_expansion(
1865 folderview->open_folder = FALSE;
1872 if (event->button == 2 || event->button == 3) {
1874 if (clist->selection) {
1875 GtkCMCTreeNode *node;
1877 node = GTK_CMCTREE_NODE(clist->selection->data);
1879 prev_row = gtkut_ctree_get_nth_from_node
1880 (GTK_CMCTREE(ctree), node);
1883 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1886 if (prev_row != row) {
1887 gtk_cmclist_unselect_all(clist);
1888 if (event->button == 2)
1889 folderview_select_node
1891 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1894 gtk_cmclist_select_row(clist, row, column);
1898 if (event->button != 3) return FALSE;
1900 folderview_set_sens_and_popup_menu(folderview, row, event);
1904 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1905 FolderView *folderview)
1907 int row = -1, column = -1;
1909 if (!event) return FALSE;
1911 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1914 if (event->button == 1 && folderview->open_folder == FALSE &&
1915 folderview->opened != NULL) {
1916 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1917 folderview->opened);
1918 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1924 #define BREAK_ON_MODIFIER_KEY() \
1925 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1927 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1928 FolderView *folderview)
1930 if (!event) return FALSE;
1932 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1935 switch (event->keyval) {
1937 #ifndef GENERIC_UMPC
1938 case GDK_KEY_Return:
1939 case GDK_KEY_KP_Enter:
1941 if (folderview->selected) {
1942 folderview_select_node(folderview,
1943 folderview->selected);
1947 case GDK_KEY_Return:
1948 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
1949 gtk_cmctree_toggle_expansion(
1950 GTK_CMCTREE(folderview->ctree),
1951 folderview->selected);
1956 BREAK_ON_MODIFIER_KEY();
1957 if (folderview->selected) {
1958 if (folderview->opened == folderview->selected &&
1959 (!folderview->summaryview->folder_item ||
1960 folderview->summaryview->folder_item->total_msgs == 0))
1961 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
1963 folderview_select_node(folderview,
1964 folderview->selected);
1974 typedef struct _PostponedSelectData
1977 GtkCMCTreeNode *row;
1979 FolderView *folderview;
1980 } PostponedSelectData;
1982 static gboolean postpone_select(void *data)
1984 PostponedSelectData *psdata = (PostponedSelectData *)data;
1985 debug_print("trying again\n");
1987 psdata->folderview->postpone_select_id = 0;
1988 psdata->folderview->open_folder = TRUE;
1989 main_window_cursor_normal(psdata->folderview->mainwin);
1990 STATUSBAR_POP(psdata->folderview->mainwin);
1991 folderview_selected(psdata->ctree, psdata->row,
1992 psdata->column, psdata->folderview);
1997 void folderview_close_opened(FolderView *folderview)
1999 if (folderview->opened) {
2000 FolderItem *olditem;
2002 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2003 folderview->opened);
2005 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2006 olditem->path ? olditem->path:olditem->name);
2007 /* will be null if we just moved the previously opened folder */
2008 STATUSBAR_PUSH(folderview->mainwin, buf);
2009 main_window_cursor_wait(folderview->mainwin);
2011 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2012 summary_show(folderview->summaryview, NULL);
2013 folder_item_close(olditem);
2014 main_window_cursor_normal(folderview->mainwin);
2015 STATUSBAR_POP(folderview->mainwin);
2016 if (olditem->folder->klass->item_closed)
2017 olditem->folder->klass->item_closed(olditem);
2022 if (folderview->opened &&
2023 !GTK_CMCTREE_ROW(folderview->opened)->children)
2024 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2026 folderview->opened = NULL;
2028 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2029 gint column, FolderView *folderview)
2031 static gboolean can_select = TRUE; /* exclusive lock */
2036 GtkCMCTreeNode *old_opened = folderview->opened;
2038 folderview->selected = row;
2040 debug_print("newly selected %p, opened %p\n", folderview->selected,
2041 folderview->opened);
2042 if (folderview->opened == row) {
2043 folderview->open_folder = FALSE;
2048 item = gtk_cmctree_node_get_row_data(ctree, row);
2051 folderview->open_folder = FALSE;
2055 if (!can_select || summary_is_locked(folderview->summaryview)) {
2056 if (folderview->opened) {
2057 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2058 gtk_cmctree_select(ctree, folderview->opened);
2060 folderview->open_folder = FALSE;
2065 if (!folderview->open_folder) {
2072 /* Save cache for old folder */
2073 /* We don't want to lose all caches if sylpheed crashed */
2074 /* resets folderview->opened to NULL */
2075 folderview_close_opened(folderview);
2077 /* CLAWS: set compose button type: news folder items
2078 * always have a news folder as parent */
2080 toolbar_set_compose_button
2081 (folderview->mainwin->toolbar,
2082 FOLDER_TYPE(item->folder) == F_NEWS ?
2083 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2086 debug_print("Folder %s is selected\n", item->path);
2088 if (!GTK_CMCTREE_ROW(row)->children)
2089 gtk_cmctree_expand(ctree, row);
2091 /* ungrab the mouse event */
2092 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2093 gtk_grab_remove(GTK_WIDGET(ctree));
2094 if (gdk_pointer_is_grabbed())
2095 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2099 /* TODO: wwp: avoid displaying (null) in the status bar */
2100 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2101 item->path : "(null)");
2102 debug_print("%s\n", buf);
2103 STATUSBAR_PUSH(folderview->mainwin, buf);
2106 main_window_cursor_wait(folderview->mainwin);
2108 if (folderview->scanning_folder == item->folder) {
2111 res = folder_item_open(item);
2114 if (res == -1 && item->no_select == FALSE) {
2115 main_window_cursor_normal(folderview->mainwin);
2116 STATUSBAR_POP(folderview->mainwin);
2118 alertpanel_error(_("Folder could not be opened."));
2120 folderview->open_folder = FALSE;
2124 } else if (res == -2 && item->no_select == FALSE) {
2125 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2126 data->ctree = ctree;
2128 data->column = column;
2129 data->folderview = folderview;
2130 debug_print("postponing open of %s till end of scan\n",
2131 item->path ? item->path:item->name);
2132 folderview->open_folder = FALSE;
2134 if (folderview->postpone_select_id != 0)
2135 g_source_remove(folderview->postpone_select_id);
2136 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2141 main_window_cursor_normal(folderview->mainwin);
2144 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2145 opened = summary_show(folderview->summaryview, item);
2147 folder_clean_cache_memory(item);
2150 gtkut_ctree_set_focus_row(ctree, old_opened);
2151 gtk_cmctree_select(ctree, old_opened);
2152 folderview->opened = old_opened;
2154 folderview->opened = row;
2155 if (gtk_cmctree_node_is_visible(ctree, row)
2156 != GTK_VISIBILITY_FULL)
2157 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2160 STATUSBAR_POP(folderview->mainwin);
2162 folderview->open_folder = FALSE;
2167 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2168 FolderView *folderview)
2172 item = gtk_cmctree_node_get_row_data(ctree, node);
2173 cm_return_if_fail(item != NULL);
2174 item->collapsed = FALSE;
2175 folderview_update_node(folderview, node);
2178 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2179 FolderView *folderview)
2183 item = gtk_cmctree_node_get_row_data(ctree, node);
2184 cm_return_if_fail(item != NULL);
2185 item->collapsed = TRUE;
2186 folderview_update_node(folderview, node);
2189 static void folderview_popup_close(GtkMenuShell *menu_shell,
2190 FolderView *folderview)
2192 if (!folderview->opened) return;
2194 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2197 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2198 FolderView *folderview)
2200 FolderColumnType type = folderview->col_state[column].type;
2202 prefs_common.folder_col_size[type] = width;
2205 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2207 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2208 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2209 GtkCMCTreeNode *node, *parent_node;
2210 gint *col_pos = folderview->col_pos;
2211 FolderItemUpdateData hookdata;
2213 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2214 if (parent_node == NULL)
2217 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2219 text[col_pos[F_COL_FOLDER]] = item->name;
2220 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2225 gtk_cmctree_expand(ctree, parent_node);
2226 gtk_cmctree_node_set_row_data(ctree, node, item);
2228 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2229 folderview_sort_folders(folderview, parent_node, item->folder);
2231 hookdata.item = item;
2232 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2233 hookdata.msg = NULL;
2234 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2236 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2239 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2241 FolderView *folderview = (FolderView *)data;
2242 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2244 GSList *mlist = NULL;
2246 FolderItem *special_trash = NULL;
2249 if (!folderview->selected) return;
2250 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2251 cm_return_if_fail(item != NULL);
2252 cm_return_if_fail(item->folder != NULL);
2254 if (NULL != (ac = account_find_from_item(item)))
2255 special_trash = account_get_special_folder(ac, F_TRASH);
2257 if (item != item->folder->trash && item != special_trash
2258 && !folder_has_parent_of_type(item, F_TRASH)) return;
2260 if (prefs_common.ask_on_clean) {
2261 if (alertpanel(_("Empty trash"),
2262 _("Delete all messages in trash?"),
2263 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2267 mlist = folder_item_get_msg_list(item);
2269 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2270 MsgInfo * msginfo = (MsgInfo *) cur->data;
2271 if (MSG_IS_LOCKED(msginfo->flags))
2273 /* is it partially received? (partial_recv isn't cached) */
2274 if (msginfo->total_size != 0 &&
2275 msginfo->size != (off_t)msginfo->total_size)
2276 partial_mark_for_delete(msginfo);
2278 procmsg_msg_list_free(mlist);
2280 folder_item_remove_all_msg(item);
2283 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2285 FolderView *folderview = (FolderView *)data;
2286 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2288 FolderItem *special_queue = NULL;
2290 gchar *errstr = NULL;
2292 if (!folderview->selected) return;
2293 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2294 cm_return_if_fail(item != NULL);
2295 cm_return_if_fail(item->folder != NULL);
2297 if (NULL != (ac = account_find_from_item(item)))
2298 special_queue = account_get_special_folder(ac, F_QUEUE);
2300 if (item != item->folder->queue && item != special_queue
2301 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2303 if (procmsg_queue_is_empty(item))
2306 if (prefs_common.work_offline)
2307 if (alertpanel(_("Offline warning"),
2308 _("You're working offline. Override?"),
2309 GTK_STOCK_NO, GTK_STOCK_YES,
2310 NULL) != G_ALERTALTERNATE)
2313 /* ask for confirmation before sending queued messages only
2314 in online mode and if there is at least one message queued
2315 in any of the folder queue
2317 if (prefs_common.confirm_send_queued_messages) {
2318 if (!prefs_common.work_offline) {
2319 if (alertpanel(_("Send queued messages"),
2320 _("Send all queued messages?"),
2321 GTK_STOCK_CANCEL, _("_Send"),
2322 NULL) != G_ALERTALTERNATE)
2327 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2329 alertpanel_error_log(_("Some errors occurred while "
2330 "sending queued messages."));
2332 alertpanel_error_log(_("Some errors occurred "
2333 "while sending queued messages:\n%s"), errstr);
2339 static void folderview_search_cb(GtkAction *action, gpointer data)
2341 FolderView *folderview = (FolderView *)data;
2342 summary_search(folderview->summaryview);
2345 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2347 FolderView *folderview = (FolderView *)data;
2348 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2351 if (!folderview->selected) return;
2353 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2354 cm_return_if_fail(item != NULL);
2355 cm_return_if_fail(item->folder != NULL);
2357 item->processing_pending = TRUE;
2358 folder_item_apply_processing(item);
2359 item->processing_pending = FALSE;
2362 static void folderview_property_cb(GtkAction *action, gpointer data)
2364 FolderView *folderview = (FolderView *)data;
2365 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2368 if (!folderview->selected) return;
2370 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2371 cm_return_if_fail(item != NULL);
2372 cm_return_if_fail(item->folder != NULL);
2374 prefs_folder_item_open(item);
2377 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2379 GSList *list = NULL;
2380 GSList *done = NULL;
2381 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2383 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2384 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2385 && list->data != node) {
2386 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2387 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2390 for (list = done; list != NULL; list = g_slist_next(list)) {
2391 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2397 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2398 FolderItem *to_folder, gboolean copy)
2400 FolderItem *new_folder = NULL;
2404 cm_return_if_fail(folderview != NULL);
2405 cm_return_if_fail(from_folder != NULL);
2406 cm_return_if_fail(to_folder != NULL);
2408 if (prefs_common.warn_dnd) {
2409 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2410 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2411 from_folder->name, to_folder->name);
2412 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2413 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2414 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2417 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2419 else if (status & G_ALERTDISABLE)
2420 prefs_common.warn_dnd = FALSE;
2423 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2424 from_folder->name, to_folder->name);
2425 STATUSBAR_PUSH(folderview->mainwin, buf);
2427 summary_clear_all(folderview->summaryview);
2428 folderview->opened = NULL;
2429 folderview->selected = NULL;
2430 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2432 main_window_cursor_wait(folderview->mainwin);
2434 statusbar_verbosity_set(FALSE);
2435 folder_item_update_freeze();
2436 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2437 statusbar_verbosity_set(FALSE);
2438 main_window_cursor_normal(folderview->mainwin);
2439 STATUSBAR_POP(folderview->mainwin);
2440 folder_item_update_thaw();
2441 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2443 folderview_sort_folders(folderview,
2444 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2445 NULL, to_folder), new_folder->folder);
2446 folderview_select(folderview, new_folder);
2448 statusbar_verbosity_set(FALSE);
2449 main_window_cursor_normal(folderview->mainwin);
2450 STATUSBAR_POP(folderview->mainwin);
2451 folder_item_update_thaw();
2453 case F_MOVE_FAILED_DEST_IS_PARENT:
2454 alertpanel_error(_("Source and destination are the same."));
2456 case F_MOVE_FAILED_DEST_IS_CHILD:
2457 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2458 _("Can't move a folder to one of its children."));
2460 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2461 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2464 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2469 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2472 static gint folderview_clist_compare(GtkCMCList *clist,
2473 gconstpointer ptr1, gconstpointer ptr2)
2475 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2476 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2478 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2480 return item1->order - item2->order;
2483 // if only one folder has an order it comes first
2484 if (item1->order > 0)
2488 if (item2->order > 0)
2494 return (item2->name != NULL);
2498 return g_utf8_collate(item1->name, item2->name);
2501 static void folderview_processing_cb(GtkAction *action, gpointer data)
2503 FolderView *folderview = (FolderView *)data;
2504 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2508 if (!folderview->selected) return;
2510 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2511 cm_return_if_fail(item != NULL);
2512 cm_return_if_fail(item->folder != NULL);
2514 id = folder_item_get_identifier(item);
2515 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2518 prefs_filtering_open(&item->prefs->processing, title,
2519 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2523 void folderview_set_target_folder_color(gint color_op)
2527 FolderView *folderview;
2529 for (list = folderview_list; list != NULL; list = list->next) {
2530 folderview = (FolderView *)list->data;
2531 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2533 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2534 folderview->color_op;
2540 static gchar *last_smallfont = NULL;
2541 static gchar *last_normalfont = NULL;
2542 static gchar *last_boldfont = NULL;
2543 static gboolean last_derive = 0;
2545 void folderview_reinit_fonts(FolderView *folderview)
2548 g_free(last_smallfont);
2549 last_smallfont = NULL;
2550 g_free(last_normalfont);
2551 last_normalfont = NULL;
2552 g_free(last_boldfont);
2553 last_boldfont = NULL;
2556 void folderview_reflect_prefs(void)
2558 gboolean update_font = FALSE;
2559 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2560 FolderItem *item = folderview_get_selected_item(folderview);
2561 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2562 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2563 gint height = gtk_adjustment_get_value(pos);
2565 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2566 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2567 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2568 last_derive != prefs_common.derive_from_normal_font)
2571 g_free(last_smallfont);
2572 last_smallfont = g_strdup(SMALL_FONT);
2573 g_free(last_normalfont);
2574 last_normalfont = g_strdup(NORMAL_FONT);
2575 g_free(last_boldfont);
2576 last_boldfont = g_strdup(BOLD_FONT);
2577 last_derive = prefs_common.derive_from_normal_font;
2580 normal_style = normal_color_style = bold_style =
2581 bold_color_style = bold_tgtfold_style = NULL;
2583 folderview_init(folderview);
2585 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2586 folderview_column_set_titles(folderview);
2587 folderview_set_all();
2589 g_signal_handlers_block_by_func
2590 (G_OBJECT(folderview->ctree),
2591 G_CALLBACK(folderview_selected), folderview);
2594 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2595 GTK_CMCTREE(folderview->ctree), NULL, item);
2597 folderview_select(folderview, item);
2598 folderview->open_folder = FALSE;
2599 folderview->selected = node;
2602 g_signal_handlers_unblock_by_func
2603 (G_OBJECT(folderview->ctree),
2604 G_CALLBACK(folderview_selected), folderview);
2606 pos = gtk_scrolled_window_get_vadjustment(
2607 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2608 gtk_adjustment_set_value(pos, height);
2609 gtk_adjustment_changed(pos);
2610 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2613 static void drag_state_stop(FolderView *folderview)
2615 if (folderview->drag_timer_id)
2616 g_source_remove(folderview->drag_timer_id);
2617 folderview->drag_timer_id = 0;
2618 folderview->drag_node = NULL;
2621 static gboolean folderview_defer_expand(FolderView *folderview)
2623 if (folderview->drag_node) {
2624 folderview_recollapse_nodes(folderview, folderview->drag_node);
2625 if (folderview->drag_item->collapsed) {
2626 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2627 folderview->nodes_to_recollapse = g_slist_append
2628 (folderview->nodes_to_recollapse, folderview->drag_node);
2631 folderview->drag_item = NULL;
2632 folderview->drag_timer_id = 0;
2636 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2638 /* the idea is that we call drag_state_start() whenever we want expansion to
2639 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2640 * we need to call drag_state_stop() */
2641 drag_state_stop(folderview);
2642 /* request expansion */
2643 if (0 != (folderview->drag_timer_id = g_timeout_add
2644 (prefs_common.hover_timeout,
2645 (GSourceFunc)folderview_defer_expand,
2647 folderview->drag_node = node;
2648 folderview->drag_item = item;
2651 #ifndef GENERIC_UMPC
2652 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2653 FolderView *folderview)
2655 GdkDragContext *context;
2657 cm_return_if_fail(folderview != NULL);
2658 if (folderview->selected == NULL) return;
2659 if (folderview->nodes_to_recollapse)
2660 g_slist_free(folderview->nodes_to_recollapse);
2661 folderview->nodes_to_recollapse = NULL;
2662 context = gtk_drag_begin(widget, folderview->target_list,
2663 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2664 gtk_drag_set_icon_default(context);
2667 static void folderview_drag_data_get(GtkWidget *widget,
2668 GdkDragContext *drag_context,
2669 GtkSelectionData *selection_data,
2672 FolderView *folderview)
2676 gchar *source = NULL;
2677 if (info == TARGET_DUMMY) {
2678 sel = GTK_CMCLIST(folderview->ctree)->selection;
2682 item = gtk_cmctree_node_get_row_data
2683 (GTK_CMCTREE(folderview->ctree),
2684 GTK_CMCTREE_NODE(sel->data));
2686 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2687 gtk_selection_data_set(selection_data,
2688 gtk_selection_data_get_target(selection_data), 8,
2689 source, strlen(source));
2692 g_warning("unknown info %d\n", info);
2696 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2698 FolderUpdateData *hookdata;
2699 FolderView *folderview;
2703 folderview = (FolderView *) userdata;
2704 cm_return_val_if_fail(hookdata != NULL, FALSE);
2705 cm_return_val_if_fail(folderview != NULL, FALSE);
2707 ctree = folderview->ctree;
2708 cm_return_val_if_fail(ctree != NULL, FALSE);
2710 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2711 folderview_create_folder_node(folderview, hookdata->item);
2712 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2713 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2714 NULL, folder_item_parent(hookdata->item));
2715 folderview_sort_folders(folderview, node, hookdata->folder);
2716 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2717 GtkCMCTreeNode *node;
2719 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2721 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2722 if (folderview->selected == node)
2723 folderview->selected = NULL;
2724 if (folderview->opened == node)
2725 folderview->opened = NULL;
2727 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2728 /* do nothing, it's done by the ADD and REMOVE) */
2729 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2730 folderview_set(folderview);
2735 static gboolean folderview_dnd_scroll_cb(gpointer data)
2737 FolderView *folderview = (FolderView *)data;
2738 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2739 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2740 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2741 gint max = (int)gtk_adjustment_get_upper(pos) -
2742 (int)gtk_adjustment_get_page_size(pos);
2744 if (folderview->scroll_value == 0) {
2745 folderview->scroll_timeout_id = 0;
2749 if (folderview->scroll_value > 0 && new_val > max) {
2751 } else if (folderview->scroll_value < 0 && new_val < 0) {
2754 gtk_adjustment_set_value(pos, new_val);
2759 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2760 GdkDragContext *context,
2764 FolderView *folderview)
2767 FolderItem *item = NULL, *src_item = NULL;
2768 GtkCMCTreeNode *node = NULL;
2769 gboolean acceptable = FALSE;
2770 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2771 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2772 int height = (int)gtk_adjustment_get_page_size(pos);
2773 int total_height = (int)gtk_adjustment_get_upper(pos);
2774 int vpos = (int)gtk_adjustment_get_value(pos);
2775 int offset = prefs_common.show_col_headers ? 24:0;
2778 if (gtk_cmclist_get_selection_info
2779 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2780 GtkWidget *srcwidget;
2782 if (y > height - (48 - offset) && height + vpos < total_height) {
2783 dist = -(height - (48 - offset) - y);
2784 folderview->scroll_value = 1.41f * (1+(dist / 6));
2785 } else if (y < 72 - (24 - offset) && y >= 0) {
2786 dist = 72 - (24 - offset) - y;
2787 folderview->scroll_value = -1.41f * (1+(dist / 6));
2789 folderview->scroll_value = 0;
2791 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2792 folderview->scroll_timeout_id =
2793 g_timeout_add(30, folderview_dnd_scroll_cb,
2797 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2798 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2799 src_item = folderview->summaryview->folder_item;
2801 srcwidget = gtk_drag_get_source_widget(context);
2802 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2803 /* comes from summaryview */
2804 /* we are copying messages, so only accept folder items that are not
2805 the source item, are no root items and can copy messages */
2806 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2807 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2808 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2810 } else if (srcwidget == folderview->ctree) {
2811 /* comes from folderview */
2812 /* we are moving folder items, only accept folders that are not
2813 the source items and can copy messages and create folder items */
2814 if (item && item->folder && src_item && src_item != item &&
2815 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2816 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2817 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2818 || item->folder == src_item->folder))
2821 /* comes from another app */
2822 /* we are adding messages, so only accept folder items that are
2823 no root items and can copy messages */
2824 if (item && item->folder && folder_item_parent(item) != NULL
2825 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2826 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2831 if (acceptable || (src_item && src_item == item))
2832 drag_state_start(folderview, node, item);
2835 g_signal_handlers_block_by_func
2837 G_CALLBACK(folderview_selected), folderview);
2838 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2839 g_signal_handlers_unblock_by_func
2841 G_CALLBACK(folderview_selected), folderview);
2842 gdk_drag_status(context,
2843 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2844 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2846 if (folderview->opened)
2847 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2848 gdk_drag_status(context, 0, time);
2854 static void folderview_drag_leave_cb(GtkWidget *widget,
2855 GdkDragContext *context,
2857 FolderView *folderview)
2859 drag_state_stop(folderview);
2860 folderview->scroll_value = 0;
2861 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2864 static void free_info (gpointer stuff, gpointer data)
2869 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2870 guint time, FolderItem *item)
2873 GSList *msglist = NULL;
2874 list = uri_list_extract_filenames(data);
2875 if (!(item && item->folder && folder_item_parent(item) != NULL
2876 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2878 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2879 debug_print("item doesn't fit\n");
2883 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2884 debug_print("list is empty\n");
2887 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2888 MsgFileInfo *info = NULL;
2890 if (file_is_email((gchar *)tmp->data)) {
2891 info = g_new0(MsgFileInfo, 1);
2892 info->msginfo = NULL;
2893 info->file = (gchar *)tmp->data;
2894 msglist = g_slist_prepend(msglist, info);
2895 debug_print("file is a mail\n");
2897 debug_print("file isn't a mail\n");
2901 msglist = g_slist_reverse(msglist);
2902 folder_item_add_msgs(item, msglist, FALSE);
2903 g_slist_foreach(msglist, free_info, NULL);
2904 g_slist_free(msglist);
2905 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2907 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2909 list_free_strings(list);
2913 static void folderview_drag_received_cb(GtkWidget *widget,
2914 GdkDragContext *drag_context,
2917 GtkSelectionData *data,
2920 FolderView *folderview)
2923 FolderItem *item = NULL, *src_item;
2924 GtkCMCTreeNode *node;
2925 int offset = prefs_common.show_col_headers ? 24:0;
2927 folderview->scroll_value = 0;
2929 if (info == TARGET_DUMMY) {
2930 drag_state_stop(folderview);
2931 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2932 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2933 /* comes from summaryview */
2934 if (gtk_cmclist_get_selection_info
2935 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2938 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2939 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2940 src_item = folderview->summaryview->folder_item;
2942 if (item->no_select) {
2943 alertpanel_error(_("The destination folder can only be used to "
2944 "store subfolders."));
2947 /* re-check (due to acceptable possibly set for folder moves */
2948 if (!(item && item->folder && item->path && !item->no_select &&
2949 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2952 if (item && src_item) {
2953 switch (gdk_drag_context_get_selected_action(drag_context)) {
2954 case GDK_ACTION_COPY:
2955 summary_copy_selected_to(folderview->summaryview, item);
2956 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2958 case GDK_ACTION_MOVE:
2959 case GDK_ACTION_DEFAULT:
2961 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2962 summary_copy_selected_to(folderview->summaryview, item);
2964 summary_move_selected_to(folderview->summaryview, item);
2965 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2968 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2970 /* comes from folderview */
2972 gboolean folder_is_normal = TRUE;
2973 gboolean copy = (GDK_ACTION_COPY ==
2974 gdk_drag_context_get_selected_action(drag_context));
2976 source = (char *)gtk_selection_data_get_data(data) + 17;
2977 if (gtk_cmclist_get_selection_info
2978 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
2980 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2983 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2984 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2985 src_item = folder_find_item_from_identifier(source);
2989 src_item->stype == F_NORMAL &&
2990 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
2991 !folder_has_parent_of_type(src_item, F_DRAFT) &&
2992 !folder_has_parent_of_type(src_item, F_QUEUE) &&
2993 !folder_has_parent_of_type(src_item, F_TRASH);
2994 if (!item || !src_item || !folder_is_normal) {
2995 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2999 folderview_move_folder(folderview, src_item, item, copy);
3000 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3002 folderview->nodes_to_recollapse = NULL;
3003 } else if (info == TARGET_MAIL_URI_LIST) {
3004 if (gtk_cmclist_get_selection_info
3005 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3008 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3010 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3011 debug_print("no node\n");
3014 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3016 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3017 debug_print("no item\n");
3020 folderview_finish_dnd(gtk_selection_data_get_data(data),
3021 drag_context, time, item);
3025 static void folderview_drag_end_cb(GtkWidget *widget,
3026 GdkDragContext *drag_context,
3027 FolderView *folderview)
3029 drag_state_stop(folderview);
3030 folderview->scroll_value = 0;
3031 g_slist_free(folderview->nodes_to_recollapse);
3032 folderview->nodes_to_recollapse = NULL;
3035 void folderview_register_popup(FolderViewPopup *fpopup)
3039 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3040 FolderView *folderview = folderviews->data;
3041 GtkActionGroup *factory;
3043 factory = create_action_group(folderview, fpopup);
3044 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3046 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3049 void folderview_unregister_popup(FolderViewPopup *fpopup)
3054 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3055 FolderView *folderview = folderviews->data;
3057 g_hash_table_remove(folderview->popups, fpopup->klass);
3059 g_hash_table_remove(folderview_popups, fpopup->klass);