2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_handler (GtkAction *action,
170 static void mark_all_read_cb (GtkAction *action,
172 static void mark_all_read_recursive_cb (GtkAction *action,
175 static void folderview_empty_trash_cb (GtkAction *action,
178 static void folderview_send_queue_cb (GtkAction *action,
181 static void folderview_search_cb (GtkAction *action,
183 static void folderview_run_processing_cb(GtkAction *action,
186 static void folderview_property_cb (GtkAction *action,
189 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
190 GdkDragContext *context,
194 FolderView *folderview);
195 static void folderview_drag_leave_cb (GtkWidget *widget,
196 GdkDragContext *context,
198 FolderView *folderview);
199 static void folderview_drag_received_cb (GtkWidget *widget,
200 GdkDragContext *drag_context,
203 GtkSelectionData *data,
206 FolderView *folderview);
208 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
209 FolderView *folderview);
211 static void folderview_drag_data_get (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 GtkSelectionData *selection_data,
216 FolderView *folderview);
217 static void folderview_drag_end_cb (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 FolderView *folderview);
221 static void folderview_create_folder_node (FolderView *folderview,
223 static gboolean folderview_update_folder (gpointer source,
225 static gboolean folderview_update_item_claws (gpointer source,
227 static void folderview_processing_cb(GtkAction *action, gpointer data);
228 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
229 GdkEventButton *event);
231 GHashTable *folderview_popups;
233 static GtkActionEntry folderview_common_popup_entries[] =
235 {"FolderViewPopup", NULL, "FolderViewPopup" },
236 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
237 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
238 {"FolderViewPopup/---", NULL, "---" },
239 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
240 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
241 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
242 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
243 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
244 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
248 GtkTargetEntry folderview_drag_types[] =
250 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
251 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
254 void folderview_initialize(void)
256 FolderViewPopup *fpopup;
258 fpopup = g_new0(FolderViewPopup, 1);
260 fpopup->klass = "common";
261 fpopup->path = "<CommonFolder>";
262 fpopup->entries = folderview_common_popup_entries;
263 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
264 fpopup->set_sensitivity = NULL;
266 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
267 g_hash_table_insert(folderview_popups, "common", fpopup);
270 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 FolderViewPopup *fpopup_common;
273 GtkActionGroup *action_group;
275 action_group = cm_menu_create_action_group(
277 fpopup->entries, fpopup->n_entries,
278 (gpointer)folderview);
280 if (fpopup->toggle_entries)
281 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
282 fpopup->n_toggle_entries,
283 (gpointer)folderview);
284 if (fpopup->radio_entries)
285 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
286 fpopup->n_radio_entries, fpopup->radio_default,
287 G_CALLBACK(fpopup->radio_callback),
288 (gpointer)folderview);
290 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
291 if (fpopup_common != fpopup) {
292 gtk_action_group_add_actions(action_group, fpopup_common->entries,
293 fpopup_common->n_entries,
294 (gpointer)folderview);
295 if (fpopup_common->toggle_entries)
296 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
297 fpopup_common->n_toggle_entries,
298 (gpointer)folderview);
299 if (fpopup_common->radio_entries)
300 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
301 fpopup_common->n_radio_entries, fpopup_common->radio_default,
302 G_CALLBACK(fpopup_common->radio_callback),
303 (gpointer)folderview);
309 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 FolderView *folderview = data;
312 FolderViewPopup *fpopup = value;
313 GtkActionGroup *group;
315 group = create_action_group(folderview, fpopup);
316 g_hash_table_insert(folderview->popups, fpopup->klass, group);
319 static void folderview_column_set_titles(FolderView *folderview)
321 GtkWidget *ctree = folderview->ctree;
322 GtkWidget *label_folder;
323 GtkWidget *label_new;
324 GtkWidget *label_unread;
325 GtkWidget *label_total;
326 GtkWidget *hbox_folder;
328 GtkWidget *hbox_unread;
329 GtkWidget *hbox_total;
330 gint *col_pos = folderview->col_pos;
332 debug_print("setting titles...\n");
333 gtk_widget_realize(folderview->ctree);
334 gtk_widget_show_all(folderview->scrolledwin);
336 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
337 * instead text (text overflows making them unreadable and ugly) */
338 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
339 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
340 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
342 label_folder = gtk_label_new(_("Folder"));
343 label_new = gtk_image_new_from_pixbuf(newxpm);
344 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
345 label_total = gtk_image_new_from_pixbuf(readxpm);
347 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
349 hbox_folder = gtk_hbox_new(FALSE, 4);
350 hbox_new = gtk_hbox_new(FALSE, 4);
351 hbox_unread = gtk_hbox_new(FALSE, 4);
352 hbox_total = gtk_hbox_new(FALSE, 4);
355 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
359 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
360 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
361 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
362 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
364 gtk_widget_show_all(hbox_folder);
365 gtk_widget_show_all(hbox_new);
366 gtk_widget_show_all(hbox_unread);
367 gtk_widget_show_all(hbox_total);
370 gtk_widget_set_size_request(hbox_new, -1, 20);
371 gtk_widget_set_size_request(hbox_unread, -1, 20);
372 gtk_widget_set_size_request(hbox_total, -1, 20);
375 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
376 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
377 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
384 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
385 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
386 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
389 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
391 FolderView *folderview = (FolderView *)data;
392 GdkEventButton event;
393 if (folderview_get_selected_item(folderview) == NULL)
397 event.time = gtk_get_current_event_time();
399 folderview_set_sens_and_popup_menu(folderview, -1,
406 static GtkWidget *folderview_ctree_create(FolderView *folderview)
410 FolderColumnState *col_state;
411 FolderColumnType type;
412 gchar *titles[N_FOLDER_COLS];
414 GtkWidget *scrolledwin = folderview->scrolledwin;
416 debug_print("creating tree...\n");
417 memset(titles, 0, sizeof(titles));
419 col_state = prefs_folder_column_get_config();
420 memset(titles, 0, sizeof(titles));
422 col_pos = folderview->col_pos;
424 for (i = 0; i < N_FOLDER_COLS; i++) {
425 folderview->col_state[i] = col_state[i];
426 type = col_state[i].type;
430 titles[col_pos[F_COL_FOLDER]] = _("Folder");
431 titles[col_pos[F_COL_NEW]] = _("New");
432 titles[col_pos[F_COL_UNREAD]] = _("Unread");
433 /* TRANSLATORS: This in Number sign in American style */
434 titles[col_pos[F_COL_TOTAL]] = _("#");
436 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
439 if (prefs_common.show_col_headers == FALSE)
440 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
443 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
444 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
446 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
447 col_pos[F_COL_UNREAD],
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
450 col_pos[F_COL_TOTAL],
452 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
453 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
454 GTK_CMCTREE_EXPANDER_TRIANGLE);
456 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
457 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
459 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
460 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
462 /* don't let title buttons take key focus */
463 for (i = 0; i < N_FOLDER_COLS; i++) {
464 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
465 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
466 prefs_common.folder_col_size[i]);
467 gtk_cmclist_set_column_visibility
468 (GTK_CMCLIST(ctree), i, col_state[i].visible);
471 g_signal_connect(G_OBJECT(ctree), "key_press_event",
472 G_CALLBACK(folderview_key_pressed),
474 g_signal_connect(G_OBJECT(ctree), "button_press_event",
475 G_CALLBACK(folderview_button_pressed),
477 g_signal_connect(G_OBJECT(ctree), "popup-menu",
478 G_CALLBACK(folderview_popup_menu), folderview);
479 g_signal_connect(G_OBJECT(ctree), "button_release_event",
480 G_CALLBACK(folderview_button_released),
482 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
483 G_CALLBACK(folderview_selected), folderview);
485 /* drag-n-dropping folders on maemo is impractical as this
486 * opens the folder almost everytime */
487 g_signal_connect(G_OBJECT(ctree), "start_drag",
488 G_CALLBACK(folderview_start_drag), folderview);
490 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
491 G_CALLBACK(folderview_drag_data_get),
494 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
495 G_CALLBACK(folderview_tree_expanded),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
498 G_CALLBACK(folderview_tree_collapsed),
501 g_signal_connect(G_OBJECT(ctree), "resize_column",
502 G_CALLBACK(folderview_col_resized),
506 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
507 folderview_drag_types, 2,
508 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
509 g_signal_connect(G_OBJECT(ctree), "drag_motion",
510 G_CALLBACK(folderview_drag_motion_cb),
512 g_signal_connect(G_OBJECT(ctree), "drag_leave",
513 G_CALLBACK(folderview_drag_leave_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
516 G_CALLBACK(folderview_drag_received_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_end",
519 G_CALLBACK(folderview_drag_end_cb),
522 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
527 void folderview_set_column_order(FolderView *folderview)
529 GtkWidget *ctree = folderview->ctree;
530 FolderItem *item = folderview_get_selected_item(folderview);
531 FolderItem *sel_item = NULL, *op_item = NULL;
532 GtkWidget *scrolledwin = folderview->scrolledwin;
534 if (folderview->drag_timer_id != 0) {
535 g_source_remove(folderview->drag_timer_id);
536 folderview->drag_timer_id = 0;
538 if (folderview->deferred_refresh_id != 0) {
539 g_source_remove(folderview->deferred_refresh_id);
540 folderview->deferred_refresh_id = 0;
542 if (folderview->scroll_timeout_id != 0) {
543 g_source_remove(folderview->scroll_timeout_id);
544 folderview->scroll_timeout_id = 0;
546 if (folderview->postpone_select_id != 0) {
547 g_source_remove(folderview->postpone_select_id);
548 folderview->postpone_select_id = 0;
551 if (folderview->selected)
552 sel_item = folderview_get_selected_item(folderview);
553 if (folderview->opened)
554 op_item = folderview_get_opened_item(folderview);
556 debug_print("recreating tree...\n");
557 gtk_widget_destroy(folderview->ctree);
560 folderview->ctree = ctree = folderview_ctree_create(folderview);
561 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
562 GTK_CMCLIST(ctree)->hadjustment);
563 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
564 GTK_CMCLIST(ctree)->vadjustment);
565 gtk_widget_show(ctree);
568 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
570 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
572 folderview_set(folderview);
573 folderview_column_set_titles(folderview);
575 folderview_select(folderview,item);
578 FolderView *folderview_create(void)
580 FolderView *folderview;
581 GtkWidget *scrolledwin;
584 debug_print("Creating folder view...\n");
585 folderview = g_new0(FolderView, 1);
587 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
588 gtk_scrolled_window_set_policy
589 (GTK_SCROLLED_WINDOW(scrolledwin),
590 GTK_POLICY_AUTOMATIC,
591 prefs_common.folderview_vscrollbar_policy);
592 gtk_widget_set_size_request(scrolledwin,
593 prefs_common.folderview_width,
594 prefs_common.folderview_height);
596 folderview->scrolledwin = scrolledwin;
597 ctree = folderview_ctree_create(folderview);
599 /* create popup factories */
600 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
601 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
603 folderview->ctree = ctree;
605 folderview->folder_update_callback_id =
606 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
607 folderview->folder_item_update_callback_id =
608 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
610 gtk_widget_show_all(scrolledwin);
612 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
613 folderview_list = g_list_append(folderview_list, folderview);
615 folderview->drag_timer_id = 0;
616 folderview->deferred_refresh_id = 0;
617 folderview->scroll_timeout_id = 0;
618 folderview->postpone_select_id = 0;
623 void folderview_init(FolderView *folderview)
625 GtkWidget *ctree = folderview->ctree;
627 PangoFontDescription *normal_font;
629 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
630 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
631 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
632 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
633 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
634 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
635 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
636 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
637 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
638 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
639 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
640 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
641 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
642 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
653 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
659 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
666 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
667 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
668 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
676 normal_font = pango_font_description_from_string(NORMAL_FONT);
678 gtk_widget_modify_font(ctree, normal_font);
679 pango_font_description_free(normal_font);
681 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
684 PangoFontDescription *font_desc;
685 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
686 font_desc = pango_font_description_from_string(NORMAL_FONT);
688 if (normal_style->font_desc)
689 pango_font_description_free
690 (normal_style->font_desc);
691 normal_style->font_desc = font_desc;
693 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
694 normal_color_style = gtk_style_copy(normal_style);
695 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
699 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
700 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
701 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
702 PangoFontDescription *font_desc;
703 font_desc = pango_font_description_from_string(NORMAL_FONT);
705 pango_font_description_free(bold_style->font_desc);
706 bold_style->font_desc = font_desc;
708 pango_font_description_set_weight
709 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
711 PangoFontDescription *font_desc;
712 font_desc = pango_font_description_from_string(BOLD_FONT);
714 if (bold_style->font_desc)
715 pango_font_description_free
716 (bold_style->font_desc);
717 bold_style->font_desc = font_desc;
720 bold_color_style = gtk_style_copy(bold_style);
721 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
723 bold_tgtfold_style = gtk_style_copy(bold_style);
724 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
728 static gboolean folderview_defer_set(gpointer data)
730 FolderView *folderview = (FolderView *)data;
731 MainWindow *mainwin = folderview->mainwin;
735 if (mainwin->lock_count)
738 debug_print("doing deferred folderview_set now\n");
739 folderview_set(folderview);
741 folderview->deferred_refresh_id = 0;
745 void folderview_set(FolderView *folderview)
747 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
748 MainWindow *mainwin = folderview->mainwin;
749 FolderItem *sel_item = NULL, *op_item = NULL;
754 if (mainwin->lock_count) {
755 if (folderview->deferred_refresh_id == 0)
756 folderview->deferred_refresh_id =
757 g_timeout_add(500, folderview_defer_set, folderview);
758 debug_print("deferred folderview_set\n");
763 debug_print("Setting folder info...\n");
764 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
766 main_window_cursor_wait(mainwin);
768 if (folderview->selected)
769 sel_item = folderview_get_selected_item(folderview);
770 if (folderview->opened)
771 op_item = folderview_get_opened_item(folderview);
773 folderview->selected = NULL;
774 folderview->opened = NULL;
776 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
777 gtk_cmclist_clear(GTK_CMCLIST(ctree));
779 folderview_set_folders(folderview);
782 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
784 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
786 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
787 main_window_cursor_normal(mainwin);
788 STATUSBAR_POP(mainwin);
792 void folderview_set_all(void)
796 for (list = folderview_list; list != NULL; list = list->next)
797 folderview_set((FolderView *)list->data);
800 void folderview_select(FolderView *folderview, FolderItem *item)
802 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
803 GtkCMCTreeNode *node;
804 GtkCMCTreeNode *old_selected = folderview->selected;
808 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
809 if (node) folderview_select_node(folderview, node);
811 if (old_selected != node)
812 folder_update_op_count();
815 static void mark_all_read_cb(GtkAction *action, gpointer data)
817 mark_all_read_handler(action, data, FALSE);
820 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
822 mark_all_read_handler(action, data, TRUE);
825 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
827 FolderView *folderview = (FolderView *)data;
832 item = folderview_get_selected_item(folderview);
836 message = recursive? _("Do you really want to mark all mails in this "
837 "folder and its sub-folders as read?") :
838 _("Do you really want to mark all mails in this "
840 if (folderview->summaryview->folder_item != item &&
841 prefs_common.ask_mark_all_read) {
842 val = alertpanel_full(_("Mark all as read"),
843 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
844 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
846 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
848 else if (val & G_ALERTDISABLE)
849 prefs_common.ask_mark_all_read = FALSE;
852 folder_item_update_freeze();
853 if (folderview->summaryview->folder_item != item && !recursive)
854 summary_lock(folderview->summaryview);
856 summary_freeze(folderview->summaryview);
859 folderutils_mark_all_read_recursive(item);
861 folderutils_mark_all_read(item);
863 if (folderview->summaryview->folder_item != item && !recursive)
864 summary_unlock(folderview->summaryview);
866 summary_thaw(folderview->summaryview);
867 folder_item_update_thaw();
870 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
872 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
874 cm_return_if_fail(node != NULL);
876 if (folderview->open_folder) {
880 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
881 gtkut_ctree_expand_parent_all(ctree, node);
883 folderview->open_folder = TRUE;
884 gtkut_ctree_set_focus_row(ctree, node);
885 gtk_cmctree_select(ctree, node);
886 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
887 if ((folderview->summaryview->folder_item &&
888 folderview->summaryview->folder_item->total_msgs > 0) ||
889 prefs_common.layout_mode == SMALL_LAYOUT)
890 summary_grab_focus(folderview->summaryview);
892 gtk_widget_grab_focus(folderview->ctree);
895 void folderview_unselect(FolderView *folderview)
897 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
899 (GTK_CMCTREE(folderview->ctree), folderview->opened);
901 folderview->selected = folderview->opened = NULL;
904 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
905 GtkCMCTreeNode *node,
911 node = gtkut_ctree_node_next(ctree, node);
913 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
915 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
916 item = gtk_cmctree_node_get_row_data(ctree, node);
919 if (item->stype == F_TRASH || item->stype == F_DRAFT)
923 if(item->unread_msgs > 0)
927 if(item->new_msgs > 0)
931 if(item->marked_msgs > 0)
940 void folderview_select_next_with_flag(FolderView *folderview,
944 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
945 GtkCMCTreeNode *node = NULL;
946 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
947 gboolean last_open = prefs_common.always_show_msg;
951 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
954 prefs_common.summary_select_prio[0] = ACTION_NEW;
957 prefs_common.summary_select_prio[0] = ACTION_MARKED;
960 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
962 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
964 folderview_select_node(folderview, node);
968 if (!folderview->opened ||
969 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
973 /* search again from the first node */
974 node = folderview_find_next_with_flag(ctree, NULL, flag);
976 folderview_select_node(folderview, node);
979 prefs_common.summary_select_prio[0] = last_summary_select_prio;
980 prefs_common.always_show_msg = last_open;
983 FolderItem *folderview_get_selected_item(FolderView *folderview)
985 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
987 if (!folderview->selected) return NULL;
988 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
991 FolderItem *folderview_get_opened_item(FolderView *folderview)
993 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
995 if (!folderview->opened) return NULL;
996 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
999 static void folderview_set_folders(FolderView *folderview)
1002 list = folder_get_list();
1004 for (; list != NULL; list = list->next) {
1005 folderview_append_folder(folderview, FOLDER(list->data));
1009 static gchar *get_scan_str(FolderItem *item)
1012 return g_strdup_printf(_("Scanning folder %s/%s..."),
1013 item->folder->name, item->path);
1015 return g_strdup_printf(_("Scanning folder %s..."),
1016 item->folder->name);
1018 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1022 for (list = folderview_list; list != NULL; list = list->next) {
1023 FolderView *folderview = (FolderView *)list->data;
1024 MainWindow *mainwin = folderview->mainwin;
1025 gchar *str = get_scan_str(item);
1027 STATUSBAR_PUSH(mainwin, str);
1028 STATUSBAR_POP(mainwin);
1033 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1036 MainWindow *mainwin = mainwindow_get_mainwindow();
1037 FolderView *folderview = NULL;
1038 GtkAdjustment *pos = NULL;
1041 cm_return_if_fail(folder != NULL);
1043 if (!folder->klass->scan_tree) return;
1046 alertpanel_full(_("Rebuild folder tree"),
1047 _("Rebuilding the folder tree will remove "
1048 "local caches. Do you want to continue?"),
1049 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1050 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1051 != G_ALERTALTERNATE) {
1057 window = label_window_create(_("Rebuilding folder tree..."));
1059 window = label_window_create(_("Scanning folder tree..."));
1062 folderview = mainwin->folderview;
1065 pos = gtk_scrolled_window_get_vadjustment(
1066 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1067 height = gtk_adjustment_get_value(pos);
1070 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1071 folder_scan_tree(folder, rebuild);
1072 folder_set_ui_func(folder, NULL, NULL);
1074 folderview_set_all();
1077 pos = gtk_scrolled_window_get_vadjustment(
1078 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1079 gtk_adjustment_set_value(pos, height);
1080 gtk_adjustment_changed(pos);
1082 label_window_destroy(window);
1086 /** folderview_check_new()
1087 * Scan and update the folder and return the
1088 * count the number of new messages since last check.
1089 * \param folder the folder to check for new messages
1090 * \return the number of new messages since last check
1092 gint folderview_check_new(Folder *folder)
1096 FolderView *folderview;
1098 GtkCMCTreeNode *node;
1100 gint former_new_msgs = 0;
1101 gint former_new = 0, former_unread = 0, former_total;
1103 for (list = folderview_list; list != NULL; list = list->next) {
1104 folderview = (FolderView *)list->data;
1105 ctree = GTK_CMCTREE(folderview->ctree);
1106 folderview->scanning_folder = folder;
1108 main_window_lock(folderview->mainwin);
1110 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1111 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1113 item = gtk_cmctree_node_get_row_data(ctree, node);
1114 if (!item || !item->path || !item->folder) continue;
1115 if (item->no_select) continue;
1116 if (folder && folder != item->folder) continue;
1117 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1118 if (!item->prefs->newmailcheck) continue;
1119 if (item->processing_pending == TRUE) {
1120 debug_print("skipping %s, processing pending\n",
1121 item->path ? item->path : item->name);
1124 if (item->scanning != ITEM_NOT_SCANNING) {
1125 debug_print("skipping %s, scanning\n",
1126 item->path ? item->path : item->name);
1130 str = get_scan_str(item);
1132 STATUSBAR_PUSH(folderview->mainwin, str);
1136 folderview_scan_tree_func(item->folder, item, NULL);
1137 former_new = item->new_msgs;
1138 former_unread = item->unread_msgs;
1139 former_total = item->total_msgs;
1141 if (item->folder->klass->scan_required &&
1142 (item->folder->klass->scan_required(item->folder, item) ||
1143 item->folder->inbox == item ||
1144 item->opened == TRUE ||
1145 item->processing_pending == TRUE)) {
1146 if (folder_item_scan(item) < 0) {
1148 summaryview_unlock(folderview->summaryview, item);
1149 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1150 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1151 item->path ? item->path:item->name);
1152 STATUSBAR_POP(folderview->mainwin);
1154 } else if (!FOLDER_IS_LOCAL(folder)) {
1155 STATUSBAR_POP(folderview->mainwin);
1160 } else if (!item->folder->klass->scan_required) {
1161 if (folder_item_scan(item) < 0) {
1162 summaryview_unlock(folderview->summaryview, item);
1163 if (folder && !FOLDER_IS_LOCAL(folder)) {
1164 STATUSBAR_POP(folderview->mainwin);
1169 if (former_new != item->new_msgs ||
1170 former_unread != item->unread_msgs ||
1171 former_total != item->total_msgs)
1172 folderview_update_node(folderview, node);
1174 new_msgs += item->new_msgs;
1175 former_new_msgs += former_new;
1176 STATUSBAR_POP(folderview->mainwin);
1178 folderview->scanning_folder = NULL;
1179 main_window_unlock(folderview->mainwin);
1183 folder_write_list();
1184 /* Number of new messages since last check is the just the difference
1185 * between former_new_msgs and new_msgs. If new_msgs is less than
1186 * former_new_msgs, that would mean another session accessed the folder
1187 * and the result is not well defined.
1189 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1193 void folderview_check_new_all(void)
1197 FolderView *folderview;
1199 folderview = (FolderView *)folderview_list->data;
1202 main_window_lock(folderview->mainwin);
1203 window = label_window_create
1204 (_("Checking for new messages in all folders..."));
1206 list = folder_get_list();
1207 for (; list != NULL; list = list->next) {
1208 Folder *folder = list->data;
1210 folderview_check_new(folder);
1213 folder_write_list();
1214 folderview_set_all();
1216 label_window_destroy(window);
1217 main_window_unlock(folderview->mainwin);
1221 static gboolean folderview_have_children_sub(FolderView *folderview,
1227 if (!item || !item->folder || !item->folder->node)
1230 node = item->folder->node;
1232 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1233 node = node->children;
1235 if (in_sub && item->total_msgs > 0) {
1239 while (node != NULL) {
1240 if (node && node->data) {
1241 FolderItem *next_item = (FolderItem*) node->data;
1243 if (folderview_have_children_sub(folderview,
1252 static gboolean folderview_have_children(FolderView *folderview,
1255 return folderview_have_children_sub(folderview, item, FALSE);
1258 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1264 if (!item || !item->folder || !item->folder->node)
1267 node = item->folder->node;
1269 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1270 node = node->children;
1273 (item->new_msgs > 0 ||
1274 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1278 while (node != NULL) {
1279 if (node && node->data) {
1280 FolderItem *next_item = (FolderItem*) node->data;
1282 if (folderview_have_new_children_sub(folderview,
1291 static gboolean folderview_have_new_children(FolderView *folderview,
1294 return folderview_have_new_children_sub(folderview, item, FALSE);
1297 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1303 if (!item || !item->folder || !item->folder->node)
1306 node = item->folder->node;
1308 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1309 node = node->children;
1312 (item->unread_msgs > 0 ||
1313 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1317 while (node != NULL) {
1318 if (node && node->data) {
1319 FolderItem *next_item = (FolderItem*) node->data;
1321 if (folderview_have_unread_children_sub(folderview,
1331 static gboolean folderview_have_unread_children(FolderView *folderview,
1334 return folderview_have_unread_children_sub(folderview, item, FALSE);
1337 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1343 if (!item || !item->folder || !item->folder->node)
1346 node = item->folder->node;
1348 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1349 node = node->children;
1351 if (in_sub && item->search_match){
1355 while (node != NULL) {
1356 if (node && node->data) {
1357 FolderItem *next_item = (FolderItem*) node->data;
1359 if (folderview_have_matching_children_sub(folderview,
1369 static gboolean folderview_have_matching_children(FolderView *folderview,
1372 return folderview_have_matching_children_sub(folderview, item, FALSE);
1375 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1381 if (!item || !item->folder || !item->folder->node)
1384 node = item->folder->node;
1386 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1387 node = node->children;
1389 if (item->marked_msgs != 0) {
1393 while (node != NULL) {
1394 if (node && node->data) {
1395 FolderItem *next_item = (FolderItem*) node->data;
1397 if (folderview_have_marked_children_sub(folderview,
1406 static gboolean folderview_have_marked_children(FolderView *folderview,
1409 return folderview_have_marked_children_sub(folderview, item, FALSE);
1412 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1414 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1415 GtkStyle *style = NULL;
1416 GtkStyle *color_style = NULL;
1418 GdkPixbuf *xpm, *openxpm;
1419 static GdkPixbuf *searchicon;
1420 gboolean mark = FALSE;
1423 gboolean add_unread_mark;
1424 gboolean add_sub_match_mark;
1425 gboolean use_bold, use_color;
1426 gint *col_pos = folderview->col_pos;
1427 SpecialFolderItemType stype;
1429 item = gtk_cmctree_node_get_row_data(ctree, node);
1430 cm_return_if_fail(item != NULL);
1432 if (!GTK_CMCTREE_ROW(node)->expanded)
1433 mark = folderview_have_marked_children(folderview, item);
1435 mark = (item->marked_msgs != 0);
1437 stype = item->stype;
1438 if (stype == F_NORMAL) {
1439 if (folder_has_parent_of_type(item, F_TRASH))
1441 else if (folder_has_parent_of_type(item, F_DRAFT))
1443 else if (folder_has_parent_of_type(item, F_OUTBOX))
1445 else if (folder_has_parent_of_type(item, F_QUEUE))
1450 if (item->hide_read_msgs || item->hide_read_threads) {
1451 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1452 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1454 xpm = mark?m_inboxxpm:inboxxpm;
1455 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1459 if (item->hide_read_msgs || item->hide_read_threads) {
1460 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1461 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1463 xpm = mark?m_outboxxpm:outboxxpm;
1464 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1468 if (item->hide_read_msgs || item->hide_read_threads) {
1469 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1470 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1472 xpm = mark?m_queuexpm:queuexpm;
1473 openxpm = mark?m_queueopenxpm:queueopenxpm;
1477 if (item->hide_read_msgs || item->hide_read_threads) {
1478 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1479 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1481 xpm = mark?m_trashxpm:trashxpm;
1482 openxpm = mark?m_trashopenxpm:trashopenxpm;
1486 xpm = mark?m_draftsxpm:draftsxpm;
1487 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1490 if (item->hide_read_msgs || item->hide_read_threads) {
1491 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1492 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1494 xpm = mark?m_folderxpm:folderxpm;
1495 openxpm = mark?m_folderopenxpm:folderopenxpm;
1499 if (item->no_select) {
1500 xpm = openxpm = noselectxpm;
1503 name = folder_item_get_name(item);
1505 if (!GTK_CMCTREE_ROW(node)->expanded) {
1506 add_unread_mark = folderview_have_unread_children(
1508 add_sub_match_mark = folderview_have_matching_children(
1511 add_unread_mark = FALSE;
1512 add_sub_match_mark = FALSE;
1515 if (item->search_match) {
1517 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1520 xpm = openxpm = searchicon;
1524 if (prefs_common.display_folder_unread) {
1525 if (folder_has_parent_of_type(item, F_QUEUE)) {
1526 /* only total_msgs matters here */
1527 if (item->total_msgs > 0) {
1528 /* show total number (should be equal to the unread number)
1530 str = g_strdup_printf("%s (%d%s%s)",
1531 name, item->total_msgs,
1532 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1533 (item->unreadmarked_msgs > 0) ? "!" : "");
1536 if (prefs_common.display_folder_unread == 1) {
1537 if (item->unread_msgs > 0) {
1538 /* show unread number and signs */
1539 str = g_strdup_printf("%s (%d%s%s)",
1540 name, item->unread_msgs,
1541 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1542 (item->unreadmarked_msgs > 0) ? "!" : "");
1545 if (item->total_msgs > 0) {
1546 /* show unread number, total number and signs if any */
1547 str = g_strdup_printf("%s (%d/%d%s%s)",
1548 name, item->unread_msgs, item->total_msgs,
1549 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1550 (item->unreadmarked_msgs > 0) ? "!" : "");
1554 if ((str == NULL) &&
1555 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1556 /* no unread/total numbers, but at least one sign */
1557 str = g_strdup_printf("%s (%s%s)",
1559 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1560 (item->unreadmarked_msgs > 0) ? "!" : "");
1564 /* last fallback, folder name only or with +! sign */
1565 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1566 str = g_strdup_printf("%s%s",
1568 } else if (item->unreadmarked_msgs > 0) {
1569 str = g_strdup_printf("%s%s",
1571 } else if (add_sub_match_mark) {
1572 str = g_strdup_printf("%s%s",
1575 str = g_strdup_printf("%s", name);
1578 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1580 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1584 if (!folder_item_parent(item)) {
1585 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1586 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1587 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1589 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1590 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1591 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1594 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1595 folder_has_parent_of_type(item, F_TRASH)) {
1596 use_bold = use_color = FALSE;
1597 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1598 GSList *list = folder_item_get_msg_list(item);
1600 use_bold = use_color = FALSE;
1601 for (cur = list; cur; cur = cur->next) {
1602 MsgInfo *msginfo = (MsgInfo *)cur->data;
1603 if (!MSG_IS_DELETED(msginfo->flags)) {
1604 /* highlight queue folder if there are any messages */
1605 use_bold = use_color = TRUE;
1609 if (!GTK_CMCTREE_ROW(node)->expanded &&
1610 use_bold == FALSE &&
1611 folderview_have_children(folderview, item))
1612 use_bold = use_color = TRUE;
1613 procmsg_msg_list_free(list);
1615 /* if unread messages exist, print with bold font */
1616 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1618 /* if new messages exist, print with colored letter */
1620 (item->new_msgs > 0) ||
1622 folderview_have_new_children(folderview, item));
1625 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1630 if (item->prefs->color > 0 && !use_color) {
1631 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1632 color_style = gtk_style_copy(bold_style);
1633 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1634 style = color_style;
1635 } else if (use_color) {
1636 style = bold_color_style;
1639 if (item->op_count > 0) {
1640 style = bold_tgtfold_style;
1642 } else if (use_color) {
1643 style = normal_color_style;
1644 gtk_cmctree_node_set_foreground(ctree, node,
1645 &folderview->color_new);
1646 } else if (item->op_count > 0) {
1647 style = bold_tgtfold_style;
1648 } else if (item->prefs->color > 0) {
1650 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1651 color_style = gtk_style_copy(normal_style);
1652 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1653 style = color_style;
1655 style = normal_style;
1658 gtk_cmctree_node_set_row_style(ctree, node, style);
1660 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1661 folderview_update_node(folderview, node);
1664 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1667 FolderView *folderview;
1669 GtkCMCTreeNode *node;
1671 cm_return_if_fail(item != NULL);
1673 for (list = folderview_list; list != NULL; list = list->next) {
1674 folderview = (FolderView *)list->data;
1675 ctree = GTK_CMCTREE(folderview->ctree);
1677 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1678 if (node && item->search_match != matches) {
1679 item->search_match = matches;
1680 folderview_update_node(folderview, node);
1685 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1687 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1688 FolderView *folderview = (FolderView *)data;
1690 GtkCMCTreeNode *node;
1691 cm_return_val_if_fail(update_info != NULL, TRUE);
1692 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1693 cm_return_val_if_fail(folderview != NULL, FALSE);
1695 ctree = GTK_CMCTREE(folderview->ctree);
1697 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1700 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1701 folderview_update_node(folderview, node);
1703 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1704 update_info->item == folderview->summaryview->folder_item &&
1705 update_info->item != NULL)
1706 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1707 summary_show(folderview->summaryview, update_info->item);
1713 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1714 GNode *gnode, GtkCMCTreeNode *cnode,
1717 FolderView *folderview = (FolderView *)data;
1718 FolderItem *item = FOLDER_ITEM(gnode->data);
1720 cm_return_val_if_fail(item != NULL, FALSE);
1722 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1723 folderview_update_node(folderview, cnode);
1728 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1731 FolderView *folderview = (FolderView *)data;
1734 if (GTK_CMCTREE_ROW(node)->children) {
1735 item = gtk_cmctree_node_get_row_data(ctree, node);
1736 cm_return_if_fail(item != NULL);
1738 if (!item->collapsed)
1739 gtk_cmctree_expand(ctree, node);
1741 folderview_update_node(folderview, node);
1745 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1746 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1749 GtkCMCTreeNode *node, *parent, *sibling;
1751 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1753 g_warning("%s not found.", item->path);
1755 parent = GTK_CMCTREE_ROW(node)->parent;
1756 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1757 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1759 sibling = GTK_CMCTREE_ROW(parent)->children;
1763 tmp = gtk_cmctree_node_get_row_data
1765 if (tmp && tmp->stype != F_NORMAL)
1766 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1770 if (node != sibling)
1771 gtk_cmctree_move(ctree, node, parent, sibling);
1778 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1781 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1782 GtkCMCTreeNode *prev = NULL;
1784 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1785 gtk_sctree_sort_recursive(ctree, root);
1786 if (root && GTK_CMCTREE_ROW(root)->parent) {
1787 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1790 set_special_folder(ctree, folder->inbox, root, &prev);
1791 set_special_folder(ctree, folder->outbox, root, &prev);
1792 set_special_folder(ctree, folder->draft, root, &prev);
1793 set_special_folder(ctree, folder->queue, root, &prev);
1794 set_special_folder(ctree, folder->trash, root, &prev);
1795 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1798 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1800 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1801 GtkCMCTreeNode *root;
1803 cm_return_if_fail(folder != NULL);
1805 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1806 folderview_gnode_func, folderview);
1807 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1809 folderview_sort_folders(folderview, root, folder);
1812 /* callback functions */
1813 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1814 GdkEventButton *event)
1818 FolderViewPopup *fpopup;
1819 GtkActionGroup *action_group;
1821 FolderItem *special_trash = NULL, *special_queue = NULL;
1823 GtkUIManager *ui_manager = gtk_ui_manager_new();
1825 if (folderview->ui_manager)
1826 g_object_unref(folderview->ui_manager);
1828 folderview->ui_manager = ui_manager;
1829 item = folderview_get_selected_item(folderview);
1831 cm_return_if_fail(item != NULL);
1832 cm_return_if_fail(item->folder != NULL);
1833 folder = item->folder;
1835 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1838 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1840 fpopup = g_hash_table_lookup(folderview_popups, "common");
1841 action_group = g_hash_table_lookup(folderview->popups, "common");
1844 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1845 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1846 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1848 if (fpopup->add_menuitems)
1849 fpopup->add_menuitems(ui_manager, item);
1851 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1855 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1856 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1857 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1859 if (fpopup->set_sensitivity != NULL)
1860 fpopup->set_sensitivity(ui_manager, item);
1862 if (NULL != (ac = account_find_from_item(item))) {
1863 special_trash = account_get_special_folder(ac, F_TRASH);
1864 special_queue = account_get_special_folder(ac, F_QUEUE);
1867 if ((item == folder->trash || item == special_trash
1868 || folder_has_parent_of_type(item, F_TRASH))) {
1869 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1870 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1873 if ((item == folder->queue || item == special_queue
1874 || folder_has_parent_of_type(item, F_QUEUE))) {
1875 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1876 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1879 #define SET_SENS(name, sens) \
1880 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1882 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1883 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1884 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1885 folderview->selected == folderview->opened);
1886 SET_SENS("FolderViewPopup/Properties", TRUE);
1888 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1889 item->total_msgs >= 1 && !item->processing_pending);
1890 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1891 !item->no_select && !item->processing_pending);
1893 if (item == folder->trash || item == special_trash
1894 || folder_has_parent_of_type(item, F_TRASH)) {
1895 GSList *msglist = folder_item_get_msg_list(item);
1896 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1897 procmsg_msg_list_free(msglist);
1899 if (item == folder->queue || item == special_queue
1900 || folder_has_parent_of_type(item, F_QUEUE)) {
1901 GSList *msglist = folder_item_get_msg_list(item);
1902 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1903 procmsg_msg_list_free(msglist);
1907 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1908 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1909 g_signal_connect(G_OBJECT(popup), "selection_done",
1910 G_CALLBACK(folderview_popup_close),
1912 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1913 event->button, event->time);
1916 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1917 FolderView *folderview)
1919 GtkCMCList *clist = GTK_CMCLIST(ctree);
1920 gint prev_row = -1, row = -1, column = -1;
1922 if (!event) return FALSE;
1924 if (event->button == 1 || event->button == 2) {
1925 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1926 folderview->open_folder = TRUE;
1928 if (event->type == GDK_2BUTTON_PRESS) {
1929 if (clist->selection) {
1930 GtkCMCTreeNode *node;
1932 node = GTK_CMCTREE_NODE(clist->selection->data);
1934 gtk_cmctree_toggle_expansion(
1937 folderview->open_folder = FALSE;
1944 if (event->button == 2 || event->button == 3) {
1946 if (clist->selection) {
1947 GtkCMCTreeNode *node;
1949 node = GTK_CMCTREE_NODE(clist->selection->data);
1951 prev_row = gtkut_ctree_get_nth_from_node
1952 (GTK_CMCTREE(ctree), node);
1955 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1958 if (prev_row != row) {
1959 gtk_cmclist_unselect_all(clist);
1960 if (event->button == 2)
1961 folderview_select_node
1963 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1966 gtk_cmclist_select_row(clist, row, column);
1970 if (event->button != 3) return FALSE;
1972 folderview_set_sens_and_popup_menu(folderview, row, event);
1976 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1977 FolderView *folderview)
1979 int row = -1, column = -1;
1981 if (!event) return FALSE;
1983 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1986 if (event->button == 1 && folderview->open_folder == FALSE &&
1987 folderview->opened != NULL) {
1988 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1989 folderview->opened);
1990 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1996 #define BREAK_ON_MODIFIER_KEY() \
1997 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1999 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2000 FolderView *folderview)
2002 GtkCMCTreeNode *node;
2005 if (!event) return FALSE;
2007 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2010 switch (event->keyval) {
2012 if (folderview->selected) {
2013 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2014 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2015 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2016 folderview->selected);
2018 folderview_select_node(folderview,
2019 folderview->selected);
2023 case GDK_KEY_Return:
2024 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2025 gtk_cmctree_toggle_expansion(
2026 GTK_CMCTREE(folderview->ctree),
2027 folderview->selected);
2031 case GDK_KEY_Return:
2032 case GDK_KEY_KP_Enter:
2033 if (folderview->selected)
2034 folderview_select_node(folderview, folderview->selected);
2038 BREAK_ON_MODIFIER_KEY();
2039 if (folderview->selected) {
2040 if (folderview->opened == folderview->selected &&
2041 (!folderview->summaryview->folder_item ||
2042 folderview->summaryview->folder_item->total_msgs == 0))
2043 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2045 folderview_select_node(folderview,
2046 folderview->selected);
2050 if (folderview->selected) {
2051 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2052 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2053 folderview->selected);
2055 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2056 folderview->selected))) {
2057 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2058 NULL, folder_item_parent(item)))) {
2059 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2060 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2061 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2070 if (event->keyval == GDK_KEY_Home)
2071 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2073 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2074 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2076 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2078 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2079 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2089 typedef struct _PostponedSelectData
2092 GtkCMCTreeNode *row;
2094 FolderView *folderview;
2095 } PostponedSelectData;
2097 static gboolean postpone_select(void *data)
2099 PostponedSelectData *psdata = (PostponedSelectData *)data;
2100 debug_print("trying again\n");
2102 psdata->folderview->postpone_select_id = 0;
2103 psdata->folderview->open_folder = TRUE;
2104 main_window_cursor_normal(psdata->folderview->mainwin);
2105 STATUSBAR_POP(psdata->folderview->mainwin);
2106 folderview_selected(psdata->ctree, psdata->row,
2107 psdata->column, psdata->folderview);
2112 void folderview_close_opened(FolderView *folderview)
2114 if (folderview->opened) {
2115 FolderItem *olditem;
2117 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2118 folderview->opened);
2120 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2121 olditem->path ? olditem->path:olditem->name);
2122 /* will be null if we just moved the previously opened folder */
2123 STATUSBAR_PUSH(folderview->mainwin, buf);
2124 main_window_cursor_wait(folderview->mainwin);
2126 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2127 summary_show(folderview->summaryview, NULL);
2128 folder_item_close(olditem);
2129 main_window_cursor_normal(folderview->mainwin);
2130 STATUSBAR_POP(folderview->mainwin);
2131 if (olditem->folder->klass->item_closed)
2132 olditem->folder->klass->item_closed(olditem);
2137 if (folderview->opened &&
2138 !GTK_CMCTREE_ROW(folderview->opened)->children)
2139 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2141 folderview->opened = NULL;
2143 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2144 gint column, FolderView *folderview)
2146 static gboolean can_select = TRUE; /* exclusive lock */
2151 GtkCMCTreeNode *old_opened = folderview->opened;
2153 folderview->selected = row;
2155 debug_print("newly selected %p, opened %p\n", folderview->selected,
2156 folderview->opened);
2157 if (folderview->opened == row) {
2158 folderview->open_folder = FALSE;
2163 item = gtk_cmctree_node_get_row_data(ctree, row);
2166 folderview->open_folder = FALSE;
2170 if (!can_select || summary_is_locked(folderview->summaryview)) {
2171 if (folderview->opened) {
2172 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2173 gtk_cmctree_select(ctree, folderview->opened);
2175 folderview->open_folder = FALSE;
2180 if (!folderview->open_folder) {
2187 /* Save cache for old folder */
2188 /* We don't want to lose all caches if sylpheed crashed */
2189 /* resets folderview->opened to NULL */
2190 folderview_close_opened(folderview);
2192 /* CLAWS: set compose button type: news folder items
2193 * always have a news folder as parent */
2195 toolbar_set_compose_button
2196 (folderview->mainwin->toolbar,
2197 FOLDER_TYPE(item->folder) == F_NEWS ?
2198 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2201 debug_print("Folder %s is selected\n", item->path);
2203 if (!GTK_CMCTREE_ROW(row)->children)
2204 gtk_cmctree_expand(ctree, row);
2206 /* ungrab the mouse event */
2207 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2208 gtk_grab_remove(GTK_WIDGET(ctree));
2209 if (gdk_pointer_is_grabbed())
2210 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2214 /* TODO: wwp: avoid displaying (null) in the status bar */
2215 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2216 item->path : "(null)");
2217 debug_print("%s\n", buf);
2218 STATUSBAR_PUSH(folderview->mainwin, buf);
2221 main_window_cursor_wait(folderview->mainwin);
2223 if (folderview->scanning_folder == item->folder) {
2226 res = folder_item_open(item);
2229 if (res == -1 && item->no_select == FALSE) {
2230 main_window_cursor_normal(folderview->mainwin);
2231 STATUSBAR_POP(folderview->mainwin);
2233 alertpanel_error(_("Folder could not be opened."));
2235 folderview->open_folder = FALSE;
2239 } else if (res == -2 && item->no_select == FALSE) {
2240 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2241 data->ctree = ctree;
2243 data->column = column;
2244 data->folderview = folderview;
2245 debug_print("postponing open of %s till end of scan\n",
2246 item->path ? item->path:item->name);
2247 folderview->open_folder = FALSE;
2249 if (folderview->postpone_select_id != 0)
2250 g_source_remove(folderview->postpone_select_id);
2251 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2256 main_window_cursor_normal(folderview->mainwin);
2259 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2260 opened = summary_show(folderview->summaryview, item);
2262 folder_clean_cache_memory(item);
2265 gtkut_ctree_set_focus_row(ctree, old_opened);
2266 gtk_cmctree_select(ctree, old_opened);
2267 folderview->opened = old_opened;
2269 folderview->opened = row;
2270 if (gtk_cmctree_node_is_visible(ctree, row)
2271 != GTK_VISIBILITY_FULL)
2272 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2275 STATUSBAR_POP(folderview->mainwin);
2277 folderview->open_folder = FALSE;
2282 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2283 FolderView *folderview)
2287 item = gtk_cmctree_node_get_row_data(ctree, node);
2288 cm_return_if_fail(item != NULL);
2289 item->collapsed = FALSE;
2290 folderview_update_node(folderview, node);
2293 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2294 FolderView *folderview)
2298 item = gtk_cmctree_node_get_row_data(ctree, node);
2299 cm_return_if_fail(item != NULL);
2300 item->collapsed = TRUE;
2301 folderview_update_node(folderview, node);
2304 static void folderview_popup_close(GtkMenuShell *menu_shell,
2305 FolderView *folderview)
2307 if (!folderview->opened) return;
2309 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2312 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2313 FolderView *folderview)
2315 FolderColumnType type = folderview->col_state[column].type;
2317 prefs_common.folder_col_size[type] = width;
2320 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2322 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2323 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2324 GtkCMCTreeNode *node, *parent_node;
2325 gint *col_pos = folderview->col_pos;
2326 FolderItemUpdateData hookdata;
2328 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2329 if (parent_node == NULL)
2332 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2334 text[col_pos[F_COL_FOLDER]] = item->name;
2335 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2340 gtk_cmctree_expand(ctree, parent_node);
2341 gtk_cmctree_node_set_row_data(ctree, node, item);
2343 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2344 folderview_sort_folders(folderview, parent_node, item->folder);
2346 hookdata.item = item;
2347 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2348 hookdata.msg = NULL;
2349 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2351 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2354 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2356 FolderView *folderview = (FolderView *)data;
2358 GSList *mlist = NULL;
2360 FolderItem *special_trash = NULL;
2363 if (!folderview->selected) return;
2364 item = folderview_get_selected_item(folderview);
2365 cm_return_if_fail(item != NULL);
2366 cm_return_if_fail(item->folder != NULL);
2368 if (NULL != (ac = account_find_from_item(item)))
2369 special_trash = account_get_special_folder(ac, F_TRASH);
2371 if (item != item->folder->trash && item != special_trash
2372 && !folder_has_parent_of_type(item, F_TRASH)) return;
2374 if (prefs_common.ask_on_clean) {
2375 if (alertpanel(_("Empty trash"),
2376 _("Delete all messages in trash?"),
2377 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2381 mlist = folder_item_get_msg_list(item);
2383 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2384 MsgInfo * msginfo = (MsgInfo *) cur->data;
2385 if (MSG_IS_LOCKED(msginfo->flags))
2387 /* is it partially received? (partial_recv isn't cached) */
2388 if (msginfo->total_size != 0 &&
2389 msginfo->size != (off_t)msginfo->total_size)
2390 partial_mark_for_delete(msginfo);
2392 procmsg_msg_list_free(mlist);
2394 folder_item_remove_all_msg(item);
2397 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2399 FolderView *folderview = (FolderView *)data;
2401 FolderItem *special_queue = NULL;
2403 gchar *errstr = NULL;
2405 if (!folderview->selected) return;
2406 item = folderview_get_selected_item(folderview);
2407 cm_return_if_fail(item != NULL);
2408 cm_return_if_fail(item->folder != NULL);
2410 if (NULL != (ac = account_find_from_item(item)))
2411 special_queue = account_get_special_folder(ac, F_QUEUE);
2413 if (item != item->folder->queue && item != special_queue
2414 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2416 if (procmsg_queue_is_empty(item))
2419 if (prefs_common.work_offline)
2420 if (alertpanel(_("Offline warning"),
2421 _("You're working offline. Override?"),
2422 GTK_STOCK_NO, GTK_STOCK_YES,
2423 NULL) != G_ALERTALTERNATE)
2426 /* ask for confirmation before sending queued messages only
2427 in online mode and if there is at least one message queued
2428 in any of the folder queue
2430 if (prefs_common.confirm_send_queued_messages) {
2431 if (!prefs_common.work_offline) {
2432 if (alertpanel(_("Send queued messages"),
2433 _("Send all queued messages?"),
2434 GTK_STOCK_CANCEL, _("_Send"),
2435 NULL) != G_ALERTALTERNATE)
2440 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2442 alertpanel_error_log(_("Some errors occurred while "
2443 "sending queued messages."));
2445 alertpanel_error_log(_("Some errors occurred "
2446 "while sending queued messages:\n%s"), errstr);
2452 static void folderview_search_cb(GtkAction *action, gpointer data)
2454 FolderView *folderview = (FolderView *)data;
2455 summary_search(folderview->summaryview);
2458 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2460 FolderView *folderview = (FolderView *)data;
2463 if (!folderview->selected) return;
2465 item = folderview_get_selected_item(folderview);
2466 cm_return_if_fail(item != NULL);
2467 cm_return_if_fail(item->folder != NULL);
2469 item->processing_pending = TRUE;
2470 folder_item_apply_processing(item);
2471 item->processing_pending = FALSE;
2474 static void folderview_property_cb(GtkAction *action, gpointer data)
2476 FolderView *folderview = (FolderView *)data;
2479 if (!folderview->selected) return;
2481 item = folderview_get_selected_item(folderview);
2482 cm_return_if_fail(item != NULL);
2483 cm_return_if_fail(item->folder != NULL);
2485 prefs_folder_item_open(item);
2488 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2490 GSList *list = NULL;
2491 GSList *done = NULL;
2492 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2494 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2495 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2496 && list->data != node) {
2497 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2498 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2501 for (list = done; list != NULL; list = g_slist_next(list)) {
2502 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2508 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2509 FolderItem *to_folder, gboolean copy)
2511 FolderItem *new_folder = NULL;
2515 cm_return_if_fail(folderview != NULL);
2516 cm_return_if_fail(from_folder != NULL);
2517 cm_return_if_fail(to_folder != NULL);
2519 if (prefs_common.warn_dnd) {
2520 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2521 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2522 from_folder->name, to_folder->name);
2523 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2524 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2525 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2528 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2530 else if (status & G_ALERTDISABLE)
2531 prefs_common.warn_dnd = FALSE;
2534 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2535 from_folder->name, to_folder->name);
2536 STATUSBAR_PUSH(folderview->mainwin, buf);
2538 summary_clear_all(folderview->summaryview);
2539 folderview->opened = NULL;
2540 folderview->selected = NULL;
2541 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2543 main_window_cursor_wait(folderview->mainwin);
2545 statusbar_verbosity_set(FALSE);
2546 folder_item_update_freeze();
2547 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2548 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2549 statusbar_verbosity_set(FALSE);
2550 main_window_cursor_normal(folderview->mainwin);
2551 STATUSBAR_POP(folderview->mainwin);
2552 folder_item_update_thaw();
2553 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2555 folderview_sort_folders(folderview,
2556 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2557 NULL, to_folder), new_folder->folder);
2558 folderview_select(folderview, new_folder);
2559 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2561 statusbar_verbosity_set(FALSE);
2562 main_window_cursor_normal(folderview->mainwin);
2563 STATUSBAR_POP(folderview->mainwin);
2564 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2565 folder_item_update_thaw();
2567 case F_MOVE_FAILED_DEST_IS_PARENT:
2568 alertpanel_error(_("Source and destination are the same."));
2570 case F_MOVE_FAILED_DEST_IS_CHILD:
2571 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2572 _("Can't move a folder to one of its children."));
2574 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2575 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2578 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2583 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2586 static gint folderview_clist_compare(GtkCMCList *clist,
2587 gconstpointer ptr1, gconstpointer ptr2)
2589 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2590 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2592 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2594 return item1->order - item2->order;
2597 // if only one folder has an order it comes first
2598 if (item1->order > 0)
2602 if (item2->order > 0)
2608 return (item2->name != NULL);
2612 return g_utf8_collate(item1->name, item2->name);
2615 static void folderview_processing_cb(GtkAction *action, gpointer data)
2617 FolderView *folderview = (FolderView *)data;
2621 if (!folderview->selected) return;
2623 item = folderview_get_selected_item(folderview);
2624 cm_return_if_fail(item != NULL);
2625 cm_return_if_fail(item->folder != NULL);
2627 id = folder_item_get_identifier(item);
2628 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2631 prefs_filtering_open(&item->prefs->processing, title,
2632 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2636 void folderview_set_target_folder_color(gint color_op)
2640 FolderView *folderview;
2642 for (list = folderview_list; list != NULL; list = list->next) {
2643 folderview = (FolderView *)list->data;
2644 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2646 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2647 folderview->color_op;
2653 static gchar *last_smallfont = NULL;
2654 static gchar *last_normalfont = NULL;
2655 static gchar *last_boldfont = NULL;
2656 static gboolean last_derive = 0;
2658 void folderview_reinit_fonts(FolderView *folderview)
2661 g_free(last_smallfont);
2662 last_smallfont = NULL;
2663 g_free(last_normalfont);
2664 last_normalfont = NULL;
2665 g_free(last_boldfont);
2666 last_boldfont = NULL;
2669 void folderview_reflect_prefs(void)
2671 gboolean update_font = FALSE;
2672 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2673 FolderItem *item = folderview_get_selected_item(folderview);
2674 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2675 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2676 gint height = gtk_adjustment_get_value(pos);
2678 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2679 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2680 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2681 last_derive != prefs_common.derive_from_normal_font)
2687 g_free(last_smallfont);
2688 last_smallfont = g_strdup(SMALL_FONT);
2689 g_free(last_normalfont);
2690 last_normalfont = g_strdup(NORMAL_FONT);
2691 g_free(last_boldfont);
2692 last_boldfont = g_strdup(BOLD_FONT);
2693 last_derive = prefs_common.derive_from_normal_font;
2695 normal_style = normal_color_style = bold_style =
2696 bold_color_style = bold_tgtfold_style = NULL;
2698 folderview_init(folderview);
2699 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2700 folderview_column_set_titles(folderview);
2701 folderview_set_all();
2703 g_signal_handlers_block_by_func
2704 (G_OBJECT(folderview->ctree),
2705 G_CALLBACK(folderview_selected), folderview);
2708 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2709 GTK_CMCTREE(folderview->ctree), NULL, item);
2711 folderview_select(folderview, item);
2712 folderview->open_folder = FALSE;
2713 folderview->selected = node;
2716 g_signal_handlers_unblock_by_func
2717 (G_OBJECT(folderview->ctree),
2718 G_CALLBACK(folderview_selected), folderview);
2720 pos = gtk_scrolled_window_get_vadjustment(
2721 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2722 gtk_adjustment_set_value(pos, height);
2723 gtk_adjustment_changed(pos);
2724 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2727 static void drag_state_stop(FolderView *folderview)
2729 if (folderview->drag_timer_id)
2730 g_source_remove(folderview->drag_timer_id);
2731 folderview->drag_timer_id = 0;
2732 folderview->drag_node = NULL;
2735 static gboolean folderview_defer_expand(FolderView *folderview)
2737 if (folderview->drag_node) {
2738 folderview_recollapse_nodes(folderview, folderview->drag_node);
2739 if (folderview->drag_item->collapsed) {
2740 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2741 folderview->nodes_to_recollapse = g_slist_append
2742 (folderview->nodes_to_recollapse, folderview->drag_node);
2745 folderview->drag_item = NULL;
2746 folderview->drag_timer_id = 0;
2750 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2752 /* the idea is that we call drag_state_start() whenever we want expansion to
2753 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2754 * we need to call drag_state_stop() */
2755 drag_state_stop(folderview);
2756 /* request expansion */
2757 if (0 != (folderview->drag_timer_id = g_timeout_add
2758 (prefs_common.hover_timeout,
2759 (GSourceFunc)folderview_defer_expand,
2761 folderview->drag_node = node;
2762 folderview->drag_item = item;
2765 #ifndef GENERIC_UMPC
2766 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2767 FolderView *folderview)
2769 GdkDragContext *context;
2771 cm_return_if_fail(folderview != NULL);
2772 if (folderview->selected == NULL) return;
2773 if (folderview->nodes_to_recollapse)
2774 g_slist_free(folderview->nodes_to_recollapse);
2775 folderview->nodes_to_recollapse = NULL;
2776 context = gtk_drag_begin(widget, folderview->target_list,
2777 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2778 gtk_drag_set_icon_default(context);
2781 static void folderview_drag_data_get(GtkWidget *widget,
2782 GdkDragContext *drag_context,
2783 GtkSelectionData *selection_data,
2786 FolderView *folderview)
2790 gchar *source = NULL;
2791 if (info == TARGET_DUMMY) {
2792 sel = GTK_CMCLIST(folderview->ctree)->selection;
2796 item = gtk_cmctree_node_get_row_data
2797 (GTK_CMCTREE(folderview->ctree),
2798 GTK_CMCTREE_NODE(sel->data));
2800 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2801 gtk_selection_data_set(selection_data,
2802 gtk_selection_data_get_target(selection_data), 8,
2803 source, strlen(source));
2806 g_warning("unknown info %d", info);
2810 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2812 FolderUpdateData *hookdata;
2813 FolderView *folderview;
2817 folderview = (FolderView *) userdata;
2818 cm_return_val_if_fail(hookdata != NULL, FALSE);
2819 cm_return_val_if_fail(folderview != NULL, FALSE);
2821 ctree = folderview->ctree;
2822 cm_return_val_if_fail(ctree != NULL, FALSE);
2824 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2825 folderview_create_folder_node(folderview, hookdata->item);
2826 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2827 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2828 NULL, folder_item_parent(hookdata->item));
2829 folderview_sort_folders(folderview, node, hookdata->folder);
2830 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2831 GtkCMCTreeNode *node;
2833 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2835 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2836 if (folderview->selected == node)
2837 folderview->selected = NULL;
2838 if (folderview->opened == node)
2839 folderview->opened = NULL;
2841 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2842 /* do nothing, it's done by the ADD and REMOVE) */
2843 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2844 folderview_set(folderview);
2849 static gboolean folderview_dnd_scroll_cb(gpointer data)
2851 FolderView *folderview = (FolderView *)data;
2852 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2853 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2854 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2855 gint max = (int)gtk_adjustment_get_upper(pos) -
2856 (int)gtk_adjustment_get_page_size(pos);
2858 if (folderview->scroll_value == 0) {
2859 folderview->scroll_timeout_id = 0;
2863 if (folderview->scroll_value > 0 && new_val > max) {
2865 } else if (folderview->scroll_value < 0 && new_val < 0) {
2868 gtk_adjustment_set_value(pos, new_val);
2873 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2874 GdkDragContext *context,
2878 FolderView *folderview)
2881 FolderItem *item = NULL, *src_item = NULL;
2882 GtkCMCTreeNode *node = NULL;
2883 gboolean acceptable = FALSE;
2884 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2885 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2886 int height = (int)gtk_adjustment_get_page_size(pos);
2887 int total_height = (int)gtk_adjustment_get_upper(pos);
2888 int vpos = (int)gtk_adjustment_get_value(pos);
2889 int offset = prefs_common.show_col_headers ? 24:0;
2892 if (gtk_cmclist_get_selection_info
2893 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2894 GtkWidget *srcwidget;
2896 if (y > height - (48 - offset) && height + vpos < total_height) {
2897 dist = -(height - (48 - offset) - y);
2898 folderview->scroll_value = 1.41f * (1+(dist / 6));
2899 } else if (y < 72 - (24 - offset) && y >= 0) {
2900 dist = 72 - (24 - offset) - y;
2901 folderview->scroll_value = -1.41f * (1+(dist / 6));
2903 folderview->scroll_value = 0;
2905 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2906 folderview->scroll_timeout_id =
2907 g_timeout_add(30, folderview_dnd_scroll_cb,
2911 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2912 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2913 src_item = folderview->summaryview->folder_item;
2915 srcwidget = gtk_drag_get_source_widget(context);
2916 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2917 /* comes from summaryview */
2918 /* we are copying messages, so only accept folder items that are not
2919 the source item, are no root items and can copy messages */
2920 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2921 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2922 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2924 } else if (srcwidget == folderview->ctree) {
2925 /* comes from folderview */
2926 /* we are moving folder items, only accept folders that are not
2927 the source items and can copy messages and create folder items */
2928 if (item && item->folder && src_item && src_item != item &&
2929 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2930 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2931 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2932 || item->folder == src_item->folder))
2935 /* comes from another app */
2936 /* we are adding messages, so only accept folder items that are
2937 no root items and can copy messages */
2938 if (item && item->folder && folder_item_parent(item) != NULL
2939 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2940 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2945 if (acceptable || (src_item && src_item == item))
2946 drag_state_start(folderview, node, item);
2949 g_signal_handlers_block_by_func
2951 G_CALLBACK(folderview_selected), folderview);
2952 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2953 g_signal_handlers_unblock_by_func
2955 G_CALLBACK(folderview_selected), folderview);
2956 gdk_drag_status(context,
2957 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2958 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2960 if (folderview->opened)
2961 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2962 gdk_drag_status(context, 0, time);
2968 static void folderview_drag_leave_cb(GtkWidget *widget,
2969 GdkDragContext *context,
2971 FolderView *folderview)
2973 drag_state_stop(folderview);
2974 folderview->scroll_value = 0;
2975 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2978 static void free_info (gpointer stuff, gpointer data)
2983 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2984 guint time, FolderItem *item)
2987 GSList *msglist = NULL;
2988 list = uri_list_extract_filenames(data);
2989 if (!(item && item->folder && folder_item_parent(item) != NULL
2990 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2992 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2993 debug_print("item doesn't fit\n");
2997 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2998 debug_print("list is empty\n");
3001 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3002 MsgFileInfo *info = NULL;
3004 if (file_is_email((gchar *)tmp->data)) {
3005 info = g_new0(MsgFileInfo, 1);
3006 info->msginfo = NULL;
3007 info->file = (gchar *)tmp->data;
3008 msglist = g_slist_prepend(msglist, info);
3009 debug_print("file is a mail\n");
3011 debug_print("file isn't a mail\n");
3015 msglist = g_slist_reverse(msglist);
3016 folder_item_add_msgs(item, msglist, FALSE);
3017 g_slist_foreach(msglist, free_info, NULL);
3018 g_slist_free(msglist);
3019 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3021 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3023 list_free_strings(list);
3027 static void folderview_drag_received_cb(GtkWidget *widget,
3028 GdkDragContext *drag_context,
3031 GtkSelectionData *data,
3034 FolderView *folderview)
3037 FolderItem *item = NULL, *src_item;
3038 GtkCMCTreeNode *node;
3039 int offset = prefs_common.show_col_headers ? 24:0;
3041 folderview->scroll_value = 0;
3043 if (info == TARGET_DUMMY) {
3044 drag_state_stop(folderview);
3045 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3046 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3047 /* comes from summaryview */
3048 if (gtk_cmclist_get_selection_info
3049 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3052 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3053 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3054 src_item = folderview->summaryview->folder_item;
3056 if (item->no_select) {
3057 alertpanel_error(_("The destination folder can only be used to "
3058 "store subfolders."));
3061 /* re-check (due to acceptable possibly set for folder moves */
3062 if (!(item && item->folder && item->path && !item->no_select &&
3063 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3066 if (item && src_item) {
3067 switch (gdk_drag_context_get_selected_action(drag_context)) {
3068 case GDK_ACTION_COPY:
3069 summary_copy_selected_to(folderview->summaryview, item);
3070 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3072 case GDK_ACTION_MOVE:
3073 case GDK_ACTION_DEFAULT:
3075 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3076 summary_copy_selected_to(folderview->summaryview, item);
3078 summary_move_selected_to(folderview->summaryview, item);
3079 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3082 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3084 /* comes from folderview */
3086 gboolean folder_is_normal = TRUE;
3087 gboolean copy = (GDK_ACTION_COPY ==
3088 gdk_drag_context_get_selected_action(drag_context));
3090 source = (char *)gtk_selection_data_get_data(data) + 17;
3091 if (gtk_cmclist_get_selection_info
3092 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3094 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3097 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3098 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3099 src_item = folder_find_item_from_identifier(source);
3103 src_item->stype == F_NORMAL &&
3104 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3105 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3106 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3107 !folder_has_parent_of_type(src_item, F_TRASH);
3108 if (!item || !src_item || !folder_is_normal) {
3109 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3113 folderview_move_folder(folderview, src_item, item, copy);
3114 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3116 folderview->nodes_to_recollapse = NULL;
3117 } else if (info == TARGET_MAIL_URI_LIST) {
3118 if (gtk_cmclist_get_selection_info
3119 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3122 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3124 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3125 debug_print("no node\n");
3128 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3130 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3131 debug_print("no item\n");
3134 folderview_finish_dnd(gtk_selection_data_get_data(data),
3135 drag_context, time, item);
3139 static void folderview_drag_end_cb(GtkWidget *widget,
3140 GdkDragContext *drag_context,
3141 FolderView *folderview)
3143 drag_state_stop(folderview);
3144 folderview->scroll_value = 0;
3145 g_slist_free(folderview->nodes_to_recollapse);
3146 folderview->nodes_to_recollapse = NULL;
3149 void folderview_register_popup(FolderViewPopup *fpopup)
3153 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3154 FolderView *folderview = folderviews->data;
3155 GtkActionGroup *factory;
3157 factory = create_action_group(folderview, fpopup);
3158 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3160 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3163 void folderview_unregister_popup(FolderViewPopup *fpopup)
3168 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3169 FolderView *folderview = folderviews->data;
3171 g_hash_table_remove(folderview->popups, fpopup->klass);
3173 g_hash_table_remove(folderview_popups, fpopup->klass);
3176 void folderview_freeze(FolderView *folderview)
3179 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3182 void folderview_thaw(FolderView *folderview)
3185 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));