2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_cb (GtkAction *action,
169 static void folderview_empty_trash_cb (GtkAction *action,
172 static void folderview_send_queue_cb (GtkAction *action,
175 static void folderview_search_cb (GtkAction *action,
177 static void folderview_run_processing_cb(GtkAction *action,
180 static void folderview_property_cb (GtkAction *action,
183 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
184 GdkDragContext *context,
188 FolderView *folderview);
189 static void folderview_drag_leave_cb (GtkWidget *widget,
190 GdkDragContext *context,
192 FolderView *folderview);
193 static void folderview_drag_received_cb (GtkWidget *widget,
194 GdkDragContext *drag_context,
197 GtkSelectionData *data,
200 FolderView *folderview);
202 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
203 FolderView *folderview);
205 static void folderview_drag_data_get (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 GtkSelectionData *selection_data,
210 FolderView *folderview);
211 static void folderview_drag_end_cb (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 FolderView *folderview);
215 static void folderview_create_folder_node (FolderView *folderview,
217 static gboolean folderview_update_folder (gpointer source,
219 static gboolean folderview_update_item_claws (gpointer source,
221 static void folderview_processing_cb(GtkAction *action, gpointer data);
222 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
223 GdkEventButton *event);
225 GHashTable *folderview_popups;
227 static GtkActionEntry folderview_common_popup_entries[] =
229 {"FolderViewPopup", NULL, "FolderViewPopup" },
230 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
231 {"FolderViewPopup/---", NULL, "---" },
232 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
233 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
234 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
235 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
236 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
237 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
241 GtkTargetEntry folderview_drag_types[] =
243 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
244 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
247 void folderview_initialize(void)
249 FolderViewPopup *fpopup;
251 fpopup = g_new0(FolderViewPopup, 1);
253 fpopup->klass = "common";
254 fpopup->path = "<CommonFolder>";
255 fpopup->entries = folderview_common_popup_entries;
256 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
257 fpopup->set_sensitivity = NULL;
259 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
260 g_hash_table_insert(folderview_popups, "common", fpopup);
263 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
265 FolderViewPopup *fpopup_common;
266 GtkActionGroup *action_group;
268 action_group = cm_menu_create_action_group(
270 fpopup->entries, fpopup->n_entries,
271 (gpointer)folderview);
273 if (fpopup->toggle_entries)
274 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
275 fpopup->n_toggle_entries,
276 (gpointer)folderview);
277 if (fpopup->radio_entries)
278 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
279 fpopup->n_radio_entries, fpopup->radio_default,
280 G_CALLBACK(fpopup->radio_callback),
281 (gpointer)folderview);
283 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
284 if (fpopup_common != fpopup) {
285 gtk_action_group_add_actions(action_group, fpopup_common->entries,
286 fpopup_common->n_entries,
287 (gpointer)folderview);
288 if (fpopup_common->toggle_entries)
289 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
290 fpopup_common->n_toggle_entries,
291 (gpointer)folderview);
292 if (fpopup_common->radio_entries)
293 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
294 fpopup_common->n_radio_entries, fpopup_common->radio_default,
295 G_CALLBACK(fpopup_common->radio_callback),
296 (gpointer)folderview);
302 static void create_action_groups(gpointer key, gpointer value, gpointer data)
304 FolderView *folderview = data;
305 FolderViewPopup *fpopup = value;
306 GtkActionGroup *group;
308 group = create_action_group(folderview, fpopup);
309 g_hash_table_insert(folderview->popups, fpopup->klass, group);
312 static void folderview_column_set_titles(FolderView *folderview)
314 GtkWidget *ctree = folderview->ctree;
315 GtkWidget *label_folder;
316 GtkWidget *label_new;
317 GtkWidget *label_unread;
318 GtkWidget *label_total;
319 GtkWidget *hbox_folder;
321 GtkWidget *hbox_unread;
322 GtkWidget *hbox_total;
323 gint *col_pos = folderview->col_pos;
325 debug_print("setting titles...\n");
326 gtk_widget_realize(folderview->ctree);
327 gtk_widget_show_all(folderview->scrolledwin);
329 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
330 * instead text (text overflows making them unreadable and ugly) */
331 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
333 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
335 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
338 label_folder = gtk_label_new(_("Folder"));
339 label_new = gtk_image_new_from_pixbuf(newxpm);
340 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
341 label_total = gtk_image_new_from_pixbuf(readxpm);
343 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
345 hbox_folder = gtk_hbox_new(FALSE, 4);
346 hbox_new = gtk_hbox_new(FALSE, 4);
347 hbox_unread = gtk_hbox_new(FALSE, 4);
348 hbox_total = gtk_hbox_new(FALSE, 4);
351 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
352 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
353 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
354 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
355 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
360 gtk_widget_show_all(hbox_folder);
361 gtk_widget_show_all(hbox_new);
362 gtk_widget_show_all(hbox_unread);
363 gtk_widget_show_all(hbox_total);
366 gtk_widget_set_size_request(hbox_new, -1, 20);
367 gtk_widget_set_size_request(hbox_unread, -1, 20);
368 gtk_widget_set_size_request(hbox_total, -1, 20);
371 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
372 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
373 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
374 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
381 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
382 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
385 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
387 FolderView *folderview = (FolderView *)data;
388 GdkEventButton event;
389 if (folderview_get_selected_item(folderview) == NULL)
393 event.time = gtk_get_current_event_time();
395 folderview_set_sens_and_popup_menu(folderview, -1,
402 static GtkWidget *folderview_ctree_create(FolderView *folderview)
406 FolderColumnState *col_state;
407 FolderColumnType type;
408 gchar *titles[N_FOLDER_COLS];
410 GtkWidget *scrolledwin = folderview->scrolledwin;
412 debug_print("creating tree...\n");
413 memset(titles, 0, sizeof(titles));
415 col_state = prefs_folder_column_get_config();
416 memset(titles, 0, sizeof(titles));
418 col_pos = folderview->col_pos;
420 for (i = 0; i < N_FOLDER_COLS; i++) {
421 folderview->col_state[i] = col_state[i];
422 type = col_state[i].type;
426 titles[col_pos[F_COL_FOLDER]] = _("Folder");
427 titles[col_pos[F_COL_NEW]] = _("New");
428 titles[col_pos[F_COL_UNREAD]] = _("Unread");
429 /* TRANSLATORS: This in Number sign in American style */
430 titles[col_pos[F_COL_TOTAL]] = _("#");
432 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
435 if (prefs_common.show_col_headers == FALSE)
436 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
439 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
440 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
442 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
443 col_pos[F_COL_UNREAD],
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
446 col_pos[F_COL_TOTAL],
448 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
449 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
450 GTK_CMCTREE_EXPANDER_TRIANGLE);
452 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
453 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
455 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
456 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
458 /* don't let title buttons take key focus */
459 for (i = 0; i < N_FOLDER_COLS; i++) {
460 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
461 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
462 prefs_common.folder_col_size[i]);
463 gtk_cmclist_set_column_visibility
464 (GTK_CMCLIST(ctree), i, col_state[i].visible);
467 g_signal_connect(G_OBJECT(ctree), "key_press_event",
468 G_CALLBACK(folderview_key_pressed),
470 g_signal_connect(G_OBJECT(ctree), "button_press_event",
471 G_CALLBACK(folderview_button_pressed),
473 g_signal_connect(G_OBJECT(ctree), "popup-menu",
474 G_CALLBACK(folderview_popup_menu), folderview);
475 g_signal_connect(G_OBJECT(ctree), "button_release_event",
476 G_CALLBACK(folderview_button_released),
478 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
479 G_CALLBACK(folderview_selected), folderview);
481 /* drag-n-dropping folders on maemo is impractical as this
482 * opens the folder almost everytime */
483 g_signal_connect(G_OBJECT(ctree), "start_drag",
484 G_CALLBACK(folderview_start_drag), folderview);
486 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
487 G_CALLBACK(folderview_drag_data_get),
490 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
491 G_CALLBACK(folderview_tree_expanded),
493 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
494 G_CALLBACK(folderview_tree_collapsed),
497 g_signal_connect(G_OBJECT(ctree), "resize_column",
498 G_CALLBACK(folderview_col_resized),
502 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
503 folderview_drag_types, 2,
504 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
505 g_signal_connect(G_OBJECT(ctree), "drag_motion",
506 G_CALLBACK(folderview_drag_motion_cb),
508 g_signal_connect(G_OBJECT(ctree), "drag_leave",
509 G_CALLBACK(folderview_drag_leave_cb),
511 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
512 G_CALLBACK(folderview_drag_received_cb),
514 g_signal_connect(G_OBJECT(ctree), "drag_end",
515 G_CALLBACK(folderview_drag_end_cb),
518 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
523 void folderview_set_column_order(FolderView *folderview)
525 GtkWidget *ctree = folderview->ctree;
526 FolderItem *item = folderview_get_selected_item(folderview);
527 FolderItem *sel_item = NULL, *op_item = NULL;
528 GtkWidget *scrolledwin = folderview->scrolledwin;
530 if (folderview->drag_timer_id != 0) {
531 g_source_remove(folderview->drag_timer_id);
532 folderview->drag_timer_id = 0;
534 if (folderview->deferred_refresh_id != 0) {
535 g_source_remove(folderview->deferred_refresh_id);
536 folderview->deferred_refresh_id = 0;
538 if (folderview->scroll_timeout_id != 0) {
539 g_source_remove(folderview->scroll_timeout_id);
540 folderview->scroll_timeout_id = 0;
542 if (folderview->postpone_select_id != 0) {
543 g_source_remove(folderview->postpone_select_id);
544 folderview->postpone_select_id = 0;
547 if (folderview->selected)
548 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
549 if (folderview->opened)
550 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
552 debug_print("recreating tree...\n");
553 gtk_widget_destroy(folderview->ctree);
556 folderview->ctree = ctree = folderview_ctree_create(folderview);
557 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
558 GTK_CMCLIST(ctree)->hadjustment);
559 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
560 GTK_CMCLIST(ctree)->vadjustment);
561 gtk_widget_show(ctree);
564 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
566 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
568 folderview_set(folderview);
569 folderview_column_set_titles(folderview);
571 folderview_select(folderview,item);
574 FolderView *folderview_create(void)
576 FolderView *folderview;
577 GtkWidget *scrolledwin;
580 debug_print("Creating folder view...\n");
581 folderview = g_new0(FolderView, 1);
583 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
584 gtk_scrolled_window_set_policy
585 (GTK_SCROLLED_WINDOW(scrolledwin),
586 GTK_POLICY_AUTOMATIC,
587 prefs_common.folderview_vscrollbar_policy);
588 gtk_widget_set_size_request(scrolledwin,
589 prefs_common.folderview_width,
590 prefs_common.folderview_height);
592 folderview->scrolledwin = scrolledwin;
593 ctree = folderview_ctree_create(folderview);
595 /* create popup factories */
596 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
597 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
599 folderview->ctree = ctree;
601 folderview->folder_update_callback_id =
602 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
603 folderview->folder_item_update_callback_id =
604 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
606 gtk_widget_show_all(scrolledwin);
608 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
609 folderview_list = g_list_append(folderview_list, folderview);
611 folderview->drag_timer_id = 0;
612 folderview->deferred_refresh_id = 0;
613 folderview->scroll_timeout_id = 0;
614 folderview->postpone_select_id = 0;
619 void folderview_init(FolderView *folderview)
621 GtkWidget *ctree = folderview->ctree;
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
672 PangoFontDescription *font_desc;
673 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
674 font_desc = pango_font_description_from_string(NORMAL_FONT);
676 if (normal_style->font_desc)
677 pango_font_description_free
678 (normal_style->font_desc);
679 normal_style->font_desc = font_desc;
681 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
682 normal_color_style = gtk_style_copy(normal_style);
683 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
685 gtk_widget_set_style(ctree, normal_style);
689 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
690 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
691 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
692 pango_font_description_set_weight
693 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
695 PangoFontDescription *font_desc;
696 font_desc = pango_font_description_from_string(BOLD_FONT);
698 if (bold_style->font_desc)
699 pango_font_description_free
700 (bold_style->font_desc);
701 bold_style->font_desc = font_desc;
704 bold_color_style = gtk_style_copy(bold_style);
705 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
707 bold_tgtfold_style = gtk_style_copy(bold_style);
708 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
712 static gboolean folderview_defer_set(gpointer data)
714 FolderView *folderview = (FolderView *)data;
715 MainWindow *mainwin = folderview->mainwin;
719 if (mainwin->lock_count)
722 debug_print("doing deferred folderview_set now\n");
723 folderview_set(folderview);
725 folderview->deferred_refresh_id = 0;
729 void folderview_set(FolderView *folderview)
731 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
732 MainWindow *mainwin = folderview->mainwin;
733 FolderItem *sel_item = NULL, *op_item = NULL;
738 if (mainwin->lock_count) {
739 if (folderview->deferred_refresh_id == 0)
740 folderview->deferred_refresh_id =
741 g_timeout_add(500, folderview_defer_set, folderview);
742 debug_print("deferred folderview_set\n");
747 debug_print("Setting folder info...\n");
748 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
750 main_window_cursor_wait(mainwin);
752 if (folderview->selected)
753 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
754 if (folderview->opened)
755 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
757 folderview->selected = NULL;
758 folderview->opened = NULL;
760 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
761 gtk_cmclist_clear(GTK_CMCLIST(ctree));
763 folderview_set_folders(folderview);
766 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
768 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
770 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
771 main_window_cursor_normal(mainwin);
772 STATUSBAR_POP(mainwin);
776 void folderview_set_all(void)
780 for (list = folderview_list; list != NULL; list = list->next)
781 folderview_set((FolderView *)list->data);
784 void folderview_select(FolderView *folderview, FolderItem *item)
786 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
787 GtkCMCTreeNode *node;
788 GtkCMCTreeNode *old_selected = folderview->selected;
792 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
793 if (node) folderview_select_node(folderview, node);
795 if (old_selected != node)
796 folder_update_op_count();
799 static void mark_all_read_cb(GtkAction *action, gpointer data)
801 FolderView *folderview = (FolderView *)data;
805 item = folderview_get_selected_item(folderview);
809 if (folderview->summaryview->folder_item != item
810 && prefs_common.ask_mark_all_read) {
811 val = alertpanel_full(_("Mark all as read"),
812 _("Do you really want to mark all mails in this "
813 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
814 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
816 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
818 else if (val & G_ALERTDISABLE)
819 prefs_common.ask_mark_all_read = FALSE;
823 folder_item_update_freeze();
824 if (folderview->summaryview->folder_item != item)
825 summary_lock(folderview->summaryview);
827 summary_freeze(folderview->summaryview);
829 folderutils_mark_all_read(item);
831 if (folderview->summaryview->folder_item != item)
832 summary_unlock(folderview->summaryview);
834 summary_thaw(folderview->summaryview);
835 folder_item_update_thaw();
838 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
840 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
842 cm_return_if_fail(node != NULL);
844 if (folderview->open_folder) {
848 folderview->open_folder = TRUE;
849 gtkut_ctree_set_focus_row(ctree, node);
850 gtk_cmctree_select(ctree, node);
851 if ((folderview->summaryview->folder_item &&
852 folderview->summaryview->folder_item->total_msgs > 0) ||
853 prefs_common.layout_mode == SMALL_LAYOUT)
854 summary_grab_focus(folderview->summaryview);
856 gtk_widget_grab_focus(folderview->ctree);
858 gtkut_ctree_expand_parent_all(ctree, node);
861 void folderview_unselect(FolderView *folderview)
863 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
865 (GTK_CMCTREE(folderview->ctree), folderview->opened);
867 folderview->selected = folderview->opened = NULL;
870 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
871 GtkCMCTreeNode *node)
876 node = gtkut_ctree_node_next(ctree, node);
878 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
880 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
881 item = gtk_cmctree_node_get_row_data(ctree, node);
882 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
889 void folderview_select_next_marked(FolderView *folderview)
891 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
892 GtkCMCTreeNode *node = NULL;
893 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
894 gboolean last_open = prefs_common.always_show_msg;
896 prefs_common.summary_select_prio[0] = ACTION_MARKED;
897 prefs_common.always_show_msg = OPENMSG_ALWAYS;
899 if ((node = folderview_find_next_marked(ctree, folderview->opened))
901 folderview_select_node(folderview, node);
905 if (!folderview->opened ||
906 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
909 /* search again from the first node */
910 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
911 folderview_select_node(folderview, node);
914 prefs_common.summary_select_prio[0] = last_summary_select_prio;
915 prefs_common.always_show_msg = last_open;
918 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
919 GtkCMCTreeNode *node)
924 node = gtkut_ctree_node_next(ctree, node);
926 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
928 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
929 item = gtk_cmctree_node_get_row_data(ctree, node);
930 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
937 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
939 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
940 GtkCMCTreeNode *node = NULL;
941 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
942 gboolean last_open = prefs_common.always_show_msg;
944 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
945 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
947 if ((node = folderview_find_next_unread(ctree, folderview->opened))
949 folderview_select_node(folderview, node);
953 if (!folderview->opened ||
954 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
957 /* search again from the first node */
958 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
959 folderview_select_node(folderview, node);
962 prefs_common.summary_select_prio[0] = last_summary_select_prio;
963 prefs_common.always_show_msg = last_open;
966 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
967 GtkCMCTreeNode *node)
972 node = gtkut_ctree_node_next(ctree, node);
974 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
976 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
977 item = gtk_cmctree_node_get_row_data(ctree, node);
978 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
985 void folderview_select_next_new(FolderView *folderview)
987 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
988 GtkCMCTreeNode *node = NULL;
989 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
990 gboolean last_open = prefs_common.always_show_msg;
992 prefs_common.summary_select_prio[0] = ACTION_NEW;
993 prefs_common.always_show_msg = OPENMSG_ALWAYS;
995 if ((node = folderview_find_next_new(ctree, folderview->opened))
997 folderview_select_node(folderview, node);
1001 if (!folderview->opened ||
1002 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1005 /* search again from the first node */
1006 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1007 folderview_select_node(folderview, node);
1010 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1011 prefs_common.always_show_msg = last_open;
1014 FolderItem *folderview_get_selected_item(FolderView *folderview)
1016 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1018 if (!folderview->selected) return NULL;
1019 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1022 static void folderview_set_folders(FolderView *folderview)
1025 list = folder_get_list();
1027 for (; list != NULL; list = list->next) {
1028 folderview_append_folder(folderview, FOLDER(list->data));
1032 static gchar *get_scan_str(FolderItem *item)
1035 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1036 item->folder->name, G_DIR_SEPARATOR,
1039 return g_strdup_printf(_("Scanning folder %s..."),
1040 item->folder->name);
1042 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1046 for (list = folderview_list; list != NULL; list = list->next) {
1047 FolderView *folderview = (FolderView *)list->data;
1048 MainWindow *mainwin = folderview->mainwin;
1049 gchar *str = get_scan_str(item);
1051 STATUSBAR_PUSH(mainwin, str);
1052 STATUSBAR_POP(mainwin);
1057 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1060 MainWindow *mainwin = mainwindow_get_mainwindow();
1061 FolderView *folderview = NULL;
1062 GtkAdjustment *pos = NULL;
1065 cm_return_if_fail(folder != NULL);
1067 if (!folder->klass->scan_tree) return;
1070 alertpanel_full(_("Rebuild folder tree"),
1071 _("Rebuilding the folder tree will remove "
1072 "local caches. Do you want to continue?"),
1073 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1074 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1075 != G_ALERTALTERNATE) {
1081 window = label_window_create(_("Rebuilding folder tree..."));
1083 window = label_window_create(_("Scanning folder tree..."));
1086 folderview = mainwin->folderview;
1089 pos = gtk_scrolled_window_get_vadjustment(
1090 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1091 height = gtk_adjustment_get_value(pos);
1094 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1095 folder_scan_tree(folder, rebuild);
1096 folder_set_ui_func(folder, NULL, NULL);
1098 folderview_set_all();
1101 pos = gtk_scrolled_window_get_vadjustment(
1102 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1103 gtk_adjustment_set_value(pos, height);
1104 gtk_adjustment_changed(pos);
1106 label_window_destroy(window);
1110 /** folderview_check_new()
1111 * Scan and update the folder and return the
1112 * count the number of new messages since last check.
1113 * \param folder the folder to check for new messages
1114 * \return the number of new messages since last check
1116 gint folderview_check_new(Folder *folder)
1120 FolderView *folderview;
1122 GtkCMCTreeNode *node;
1124 gint former_new_msgs = 0;
1125 gint former_new = 0, former_unread = 0, former_total;
1127 for (list = folderview_list; list != NULL; list = list->next) {
1128 folderview = (FolderView *)list->data;
1129 ctree = GTK_CMCTREE(folderview->ctree);
1130 folderview->scanning_folder = folder;
1132 main_window_lock(folderview->mainwin);
1134 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1135 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1137 item = gtk_cmctree_node_get_row_data(ctree, node);
1138 if (!item || !item->path || !item->folder) continue;
1139 if (item->no_select) continue;
1140 if (folder && folder != item->folder) continue;
1141 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1142 if (!item->prefs->newmailcheck) continue;
1143 if (item->processing_pending == TRUE) {
1144 debug_print("skipping %s, processing pending\n",
1145 item->path ? item->path : item->name);
1148 if (item->scanning != ITEM_NOT_SCANNING) {
1149 debug_print("skipping %s, scanning\n",
1150 item->path ? item->path : item->name);
1154 str = get_scan_str(item);
1156 STATUSBAR_PUSH(folderview->mainwin, str);
1160 folderview_scan_tree_func(item->folder, item, NULL);
1161 former_new = item->new_msgs;
1162 former_unread = item->unread_msgs;
1163 former_total = item->total_msgs;
1165 if (item->folder->klass->scan_required &&
1166 (item->folder->klass->scan_required(item->folder, item) ||
1167 item->folder->inbox == item ||
1168 item->opened == TRUE ||
1169 item->processing_pending == TRUE)) {
1170 if (folder_item_scan(item) < 0) {
1172 summaryview_unlock(folderview->summaryview, item);
1173 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1174 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1175 item->path ? item->path:item->name);
1176 STATUSBAR_POP(folderview->mainwin);
1178 } else if (!FOLDER_IS_LOCAL(folder)) {
1179 STATUSBAR_POP(folderview->mainwin);
1184 } else if (!item->folder->klass->scan_required) {
1185 if (folder_item_scan(item) < 0) {
1186 summaryview_unlock(folderview->summaryview, item);
1187 if (folder && !FOLDER_IS_LOCAL(folder)) {
1188 STATUSBAR_POP(folderview->mainwin);
1193 if (former_new != item->new_msgs ||
1194 former_unread != item->unread_msgs ||
1195 former_total != item->total_msgs)
1196 folderview_update_node(folderview, node);
1198 new_msgs += item->new_msgs;
1199 former_new_msgs += former_new;
1200 STATUSBAR_POP(folderview->mainwin);
1202 folderview->scanning_folder = NULL;
1203 main_window_unlock(folderview->mainwin);
1207 folder_write_list();
1208 /* Number of new messages since last check is the just the difference
1209 * between former_new_msgs and new_msgs. If new_msgs is less than
1210 * former_new_msgs, that would mean another session accessed the folder
1211 * and the result is not well defined.
1213 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1217 void folderview_check_new_all(void)
1221 FolderView *folderview;
1223 folderview = (FolderView *)folderview_list->data;
1226 main_window_lock(folderview->mainwin);
1227 window = label_window_create
1228 (_("Checking for new messages in all folders..."));
1230 list = folder_get_list();
1231 for (; list != NULL; list = list->next) {
1232 Folder *folder = list->data;
1234 folderview_check_new(folder);
1237 folder_write_list();
1238 folderview_set_all();
1240 label_window_destroy(window);
1241 main_window_unlock(folderview->mainwin);
1245 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1251 if (!item || !item->folder || !item->folder->node)
1254 node = item->folder->node;
1256 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1257 node = node->children;
1260 (item->new_msgs > 0 ||
1261 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1265 while (node != NULL) {
1266 if (node && node->data) {
1267 FolderItem *next_item = (FolderItem*) node->data;
1269 if (folderview_have_new_children_sub(folderview,
1278 static gboolean folderview_have_new_children(FolderView *folderview,
1281 return folderview_have_new_children_sub(folderview, item, FALSE);
1284 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1290 if (!item || !item->folder || !item->folder->node)
1293 node = item->folder->node;
1295 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1296 node = node->children;
1299 (item->unread_msgs > 0 ||
1300 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1304 while (node != NULL) {
1305 if (node && node->data) {
1306 FolderItem *next_item = (FolderItem*) node->data;
1308 if (folderview_have_unread_children_sub(folderview,
1318 static gboolean folderview_have_unread_children(FolderView *folderview,
1321 return folderview_have_unread_children_sub(folderview, item, FALSE);
1324 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1330 if (!item || !item->folder || !item->folder->node)
1333 node = item->folder->node;
1335 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1336 node = node->children;
1338 if (in_sub && item->search_match){
1342 while (node != NULL) {
1343 if (node && node->data) {
1344 FolderItem *next_item = (FolderItem*) node->data;
1346 if (folderview_have_matching_children_sub(folderview,
1356 static gboolean folderview_have_matching_children(FolderView *folderview,
1359 return folderview_have_matching_children_sub(folderview, item, FALSE);
1362 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1368 if (!item || !item->folder || !item->folder->node)
1371 node = item->folder->node;
1373 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1374 node = node->children;
1376 if (item->marked_msgs != 0) {
1380 while (node != NULL) {
1381 if (node && node->data) {
1382 FolderItem *next_item = (FolderItem*) node->data;
1384 if (folderview_have_marked_children_sub(folderview,
1393 static gboolean folderview_have_marked_children(FolderView *folderview,
1396 return folderview_have_marked_children_sub(folderview, item, FALSE);
1399 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1401 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1402 GtkStyle *style = NULL;
1403 GtkStyle *color_style = NULL;
1405 GdkPixbuf *xpm, *openxpm;
1406 static GdkPixbuf *searchicon;
1407 gboolean mark = FALSE;
1410 gboolean add_unread_mark;
1411 gboolean add_sub_match_mark;
1412 gboolean use_bold, use_color;
1413 gint *col_pos = folderview->col_pos;
1414 SpecialFolderItemType stype;
1416 item = gtk_cmctree_node_get_row_data(ctree, node);
1417 cm_return_if_fail(item != NULL);
1419 if (!GTK_CMCTREE_ROW(node)->expanded)
1420 mark = folderview_have_marked_children(folderview, item);
1422 mark = (item->marked_msgs != 0);
1424 stype = item->stype;
1425 if (stype == F_NORMAL) {
1426 if (folder_has_parent_of_type(item, F_TRASH))
1428 else if (folder_has_parent_of_type(item, F_DRAFT))
1430 else if (folder_has_parent_of_type(item, F_OUTBOX))
1432 else if (folder_has_parent_of_type(item, F_QUEUE))
1437 if (item->hide_read_msgs || item->hide_read_threads) {
1438 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1439 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1441 xpm = mark?m_inboxxpm:inboxxpm;
1442 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1446 if (item->hide_read_msgs || item->hide_read_threads) {
1447 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1448 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1450 xpm = mark?m_outboxxpm:outboxxpm;
1451 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1455 if (item->hide_read_msgs || item->hide_read_threads) {
1456 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1457 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1459 xpm = mark?m_queuexpm:queuexpm;
1460 openxpm = mark?m_queueopenxpm:queueopenxpm;
1464 if (item->hide_read_msgs || item->hide_read_threads) {
1465 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1466 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1468 xpm = mark?m_trashxpm:trashxpm;
1469 openxpm = mark?m_trashopenxpm:trashopenxpm;
1473 xpm = mark?m_draftsxpm:draftsxpm;
1474 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1477 if (item->hide_read_msgs || item->hide_read_threads) {
1478 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1479 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1481 xpm = mark?m_folderxpm:folderxpm;
1482 openxpm = mark?m_folderopenxpm:folderopenxpm;
1486 if (item->no_select) {
1487 xpm = openxpm = noselectxpm;
1490 name = folder_item_get_name(item);
1492 if (!GTK_CMCTREE_ROW(node)->expanded) {
1493 add_unread_mark = folderview_have_unread_children(
1495 add_sub_match_mark = folderview_have_matching_children(
1498 add_unread_mark = FALSE;
1499 add_sub_match_mark = FALSE;
1502 if (item->search_match) {
1504 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1507 xpm = openxpm = searchicon;
1511 if (prefs_common.display_folder_unread) {
1512 if (folder_has_parent_of_type(item, F_QUEUE)) {
1513 /* only total_msgs matters here */
1514 if (item->total_msgs > 0) {
1515 /* show total number (should be equal to the unread number)
1517 str = g_strdup_printf("%s (%d%s%s)",
1518 name, item->total_msgs,
1519 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1520 (item->unreadmarked_msgs > 0) ? "!" : "");
1523 if (prefs_common.display_folder_unread == 1) {
1524 if (item->unread_msgs > 0) {
1525 /* show unread number and signs */
1526 str = g_strdup_printf("%s (%d%s%s)",
1527 name, item->unread_msgs,
1528 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1529 (item->unreadmarked_msgs > 0) ? "!" : "");
1532 if (item->total_msgs > 0) {
1533 /* show unread number, total number and signs if any */
1534 str = g_strdup_printf("%s (%d/%d%s%s)",
1535 name, item->unread_msgs, item->total_msgs,
1536 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1537 (item->unreadmarked_msgs > 0) ? "!" : "");
1541 if ((str == NULL) &&
1542 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1543 /* no unread/total numbers, but at least one sign */
1544 str = g_strdup_printf("%s (%s%s)",
1546 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1547 (item->unreadmarked_msgs > 0) ? "!" : "");
1551 /* last fallback, folder name only or with +! sign */
1552 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1553 str = g_strdup_printf("%s%s",
1555 } else if (item->unreadmarked_msgs > 0) {
1556 str = g_strdup_printf("%s%s",
1558 } else if (add_sub_match_mark) {
1559 str = g_strdup_printf("%s%s",
1562 str = g_strdup_printf("%s", name);
1565 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1567 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1571 if (!folder_item_parent(item)) {
1572 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1573 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1574 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1576 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1577 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1578 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1581 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1582 folder_has_parent_of_type(item, F_TRASH)) {
1583 use_bold = use_color = FALSE;
1584 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1585 GSList *list = folder_item_get_msg_list(item);
1587 use_bold = use_color = FALSE;
1588 for (cur = list; cur; cur = cur->next) {
1589 MsgInfo *msginfo = (MsgInfo *)cur->data;
1590 if (!MSG_IS_DELETED(msginfo->flags)) {
1591 /* highlight queue folder if there are any messages */
1592 use_bold = use_color = TRUE;
1596 procmsg_msg_list_free(list);
1598 /* if unread messages exist, print with bold font */
1599 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1601 /* if new messages exist, print with colored letter */
1603 (item->new_msgs > 0) ||
1605 folderview_have_new_children(folderview, item));
1608 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1613 if (item->prefs->color > 0 && !use_color) {
1614 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1615 color_style = gtk_style_copy(bold_style);
1616 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1617 style = color_style;
1618 } else if (use_color) {
1619 style = bold_color_style;
1622 if (item->op_count > 0) {
1623 style = bold_tgtfold_style;
1625 } else if (use_color) {
1626 style = normal_color_style;
1627 gtk_cmctree_node_set_foreground(ctree, node,
1628 &folderview->color_new);
1629 } else if (item->op_count > 0) {
1630 style = bold_tgtfold_style;
1631 } else if (item->prefs->color > 0) {
1633 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1634 color_style = gtk_style_copy(normal_style);
1635 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1636 style = color_style;
1638 style = normal_style;
1641 gtk_cmctree_node_set_row_style(ctree, node, style);
1643 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1644 folderview_update_node(folderview, node);
1647 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1650 FolderView *folderview;
1652 GtkCMCTreeNode *node;
1654 cm_return_if_fail(item != NULL);
1656 for (list = folderview_list; list != NULL; list = list->next) {
1657 folderview = (FolderView *)list->data;
1658 ctree = GTK_CMCTREE(folderview->ctree);
1660 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1661 if (node && item->search_match != matches) {
1662 item->search_match = matches;
1663 folderview_update_node(folderview, node);
1668 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1670 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1671 FolderView *folderview = (FolderView *)data;
1673 GtkCMCTreeNode *node;
1674 cm_return_val_if_fail(update_info != NULL, TRUE);
1675 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1676 cm_return_val_if_fail(folderview != NULL, FALSE);
1678 ctree = GTK_CMCTREE(folderview->ctree);
1680 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1683 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1684 folderview_update_node(folderview, node);
1686 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1687 update_info->item == folderview->summaryview->folder_item &&
1688 update_info->item != NULL)
1689 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1690 summary_show(folderview->summaryview, update_info->item);
1696 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1697 GNode *gnode, GtkCMCTreeNode *cnode,
1700 FolderView *folderview = (FolderView *)data;
1701 FolderItem *item = FOLDER_ITEM(gnode->data);
1703 cm_return_val_if_fail(item != NULL, FALSE);
1705 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1706 folderview_update_node(folderview, cnode);
1711 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1714 FolderView *folderview = (FolderView *)data;
1717 if (GTK_CMCTREE_ROW(node)->children) {
1718 item = gtk_cmctree_node_get_row_data(ctree, node);
1719 cm_return_if_fail(item != NULL);
1721 if (!item->collapsed)
1722 gtk_cmctree_expand(ctree, node);
1724 folderview_update_node(folderview, node);
1728 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1729 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1732 GtkCMCTreeNode *node, *parent, *sibling;
1734 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1736 g_warning("%s not found.\n", item->path);
1738 parent = GTK_CMCTREE_ROW(node)->parent;
1739 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1740 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1742 sibling = GTK_CMCTREE_ROW(parent)->children;
1746 tmp = gtk_cmctree_node_get_row_data
1748 if (tmp->stype != F_NORMAL)
1749 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1753 if (node != sibling)
1754 gtk_cmctree_move(ctree, node, parent, sibling);
1761 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1764 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1765 GtkCMCTreeNode *prev = NULL;
1767 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1768 gtk_sctree_sort_recursive(ctree, root);
1769 if (root && GTK_CMCTREE_ROW(root)->parent) {
1770 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1773 set_special_folder(ctree, folder->inbox, root, &prev);
1774 set_special_folder(ctree, folder->outbox, root, &prev);
1775 set_special_folder(ctree, folder->draft, root, &prev);
1776 set_special_folder(ctree, folder->queue, root, &prev);
1777 set_special_folder(ctree, folder->trash, root, &prev);
1778 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1781 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1783 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1784 GtkCMCTreeNode *root;
1786 cm_return_if_fail(folder != NULL);
1788 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1789 folderview_gnode_func, folderview);
1790 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1792 folderview_sort_folders(folderview, root, folder);
1795 /* callback functions */
1796 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1797 GdkEventButton *event)
1801 FolderViewPopup *fpopup;
1802 GtkActionGroup *action_group;
1804 FolderItem *special_trash = NULL, *special_queue = NULL;
1806 GtkUIManager *ui_manager = gtk_ui_manager_new();
1808 if (folderview->ui_manager)
1809 g_object_unref(folderview->ui_manager);
1811 folderview->ui_manager = ui_manager;
1812 item = folderview_get_selected_item(folderview);
1814 cm_return_if_fail(item != NULL);
1815 cm_return_if_fail(item->folder != NULL);
1816 folder = item->folder;
1818 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1821 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1823 fpopup = g_hash_table_lookup(folderview_popups, "common");
1824 action_group = g_hash_table_lookup(folderview->popups, "common");
1827 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1828 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1829 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1831 if (fpopup->add_menuitems)
1832 fpopup->add_menuitems(ui_manager, item);
1834 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1835 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1836 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1837 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1838 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1839 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1841 if (fpopup->set_sensitivity != NULL)
1842 fpopup->set_sensitivity(ui_manager, item);
1844 if (NULL != (ac = account_find_from_item(item))) {
1845 special_trash = account_get_special_folder(ac, F_TRASH);
1846 special_queue = account_get_special_folder(ac, F_QUEUE);
1849 if ((item == folder->trash || item == special_trash
1850 || folder_has_parent_of_type(item, F_TRASH))) {
1851 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1855 if ((item == folder->queue || item == special_queue
1856 || folder_has_parent_of_type(item, F_QUEUE))) {
1857 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1858 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1861 #define SET_SENS(name, sens) \
1862 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1864 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1865 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1866 folderview->selected == folderview->opened);
1867 SET_SENS("FolderViewPopup/Properties", TRUE);
1869 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1870 item->total_msgs >= 1 && !item->processing_pending);
1871 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1872 !item->no_select && !item->processing_pending);
1874 if (item == folder->trash || item == special_trash
1875 || folder_has_parent_of_type(item, F_TRASH)) {
1876 GSList *msglist = folder_item_get_msg_list(item);
1877 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1878 procmsg_msg_list_free(msglist);
1880 if (item == folder->queue || item == special_queue
1881 || folder_has_parent_of_type(item, F_QUEUE)) {
1882 GSList *msglist = folder_item_get_msg_list(item);
1883 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1884 procmsg_msg_list_free(msglist);
1888 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1889 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1890 g_signal_connect(G_OBJECT(popup), "selection_done",
1891 G_CALLBACK(folderview_popup_close),
1893 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1894 event->button, event->time);
1897 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1898 FolderView *folderview)
1900 GtkCMCList *clist = GTK_CMCLIST(ctree);
1901 gint prev_row = -1, row = -1, column = -1;
1903 if (!event) return FALSE;
1905 if (event->button == 1 || event->button == 2) {
1906 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1907 folderview->open_folder = TRUE;
1909 if (event->type == GDK_2BUTTON_PRESS) {
1910 if (clist->selection) {
1911 GtkCMCTreeNode *node;
1913 node = GTK_CMCTREE_NODE(clist->selection->data);
1915 gtk_cmctree_toggle_expansion(
1918 folderview->open_folder = FALSE;
1925 if (event->button == 2 || event->button == 3) {
1927 if (clist->selection) {
1928 GtkCMCTreeNode *node;
1930 node = GTK_CMCTREE_NODE(clist->selection->data);
1932 prev_row = gtkut_ctree_get_nth_from_node
1933 (GTK_CMCTREE(ctree), node);
1936 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1939 if (prev_row != row) {
1940 gtk_cmclist_unselect_all(clist);
1941 if (event->button == 2)
1942 folderview_select_node
1944 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1947 gtk_cmclist_select_row(clist, row, column);
1951 if (event->button != 3) return FALSE;
1953 folderview_set_sens_and_popup_menu(folderview, row, event);
1957 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1958 FolderView *folderview)
1960 int row = -1, column = -1;
1962 if (!event) return FALSE;
1964 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1967 if (event->button == 1 && folderview->open_folder == FALSE &&
1968 folderview->opened != NULL) {
1969 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1970 folderview->opened);
1971 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1977 #define BREAK_ON_MODIFIER_KEY() \
1978 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1980 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1981 FolderView *folderview)
1983 if (!event) return FALSE;
1985 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1988 switch (event->keyval) {
1990 #ifndef GENERIC_UMPC
1991 case GDK_KEY_Return:
1992 case GDK_KEY_KP_Enter:
1994 if (folderview->selected) {
1995 folderview_select_node(folderview,
1996 folderview->selected);
2000 case GDK_KEY_Return:
2001 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2002 gtk_cmctree_toggle_expansion(
2003 GTK_CMCTREE(folderview->ctree),
2004 folderview->selected);
2009 BREAK_ON_MODIFIER_KEY();
2010 if (folderview->selected) {
2011 if (folderview->opened == folderview->selected &&
2012 (!folderview->summaryview->folder_item ||
2013 folderview->summaryview->folder_item->total_msgs == 0))
2014 folderview_select_next_unread(folderview, TRUE);
2016 folderview_select_node(folderview,
2017 folderview->selected);
2027 typedef struct _PostponedSelectData
2030 GtkCMCTreeNode *row;
2032 FolderView *folderview;
2033 } PostponedSelectData;
2035 static gboolean postpone_select(void *data)
2037 PostponedSelectData *psdata = (PostponedSelectData *)data;
2038 debug_print("trying again\n");
2040 psdata->folderview->postpone_select_id = 0;
2041 psdata->folderview->open_folder = TRUE;
2042 main_window_cursor_normal(psdata->folderview->mainwin);
2043 STATUSBAR_POP(psdata->folderview->mainwin);
2044 folderview_selected(psdata->ctree, psdata->row,
2045 psdata->column, psdata->folderview);
2050 void folderview_close_opened(FolderView *folderview)
2052 if (folderview->opened) {
2053 FolderItem *olditem;
2055 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2056 folderview->opened);
2058 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2059 olditem->path ? olditem->path:olditem->name);
2060 /* will be null if we just moved the previously opened folder */
2061 STATUSBAR_PUSH(folderview->mainwin, buf);
2062 main_window_cursor_wait(folderview->mainwin);
2064 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2065 summary_show(folderview->summaryview, NULL);
2066 folder_item_close(olditem);
2067 main_window_cursor_normal(folderview->mainwin);
2068 STATUSBAR_POP(folderview->mainwin);
2069 if (olditem->folder->klass->item_closed)
2070 olditem->folder->klass->item_closed(olditem);
2075 if (folderview->opened &&
2076 !GTK_CMCTREE_ROW(folderview->opened)->children)
2077 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2079 folderview->opened = NULL;
2081 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2082 gint column, FolderView *folderview)
2084 static gboolean can_select = TRUE; /* exclusive lock */
2089 GtkCMCTreeNode *old_opened = folderview->opened;
2091 folderview->selected = row;
2093 debug_print("newly selected %p, opened %p\n", folderview->selected,
2094 folderview->opened);
2095 if (folderview->opened == row) {
2096 folderview->open_folder = FALSE;
2101 item = gtk_cmctree_node_get_row_data(ctree, row);
2104 folderview->open_folder = FALSE;
2108 if (!can_select || summary_is_locked(folderview->summaryview)) {
2109 if (folderview->opened) {
2110 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2111 gtk_cmctree_select(ctree, folderview->opened);
2113 folderview->open_folder = FALSE;
2118 if (!folderview->open_folder) {
2125 /* Save cache for old folder */
2126 /* We don't want to lose all caches if sylpheed crashed */
2127 /* resets folderview->opened to NULL */
2128 folderview_close_opened(folderview);
2130 /* CLAWS: set compose button type: news folder items
2131 * always have a news folder as parent */
2133 toolbar_set_compose_button
2134 (folderview->mainwin->toolbar,
2135 FOLDER_TYPE(item->folder) == F_NEWS ?
2136 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2139 debug_print("Folder %s is selected\n", item->path);
2141 if (!GTK_CMCTREE_ROW(row)->children)
2142 gtk_cmctree_expand(ctree, row);
2144 /* ungrab the mouse event */
2145 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2146 gtk_grab_remove(GTK_WIDGET(ctree));
2147 if (gdk_pointer_is_grabbed())
2148 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2152 /* TODO: wwp: avoid displaying (null) in the status bar */
2153 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2154 item->path : "(null)");
2155 debug_print("%s\n", buf);
2156 STATUSBAR_PUSH(folderview->mainwin, buf);
2159 main_window_cursor_wait(folderview->mainwin);
2161 if (folderview->scanning_folder == item->folder) {
2164 res = folder_item_open(item);
2167 if (res == -1 && item->no_select == FALSE) {
2168 main_window_cursor_normal(folderview->mainwin);
2169 STATUSBAR_POP(folderview->mainwin);
2171 alertpanel_error(_("Folder could not be opened."));
2173 folderview->open_folder = FALSE;
2177 } else if (res == -2 && item->no_select == FALSE) {
2178 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2179 data->ctree = ctree;
2181 data->column = column;
2182 data->folderview = folderview;
2183 debug_print("postponing open of %s till end of scan\n",
2184 item->path ? item->path:item->name);
2185 folderview->open_folder = FALSE;
2187 if (folderview->postpone_select_id != 0)
2188 g_source_remove(folderview->postpone_select_id);
2189 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2194 main_window_cursor_normal(folderview->mainwin);
2197 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2198 opened = summary_show(folderview->summaryview, item);
2200 folder_clean_cache_memory(item);
2203 gtkut_ctree_set_focus_row(ctree, old_opened);
2204 gtk_cmctree_select(ctree, old_opened);
2205 folderview->opened = old_opened;
2207 folderview->opened = row;
2208 if (gtk_cmctree_node_is_visible(ctree, row)
2209 != GTK_VISIBILITY_FULL)
2210 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2213 STATUSBAR_POP(folderview->mainwin);
2215 folderview->open_folder = FALSE;
2220 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2221 FolderView *folderview)
2225 item = gtk_cmctree_node_get_row_data(ctree, node);
2226 cm_return_if_fail(item != NULL);
2227 item->collapsed = FALSE;
2228 folderview_update_node(folderview, node);
2231 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2232 FolderView *folderview)
2236 item = gtk_cmctree_node_get_row_data(ctree, node);
2237 cm_return_if_fail(item != NULL);
2238 item->collapsed = TRUE;
2239 folderview_update_node(folderview, node);
2242 static void folderview_popup_close(GtkMenuShell *menu_shell,
2243 FolderView *folderview)
2245 if (!folderview->opened) return;
2247 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2250 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2251 FolderView *folderview)
2253 FolderColumnType type = folderview->col_state[column].type;
2255 prefs_common.folder_col_size[type] = width;
2258 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2260 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2261 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2262 GtkCMCTreeNode *node, *parent_node;
2263 gint *col_pos = folderview->col_pos;
2264 FolderItemUpdateData hookdata;
2266 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2267 if (parent_node == NULL)
2270 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2272 text[col_pos[F_COL_FOLDER]] = item->name;
2273 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2278 gtk_cmctree_expand(ctree, parent_node);
2279 gtk_cmctree_node_set_row_data(ctree, node, item);
2281 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2282 folderview_sort_folders(folderview, parent_node, item->folder);
2284 hookdata.item = item;
2285 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2286 hookdata.msg = NULL;
2287 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2289 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2292 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2294 FolderView *folderview = (FolderView *)data;
2295 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2297 GSList *mlist = NULL;
2299 FolderItem *special_trash = NULL;
2302 if (!folderview->selected) return;
2303 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2304 cm_return_if_fail(item != NULL);
2305 cm_return_if_fail(item->folder != NULL);
2307 if (NULL != (ac = account_find_from_item(item)))
2308 special_trash = account_get_special_folder(ac, F_TRASH);
2310 if (item != item->folder->trash && item != special_trash
2311 && !folder_has_parent_of_type(item, F_TRASH)) return;
2313 if (prefs_common.ask_on_clean) {
2314 if (alertpanel(_("Empty trash"),
2315 _("Delete all messages in trash?"),
2316 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2320 mlist = folder_item_get_msg_list(item);
2322 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2323 MsgInfo * msginfo = (MsgInfo *) cur->data;
2324 if (MSG_IS_LOCKED(msginfo->flags))
2326 /* is it partially received? (partial_recv isn't cached) */
2327 if (msginfo->total_size != 0 &&
2328 msginfo->size != (off_t)msginfo->total_size)
2329 partial_mark_for_delete(msginfo);
2331 procmsg_msg_list_free(mlist);
2333 folder_item_remove_all_msg(item);
2336 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2338 FolderView *folderview = (FolderView *)data;
2339 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2341 FolderItem *special_queue = NULL;
2343 gchar *errstr = NULL;
2345 if (!folderview->selected) return;
2346 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2347 cm_return_if_fail(item != NULL);
2348 cm_return_if_fail(item->folder != NULL);
2350 if (NULL != (ac = account_find_from_item(item)))
2351 special_queue = account_get_special_folder(ac, F_QUEUE);
2353 if (item != item->folder->queue && item != special_queue
2354 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2356 if (procmsg_queue_is_empty(item))
2359 if (prefs_common.work_offline)
2360 if (alertpanel(_("Offline warning"),
2361 _("You're working offline. Override?"),
2362 GTK_STOCK_NO, GTK_STOCK_YES,
2363 NULL) != G_ALERTALTERNATE)
2366 /* ask for confirmation before sending queued messages only
2367 in online mode and if there is at least one message queued
2368 in any of the folder queue
2370 if (prefs_common.confirm_send_queued_messages) {
2371 if (!prefs_common.work_offline) {
2372 if (alertpanel(_("Send queued messages"),
2373 _("Send all queued messages?"),
2374 GTK_STOCK_CANCEL, _("_Send"),
2375 NULL) != G_ALERTALTERNATE)
2380 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2382 alertpanel_error_log(_("Some errors occurred while "
2383 "sending queued messages."));
2385 alertpanel_error_log(_("Some errors occurred "
2386 "while sending queued messages:\n%s"), errstr);
2392 static void folderview_search_cb(GtkAction *action, gpointer data)
2394 FolderView *folderview = (FolderView *)data;
2395 summary_search(folderview->summaryview);
2398 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2400 FolderView *folderview = (FolderView *)data;
2401 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2404 if (!folderview->selected) return;
2406 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2407 cm_return_if_fail(item != NULL);
2408 cm_return_if_fail(item->folder != NULL);
2410 item->processing_pending = TRUE;
2411 folder_item_apply_processing(item);
2412 item->processing_pending = FALSE;
2415 static void folderview_property_cb(GtkAction *action, gpointer data)
2417 FolderView *folderview = (FolderView *)data;
2418 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2421 if (!folderview->selected) return;
2423 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2424 cm_return_if_fail(item != NULL);
2425 cm_return_if_fail(item->folder != NULL);
2427 prefs_folder_item_open(item);
2430 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2432 GSList *list = NULL;
2433 GSList *done = NULL;
2434 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2436 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2437 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2438 && list->data != node) {
2439 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2440 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2443 for (list = done; list != NULL; list = g_slist_next(list)) {
2444 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2450 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2451 FolderItem *to_folder, gboolean copy)
2453 FolderItem *new_folder = NULL;
2457 cm_return_if_fail(folderview != NULL);
2458 cm_return_if_fail(from_folder != NULL);
2459 cm_return_if_fail(to_folder != NULL);
2461 if (prefs_common.warn_dnd) {
2462 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2463 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2464 from_folder->name, to_folder->name);
2465 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2466 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2467 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2470 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2472 else if (status & G_ALERTDISABLE)
2473 prefs_common.warn_dnd = FALSE;
2476 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2477 from_folder->name, to_folder->name);
2478 STATUSBAR_PUSH(folderview->mainwin, buf);
2480 summary_clear_all(folderview->summaryview);
2481 folderview->opened = NULL;
2482 folderview->selected = NULL;
2483 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2485 main_window_cursor_wait(folderview->mainwin);
2487 statusbar_verbosity_set(FALSE);
2488 folder_item_update_freeze();
2489 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2490 statusbar_verbosity_set(FALSE);
2491 main_window_cursor_normal(folderview->mainwin);
2492 STATUSBAR_POP(folderview->mainwin);
2493 folder_item_update_thaw();
2494 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2496 folderview_sort_folders(folderview,
2497 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2498 NULL, to_folder), new_folder->folder);
2499 folderview_select(folderview, new_folder);
2501 statusbar_verbosity_set(FALSE);
2502 main_window_cursor_normal(folderview->mainwin);
2503 STATUSBAR_POP(folderview->mainwin);
2504 folder_item_update_thaw();
2506 case F_MOVE_FAILED_DEST_IS_PARENT:
2507 alertpanel_error(_("Source and destination are the same."));
2509 case F_MOVE_FAILED_DEST_IS_CHILD:
2510 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2511 _("Can't move a folder to one of its children."));
2513 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2514 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2517 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2522 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2525 static gint folderview_clist_compare(GtkCMCList *clist,
2526 gconstpointer ptr1, gconstpointer ptr2)
2528 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2529 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2531 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2533 return item1->order - item2->order;
2536 // if only one folder has an order it comes first
2537 if (item1->order > 0)
2541 if (item2->order > 0)
2547 return (item2->name != NULL);
2551 return g_utf8_collate(item1->name, item2->name);
2554 static void folderview_processing_cb(GtkAction *action, gpointer data)
2556 FolderView *folderview = (FolderView *)data;
2557 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2561 if (!folderview->selected) return;
2563 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2564 cm_return_if_fail(item != NULL);
2565 cm_return_if_fail(item->folder != NULL);
2567 id = folder_item_get_identifier(item);
2568 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2571 prefs_filtering_open(&item->prefs->processing, title,
2572 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2576 void folderview_set_target_folder_color(gint color_op)
2580 FolderView *folderview;
2582 for (list = folderview_list; list != NULL; list = list->next) {
2583 folderview = (FolderView *)list->data;
2584 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2586 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2587 folderview->color_op;
2593 static gchar *last_smallfont = NULL;
2594 static gchar *last_normalfont = NULL;
2595 static gchar *last_boldfont = NULL;
2596 static gboolean last_derive = 0;
2598 void folderview_reinit_fonts(FolderView *folderview)
2601 g_free(last_smallfont);
2602 last_smallfont = NULL;
2603 g_free(last_normalfont);
2604 last_normalfont = NULL;
2605 g_free(last_boldfont);
2606 last_boldfont = NULL;
2609 void folderview_reflect_prefs(void)
2611 gboolean update_font = FALSE;
2612 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2613 FolderItem *item = folderview_get_selected_item(folderview);
2614 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2615 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2616 gint height = gtk_adjustment_get_value(pos);
2618 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2619 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2620 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2621 last_derive != prefs_common.derive_from_normal_font)
2624 g_free(last_smallfont);
2625 last_smallfont = g_strdup(SMALL_FONT);
2626 g_free(last_normalfont);
2627 last_normalfont = g_strdup(NORMAL_FONT);
2628 g_free(last_boldfont);
2629 last_boldfont = g_strdup(BOLD_FONT);
2630 last_derive = prefs_common.derive_from_normal_font;
2633 normal_style = normal_color_style = bold_style =
2634 bold_color_style = bold_tgtfold_style = NULL;
2636 folderview_init(folderview);
2638 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2639 folderview_column_set_titles(folderview);
2640 folderview_set_all();
2642 g_signal_handlers_block_by_func
2643 (G_OBJECT(folderview->ctree),
2644 G_CALLBACK(folderview_selected), folderview);
2647 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2648 GTK_CMCTREE(folderview->ctree), NULL, item);
2650 folderview_select(folderview, item);
2651 folderview->open_folder = FALSE;
2652 folderview->selected = node;
2655 g_signal_handlers_unblock_by_func
2656 (G_OBJECT(folderview->ctree),
2657 G_CALLBACK(folderview_selected), folderview);
2659 pos = gtk_scrolled_window_get_vadjustment(
2660 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2661 gtk_adjustment_set_value(pos, height);
2662 gtk_adjustment_changed(pos);
2663 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2666 static void drag_state_stop(FolderView *folderview)
2668 if (folderview->drag_timer_id)
2669 g_source_remove(folderview->drag_timer_id);
2670 folderview->drag_timer_id = 0;
2671 folderview->drag_node = NULL;
2674 static gboolean folderview_defer_expand(FolderView *folderview)
2676 if (folderview->drag_node) {
2677 folderview_recollapse_nodes(folderview, folderview->drag_node);
2678 if (folderview->drag_item->collapsed) {
2679 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2680 folderview->nodes_to_recollapse = g_slist_append
2681 (folderview->nodes_to_recollapse, folderview->drag_node);
2684 folderview->drag_item = NULL;
2685 folderview->drag_timer_id = 0;
2689 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2691 /* the idea is that we call drag_state_start() whenever we want expansion to
2692 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2693 * we need to call drag_state_stop() */
2694 drag_state_stop(folderview);
2695 /* request expansion */
2696 if (0 != (folderview->drag_timer_id = g_timeout_add
2697 (prefs_common.hover_timeout,
2698 (GSourceFunc)folderview_defer_expand,
2700 folderview->drag_node = node;
2701 folderview->drag_item = item;
2704 #ifndef GENERIC_UMPC
2705 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2706 FolderView *folderview)
2708 GdkDragContext *context;
2710 cm_return_if_fail(folderview != NULL);
2711 if (folderview->selected == NULL) return;
2712 if (folderview->nodes_to_recollapse)
2713 g_slist_free(folderview->nodes_to_recollapse);
2714 folderview->nodes_to_recollapse = NULL;
2715 context = gtk_drag_begin(widget, folderview->target_list,
2716 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2717 gtk_drag_set_icon_default(context);
2720 static void folderview_drag_data_get(GtkWidget *widget,
2721 GdkDragContext *drag_context,
2722 GtkSelectionData *selection_data,
2725 FolderView *folderview)
2729 gchar *source = NULL;
2730 if (info == TARGET_DUMMY) {
2731 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2732 cur != NULL; cur = cur->next) {
2733 item = gtk_cmctree_node_get_row_data
2734 (GTK_CMCTREE(folderview->ctree),
2735 GTK_CMCTREE_NODE(cur->data));
2737 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2738 gtk_selection_data_set(selection_data,
2739 gtk_selection_data_get_target(selection_data), 8,
2740 source, strlen(source));
2746 g_warning("unknown info %d\n", info);
2750 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2752 FolderUpdateData *hookdata;
2753 FolderView *folderview;
2757 folderview = (FolderView *) userdata;
2758 cm_return_val_if_fail(hookdata != NULL, FALSE);
2759 cm_return_val_if_fail(folderview != NULL, FALSE);
2761 ctree = folderview->ctree;
2762 cm_return_val_if_fail(ctree != NULL, FALSE);
2764 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2765 folderview_create_folder_node(folderview, hookdata->item);
2766 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2767 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2768 NULL, folder_item_parent(hookdata->item));
2769 folderview_sort_folders(folderview, node, hookdata->folder);
2770 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2771 GtkCMCTreeNode *node;
2773 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2775 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2776 if (folderview->selected == node)
2777 folderview->selected = NULL;
2778 if (folderview->opened == node)
2779 folderview->opened = NULL;
2781 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2782 folderview_set(folderview);
2787 static gboolean folderview_dnd_scroll_cb(gpointer data)
2789 FolderView *folderview = (FolderView *)data;
2790 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2791 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2792 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2793 gint max = (int)gtk_adjustment_get_upper(pos) -
2794 (int)gtk_adjustment_get_page_size(pos);
2796 if (folderview->scroll_value == 0) {
2797 folderview->scroll_timeout_id = 0;
2801 if (folderview->scroll_value > 0 && new_val > max) {
2803 } else if (folderview->scroll_value < 0 && new_val < 0) {
2806 gtk_adjustment_set_value(pos, new_val);
2811 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2812 GdkDragContext *context,
2816 FolderView *folderview)
2819 FolderItem *item = NULL, *src_item = NULL;
2820 GtkCMCTreeNode *node = NULL;
2821 gboolean acceptable = FALSE;
2822 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2823 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2824 int height = (int)gtk_adjustment_get_page_size(pos);
2825 int total_height = (int)gtk_adjustment_get_upper(pos);
2826 int vpos = (int)gtk_adjustment_get_value(pos);
2827 int offset = prefs_common.show_col_headers ? 24:0;
2830 if (gtk_cmclist_get_selection_info
2831 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2832 GtkWidget *srcwidget;
2834 if (y > height - (48 - offset) && height + vpos < total_height) {
2835 dist = -(height - (48 - offset) - y);
2836 folderview->scroll_value = 1.41f * (1+(dist / 6));
2837 } else if (y < 72 - (24 - offset) && y >= 0) {
2838 dist = 72 - (24 - offset) - y;
2839 folderview->scroll_value = -1.41f * (1+(dist / 6));
2841 folderview->scroll_value = 0;
2843 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2844 folderview->scroll_timeout_id =
2845 g_timeout_add(30, folderview_dnd_scroll_cb,
2849 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2850 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2851 src_item = folderview->summaryview->folder_item;
2853 srcwidget = gtk_drag_get_source_widget(context);
2854 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2855 /* comes from summaryview */
2856 /* we are copying messages, so only accept folder items that are not
2857 the source item, are no root items and can copy messages */
2858 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2859 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2860 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2862 } else if (srcwidget == folderview->ctree) {
2863 /* comes from folderview */
2864 /* we are moving folder items, only accept folders that are not
2865 the source items and can copy messages and create folder items */
2866 if (item && item->folder && src_item && src_item != item &&
2867 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2868 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2869 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2870 || item->folder == src_item->folder))
2873 /* comes from another app */
2874 /* we are adding messages, so only accept folder items that are
2875 no root items and can copy messages */
2876 if (item && item->folder && folder_item_parent(item) != NULL
2877 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2878 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2883 if (acceptable || (src_item && src_item == item))
2884 drag_state_start(folderview, node, item);
2887 g_signal_handlers_block_by_func
2889 G_CALLBACK(folderview_selected), folderview);
2890 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2891 g_signal_handlers_unblock_by_func
2893 G_CALLBACK(folderview_selected), folderview);
2894 gdk_drag_status(context,
2895 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2896 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2898 if (folderview->opened)
2899 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2900 gdk_drag_status(context, 0, time);
2906 static void folderview_drag_leave_cb(GtkWidget *widget,
2907 GdkDragContext *context,
2909 FolderView *folderview)
2911 drag_state_stop(folderview);
2912 folderview->scroll_value = 0;
2913 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2916 static void free_info (gpointer stuff, gpointer data)
2921 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2922 guint time, FolderItem *item)
2925 GSList *msglist = NULL;
2926 list = uri_list_extract_filenames(data);
2927 if (!(item && item->folder && folder_item_parent(item) != NULL
2928 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2930 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2931 debug_print("item doesn't fit\n");
2935 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2936 debug_print("list is empty\n");
2939 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2940 MsgFileInfo *info = NULL;
2942 if (file_is_email((gchar *)tmp->data)) {
2943 info = g_new0(MsgFileInfo, 1);
2944 info->msginfo = NULL;
2945 info->file = (gchar *)tmp->data;
2946 msglist = g_slist_prepend(msglist, info);
2947 debug_print("file is a mail\n");
2949 debug_print("file isn't a mail\n");
2953 msglist = g_slist_reverse(msglist);
2954 folder_item_add_msgs(item, msglist, FALSE);
2955 g_slist_foreach(msglist, free_info, NULL);
2956 g_slist_free(msglist);
2957 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2959 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2961 list_free_strings(list);
2965 static void folderview_drag_received_cb(GtkWidget *widget,
2966 GdkDragContext *drag_context,
2969 GtkSelectionData *data,
2972 FolderView *folderview)
2975 FolderItem *item = NULL, *src_item;
2976 GtkCMCTreeNode *node;
2977 int offset = prefs_common.show_col_headers ? 24:0;
2979 folderview->scroll_value = 0;
2981 if (info == TARGET_DUMMY) {
2982 drag_state_stop(folderview);
2983 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2984 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2985 /* comes from summaryview */
2986 if (gtk_cmclist_get_selection_info
2987 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2990 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2991 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2992 src_item = folderview->summaryview->folder_item;
2994 if (item->no_select) {
2995 alertpanel_error(_("The destination folder can only be used to "
2996 "store subfolders."));
2999 /* re-check (due to acceptable possibly set for folder moves */
3000 if (!(item && item->folder && item->path && !item->no_select &&
3001 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3004 if (item && src_item) {
3005 switch (gdk_drag_context_get_selected_action(drag_context)) {
3006 case GDK_ACTION_COPY:
3007 summary_copy_selected_to(folderview->summaryview, item);
3008 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3010 case GDK_ACTION_MOVE:
3011 case GDK_ACTION_DEFAULT:
3013 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3014 summary_copy_selected_to(folderview->summaryview, item);
3016 summary_move_selected_to(folderview->summaryview, item);
3017 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3020 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3022 /* comes from folderview */
3024 gboolean folder_is_normal = TRUE;
3025 gboolean copy = (GDK_ACTION_COPY ==
3026 gdk_drag_context_get_selected_action(drag_context));
3028 source = (char *)gtk_selection_data_get_data(data) + 17;
3029 if (gtk_cmclist_get_selection_info
3030 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3032 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3035 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3036 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3037 src_item = folder_find_item_from_identifier(source);
3041 src_item->stype == F_NORMAL &&
3042 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3043 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3044 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3045 !folder_has_parent_of_type(src_item, F_TRASH);
3046 if (!item || !src_item || !folder_is_normal) {
3047 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3051 folderview_move_folder(folderview, src_item, item, copy);
3052 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3054 folderview->nodes_to_recollapse = NULL;
3055 } else if (info == TARGET_MAIL_URI_LIST) {
3056 if (gtk_cmclist_get_selection_info
3057 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3060 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3062 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3063 debug_print("no node\n");
3066 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3068 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3069 debug_print("no item\n");
3072 folderview_finish_dnd(gtk_selection_data_get_data(data),
3073 drag_context, time, item);
3077 static void folderview_drag_end_cb(GtkWidget *widget,
3078 GdkDragContext *drag_context,
3079 FolderView *folderview)
3081 drag_state_stop(folderview);
3082 folderview->scroll_value = 0;
3083 g_slist_free(folderview->nodes_to_recollapse);
3084 folderview->nodes_to_recollapse = NULL;
3087 void folderview_register_popup(FolderViewPopup *fpopup)
3091 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3092 FolderView *folderview = folderviews->data;
3093 GtkActionGroup *factory;
3095 factory = create_action_group(folderview, fpopup);
3096 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3098 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3101 void folderview_unregister_popup(FolderViewPopup *fpopup)
3106 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3107 FolderView *folderview = folderviews->data;
3109 g_hash_table_remove(folderview->popups, fpopup->klass);
3111 g_hash_table_remove(folderview_popups, fpopup->klass);