2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
467 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
468 prefs_common.folder_col_size[i]);
469 gtk_cmclist_set_column_visibility
470 (GTK_CMCLIST(ctree), i, col_state[i].visible);
473 g_signal_connect(G_OBJECT(ctree), "key_press_event",
474 G_CALLBACK(folderview_key_pressed),
476 g_signal_connect(G_OBJECT(ctree), "button_press_event",
477 G_CALLBACK(folderview_button_pressed),
480 g_signal_connect(G_OBJECT(ctree), "popup-menu",
481 G_CALLBACK(folderview_popup_menu), folderview);
483 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
484 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
485 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
486 G_CALLBACK(folderview_popup_menu), folderview);
488 g_signal_connect(G_OBJECT(ctree), "button_release_event",
489 G_CALLBACK(folderview_button_released),
491 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
492 G_CALLBACK(folderview_selected), folderview);
494 /* drag-n-dropping folders on maemo is impractical as this
495 * opens the folder almost everytime */
496 g_signal_connect(G_OBJECT(ctree), "start_drag",
497 G_CALLBACK(folderview_start_drag), folderview);
499 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
500 G_CALLBACK(folderview_drag_data_get),
503 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
504 G_CALLBACK(folderview_tree_expanded),
506 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
507 G_CALLBACK(folderview_tree_collapsed),
510 g_signal_connect(G_OBJECT(ctree), "resize_column",
511 G_CALLBACK(folderview_col_resized),
515 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
516 folderview_drag_types, 2,
517 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
518 g_signal_connect(G_OBJECT(ctree), "drag_motion",
519 G_CALLBACK(folderview_drag_motion_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_leave",
522 G_CALLBACK(folderview_drag_leave_cb),
524 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
525 G_CALLBACK(folderview_drag_received_cb),
527 g_signal_connect(G_OBJECT(ctree), "drag_end",
528 G_CALLBACK(folderview_drag_end_cb),
531 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
536 void folderview_set_column_order(FolderView *folderview)
538 GtkWidget *ctree = folderview->ctree;
539 FolderItem *item = folderview_get_selected_item(folderview);
540 FolderItem *sel_item = NULL, *op_item = NULL;
541 GtkWidget *scrolledwin = folderview->scrolledwin;
543 if (folderview->selected)
544 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
545 if (folderview->opened)
546 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
548 debug_print("recreating tree...\n");
549 gtk_widget_destroy(folderview->ctree);
552 folderview->ctree = ctree = folderview_ctree_create(folderview);
553 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
554 GTK_CMCLIST(ctree)->hadjustment);
555 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
556 GTK_CMCLIST(ctree)->vadjustment);
557 gtk_widget_show(ctree);
560 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
562 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
564 folderview_set(folderview);
565 folderview_column_set_titles(folderview);
567 folderview_select(folderview,item);
570 FolderView *folderview_create(void)
572 FolderView *folderview;
573 GtkWidget *scrolledwin;
576 debug_print("Creating folder view...\n");
577 folderview = g_new0(FolderView, 1);
579 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
580 gtk_scrolled_window_set_policy
581 (GTK_SCROLLED_WINDOW(scrolledwin),
582 GTK_POLICY_AUTOMATIC,
583 prefs_common.folderview_vscrollbar_policy);
584 gtk_widget_set_size_request(scrolledwin,
585 prefs_common.folderview_width,
586 prefs_common.folderview_height);
588 folderview->scrolledwin = scrolledwin;
589 ctree = folderview_ctree_create(folderview);
591 /* create popup factories */
592 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
593 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
595 folderview->ctree = ctree;
597 folderview->folder_update_callback_id =
598 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
599 folderview->folder_item_update_callback_id =
600 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
602 gtk_widget_show_all(scrolledwin);
604 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
605 folderview_list = g_list_append(folderview_list, folderview);
606 folderview->deferred_refresh_id = -1;
607 folderview->scroll_timeout_id = -1;
611 void folderview_init(FolderView *folderview)
613 GtkWidget *ctree = folderview->ctree;
616 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
617 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
618 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
619 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
620 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
621 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
622 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
623 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
664 PangoFontDescription *font_desc;
665 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
666 font_desc = pango_font_description_from_string(NORMAL_FONT);
668 if (normal_style->font_desc)
669 pango_font_description_free
670 (normal_style->font_desc);
671 normal_style->font_desc = font_desc;
673 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
674 normal_color_style = gtk_style_copy(normal_style);
675 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
677 gtk_widget_set_style(ctree, normal_style);
681 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
682 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
683 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
684 pango_font_description_set_weight
685 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
687 PangoFontDescription *font_desc;
688 font_desc = pango_font_description_from_string(BOLD_FONT);
690 if (bold_style->font_desc)
691 pango_font_description_free
692 (bold_style->font_desc);
693 bold_style->font_desc = font_desc;
696 bold_color_style = gtk_style_copy(bold_style);
697 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
699 bold_tgtfold_style = gtk_style_copy(bold_style);
700 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
704 static gboolean folderview_defer_set(gpointer data)
706 FolderView *folderview = (FolderView *)data;
707 MainWindow *mainwin = folderview->mainwin;
711 if (mainwin->lock_count)
714 debug_print("doing deferred folderview_set now\n");
715 folderview_set(folderview);
717 folderview->deferred_refresh_id = -1;
721 void folderview_set(FolderView *folderview)
723 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
724 MainWindow *mainwin = folderview->mainwin;
725 FolderItem *sel_item = NULL, *op_item = NULL;
730 if (mainwin->lock_count) {
731 if (folderview->deferred_refresh_id == -1)
732 folderview->deferred_refresh_id =
733 g_timeout_add(500, folderview_defer_set, folderview);
734 debug_print("deferred folderview_set\n");
739 debug_print("Setting folder info...\n");
740 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
742 main_window_cursor_wait(mainwin);
744 if (folderview->selected)
745 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
746 if (folderview->opened)
747 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
749 folderview->selected = NULL;
750 folderview->opened = NULL;
752 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
753 gtk_cmclist_clear(GTK_CMCLIST(ctree));
755 folderview_set_folders(folderview);
758 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
760 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
762 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
763 main_window_cursor_normal(mainwin);
764 STATUSBAR_POP(mainwin);
768 void folderview_set_all(void)
772 for (list = folderview_list; list != NULL; list = list->next)
773 folderview_set((FolderView *)list->data);
776 void folderview_select(FolderView *folderview, FolderItem *item)
778 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
779 GtkCMCTreeNode *node;
780 GtkCMCTreeNode *old_selected = folderview->selected;
784 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
785 if (node) folderview_select_node(folderview, node);
787 if (old_selected != node)
788 folder_update_op_count();
791 static void mark_all_read_cb(GtkAction *action, gpointer data)
793 FolderView *folderview = (FolderView *)data;
797 item = folderview_get_selected_item(folderview);
801 if (folderview->summaryview->folder_item != item
802 && prefs_common.ask_mark_all_read) {
803 val = alertpanel_full(_("Mark all as read"),
804 _("Do you really want to mark all mails in this "
805 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
806 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
808 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
810 else if (val & G_ALERTDISABLE)
811 prefs_common.ask_mark_all_read = FALSE;
815 folder_item_update_freeze();
816 if (folderview->summaryview->folder_item != item)
817 summary_lock(folderview->summaryview);
819 summary_freeze(folderview->summaryview);
821 folderutils_mark_all_read(item);
823 if (folderview->summaryview->folder_item != item)
824 summary_unlock(folderview->summaryview);
826 summary_thaw(folderview->summaryview);
827 folder_item_update_thaw();
830 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
832 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
834 cm_return_if_fail(node != NULL);
836 if (folderview->open_folder) {
840 folderview->open_folder = TRUE;
841 gtkut_ctree_set_focus_row(ctree, node);
842 gtk_cmctree_select(ctree, node);
843 if ((folderview->summaryview->folder_item &&
844 folderview->summaryview->folder_item->total_msgs > 0) ||
845 prefs_common.layout_mode == SMALL_LAYOUT)
846 summary_grab_focus(folderview->summaryview);
848 gtk_widget_grab_focus(folderview->ctree);
850 gtkut_ctree_expand_parent_all(ctree, node);
853 void folderview_unselect(FolderView *folderview)
855 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
857 (GTK_CMCTREE(folderview->ctree), folderview->opened);
859 folderview->selected = folderview->opened = NULL;
862 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
863 GtkCMCTreeNode *node)
868 node = gtkut_ctree_node_next(ctree, node);
870 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
872 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
873 item = gtk_cmctree_node_get_row_data(ctree, node);
874 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
881 void folderview_select_next_marked(FolderView *folderview)
883 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
884 GtkCMCTreeNode *node = NULL;
885 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
886 gboolean last_open = prefs_common.always_show_msg;
888 prefs_common.summary_select_prio[0] = ACTION_MARKED;
889 prefs_common.always_show_msg = OPENMSG_ALWAYS;
891 if ((node = folderview_find_next_marked(ctree, folderview->opened))
893 folderview_select_node(folderview, node);
897 if (!folderview->opened ||
898 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
901 /* search again from the first node */
902 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
903 folderview_select_node(folderview, node);
906 prefs_common.summary_select_prio[0] = last_summary_select_prio;
907 prefs_common.always_show_msg = last_open;
910 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
911 GtkCMCTreeNode *node)
916 node = gtkut_ctree_node_next(ctree, node);
918 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
920 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
921 item = gtk_cmctree_node_get_row_data(ctree, node);
922 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
929 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
931 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
932 GtkCMCTreeNode *node = NULL;
933 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
934 gboolean last_open = prefs_common.always_show_msg;
936 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
937 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
939 if ((node = folderview_find_next_unread(ctree, folderview->opened))
941 folderview_select_node(folderview, node);
945 if (!folderview->opened ||
946 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
949 /* search again from the first node */
950 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
951 folderview_select_node(folderview, node);
954 prefs_common.summary_select_prio[0] = last_summary_select_prio;
955 prefs_common.always_show_msg = last_open;
958 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
959 GtkCMCTreeNode *node)
964 node = gtkut_ctree_node_next(ctree, node);
966 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
968 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
969 item = gtk_cmctree_node_get_row_data(ctree, node);
970 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
977 void folderview_select_next_new(FolderView *folderview)
979 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
980 GtkCMCTreeNode *node = NULL;
981 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
982 gboolean last_open = prefs_common.always_show_msg;
984 prefs_common.summary_select_prio[0] = ACTION_NEW;
985 prefs_common.always_show_msg = OPENMSG_ALWAYS;
987 if ((node = folderview_find_next_new(ctree, folderview->opened))
989 folderview_select_node(folderview, node);
993 if (!folderview->opened ||
994 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
997 /* search again from the first node */
998 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
999 folderview_select_node(folderview, node);
1002 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1003 prefs_common.always_show_msg = last_open;
1006 FolderItem *folderview_get_selected_item(FolderView *folderview)
1008 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1010 if (!folderview->selected) return NULL;
1011 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1014 static void folderview_set_folders(FolderView *folderview)
1017 list = folder_get_list();
1019 for (; list != NULL; list = list->next) {
1020 folderview_append_folder(folderview, FOLDER(list->data));
1024 static gchar *get_scan_str(FolderItem *item)
1027 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1028 item->folder->name, G_DIR_SEPARATOR,
1031 return g_strdup_printf(_("Scanning folder %s ..."),
1032 item->folder->name);
1034 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1038 for (list = folderview_list; list != NULL; list = list->next) {
1039 FolderView *folderview = (FolderView *)list->data;
1040 MainWindow *mainwin = folderview->mainwin;
1041 gchar *str = get_scan_str(item);
1043 STATUSBAR_PUSH(mainwin, str);
1044 STATUSBAR_POP(mainwin);
1049 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1052 MainWindow *mainwin = mainwindow_get_mainwindow();
1053 FolderView *folderview = NULL;
1054 GtkAdjustment *pos = NULL;
1057 cm_return_if_fail(folder != NULL);
1059 if (!folder->klass->scan_tree) return;
1062 alertpanel_full(_("Rebuild folder tree"),
1063 _("Rebuilding the folder tree will remove "
1064 "local caches. Do you want to continue?"),
1065 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1066 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1067 != G_ALERTALTERNATE) {
1073 window = label_window_create(_("Rebuilding folder tree..."));
1075 window = label_window_create(_("Scanning folder tree..."));
1078 folderview = mainwin->folderview;
1081 pos = gtk_scrolled_window_get_vadjustment(
1082 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1083 height = gtk_adjustment_get_value(pos);
1086 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1087 folder_scan_tree(folder, rebuild);
1088 folder_set_ui_func(folder, NULL, NULL);
1090 folderview_set_all();
1093 pos = gtk_scrolled_window_get_vadjustment(
1094 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1095 gtk_adjustment_set_value(pos, height);
1096 gtk_adjustment_changed(pos);
1098 label_window_destroy(window);
1102 void folderview_fast_rescan_tree(Folder *folder)
1105 MainWindow *mainwin = mainwindow_get_mainwindow();
1106 FolderView *folderview = NULL;
1107 GtkAdjustment *pos = NULL;
1110 cm_return_if_fail(folder != NULL);
1112 if (!folder->klass->scan_tree) return;
1116 window = label_window_create(_("Scanning folder tree..."));
1119 folderview = mainwin->folderview;
1122 pos = gtk_scrolled_window_get_vadjustment(
1123 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1124 height = gtk_adjustment_get_value(pos);
1127 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1128 folder_fast_scan_tree(folder);
1129 folder_set_ui_func(folder, NULL, NULL);
1131 folderview_set_all();
1134 pos = gtk_scrolled_window_get_vadjustment(
1135 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1136 gtk_adjustment_set_value(pos, height);
1137 gtk_adjustment_changed(pos);
1139 label_window_destroy(window);
1143 /** folderview_check_new()
1144 * Scan and update the folder and return the
1145 * count the number of new messages since last check.
1146 * \param folder the folder to check for new messages
1147 * \return the number of new messages since last check
1149 gint folderview_check_new(Folder *folder)
1153 FolderView *folderview;
1155 GtkCMCTreeNode *node;
1157 gint former_new_msgs = 0;
1158 gint former_new = 0, former_unread = 0, former_total;
1160 for (list = folderview_list; list != NULL; list = list->next) {
1161 folderview = (FolderView *)list->data;
1162 ctree = GTK_CMCTREE(folderview->ctree);
1163 folderview->scanning_folder = folder;
1165 main_window_lock(folderview->mainwin);
1167 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1168 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1170 item = gtk_cmctree_node_get_row_data(ctree, node);
1171 if (!item || !item->path || !item->folder) continue;
1172 if (item->no_select) continue;
1173 if (folder && folder != item->folder) continue;
1174 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1175 if (!item->prefs->newmailcheck) continue;
1176 if (item->processing_pending == TRUE) {
1177 debug_print("skipping %s, processing pending\n",
1178 item->path ? item->path : item->name);
1181 if (item->scanning != ITEM_NOT_SCANNING) {
1182 debug_print("skipping %s, scanning\n",
1183 item->path ? item->path : item->name);
1187 str = get_scan_str(item);
1189 STATUSBAR_PUSH(folderview->mainwin, str);
1193 folderview_scan_tree_func(item->folder, item, NULL);
1194 former_new = item->new_msgs;
1195 former_unread = item->unread_msgs;
1196 former_total = item->total_msgs;
1198 if (item->folder->klass->scan_required &&
1199 (item->folder->klass->scan_required(item->folder, item) ||
1200 item->folder->inbox == item ||
1201 item->opened == TRUE ||
1202 item->processing_pending == TRUE)) {
1203 if (folder_item_scan(item) < 0) {
1205 summaryview_unlock(folderview->summaryview, item);
1206 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1207 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1208 item->path ? item->path:item->name);
1209 STATUSBAR_POP(folderview->mainwin);
1211 } else if (!FOLDER_IS_LOCAL(folder)) {
1212 STATUSBAR_POP(folderview->mainwin);
1217 } else if (!item->folder->klass->scan_required) {
1218 if (folder_item_scan(item) < 0) {
1219 summaryview_unlock(folderview->summaryview, item);
1220 if (folder && !FOLDER_IS_LOCAL(folder)) {
1221 STATUSBAR_POP(folderview->mainwin);
1226 if (former_new != item->new_msgs ||
1227 former_unread != item->unread_msgs ||
1228 former_total != item->total_msgs)
1229 folderview_update_node(folderview, node);
1231 new_msgs += item->new_msgs;
1232 former_new_msgs += former_new;
1233 STATUSBAR_POP(folderview->mainwin);
1235 folderview->scanning_folder = NULL;
1236 main_window_unlock(folderview->mainwin);
1240 folder_write_list();
1241 /* Number of new messages since last check is the just the difference
1242 * between former_new_msgs and new_msgs. If new_msgs is less than
1243 * former_new_msgs, that would mean another session accessed the folder
1244 * and the result is not well defined.
1246 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1250 void folderview_check_new_all(void)
1254 FolderView *folderview;
1256 folderview = (FolderView *)folderview_list->data;
1259 main_window_lock(folderview->mainwin);
1260 window = label_window_create
1261 (_("Checking for new messages in all folders..."));
1263 list = folder_get_list();
1264 for (; list != NULL; list = list->next) {
1265 Folder *folder = list->data;
1267 folderview_check_new(folder);
1270 folder_write_list();
1271 folderview_set_all();
1273 label_window_destroy(window);
1274 main_window_unlock(folderview->mainwin);
1278 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1284 if (!item || !item->folder || !item->folder->node)
1287 node = item->folder->node;
1289 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1290 node = node->children;
1293 (item->new_msgs > 0 ||
1294 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1298 while (node != NULL) {
1299 if (node && node->data) {
1300 FolderItem *next_item = (FolderItem*) node->data;
1302 if (folderview_have_new_children_sub(folderview,
1311 static gboolean folderview_have_new_children(FolderView *folderview,
1314 return folderview_have_new_children_sub(folderview, item, FALSE);
1317 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1323 if (!item || !item->folder || !item->folder->node)
1326 node = item->folder->node;
1328 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1329 node = node->children;
1332 (item->unread_msgs > 0 ||
1333 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1337 while (node != NULL) {
1338 if (node && node->data) {
1339 FolderItem *next_item = (FolderItem*) node->data;
1341 if (folderview_have_unread_children_sub(folderview,
1351 static gboolean folderview_have_unread_children(FolderView *folderview,
1354 return folderview_have_unread_children_sub(folderview, item, FALSE);
1357 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1363 if (!item || !item->folder || !item->folder->node)
1366 node = item->folder->node;
1368 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1369 node = node->children;
1371 if (in_sub && item->search_match){
1375 while (node != NULL) {
1376 if (node && node->data) {
1377 FolderItem *next_item = (FolderItem*) node->data;
1379 if (folderview_have_matching_children_sub(folderview,
1389 static gboolean folderview_have_matching_children(FolderView *folderview,
1392 return folderview_have_matching_children_sub(folderview, item, FALSE);
1395 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1401 if (!item || !item->folder || !item->folder->node)
1404 node = item->folder->node;
1406 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1407 node = node->children;
1409 if (item->marked_msgs != 0) {
1413 while (node != NULL) {
1414 if (node && node->data) {
1415 FolderItem *next_item = (FolderItem*) node->data;
1417 if (folderview_have_marked_children_sub(folderview,
1426 static gboolean folderview_have_marked_children(FolderView *folderview,
1429 return folderview_have_marked_children_sub(folderview, item, FALSE);
1432 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1434 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1435 GtkStyle *style = NULL;
1436 GtkStyle *color_style = NULL;
1438 GdkPixbuf *xpm, *openxpm;
1439 static GdkPixbuf *searchicon;
1440 gboolean mark = FALSE;
1443 gboolean add_unread_mark;
1444 gboolean add_sub_match_mark;
1445 gboolean use_bold, use_color;
1446 gint *col_pos = folderview->col_pos;
1447 SpecialFolderItemType stype;
1449 item = gtk_cmctree_node_get_row_data(ctree, node);
1450 cm_return_if_fail(item != NULL);
1452 if (!GTK_CMCTREE_ROW(node)->expanded)
1453 mark = folderview_have_marked_children(folderview, item);
1455 mark = (item->marked_msgs != 0);
1457 stype = item->stype;
1458 if (stype == F_NORMAL) {
1459 if (folder_has_parent_of_type(item, F_TRASH))
1461 else if (folder_has_parent_of_type(item, F_DRAFT))
1463 else if (folder_has_parent_of_type(item, F_OUTBOX))
1465 else if (folder_has_parent_of_type(item, F_QUEUE))
1470 if (item->hide_read_msgs) {
1471 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1472 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1474 xpm = mark?m_inboxxpm:inboxxpm;
1475 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1479 if (item->hide_read_msgs) {
1480 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1481 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1483 xpm = mark?m_outboxxpm:outboxxpm;
1484 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1488 if (item->hide_read_msgs) {
1489 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1490 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1492 xpm = mark?m_queuexpm:queuexpm;
1493 openxpm = mark?m_queueopenxpm:queueopenxpm;
1497 if (item->hide_read_msgs) {
1498 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1499 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1501 xpm = mark?m_trashxpm:trashxpm;
1502 openxpm = mark?m_trashopenxpm:trashopenxpm;
1506 xpm = mark?m_draftsxpm:draftsxpm;
1507 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1510 if (item->hide_read_msgs) {
1511 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1512 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1514 xpm = mark?m_folderxpm:folderxpm;
1515 openxpm = mark?m_folderopenxpm:folderopenxpm;
1519 if (item->no_select) {
1520 xpm = openxpm = noselectxpm;
1523 name = folder_item_get_name(item);
1525 if (!GTK_CMCTREE_ROW(node)->expanded) {
1526 add_unread_mark = folderview_have_unread_children(
1528 add_sub_match_mark = folderview_have_matching_children(
1531 add_unread_mark = FALSE;
1532 add_sub_match_mark = FALSE;
1535 if (item->search_match) {
1537 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1540 xpm = openxpm = searchicon;
1544 if (prefs_common.display_folder_unread) {
1545 if (folder_has_parent_of_type(item, F_QUEUE)) {
1546 /* only total_msgs matters here */
1547 if (item->total_msgs > 0) {
1548 /* show total number (should be equal to the unread number)
1550 str = g_strdup_printf("%s (%d%s%s)",
1551 name, item->total_msgs,
1552 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1553 (item->unreadmarked_msgs > 0) ? "!" : "");
1556 if (prefs_common.display_folder_unread == 1) {
1557 if (item->unread_msgs > 0) {
1558 /* show unread number and signs */
1559 str = g_strdup_printf("%s (%d%s%s)",
1560 name, item->unread_msgs,
1561 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1562 (item->unreadmarked_msgs > 0) ? "!" : "");
1565 if (item->total_msgs > 0) {
1566 /* show unread number, total number and signs if any */
1567 str = g_strdup_printf("%s (%d/%d%s%s)",
1568 name, item->unread_msgs, item->total_msgs,
1569 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1570 (item->unreadmarked_msgs > 0) ? "!" : "");
1574 if ((str == NULL) &&
1575 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1576 /* no unread/total numbers, but at least one sign */
1577 str = g_strdup_printf("%s (%s%s)",
1579 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1580 (item->unreadmarked_msgs > 0) ? "!" : "");
1584 /* last fallback, folder name only or with +! sign */
1585 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1586 str = g_strdup_printf("%s%s",
1588 } else if (item->unreadmarked_msgs > 0) {
1589 str = g_strdup_printf("%s%s",
1591 } else if (add_sub_match_mark) {
1592 str = g_strdup_printf("%s%s",
1595 str = g_strdup_printf("%s", name);
1598 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1600 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1604 if (!folder_item_parent(item)) {
1605 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1606 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1607 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1609 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1610 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1611 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1614 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1615 folder_has_parent_of_type(item, F_DRAFT) ||
1616 folder_has_parent_of_type(item, F_TRASH)) {
1617 use_bold = use_color = FALSE;
1618 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1619 GSList *list = folder_item_get_msg_list(item);
1621 use_bold = use_color = FALSE;
1622 for (cur = list; cur; cur = cur->next) {
1623 MsgInfo *msginfo = (MsgInfo *)cur->data;
1624 if (!MSG_IS_DELETED(msginfo->flags)) {
1625 /* highlight queue folder if there are any messages */
1626 use_bold = use_color = TRUE;
1631 /* if unread messages exist, print with bold font */
1632 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1634 /* if new messages exist, print with colored letter */
1636 (item->new_msgs > 0) ||
1638 folderview_have_new_children(folderview, item));
1641 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1646 if (item->prefs->color > 0 && !use_color) {
1647 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1648 color_style = gtk_style_copy(bold_style);
1649 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1650 style = color_style;
1651 } else if (use_color) {
1652 style = bold_color_style;
1655 if (item->op_count > 0) {
1656 style = bold_tgtfold_style;
1658 } else if (use_color) {
1659 style = normal_color_style;
1660 gtk_cmctree_node_set_foreground(ctree, node,
1661 &folderview->color_new);
1662 } else if (item->op_count > 0) {
1663 style = bold_tgtfold_style;
1664 } else if (item->prefs->color > 0) {
1666 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1667 color_style = gtk_style_copy(normal_style);
1668 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1669 style = color_style;
1671 style = normal_style;
1674 gtk_cmctree_node_set_row_style(ctree, node, style);
1676 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1677 folderview_update_node(folderview, node);
1680 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1683 FolderView *folderview;
1685 GtkCMCTreeNode *node;
1687 cm_return_if_fail(item != NULL);
1689 for (list = folderview_list; list != NULL; list = list->next) {
1690 folderview = (FolderView *)list->data;
1691 ctree = GTK_CMCTREE(folderview->ctree);
1693 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1695 item->search_match = matches;
1696 folderview_update_node(folderview, node);
1701 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1703 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1704 FolderView *folderview = (FolderView *)data;
1706 GtkCMCTreeNode *node;
1707 cm_return_val_if_fail(update_info != NULL, TRUE);
1708 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1709 cm_return_val_if_fail(folderview != NULL, FALSE);
1711 ctree = GTK_CMCTREE(folderview->ctree);
1713 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1716 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1717 folderview_update_node(folderview, node);
1719 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1720 update_info->item == folderview->summaryview->folder_item &&
1721 update_info->item != NULL)
1722 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1723 summary_show(folderview->summaryview, update_info->item);
1729 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1730 GNode *gnode, GtkCMCTreeNode *cnode,
1733 FolderView *folderview = (FolderView *)data;
1734 FolderItem *item = FOLDER_ITEM(gnode->data);
1736 cm_return_val_if_fail(item != NULL, FALSE);
1738 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1739 folderview_update_node(folderview, cnode);
1744 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1747 FolderView *folderview = (FolderView *)data;
1750 if (GTK_CMCTREE_ROW(node)->children) {
1751 item = gtk_cmctree_node_get_row_data(ctree, node);
1752 cm_return_if_fail(item != NULL);
1754 if (!item->collapsed)
1755 gtk_cmctree_expand(ctree, node);
1757 folderview_update_node(folderview, node);
1761 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1762 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1765 GtkCMCTreeNode *node, *parent, *sibling;
1767 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1769 g_warning("%s not found.\n", item->path);
1771 parent = GTK_CMCTREE_ROW(node)->parent;
1772 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1773 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1775 sibling = GTK_CMCTREE_ROW(parent)->children;
1779 tmp = gtk_cmctree_node_get_row_data
1781 if (tmp->stype != F_NORMAL)
1782 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1786 if (node != sibling)
1787 gtk_cmctree_move(ctree, node, parent, sibling);
1794 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1797 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1798 GtkCMCTreeNode *prev = NULL;
1800 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1801 gtk_sctree_sort_recursive(ctree, root);
1802 if (root && GTK_CMCTREE_ROW(root)->parent) {
1803 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1806 set_special_folder(ctree, folder->inbox, root, &prev);
1807 set_special_folder(ctree, folder->outbox, root, &prev);
1808 set_special_folder(ctree, folder->draft, root, &prev);
1809 set_special_folder(ctree, folder->queue, root, &prev);
1810 set_special_folder(ctree, folder->trash, root, &prev);
1811 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1814 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1816 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1817 GtkCMCTreeNode *root;
1819 cm_return_if_fail(folder != NULL);
1821 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1822 folderview_gnode_func, folderview);
1823 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1825 folderview_sort_folders(folderview, root, folder);
1828 /* callback functions */
1829 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1830 GdkEventButton *event)
1834 FolderViewPopup *fpopup;
1835 GtkActionGroup *action_group;
1837 FolderItem *special_trash = NULL, *special_queue = NULL;
1839 GtkUIManager *ui_manager = gtk_ui_manager_new();
1841 if (folderview->ui_manager)
1842 g_object_unref(folderview->ui_manager);
1844 folderview->ui_manager = ui_manager;
1845 item = folderview_get_selected_item(folderview);
1847 cm_return_if_fail(item != NULL);
1848 cm_return_if_fail(item->folder != NULL);
1849 folder = item->folder;
1851 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1854 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1856 fpopup = g_hash_table_lookup(folderview_popups, "common");
1857 action_group = g_hash_table_lookup(folderview->popups, "common");
1860 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1864 if (fpopup->add_menuitems)
1865 fpopup->add_menuitems(ui_manager, item);
1867 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1868 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1869 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1870 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1871 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1874 if (fpopup->set_sensitivity != NULL)
1875 fpopup->set_sensitivity(ui_manager, item);
1877 if (NULL != (ac = account_find_from_item(item))) {
1878 special_trash = account_get_special_folder(ac, F_TRASH);
1879 special_queue = account_get_special_folder(ac, F_QUEUE);
1882 if ((item == folder->trash || item == special_trash
1883 || folder_has_parent_of_type(item, F_TRASH))) {
1884 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1885 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1888 if ((item == folder->queue || item == special_queue
1889 || folder_has_parent_of_type(item, F_QUEUE))) {
1890 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1891 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1894 #define SET_SENS(name, sens) \
1895 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1897 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1898 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1899 folderview->selected == folderview->opened);
1900 SET_SENS("FolderViewPopup/Properties", TRUE);
1902 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1903 item->total_msgs >= 1 && !item->processing_pending);
1904 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1905 !item->no_select && !item->processing_pending);
1907 if (item == folder->trash || item == special_trash
1908 || folder_has_parent_of_type(item, F_TRASH)) {
1909 GSList *msglist = folder_item_get_msg_list(item);
1910 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1911 procmsg_msg_list_free(msglist);
1913 if (item == folder->queue || item == special_queue
1914 || folder_has_parent_of_type(item, F_QUEUE)) {
1915 GSList *msglist = folder_item_get_msg_list(item);
1916 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1917 procmsg_msg_list_free(msglist);
1921 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1922 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1923 g_signal_connect(G_OBJECT(popup), "selection_done",
1924 G_CALLBACK(folderview_popup_close),
1926 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1927 event->button, event->time);
1930 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1931 FolderView *folderview)
1933 GtkCMCList *clist = GTK_CMCLIST(ctree);
1934 gint prev_row = -1, row = -1, column = -1;
1936 if (!event) return FALSE;
1938 if (event->button == 1 || event->button == 2) {
1939 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1940 folderview->open_folder = TRUE;
1942 if (event->type == GDK_2BUTTON_PRESS) {
1943 if (clist->selection) {
1944 GtkCMCTreeNode *node;
1946 node = GTK_CMCTREE_NODE(clist->selection->data);
1948 gtk_cmctree_toggle_expansion(
1951 folderview->open_folder = FALSE;
1958 if (event->button == 2 || event->button == 3) {
1960 if (clist->selection) {
1961 GtkCMCTreeNode *node;
1963 node = GTK_CMCTREE_NODE(clist->selection->data);
1965 prev_row = gtkut_ctree_get_nth_from_node
1966 (GTK_CMCTREE(ctree), node);
1969 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1972 if (prev_row != row) {
1973 gtk_cmclist_unselect_all(clist);
1974 if (event->button == 2)
1975 folderview_select_node
1977 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1980 gtk_cmclist_select_row(clist, row, column);
1984 if (event->button != 3) return FALSE;
1986 folderview_set_sens_and_popup_menu(folderview, row, event);
1990 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1991 FolderView *folderview)
1993 int row = -1, column = -1;
1995 if (!event) return FALSE;
1997 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2000 if (event->button == 1 && folderview->open_folder == FALSE &&
2001 folderview->opened != NULL) {
2002 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2003 folderview->opened);
2004 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2010 #define BREAK_ON_MODIFIER_KEY() \
2011 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2013 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2014 FolderView *folderview)
2016 if (!event) return FALSE;
2018 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2021 switch (event->keyval) {
2023 #ifndef GENERIC_UMPC
2024 case GDK_KEY_Return:
2025 case GDK_KEY_KP_Enter:
2027 if (folderview->selected) {
2028 folderview_select_node(folderview,
2029 folderview->selected);
2033 case GDK_KEY_Return:
2034 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2035 gtk_cmctree_toggle_expansion(
2036 GTK_CMCTREE(folderview->ctree),
2037 folderview->selected);
2042 BREAK_ON_MODIFIER_KEY();
2043 if (folderview->selected) {
2044 if (folderview->opened == folderview->selected &&
2045 (!folderview->summaryview->folder_item ||
2046 folderview->summaryview->folder_item->total_msgs == 0))
2047 folderview_select_next_unread(folderview, TRUE);
2049 folderview_select_node(folderview,
2050 folderview->selected);
2060 typedef struct _PostponedSelectData
2063 GtkCMCTreeNode *row;
2065 FolderView *folderview;
2066 } PostponedSelectData;
2068 static gboolean postpone_select(void *data)
2070 PostponedSelectData *psdata = (PostponedSelectData *)data;
2071 debug_print("trying again\n");
2072 psdata->folderview->open_folder = TRUE;
2073 main_window_cursor_normal(psdata->folderview->mainwin);
2074 STATUSBAR_POP(psdata->folderview->mainwin);
2075 folderview_selected(psdata->ctree, psdata->row,
2076 psdata->column, psdata->folderview);
2081 void folderview_close_opened(FolderView *folderview)
2083 if (folderview->opened) {
2084 FolderItem *olditem;
2086 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2087 folderview->opened);
2089 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2090 olditem->path ? olditem->path:olditem->name);
2091 /* will be null if we just moved the previously opened folder */
2092 STATUSBAR_PUSH(folderview->mainwin, buf);
2093 main_window_cursor_wait(folderview->mainwin);
2095 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2096 summary_show(folderview->summaryview, NULL);
2097 folder_item_close(olditem);
2098 main_window_cursor_normal(folderview->mainwin);
2099 STATUSBAR_POP(folderview->mainwin);
2100 if (olditem->folder->klass->item_closed)
2101 olditem->folder->klass->item_closed(olditem);
2106 if (folderview->opened &&
2107 !GTK_CMCTREE_ROW(folderview->opened)->children)
2108 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2110 folderview->opened = NULL;
2112 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2113 gint column, FolderView *folderview)
2115 static gboolean can_select = TRUE; /* exclusive lock */
2120 GtkCMCTreeNode *old_opened = folderview->opened;
2122 folderview->selected = row;
2124 debug_print("newly selected %p, opened %p\n", folderview->selected,
2125 folderview->opened);
2126 if (folderview->opened == row) {
2127 folderview->open_folder = FALSE;
2132 item = gtk_cmctree_node_get_row_data(ctree, row);
2135 folderview->open_folder = FALSE;
2139 if (!can_select || summary_is_locked(folderview->summaryview)) {
2140 if (folderview->opened) {
2141 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2142 gtk_cmctree_select(ctree, folderview->opened);
2144 folderview->open_folder = FALSE;
2149 if (!folderview->open_folder) {
2156 /* Save cache for old folder */
2157 /* We don't want to lose all caches if sylpheed crashed */
2158 /* resets folderview->opened to NULL */
2159 folderview_close_opened(folderview);
2161 /* CLAWS: set compose button type: news folder items
2162 * always have a news folder as parent */
2164 toolbar_set_compose_button
2165 (folderview->mainwin->toolbar,
2166 FOLDER_TYPE(item->folder) == F_NEWS ?
2167 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2170 debug_print("Folder %s is selected\n", item->path);
2172 if (!GTK_CMCTREE_ROW(row)->children)
2173 gtk_cmctree_expand(ctree, row);
2175 /* ungrab the mouse event */
2176 if (gtkut_widget_has_grab(GTK_WIDGET(ctree))) {
2177 gtk_grab_remove(GTK_WIDGET(ctree));
2178 if (gdk_pointer_is_grabbed())
2179 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2183 /* TODO: wwp: avoid displaying (null) in the status bar */
2184 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2185 item->path : "(null)");
2186 debug_print("%s\n", buf);
2187 STATUSBAR_PUSH(folderview->mainwin, buf);
2190 main_window_cursor_wait(folderview->mainwin);
2192 if (folderview->scanning_folder == item->folder) {
2195 res = folder_item_open(item);
2198 if (res == -1 && item->no_select == FALSE) {
2199 main_window_cursor_normal(folderview->mainwin);
2200 STATUSBAR_POP(folderview->mainwin);
2202 alertpanel_error(_("Folder could not be opened."));
2204 folderview->open_folder = FALSE;
2208 } else if (res == -2 && item->no_select == FALSE) {
2209 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2210 data->ctree = ctree;
2212 data->column = column;
2213 data->folderview = folderview;
2214 debug_print("postponing open of %s till end of scan\n",
2215 item->path ? item->path:item->name);
2216 folderview->open_folder = FALSE;
2218 g_timeout_add(500, postpone_select, data);
2223 main_window_cursor_normal(folderview->mainwin);
2226 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2227 opened = summary_show(folderview->summaryview, item);
2229 folder_clean_cache_memory(item);
2232 gtkut_ctree_set_focus_row(ctree, old_opened);
2233 gtk_cmctree_select(ctree, old_opened);
2234 folderview->opened = old_opened;
2236 folderview->opened = row;
2237 if (gtk_cmctree_node_is_visible(ctree, row)
2238 != GTK_VISIBILITY_FULL)
2239 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2242 STATUSBAR_POP(folderview->mainwin);
2244 folderview->open_folder = FALSE;
2249 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2250 FolderView *folderview)
2254 item = gtk_cmctree_node_get_row_data(ctree, node);
2255 cm_return_if_fail(item != NULL);
2256 item->collapsed = FALSE;
2257 folderview_update_node(folderview, node);
2260 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2261 FolderView *folderview)
2265 item = gtk_cmctree_node_get_row_data(ctree, node);
2266 cm_return_if_fail(item != NULL);
2267 item->collapsed = TRUE;
2268 folderview_update_node(folderview, node);
2271 static void folderview_popup_close(GtkMenuShell *menu_shell,
2272 FolderView *folderview)
2274 if (!folderview->opened) return;
2276 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2279 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2280 FolderView *folderview)
2282 FolderColumnType type = folderview->col_state[column].type;
2284 prefs_common.folder_col_size[type] = width;
2287 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2289 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2290 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2291 GtkCMCTreeNode *node, *parent_node;
2292 gint *col_pos = folderview->col_pos;
2293 FolderItemUpdateData hookdata;
2295 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2296 if (parent_node == NULL)
2299 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2301 text[col_pos[F_COL_FOLDER]] = item->name;
2302 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2307 gtk_cmctree_expand(ctree, parent_node);
2308 gtk_cmctree_node_set_row_data(ctree, node, item);
2310 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2311 folderview_sort_folders(folderview, parent_node, item->folder);
2313 hookdata.item = item;
2314 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2315 hookdata.msg = NULL;
2316 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2318 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2321 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2323 FolderView *folderview = (FolderView *)data;
2324 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2326 GSList *mlist = NULL;
2328 FolderItem *special_trash = NULL;
2331 if (!folderview->selected) return;
2332 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2333 cm_return_if_fail(item != NULL);
2334 cm_return_if_fail(item->folder != NULL);
2336 if (NULL != (ac = account_find_from_item(item)))
2337 special_trash = account_get_special_folder(ac, F_TRASH);
2339 if (item != item->folder->trash && item != special_trash
2340 && !folder_has_parent_of_type(item, F_TRASH)) return;
2342 if (prefs_common.ask_on_clean) {
2343 if (alertpanel(_("Empty trash"),
2344 _("Delete all messages in trash?"),
2345 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2349 mlist = folder_item_get_msg_list(item);
2351 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2352 MsgInfo * msginfo = (MsgInfo *) cur->data;
2353 if (MSG_IS_LOCKED(msginfo->flags))
2355 /* is it partially received? (partial_recv isn't cached) */
2356 if (msginfo->total_size != 0 &&
2357 msginfo->size != (off_t)msginfo->total_size)
2358 partial_mark_for_delete(msginfo);
2360 procmsg_msg_list_free(mlist);
2362 folder_item_remove_all_msg(item);
2365 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2367 FolderView *folderview = (FolderView *)data;
2368 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2370 FolderItem *special_queue = NULL;
2372 gchar *errstr = NULL;
2374 if (!folderview->selected) return;
2375 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2376 cm_return_if_fail(item != NULL);
2377 cm_return_if_fail(item->folder != NULL);
2379 if (NULL != (ac = account_find_from_item(item)))
2380 special_queue = account_get_special_folder(ac, F_QUEUE);
2382 if (item != item->folder->queue && item != special_queue
2383 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2385 if (procmsg_queue_is_empty(item))
2388 if (prefs_common.work_offline)
2389 if (alertpanel(_("Offline warning"),
2390 _("You're working offline. Override?"),
2391 GTK_STOCK_NO, GTK_STOCK_YES,
2392 NULL) != G_ALERTALTERNATE)
2395 /* ask for confirmation before sending queued messages only
2396 in online mode and if there is at least one message queued
2397 in any of the folder queue
2399 if (prefs_common.confirm_send_queued_messages) {
2400 if (!prefs_common.work_offline) {
2401 if (alertpanel(_("Send queued messages"),
2402 _("Send all queued messages?"),
2403 GTK_STOCK_CANCEL, _("_Send"),
2404 NULL) != G_ALERTALTERNATE)
2409 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2411 alertpanel_error_log(_("Some errors occurred while "
2412 "sending queued messages."));
2414 alertpanel_error_log(_("Some errors occurred "
2415 "while sending queued messages:\n%s"), errstr);
2421 static void folderview_search_cb(GtkAction *action, gpointer data)
2423 FolderView *folderview = (FolderView *)data;
2424 summary_search(folderview->summaryview);
2427 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2429 FolderView *folderview = (FolderView *)data;
2430 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2433 if (!folderview->selected) return;
2435 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2436 cm_return_if_fail(item != NULL);
2437 cm_return_if_fail(item->folder != NULL);
2439 item->processing_pending = TRUE;
2440 folder_item_apply_processing(item);
2441 item->processing_pending = FALSE;
2444 static void folderview_property_cb(GtkAction *action, gpointer data)
2446 FolderView *folderview = (FolderView *)data;
2447 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2450 if (!folderview->selected) return;
2452 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2453 cm_return_if_fail(item != NULL);
2454 cm_return_if_fail(item->folder != NULL);
2456 prefs_folder_item_open(item);
2459 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2461 GSList *list = NULL;
2462 GSList *done = NULL;
2463 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2465 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2466 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2467 && list->data != node) {
2468 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2469 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2472 for (list = done; list != NULL; list = g_slist_next(list)) {
2473 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2479 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2480 FolderItem *to_folder, gboolean copy)
2482 FolderItem *from_parent = NULL;
2483 FolderItem *new_folder = NULL;
2484 GtkCMCTreeNode *src_node = NULL;
2488 cm_return_if_fail(folderview != NULL);
2489 cm_return_if_fail(from_folder != NULL);
2490 cm_return_if_fail(to_folder != NULL);
2492 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2493 from_parent = folder_item_parent(from_folder);
2495 if (prefs_common.warn_dnd) {
2496 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2497 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2498 from_folder->name, to_folder->name);
2499 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2500 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2501 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2504 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2506 else if (status & G_ALERTDISABLE)
2507 prefs_common.warn_dnd = FALSE;
2510 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2511 from_folder->name, to_folder->name);
2512 STATUSBAR_PUSH(folderview->mainwin, buf);
2514 summary_clear_all(folderview->summaryview);
2515 folderview->opened = NULL;
2516 folderview->selected = NULL;
2517 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2519 main_window_cursor_wait(folderview->mainwin);
2521 statusbar_verbosity_set(FALSE);
2522 folder_item_update_freeze();
2523 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2524 statusbar_verbosity_set(FALSE);
2525 main_window_cursor_normal(folderview->mainwin);
2526 STATUSBAR_POP(folderview->mainwin);
2527 folder_item_update_thaw();
2528 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2530 folderview_sort_folders(folderview,
2531 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2532 NULL, to_folder), new_folder->folder);
2533 folderview_select(folderview, new_folder);
2535 statusbar_verbosity_set(FALSE);
2536 main_window_cursor_normal(folderview->mainwin);
2537 STATUSBAR_POP(folderview->mainwin);
2538 folder_item_update_thaw();
2540 case F_MOVE_FAILED_DEST_IS_PARENT:
2541 alertpanel_error(_("Source and destination are the same."));
2543 case F_MOVE_FAILED_DEST_IS_CHILD:
2544 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2545 _("Can't move a folder to one of its children."));
2547 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2548 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2551 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2556 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2559 static gint folderview_clist_compare(GtkCMCList *clist,
2560 gconstpointer ptr1, gconstpointer ptr2)
2562 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2563 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2565 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2567 return item1->order - item2->order;
2570 // if only one folder has an order it comes first
2571 if (item1->order > 0)
2575 if (item2->order > 0)
2581 return (item2->name != NULL);
2585 return g_utf8_collate(item1->name, item2->name);
2588 static void folderview_processing_cb(GtkAction *action, gpointer data)
2590 FolderView *folderview = (FolderView *)data;
2591 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2595 if (!folderview->selected) return;
2597 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2598 cm_return_if_fail(item != NULL);
2599 cm_return_if_fail(item->folder != NULL);
2601 id = folder_item_get_identifier(item);
2602 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2605 prefs_filtering_open(&item->prefs->processing, title,
2606 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2610 void folderview_set_target_folder_color(gint color_op)
2614 FolderView *folderview;
2616 for (list = folderview_list; list != NULL; list = list->next) {
2617 folderview = (FolderView *)list->data;
2618 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2620 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2621 folderview->color_op;
2627 static gchar *last_smallfont = NULL;
2628 static gchar *last_normalfont = NULL;
2629 static gchar *last_boldfont = NULL;
2630 static gboolean last_derive = 0;
2632 void folderview_reinit_fonts(FolderView *folderview)
2635 g_free(last_smallfont);
2636 last_smallfont = NULL;
2637 g_free(last_normalfont);
2638 last_normalfont = NULL;
2639 g_free(last_boldfont);
2640 last_boldfont = NULL;
2643 void folderview_reflect_prefs(void)
2645 gboolean update_font = FALSE;
2646 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2647 FolderItem *item = folderview_get_selected_item(folderview);
2648 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2649 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2650 gint height = gtk_adjustment_get_value(pos);
2652 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2653 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2654 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2655 last_derive != prefs_common.derive_from_normal_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);
2664 last_derive = prefs_common.derive_from_normal_font;
2667 normal_style = normal_color_style = bold_style =
2668 bold_color_style = bold_tgtfold_style = NULL;
2670 folderview_init(folderview);
2672 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2673 folderview_column_set_titles(folderview);
2674 folderview_set_all();
2676 g_signal_handlers_block_by_func
2677 (G_OBJECT(folderview->ctree),
2678 G_CALLBACK(folderview_selected), folderview);
2681 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2682 GTK_CMCTREE(folderview->ctree), NULL, item);
2684 folderview_select(folderview, item);
2685 folderview->open_folder = FALSE;
2686 folderview->selected = node;
2689 g_signal_handlers_unblock_by_func
2690 (G_OBJECT(folderview->ctree),
2691 G_CALLBACK(folderview_selected), folderview);
2693 pos = gtk_scrolled_window_get_vadjustment(
2694 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2695 gtk_adjustment_set_value(pos, height);
2696 gtk_adjustment_changed(pos);
2697 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2700 static void drag_state_stop(FolderView *folderview)
2702 if (folderview->drag_timer)
2703 g_source_remove(folderview->drag_timer);
2704 folderview->drag_timer = 0;
2705 folderview->drag_node = NULL;
2708 static gboolean folderview_defer_expand(FolderView *folderview)
2710 if (folderview->drag_node) {
2711 folderview_recollapse_nodes(folderview, folderview->drag_node);
2712 if (folderview->drag_item->collapsed) {
2713 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2714 folderview->nodes_to_recollapse = g_slist_append
2715 (folderview->nodes_to_recollapse, folderview->drag_node);
2718 folderview->drag_item = NULL;
2719 folderview->drag_timer = 0;
2723 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2725 /* the idea is that we call drag_state_start() whenever we want expansion to
2726 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2727 * we need to call drag_state_stop() */
2728 drag_state_stop(folderview);
2729 /* request expansion */
2730 if (0 != (folderview->drag_timer = g_timeout_add
2731 (prefs_common.hover_timeout,
2732 (GSourceFunc)folderview_defer_expand,
2734 folderview->drag_node = node;
2735 folderview->drag_item = item;
2738 #ifndef GENERIC_UMPC
2739 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2740 FolderView *folderview)
2742 GdkDragContext *context;
2744 cm_return_if_fail(folderview != NULL);
2745 if (folderview->selected == NULL) return;
2746 if (folderview->nodes_to_recollapse)
2747 g_slist_free(folderview->nodes_to_recollapse);
2748 folderview->nodes_to_recollapse = NULL;
2749 context = gtk_drag_begin(widget, folderview->target_list,
2750 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2751 gtk_drag_set_icon_default(context);
2754 static void folderview_drag_data_get(GtkWidget *widget,
2755 GdkDragContext *drag_context,
2756 GtkSelectionData *selection_data,
2759 FolderView *folderview)
2763 gchar *source = NULL;
2764 if (info == TARGET_DUMMY) {
2765 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2766 cur != NULL; cur = cur->next) {
2767 item = gtk_cmctree_node_get_row_data
2768 (GTK_CMCTREE(folderview->ctree),
2769 GTK_CMCTREE_NODE(cur->data));
2771 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2772 gtk_selection_data_set(selection_data,
2773 gtk_selection_data_get_target(selection_data), 8,
2774 source, strlen(source));
2780 g_warning("unknown info %d\n", info);
2784 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2786 FolderUpdateData *hookdata;
2787 FolderView *folderview;
2791 folderview = (FolderView *) userdata;
2792 cm_return_val_if_fail(hookdata != NULL, FALSE);
2793 cm_return_val_if_fail(folderview != NULL, FALSE);
2795 ctree = folderview->ctree;
2796 cm_return_val_if_fail(ctree != NULL, FALSE);
2798 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2799 folderview_create_folder_node(folderview, hookdata->item);
2800 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2801 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2802 NULL, folder_item_parent(hookdata->item));
2803 folderview_sort_folders(folderview, node, hookdata->folder);
2804 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2805 GtkCMCTreeNode *node;
2807 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2809 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2810 if (folderview->selected == node)
2811 folderview->selected = NULL;
2812 if (folderview->opened == node)
2813 folderview->opened = NULL;
2815 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2816 folderview_set(folderview);
2821 static gboolean folderview_dnd_scroll_cb(gpointer data)
2823 FolderView *folderview = (FolderView *)data;
2824 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2825 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2826 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2827 gint max = (int)gtk_adjustment_get_upper(pos) -
2828 (int)gtk_adjustment_get_page_size(pos);
2830 if (folderview->scroll_value == 0) {
2831 folderview->scroll_timeout_id = -1;
2835 if (folderview->scroll_value > 0 && new_val > max) {
2837 } else if (folderview->scroll_value < 0 && new_val < 0) {
2840 gtk_adjustment_set_value(pos, new_val);
2845 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2846 GdkDragContext *context,
2850 FolderView *folderview)
2853 FolderItem *item = NULL, *src_item = NULL;
2854 GtkCMCTreeNode *node = NULL;
2855 gboolean acceptable = FALSE;
2856 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2857 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2858 int height = (int)gtk_adjustment_get_page_size(pos);
2859 int total_height = (int)gtk_adjustment_get_upper(pos);
2860 int vpos = (int)gtk_adjustment_get_value(pos);
2861 int offset = prefs_common.show_col_headers ? 24:0;
2864 if (gtk_cmclist_get_selection_info
2865 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2866 GtkWidget *srcwidget;
2868 if (y > height - (48 - offset) && height + vpos < total_height) {
2869 dist = -(height - (48 - offset) - y);
2870 folderview->scroll_value = 1.41f * (1+(dist / 6));
2871 } else if (y < 72 - (24 - offset) && y >= 0) {
2872 dist = 72 - (24 - offset) - y;
2873 folderview->scroll_value = -1.41f * (1+(dist / 6));
2875 folderview->scroll_value = 0;
2877 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == -1) {
2878 folderview->scroll_timeout_id =
2879 g_timeout_add(30, folderview_dnd_scroll_cb,
2883 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2884 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2885 src_item = folderview->summaryview->folder_item;
2887 srcwidget = gtk_drag_get_source_widget(context);
2888 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2889 /* comes from summaryview */
2890 /* we are copying messages, so only accept folder items that are not
2891 the source item, are no root items and can copy messages */
2892 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2893 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2894 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2896 } else if (srcwidget == folderview->ctree) {
2897 /* comes from folderview */
2898 /* we are moving folder items, only accept folders that are not
2899 the source items and can copy messages and create folder items */
2900 if (item && item->folder && src_item && src_item != item &&
2901 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2902 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2903 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2904 || item->folder == src_item->folder))
2907 /* comes from another app */
2908 /* we are adding messages, so only accept folder items that are
2909 no root items and can copy messages */
2910 if (item && item->folder && folder_item_parent(item) != NULL
2911 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2912 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2917 if (acceptable || (src_item && src_item == item))
2918 drag_state_start(folderview, node, item);
2921 g_signal_handlers_block_by_func
2923 G_CALLBACK(folderview_selected), folderview);
2924 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2925 g_signal_handlers_unblock_by_func
2927 G_CALLBACK(folderview_selected), folderview);
2928 gdk_drag_status(context,
2929 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2930 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2932 if (folderview->opened)
2933 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2934 gdk_drag_status(context, 0, time);
2940 static void folderview_drag_leave_cb(GtkWidget *widget,
2941 GdkDragContext *context,
2943 FolderView *folderview)
2945 drag_state_stop(folderview);
2946 folderview->scroll_value = 0;
2947 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2950 static void free_info (gpointer stuff, gpointer data)
2955 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2956 guint time, FolderItem *item)
2959 GSList *msglist = NULL;
2960 list = uri_list_extract_filenames(data);
2961 if (!(item && item->folder && folder_item_parent(item) != NULL
2962 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2964 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2965 debug_print("item doesn't fit\n");
2969 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2970 debug_print("list is empty\n");
2973 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2974 MsgFileInfo *info = NULL;
2976 if (file_is_email((gchar *)tmp->data)) {
2977 info = g_new0(MsgFileInfo, 1);
2978 info->msginfo = NULL;
2979 info->file = (gchar *)tmp->data;
2980 msglist = g_slist_prepend(msglist, info);
2981 debug_print("file is a mail\n");
2983 debug_print("file isn't a mail\n");
2987 msglist = g_slist_reverse(msglist);
2988 folder_item_add_msgs(item, msglist, FALSE);
2989 g_slist_foreach(msglist, free_info, NULL);
2990 g_slist_free(msglist);
2991 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2993 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2995 list_free_strings(list);
2999 static void folderview_drag_received_cb(GtkWidget *widget,
3000 GdkDragContext *drag_context,
3003 GtkSelectionData *data,
3006 FolderView *folderview)
3009 FolderItem *item = NULL, *src_item;
3010 GtkCMCTreeNode *node;
3011 int offset = prefs_common.show_col_headers ? 24:0;
3013 folderview->scroll_value = 0;
3015 if (info == TARGET_DUMMY) {
3016 drag_state_stop(folderview);
3017 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3018 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3019 /* comes from summaryview */
3020 if (gtk_cmclist_get_selection_info
3021 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3024 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3025 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3026 src_item = folderview->summaryview->folder_item;
3028 if (item->no_select) {
3029 alertpanel_error(_("The destination folder can only be used to "
3030 "store subfolders."));
3033 /* re-check (due to acceptable possibly set for folder moves */
3034 if (!(item && item->folder && item->path && !item->no_select &&
3035 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3038 if (item && src_item) {
3039 switch (gdk_drag_context_get_selected_action(drag_context)) {
3040 case GDK_ACTION_COPY:
3041 summary_copy_selected_to(folderview->summaryview, item);
3042 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3044 case GDK_ACTION_MOVE:
3045 case GDK_ACTION_DEFAULT:
3047 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3048 summary_copy_selected_to(folderview->summaryview, item);
3050 summary_move_selected_to(folderview->summaryview, item);
3051 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3054 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3056 /* comes from folderview */
3058 gboolean folder_is_normal = TRUE;
3059 gboolean copy = (GDK_ACTION_COPY ==
3060 gdk_drag_context_get_selected_action(drag_context));
3062 source = (char *)gtk_selection_data_get_data(data) + 17;
3063 if (gtk_cmclist_get_selection_info
3064 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3066 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3069 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3070 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3071 src_item = folder_find_item_from_identifier(source);
3075 src_item->stype == F_NORMAL &&
3076 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3077 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3078 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3079 !folder_has_parent_of_type(src_item, F_TRASH);
3080 if (!item || !src_item || !folder_is_normal) {
3081 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3085 folderview_move_folder(folderview, src_item, item, copy);
3086 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3088 folderview->nodes_to_recollapse = NULL;
3089 } else if (info == TARGET_MAIL_URI_LIST) {
3090 if (gtk_cmclist_get_selection_info
3091 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3094 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3096 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3097 debug_print("no node\n");
3100 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3102 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3103 debug_print("no item\n");
3106 folderview_finish_dnd(gtk_selection_data_get_data(data),
3107 drag_context, time, item);
3111 static void folderview_drag_end_cb(GtkWidget *widget,
3112 GdkDragContext *drag_context,
3113 FolderView *folderview)
3115 drag_state_stop(folderview);
3116 folderview->scroll_value = 0;
3117 g_slist_free(folderview->nodes_to_recollapse);
3118 folderview->nodes_to_recollapse = NULL;
3121 void folderview_register_popup(FolderViewPopup *fpopup)
3125 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3126 FolderView *folderview = folderviews->data;
3127 GtkActionGroup *factory;
3129 factory = create_action_group(folderview, fpopup);
3130 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3132 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3135 void folderview_unregister_popup(FolderViewPopup *fpopup)
3140 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3141 FolderView *folderview = folderviews->data;
3143 g_hash_table_remove(folderview->popups, fpopup->klass);
3145 g_hash_table_remove(folderview_popups, fpopup->klass);