2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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),
474 g_signal_connect(G_OBJECT(ctree), "popup-menu",
475 G_CALLBACK(folderview_popup_menu), folderview);
477 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
478 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
479 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
480 G_CALLBACK(folderview_popup_menu), folderview);
482 g_signal_connect(G_OBJECT(ctree), "button_release_event",
483 G_CALLBACK(folderview_button_released),
485 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
486 G_CALLBACK(folderview_selected), folderview);
488 /* drag-n-dropping folders on maemo is impractical as this
489 * opens the folder almost everytime */
490 g_signal_connect(G_OBJECT(ctree), "start_drag",
491 G_CALLBACK(folderview_start_drag), folderview);
493 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
494 G_CALLBACK(folderview_drag_data_get),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
498 G_CALLBACK(folderview_tree_expanded),
500 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
501 G_CALLBACK(folderview_tree_collapsed),
504 g_signal_connect(G_OBJECT(ctree), "resize_column",
505 G_CALLBACK(folderview_col_resized),
509 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
510 folderview_drag_types, 2,
511 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
512 g_signal_connect(G_OBJECT(ctree), "drag_motion",
513 G_CALLBACK(folderview_drag_motion_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_leave",
516 G_CALLBACK(folderview_drag_leave_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
519 G_CALLBACK(folderview_drag_received_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_end",
522 G_CALLBACK(folderview_drag_end_cb),
525 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
530 void folderview_set_column_order(FolderView *folderview)
532 GtkWidget *ctree = folderview->ctree;
533 FolderItem *item = folderview_get_selected_item(folderview);
534 FolderItem *sel_item = NULL, *op_item = NULL;
535 GtkWidget *scrolledwin = folderview->scrolledwin;
537 if (folderview->selected)
538 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
539 if (folderview->opened)
540 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
542 debug_print("recreating tree...\n");
543 gtk_widget_destroy(folderview->ctree);
546 folderview->ctree = ctree = folderview_ctree_create(folderview);
547 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
548 GTK_CMCLIST(ctree)->hadjustment);
549 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
550 GTK_CMCLIST(ctree)->vadjustment);
551 gtk_widget_show(ctree);
554 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
556 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
558 folderview_set(folderview);
559 folderview_column_set_titles(folderview);
561 folderview_select(folderview,item);
564 FolderView *folderview_create(void)
566 FolderView *folderview;
567 GtkWidget *scrolledwin;
570 debug_print("Creating folder view...\n");
571 folderview = g_new0(FolderView, 1);
573 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
574 gtk_scrolled_window_set_policy
575 (GTK_SCROLLED_WINDOW(scrolledwin),
576 GTK_POLICY_AUTOMATIC,
577 prefs_common.folderview_vscrollbar_policy);
578 gtk_widget_set_size_request(scrolledwin,
579 prefs_common.folderview_width,
580 prefs_common.folderview_height);
582 folderview->scrolledwin = scrolledwin;
583 ctree = folderview_ctree_create(folderview);
585 /* create popup factories */
586 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
587 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
589 folderview->ctree = ctree;
591 folderview->folder_update_callback_id =
592 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
593 folderview->folder_item_update_callback_id =
594 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
596 gtk_widget_show_all(scrolledwin);
598 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
599 folderview_list = g_list_append(folderview_list, folderview);
600 folderview->deferred_refresh_id = -1;
601 folderview->scroll_timeout_id = -1;
605 void folderview_init(FolderView *folderview)
607 GtkWidget *ctree = folderview->ctree;
610 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
611 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
612 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
613 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
614 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
615 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
616 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
617 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
618 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
619 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
620 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
621 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
622 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
623 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
658 PangoFontDescription *font_desc;
659 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
660 font_desc = pango_font_description_from_string(NORMAL_FONT);
662 if (normal_style->font_desc)
663 pango_font_description_free
664 (normal_style->font_desc);
665 normal_style->font_desc = font_desc;
667 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
668 normal_color_style = gtk_style_copy(normal_style);
669 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
671 gtk_widget_set_style(ctree, normal_style);
675 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
676 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
677 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
678 pango_font_description_set_weight
679 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
681 PangoFontDescription *font_desc;
682 font_desc = pango_font_description_from_string(BOLD_FONT);
684 if (bold_style->font_desc)
685 pango_font_description_free
686 (bold_style->font_desc);
687 bold_style->font_desc = font_desc;
690 bold_color_style = gtk_style_copy(bold_style);
691 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
693 bold_tgtfold_style = gtk_style_copy(bold_style);
694 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
698 static gboolean folderview_defer_set(gpointer data)
700 FolderView *folderview = (FolderView *)data;
701 MainWindow *mainwin = folderview->mainwin;
705 if (mainwin->lock_count)
708 debug_print("doing deferred folderview_set now\n");
709 folderview_set(folderview);
711 folderview->deferred_refresh_id = -1;
715 void folderview_set(FolderView *folderview)
717 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
718 MainWindow *mainwin = folderview->mainwin;
719 FolderItem *sel_item = NULL, *op_item = NULL;
724 if (mainwin->lock_count) {
725 if (folderview->deferred_refresh_id == -1)
726 folderview->deferred_refresh_id =
727 g_timeout_add(500, folderview_defer_set, folderview);
728 debug_print("deferred folderview_set\n");
733 debug_print("Setting folder info...\n");
734 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
736 main_window_cursor_wait(mainwin);
738 if (folderview->selected)
739 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
740 if (folderview->opened)
741 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
743 folderview->selected = NULL;
744 folderview->opened = NULL;
746 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
747 gtk_cmclist_clear(GTK_CMCLIST(ctree));
749 folderview_set_folders(folderview);
752 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
754 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
756 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
757 main_window_cursor_normal(mainwin);
758 STATUSBAR_POP(mainwin);
762 void folderview_set_all(void)
766 for (list = folderview_list; list != NULL; list = list->next)
767 folderview_set((FolderView *)list->data);
770 void folderview_select(FolderView *folderview, FolderItem *item)
772 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
773 GtkCMCTreeNode *node;
774 GtkCMCTreeNode *old_selected = folderview->selected;
778 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
779 if (node) folderview_select_node(folderview, node);
781 if (old_selected != node)
782 folder_update_op_count();
785 static void mark_all_read_cb(GtkAction *action, gpointer data)
787 FolderView *folderview = (FolderView *)data;
791 item = folderview_get_selected_item(folderview);
795 if (folderview->summaryview->folder_item != item
796 && prefs_common.ask_mark_all_read) {
797 val = alertpanel_full(_("Mark all as read"),
798 _("Do you really want to mark all mails in this "
799 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
800 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
802 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
804 else if (val & G_ALERTDISABLE)
805 prefs_common.ask_mark_all_read = FALSE;
809 folder_item_update_freeze();
810 if (folderview->summaryview->folder_item != item)
811 summary_lock(folderview->summaryview);
813 summary_freeze(folderview->summaryview);
815 folderutils_mark_all_read(item);
817 if (folderview->summaryview->folder_item != item)
818 summary_unlock(folderview->summaryview);
820 summary_thaw(folderview->summaryview);
821 folder_item_update_thaw();
824 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
826 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
828 cm_return_if_fail(node != NULL);
830 if (folderview->open_folder) {
834 folderview->open_folder = TRUE;
835 gtkut_ctree_set_focus_row(ctree, node);
836 gtk_cmctree_select(ctree, node);
837 if ((folderview->summaryview->folder_item &&
838 folderview->summaryview->folder_item->total_msgs > 0) ||
839 prefs_common.layout_mode == SMALL_LAYOUT)
840 summary_grab_focus(folderview->summaryview);
842 gtk_widget_grab_focus(folderview->ctree);
844 gtkut_ctree_expand_parent_all(ctree, node);
847 void folderview_unselect(FolderView *folderview)
849 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
851 (GTK_CMCTREE(folderview->ctree), folderview->opened);
853 folderview->selected = folderview->opened = NULL;
856 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
857 GtkCMCTreeNode *node)
862 node = gtkut_ctree_node_next(ctree, node);
864 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
866 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
867 item = gtk_cmctree_node_get_row_data(ctree, node);
868 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
875 void folderview_select_next_marked(FolderView *folderview)
877 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
878 GtkCMCTreeNode *node = NULL;
879 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
880 gboolean last_open = prefs_common.always_show_msg;
882 prefs_common.summary_select_prio[0] = ACTION_MARKED;
883 prefs_common.always_show_msg = OPENMSG_ALWAYS;
885 if ((node = folderview_find_next_marked(ctree, folderview->opened))
887 folderview_select_node(folderview, node);
891 if (!folderview->opened ||
892 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
895 /* search again from the first node */
896 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
897 folderview_select_node(folderview, node);
900 prefs_common.summary_select_prio[0] = last_summary_select_prio;
901 prefs_common.always_show_msg = last_open;
904 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
905 GtkCMCTreeNode *node)
910 node = gtkut_ctree_node_next(ctree, node);
912 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
914 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
915 item = gtk_cmctree_node_get_row_data(ctree, node);
916 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
923 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
925 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
926 GtkCMCTreeNode *node = NULL;
927 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
928 gboolean last_open = prefs_common.always_show_msg;
930 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
931 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
933 if ((node = folderview_find_next_unread(ctree, folderview->opened))
935 folderview_select_node(folderview, node);
939 if (!folderview->opened ||
940 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
943 /* search again from the first node */
944 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
945 folderview_select_node(folderview, node);
948 prefs_common.summary_select_prio[0] = last_summary_select_prio;
949 prefs_common.always_show_msg = last_open;
952 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
953 GtkCMCTreeNode *node)
958 node = gtkut_ctree_node_next(ctree, node);
960 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
962 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
963 item = gtk_cmctree_node_get_row_data(ctree, node);
964 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
971 void folderview_select_next_new(FolderView *folderview)
973 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
974 GtkCMCTreeNode *node = NULL;
975 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
976 gboolean last_open = prefs_common.always_show_msg;
978 prefs_common.summary_select_prio[0] = ACTION_NEW;
979 prefs_common.always_show_msg = OPENMSG_ALWAYS;
981 if ((node = folderview_find_next_new(ctree, folderview->opened))
983 folderview_select_node(folderview, node);
987 if (!folderview->opened ||
988 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
991 /* search again from the first node */
992 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
993 folderview_select_node(folderview, node);
996 prefs_common.summary_select_prio[0] = last_summary_select_prio;
997 prefs_common.always_show_msg = last_open;
1000 FolderItem *folderview_get_selected_item(FolderView *folderview)
1002 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1004 if (!folderview->selected) return NULL;
1005 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1008 static void folderview_set_folders(FolderView *folderview)
1011 list = folder_get_list();
1013 for (; list != NULL; list = list->next) {
1014 folderview_append_folder(folderview, FOLDER(list->data));
1018 static gchar *get_scan_str(FolderItem *item)
1021 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1022 item->folder->name, G_DIR_SEPARATOR,
1025 return g_strdup_printf(_("Scanning folder %s..."),
1026 item->folder->name);
1028 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1032 for (list = folderview_list; list != NULL; list = list->next) {
1033 FolderView *folderview = (FolderView *)list->data;
1034 MainWindow *mainwin = folderview->mainwin;
1035 gchar *str = get_scan_str(item);
1037 STATUSBAR_PUSH(mainwin, str);
1038 STATUSBAR_POP(mainwin);
1043 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1046 MainWindow *mainwin = mainwindow_get_mainwindow();
1047 FolderView *folderview = NULL;
1048 GtkAdjustment *pos = NULL;
1051 cm_return_if_fail(folder != NULL);
1053 if (!folder->klass->scan_tree) return;
1056 alertpanel_full(_("Rebuild folder tree"),
1057 _("Rebuilding the folder tree will remove "
1058 "local caches. Do you want to continue?"),
1059 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1060 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1061 != G_ALERTALTERNATE) {
1067 window = label_window_create(_("Rebuilding folder tree..."));
1069 window = label_window_create(_("Scanning folder tree..."));
1072 folderview = mainwin->folderview;
1075 pos = gtk_scrolled_window_get_vadjustment(
1076 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1077 height = gtk_adjustment_get_value(pos);
1080 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1081 folder_scan_tree(folder, rebuild);
1082 folder_set_ui_func(folder, NULL, NULL);
1084 folderview_set_all();
1087 pos = gtk_scrolled_window_get_vadjustment(
1088 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1089 gtk_adjustment_set_value(pos, height);
1090 gtk_adjustment_changed(pos);
1092 label_window_destroy(window);
1096 void folderview_fast_rescan_tree(Folder *folder)
1099 MainWindow *mainwin = mainwindow_get_mainwindow();
1100 FolderView *folderview = NULL;
1101 GtkAdjustment *pos = NULL;
1104 cm_return_if_fail(folder != NULL);
1106 if (!folder->klass->scan_tree) return;
1110 window = label_window_create(_("Scanning folder tree..."));
1113 folderview = mainwin->folderview;
1116 pos = gtk_scrolled_window_get_vadjustment(
1117 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1118 height = gtk_adjustment_get_value(pos);
1121 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1122 folder_fast_scan_tree(folder);
1123 folder_set_ui_func(folder, NULL, NULL);
1125 folderview_set_all();
1128 pos = gtk_scrolled_window_get_vadjustment(
1129 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1130 gtk_adjustment_set_value(pos, height);
1131 gtk_adjustment_changed(pos);
1133 label_window_destroy(window);
1137 /** folderview_check_new()
1138 * Scan and update the folder and return the
1139 * count the number of new messages since last check.
1140 * \param folder the folder to check for new messages
1141 * \return the number of new messages since last check
1143 gint folderview_check_new(Folder *folder)
1147 FolderView *folderview;
1149 GtkCMCTreeNode *node;
1151 gint former_new_msgs = 0;
1152 gint former_new = 0, former_unread = 0, former_total;
1154 for (list = folderview_list; list != NULL; list = list->next) {
1155 folderview = (FolderView *)list->data;
1156 ctree = GTK_CMCTREE(folderview->ctree);
1157 folderview->scanning_folder = folder;
1159 main_window_lock(folderview->mainwin);
1161 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1162 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1164 item = gtk_cmctree_node_get_row_data(ctree, node);
1165 if (!item || !item->path || !item->folder) continue;
1166 if (item->no_select) continue;
1167 if (folder && folder != item->folder) continue;
1168 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1169 if (!item->prefs->newmailcheck) continue;
1170 if (item->processing_pending == TRUE) {
1171 debug_print("skipping %s, processing pending\n",
1172 item->path ? item->path : item->name);
1175 if (item->scanning != ITEM_NOT_SCANNING) {
1176 debug_print("skipping %s, scanning\n",
1177 item->path ? item->path : item->name);
1181 str = get_scan_str(item);
1183 STATUSBAR_PUSH(folderview->mainwin, str);
1187 folderview_scan_tree_func(item->folder, item, NULL);
1188 former_new = item->new_msgs;
1189 former_unread = item->unread_msgs;
1190 former_total = item->total_msgs;
1192 if (item->folder->klass->scan_required &&
1193 (item->folder->klass->scan_required(item->folder, item) ||
1194 item->folder->inbox == item ||
1195 item->opened == TRUE ||
1196 item->processing_pending == TRUE)) {
1197 if (folder_item_scan(item) < 0) {
1199 summaryview_unlock(folderview->summaryview, item);
1200 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1201 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1202 item->path ? item->path:item->name);
1203 STATUSBAR_POP(folderview->mainwin);
1205 } else if (!FOLDER_IS_LOCAL(folder)) {
1206 STATUSBAR_POP(folderview->mainwin);
1211 } else if (!item->folder->klass->scan_required) {
1212 if (folder_item_scan(item) < 0) {
1213 summaryview_unlock(folderview->summaryview, item);
1214 if (folder && !FOLDER_IS_LOCAL(folder)) {
1215 STATUSBAR_POP(folderview->mainwin);
1220 if (former_new != item->new_msgs ||
1221 former_unread != item->unread_msgs ||
1222 former_total != item->total_msgs)
1223 folderview_update_node(folderview, node);
1225 new_msgs += item->new_msgs;
1226 former_new_msgs += former_new;
1227 STATUSBAR_POP(folderview->mainwin);
1229 folderview->scanning_folder = NULL;
1230 main_window_unlock(folderview->mainwin);
1234 folder_write_list();
1235 /* Number of new messages since last check is the just the difference
1236 * between former_new_msgs and new_msgs. If new_msgs is less than
1237 * former_new_msgs, that would mean another session accessed the folder
1238 * and the result is not well defined.
1240 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1244 void folderview_check_new_all(void)
1248 FolderView *folderview;
1250 folderview = (FolderView *)folderview_list->data;
1253 main_window_lock(folderview->mainwin);
1254 window = label_window_create
1255 (_("Checking for new messages in all folders..."));
1257 list = folder_get_list();
1258 for (; list != NULL; list = list->next) {
1259 Folder *folder = list->data;
1261 folderview_check_new(folder);
1264 folder_write_list();
1265 folderview_set_all();
1267 label_window_destroy(window);
1268 main_window_unlock(folderview->mainwin);
1272 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1278 if (!item || !item->folder || !item->folder->node)
1281 node = item->folder->node;
1283 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1284 node = node->children;
1287 (item->new_msgs > 0 ||
1288 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1292 while (node != NULL) {
1293 if (node && node->data) {
1294 FolderItem *next_item = (FolderItem*) node->data;
1296 if (folderview_have_new_children_sub(folderview,
1305 static gboolean folderview_have_new_children(FolderView *folderview,
1308 return folderview_have_new_children_sub(folderview, item, FALSE);
1311 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1317 if (!item || !item->folder || !item->folder->node)
1320 node = item->folder->node;
1322 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1323 node = node->children;
1326 (item->unread_msgs > 0 ||
1327 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1331 while (node != NULL) {
1332 if (node && node->data) {
1333 FolderItem *next_item = (FolderItem*) node->data;
1335 if (folderview_have_unread_children_sub(folderview,
1345 static gboolean folderview_have_unread_children(FolderView *folderview,
1348 return folderview_have_unread_children_sub(folderview, item, FALSE);
1351 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1357 if (!item || !item->folder || !item->folder->node)
1360 node = item->folder->node;
1362 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1363 node = node->children;
1365 if (in_sub && item->search_match){
1369 while (node != NULL) {
1370 if (node && node->data) {
1371 FolderItem *next_item = (FolderItem*) node->data;
1373 if (folderview_have_matching_children_sub(folderview,
1383 static gboolean folderview_have_matching_children(FolderView *folderview,
1386 return folderview_have_matching_children_sub(folderview, item, FALSE);
1389 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1395 if (!item || !item->folder || !item->folder->node)
1398 node = item->folder->node;
1400 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1401 node = node->children;
1403 if (item->marked_msgs != 0) {
1407 while (node != NULL) {
1408 if (node && node->data) {
1409 FolderItem *next_item = (FolderItem*) node->data;
1411 if (folderview_have_marked_children_sub(folderview,
1420 static gboolean folderview_have_marked_children(FolderView *folderview,
1423 return folderview_have_marked_children_sub(folderview, item, FALSE);
1426 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1428 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1429 GtkStyle *style = NULL;
1430 GtkStyle *color_style = NULL;
1432 GdkPixbuf *xpm, *openxpm;
1433 static GdkPixbuf *searchicon;
1434 gboolean mark = FALSE;
1437 gboolean add_unread_mark;
1438 gboolean add_sub_match_mark;
1439 gboolean use_bold, use_color;
1440 gint *col_pos = folderview->col_pos;
1441 SpecialFolderItemType stype;
1443 item = gtk_cmctree_node_get_row_data(ctree, node);
1444 cm_return_if_fail(item != NULL);
1446 if (!GTK_CMCTREE_ROW(node)->expanded)
1447 mark = folderview_have_marked_children(folderview, item);
1449 mark = (item->marked_msgs != 0);
1451 stype = item->stype;
1452 if (stype == F_NORMAL) {
1453 if (folder_has_parent_of_type(item, F_TRASH))
1455 else if (folder_has_parent_of_type(item, F_DRAFT))
1457 else if (folder_has_parent_of_type(item, F_OUTBOX))
1459 else if (folder_has_parent_of_type(item, F_QUEUE))
1464 if (item->hide_read_msgs || item->hide_read_threads) {
1465 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1466 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1468 xpm = mark?m_inboxxpm:inboxxpm;
1469 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1473 if (item->hide_read_msgs || item->hide_read_threads) {
1474 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1475 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1477 xpm = mark?m_outboxxpm:outboxxpm;
1478 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1482 if (item->hide_read_msgs || item->hide_read_threads) {
1483 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1484 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1486 xpm = mark?m_queuexpm:queuexpm;
1487 openxpm = mark?m_queueopenxpm:queueopenxpm;
1491 if (item->hide_read_msgs || item->hide_read_threads) {
1492 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1493 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1495 xpm = mark?m_trashxpm:trashxpm;
1496 openxpm = mark?m_trashopenxpm:trashopenxpm;
1500 xpm = mark?m_draftsxpm:draftsxpm;
1501 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1504 if (item->hide_read_msgs || item->hide_read_threads) {
1505 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1506 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1508 xpm = mark?m_folderxpm:folderxpm;
1509 openxpm = mark?m_folderopenxpm:folderopenxpm;
1513 if (item->no_select) {
1514 xpm = openxpm = noselectxpm;
1517 name = folder_item_get_name(item);
1519 if (!GTK_CMCTREE_ROW(node)->expanded) {
1520 add_unread_mark = folderview_have_unread_children(
1522 add_sub_match_mark = folderview_have_matching_children(
1525 add_unread_mark = FALSE;
1526 add_sub_match_mark = FALSE;
1529 if (item->search_match) {
1531 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1534 xpm = openxpm = searchicon;
1538 if (prefs_common.display_folder_unread) {
1539 if (folder_has_parent_of_type(item, F_QUEUE)) {
1540 /* only total_msgs matters here */
1541 if (item->total_msgs > 0) {
1542 /* show total number (should be equal to the unread number)
1544 str = g_strdup_printf("%s (%d%s%s)",
1545 name, item->total_msgs,
1546 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1547 (item->unreadmarked_msgs > 0) ? "!" : "");
1550 if (prefs_common.display_folder_unread == 1) {
1551 if (item->unread_msgs > 0) {
1552 /* show unread number and signs */
1553 str = g_strdup_printf("%s (%d%s%s)",
1554 name, item->unread_msgs,
1555 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1556 (item->unreadmarked_msgs > 0) ? "!" : "");
1559 if (item->total_msgs > 0) {
1560 /* show unread number, total number and signs if any */
1561 str = g_strdup_printf("%s (%d/%d%s%s)",
1562 name, item->unread_msgs, item->total_msgs,
1563 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1564 (item->unreadmarked_msgs > 0) ? "!" : "");
1568 if ((str == NULL) &&
1569 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1570 /* no unread/total numbers, but at least one sign */
1571 str = g_strdup_printf("%s (%s%s)",
1573 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1574 (item->unreadmarked_msgs > 0) ? "!" : "");
1578 /* last fallback, folder name only or with +! sign */
1579 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1580 str = g_strdup_printf("%s%s",
1582 } else if (item->unreadmarked_msgs > 0) {
1583 str = g_strdup_printf("%s%s",
1585 } else if (add_sub_match_mark) {
1586 str = g_strdup_printf("%s%s",
1589 str = g_strdup_printf("%s", name);
1592 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1594 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1598 if (!folder_item_parent(item)) {
1599 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1600 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1601 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1603 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1604 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1605 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1608 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1609 folder_has_parent_of_type(item, F_DRAFT) ||
1610 folder_has_parent_of_type(item, F_TRASH)) {
1611 use_bold = use_color = FALSE;
1612 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1613 GSList *list = folder_item_get_msg_list(item);
1615 use_bold = use_color = FALSE;
1616 for (cur = list; cur; cur = cur->next) {
1617 MsgInfo *msginfo = (MsgInfo *)cur->data;
1618 if (!MSG_IS_DELETED(msginfo->flags)) {
1619 /* highlight queue folder if there are any messages */
1620 use_bold = use_color = TRUE;
1625 /* if unread messages exist, print with bold font */
1626 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1628 /* if new messages exist, print with colored letter */
1630 (item->new_msgs > 0) ||
1632 folderview_have_new_children(folderview, item));
1635 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1640 if (item->prefs->color > 0 && !use_color) {
1641 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1642 color_style = gtk_style_copy(bold_style);
1643 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1644 style = color_style;
1645 } else if (use_color) {
1646 style = bold_color_style;
1649 if (item->op_count > 0) {
1650 style = bold_tgtfold_style;
1652 } else if (use_color) {
1653 style = normal_color_style;
1654 gtk_cmctree_node_set_foreground(ctree, node,
1655 &folderview->color_new);
1656 } else if (item->op_count > 0) {
1657 style = bold_tgtfold_style;
1658 } else if (item->prefs->color > 0) {
1660 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1661 color_style = gtk_style_copy(normal_style);
1662 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1663 style = color_style;
1665 style = normal_style;
1668 gtk_cmctree_node_set_row_style(ctree, node, style);
1670 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1671 folderview_update_node(folderview, node);
1674 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1677 FolderView *folderview;
1679 GtkCMCTreeNode *node;
1681 cm_return_if_fail(item != NULL);
1683 for (list = folderview_list; list != NULL; list = list->next) {
1684 folderview = (FolderView *)list->data;
1685 ctree = GTK_CMCTREE(folderview->ctree);
1687 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1688 if (node && item->search_match != matches) {
1689 item->search_match = matches;
1690 folderview_update_node(folderview, node);
1695 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1697 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1698 FolderView *folderview = (FolderView *)data;
1700 GtkCMCTreeNode *node;
1701 cm_return_val_if_fail(update_info != NULL, TRUE);
1702 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1703 cm_return_val_if_fail(folderview != NULL, FALSE);
1705 ctree = GTK_CMCTREE(folderview->ctree);
1707 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1710 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1711 folderview_update_node(folderview, node);
1713 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1714 update_info->item == folderview->summaryview->folder_item &&
1715 update_info->item != NULL)
1716 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1717 summary_show(folderview->summaryview, update_info->item);
1723 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1724 GNode *gnode, GtkCMCTreeNode *cnode,
1727 FolderView *folderview = (FolderView *)data;
1728 FolderItem *item = FOLDER_ITEM(gnode->data);
1730 cm_return_val_if_fail(item != NULL, FALSE);
1732 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1733 folderview_update_node(folderview, cnode);
1738 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1741 FolderView *folderview = (FolderView *)data;
1744 if (GTK_CMCTREE_ROW(node)->children) {
1745 item = gtk_cmctree_node_get_row_data(ctree, node);
1746 cm_return_if_fail(item != NULL);
1748 if (!item->collapsed)
1749 gtk_cmctree_expand(ctree, node);
1751 folderview_update_node(folderview, node);
1755 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1756 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1759 GtkCMCTreeNode *node, *parent, *sibling;
1761 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1763 g_warning("%s not found.\n", item->path);
1765 parent = GTK_CMCTREE_ROW(node)->parent;
1766 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1767 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1769 sibling = GTK_CMCTREE_ROW(parent)->children;
1773 tmp = gtk_cmctree_node_get_row_data
1775 if (tmp->stype != F_NORMAL)
1776 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1780 if (node != sibling)
1781 gtk_cmctree_move(ctree, node, parent, sibling);
1788 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1791 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1792 GtkCMCTreeNode *prev = NULL;
1794 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1795 gtk_sctree_sort_recursive(ctree, root);
1796 if (root && GTK_CMCTREE_ROW(root)->parent) {
1797 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1800 set_special_folder(ctree, folder->inbox, root, &prev);
1801 set_special_folder(ctree, folder->outbox, root, &prev);
1802 set_special_folder(ctree, folder->draft, root, &prev);
1803 set_special_folder(ctree, folder->queue, root, &prev);
1804 set_special_folder(ctree, folder->trash, root, &prev);
1805 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1808 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1810 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1811 GtkCMCTreeNode *root;
1813 cm_return_if_fail(folder != NULL);
1815 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1816 folderview_gnode_func, folderview);
1817 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1819 folderview_sort_folders(folderview, root, folder);
1822 /* callback functions */
1823 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1824 GdkEventButton *event)
1828 FolderViewPopup *fpopup;
1829 GtkActionGroup *action_group;
1831 FolderItem *special_trash = NULL, *special_queue = NULL;
1833 GtkUIManager *ui_manager = gtk_ui_manager_new();
1835 if (folderview->ui_manager)
1836 g_object_unref(folderview->ui_manager);
1838 folderview->ui_manager = ui_manager;
1839 item = folderview_get_selected_item(folderview);
1841 cm_return_if_fail(item != NULL);
1842 cm_return_if_fail(item->folder != NULL);
1843 folder = item->folder;
1845 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1848 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1850 fpopup = g_hash_table_lookup(folderview_popups, "common");
1851 action_group = g_hash_table_lookup(folderview->popups, "common");
1854 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1855 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1856 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1858 if (fpopup->add_menuitems)
1859 fpopup->add_menuitems(ui_manager, item);
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1864 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1865 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1868 if (fpopup->set_sensitivity != NULL)
1869 fpopup->set_sensitivity(ui_manager, item);
1871 if (NULL != (ac = account_find_from_item(item))) {
1872 special_trash = account_get_special_folder(ac, F_TRASH);
1873 special_queue = account_get_special_folder(ac, F_QUEUE);
1876 if ((item == folder->trash || item == special_trash
1877 || folder_has_parent_of_type(item, F_TRASH))) {
1878 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1879 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1882 if ((item == folder->queue || item == special_queue
1883 || folder_has_parent_of_type(item, F_QUEUE))) {
1884 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1885 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1888 #define SET_SENS(name, sens) \
1889 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1891 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1892 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1893 folderview->selected == folderview->opened);
1894 SET_SENS("FolderViewPopup/Properties", TRUE);
1896 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1897 item->total_msgs >= 1 && !item->processing_pending);
1898 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1899 !item->no_select && !item->processing_pending);
1901 if (item == folder->trash || item == special_trash
1902 || folder_has_parent_of_type(item, F_TRASH)) {
1903 GSList *msglist = folder_item_get_msg_list(item);
1904 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1905 procmsg_msg_list_free(msglist);
1907 if (item == folder->queue || item == special_queue
1908 || folder_has_parent_of_type(item, F_QUEUE)) {
1909 GSList *msglist = folder_item_get_msg_list(item);
1910 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1911 procmsg_msg_list_free(msglist);
1915 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1916 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1917 g_signal_connect(G_OBJECT(popup), "selection_done",
1918 G_CALLBACK(folderview_popup_close),
1920 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1921 event->button, event->time);
1924 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1925 FolderView *folderview)
1927 GtkCMCList *clist = GTK_CMCLIST(ctree);
1928 gint prev_row = -1, row = -1, column = -1;
1930 if (!event) return FALSE;
1932 if (event->button == 1 || event->button == 2) {
1933 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1934 folderview->open_folder = TRUE;
1936 if (event->type == GDK_2BUTTON_PRESS) {
1937 if (clist->selection) {
1938 GtkCMCTreeNode *node;
1940 node = GTK_CMCTREE_NODE(clist->selection->data);
1942 gtk_cmctree_toggle_expansion(
1945 folderview->open_folder = FALSE;
1952 if (event->button == 2 || event->button == 3) {
1954 if (clist->selection) {
1955 GtkCMCTreeNode *node;
1957 node = GTK_CMCTREE_NODE(clist->selection->data);
1959 prev_row = gtkut_ctree_get_nth_from_node
1960 (GTK_CMCTREE(ctree), node);
1963 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1966 if (prev_row != row) {
1967 gtk_cmclist_unselect_all(clist);
1968 if (event->button == 2)
1969 folderview_select_node
1971 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1974 gtk_cmclist_select_row(clist, row, column);
1978 if (event->button != 3) return FALSE;
1980 folderview_set_sens_and_popup_menu(folderview, row, event);
1984 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1985 FolderView *folderview)
1987 int row = -1, column = -1;
1989 if (!event) return FALSE;
1991 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1994 if (event->button == 1 && folderview->open_folder == FALSE &&
1995 folderview->opened != NULL) {
1996 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1997 folderview->opened);
1998 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2004 #define BREAK_ON_MODIFIER_KEY() \
2005 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2007 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2008 FolderView *folderview)
2010 if (!event) return FALSE;
2012 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2015 switch (event->keyval) {
2017 #ifndef GENERIC_UMPC
2018 case GDK_KEY_Return:
2019 case GDK_KEY_KP_Enter:
2021 if (folderview->selected) {
2022 folderview_select_node(folderview,
2023 folderview->selected);
2027 case GDK_KEY_Return:
2028 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2029 gtk_cmctree_toggle_expansion(
2030 GTK_CMCTREE(folderview->ctree),
2031 folderview->selected);
2036 BREAK_ON_MODIFIER_KEY();
2037 if (folderview->selected) {
2038 if (folderview->opened == folderview->selected &&
2039 (!folderview->summaryview->folder_item ||
2040 folderview->summaryview->folder_item->total_msgs == 0))
2041 folderview_select_next_unread(folderview, TRUE);
2043 folderview_select_node(folderview,
2044 folderview->selected);
2054 typedef struct _PostponedSelectData
2057 GtkCMCTreeNode *row;
2059 FolderView *folderview;
2060 } PostponedSelectData;
2062 static gboolean postpone_select(void *data)
2064 PostponedSelectData *psdata = (PostponedSelectData *)data;
2065 debug_print("trying again\n");
2066 psdata->folderview->open_folder = TRUE;
2067 main_window_cursor_normal(psdata->folderview->mainwin);
2068 STATUSBAR_POP(psdata->folderview->mainwin);
2069 folderview_selected(psdata->ctree, psdata->row,
2070 psdata->column, psdata->folderview);
2075 void folderview_close_opened(FolderView *folderview)
2077 if (folderview->opened) {
2078 FolderItem *olditem;
2080 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2081 folderview->opened);
2083 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2084 olditem->path ? olditem->path:olditem->name);
2085 /* will be null if we just moved the previously opened folder */
2086 STATUSBAR_PUSH(folderview->mainwin, buf);
2087 main_window_cursor_wait(folderview->mainwin);
2089 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2090 summary_show(folderview->summaryview, NULL);
2091 folder_item_close(olditem);
2092 main_window_cursor_normal(folderview->mainwin);
2093 STATUSBAR_POP(folderview->mainwin);
2094 if (olditem->folder->klass->item_closed)
2095 olditem->folder->klass->item_closed(olditem);
2100 if (folderview->opened &&
2101 !GTK_CMCTREE_ROW(folderview->opened)->children)
2102 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2104 folderview->opened = NULL;
2106 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2107 gint column, FolderView *folderview)
2109 static gboolean can_select = TRUE; /* exclusive lock */
2114 GtkCMCTreeNode *old_opened = folderview->opened;
2116 folderview->selected = row;
2118 debug_print("newly selected %p, opened %p\n", folderview->selected,
2119 folderview->opened);
2120 if (folderview->opened == row) {
2121 folderview->open_folder = FALSE;
2126 item = gtk_cmctree_node_get_row_data(ctree, row);
2129 folderview->open_folder = FALSE;
2133 if (!can_select || summary_is_locked(folderview->summaryview)) {
2134 if (folderview->opened) {
2135 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2136 gtk_cmctree_select(ctree, folderview->opened);
2138 folderview->open_folder = FALSE;
2143 if (!folderview->open_folder) {
2150 /* Save cache for old folder */
2151 /* We don't want to lose all caches if sylpheed crashed */
2152 /* resets folderview->opened to NULL */
2153 folderview_close_opened(folderview);
2155 /* CLAWS: set compose button type: news folder items
2156 * always have a news folder as parent */
2158 toolbar_set_compose_button
2159 (folderview->mainwin->toolbar,
2160 FOLDER_TYPE(item->folder) == F_NEWS ?
2161 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2164 debug_print("Folder %s is selected\n", item->path);
2166 if (!GTK_CMCTREE_ROW(row)->children)
2167 gtk_cmctree_expand(ctree, row);
2169 /* ungrab the mouse event */
2170 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2171 gtk_grab_remove(GTK_WIDGET(ctree));
2172 if (gdk_pointer_is_grabbed())
2173 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2177 /* TODO: wwp: avoid displaying (null) in the status bar */
2178 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2179 item->path : "(null)");
2180 debug_print("%s\n", buf);
2181 STATUSBAR_PUSH(folderview->mainwin, buf);
2184 main_window_cursor_wait(folderview->mainwin);
2186 if (folderview->scanning_folder == item->folder) {
2189 res = folder_item_open(item);
2192 if (res == -1 && item->no_select == FALSE) {
2193 main_window_cursor_normal(folderview->mainwin);
2194 STATUSBAR_POP(folderview->mainwin);
2196 alertpanel_error(_("Folder could not be opened."));
2198 folderview->open_folder = FALSE;
2202 } else if (res == -2 && item->no_select == FALSE) {
2203 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2204 data->ctree = ctree;
2206 data->column = column;
2207 data->folderview = folderview;
2208 debug_print("postponing open of %s till end of scan\n",
2209 item->path ? item->path:item->name);
2210 folderview->open_folder = FALSE;
2212 g_timeout_add(500, postpone_select, data);
2217 main_window_cursor_normal(folderview->mainwin);
2220 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2221 opened = summary_show(folderview->summaryview, item);
2223 folder_clean_cache_memory(item);
2226 gtkut_ctree_set_focus_row(ctree, old_opened);
2227 gtk_cmctree_select(ctree, old_opened);
2228 folderview->opened = old_opened;
2230 folderview->opened = row;
2231 if (gtk_cmctree_node_is_visible(ctree, row)
2232 != GTK_VISIBILITY_FULL)
2233 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2236 STATUSBAR_POP(folderview->mainwin);
2238 folderview->open_folder = FALSE;
2243 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2244 FolderView *folderview)
2248 item = gtk_cmctree_node_get_row_data(ctree, node);
2249 cm_return_if_fail(item != NULL);
2250 item->collapsed = FALSE;
2251 folderview_update_node(folderview, node);
2254 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2255 FolderView *folderview)
2259 item = gtk_cmctree_node_get_row_data(ctree, node);
2260 cm_return_if_fail(item != NULL);
2261 item->collapsed = TRUE;
2262 folderview_update_node(folderview, node);
2265 static void folderview_popup_close(GtkMenuShell *menu_shell,
2266 FolderView *folderview)
2268 if (!folderview->opened) return;
2270 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2273 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2274 FolderView *folderview)
2276 FolderColumnType type = folderview->col_state[column].type;
2278 prefs_common.folder_col_size[type] = width;
2281 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2283 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2284 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2285 GtkCMCTreeNode *node, *parent_node;
2286 gint *col_pos = folderview->col_pos;
2287 FolderItemUpdateData hookdata;
2289 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2290 if (parent_node == NULL)
2293 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2295 text[col_pos[F_COL_FOLDER]] = item->name;
2296 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2301 gtk_cmctree_expand(ctree, parent_node);
2302 gtk_cmctree_node_set_row_data(ctree, node, item);
2304 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2305 folderview_sort_folders(folderview, parent_node, item->folder);
2307 hookdata.item = item;
2308 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2309 hookdata.msg = NULL;
2310 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2312 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2315 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2317 FolderView *folderview = (FolderView *)data;
2318 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2320 GSList *mlist = NULL;
2322 FolderItem *special_trash = NULL;
2325 if (!folderview->selected) return;
2326 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2327 cm_return_if_fail(item != NULL);
2328 cm_return_if_fail(item->folder != NULL);
2330 if (NULL != (ac = account_find_from_item(item)))
2331 special_trash = account_get_special_folder(ac, F_TRASH);
2333 if (item != item->folder->trash && item != special_trash
2334 && !folder_has_parent_of_type(item, F_TRASH)) return;
2336 if (prefs_common.ask_on_clean) {
2337 if (alertpanel(_("Empty trash"),
2338 _("Delete all messages in trash?"),
2339 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2343 mlist = folder_item_get_msg_list(item);
2345 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2346 MsgInfo * msginfo = (MsgInfo *) cur->data;
2347 if (MSG_IS_LOCKED(msginfo->flags))
2349 /* is it partially received? (partial_recv isn't cached) */
2350 if (msginfo->total_size != 0 &&
2351 msginfo->size != (off_t)msginfo->total_size)
2352 partial_mark_for_delete(msginfo);
2354 procmsg_msg_list_free(mlist);
2356 folder_item_remove_all_msg(item);
2359 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2361 FolderView *folderview = (FolderView *)data;
2362 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2364 FolderItem *special_queue = NULL;
2366 gchar *errstr = NULL;
2368 if (!folderview->selected) return;
2369 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2370 cm_return_if_fail(item != NULL);
2371 cm_return_if_fail(item->folder != NULL);
2373 if (NULL != (ac = account_find_from_item(item)))
2374 special_queue = account_get_special_folder(ac, F_QUEUE);
2376 if (item != item->folder->queue && item != special_queue
2377 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2379 if (procmsg_queue_is_empty(item))
2382 if (prefs_common.work_offline)
2383 if (alertpanel(_("Offline warning"),
2384 _("You're working offline. Override?"),
2385 GTK_STOCK_NO, GTK_STOCK_YES,
2386 NULL) != G_ALERTALTERNATE)
2389 /* ask for confirmation before sending queued messages only
2390 in online mode and if there is at least one message queued
2391 in any of the folder queue
2393 if (prefs_common.confirm_send_queued_messages) {
2394 if (!prefs_common.work_offline) {
2395 if (alertpanel(_("Send queued messages"),
2396 _("Send all queued messages?"),
2397 GTK_STOCK_CANCEL, _("_Send"),
2398 NULL) != G_ALERTALTERNATE)
2403 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2405 alertpanel_error_log(_("Some errors occurred while "
2406 "sending queued messages."));
2408 alertpanel_error_log(_("Some errors occurred "
2409 "while sending queued messages:\n%s"), errstr);
2415 static void folderview_search_cb(GtkAction *action, gpointer data)
2417 FolderView *folderview = (FolderView *)data;
2418 summary_search(folderview->summaryview);
2421 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2423 FolderView *folderview = (FolderView *)data;
2424 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2427 if (!folderview->selected) return;
2429 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2430 cm_return_if_fail(item != NULL);
2431 cm_return_if_fail(item->folder != NULL);
2433 item->processing_pending = TRUE;
2434 folder_item_apply_processing(item);
2435 item->processing_pending = FALSE;
2438 static void folderview_property_cb(GtkAction *action, gpointer data)
2440 FolderView *folderview = (FolderView *)data;
2441 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2444 if (!folderview->selected) return;
2446 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2447 cm_return_if_fail(item != NULL);
2448 cm_return_if_fail(item->folder != NULL);
2450 prefs_folder_item_open(item);
2453 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2455 GSList *list = NULL;
2456 GSList *done = NULL;
2457 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2459 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2460 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2461 && list->data != node) {
2462 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2463 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2466 for (list = done; list != NULL; list = g_slist_next(list)) {
2467 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2473 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2474 FolderItem *to_folder, gboolean copy)
2476 FolderItem *new_folder = NULL;
2480 cm_return_if_fail(folderview != NULL);
2481 cm_return_if_fail(from_folder != NULL);
2482 cm_return_if_fail(to_folder != NULL);
2484 if (prefs_common.warn_dnd) {
2485 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2486 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2487 from_folder->name, to_folder->name);
2488 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2489 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2490 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2493 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2495 else if (status & G_ALERTDISABLE)
2496 prefs_common.warn_dnd = FALSE;
2499 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2500 from_folder->name, to_folder->name);
2501 STATUSBAR_PUSH(folderview->mainwin, buf);
2503 summary_clear_all(folderview->summaryview);
2504 folderview->opened = NULL;
2505 folderview->selected = NULL;
2506 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2508 main_window_cursor_wait(folderview->mainwin);
2510 statusbar_verbosity_set(FALSE);
2511 folder_item_update_freeze();
2512 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2513 statusbar_verbosity_set(FALSE);
2514 main_window_cursor_normal(folderview->mainwin);
2515 STATUSBAR_POP(folderview->mainwin);
2516 folder_item_update_thaw();
2517 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2519 folderview_sort_folders(folderview,
2520 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2521 NULL, to_folder), new_folder->folder);
2522 folderview_select(folderview, new_folder);
2524 statusbar_verbosity_set(FALSE);
2525 main_window_cursor_normal(folderview->mainwin);
2526 STATUSBAR_POP(folderview->mainwin);
2527 folder_item_update_thaw();
2529 case F_MOVE_FAILED_DEST_IS_PARENT:
2530 alertpanel_error(_("Source and destination are the same."));
2532 case F_MOVE_FAILED_DEST_IS_CHILD:
2533 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2534 _("Can't move a folder to one of its children."));
2536 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2537 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2540 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2545 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2548 static gint folderview_clist_compare(GtkCMCList *clist,
2549 gconstpointer ptr1, gconstpointer ptr2)
2551 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2552 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2554 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2556 return item1->order - item2->order;
2559 // if only one folder has an order it comes first
2560 if (item1->order > 0)
2564 if (item2->order > 0)
2570 return (item2->name != NULL);
2574 return g_utf8_collate(item1->name, item2->name);
2577 static void folderview_processing_cb(GtkAction *action, gpointer data)
2579 FolderView *folderview = (FolderView *)data;
2580 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2584 if (!folderview->selected) return;
2586 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2587 cm_return_if_fail(item != NULL);
2588 cm_return_if_fail(item->folder != NULL);
2590 id = folder_item_get_identifier(item);
2591 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2594 prefs_filtering_open(&item->prefs->processing, title,
2595 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2599 void folderview_set_target_folder_color(gint color_op)
2603 FolderView *folderview;
2605 for (list = folderview_list; list != NULL; list = list->next) {
2606 folderview = (FolderView *)list->data;
2607 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2609 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2610 folderview->color_op;
2616 static gchar *last_smallfont = NULL;
2617 static gchar *last_normalfont = NULL;
2618 static gchar *last_boldfont = NULL;
2619 static gboolean last_derive = 0;
2621 void folderview_reinit_fonts(FolderView *folderview)
2624 g_free(last_smallfont);
2625 last_smallfont = NULL;
2626 g_free(last_normalfont);
2627 last_normalfont = NULL;
2628 g_free(last_boldfont);
2629 last_boldfont = NULL;
2632 void folderview_reflect_prefs(void)
2634 gboolean update_font = FALSE;
2635 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2636 FolderItem *item = folderview_get_selected_item(folderview);
2637 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2638 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2639 gint height = gtk_adjustment_get_value(pos);
2641 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2642 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2643 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2644 last_derive != prefs_common.derive_from_normal_font)
2647 g_free(last_smallfont);
2648 last_smallfont = g_strdup(SMALL_FONT);
2649 g_free(last_normalfont);
2650 last_normalfont = g_strdup(NORMAL_FONT);
2651 g_free(last_boldfont);
2652 last_boldfont = g_strdup(BOLD_FONT);
2653 last_derive = prefs_common.derive_from_normal_font;
2656 normal_style = normal_color_style = bold_style =
2657 bold_color_style = bold_tgtfold_style = NULL;
2659 folderview_init(folderview);
2661 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2662 folderview_column_set_titles(folderview);
2663 folderview_set_all();
2665 g_signal_handlers_block_by_func
2666 (G_OBJECT(folderview->ctree),
2667 G_CALLBACK(folderview_selected), folderview);
2670 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2671 GTK_CMCTREE(folderview->ctree), NULL, item);
2673 folderview_select(folderview, item);
2674 folderview->open_folder = FALSE;
2675 folderview->selected = node;
2678 g_signal_handlers_unblock_by_func
2679 (G_OBJECT(folderview->ctree),
2680 G_CALLBACK(folderview_selected), folderview);
2682 pos = gtk_scrolled_window_get_vadjustment(
2683 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2684 gtk_adjustment_set_value(pos, height);
2685 gtk_adjustment_changed(pos);
2686 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2689 static void drag_state_stop(FolderView *folderview)
2691 if (folderview->drag_timer)
2692 g_source_remove(folderview->drag_timer);
2693 folderview->drag_timer = 0;
2694 folderview->drag_node = NULL;
2697 static gboolean folderview_defer_expand(FolderView *folderview)
2699 if (folderview->drag_node) {
2700 folderview_recollapse_nodes(folderview, folderview->drag_node);
2701 if (folderview->drag_item->collapsed) {
2702 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2703 folderview->nodes_to_recollapse = g_slist_append
2704 (folderview->nodes_to_recollapse, folderview->drag_node);
2707 folderview->drag_item = NULL;
2708 folderview->drag_timer = 0;
2712 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2714 /* the idea is that we call drag_state_start() whenever we want expansion to
2715 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2716 * we need to call drag_state_stop() */
2717 drag_state_stop(folderview);
2718 /* request expansion */
2719 if (0 != (folderview->drag_timer = g_timeout_add
2720 (prefs_common.hover_timeout,
2721 (GSourceFunc)folderview_defer_expand,
2723 folderview->drag_node = node;
2724 folderview->drag_item = item;
2727 #ifndef GENERIC_UMPC
2728 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2729 FolderView *folderview)
2731 GdkDragContext *context;
2733 cm_return_if_fail(folderview != NULL);
2734 if (folderview->selected == NULL) return;
2735 if (folderview->nodes_to_recollapse)
2736 g_slist_free(folderview->nodes_to_recollapse);
2737 folderview->nodes_to_recollapse = NULL;
2738 context = gtk_drag_begin(widget, folderview->target_list,
2739 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2740 gtk_drag_set_icon_default(context);
2743 static void folderview_drag_data_get(GtkWidget *widget,
2744 GdkDragContext *drag_context,
2745 GtkSelectionData *selection_data,
2748 FolderView *folderview)
2752 gchar *source = NULL;
2753 if (info == TARGET_DUMMY) {
2754 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2755 cur != NULL; cur = cur->next) {
2756 item = gtk_cmctree_node_get_row_data
2757 (GTK_CMCTREE(folderview->ctree),
2758 GTK_CMCTREE_NODE(cur->data));
2760 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2761 gtk_selection_data_set(selection_data,
2762 gtk_selection_data_get_target(selection_data), 8,
2763 source, strlen(source));
2769 g_warning("unknown info %d\n", info);
2773 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2775 FolderUpdateData *hookdata;
2776 FolderView *folderview;
2780 folderview = (FolderView *) userdata;
2781 cm_return_val_if_fail(hookdata != NULL, FALSE);
2782 cm_return_val_if_fail(folderview != NULL, FALSE);
2784 ctree = folderview->ctree;
2785 cm_return_val_if_fail(ctree != NULL, FALSE);
2787 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2788 folderview_create_folder_node(folderview, hookdata->item);
2789 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2790 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2791 NULL, folder_item_parent(hookdata->item));
2792 folderview_sort_folders(folderview, node, hookdata->folder);
2793 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2794 GtkCMCTreeNode *node;
2796 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2798 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2799 if (folderview->selected == node)
2800 folderview->selected = NULL;
2801 if (folderview->opened == node)
2802 folderview->opened = NULL;
2804 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2805 folderview_set(folderview);
2810 static gboolean folderview_dnd_scroll_cb(gpointer data)
2812 FolderView *folderview = (FolderView *)data;
2813 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2814 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2815 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2816 gint max = (int)gtk_adjustment_get_upper(pos) -
2817 (int)gtk_adjustment_get_page_size(pos);
2819 if (folderview->scroll_value == 0) {
2820 folderview->scroll_timeout_id = -1;
2824 if (folderview->scroll_value > 0 && new_val > max) {
2826 } else if (folderview->scroll_value < 0 && new_val < 0) {
2829 gtk_adjustment_set_value(pos, new_val);
2834 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2835 GdkDragContext *context,
2839 FolderView *folderview)
2842 FolderItem *item = NULL, *src_item = NULL;
2843 GtkCMCTreeNode *node = NULL;
2844 gboolean acceptable = FALSE;
2845 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2846 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2847 int height = (int)gtk_adjustment_get_page_size(pos);
2848 int total_height = (int)gtk_adjustment_get_upper(pos);
2849 int vpos = (int)gtk_adjustment_get_value(pos);
2850 int offset = prefs_common.show_col_headers ? 24:0;
2853 if (gtk_cmclist_get_selection_info
2854 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2855 GtkWidget *srcwidget;
2857 if (y > height - (48 - offset) && height + vpos < total_height) {
2858 dist = -(height - (48 - offset) - y);
2859 folderview->scroll_value = 1.41f * (1+(dist / 6));
2860 } else if (y < 72 - (24 - offset) && y >= 0) {
2861 dist = 72 - (24 - offset) - y;
2862 folderview->scroll_value = -1.41f * (1+(dist / 6));
2864 folderview->scroll_value = 0;
2866 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == -1) {
2867 folderview->scroll_timeout_id =
2868 g_timeout_add(30, folderview_dnd_scroll_cb,
2872 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2873 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2874 src_item = folderview->summaryview->folder_item;
2876 srcwidget = gtk_drag_get_source_widget(context);
2877 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2878 /* comes from summaryview */
2879 /* we are copying messages, so only accept folder items that are not
2880 the source item, are no root items and can copy messages */
2881 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2882 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2883 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2885 } else if (srcwidget == folderview->ctree) {
2886 /* comes from folderview */
2887 /* we are moving folder items, only accept folders that are not
2888 the source items and can copy messages and create folder items */
2889 if (item && item->folder && src_item && src_item != item &&
2890 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2891 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2892 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2893 || item->folder == src_item->folder))
2896 /* comes from another app */
2897 /* we are adding messages, so only accept folder items that are
2898 no root items and can copy messages */
2899 if (item && item->folder && folder_item_parent(item) != NULL
2900 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2901 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2906 if (acceptable || (src_item && src_item == item))
2907 drag_state_start(folderview, node, item);
2910 g_signal_handlers_block_by_func
2912 G_CALLBACK(folderview_selected), folderview);
2913 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2914 g_signal_handlers_unblock_by_func
2916 G_CALLBACK(folderview_selected), folderview);
2917 gdk_drag_status(context,
2918 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2919 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2921 if (folderview->opened)
2922 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2923 gdk_drag_status(context, 0, time);
2929 static void folderview_drag_leave_cb(GtkWidget *widget,
2930 GdkDragContext *context,
2932 FolderView *folderview)
2934 drag_state_stop(folderview);
2935 folderview->scroll_value = 0;
2936 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2939 static void free_info (gpointer stuff, gpointer data)
2944 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2945 guint time, FolderItem *item)
2948 GSList *msglist = NULL;
2949 list = uri_list_extract_filenames(data);
2950 if (!(item && item->folder && folder_item_parent(item) != NULL
2951 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2953 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2954 debug_print("item doesn't fit\n");
2958 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2959 debug_print("list is empty\n");
2962 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2963 MsgFileInfo *info = NULL;
2965 if (file_is_email((gchar *)tmp->data)) {
2966 info = g_new0(MsgFileInfo, 1);
2967 info->msginfo = NULL;
2968 info->file = (gchar *)tmp->data;
2969 msglist = g_slist_prepend(msglist, info);
2970 debug_print("file is a mail\n");
2972 debug_print("file isn't a mail\n");
2976 msglist = g_slist_reverse(msglist);
2977 folder_item_add_msgs(item, msglist, FALSE);
2978 g_slist_foreach(msglist, free_info, NULL);
2979 g_slist_free(msglist);
2980 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2982 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2984 list_free_strings(list);
2988 static void folderview_drag_received_cb(GtkWidget *widget,
2989 GdkDragContext *drag_context,
2992 GtkSelectionData *data,
2995 FolderView *folderview)
2998 FolderItem *item = NULL, *src_item;
2999 GtkCMCTreeNode *node;
3000 int offset = prefs_common.show_col_headers ? 24:0;
3002 folderview->scroll_value = 0;
3004 if (info == TARGET_DUMMY) {
3005 drag_state_stop(folderview);
3006 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3007 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3008 /* comes from summaryview */
3009 if (gtk_cmclist_get_selection_info
3010 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3013 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3014 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3015 src_item = folderview->summaryview->folder_item;
3017 if (item->no_select) {
3018 alertpanel_error(_("The destination folder can only be used to "
3019 "store subfolders."));
3022 /* re-check (due to acceptable possibly set for folder moves */
3023 if (!(item && item->folder && item->path && !item->no_select &&
3024 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3027 if (item && src_item) {
3028 switch (gdk_drag_context_get_selected_action(drag_context)) {
3029 case GDK_ACTION_COPY:
3030 summary_copy_selected_to(folderview->summaryview, item);
3031 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3033 case GDK_ACTION_MOVE:
3034 case GDK_ACTION_DEFAULT:
3036 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3037 summary_copy_selected_to(folderview->summaryview, item);
3039 summary_move_selected_to(folderview->summaryview, item);
3040 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3043 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3045 /* comes from folderview */
3047 gboolean folder_is_normal = TRUE;
3048 gboolean copy = (GDK_ACTION_COPY ==
3049 gdk_drag_context_get_selected_action(drag_context));
3051 source = (char *)gtk_selection_data_get_data(data) + 17;
3052 if (gtk_cmclist_get_selection_info
3053 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3055 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3058 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3059 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3060 src_item = folder_find_item_from_identifier(source);
3064 src_item->stype == F_NORMAL &&
3065 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3066 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3067 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3068 !folder_has_parent_of_type(src_item, F_TRASH);
3069 if (!item || !src_item || !folder_is_normal) {
3070 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3074 folderview_move_folder(folderview, src_item, item, copy);
3075 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3077 folderview->nodes_to_recollapse = NULL;
3078 } else if (info == TARGET_MAIL_URI_LIST) {
3079 if (gtk_cmclist_get_selection_info
3080 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3083 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3085 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3086 debug_print("no node\n");
3089 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3091 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3092 debug_print("no item\n");
3095 folderview_finish_dnd(gtk_selection_data_get_data(data),
3096 drag_context, time, item);
3100 static void folderview_drag_end_cb(GtkWidget *widget,
3101 GdkDragContext *drag_context,
3102 FolderView *folderview)
3104 drag_state_stop(folderview);
3105 folderview->scroll_value = 0;
3106 g_slist_free(folderview->nodes_to_recollapse);
3107 folderview->nodes_to_recollapse = NULL;
3110 void folderview_register_popup(FolderViewPopup *fpopup)
3114 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3115 FolderView *folderview = folderviews->data;
3116 GtkActionGroup *factory;
3118 factory = create_action_group(folderview, fpopup);
3119 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3121 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3124 void folderview_unregister_popup(FolderViewPopup *fpopup)
3129 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3130 FolderView *folderview = folderviews->data;
3132 g_hash_table_remove(folderview->popups, fpopup->klass);
3134 g_hash_table_remove(folderview_popups, fpopup->klass);