2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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 g_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 g_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 g_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 g_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 str = g_strdup_printf("%s%s",
1587 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1589 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1591 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1595 if (!folder_item_parent(item)) {
1596 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1597 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1598 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1600 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1601 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1602 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1605 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1606 folder_has_parent_of_type(item, F_DRAFT) ||
1607 folder_has_parent_of_type(item, F_TRASH)) {
1608 use_bold = use_color = FALSE;
1609 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1610 /* highlight queue folder if there are any messages */
1611 use_bold = use_color = (item->total_msgs > 0);
1613 /* if unread messages exist, print with bold font */
1614 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1616 /* if new messages exist, print with colored letter */
1618 (item->new_msgs > 0) ||
1620 folderview_have_new_children(folderview, item));
1623 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1628 if (item->prefs->color > 0 && !use_color) {
1629 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1630 color_style = gtk_style_copy(bold_style);
1631 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1632 style = color_style;
1633 } else if (use_color) {
1634 style = bold_color_style;
1637 if (item->op_count > 0) {
1638 style = bold_tgtfold_style;
1640 } else if (use_color) {
1641 style = normal_color_style;
1642 gtk_cmctree_node_set_foreground(ctree, node,
1643 &folderview->color_new);
1644 } else if (item->op_count > 0) {
1645 style = bold_tgtfold_style;
1646 } else if (item->prefs->color > 0) {
1648 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1649 color_style = gtk_style_copy(normal_style);
1650 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1651 style = color_style;
1653 style = normal_style;
1656 gtk_cmctree_node_set_row_style(ctree, node, style);
1658 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1659 folderview_update_node(folderview, node);
1662 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1665 FolderView *folderview;
1667 GtkCMCTreeNode *node;
1669 g_return_if_fail(item != NULL);
1671 for (list = folderview_list; list != NULL; list = list->next) {
1672 folderview = (FolderView *)list->data;
1673 ctree = GTK_CMCTREE(folderview->ctree);
1675 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1677 item->search_match = matches;
1678 folderview_update_node(folderview, node);
1683 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1685 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1686 FolderView *folderview = (FolderView *)data;
1688 GtkCMCTreeNode *node;
1689 g_return_val_if_fail(update_info != NULL, TRUE);
1690 g_return_val_if_fail(update_info->item != NULL, TRUE);
1691 g_return_val_if_fail(folderview != NULL, FALSE);
1693 ctree = GTK_CMCTREE(folderview->ctree);
1695 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1698 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1699 folderview_update_node(folderview, node);
1701 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1702 update_info->item == folderview->summaryview->folder_item &&
1703 update_info->item != NULL)
1704 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1705 summary_show(folderview->summaryview, update_info->item);
1711 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1712 GNode *gnode, GtkCMCTreeNode *cnode,
1715 FolderView *folderview = (FolderView *)data;
1716 FolderItem *item = FOLDER_ITEM(gnode->data);
1718 g_return_val_if_fail(item != NULL, FALSE);
1720 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1721 folderview_update_node(folderview, cnode);
1726 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1729 FolderView *folderview = (FolderView *)data;
1732 if (GTK_CMCTREE_ROW(node)->children) {
1733 item = gtk_cmctree_node_get_row_data(ctree, node);
1734 g_return_if_fail(item != NULL);
1736 if (!item->collapsed)
1737 gtk_cmctree_expand(ctree, node);
1739 folderview_update_node(folderview, node);
1743 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1744 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1747 GtkCMCTreeNode *node, *parent, *sibling;
1749 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1751 g_warning("%s not found.\n", item->path);
1753 parent = GTK_CMCTREE_ROW(node)->parent;
1754 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1755 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1757 sibling = GTK_CMCTREE_ROW(parent)->children;
1761 tmp = gtk_cmctree_node_get_row_data
1763 if (tmp->stype != F_NORMAL)
1764 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1768 if (node != sibling)
1769 gtk_cmctree_move(ctree, node, parent, sibling);
1776 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1779 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1780 GtkCMCTreeNode *prev = NULL;
1782 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1783 gtk_sctree_sort_recursive(ctree, root);
1784 if (root && GTK_CMCTREE_ROW(root)->parent) {
1785 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1788 set_special_folder(ctree, folder->inbox, root, &prev);
1789 set_special_folder(ctree, folder->outbox, root, &prev);
1790 set_special_folder(ctree, folder->draft, root, &prev);
1791 set_special_folder(ctree, folder->queue, root, &prev);
1792 set_special_folder(ctree, folder->trash, root, &prev);
1793 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1796 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1798 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1799 GtkCMCTreeNode *root;
1801 g_return_if_fail(folder != NULL);
1803 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1804 folderview_gnode_func, folderview);
1805 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1807 folderview_sort_folders(folderview, root, folder);
1810 /* callback functions */
1811 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1812 GdkEventButton *event)
1816 FolderViewPopup *fpopup;
1817 GtkActionGroup *action_group;
1819 FolderItem *special_trash = NULL, *special_queue = NULL;
1821 GtkUIManager *ui_manager = gtk_ui_manager_new();
1823 if (folderview->ui_manager)
1824 g_object_unref(folderview->ui_manager);
1826 folderview->ui_manager = ui_manager;
1827 item = folderview_get_selected_item(folderview);
1829 g_return_if_fail(item != NULL);
1830 g_return_if_fail(item->folder != NULL);
1831 folder = item->folder;
1833 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1836 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1838 fpopup = g_hash_table_lookup(folderview_popups, "common");
1839 action_group = g_hash_table_lookup(folderview->popups, "common");
1842 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1843 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1844 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1846 if (fpopup->add_menuitems)
1847 fpopup->add_menuitems(ui_manager, item);
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1850 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1851 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1856 if (fpopup->set_sensitivity != NULL)
1857 fpopup->set_sensitivity(ui_manager, item);
1859 if (NULL != (ac = account_find_from_item(item))) {
1860 special_trash = account_get_special_folder(ac, F_TRASH);
1861 special_queue = account_get_special_folder(ac, F_QUEUE);
1864 if ((item == folder->trash || item == special_trash
1865 || folder_has_parent_of_type(item, F_TRASH))) {
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1867 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1870 if ((item == folder->queue || item == special_queue
1871 || folder_has_parent_of_type(item, F_QUEUE))) {
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1876 #define SET_SENS(name, sens) \
1877 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1879 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1880 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1881 folderview->selected == folderview->opened);
1882 SET_SENS("FolderViewPopup/Properties", TRUE);
1884 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1885 item->total_msgs >= 1 && !item->processing_pending);
1886 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1887 !item->no_select && !item->processing_pending);
1889 if (item == folder->trash || item == special_trash
1890 || folder_has_parent_of_type(item, F_TRASH)) {
1891 GSList *msglist = folder_item_get_msg_list(item);
1892 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1893 procmsg_msg_list_free(msglist);
1895 if (item == folder->queue || item == special_queue
1896 || folder_has_parent_of_type(item, F_QUEUE)) {
1897 GSList *msglist = folder_item_get_msg_list(item);
1898 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1899 procmsg_msg_list_free(msglist);
1903 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1904 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1905 g_signal_connect(G_OBJECT(popup), "selection_done",
1906 G_CALLBACK(folderview_popup_close),
1908 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1909 event->button, event->time);
1912 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1913 FolderView *folderview)
1915 GtkCMCList *clist = GTK_CMCLIST(ctree);
1916 gint prev_row = -1, row = -1, column = -1;
1918 if (!event) return FALSE;
1920 if (event->button == 1 || event->button == 2) {
1921 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1922 folderview->open_folder = TRUE;
1924 if (event->type == GDK_2BUTTON_PRESS) {
1925 if (clist->selection) {
1926 GtkCMCTreeNode *node;
1928 node = GTK_CMCTREE_NODE(clist->selection->data);
1930 gtk_cmctree_toggle_expansion(
1933 folderview->open_folder = FALSE;
1940 if (event->button == 2 || event->button == 3) {
1942 if (clist->selection) {
1943 GtkCMCTreeNode *node;
1945 node = GTK_CMCTREE_NODE(clist->selection->data);
1947 prev_row = gtkut_ctree_get_nth_from_node
1948 (GTK_CMCTREE(ctree), node);
1951 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1954 if (prev_row != row) {
1955 gtk_cmclist_unselect_all(clist);
1956 if (event->button == 2)
1957 folderview_select_node
1959 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1962 gtk_cmclist_select_row(clist, row, column);
1966 if (event->button != 3) return FALSE;
1968 folderview_set_sens_and_popup_menu(folderview, row, event);
1972 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1973 FolderView *folderview)
1975 int row = -1, column = -1;
1977 if (!event) return FALSE;
1979 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1982 if (event->button == 1 && folderview->open_folder == FALSE &&
1983 folderview->opened != NULL) {
1984 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1985 folderview->opened);
1986 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1992 #define BREAK_ON_MODIFIER_KEY() \
1993 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1995 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1996 FolderView *folderview)
1998 if (!event) return FALSE;
2000 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2003 switch (event->keyval) {
2005 #ifndef GENERIC_UMPC
2009 if (folderview->selected) {
2010 folderview_select_node(folderview,
2011 folderview->selected);
2016 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2017 gtk_cmctree_toggle_expansion(
2018 GTK_CMCTREE(folderview->ctree),
2019 folderview->selected);
2024 BREAK_ON_MODIFIER_KEY();
2025 if (folderview->selected) {
2026 if (folderview->opened == folderview->selected &&
2027 (!folderview->summaryview->folder_item ||
2028 folderview->summaryview->folder_item->total_msgs == 0))
2029 folderview_select_next_unread(folderview, TRUE);
2031 folderview_select_node(folderview,
2032 folderview->selected);
2042 typedef struct _PostponedSelectData
2045 GtkCMCTreeNode *row;
2047 FolderView *folderview;
2048 } PostponedSelectData;
2050 static gboolean postpone_select(void *data)
2052 PostponedSelectData *psdata = (PostponedSelectData *)data;
2053 debug_print("trying again\n");
2054 psdata->folderview->open_folder = TRUE;
2055 main_window_cursor_normal(psdata->folderview->mainwin);
2056 STATUSBAR_POP(psdata->folderview->mainwin);
2057 folderview_selected(psdata->ctree, psdata->row,
2058 psdata->column, psdata->folderview);
2063 void folderview_close_opened(FolderView *folderview)
2065 if (folderview->opened) {
2066 FolderItem *olditem;
2068 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2069 folderview->opened);
2071 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2072 olditem->path ? olditem->path:olditem->name);
2073 /* will be null if we just moved the previously opened folder */
2074 STATUSBAR_PUSH(folderview->mainwin, buf);
2075 main_window_cursor_wait(folderview->mainwin);
2077 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2078 summary_show(folderview->summaryview, NULL);
2079 folder_item_close(olditem);
2080 main_window_cursor_normal(folderview->mainwin);
2081 STATUSBAR_POP(folderview->mainwin);
2082 if (olditem->folder->klass->item_closed)
2083 olditem->folder->klass->item_closed(olditem);
2088 if (folderview->opened &&
2089 !GTK_CMCTREE_ROW(folderview->opened)->children)
2090 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2092 folderview->opened = NULL;
2094 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2095 gint column, FolderView *folderview)
2097 static gboolean can_select = TRUE; /* exclusive lock */
2102 GtkCMCTreeNode *old_opened = folderview->opened;
2104 folderview->selected = row;
2106 debug_print("newly selected %p, opened %p\n", folderview->selected,
2107 folderview->opened);
2108 if (folderview->opened == row) {
2109 folderview->open_folder = FALSE;
2114 item = gtk_cmctree_node_get_row_data(ctree, row);
2117 folderview->open_folder = FALSE;
2121 if (!can_select || summary_is_locked(folderview->summaryview)) {
2122 if (folderview->opened) {
2123 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2124 gtk_cmctree_select(ctree, folderview->opened);
2126 folderview->open_folder = FALSE;
2131 if (!folderview->open_folder) {
2138 /* Save cache for old folder */
2139 /* We don't want to lose all caches if sylpheed crashed */
2140 /* resets folderview->opened to NULL */
2141 folderview_close_opened(folderview);
2143 /* CLAWS: set compose button type: news folder items
2144 * always have a news folder as parent */
2146 toolbar_set_compose_button
2147 (folderview->mainwin->toolbar,
2148 FOLDER_TYPE(item->folder) == F_NEWS ?
2149 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2152 debug_print("Folder %s is selected\n", item->path);
2154 if (!GTK_CMCTREE_ROW(row)->children)
2155 gtk_cmctree_expand(ctree, row);
2157 /* ungrab the mouse event */
2158 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2159 gtk_grab_remove(GTK_WIDGET(ctree));
2160 if (gdk_pointer_is_grabbed())
2161 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2165 /* TODO: wwp: avoid displaying (null) in the status bar */
2166 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2167 item->path : "(null)");
2168 debug_print("%s\n", buf);
2169 STATUSBAR_PUSH(folderview->mainwin, buf);
2172 main_window_cursor_wait(folderview->mainwin);
2174 if (folderview->scanning_folder == item->folder) {
2177 res = folder_item_open(item);
2180 if (res == -1 && item->no_select == FALSE) {
2181 main_window_cursor_normal(folderview->mainwin);
2182 STATUSBAR_POP(folderview->mainwin);
2184 alertpanel_error(_("Folder could not be opened."));
2186 folderview->open_folder = FALSE;
2190 } else if (res == -2 && item->no_select == FALSE) {
2191 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2192 data->ctree = ctree;
2194 data->column = column;
2195 data->folderview = folderview;
2196 debug_print("postponing open of %s till end of scan\n",
2197 item->path ? item->path:item->name);
2198 folderview->open_folder = FALSE;
2200 g_timeout_add(500, postpone_select, data);
2205 main_window_cursor_normal(folderview->mainwin);
2208 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2209 opened = summary_show(folderview->summaryview, item);
2211 folder_clean_cache_memory(item);
2214 gtkut_ctree_set_focus_row(ctree, old_opened);
2215 gtk_cmctree_select(ctree, old_opened);
2216 folderview->opened = old_opened;
2218 folderview->opened = row;
2219 if (gtk_cmctree_node_is_visible(ctree, row)
2220 != GTK_VISIBILITY_FULL)
2221 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2224 STATUSBAR_POP(folderview->mainwin);
2226 folderview->open_folder = FALSE;
2231 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2232 FolderView *folderview)
2236 item = gtk_cmctree_node_get_row_data(ctree, node);
2237 g_return_if_fail(item != NULL);
2238 item->collapsed = FALSE;
2239 folderview_update_node(folderview, node);
2242 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2243 FolderView *folderview)
2247 item = gtk_cmctree_node_get_row_data(ctree, node);
2248 g_return_if_fail(item != NULL);
2249 item->collapsed = TRUE;
2250 folderview_update_node(folderview, node);
2253 static void folderview_popup_close(GtkMenuShell *menu_shell,
2254 FolderView *folderview)
2256 if (!folderview->opened) return;
2258 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2261 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2262 FolderView *folderview)
2264 FolderColumnType type = folderview->col_state[column].type;
2266 prefs_common.folder_col_size[type] = width;
2269 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2273 folderview_create_folder_node(folderview, item);
2275 if (!item || !item->folder || !item->folder->node)
2278 srcnode = item->folder->node;
2279 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2280 srcnode = srcnode->children;
2281 while (srcnode != NULL) {
2282 if (srcnode && srcnode->data) {
2283 FolderItem *next_item = (FolderItem*) srcnode->data;
2284 folderview_create_folder_node_recursive(folderview, next_item);
2286 srcnode = srcnode->next;
2290 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2292 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2293 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2294 GtkCMCTreeNode *node, *parent_node;
2295 gint *col_pos = folderview->col_pos;
2296 FolderItemUpdateData hookdata;
2298 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2299 if (parent_node == NULL)
2302 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2304 text[col_pos[F_COL_FOLDER]] = item->name;
2305 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2310 gtk_cmctree_expand(ctree, parent_node);
2311 gtk_cmctree_node_set_row_data(ctree, node, item);
2313 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2314 folderview_sort_folders(folderview, parent_node, item->folder);
2316 hookdata.item = item;
2317 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2318 hookdata.msg = NULL;
2319 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2321 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2324 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2326 FolderView *folderview = (FolderView *)data;
2327 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2329 GSList *mlist = NULL;
2331 FolderItem *special_trash = NULL;
2334 if (!folderview->selected) return;
2335 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2336 g_return_if_fail(item != NULL);
2337 g_return_if_fail(item->folder != NULL);
2339 if (NULL != (ac = account_find_from_item(item)))
2340 special_trash = account_get_special_folder(ac, F_TRASH);
2342 if (item != item->folder->trash && item != special_trash
2343 && !folder_has_parent_of_type(item, F_TRASH)) return;
2345 if (prefs_common.ask_on_clean) {
2346 if (alertpanel(_("Empty trash"),
2347 _("Delete all messages in trash?"),
2348 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2352 mlist = folder_item_get_msg_list(item);
2354 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2355 MsgInfo * msginfo = (MsgInfo *) cur->data;
2356 if (MSG_IS_LOCKED(msginfo->flags))
2358 /* is it partially received? (partial_recv isn't cached) */
2359 if (msginfo->total_size != 0 &&
2360 msginfo->size != (off_t)msginfo->total_size)
2361 partial_mark_for_delete(msginfo);
2363 procmsg_msg_list_free(mlist);
2365 folder_item_remove_all_msg(item);
2368 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2370 FolderView *folderview = (FolderView *)data;
2371 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2373 FolderItem *special_queue = NULL;
2375 gchar *errstr = NULL;
2377 if (!folderview->selected) return;
2378 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2379 g_return_if_fail(item != NULL);
2380 g_return_if_fail(item->folder != NULL);
2382 if (NULL != (ac = account_find_from_item(item)))
2383 special_queue = account_get_special_folder(ac, F_QUEUE);
2385 if (item != item->folder->queue && item != special_queue
2386 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2388 if (procmsg_queue_is_empty(item))
2391 if (prefs_common.work_offline)
2392 if (alertpanel(_("Offline warning"),
2393 _("You're working offline. Override?"),
2394 GTK_STOCK_NO, GTK_STOCK_YES,
2395 NULL) != G_ALERTALTERNATE)
2398 /* ask for confirmation before sending queued messages only
2399 in online mode and if there is at least one message queued
2400 in any of the folder queue
2402 if (prefs_common.confirm_send_queued_messages) {
2403 if (!prefs_common.work_offline) {
2404 if (alertpanel(_("Send queued messages"),
2405 _("Send all queued messages?"),
2406 GTK_STOCK_CANCEL, _("_Send"),
2407 NULL) != G_ALERTALTERNATE)
2412 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2414 alertpanel_error_log(_("Some errors occurred while "
2415 "sending queued messages."));
2417 alertpanel_error_log(_("Some errors occurred "
2418 "while sending queued messages:\n%s"), errstr);
2424 static void folderview_search_cb(GtkAction *action, gpointer data)
2426 FolderView *folderview = (FolderView *)data;
2427 summary_search(folderview->summaryview);
2430 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2432 FolderView *folderview = (FolderView *)data;
2433 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2436 if (!folderview->selected) return;
2438 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2439 g_return_if_fail(item != NULL);
2440 g_return_if_fail(item->folder != NULL);
2442 item->processing_pending = TRUE;
2443 folder_item_apply_processing(item);
2444 item->processing_pending = FALSE;
2447 static void folderview_property_cb(GtkAction *action, gpointer data)
2449 FolderView *folderview = (FolderView *)data;
2450 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2453 if (!folderview->selected) return;
2455 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2456 g_return_if_fail(item != NULL);
2457 g_return_if_fail(item->folder != NULL);
2459 prefs_folder_item_open(item);
2462 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2464 GSList *list = NULL;
2465 GSList *done = NULL;
2466 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2468 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2469 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2470 && list->data != node) {
2471 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2472 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2475 for (list = done; list != NULL; list = g_slist_next(list)) {
2476 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2482 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2483 FolderItem *to_folder, gboolean copy)
2485 FolderItem *from_parent = NULL;
2486 FolderItem *new_folder = NULL;
2487 GtkCMCTreeNode *src_node = NULL;
2491 g_return_if_fail(folderview != NULL);
2492 g_return_if_fail(from_folder != NULL);
2493 g_return_if_fail(to_folder != NULL);
2495 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2496 from_parent = folder_item_parent(from_folder);
2498 if (prefs_common.warn_dnd) {
2499 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2500 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2501 from_folder->name, to_folder->name);
2502 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2503 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2504 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2507 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2509 else if (status & G_ALERTDISABLE)
2510 prefs_common.warn_dnd = FALSE;
2513 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2514 from_folder->name, to_folder->name);
2515 STATUSBAR_PUSH(folderview->mainwin, buf);
2517 summary_clear_all(folderview->summaryview);
2518 folderview->opened = NULL;
2519 folderview->selected = NULL;
2520 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2522 main_window_cursor_wait(folderview->mainwin);
2524 statusbar_verbosity_set(FALSE);
2525 folder_item_update_freeze();
2526 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2527 statusbar_verbosity_set(FALSE);
2528 main_window_cursor_normal(folderview->mainwin);
2529 STATUSBAR_POP(folderview->mainwin);
2530 folder_item_update_thaw();
2531 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2533 folderview_sort_folders(folderview,
2534 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2535 NULL, to_folder), new_folder->folder);
2536 folderview_select(folderview, new_folder);
2538 statusbar_verbosity_set(FALSE);
2539 main_window_cursor_normal(folderview->mainwin);
2540 STATUSBAR_POP(folderview->mainwin);
2541 folder_item_update_thaw();
2543 case F_MOVE_FAILED_DEST_IS_PARENT:
2544 alertpanel_error(_("Source and destination are the same."));
2546 case F_MOVE_FAILED_DEST_IS_CHILD:
2547 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2548 _("Can't move a folder to one of its children."));
2550 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2551 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2554 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2559 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2562 static gint folderview_clist_compare(GtkCMCList *clist,
2563 gconstpointer ptr1, gconstpointer ptr2)
2565 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2566 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2568 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2570 return item1->order - item2->order;
2573 // if only one folder has an order it comes first
2574 if (item1->order > 0)
2578 if (item2->order > 0)
2584 return (item2->name != NULL);
2588 return g_utf8_collate(item1->name, item2->name);
2591 static void folderview_processing_cb(GtkAction *action, gpointer data)
2593 FolderView *folderview = (FolderView *)data;
2594 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2598 if (!folderview->selected) return;
2600 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2601 g_return_if_fail(item != NULL);
2602 g_return_if_fail(item->folder != NULL);
2604 id = folder_item_get_identifier(item);
2605 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2608 prefs_filtering_open(&item->prefs->processing, title,
2609 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2613 void folderview_set_target_folder_color(gint color_op)
2617 FolderView *folderview;
2619 for (list = folderview_list; list != NULL; list = list->next) {
2620 folderview = (FolderView *)list->data;
2621 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2623 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2624 folderview->color_op;
2630 static gchar *last_smallfont = NULL;
2631 static gchar *last_normalfont = NULL;
2632 static gchar *last_boldfont = NULL;
2633 void folderview_reflect_prefs_pixmap_theme(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))
2658 g_free(last_smallfont);
2659 last_smallfont = g_strdup(SMALL_FONT);
2660 g_free(last_normalfont);
2661 last_normalfont = g_strdup(NORMAL_FONT);
2662 g_free(last_boldfont);
2663 last_boldfont = g_strdup(BOLD_FONT);
2666 normal_style = normal_color_style = bold_style =
2667 bold_color_style = bold_tgtfold_style = NULL;
2669 folderview_init(folderview);
2671 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2672 folderview_column_set_titles(folderview);
2673 folderview_set_all();
2675 g_signal_handlers_block_by_func
2676 (G_OBJECT(folderview->ctree),
2677 G_CALLBACK(folderview_selected), folderview);
2680 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2681 GTK_CMCTREE(folderview->ctree), NULL, item);
2683 folderview_select(folderview, item);
2684 folderview->open_folder = FALSE;
2685 folderview->selected = node;
2688 g_signal_handlers_unblock_by_func
2689 (G_OBJECT(folderview->ctree),
2690 G_CALLBACK(folderview_selected), folderview);
2692 pos = gtk_scrolled_window_get_vadjustment(
2693 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2694 gtk_adjustment_set_value(pos, height);
2695 gtk_adjustment_changed(pos);
2696 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2699 static void drag_state_stop(FolderView *folderview)
2701 if (folderview->drag_timer)
2702 g_source_remove(folderview->drag_timer);
2703 folderview->drag_timer = 0;
2704 folderview->drag_node = NULL;
2707 static gint folderview_defer_expand(FolderView *folderview)
2709 if (folderview->drag_node) {
2710 folderview_recollapse_nodes(folderview, folderview->drag_node);
2711 if (folderview->drag_item->collapsed) {
2712 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2713 folderview->nodes_to_recollapse = g_slist_append
2714 (folderview->nodes_to_recollapse, folderview->drag_node);
2717 folderview->drag_item = NULL;
2718 folderview->drag_timer = 0;
2722 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2724 /* the idea is that we call drag_state_start() whenever we want expansion to
2725 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2726 * we need to call drag_state_stop() */
2727 drag_state_stop(folderview);
2728 /* request expansion */
2729 if (0 != (folderview->drag_timer = g_timeout_add
2730 (prefs_common.hover_timeout,
2731 (GtkFunction)folderview_defer_expand,
2733 folderview->drag_node = node;
2734 folderview->drag_item = item;
2737 #ifndef GENERIC_UMPC
2738 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2739 FolderView *folderview)
2741 GdkDragContext *context;
2743 g_return_if_fail(folderview != NULL);
2744 if (folderview->selected == NULL) return;
2745 if (folderview->nodes_to_recollapse)
2746 g_slist_free(folderview->nodes_to_recollapse);
2747 folderview->nodes_to_recollapse = NULL;
2748 context = gtk_drag_begin(widget, folderview->target_list,
2749 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2750 gtk_drag_set_icon_default(context);
2753 static void folderview_drag_data_get(GtkWidget *widget,
2754 GdkDragContext *drag_context,
2755 GtkSelectionData *selection_data,
2758 FolderView *folderview)
2762 gchar *source = NULL;
2763 if (info == TARGET_DUMMY) {
2764 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2765 cur != NULL; cur = cur->next) {
2766 item = gtk_cmctree_node_get_row_data
2767 (GTK_CMCTREE(folderview->ctree),
2768 GTK_CMCTREE_NODE(cur->data));
2770 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2771 gtk_selection_data_set(selection_data,
2772 selection_data->target, 8,
2773 source, strlen(source));
2779 g_warning("unknown info %d\n", info);
2783 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2785 FolderUpdateData *hookdata;
2786 FolderView *folderview;
2790 folderview = (FolderView *) userdata;
2791 g_return_val_if_fail(hookdata != NULL, FALSE);
2792 g_return_val_if_fail(folderview != NULL, FALSE);
2794 ctree = folderview->ctree;
2795 g_return_val_if_fail(ctree != NULL, FALSE);
2797 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2798 folderview_create_folder_node(folderview, hookdata->item);
2799 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2800 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2801 NULL, folder_item_parent(hookdata->item));
2802 folderview_sort_folders(folderview, node, hookdata->folder);
2803 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2804 GtkCMCTreeNode *node;
2806 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2808 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2809 if (folderview->selected == node)
2810 folderview->selected = NULL;
2811 if (folderview->opened == node)
2812 folderview->opened = NULL;
2814 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2815 folderview_set(folderview);
2820 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2821 GdkDragContext *context,
2825 FolderView *folderview)
2828 FolderItem *item = NULL, *src_item = NULL;
2829 GtkCMCTreeNode *node = NULL;
2830 gboolean acceptable = FALSE;
2831 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2832 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2833 int height = (int)pos->page_size;
2834 int total_height = (int)pos->upper;
2835 int vpos = (int) pos->value;
2836 int offset = prefs_common.show_col_headers ? 24:0;
2838 if (gtk_cmclist_get_selection_info
2839 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2840 GtkWidget *srcwidget;
2842 if (y > height - 24 && height + vpos < total_height) {
2843 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2844 gtk_adjustment_changed(pos);
2846 if (y < 48 && y > 0) {
2847 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2848 gtk_adjustment_changed(pos);
2851 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2852 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2853 src_item = folderview->summaryview->folder_item;
2855 srcwidget = gtk_drag_get_source_widget(context);
2856 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2857 /* comes from summaryview */
2858 /* we are copying messages, so only accept folder items that are not
2859 the source item, are no root items and can copy messages */
2860 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2861 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2862 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2864 } else if (srcwidget == folderview->ctree) {
2865 /* comes from folderview */
2866 /* we are moving folder items, only accept folders that are not
2867 the source items and can copy messages and create folder items */
2868 if (item && item->folder && src_item && src_item != item &&
2869 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2870 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2871 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2872 || item->folder == src_item->folder))
2875 /* comes from another app */
2876 /* we are adding messages, so only accept folder items that are
2877 no root items and can copy messages */
2878 if (item && item->folder && folder_item_parent(item) != NULL
2879 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2880 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2885 if (acceptable || (src_item && src_item == item))
2886 drag_state_start(folderview, node, item);
2889 g_signal_handlers_block_by_func
2891 G_CALLBACK(folderview_selected), folderview);
2892 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2893 g_signal_handlers_unblock_by_func
2895 G_CALLBACK(folderview_selected), folderview);
2896 gdk_drag_status(context,
2897 (context->actions == GDK_ACTION_COPY ?
2898 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2900 if (folderview->opened)
2901 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2902 gdk_drag_status(context, 0, time);
2908 static void folderview_drag_leave_cb(GtkWidget *widget,
2909 GdkDragContext *context,
2911 FolderView *folderview)
2913 drag_state_stop(folderview);
2914 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2917 static void free_info (gpointer stuff, gpointer data)
2922 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2923 guint time, FolderItem *item)
2926 GSList *msglist = NULL;
2927 list = uri_list_extract_filenames(data);
2928 if (!(item && item->folder && folder_item_parent(item) != NULL
2929 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2931 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2932 debug_print("item doesn't fit\n");
2936 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2937 debug_print("list is empty\n");
2940 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2941 MsgFileInfo *info = NULL;
2943 if (file_is_email((gchar *)tmp->data)) {
2944 info = g_new0(MsgFileInfo, 1);
2945 info->msginfo = NULL;
2946 info->file = (gchar *)tmp->data;
2947 msglist = g_slist_prepend(msglist, info);
2948 debug_print("file is a mail\n");
2950 debug_print("file isn't a mail\n");
2954 msglist = g_slist_reverse(msglist);
2955 folder_item_add_msgs(item, msglist, FALSE);
2956 g_slist_foreach(msglist, free_info, NULL);
2957 g_slist_free(msglist);
2958 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2960 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2962 list_free_strings(list);
2966 static void folderview_drag_received_cb(GtkWidget *widget,
2967 GdkDragContext *drag_context,
2970 GtkSelectionData *data,
2973 FolderView *folderview)
2976 FolderItem *item = NULL, *src_item;
2977 GtkCMCTreeNode *node;
2978 int offset = prefs_common.show_col_headers ? 24:0;
2980 if (info == TARGET_DUMMY) {
2981 drag_state_stop(folderview);
2982 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2983 /* comes from summaryview */
2984 if (gtk_cmclist_get_selection_info
2985 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2988 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2989 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2990 src_item = folderview->summaryview->folder_item;
2992 if (item->no_select) {
2993 alertpanel_error(_("The destination folder can only be used to "
2994 "store subfolders."));
2997 /* re-check (due to acceptable possibly set for folder moves */
2998 if (!(item && item->folder && item->path && !item->no_select &&
2999 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3002 if (item && src_item) {
3003 switch (drag_context->action) {
3004 case GDK_ACTION_COPY:
3005 summary_copy_selected_to(folderview->summaryview, item);
3006 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3008 case GDK_ACTION_MOVE:
3009 case GDK_ACTION_DEFAULT:
3011 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3012 summary_copy_selected_to(folderview->summaryview, item);
3014 summary_move_selected_to(folderview->summaryview, item);
3015 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3018 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3020 /* comes from folderview */
3022 gboolean folder_is_normal = TRUE;
3023 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3025 source = data->data + 17;
3026 if (gtk_cmclist_get_selection_info
3027 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3029 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3032 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3033 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3034 src_item = folder_find_item_from_identifier(source);
3038 src_item->stype == F_NORMAL &&
3039 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3040 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3041 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3042 !folder_has_parent_of_type(src_item, F_TRASH);
3043 if (!item || !src_item || !folder_is_normal) {
3044 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3048 folderview_move_folder(folderview, src_item, item, copy);
3049 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3051 folderview->nodes_to_recollapse = NULL;
3052 } else if (info == TARGET_MAIL_URI_LIST) {
3053 if (gtk_cmclist_get_selection_info
3054 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3057 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3059 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3060 debug_print("no node\n");
3063 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3065 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3066 debug_print("no item\n");
3069 folderview_finish_dnd(data->data, drag_context, time, item);
3073 static void folderview_drag_end_cb(GtkWidget *widget,
3074 GdkDragContext *drag_context,
3075 FolderView *folderview)
3077 drag_state_stop(folderview);
3078 g_slist_free(folderview->nodes_to_recollapse);
3079 folderview->nodes_to_recollapse = NULL;
3082 void folderview_register_popup(FolderViewPopup *fpopup)
3086 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3087 FolderView *folderview = folderviews->data;
3088 GtkActionGroup *factory;
3090 factory = create_action_group(folderview, fpopup);
3091 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3093 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3096 void folderview_unregister_popup(FolderViewPopup *fpopup)
3101 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3102 FolderView *folderview = folderviews->data;
3104 g_hash_table_remove(folderview->popups, fpopup->klass);
3106 g_hash_table_remove(folderview_popups, fpopup->klass);