2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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 if (prefs_common.enable_dotted_lines) {
449 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_DOTTED);
450 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
451 GTK_CMCTREE_EXPANDER_SQUARE);
453 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
454 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
455 GTK_CMCTREE_EXPANDER_TRIANGLE);
458 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
459 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
461 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
462 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
464 /* don't let title buttons take key focus */
465 for (i = 0; i < N_FOLDER_COLS; i++) {
466 GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(ctree)->column[i].button,
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
481 g_signal_connect(G_OBJECT(ctree), "popup-menu",
482 G_CALLBACK(folderview_popup_menu), folderview);
484 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
485 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
486 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
487 G_CALLBACK(folderview_popup_menu), folderview);
489 g_signal_connect(G_OBJECT(ctree), "button_release_event",
490 G_CALLBACK(folderview_button_released),
492 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
493 G_CALLBACK(folderview_selected), folderview);
495 /* drag-n-dropping folders on maemo is impractical as this
496 * opens the folder almost everytime */
497 g_signal_connect(G_OBJECT(ctree), "start_drag",
498 G_CALLBACK(folderview_start_drag), folderview);
500 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
501 G_CALLBACK(folderview_drag_data_get),
504 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
505 G_CALLBACK(folderview_tree_expanded),
507 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
508 G_CALLBACK(folderview_tree_collapsed),
511 g_signal_connect(G_OBJECT(ctree), "resize_column",
512 G_CALLBACK(folderview_col_resized),
516 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
517 folderview_drag_types, 2,
518 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
519 g_signal_connect(G_OBJECT(ctree), "drag_motion",
520 G_CALLBACK(folderview_drag_motion_cb),
522 g_signal_connect(G_OBJECT(ctree), "drag_leave",
523 G_CALLBACK(folderview_drag_leave_cb),
525 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
526 G_CALLBACK(folderview_drag_received_cb),
528 g_signal_connect(G_OBJECT(ctree), "drag_end",
529 G_CALLBACK(folderview_drag_end_cb),
532 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
537 void folderview_set_column_order(FolderView *folderview)
539 GtkWidget *ctree = folderview->ctree;
540 FolderItem *item = folderview_get_selected_item(folderview);
541 FolderItem *sel_item = NULL, *op_item = NULL;
542 GtkWidget *scrolledwin = folderview->scrolledwin;
544 if (folderview->selected)
545 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
546 if (folderview->opened)
547 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
549 debug_print("recreating tree...\n");
550 gtk_widget_destroy(folderview->ctree);
553 folderview->ctree = ctree = folderview_ctree_create(folderview);
554 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
555 GTK_CMCLIST(ctree)->hadjustment);
556 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
557 GTK_CMCLIST(ctree)->vadjustment);
558 gtk_widget_show(ctree);
561 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
563 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
565 folderview_set(folderview);
566 folderview_column_set_titles(folderview);
568 folderview_select(folderview,item);
571 FolderView *folderview_create(void)
573 FolderView *folderview;
574 GtkWidget *scrolledwin;
577 debug_print("Creating folder view...\n");
578 folderview = g_new0(FolderView, 1);
580 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
581 gtk_scrolled_window_set_policy
582 (GTK_SCROLLED_WINDOW(scrolledwin),
583 GTK_POLICY_AUTOMATIC,
584 prefs_common.folderview_vscrollbar_policy);
585 gtk_widget_set_size_request(scrolledwin,
586 prefs_common.folderview_width,
587 prefs_common.folderview_height);
589 folderview->scrolledwin = scrolledwin;
590 ctree = folderview_ctree_create(folderview);
592 /* create popup factories */
593 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
594 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
596 folderview->ctree = ctree;
598 folderview->folder_update_callback_id =
599 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
600 folderview->folder_item_update_callback_id =
601 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
603 gtk_widget_show_all(scrolledwin);
605 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
606 folderview_list = g_list_append(folderview_list, folderview);
607 folderview->deferred_refresh_id = -1;
612 void folderview_init(FolderView *folderview)
614 GtkWidget *ctree = folderview->ctree;
617 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
618 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
619 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
620 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
621 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
622 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
623 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
665 PangoFontDescription *font_desc;
666 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
667 font_desc = pango_font_description_from_string(NORMAL_FONT);
669 if (normal_style->font_desc)
670 pango_font_description_free
671 (normal_style->font_desc);
672 normal_style->font_desc = font_desc;
674 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
675 normal_color_style = gtk_style_copy(normal_style);
676 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
678 gtk_widget_set_style(ctree, normal_style);
682 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
683 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
684 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
685 pango_font_description_set_weight
686 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
688 PangoFontDescription *font_desc;
689 font_desc = pango_font_description_from_string(BOLD_FONT);
691 if (bold_style->font_desc)
692 pango_font_description_free
693 (bold_style->font_desc);
694 bold_style->font_desc = font_desc;
697 bold_color_style = gtk_style_copy(bold_style);
698 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
700 bold_tgtfold_style = gtk_style_copy(bold_style);
701 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
705 static gboolean folderview_defer_set(gpointer data)
707 FolderView *folderview = (FolderView *)data;
708 MainWindow *mainwin = folderview->mainwin;
712 if (mainwin->lock_count)
715 debug_print("doing deferred folderview_set now\n");
716 folderview_set(folderview);
718 folderview->deferred_refresh_id = -1;
722 void folderview_set(FolderView *folderview)
724 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
725 MainWindow *mainwin = folderview->mainwin;
726 FolderItem *sel_item = NULL, *op_item = NULL;
731 if (mainwin->lock_count) {
732 if (folderview->deferred_refresh_id == -1)
733 folderview->deferred_refresh_id =
734 g_timeout_add(500, folderview_defer_set, folderview);
735 debug_print("deferred folderview_set\n");
740 debug_print("Setting folder info...\n");
741 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
743 main_window_cursor_wait(mainwin);
745 if (folderview->selected)
746 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
747 if (folderview->opened)
748 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
750 folderview->selected = NULL;
751 folderview->opened = NULL;
753 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
754 gtk_cmclist_clear(GTK_CMCLIST(ctree));
756 folderview_set_folders(folderview);
759 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
761 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
763 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
764 main_window_cursor_normal(mainwin);
765 STATUSBAR_POP(mainwin);
769 void folderview_set_all(void)
773 for (list = folderview_list; list != NULL; list = list->next)
774 folderview_set((FolderView *)list->data);
777 void folderview_select(FolderView *folderview, FolderItem *item)
779 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
780 GtkCMCTreeNode *node;
781 GtkCMCTreeNode *old_selected = folderview->selected;
785 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
786 if (node) folderview_select_node(folderview, node);
788 if (old_selected != node)
789 folder_update_op_count();
792 static void mark_all_read_cb(GtkAction *action, gpointer data)
794 FolderView *folderview = (FolderView *)data;
798 item = folderview_get_selected_item(folderview);
802 if (folderview->summaryview->folder_item != item
803 && prefs_common.ask_mark_all_read) {
804 val = alertpanel_full(_("Mark all as read"),
805 _("Do you really want to mark all mails in this "
806 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
807 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
809 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
811 else if (val & G_ALERTDISABLE)
812 prefs_common.ask_mark_all_read = FALSE;
816 folder_item_update_freeze();
817 if (folderview->summaryview->folder_item != item)
818 summary_lock(folderview->summaryview);
820 summary_freeze(folderview->summaryview);
822 folderutils_mark_all_read(item);
824 if (folderview->summaryview->folder_item != item)
825 summary_unlock(folderview->summaryview);
827 summary_thaw(folderview->summaryview);
828 folder_item_update_thaw();
831 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
833 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
835 cm_return_if_fail(node != NULL);
837 if (folderview->open_folder) {
841 folderview->open_folder = TRUE;
842 gtkut_ctree_set_focus_row(ctree, node);
843 gtk_cmctree_select(ctree, node);
844 if ((folderview->summaryview->folder_item &&
845 folderview->summaryview->folder_item->total_msgs > 0) ||
846 prefs_common.layout_mode == SMALL_LAYOUT)
847 summary_grab_focus(folderview->summaryview);
849 gtk_widget_grab_focus(folderview->ctree);
851 gtkut_ctree_expand_parent_all(ctree, node);
854 void folderview_unselect(FolderView *folderview)
856 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
858 (GTK_CMCTREE(folderview->ctree), folderview->opened);
860 folderview->selected = folderview->opened = NULL;
863 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
864 GtkCMCTreeNode *node)
869 node = gtkut_ctree_node_next(ctree, node);
871 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
873 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
874 item = gtk_cmctree_node_get_row_data(ctree, node);
875 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
882 void folderview_select_next_marked(FolderView *folderview)
884 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
885 GtkCMCTreeNode *node = NULL;
886 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
887 gboolean last_open = prefs_common.always_show_msg;
889 prefs_common.summary_select_prio[0] = ACTION_MARKED;
890 prefs_common.always_show_msg = OPENMSG_ALWAYS;
892 if ((node = folderview_find_next_marked(ctree, folderview->opened))
894 folderview_select_node(folderview, node);
898 if (!folderview->opened ||
899 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
902 /* search again from the first node */
903 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
904 folderview_select_node(folderview, node);
907 prefs_common.summary_select_prio[0] = last_summary_select_prio;
908 prefs_common.always_show_msg = last_open;
911 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
912 GtkCMCTreeNode *node)
917 node = gtkut_ctree_node_next(ctree, node);
919 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
921 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
922 item = gtk_cmctree_node_get_row_data(ctree, node);
923 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
930 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
932 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
933 GtkCMCTreeNode *node = NULL;
934 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
935 gboolean last_open = prefs_common.always_show_msg;
937 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
938 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
940 if ((node = folderview_find_next_unread(ctree, folderview->opened))
942 folderview_select_node(folderview, node);
946 if (!folderview->opened ||
947 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
950 /* search again from the first node */
951 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
952 folderview_select_node(folderview, node);
955 prefs_common.summary_select_prio[0] = last_summary_select_prio;
956 prefs_common.always_show_msg = last_open;
959 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
960 GtkCMCTreeNode *node)
965 node = gtkut_ctree_node_next(ctree, node);
967 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
969 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
970 item = gtk_cmctree_node_get_row_data(ctree, node);
971 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
978 void folderview_select_next_new(FolderView *folderview)
980 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
981 GtkCMCTreeNode *node = NULL;
982 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
983 gboolean last_open = prefs_common.always_show_msg;
985 prefs_common.summary_select_prio[0] = ACTION_NEW;
986 prefs_common.always_show_msg = OPENMSG_ALWAYS;
988 if ((node = folderview_find_next_new(ctree, folderview->opened))
990 folderview_select_node(folderview, node);
994 if (!folderview->opened ||
995 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
998 /* search again from the first node */
999 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1000 folderview_select_node(folderview, node);
1003 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1004 prefs_common.always_show_msg = last_open;
1007 FolderItem *folderview_get_selected_item(FolderView *folderview)
1009 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1011 if (!folderview->selected) return NULL;
1012 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1015 static void folderview_set_folders(FolderView *folderview)
1018 list = folder_get_list();
1020 for (; list != NULL; list = list->next) {
1021 folderview_append_folder(folderview, FOLDER(list->data));
1025 static gchar *get_scan_str(FolderItem *item)
1028 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1029 item->folder->name, G_DIR_SEPARATOR,
1032 return g_strdup_printf(_("Scanning folder %s ..."),
1033 item->folder->name);
1035 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1039 for (list = folderview_list; list != NULL; list = list->next) {
1040 FolderView *folderview = (FolderView *)list->data;
1041 MainWindow *mainwin = folderview->mainwin;
1042 gchar *str = get_scan_str(item);
1044 STATUSBAR_PUSH(mainwin, str);
1045 STATUSBAR_POP(mainwin);
1050 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1053 MainWindow *mainwin = mainwindow_get_mainwindow();
1054 FolderView *folderview = NULL;
1055 GtkAdjustment *pos = NULL;
1058 cm_return_if_fail(folder != NULL);
1060 if (!folder->klass->scan_tree) return;
1063 alertpanel_full(_("Rebuild folder tree"),
1064 _("Rebuilding the folder tree will remove "
1065 "local caches. Do you want to continue?"),
1066 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1067 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1068 != G_ALERTALTERNATE) {
1074 window = label_window_create(_("Rebuilding folder tree..."));
1076 window = label_window_create(_("Scanning folder tree..."));
1079 folderview = mainwin->folderview;
1082 pos = gtk_scrolled_window_get_vadjustment(
1083 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1084 height = pos->value;
1087 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1088 folder_scan_tree(folder, rebuild);
1089 folder_set_ui_func(folder, NULL, NULL);
1091 folderview_set_all();
1094 pos = gtk_scrolled_window_get_vadjustment(
1095 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1096 gtk_adjustment_set_value(pos, height);
1097 gtk_adjustment_changed(pos);
1099 label_window_destroy(window);
1103 void folderview_fast_rescan_tree(Folder *folder)
1106 MainWindow *mainwin = mainwindow_get_mainwindow();
1107 FolderView *folderview = NULL;
1108 GtkAdjustment *pos = NULL;
1111 cm_return_if_fail(folder != NULL);
1113 if (!folder->klass->scan_tree) return;
1117 window = label_window_create(_("Scanning folder tree..."));
1120 folderview = mainwin->folderview;
1123 pos = gtk_scrolled_window_get_vadjustment(
1124 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1125 height = pos->value;
1128 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1129 folder_fast_scan_tree(folder);
1130 folder_set_ui_func(folder, NULL, NULL);
1132 folderview_set_all();
1135 pos = gtk_scrolled_window_get_vadjustment(
1136 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1137 gtk_adjustment_set_value(pos, height);
1138 gtk_adjustment_changed(pos);
1140 label_window_destroy(window);
1144 /** folderview_check_new()
1145 * Scan and update the folder and return the
1146 * count the number of new messages since last check.
1147 * \param folder the folder to check for new messages
1148 * \return the number of new messages since last check
1150 gint folderview_check_new(Folder *folder)
1154 FolderView *folderview;
1156 GtkCMCTreeNode *node;
1158 gint former_new_msgs = 0;
1159 gint former_new = 0, former_unread = 0, former_total;
1161 for (list = folderview_list; list != NULL; list = list->next) {
1162 folderview = (FolderView *)list->data;
1163 ctree = GTK_CMCTREE(folderview->ctree);
1164 folderview->scanning_folder = folder;
1166 main_window_lock(folderview->mainwin);
1168 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1169 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1171 item = gtk_cmctree_node_get_row_data(ctree, node);
1172 if (!item || !item->path || !item->folder) continue;
1173 if (item->no_select) continue;
1174 if (folder && folder != item->folder) continue;
1175 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1176 if (!item->prefs->newmailcheck) continue;
1177 if (item->processing_pending == TRUE) {
1178 debug_print("skipping %s, processing pending\n",
1179 item->path ? item->path : item->name);
1182 if (item->scanning != ITEM_NOT_SCANNING) {
1183 debug_print("skipping %s, scanning\n",
1184 item->path ? item->path : item->name);
1188 str = get_scan_str(item);
1190 STATUSBAR_PUSH(folderview->mainwin, str);
1194 folderview_scan_tree_func(item->folder, item, NULL);
1195 former_new = item->new_msgs;
1196 former_unread = item->unread_msgs;
1197 former_total = item->total_msgs;
1199 if (item->folder->klass->scan_required &&
1200 (item->folder->klass->scan_required(item->folder, item) ||
1201 item->folder->inbox == item ||
1202 item->opened == TRUE ||
1203 item->processing_pending == TRUE)) {
1204 if (folder_item_scan(item) < 0) {
1206 summaryview_unlock(folderview->summaryview, item);
1207 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1208 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1209 item->path ? item->path:item->name);
1210 STATUSBAR_POP(folderview->mainwin);
1212 } else if (!FOLDER_IS_LOCAL(folder)) {
1213 STATUSBAR_POP(folderview->mainwin);
1218 } else if (!item->folder->klass->scan_required) {
1219 if (folder_item_scan(item) < 0) {
1220 summaryview_unlock(folderview->summaryview, item);
1221 if (folder && !FOLDER_IS_LOCAL(folder)) {
1222 STATUSBAR_POP(folderview->mainwin);
1227 if (former_new != item->new_msgs ||
1228 former_unread != item->unread_msgs ||
1229 former_total != item->total_msgs)
1230 folderview_update_node(folderview, node);
1232 new_msgs += item->new_msgs;
1233 former_new_msgs += former_new;
1234 STATUSBAR_POP(folderview->mainwin);
1236 folderview->scanning_folder = NULL;
1237 main_window_unlock(folderview->mainwin);
1241 folder_write_list();
1242 /* Number of new messages since last check is the just the difference
1243 * between former_new_msgs and new_msgs. If new_msgs is less than
1244 * former_new_msgs, that would mean another session accessed the folder
1245 * and the result is not well defined.
1247 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1251 void folderview_check_new_all(void)
1255 FolderView *folderview;
1257 folderview = (FolderView *)folderview_list->data;
1260 main_window_lock(folderview->mainwin);
1261 window = label_window_create
1262 (_("Checking for new messages in all folders..."));
1264 list = folder_get_list();
1265 for (; list != NULL; list = list->next) {
1266 Folder *folder = list->data;
1268 folderview_check_new(folder);
1271 folder_write_list();
1272 folderview_set_all();
1274 label_window_destroy(window);
1275 main_window_unlock(folderview->mainwin);
1279 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1285 if (!item || !item->folder || !item->folder->node)
1288 node = item->folder->node;
1290 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1291 node = node->children;
1294 (item->new_msgs > 0 ||
1295 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1299 while (node != NULL) {
1300 if (node && node->data) {
1301 FolderItem *next_item = (FolderItem*) node->data;
1303 if (folderview_have_new_children_sub(folderview,
1312 static gboolean folderview_have_new_children(FolderView *folderview,
1315 return folderview_have_new_children_sub(folderview, item, FALSE);
1318 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1324 if (!item || !item->folder || !item->folder->node)
1327 node = item->folder->node;
1329 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1330 node = node->children;
1333 (item->unread_msgs > 0 ||
1334 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1338 while (node != NULL) {
1339 if (node && node->data) {
1340 FolderItem *next_item = (FolderItem*) node->data;
1342 if (folderview_have_unread_children_sub(folderview,
1352 static gboolean folderview_have_unread_children(FolderView *folderview,
1355 return folderview_have_unread_children_sub(folderview, item, FALSE);
1358 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1364 if (!item || !item->folder || !item->folder->node)
1367 node = item->folder->node;
1369 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1370 node = node->children;
1372 if (in_sub && item->search_match){
1376 while (node != NULL) {
1377 if (node && node->data) {
1378 FolderItem *next_item = (FolderItem*) node->data;
1380 if (folderview_have_matching_children_sub(folderview,
1390 static gboolean folderview_have_matching_children(FolderView *folderview,
1393 return folderview_have_matching_children_sub(folderview, item, FALSE);
1396 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1402 if (!item || !item->folder || !item->folder->node)
1405 node = item->folder->node;
1407 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1408 node = node->children;
1410 if (item->marked_msgs != 0) {
1414 while (node != NULL) {
1415 if (node && node->data) {
1416 FolderItem *next_item = (FolderItem*) node->data;
1418 if (folderview_have_marked_children_sub(folderview,
1427 static gboolean folderview_have_marked_children(FolderView *folderview,
1430 return folderview_have_marked_children_sub(folderview, item, FALSE);
1433 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1435 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1436 GtkStyle *style = NULL;
1437 GtkStyle *color_style = NULL;
1439 GdkPixbuf *xpm, *openxpm;
1440 static GdkPixbuf *searchicon;
1441 gboolean mark = FALSE;
1444 gboolean add_unread_mark;
1445 gboolean add_sub_match_mark;
1446 gboolean use_bold, use_color;
1447 gint *col_pos = folderview->col_pos;
1448 SpecialFolderItemType stype;
1450 item = gtk_cmctree_node_get_row_data(ctree, node);
1451 cm_return_if_fail(item != NULL);
1453 if (!GTK_CMCTREE_ROW(node)->expanded)
1454 mark = folderview_have_marked_children(folderview, item);
1456 mark = (item->marked_msgs != 0);
1458 stype = item->stype;
1459 if (stype == F_NORMAL) {
1460 if (folder_has_parent_of_type(item, F_TRASH))
1462 else if (folder_has_parent_of_type(item, F_DRAFT))
1464 else if (folder_has_parent_of_type(item, F_OUTBOX))
1466 else if (folder_has_parent_of_type(item, F_QUEUE))
1471 if (item->hide_read_msgs) {
1472 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1473 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1475 xpm = mark?m_inboxxpm:inboxxpm;
1476 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1480 if (item->hide_read_msgs) {
1481 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1482 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1484 xpm = mark?m_outboxxpm:outboxxpm;
1485 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1489 if (item->hide_read_msgs) {
1490 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1491 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1493 xpm = mark?m_queuexpm:queuexpm;
1494 openxpm = mark?m_queueopenxpm:queueopenxpm;
1498 if (item->hide_read_msgs) {
1499 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1500 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1502 xpm = mark?m_trashxpm:trashxpm;
1503 openxpm = mark?m_trashopenxpm:trashopenxpm;
1507 xpm = mark?m_draftsxpm:draftsxpm;
1508 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1511 if (item->hide_read_msgs) {
1512 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1513 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1515 xpm = mark?m_folderxpm:folderxpm;
1516 openxpm = mark?m_folderopenxpm:folderopenxpm;
1520 if (item->no_select) {
1521 xpm = openxpm = noselectxpm;
1524 name = folder_item_get_name(item);
1526 if (!GTK_CMCTREE_ROW(node)->expanded) {
1527 add_unread_mark = folderview_have_unread_children(
1529 add_sub_match_mark = folderview_have_matching_children(
1532 add_unread_mark = FALSE;
1533 add_sub_match_mark = FALSE;
1536 if (item->search_match) {
1538 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1541 xpm = openxpm = searchicon;
1545 if (prefs_common.display_folder_unread) {
1546 if (folder_has_parent_of_type(item, F_QUEUE)) {
1547 /* only total_msgs matters here */
1548 if (item->total_msgs > 0) {
1549 /* show total number (should be equal to the unread number)
1551 str = g_strdup_printf("%s (%d%s%s)",
1552 name, item->total_msgs,
1553 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1554 (item->unreadmarked_msgs > 0) ? "!" : "");
1557 if (prefs_common.display_folder_unread == 1) {
1558 if (item->unread_msgs > 0) {
1559 /* show unread number and signs */
1560 str = g_strdup_printf("%s (%d%s%s)",
1561 name, item->unread_msgs,
1562 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1563 (item->unreadmarked_msgs > 0) ? "!" : "");
1566 if (item->total_msgs > 0) {
1567 /* show unread number, total number and signs if any */
1568 str = g_strdup_printf("%s (%d/%d%s%s)",
1569 name, item->unread_msgs, item->total_msgs,
1570 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1571 (item->unreadmarked_msgs > 0) ? "!" : "");
1575 if ((str == NULL) &&
1576 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1577 /* no unread/total numbers, but at least one sign */
1578 str = g_strdup_printf("%s (%s%s)",
1580 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1581 (item->unreadmarked_msgs > 0) ? "!" : "");
1585 /* last fallback, folder name only or with +! sign */
1586 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1587 str = g_strdup_printf("%s%s",
1589 } else if (item->unreadmarked_msgs > 0) {
1590 str = g_strdup_printf("%s%s",
1592 } else if (add_sub_match_mark) {
1593 str = g_strdup_printf("%s%s",
1596 str = g_strdup_printf("%s", name);
1599 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1601 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1605 if (!folder_item_parent(item)) {
1606 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1607 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1608 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1610 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1611 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1612 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1615 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1616 folder_has_parent_of_type(item, F_DRAFT) ||
1617 folder_has_parent_of_type(item, F_TRASH)) {
1618 use_bold = use_color = FALSE;
1619 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1620 GSList *list = folder_item_get_msg_list(item);
1622 use_bold = use_color = FALSE;
1623 for (cur = list; cur; cur = cur->next) {
1624 MsgInfo *msginfo = (MsgInfo *)cur->data;
1625 if (!MSG_IS_DELETED(msginfo->flags)) {
1626 /* highlight queue folder if there are any messages */
1627 use_bold = use_color = TRUE;
1632 /* if unread messages exist, print with bold font */
1633 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1635 /* if new messages exist, print with colored letter */
1637 (item->new_msgs > 0) ||
1639 folderview_have_new_children(folderview, item));
1642 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1647 if (item->prefs->color > 0 && !use_color) {
1648 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1649 color_style = gtk_style_copy(bold_style);
1650 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1651 style = color_style;
1652 } else if (use_color) {
1653 style = bold_color_style;
1656 if (item->op_count > 0) {
1657 style = bold_tgtfold_style;
1659 } else if (use_color) {
1660 style = normal_color_style;
1661 gtk_cmctree_node_set_foreground(ctree, node,
1662 &folderview->color_new);
1663 } else if (item->op_count > 0) {
1664 style = bold_tgtfold_style;
1665 } else if (item->prefs->color > 0) {
1667 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1668 color_style = gtk_style_copy(normal_style);
1669 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1670 style = color_style;
1672 style = normal_style;
1675 gtk_cmctree_node_set_row_style(ctree, node, style);
1677 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1678 folderview_update_node(folderview, node);
1681 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1684 FolderView *folderview;
1686 GtkCMCTreeNode *node;
1688 cm_return_if_fail(item != NULL);
1690 for (list = folderview_list; list != NULL; list = list->next) {
1691 folderview = (FolderView *)list->data;
1692 ctree = GTK_CMCTREE(folderview->ctree);
1694 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1696 item->search_match = matches;
1697 folderview_update_node(folderview, node);
1702 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1704 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1705 FolderView *folderview = (FolderView *)data;
1707 GtkCMCTreeNode *node;
1708 cm_return_val_if_fail(update_info != NULL, TRUE);
1709 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1710 cm_return_val_if_fail(folderview != NULL, FALSE);
1712 ctree = GTK_CMCTREE(folderview->ctree);
1714 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1717 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1718 folderview_update_node(folderview, node);
1720 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1721 update_info->item == folderview->summaryview->folder_item &&
1722 update_info->item != NULL)
1723 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1724 summary_show(folderview->summaryview, update_info->item);
1730 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1731 GNode *gnode, GtkCMCTreeNode *cnode,
1734 FolderView *folderview = (FolderView *)data;
1735 FolderItem *item = FOLDER_ITEM(gnode->data);
1737 cm_return_val_if_fail(item != NULL, FALSE);
1739 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1740 folderview_update_node(folderview, cnode);
1745 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1748 FolderView *folderview = (FolderView *)data;
1751 if (GTK_CMCTREE_ROW(node)->children) {
1752 item = gtk_cmctree_node_get_row_data(ctree, node);
1753 cm_return_if_fail(item != NULL);
1755 if (!item->collapsed)
1756 gtk_cmctree_expand(ctree, node);
1758 folderview_update_node(folderview, node);
1762 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1763 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1766 GtkCMCTreeNode *node, *parent, *sibling;
1768 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1770 g_warning("%s not found.\n", item->path);
1772 parent = GTK_CMCTREE_ROW(node)->parent;
1773 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1774 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1776 sibling = GTK_CMCTREE_ROW(parent)->children;
1780 tmp = gtk_cmctree_node_get_row_data
1782 if (tmp->stype != F_NORMAL)
1783 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1787 if (node != sibling)
1788 gtk_cmctree_move(ctree, node, parent, sibling);
1795 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1798 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1799 GtkCMCTreeNode *prev = NULL;
1801 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1802 gtk_sctree_sort_recursive(ctree, root);
1803 if (root && GTK_CMCTREE_ROW(root)->parent) {
1804 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1807 set_special_folder(ctree, folder->inbox, root, &prev);
1808 set_special_folder(ctree, folder->outbox, root, &prev);
1809 set_special_folder(ctree, folder->draft, root, &prev);
1810 set_special_folder(ctree, folder->queue, root, &prev);
1811 set_special_folder(ctree, folder->trash, root, &prev);
1812 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1815 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1817 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1818 GtkCMCTreeNode *root;
1820 cm_return_if_fail(folder != NULL);
1822 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1823 folderview_gnode_func, folderview);
1824 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1826 folderview_sort_folders(folderview, root, folder);
1829 /* callback functions */
1830 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1831 GdkEventButton *event)
1835 FolderViewPopup *fpopup;
1836 GtkActionGroup *action_group;
1838 FolderItem *special_trash = NULL, *special_queue = NULL;
1840 GtkUIManager *ui_manager = gtk_ui_manager_new();
1842 if (folderview->ui_manager)
1843 g_object_unref(folderview->ui_manager);
1845 folderview->ui_manager = ui_manager;
1846 item = folderview_get_selected_item(folderview);
1848 cm_return_if_fail(item != NULL);
1849 cm_return_if_fail(item->folder != NULL);
1850 folder = item->folder;
1852 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1855 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1857 fpopup = g_hash_table_lookup(folderview_popups, "common");
1858 action_group = g_hash_table_lookup(folderview->popups, "common");
1861 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1865 if (fpopup->add_menuitems)
1866 fpopup->add_menuitems(ui_manager, item);
1868 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1869 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1870 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1871 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1875 if (fpopup->set_sensitivity != NULL)
1876 fpopup->set_sensitivity(ui_manager, item);
1878 if (NULL != (ac = account_find_from_item(item))) {
1879 special_trash = account_get_special_folder(ac, F_TRASH);
1880 special_queue = account_get_special_folder(ac, F_QUEUE);
1883 if ((item == folder->trash || item == special_trash
1884 || folder_has_parent_of_type(item, F_TRASH))) {
1885 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1886 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1889 if ((item == folder->queue || item == special_queue
1890 || folder_has_parent_of_type(item, F_QUEUE))) {
1891 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1892 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1895 #define SET_SENS(name, sens) \
1896 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1898 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1899 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1900 folderview->selected == folderview->opened);
1901 SET_SENS("FolderViewPopup/Properties", TRUE);
1903 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1904 item->total_msgs >= 1 && !item->processing_pending);
1905 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1906 !item->no_select && !item->processing_pending);
1908 if (item == folder->trash || item == special_trash
1909 || folder_has_parent_of_type(item, F_TRASH)) {
1910 GSList *msglist = folder_item_get_msg_list(item);
1911 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1912 procmsg_msg_list_free(msglist);
1914 if (item == folder->queue || item == special_queue
1915 || folder_has_parent_of_type(item, F_QUEUE)) {
1916 GSList *msglist = folder_item_get_msg_list(item);
1917 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1918 procmsg_msg_list_free(msglist);
1922 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1923 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1924 g_signal_connect(G_OBJECT(popup), "selection_done",
1925 G_CALLBACK(folderview_popup_close),
1927 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1928 event->button, event->time);
1931 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1932 FolderView *folderview)
1934 GtkCMCList *clist = GTK_CMCLIST(ctree);
1935 gint prev_row = -1, row = -1, column = -1;
1937 if (!event) return FALSE;
1939 if (event->button == 1 || event->button == 2) {
1940 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1941 folderview->open_folder = TRUE;
1943 if (event->type == GDK_2BUTTON_PRESS) {
1944 if (clist->selection) {
1945 GtkCMCTreeNode *node;
1947 node = GTK_CMCTREE_NODE(clist->selection->data);
1949 gtk_cmctree_toggle_expansion(
1952 folderview->open_folder = FALSE;
1959 if (event->button == 2 || event->button == 3) {
1961 if (clist->selection) {
1962 GtkCMCTreeNode *node;
1964 node = GTK_CMCTREE_NODE(clist->selection->data);
1966 prev_row = gtkut_ctree_get_nth_from_node
1967 (GTK_CMCTREE(ctree), node);
1970 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1973 if (prev_row != row) {
1974 gtk_cmclist_unselect_all(clist);
1975 if (event->button == 2)
1976 folderview_select_node
1978 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1981 gtk_cmclist_select_row(clist, row, column);
1985 if (event->button != 3) return FALSE;
1987 folderview_set_sens_and_popup_menu(folderview, row, event);
1991 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1992 FolderView *folderview)
1994 int row = -1, column = -1;
1996 if (!event) return FALSE;
1998 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2001 if (event->button == 1 && folderview->open_folder == FALSE &&
2002 folderview->opened != NULL) {
2003 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2004 folderview->opened);
2005 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2011 #define BREAK_ON_MODIFIER_KEY() \
2012 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2014 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2015 FolderView *folderview)
2017 if (!event) return FALSE;
2019 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2022 switch (event->keyval) {
2024 #ifndef GENERIC_UMPC
2028 if (folderview->selected) {
2029 folderview_select_node(folderview,
2030 folderview->selected);
2035 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2036 gtk_cmctree_toggle_expansion(
2037 GTK_CMCTREE(folderview->ctree),
2038 folderview->selected);
2043 BREAK_ON_MODIFIER_KEY();
2044 if (folderview->selected) {
2045 if (folderview->opened == folderview->selected &&
2046 (!folderview->summaryview->folder_item ||
2047 folderview->summaryview->folder_item->total_msgs == 0))
2048 folderview_select_next_unread(folderview, TRUE);
2050 folderview_select_node(folderview,
2051 folderview->selected);
2061 typedef struct _PostponedSelectData
2064 GtkCMCTreeNode *row;
2066 FolderView *folderview;
2067 } PostponedSelectData;
2069 static gboolean postpone_select(void *data)
2071 PostponedSelectData *psdata = (PostponedSelectData *)data;
2072 debug_print("trying again\n");
2073 psdata->folderview->open_folder = TRUE;
2074 main_window_cursor_normal(psdata->folderview->mainwin);
2075 STATUSBAR_POP(psdata->folderview->mainwin);
2076 folderview_selected(psdata->ctree, psdata->row,
2077 psdata->column, psdata->folderview);
2082 void folderview_close_opened(FolderView *folderview)
2084 if (folderview->opened) {
2085 FolderItem *olditem;
2087 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2088 folderview->opened);
2090 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2091 olditem->path ? olditem->path:olditem->name);
2092 /* will be null if we just moved the previously opened folder */
2093 STATUSBAR_PUSH(folderview->mainwin, buf);
2094 main_window_cursor_wait(folderview->mainwin);
2096 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2097 summary_show(folderview->summaryview, NULL);
2098 folder_item_close(olditem);
2099 main_window_cursor_normal(folderview->mainwin);
2100 STATUSBAR_POP(folderview->mainwin);
2101 if (olditem->folder->klass->item_closed)
2102 olditem->folder->klass->item_closed(olditem);
2107 if (folderview->opened &&
2108 !GTK_CMCTREE_ROW(folderview->opened)->children)
2109 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2111 folderview->opened = NULL;
2113 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2114 gint column, FolderView *folderview)
2116 static gboolean can_select = TRUE; /* exclusive lock */
2121 GtkCMCTreeNode *old_opened = folderview->opened;
2123 folderview->selected = row;
2125 debug_print("newly selected %p, opened %p\n", folderview->selected,
2126 folderview->opened);
2127 if (folderview->opened == row) {
2128 folderview->open_folder = FALSE;
2133 item = gtk_cmctree_node_get_row_data(ctree, row);
2136 folderview->open_folder = FALSE;
2140 if (!can_select || summary_is_locked(folderview->summaryview)) {
2141 if (folderview->opened) {
2142 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2143 gtk_cmctree_select(ctree, folderview->opened);
2145 folderview->open_folder = FALSE;
2150 if (!folderview->open_folder) {
2157 /* Save cache for old folder */
2158 /* We don't want to lose all caches if sylpheed crashed */
2159 /* resets folderview->opened to NULL */
2160 folderview_close_opened(folderview);
2162 /* CLAWS: set compose button type: news folder items
2163 * always have a news folder as parent */
2165 toolbar_set_compose_button
2166 (folderview->mainwin->toolbar,
2167 FOLDER_TYPE(item->folder) == F_NEWS ?
2168 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2171 debug_print("Folder %s is selected\n", item->path);
2173 if (!GTK_CMCTREE_ROW(row)->children)
2174 gtk_cmctree_expand(ctree, row);
2176 /* ungrab the mouse event */
2177 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2178 gtk_grab_remove(GTK_WIDGET(ctree));
2179 if (gdk_pointer_is_grabbed())
2180 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2184 /* TODO: wwp: avoid displaying (null) in the status bar */
2185 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2186 item->path : "(null)");
2187 debug_print("%s\n", buf);
2188 STATUSBAR_PUSH(folderview->mainwin, buf);
2191 main_window_cursor_wait(folderview->mainwin);
2193 if (folderview->scanning_folder == item->folder) {
2196 res = folder_item_open(item);
2199 if (res == -1 && item->no_select == FALSE) {
2200 main_window_cursor_normal(folderview->mainwin);
2201 STATUSBAR_POP(folderview->mainwin);
2203 alertpanel_error(_("Folder could not be opened."));
2205 folderview->open_folder = FALSE;
2209 } else if (res == -2 && item->no_select == FALSE) {
2210 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2211 data->ctree = ctree;
2213 data->column = column;
2214 data->folderview = folderview;
2215 debug_print("postponing open of %s till end of scan\n",
2216 item->path ? item->path:item->name);
2217 folderview->open_folder = FALSE;
2219 g_timeout_add(500, postpone_select, data);
2224 main_window_cursor_normal(folderview->mainwin);
2227 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2228 opened = summary_show(folderview->summaryview, item);
2230 folder_clean_cache_memory(item);
2233 gtkut_ctree_set_focus_row(ctree, old_opened);
2234 gtk_cmctree_select(ctree, old_opened);
2235 folderview->opened = old_opened;
2237 folderview->opened = row;
2238 if (gtk_cmctree_node_is_visible(ctree, row)
2239 != GTK_VISIBILITY_FULL)
2240 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2243 STATUSBAR_POP(folderview->mainwin);
2245 folderview->open_folder = FALSE;
2250 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2251 FolderView *folderview)
2255 item = gtk_cmctree_node_get_row_data(ctree, node);
2256 cm_return_if_fail(item != NULL);
2257 item->collapsed = FALSE;
2258 folderview_update_node(folderview, node);
2261 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2262 FolderView *folderview)
2266 item = gtk_cmctree_node_get_row_data(ctree, node);
2267 cm_return_if_fail(item != NULL);
2268 item->collapsed = TRUE;
2269 folderview_update_node(folderview, node);
2272 static void folderview_popup_close(GtkMenuShell *menu_shell,
2273 FolderView *folderview)
2275 if (!folderview->opened) return;
2277 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2280 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2281 FolderView *folderview)
2283 FolderColumnType type = folderview->col_state[column].type;
2285 prefs_common.folder_col_size[type] = width;
2288 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2290 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2291 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2292 GtkCMCTreeNode *node, *parent_node;
2293 gint *col_pos = folderview->col_pos;
2294 FolderItemUpdateData hookdata;
2296 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2297 if (parent_node == NULL)
2300 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2302 text[col_pos[F_COL_FOLDER]] = item->name;
2303 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2308 gtk_cmctree_expand(ctree, parent_node);
2309 gtk_cmctree_node_set_row_data(ctree, node, item);
2311 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2312 folderview_sort_folders(folderview, parent_node, item->folder);
2314 hookdata.item = item;
2315 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2316 hookdata.msg = NULL;
2317 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2319 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2322 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2324 FolderView *folderview = (FolderView *)data;
2325 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2327 GSList *mlist = NULL;
2329 FolderItem *special_trash = NULL;
2332 if (!folderview->selected) return;
2333 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2334 cm_return_if_fail(item != NULL);
2335 cm_return_if_fail(item->folder != NULL);
2337 if (NULL != (ac = account_find_from_item(item)))
2338 special_trash = account_get_special_folder(ac, F_TRASH);
2340 if (item != item->folder->trash && item != special_trash
2341 && !folder_has_parent_of_type(item, F_TRASH)) return;
2343 if (prefs_common.ask_on_clean) {
2344 if (alertpanel(_("Empty trash"),
2345 _("Delete all messages in trash?"),
2346 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2350 mlist = folder_item_get_msg_list(item);
2352 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2353 MsgInfo * msginfo = (MsgInfo *) cur->data;
2354 if (MSG_IS_LOCKED(msginfo->flags))
2356 /* is it partially received? (partial_recv isn't cached) */
2357 if (msginfo->total_size != 0 &&
2358 msginfo->size != (off_t)msginfo->total_size)
2359 partial_mark_for_delete(msginfo);
2361 procmsg_msg_list_free(mlist);
2363 folder_item_remove_all_msg(item);
2366 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2368 FolderView *folderview = (FolderView *)data;
2369 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2371 FolderItem *special_queue = NULL;
2373 gchar *errstr = NULL;
2375 if (!folderview->selected) return;
2376 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2377 cm_return_if_fail(item != NULL);
2378 cm_return_if_fail(item->folder != NULL);
2380 if (NULL != (ac = account_find_from_item(item)))
2381 special_queue = account_get_special_folder(ac, F_QUEUE);
2383 if (item != item->folder->queue && item != special_queue
2384 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2386 if (procmsg_queue_is_empty(item))
2389 if (prefs_common.work_offline)
2390 if (alertpanel(_("Offline warning"),
2391 _("You're working offline. Override?"),
2392 GTK_STOCK_NO, GTK_STOCK_YES,
2393 NULL) != G_ALERTALTERNATE)
2396 /* ask for confirmation before sending queued messages only
2397 in online mode and if there is at least one message queued
2398 in any of the folder queue
2400 if (prefs_common.confirm_send_queued_messages) {
2401 if (!prefs_common.work_offline) {
2402 if (alertpanel(_("Send queued messages"),
2403 _("Send all queued messages?"),
2404 GTK_STOCK_CANCEL, _("_Send"),
2405 NULL) != G_ALERTALTERNATE)
2410 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2412 alertpanel_error_log(_("Some errors occurred while "
2413 "sending queued messages."));
2415 alertpanel_error_log(_("Some errors occurred "
2416 "while sending queued messages:\n%s"), errstr);
2422 static void folderview_search_cb(GtkAction *action, gpointer data)
2424 FolderView *folderview = (FolderView *)data;
2425 summary_search(folderview->summaryview);
2428 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2430 FolderView *folderview = (FolderView *)data;
2431 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2434 if (!folderview->selected) return;
2436 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2437 cm_return_if_fail(item != NULL);
2438 cm_return_if_fail(item->folder != NULL);
2440 item->processing_pending = TRUE;
2441 folder_item_apply_processing(item);
2442 item->processing_pending = FALSE;
2445 static void folderview_property_cb(GtkAction *action, gpointer data)
2447 FolderView *folderview = (FolderView *)data;
2448 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2451 if (!folderview->selected) return;
2453 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2454 cm_return_if_fail(item != NULL);
2455 cm_return_if_fail(item->folder != NULL);
2457 prefs_folder_item_open(item);
2460 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2462 GSList *list = NULL;
2463 GSList *done = NULL;
2464 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2466 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2467 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2468 && list->data != node) {
2469 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2470 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2473 for (list = done; list != NULL; list = g_slist_next(list)) {
2474 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2480 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2481 FolderItem *to_folder, gboolean copy)
2483 FolderItem *from_parent = NULL;
2484 FolderItem *new_folder = NULL;
2485 GtkCMCTreeNode *src_node = NULL;
2489 cm_return_if_fail(folderview != NULL);
2490 cm_return_if_fail(from_folder != NULL);
2491 cm_return_if_fail(to_folder != NULL);
2493 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2494 from_parent = folder_item_parent(from_folder);
2496 if (prefs_common.warn_dnd) {
2497 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2498 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2499 from_folder->name, to_folder->name);
2500 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2501 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2502 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2505 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2507 else if (status & G_ALERTDISABLE)
2508 prefs_common.warn_dnd = FALSE;
2511 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2512 from_folder->name, to_folder->name);
2513 STATUSBAR_PUSH(folderview->mainwin, buf);
2515 summary_clear_all(folderview->summaryview);
2516 folderview->opened = NULL;
2517 folderview->selected = NULL;
2518 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2520 main_window_cursor_wait(folderview->mainwin);
2522 statusbar_verbosity_set(FALSE);
2523 folder_item_update_freeze();
2524 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2525 statusbar_verbosity_set(FALSE);
2526 main_window_cursor_normal(folderview->mainwin);
2527 STATUSBAR_POP(folderview->mainwin);
2528 folder_item_update_thaw();
2529 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2531 folderview_sort_folders(folderview,
2532 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2533 NULL, to_folder), new_folder->folder);
2534 folderview_select(folderview, new_folder);
2536 statusbar_verbosity_set(FALSE);
2537 main_window_cursor_normal(folderview->mainwin);
2538 STATUSBAR_POP(folderview->mainwin);
2539 folder_item_update_thaw();
2541 case F_MOVE_FAILED_DEST_IS_PARENT:
2542 alertpanel_error(_("Source and destination are the same."));
2544 case F_MOVE_FAILED_DEST_IS_CHILD:
2545 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2546 _("Can't move a folder to one of its children."));
2548 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2549 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2552 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2557 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2560 static gint folderview_clist_compare(GtkCMCList *clist,
2561 gconstpointer ptr1, gconstpointer ptr2)
2563 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2564 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2566 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2568 return item1->order - item2->order;
2571 // if only one folder has an order it comes first
2572 if (item1->order > 0)
2576 if (item2->order > 0)
2582 return (item2->name != NULL);
2586 return g_utf8_collate(item1->name, item2->name);
2589 static void folderview_processing_cb(GtkAction *action, gpointer data)
2591 FolderView *folderview = (FolderView *)data;
2592 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2596 if (!folderview->selected) return;
2598 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2599 cm_return_if_fail(item != NULL);
2600 cm_return_if_fail(item->folder != NULL);
2602 id = folder_item_get_identifier(item);
2603 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2606 prefs_filtering_open(&item->prefs->processing, title,
2607 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2611 void folderview_set_target_folder_color(gint color_op)
2615 FolderView *folderview;
2617 for (list = folderview_list; list != NULL; list = list->next) {
2618 folderview = (FolderView *)list->data;
2619 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2621 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2622 folderview->color_op;
2628 static gchar *last_smallfont = NULL;
2629 static gchar *last_normalfont = NULL;
2630 static gchar *last_boldfont = NULL;
2631 static gboolean last_derive = 0;
2633 void folderview_reinit_fonts(FolderView *folderview)
2636 g_free(last_smallfont);
2637 last_smallfont = NULL;
2638 g_free(last_normalfont);
2639 last_normalfont = NULL;
2640 g_free(last_boldfont);
2641 last_boldfont = NULL;
2644 void folderview_reflect_prefs(void)
2646 gboolean update_font = FALSE;
2647 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2648 FolderItem *item = folderview_get_selected_item(folderview);
2649 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2650 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2651 gint height = pos->value;
2653 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2654 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2655 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2656 last_derive != prefs_common.derive_from_normal_font)
2659 g_free(last_smallfont);
2660 last_smallfont = g_strdup(SMALL_FONT);
2661 g_free(last_normalfont);
2662 last_normalfont = g_strdup(NORMAL_FONT);
2663 g_free(last_boldfont);
2664 last_boldfont = g_strdup(BOLD_FONT);
2665 last_derive = prefs_common.derive_from_normal_font;
2668 normal_style = normal_color_style = bold_style =
2669 bold_color_style = bold_tgtfold_style = NULL;
2671 folderview_init(folderview);
2673 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2674 folderview_column_set_titles(folderview);
2675 folderview_set_all();
2677 g_signal_handlers_block_by_func
2678 (G_OBJECT(folderview->ctree),
2679 G_CALLBACK(folderview_selected), folderview);
2682 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2683 GTK_CMCTREE(folderview->ctree), NULL, item);
2685 folderview_select(folderview, item);
2686 folderview->open_folder = FALSE;
2687 folderview->selected = node;
2690 g_signal_handlers_unblock_by_func
2691 (G_OBJECT(folderview->ctree),
2692 G_CALLBACK(folderview_selected), folderview);
2694 pos = gtk_scrolled_window_get_vadjustment(
2695 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2696 gtk_adjustment_set_value(pos, height);
2697 gtk_adjustment_changed(pos);
2698 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2701 static void drag_state_stop(FolderView *folderview)
2703 if (folderview->drag_timer)
2704 g_source_remove(folderview->drag_timer);
2705 folderview->drag_timer = 0;
2706 folderview->drag_node = NULL;
2709 static gint folderview_defer_expand(FolderView *folderview)
2711 if (folderview->drag_node) {
2712 folderview_recollapse_nodes(folderview, folderview->drag_node);
2713 if (folderview->drag_item->collapsed) {
2714 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2715 folderview->nodes_to_recollapse = g_slist_append
2716 (folderview->nodes_to_recollapse, folderview->drag_node);
2719 folderview->drag_item = NULL;
2720 folderview->drag_timer = 0;
2724 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2726 /* the idea is that we call drag_state_start() whenever we want expansion to
2727 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2728 * we need to call drag_state_stop() */
2729 drag_state_stop(folderview);
2730 /* request expansion */
2731 if (0 != (folderview->drag_timer = g_timeout_add
2732 (prefs_common.hover_timeout,
2733 (GtkFunction)folderview_defer_expand,
2735 folderview->drag_node = node;
2736 folderview->drag_item = item;
2739 #ifndef GENERIC_UMPC
2740 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2741 FolderView *folderview)
2743 GdkDragContext *context;
2745 cm_return_if_fail(folderview != NULL);
2746 if (folderview->selected == NULL) return;
2747 if (folderview->nodes_to_recollapse)
2748 g_slist_free(folderview->nodes_to_recollapse);
2749 folderview->nodes_to_recollapse = NULL;
2750 context = gtk_drag_begin(widget, folderview->target_list,
2751 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2752 gtk_drag_set_icon_default(context);
2755 static void folderview_drag_data_get(GtkWidget *widget,
2756 GdkDragContext *drag_context,
2757 GtkSelectionData *selection_data,
2760 FolderView *folderview)
2764 gchar *source = NULL;
2765 if (info == TARGET_DUMMY) {
2766 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2767 cur != NULL; cur = cur->next) {
2768 item = gtk_cmctree_node_get_row_data
2769 (GTK_CMCTREE(folderview->ctree),
2770 GTK_CMCTREE_NODE(cur->data));
2772 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2773 gtk_selection_data_set(selection_data,
2774 selection_data->target, 8,
2775 source, strlen(source));
2781 g_warning("unknown info %d\n", info);
2785 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2787 FolderUpdateData *hookdata;
2788 FolderView *folderview;
2792 folderview = (FolderView *) userdata;
2793 cm_return_val_if_fail(hookdata != NULL, FALSE);
2794 cm_return_val_if_fail(folderview != NULL, FALSE);
2796 ctree = folderview->ctree;
2797 cm_return_val_if_fail(ctree != NULL, FALSE);
2799 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2800 folderview_create_folder_node(folderview, hookdata->item);
2801 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2802 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2803 NULL, folder_item_parent(hookdata->item));
2804 folderview_sort_folders(folderview, node, hookdata->folder);
2805 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2806 GtkCMCTreeNode *node;
2808 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2810 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2811 if (folderview->selected == node)
2812 folderview->selected = NULL;
2813 if (folderview->opened == node)
2814 folderview->opened = NULL;
2816 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2817 folderview_set(folderview);
2822 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2823 GdkDragContext *context,
2827 FolderView *folderview)
2830 FolderItem *item = NULL, *src_item = NULL;
2831 GtkCMCTreeNode *node = NULL;
2832 gboolean acceptable = FALSE;
2833 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2834 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2835 int height = (int)pos->page_size;
2836 int total_height = (int)pos->upper;
2837 int vpos = (int) pos->value;
2838 int offset = prefs_common.show_col_headers ? 24:0;
2840 if (gtk_cmclist_get_selection_info
2841 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2842 GtkWidget *srcwidget;
2844 if (y > height - 24 && height + vpos < total_height) {
2845 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2846 gtk_adjustment_changed(pos);
2848 if (y < 48 && y > 0) {
2849 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2850 gtk_adjustment_changed(pos);
2853 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2854 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2855 src_item = folderview->summaryview->folder_item;
2857 srcwidget = gtk_drag_get_source_widget(context);
2858 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2859 /* comes from summaryview */
2860 /* we are copying messages, so only accept folder items that are not
2861 the source item, are no root items and can copy messages */
2862 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2863 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2864 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2866 } else if (srcwidget == folderview->ctree) {
2867 /* comes from folderview */
2868 /* we are moving folder items, only accept folders that are not
2869 the source items and can copy messages and create folder items */
2870 if (item && item->folder && src_item && src_item != item &&
2871 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2872 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2873 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2874 || item->folder == src_item->folder))
2877 /* comes from another app */
2878 /* we are adding messages, so only accept folder items that are
2879 no root items and can copy messages */
2880 if (item && item->folder && folder_item_parent(item) != NULL
2881 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2882 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2887 if (acceptable || (src_item && src_item == item))
2888 drag_state_start(folderview, node, item);
2891 g_signal_handlers_block_by_func
2893 G_CALLBACK(folderview_selected), folderview);
2894 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2895 g_signal_handlers_unblock_by_func
2897 G_CALLBACK(folderview_selected), folderview);
2898 gdk_drag_status(context,
2899 (context->actions == GDK_ACTION_COPY ?
2900 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2902 if (folderview->opened)
2903 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2904 gdk_drag_status(context, 0, time);
2910 static void folderview_drag_leave_cb(GtkWidget *widget,
2911 GdkDragContext *context,
2913 FolderView *folderview)
2915 drag_state_stop(folderview);
2916 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2919 static void free_info (gpointer stuff, gpointer data)
2924 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2925 guint time, FolderItem *item)
2928 GSList *msglist = NULL;
2929 list = uri_list_extract_filenames(data);
2930 if (!(item && item->folder && folder_item_parent(item) != NULL
2931 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2933 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2934 debug_print("item doesn't fit\n");
2938 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2939 debug_print("list is empty\n");
2942 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2943 MsgFileInfo *info = NULL;
2945 if (file_is_email((gchar *)tmp->data)) {
2946 info = g_new0(MsgFileInfo, 1);
2947 info->msginfo = NULL;
2948 info->file = (gchar *)tmp->data;
2949 msglist = g_slist_prepend(msglist, info);
2950 debug_print("file is a mail\n");
2952 debug_print("file isn't a mail\n");
2956 msglist = g_slist_reverse(msglist);
2957 folder_item_add_msgs(item, msglist, FALSE);
2958 g_slist_foreach(msglist, free_info, NULL);
2959 g_slist_free(msglist);
2960 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2962 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2964 list_free_strings(list);
2968 static void folderview_drag_received_cb(GtkWidget *widget,
2969 GdkDragContext *drag_context,
2972 GtkSelectionData *data,
2975 FolderView *folderview)
2978 FolderItem *item = NULL, *src_item;
2979 GtkCMCTreeNode *node;
2980 int offset = prefs_common.show_col_headers ? 24:0;
2982 if (info == TARGET_DUMMY) {
2983 drag_state_stop(folderview);
2984 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
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 (drag_context->action) {
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 = (drag_context->action == GDK_ACTION_COPY);
3027 source = data->data + 17;
3028 if (gtk_cmclist_get_selection_info
3029 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3031 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3034 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3035 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3036 src_item = folder_find_item_from_identifier(source);
3040 src_item->stype == F_NORMAL &&
3041 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3042 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3043 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3044 !folder_has_parent_of_type(src_item, F_TRASH);
3045 if (!item || !src_item || !folder_is_normal) {
3046 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3050 folderview_move_folder(folderview, src_item, item, copy);
3051 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3053 folderview->nodes_to_recollapse = NULL;
3054 } else if (info == TARGET_MAIL_URI_LIST) {
3055 if (gtk_cmclist_get_selection_info
3056 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3059 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3061 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3062 debug_print("no node\n");
3065 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3067 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3068 debug_print("no item\n");
3071 folderview_finish_dnd(data->data, drag_context, time, item);
3075 static void folderview_drag_end_cb(GtkWidget *widget,
3076 GdkDragContext *drag_context,
3077 FolderView *folderview)
3079 drag_state_stop(folderview);
3080 g_slist_free(folderview->nodes_to_recollapse);
3081 folderview->nodes_to_recollapse = NULL;
3084 void folderview_register_popup(FolderViewPopup *fpopup)
3088 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3089 FolderView *folderview = folderviews->data;
3090 GtkActionGroup *factory;
3092 factory = create_action_group(folderview, fpopup);
3093 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3095 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3098 void folderview_unregister_popup(FolderViewPopup *fpopup)
3103 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3104 FolderView *folderview = folderviews->data;
3106 g_hash_table_remove(folderview->popups, fpopup->klass);
3108 g_hash_table_remove(folderview_popups, fpopup->klass);