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 /* highlight queue folder if there are any messages */
1621 use_bold = use_color = (item->total_msgs > 0);
1623 /* if unread messages exist, print with bold font */
1624 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1626 /* if new messages exist, print with colored letter */
1628 (item->new_msgs > 0) ||
1630 folderview_have_new_children(folderview, item));
1633 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1638 if (item->prefs->color > 0 && !use_color) {
1639 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1640 color_style = gtk_style_copy(bold_style);
1641 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1642 style = color_style;
1643 } else if (use_color) {
1644 style = bold_color_style;
1647 if (item->op_count > 0) {
1648 style = bold_tgtfold_style;
1650 } else if (use_color) {
1651 style = normal_color_style;
1652 gtk_cmctree_node_set_foreground(ctree, node,
1653 &folderview->color_new);
1654 } else if (item->op_count > 0) {
1655 style = bold_tgtfold_style;
1656 } else if (item->prefs->color > 0) {
1658 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1659 color_style = gtk_style_copy(normal_style);
1660 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1661 style = color_style;
1663 style = normal_style;
1666 gtk_cmctree_node_set_row_style(ctree, node, style);
1668 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1669 folderview_update_node(folderview, node);
1672 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1675 FolderView *folderview;
1677 GtkCMCTreeNode *node;
1679 cm_return_if_fail(item != NULL);
1681 for (list = folderview_list; list != NULL; list = list->next) {
1682 folderview = (FolderView *)list->data;
1683 ctree = GTK_CMCTREE(folderview->ctree);
1685 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1687 item->search_match = matches;
1688 folderview_update_node(folderview, node);
1693 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1695 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1696 FolderView *folderview = (FolderView *)data;
1698 GtkCMCTreeNode *node;
1699 cm_return_val_if_fail(update_info != NULL, TRUE);
1700 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1701 cm_return_val_if_fail(folderview != NULL, FALSE);
1703 ctree = GTK_CMCTREE(folderview->ctree);
1705 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1708 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1709 folderview_update_node(folderview, node);
1711 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1712 update_info->item == folderview->summaryview->folder_item &&
1713 update_info->item != NULL)
1714 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1715 summary_show(folderview->summaryview, update_info->item);
1721 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1722 GNode *gnode, GtkCMCTreeNode *cnode,
1725 FolderView *folderview = (FolderView *)data;
1726 FolderItem *item = FOLDER_ITEM(gnode->data);
1728 cm_return_val_if_fail(item != NULL, FALSE);
1730 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1731 folderview_update_node(folderview, cnode);
1736 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1739 FolderView *folderview = (FolderView *)data;
1742 if (GTK_CMCTREE_ROW(node)->children) {
1743 item = gtk_cmctree_node_get_row_data(ctree, node);
1744 cm_return_if_fail(item != NULL);
1746 if (!item->collapsed)
1747 gtk_cmctree_expand(ctree, node);
1749 folderview_update_node(folderview, node);
1753 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1754 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1757 GtkCMCTreeNode *node, *parent, *sibling;
1759 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1761 g_warning("%s not found.\n", item->path);
1763 parent = GTK_CMCTREE_ROW(node)->parent;
1764 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1765 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1767 sibling = GTK_CMCTREE_ROW(parent)->children;
1771 tmp = gtk_cmctree_node_get_row_data
1773 if (tmp->stype != F_NORMAL)
1774 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1778 if (node != sibling)
1779 gtk_cmctree_move(ctree, node, parent, sibling);
1786 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1789 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1790 GtkCMCTreeNode *prev = NULL;
1792 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1793 gtk_sctree_sort_recursive(ctree, root);
1794 if (root && GTK_CMCTREE_ROW(root)->parent) {
1795 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1798 set_special_folder(ctree, folder->inbox, root, &prev);
1799 set_special_folder(ctree, folder->outbox, root, &prev);
1800 set_special_folder(ctree, folder->draft, root, &prev);
1801 set_special_folder(ctree, folder->queue, root, &prev);
1802 set_special_folder(ctree, folder->trash, root, &prev);
1803 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1806 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1808 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1809 GtkCMCTreeNode *root;
1811 cm_return_if_fail(folder != NULL);
1813 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1814 folderview_gnode_func, folderview);
1815 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1817 folderview_sort_folders(folderview, root, folder);
1820 /* callback functions */
1821 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1822 GdkEventButton *event)
1826 FolderViewPopup *fpopup;
1827 GtkActionGroup *action_group;
1829 FolderItem *special_trash = NULL, *special_queue = NULL;
1831 GtkUIManager *ui_manager = gtk_ui_manager_new();
1833 if (folderview->ui_manager)
1834 g_object_unref(folderview->ui_manager);
1836 folderview->ui_manager = ui_manager;
1837 item = folderview_get_selected_item(folderview);
1839 cm_return_if_fail(item != NULL);
1840 cm_return_if_fail(item->folder != NULL);
1841 folder = item->folder;
1843 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1846 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1848 fpopup = g_hash_table_lookup(folderview_popups, "common");
1849 action_group = g_hash_table_lookup(folderview->popups, "common");
1852 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1856 if (fpopup->add_menuitems)
1857 fpopup->add_menuitems(ui_manager, item);
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1864 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1866 if (fpopup->set_sensitivity != NULL)
1867 fpopup->set_sensitivity(ui_manager, item);
1869 if (NULL != (ac = account_find_from_item(item))) {
1870 special_trash = account_get_special_folder(ac, F_TRASH);
1871 special_queue = account_get_special_folder(ac, F_QUEUE);
1874 if ((item == folder->trash || item == special_trash
1875 || folder_has_parent_of_type(item, F_TRASH))) {
1876 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1877 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1880 if ((item == folder->queue || item == special_queue
1881 || folder_has_parent_of_type(item, F_QUEUE))) {
1882 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1883 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1886 #define SET_SENS(name, sens) \
1887 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1889 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1890 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1891 folderview->selected == folderview->opened);
1892 SET_SENS("FolderViewPopup/Properties", TRUE);
1894 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1895 item->total_msgs >= 1 && !item->processing_pending);
1896 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1897 !item->no_select && !item->processing_pending);
1899 if (item == folder->trash || item == special_trash
1900 || folder_has_parent_of_type(item, F_TRASH)) {
1901 GSList *msglist = folder_item_get_msg_list(item);
1902 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1903 procmsg_msg_list_free(msglist);
1905 if (item == folder->queue || item == special_queue
1906 || folder_has_parent_of_type(item, F_QUEUE)) {
1907 GSList *msglist = folder_item_get_msg_list(item);
1908 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1909 procmsg_msg_list_free(msglist);
1913 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1914 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1915 g_signal_connect(G_OBJECT(popup), "selection_done",
1916 G_CALLBACK(folderview_popup_close),
1918 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1919 event->button, event->time);
1922 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1923 FolderView *folderview)
1925 GtkCMCList *clist = GTK_CMCLIST(ctree);
1926 gint prev_row = -1, row = -1, column = -1;
1928 if (!event) return FALSE;
1930 if (event->button == 1 || event->button == 2) {
1931 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1932 folderview->open_folder = TRUE;
1934 if (event->type == GDK_2BUTTON_PRESS) {
1935 if (clist->selection) {
1936 GtkCMCTreeNode *node;
1938 node = GTK_CMCTREE_NODE(clist->selection->data);
1940 gtk_cmctree_toggle_expansion(
1943 folderview->open_folder = FALSE;
1950 if (event->button == 2 || event->button == 3) {
1952 if (clist->selection) {
1953 GtkCMCTreeNode *node;
1955 node = GTK_CMCTREE_NODE(clist->selection->data);
1957 prev_row = gtkut_ctree_get_nth_from_node
1958 (GTK_CMCTREE(ctree), node);
1961 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1964 if (prev_row != row) {
1965 gtk_cmclist_unselect_all(clist);
1966 if (event->button == 2)
1967 folderview_select_node
1969 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1972 gtk_cmclist_select_row(clist, row, column);
1976 if (event->button != 3) return FALSE;
1978 folderview_set_sens_and_popup_menu(folderview, row, event);
1982 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1983 FolderView *folderview)
1985 int row = -1, column = -1;
1987 if (!event) return FALSE;
1989 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1992 if (event->button == 1 && folderview->open_folder == FALSE &&
1993 folderview->opened != NULL) {
1994 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1995 folderview->opened);
1996 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2002 #define BREAK_ON_MODIFIER_KEY() \
2003 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2005 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2006 FolderView *folderview)
2008 if (!event) return FALSE;
2010 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2013 switch (event->keyval) {
2015 #ifndef GENERIC_UMPC
2019 if (folderview->selected) {
2020 folderview_select_node(folderview,
2021 folderview->selected);
2026 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2027 gtk_cmctree_toggle_expansion(
2028 GTK_CMCTREE(folderview->ctree),
2029 folderview->selected);
2034 BREAK_ON_MODIFIER_KEY();
2035 if (folderview->selected) {
2036 if (folderview->opened == folderview->selected &&
2037 (!folderview->summaryview->folder_item ||
2038 folderview->summaryview->folder_item->total_msgs == 0))
2039 folderview_select_next_unread(folderview, TRUE);
2041 folderview_select_node(folderview,
2042 folderview->selected);
2052 typedef struct _PostponedSelectData
2055 GtkCMCTreeNode *row;
2057 FolderView *folderview;
2058 } PostponedSelectData;
2060 static gboolean postpone_select(void *data)
2062 PostponedSelectData *psdata = (PostponedSelectData *)data;
2063 debug_print("trying again\n");
2064 psdata->folderview->open_folder = TRUE;
2065 main_window_cursor_normal(psdata->folderview->mainwin);
2066 STATUSBAR_POP(psdata->folderview->mainwin);
2067 folderview_selected(psdata->ctree, psdata->row,
2068 psdata->column, psdata->folderview);
2073 void folderview_close_opened(FolderView *folderview)
2075 if (folderview->opened) {
2076 FolderItem *olditem;
2078 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2079 folderview->opened);
2081 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2082 olditem->path ? olditem->path:olditem->name);
2083 /* will be null if we just moved the previously opened folder */
2084 STATUSBAR_PUSH(folderview->mainwin, buf);
2085 main_window_cursor_wait(folderview->mainwin);
2087 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2088 summary_show(folderview->summaryview, NULL);
2089 folder_item_close(olditem);
2090 main_window_cursor_normal(folderview->mainwin);
2091 STATUSBAR_POP(folderview->mainwin);
2092 if (olditem->folder->klass->item_closed)
2093 olditem->folder->klass->item_closed(olditem);
2098 if (folderview->opened &&
2099 !GTK_CMCTREE_ROW(folderview->opened)->children)
2100 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2102 folderview->opened = NULL;
2104 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2105 gint column, FolderView *folderview)
2107 static gboolean can_select = TRUE; /* exclusive lock */
2112 GtkCMCTreeNode *old_opened = folderview->opened;
2114 folderview->selected = row;
2116 debug_print("newly selected %p, opened %p\n", folderview->selected,
2117 folderview->opened);
2118 if (folderview->opened == row) {
2119 folderview->open_folder = FALSE;
2124 item = gtk_cmctree_node_get_row_data(ctree, row);
2127 folderview->open_folder = FALSE;
2131 if (!can_select || summary_is_locked(folderview->summaryview)) {
2132 if (folderview->opened) {
2133 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2134 gtk_cmctree_select(ctree, folderview->opened);
2136 folderview->open_folder = FALSE;
2141 if (!folderview->open_folder) {
2148 /* Save cache for old folder */
2149 /* We don't want to lose all caches if sylpheed crashed */
2150 /* resets folderview->opened to NULL */
2151 folderview_close_opened(folderview);
2153 /* CLAWS: set compose button type: news folder items
2154 * always have a news folder as parent */
2156 toolbar_set_compose_button
2157 (folderview->mainwin->toolbar,
2158 FOLDER_TYPE(item->folder) == F_NEWS ?
2159 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2162 debug_print("Folder %s is selected\n", item->path);
2164 if (!GTK_CMCTREE_ROW(row)->children)
2165 gtk_cmctree_expand(ctree, row);
2167 /* ungrab the mouse event */
2168 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2169 gtk_grab_remove(GTK_WIDGET(ctree));
2170 if (gdk_pointer_is_grabbed())
2171 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2175 /* TODO: wwp: avoid displaying (null) in the status bar */
2176 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2177 item->path : "(null)");
2178 debug_print("%s\n", buf);
2179 STATUSBAR_PUSH(folderview->mainwin, buf);
2182 main_window_cursor_wait(folderview->mainwin);
2184 if (folderview->scanning_folder == item->folder) {
2187 res = folder_item_open(item);
2190 if (res == -1 && item->no_select == FALSE) {
2191 main_window_cursor_normal(folderview->mainwin);
2192 STATUSBAR_POP(folderview->mainwin);
2194 alertpanel_error(_("Folder could not be opened."));
2196 folderview->open_folder = FALSE;
2200 } else if (res == -2 && item->no_select == FALSE) {
2201 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2202 data->ctree = ctree;
2204 data->column = column;
2205 data->folderview = folderview;
2206 debug_print("postponing open of %s till end of scan\n",
2207 item->path ? item->path:item->name);
2208 folderview->open_folder = FALSE;
2210 g_timeout_add(500, postpone_select, data);
2215 main_window_cursor_normal(folderview->mainwin);
2218 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2219 opened = summary_show(folderview->summaryview, item);
2221 folder_clean_cache_memory(item);
2224 gtkut_ctree_set_focus_row(ctree, old_opened);
2225 gtk_cmctree_select(ctree, old_opened);
2226 folderview->opened = old_opened;
2228 folderview->opened = row;
2229 if (gtk_cmctree_node_is_visible(ctree, row)
2230 != GTK_VISIBILITY_FULL)
2231 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2234 STATUSBAR_POP(folderview->mainwin);
2236 folderview->open_folder = FALSE;
2241 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2242 FolderView *folderview)
2246 item = gtk_cmctree_node_get_row_data(ctree, node);
2247 cm_return_if_fail(item != NULL);
2248 item->collapsed = FALSE;
2249 folderview_update_node(folderview, node);
2252 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2253 FolderView *folderview)
2257 item = gtk_cmctree_node_get_row_data(ctree, node);
2258 cm_return_if_fail(item != NULL);
2259 item->collapsed = TRUE;
2260 folderview_update_node(folderview, node);
2263 static void folderview_popup_close(GtkMenuShell *menu_shell,
2264 FolderView *folderview)
2266 if (!folderview->opened) return;
2268 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2271 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2272 FolderView *folderview)
2274 FolderColumnType type = folderview->col_state[column].type;
2276 prefs_common.folder_col_size[type] = width;
2279 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2281 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2282 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2283 GtkCMCTreeNode *node, *parent_node;
2284 gint *col_pos = folderview->col_pos;
2285 FolderItemUpdateData hookdata;
2287 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2288 if (parent_node == NULL)
2291 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2293 text[col_pos[F_COL_FOLDER]] = item->name;
2294 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2299 gtk_cmctree_expand(ctree, parent_node);
2300 gtk_cmctree_node_set_row_data(ctree, node, item);
2302 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2303 folderview_sort_folders(folderview, parent_node, item->folder);
2305 hookdata.item = item;
2306 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2307 hookdata.msg = NULL;
2308 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2310 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2313 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2315 FolderView *folderview = (FolderView *)data;
2316 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2318 GSList *mlist = NULL;
2320 FolderItem *special_trash = NULL;
2323 if (!folderview->selected) return;
2324 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2325 cm_return_if_fail(item != NULL);
2326 cm_return_if_fail(item->folder != NULL);
2328 if (NULL != (ac = account_find_from_item(item)))
2329 special_trash = account_get_special_folder(ac, F_TRASH);
2331 if (item != item->folder->trash && item != special_trash
2332 && !folder_has_parent_of_type(item, F_TRASH)) return;
2334 if (prefs_common.ask_on_clean) {
2335 if (alertpanel(_("Empty trash"),
2336 _("Delete all messages in trash?"),
2337 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2341 mlist = folder_item_get_msg_list(item);
2343 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2344 MsgInfo * msginfo = (MsgInfo *) cur->data;
2345 if (MSG_IS_LOCKED(msginfo->flags))
2347 /* is it partially received? (partial_recv isn't cached) */
2348 if (msginfo->total_size != 0 &&
2349 msginfo->size != (off_t)msginfo->total_size)
2350 partial_mark_for_delete(msginfo);
2352 procmsg_msg_list_free(mlist);
2354 folder_item_remove_all_msg(item);
2357 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2359 FolderView *folderview = (FolderView *)data;
2360 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2362 FolderItem *special_queue = NULL;
2364 gchar *errstr = NULL;
2366 if (!folderview->selected) return;
2367 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2368 cm_return_if_fail(item != NULL);
2369 cm_return_if_fail(item->folder != NULL);
2371 if (NULL != (ac = account_find_from_item(item)))
2372 special_queue = account_get_special_folder(ac, F_QUEUE);
2374 if (item != item->folder->queue && item != special_queue
2375 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2377 if (procmsg_queue_is_empty(item))
2380 if (prefs_common.work_offline)
2381 if (alertpanel(_("Offline warning"),
2382 _("You're working offline. Override?"),
2383 GTK_STOCK_NO, GTK_STOCK_YES,
2384 NULL) != G_ALERTALTERNATE)
2387 /* ask for confirmation before sending queued messages only
2388 in online mode and if there is at least one message queued
2389 in any of the folder queue
2391 if (prefs_common.confirm_send_queued_messages) {
2392 if (!prefs_common.work_offline) {
2393 if (alertpanel(_("Send queued messages"),
2394 _("Send all queued messages?"),
2395 GTK_STOCK_CANCEL, _("_Send"),
2396 NULL) != G_ALERTALTERNATE)
2401 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2403 alertpanel_error_log(_("Some errors occurred while "
2404 "sending queued messages."));
2406 alertpanel_error_log(_("Some errors occurred "
2407 "while sending queued messages:\n%s"), errstr);
2413 static void folderview_search_cb(GtkAction *action, gpointer data)
2415 FolderView *folderview = (FolderView *)data;
2416 summary_search(folderview->summaryview);
2419 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2421 FolderView *folderview = (FolderView *)data;
2422 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2425 if (!folderview->selected) return;
2427 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2428 cm_return_if_fail(item != NULL);
2429 cm_return_if_fail(item->folder != NULL);
2431 item->processing_pending = TRUE;
2432 folder_item_apply_processing(item);
2433 item->processing_pending = FALSE;
2436 static void folderview_property_cb(GtkAction *action, gpointer data)
2438 FolderView *folderview = (FolderView *)data;
2439 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2442 if (!folderview->selected) return;
2444 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2445 cm_return_if_fail(item != NULL);
2446 cm_return_if_fail(item->folder != NULL);
2448 prefs_folder_item_open(item);
2451 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2453 GSList *list = NULL;
2454 GSList *done = NULL;
2455 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2457 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2458 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2459 && list->data != node) {
2460 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2461 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2464 for (list = done; list != NULL; list = g_slist_next(list)) {
2465 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2471 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2472 FolderItem *to_folder, gboolean copy)
2474 FolderItem *from_parent = NULL;
2475 FolderItem *new_folder = NULL;
2476 GtkCMCTreeNode *src_node = NULL;
2480 cm_return_if_fail(folderview != NULL);
2481 cm_return_if_fail(from_folder != NULL);
2482 cm_return_if_fail(to_folder != NULL);
2484 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2485 from_parent = folder_item_parent(from_folder);
2487 if (prefs_common.warn_dnd) {
2488 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2489 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2490 from_folder->name, to_folder->name);
2491 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2492 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2493 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2496 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2498 else if (status & G_ALERTDISABLE)
2499 prefs_common.warn_dnd = FALSE;
2502 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2503 from_folder->name, to_folder->name);
2504 STATUSBAR_PUSH(folderview->mainwin, buf);
2506 summary_clear_all(folderview->summaryview);
2507 folderview->opened = NULL;
2508 folderview->selected = NULL;
2509 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2511 main_window_cursor_wait(folderview->mainwin);
2513 statusbar_verbosity_set(FALSE);
2514 folder_item_update_freeze();
2515 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2516 statusbar_verbosity_set(FALSE);
2517 main_window_cursor_normal(folderview->mainwin);
2518 STATUSBAR_POP(folderview->mainwin);
2519 folder_item_update_thaw();
2520 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2522 folderview_sort_folders(folderview,
2523 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2524 NULL, to_folder), new_folder->folder);
2525 folderview_select(folderview, new_folder);
2527 statusbar_verbosity_set(FALSE);
2528 main_window_cursor_normal(folderview->mainwin);
2529 STATUSBAR_POP(folderview->mainwin);
2530 folder_item_update_thaw();
2532 case F_MOVE_FAILED_DEST_IS_PARENT:
2533 alertpanel_error(_("Source and destination are the same."));
2535 case F_MOVE_FAILED_DEST_IS_CHILD:
2536 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2537 _("Can't move a folder to one of its children."));
2539 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2540 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2543 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2548 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2551 static gint folderview_clist_compare(GtkCMCList *clist,
2552 gconstpointer ptr1, gconstpointer ptr2)
2554 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2555 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2557 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2559 return item1->order - item2->order;
2562 // if only one folder has an order it comes first
2563 if (item1->order > 0)
2567 if (item2->order > 0)
2573 return (item2->name != NULL);
2577 return g_utf8_collate(item1->name, item2->name);
2580 static void folderview_processing_cb(GtkAction *action, gpointer data)
2582 FolderView *folderview = (FolderView *)data;
2583 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2587 if (!folderview->selected) return;
2589 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2590 cm_return_if_fail(item != NULL);
2591 cm_return_if_fail(item->folder != NULL);
2593 id = folder_item_get_identifier(item);
2594 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2597 prefs_filtering_open(&item->prefs->processing, title,
2598 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2602 void folderview_set_target_folder_color(gint color_op)
2606 FolderView *folderview;
2608 for (list = folderview_list; list != NULL; list = list->next) {
2609 folderview = (FolderView *)list->data;
2610 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2612 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2613 folderview->color_op;
2619 static gchar *last_smallfont = NULL;
2620 static gchar *last_normalfont = NULL;
2621 static gchar *last_boldfont = NULL;
2622 static gboolean last_derive = 0;
2624 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2627 g_free(last_smallfont);
2628 last_smallfont = NULL;
2629 g_free(last_normalfont);
2630 last_normalfont = NULL;
2631 g_free(last_boldfont);
2632 last_boldfont = NULL;
2635 void folderview_reflect_prefs(void)
2637 gboolean update_font = FALSE;
2638 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2639 FolderItem *item = folderview_get_selected_item(folderview);
2640 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2641 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2642 gint height = pos->value;
2644 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2645 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2646 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2647 last_derive != prefs_common.derive_from_normal_font)
2650 g_free(last_smallfont);
2651 last_smallfont = g_strdup(SMALL_FONT);
2652 g_free(last_normalfont);
2653 last_normalfont = g_strdup(NORMAL_FONT);
2654 g_free(last_boldfont);
2655 last_boldfont = g_strdup(BOLD_FONT);
2656 last_derive = prefs_common.derive_from_normal_font;
2659 normal_style = normal_color_style = bold_style =
2660 bold_color_style = bold_tgtfold_style = NULL;
2662 folderview_init(folderview);
2664 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2665 folderview_column_set_titles(folderview);
2666 folderview_set_all();
2668 g_signal_handlers_block_by_func
2669 (G_OBJECT(folderview->ctree),
2670 G_CALLBACK(folderview_selected), folderview);
2673 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2674 GTK_CMCTREE(folderview->ctree), NULL, item);
2676 folderview_select(folderview, item);
2677 folderview->open_folder = FALSE;
2678 folderview->selected = node;
2681 g_signal_handlers_unblock_by_func
2682 (G_OBJECT(folderview->ctree),
2683 G_CALLBACK(folderview_selected), folderview);
2685 pos = gtk_scrolled_window_get_vadjustment(
2686 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2687 gtk_adjustment_set_value(pos, height);
2688 gtk_adjustment_changed(pos);
2689 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2692 static void drag_state_stop(FolderView *folderview)
2694 if (folderview->drag_timer)
2695 g_source_remove(folderview->drag_timer);
2696 folderview->drag_timer = 0;
2697 folderview->drag_node = NULL;
2700 static gint folderview_defer_expand(FolderView *folderview)
2702 if (folderview->drag_node) {
2703 folderview_recollapse_nodes(folderview, folderview->drag_node);
2704 if (folderview->drag_item->collapsed) {
2705 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2706 folderview->nodes_to_recollapse = g_slist_append
2707 (folderview->nodes_to_recollapse, folderview->drag_node);
2710 folderview->drag_item = NULL;
2711 folderview->drag_timer = 0;
2715 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2717 /* the idea is that we call drag_state_start() whenever we want expansion to
2718 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2719 * we need to call drag_state_stop() */
2720 drag_state_stop(folderview);
2721 /* request expansion */
2722 if (0 != (folderview->drag_timer = g_timeout_add
2723 (prefs_common.hover_timeout,
2724 (GtkFunction)folderview_defer_expand,
2726 folderview->drag_node = node;
2727 folderview->drag_item = item;
2730 #ifndef GENERIC_UMPC
2731 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2732 FolderView *folderview)
2734 GdkDragContext *context;
2736 cm_return_if_fail(folderview != NULL);
2737 if (folderview->selected == NULL) return;
2738 if (folderview->nodes_to_recollapse)
2739 g_slist_free(folderview->nodes_to_recollapse);
2740 folderview->nodes_to_recollapse = NULL;
2741 context = gtk_drag_begin(widget, folderview->target_list,
2742 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2743 gtk_drag_set_icon_default(context);
2746 static void folderview_drag_data_get(GtkWidget *widget,
2747 GdkDragContext *drag_context,
2748 GtkSelectionData *selection_data,
2751 FolderView *folderview)
2755 gchar *source = NULL;
2756 if (info == TARGET_DUMMY) {
2757 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2758 cur != NULL; cur = cur->next) {
2759 item = gtk_cmctree_node_get_row_data
2760 (GTK_CMCTREE(folderview->ctree),
2761 GTK_CMCTREE_NODE(cur->data));
2763 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2764 gtk_selection_data_set(selection_data,
2765 selection_data->target, 8,
2766 source, strlen(source));
2772 g_warning("unknown info %d\n", info);
2776 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2778 FolderUpdateData *hookdata;
2779 FolderView *folderview;
2783 folderview = (FolderView *) userdata;
2784 cm_return_val_if_fail(hookdata != NULL, FALSE);
2785 cm_return_val_if_fail(folderview != NULL, FALSE);
2787 ctree = folderview->ctree;
2788 cm_return_val_if_fail(ctree != NULL, FALSE);
2790 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2791 folderview_create_folder_node(folderview, hookdata->item);
2792 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2793 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2794 NULL, folder_item_parent(hookdata->item));
2795 folderview_sort_folders(folderview, node, hookdata->folder);
2796 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2797 GtkCMCTreeNode *node;
2799 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2801 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2802 if (folderview->selected == node)
2803 folderview->selected = NULL;
2804 if (folderview->opened == node)
2805 folderview->opened = NULL;
2807 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2808 folderview_set(folderview);
2813 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2814 GdkDragContext *context,
2818 FolderView *folderview)
2821 FolderItem *item = NULL, *src_item = NULL;
2822 GtkCMCTreeNode *node = NULL;
2823 gboolean acceptable = FALSE;
2824 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2825 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2826 int height = (int)pos->page_size;
2827 int total_height = (int)pos->upper;
2828 int vpos = (int) pos->value;
2829 int offset = prefs_common.show_col_headers ? 24:0;
2831 if (gtk_cmclist_get_selection_info
2832 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2833 GtkWidget *srcwidget;
2835 if (y > height - 24 && height + vpos < total_height) {
2836 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2837 gtk_adjustment_changed(pos);
2839 if (y < 48 && y > 0) {
2840 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2841 gtk_adjustment_changed(pos);
2844 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2845 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2846 src_item = folderview->summaryview->folder_item;
2848 srcwidget = gtk_drag_get_source_widget(context);
2849 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2850 /* comes from summaryview */
2851 /* we are copying messages, so only accept folder items that are not
2852 the source item, are no root items and can copy messages */
2853 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2854 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2855 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2857 } else if (srcwidget == folderview->ctree) {
2858 /* comes from folderview */
2859 /* we are moving folder items, only accept folders that are not
2860 the source items and can copy messages and create folder items */
2861 if (item && item->folder && src_item && src_item != item &&
2862 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2863 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2864 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2865 || item->folder == src_item->folder))
2868 /* comes from another app */
2869 /* we are adding messages, so only accept folder items that are
2870 no root items and can copy messages */
2871 if (item && item->folder && folder_item_parent(item) != NULL
2872 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2873 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2878 if (acceptable || (src_item && src_item == item))
2879 drag_state_start(folderview, node, item);
2882 g_signal_handlers_block_by_func
2884 G_CALLBACK(folderview_selected), folderview);
2885 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2886 g_signal_handlers_unblock_by_func
2888 G_CALLBACK(folderview_selected), folderview);
2889 gdk_drag_status(context,
2890 (context->actions == GDK_ACTION_COPY ?
2891 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2893 if (folderview->opened)
2894 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2895 gdk_drag_status(context, 0, time);
2901 static void folderview_drag_leave_cb(GtkWidget *widget,
2902 GdkDragContext *context,
2904 FolderView *folderview)
2906 drag_state_stop(folderview);
2907 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2910 static void free_info (gpointer stuff, gpointer data)
2915 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2916 guint time, FolderItem *item)
2919 GSList *msglist = NULL;
2920 list = uri_list_extract_filenames(data);
2921 if (!(item && item->folder && folder_item_parent(item) != NULL
2922 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2924 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2925 debug_print("item doesn't fit\n");
2929 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2930 debug_print("list is empty\n");
2933 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2934 MsgFileInfo *info = NULL;
2936 if (file_is_email((gchar *)tmp->data)) {
2937 info = g_new0(MsgFileInfo, 1);
2938 info->msginfo = NULL;
2939 info->file = (gchar *)tmp->data;
2940 msglist = g_slist_prepend(msglist, info);
2941 debug_print("file is a mail\n");
2943 debug_print("file isn't a mail\n");
2947 msglist = g_slist_reverse(msglist);
2948 folder_item_add_msgs(item, msglist, FALSE);
2949 g_slist_foreach(msglist, free_info, NULL);
2950 g_slist_free(msglist);
2951 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2953 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2955 list_free_strings(list);
2959 static void folderview_drag_received_cb(GtkWidget *widget,
2960 GdkDragContext *drag_context,
2963 GtkSelectionData *data,
2966 FolderView *folderview)
2969 FolderItem *item = NULL, *src_item;
2970 GtkCMCTreeNode *node;
2971 int offset = prefs_common.show_col_headers ? 24:0;
2973 if (info == TARGET_DUMMY) {
2974 drag_state_stop(folderview);
2975 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2976 /* comes from summaryview */
2977 if (gtk_cmclist_get_selection_info
2978 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2981 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2982 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2983 src_item = folderview->summaryview->folder_item;
2985 if (item->no_select) {
2986 alertpanel_error(_("The destination folder can only be used to "
2987 "store subfolders."));
2990 /* re-check (due to acceptable possibly set for folder moves */
2991 if (!(item && item->folder && item->path && !item->no_select &&
2992 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2995 if (item && src_item) {
2996 switch (drag_context->action) {
2997 case GDK_ACTION_COPY:
2998 summary_copy_selected_to(folderview->summaryview, item);
2999 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3001 case GDK_ACTION_MOVE:
3002 case GDK_ACTION_DEFAULT:
3004 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3005 summary_copy_selected_to(folderview->summaryview, item);
3007 summary_move_selected_to(folderview->summaryview, item);
3008 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3011 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3013 /* comes from folderview */
3015 gboolean folder_is_normal = TRUE;
3016 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3018 source = data->data + 17;
3019 if (gtk_cmclist_get_selection_info
3020 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3022 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3025 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3026 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3027 src_item = folder_find_item_from_identifier(source);
3031 src_item->stype == F_NORMAL &&
3032 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3033 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3034 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3035 !folder_has_parent_of_type(src_item, F_TRASH);
3036 if (!item || !src_item || !folder_is_normal) {
3037 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3041 folderview_move_folder(folderview, src_item, item, copy);
3042 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3044 folderview->nodes_to_recollapse = NULL;
3045 } else if (info == TARGET_MAIL_URI_LIST) {
3046 if (gtk_cmclist_get_selection_info
3047 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3050 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3052 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3053 debug_print("no node\n");
3056 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3058 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3059 debug_print("no item\n");
3062 folderview_finish_dnd(data->data, drag_context, time, item);
3066 static void folderview_drag_end_cb(GtkWidget *widget,
3067 GdkDragContext *drag_context,
3068 FolderView *folderview)
3070 drag_state_stop(folderview);
3071 g_slist_free(folderview->nodes_to_recollapse);
3072 folderview->nodes_to_recollapse = NULL;
3075 void folderview_register_popup(FolderViewPopup *fpopup)
3079 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3080 FolderView *folderview = folderviews->data;
3081 GtkActionGroup *factory;
3083 factory = create_action_group(folderview, fpopup);
3084 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3086 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3089 void folderview_unregister_popup(FolderViewPopup *fpopup)
3094 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3095 FolderView *folderview = folderviews->data;
3097 g_hash_table_remove(folderview->popups, fpopup->klass);
3099 g_hash_table_remove(folderview_popups, fpopup->klass);