2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
61 #include "gtkcmctree.h"
63 #define COL_FOLDER_WIDTH 150
64 #define COL_NUM_WIDTH 32
66 static GList *folderview_list = NULL;
68 static GtkStyle *normal_style;
69 static GtkStyle *normal_color_style;
70 static GtkStyle *bold_style;
71 static GtkStyle *bold_color_style;
72 static GtkStyle *bold_tgtfold_style;
74 static GdkPixbuf *inboxxpm;
75 static GdkPixbuf *inboxhrmxpm;
76 static GdkPixbuf *inboxopenxpm;
77 static GdkPixbuf *inboxopenhrmxpm;
78 static GdkPixbuf *outboxxpm;
79 static GdkPixbuf *outboxhrmxpm;
80 static GdkPixbuf *outboxopenxpm;
81 static GdkPixbuf *outboxopenhrmxpm;
82 static GdkPixbuf *folderxpm;
83 static GdkPixbuf *folderhrmxpm;
84 static GdkPixbuf *folderopenxpm;
85 static GdkPixbuf *folderopenhrmxpm;
86 static GdkPixbuf *trashopenxpm;
87 static GdkPixbuf *trashopenhrmxpm;
88 static GdkPixbuf *trashxpm;
89 static GdkPixbuf *trashhrmxpm;
90 static GdkPixbuf *queuexpm;
91 static GdkPixbuf *queuehrmxpm;
92 static GdkPixbuf *queueopenxpm;
93 static GdkPixbuf *queueopenhrmxpm;
94 static GdkPixbuf *draftsxpm;
95 static GdkPixbuf *draftsopenxpm;
96 static GdkPixbuf *noselectxpm;
98 static GdkPixbuf *m_inboxxpm;
99 static GdkPixbuf *m_inboxhrmxpm;
100 static GdkPixbuf *m_inboxopenxpm;
101 static GdkPixbuf *m_inboxopenhrmxpm;
102 static GdkPixbuf *m_outboxxpm;
103 static GdkPixbuf *m_outboxhrmxpm;
104 static GdkPixbuf *m_outboxopenxpm;
105 static GdkPixbuf *m_outboxopenhrmxpm;
106 static GdkPixbuf *m_folderxpm;
107 static GdkPixbuf *m_folderhrmxpm;
108 static GdkPixbuf *m_folderopenxpm;
109 static GdkPixbuf *m_folderopenhrmxpm;
110 static GdkPixbuf *m_trashopenxpm;
111 static GdkPixbuf *m_trashopenhrmxpm;
112 static GdkPixbuf *m_trashxpm;
113 static GdkPixbuf *m_trashhrmxpm;
114 static GdkPixbuf *m_queuexpm;
115 static GdkPixbuf *m_queuehrmxpm;
116 static GdkPixbuf *m_queueopenxpm;
117 static GdkPixbuf *m_queueopenhrmxpm;
118 static GdkPixbuf *m_draftsxpm;
119 static GdkPixbuf *m_draftsopenxpm;
121 static GdkPixbuf *newxpm;
122 static GdkPixbuf *unreadxpm;
123 static GdkPixbuf *readxpm;
125 static void folderview_select_node (FolderView *folderview,
126 GtkCMCTreeNode *node);
127 static void folderview_set_folders (FolderView *folderview);
128 static void folderview_sort_folders (FolderView *folderview,
129 GtkCMCTreeNode *root,
131 static void folderview_append_folder (FolderView *folderview,
133 static void folderview_update_node (FolderView *folderview,
134 GtkCMCTreeNode *node);
136 static gint folderview_clist_compare (GtkCMCList *clist,
140 /* callback functions */
141 static gboolean folderview_button_pressed (GtkWidget *ctree,
142 GdkEventButton *event,
143 FolderView *folderview);
144 static gboolean folderview_button_released (GtkWidget *ctree,
145 GdkEventButton *event,
146 FolderView *folderview);
147 static gboolean folderview_key_pressed (GtkWidget *widget,
149 FolderView *folderview);
150 static void folderview_selected (GtkCMCTree *ctree,
153 FolderView *folderview);
154 static void folderview_tree_expanded (GtkCMCTree *ctree,
155 GtkCMCTreeNode *node,
156 FolderView *folderview);
157 static void folderview_tree_collapsed (GtkCMCTree *ctree,
158 GtkCMCTreeNode *node,
159 FolderView *folderview);
160 static void folderview_popup_close (GtkMenuShell *menu_shell,
161 FolderView *folderview);
162 static void folderview_col_resized (GtkCMCList *clist,
165 FolderView *folderview);
167 static void mark_all_read_handler (GtkAction *action,
171 static void mark_all_read_cb (GtkAction *action,
173 static void mark_all_read_recursive_cb (GtkAction *action,
176 static void folderview_empty_trash_cb (GtkAction *action,
179 static void folderview_send_queue_cb (GtkAction *action,
182 static void folderview_search_cb (GtkAction *action,
184 static void folderview_run_processing_cb(GtkAction *action,
187 static void folderview_property_cb (GtkAction *action,
190 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
191 GdkDragContext *context,
195 FolderView *folderview);
196 static void folderview_drag_leave_cb (GtkWidget *widget,
197 GdkDragContext *context,
199 FolderView *folderview);
200 static void folderview_drag_received_cb (GtkWidget *widget,
201 GdkDragContext *drag_context,
204 GtkSelectionData *data,
207 FolderView *folderview);
209 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
210 FolderView *folderview);
212 static void folderview_drag_data_get (GtkWidget *widget,
213 GdkDragContext *drag_context,
214 GtkSelectionData *selection_data,
217 FolderView *folderview);
218 static void folderview_drag_end_cb (GtkWidget *widget,
219 GdkDragContext *drag_context,
220 FolderView *folderview);
222 static void folderview_create_folder_node (FolderView *folderview,
224 static gboolean folderview_update_folder (gpointer source,
226 static gboolean folderview_update_item_claws (gpointer source,
228 static void folderview_processing_cb(GtkAction *action, gpointer data);
229 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
230 GdkEventButton *event);
232 GHashTable *folderview_popups;
234 static GtkActionEntry folderview_common_popup_entries[] =
236 {"FolderViewPopup", NULL, "FolderViewPopup" },
237 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
238 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
239 {"FolderViewPopup/---", NULL, "---" },
240 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
241 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
242 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
243 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
244 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
245 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
249 GtkTargetEntry folderview_drag_types[] =
251 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
252 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
255 void folderview_initialize(void)
257 FolderViewPopup *fpopup;
259 fpopup = g_new0(FolderViewPopup, 1);
261 fpopup->klass = "common";
262 fpopup->path = "<CommonFolder>";
263 fpopup->entries = folderview_common_popup_entries;
264 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
265 fpopup->set_sensitivity = NULL;
267 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
268 g_hash_table_insert(folderview_popups, "common", fpopup);
271 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
273 FolderViewPopup *fpopup_common;
274 GtkActionGroup *action_group;
276 action_group = cm_menu_create_action_group(
278 fpopup->entries, fpopup->n_entries,
279 (gpointer)folderview);
281 if (fpopup->toggle_entries)
282 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
283 fpopup->n_toggle_entries,
284 (gpointer)folderview);
285 if (fpopup->radio_entries)
286 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
287 fpopup->n_radio_entries, fpopup->radio_default,
288 G_CALLBACK(fpopup->radio_callback),
289 (gpointer)folderview);
291 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
292 if (fpopup_common != fpopup) {
293 gtk_action_group_add_actions(action_group, fpopup_common->entries,
294 fpopup_common->n_entries,
295 (gpointer)folderview);
296 if (fpopup_common->toggle_entries)
297 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
298 fpopup_common->n_toggle_entries,
299 (gpointer)folderview);
300 if (fpopup_common->radio_entries)
301 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
302 fpopup_common->n_radio_entries, fpopup_common->radio_default,
303 G_CALLBACK(fpopup_common->radio_callback),
304 (gpointer)folderview);
310 static void create_action_groups(gpointer key, gpointer value, gpointer data)
312 FolderView *folderview = data;
313 FolderViewPopup *fpopup = value;
314 GtkActionGroup *group;
316 group = create_action_group(folderview, fpopup);
317 g_hash_table_insert(folderview->popups, fpopup->klass, group);
320 static void folderview_column_set_titles(FolderView *folderview)
322 GtkWidget *ctree = folderview->ctree;
323 GtkWidget *label_folder;
324 GtkWidget *label_new;
325 GtkWidget *label_unread;
326 GtkWidget *label_total;
327 GtkWidget *hbox_folder;
329 GtkWidget *hbox_unread;
330 GtkWidget *hbox_total;
331 gint *col_pos = folderview->col_pos;
333 debug_print("setting titles...\n");
334 gtk_widget_realize(folderview->ctree);
335 gtk_widget_show_all(folderview->scrolledwin);
337 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
338 * instead text (text overflows making them unreadable and ugly) */
339 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
340 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
341 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
343 label_folder = gtk_label_new(_("Folder"));
344 label_new = gtk_image_new_from_pixbuf(newxpm);
345 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
346 label_total = gtk_image_new_from_pixbuf(readxpm);
348 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
350 hbox_folder = gtk_hbox_new(FALSE, 4);
351 hbox_new = gtk_hbox_new(FALSE, 4);
352 hbox_unread = gtk_hbox_new(FALSE, 4);
353 hbox_total = gtk_hbox_new(FALSE, 4);
356 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
357 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
358 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
359 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
360 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
365 gtk_widget_show_all(hbox_folder);
366 gtk_widget_show_all(hbox_new);
367 gtk_widget_show_all(hbox_unread);
368 gtk_widget_show_all(hbox_total);
371 gtk_widget_set_size_request(hbox_new, -1, 20);
372 gtk_widget_set_size_request(hbox_unread, -1, 20);
373 gtk_widget_set_size_request(hbox_total, -1, 20);
376 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
377 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
379 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
385 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
386 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
387 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
390 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
392 FolderView *folderview = (FolderView *)data;
393 GdkEventButton event;
394 if (folderview_get_selected_item(folderview) == NULL)
398 event.time = gtk_get_current_event_time();
400 folderview_set_sens_and_popup_menu(folderview, -1,
407 static GtkWidget *folderview_ctree_create(FolderView *folderview)
411 FolderColumnState *col_state;
412 FolderColumnType type;
413 gchar *titles[N_FOLDER_COLS];
415 GtkWidget *scrolledwin = folderview->scrolledwin;
417 debug_print("creating tree...\n");
418 memset(titles, 0, sizeof(titles));
420 col_state = prefs_folder_column_get_config();
421 memset(titles, 0, sizeof(titles));
423 col_pos = folderview->col_pos;
425 for (i = 0; i < N_FOLDER_COLS; i++) {
426 folderview->col_state[i] = col_state[i];
427 type = col_state[i].type;
431 titles[col_pos[F_COL_FOLDER]] = _("Folder");
432 titles[col_pos[F_COL_NEW]] = _("New");
433 titles[col_pos[F_COL_UNREAD]] = _("Unread");
434 /* TRANSLATORS: This in Number sign in American style */
435 titles[col_pos[F_COL_TOTAL]] = _("#");
437 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
440 if (prefs_common.show_col_headers == FALSE)
441 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
444 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
447 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
448 col_pos[F_COL_UNREAD],
450 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
451 col_pos[F_COL_TOTAL],
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);
457 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
458 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
460 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
461 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
463 /* don't let title buttons take key focus */
464 for (i = 0; i < N_FOLDER_COLS; i++) {
465 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
466 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
467 prefs_common.folder_col_size[i]);
468 gtk_cmclist_set_column_visibility
469 (GTK_CMCLIST(ctree), i, col_state[i].visible);
472 g_signal_connect(G_OBJECT(ctree), "key_press_event",
473 G_CALLBACK(folderview_key_pressed),
475 g_signal_connect(G_OBJECT(ctree), "button_press_event",
476 G_CALLBACK(folderview_button_pressed),
478 g_signal_connect(G_OBJECT(ctree), "popup-menu",
479 G_CALLBACK(folderview_popup_menu), folderview);
480 g_signal_connect(G_OBJECT(ctree), "button_release_event",
481 G_CALLBACK(folderview_button_released),
483 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
484 G_CALLBACK(folderview_selected), folderview);
486 /* drag-n-dropping folders on maemo is impractical as this
487 * opens the folder almost everytime */
488 g_signal_connect(G_OBJECT(ctree), "start_drag",
489 G_CALLBACK(folderview_start_drag), folderview);
491 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
492 G_CALLBACK(folderview_drag_data_get),
495 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
496 G_CALLBACK(folderview_tree_expanded),
498 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
499 G_CALLBACK(folderview_tree_collapsed),
502 g_signal_connect(G_OBJECT(ctree), "resize_column",
503 G_CALLBACK(folderview_col_resized),
507 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
508 folderview_drag_types, 2,
509 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
510 g_signal_connect(G_OBJECT(ctree), "drag_motion",
511 G_CALLBACK(folderview_drag_motion_cb),
513 g_signal_connect(G_OBJECT(ctree), "drag_leave",
514 G_CALLBACK(folderview_drag_leave_cb),
516 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
517 G_CALLBACK(folderview_drag_received_cb),
519 g_signal_connect(G_OBJECT(ctree), "drag_end",
520 G_CALLBACK(folderview_drag_end_cb),
523 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
528 void folderview_set_column_order(FolderView *folderview)
530 GtkWidget *ctree = folderview->ctree;
531 FolderItem *item = folderview_get_selected_item(folderview);
532 FolderItem *sel_item = NULL, *op_item = NULL;
533 GtkWidget *scrolledwin = folderview->scrolledwin;
535 if (folderview->drag_timer_id != 0) {
536 g_source_remove(folderview->drag_timer_id);
537 folderview->drag_timer_id = 0;
539 if (folderview->deferred_refresh_id != 0) {
540 g_source_remove(folderview->deferred_refresh_id);
541 folderview->deferred_refresh_id = 0;
543 if (folderview->scroll_timeout_id != 0) {
544 g_source_remove(folderview->scroll_timeout_id);
545 folderview->scroll_timeout_id = 0;
547 if (folderview->postpone_select_id != 0) {
548 g_source_remove(folderview->postpone_select_id);
549 folderview->postpone_select_id = 0;
552 if (folderview->selected)
553 sel_item = folderview_get_selected_item(folderview);
554 if (folderview->opened)
555 op_item = folderview_get_opened_item(folderview);
557 debug_print("recreating tree...\n");
558 gtk_widget_destroy(folderview->ctree);
561 folderview->ctree = ctree = folderview_ctree_create(folderview);
562 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
563 GTK_CMCLIST(ctree)->hadjustment);
564 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565 GTK_CMCLIST(ctree)->vadjustment);
566 gtk_widget_show(ctree);
569 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
571 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
573 folderview_set(folderview);
574 folderview_column_set_titles(folderview);
576 folderview_select(folderview,item);
579 FolderView *folderview_create(void)
581 FolderView *folderview;
582 GtkWidget *scrolledwin;
585 debug_print("Creating folder view...\n");
586 folderview = g_new0(FolderView, 1);
588 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
589 gtk_scrolled_window_set_policy
590 (GTK_SCROLLED_WINDOW(scrolledwin),
591 GTK_POLICY_AUTOMATIC,
592 prefs_common.folderview_vscrollbar_policy);
593 gtk_widget_set_size_request(scrolledwin,
594 prefs_common.folderview_width,
595 prefs_common.folderview_height);
597 folderview->scrolledwin = scrolledwin;
598 ctree = folderview_ctree_create(folderview);
600 /* create popup factories */
601 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
602 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
604 folderview->ctree = ctree;
606 folderview->folder_update_callback_id =
607 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
608 folderview->folder_item_update_callback_id =
609 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
611 gtk_widget_show_all(scrolledwin);
613 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
614 folderview_list = g_list_append(folderview_list, folderview);
616 folderview->drag_timer_id = 0;
617 folderview->deferred_refresh_id = 0;
618 folderview->scroll_timeout_id = 0;
619 folderview->postpone_select_id = 0;
624 void folderview_init(FolderView *folderview)
626 GtkWidget *ctree = folderview->ctree;
628 PangoFontDescription *normal_font;
630 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
631 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
632 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
633 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
634 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
635 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
636 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
637 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
638 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
639 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
640 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
641 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
642 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
652 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
659 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
666 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
667 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
668 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
677 normal_font = pango_font_description_from_string(NORMAL_FONT);
679 gtk_widget_modify_font(ctree, normal_font);
680 pango_font_description_free(normal_font);
682 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
685 PangoFontDescription *font_desc;
686 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
687 font_desc = pango_font_description_from_string(NORMAL_FONT);
689 if (normal_style->font_desc)
690 pango_font_description_free
691 (normal_style->font_desc);
692 normal_style->font_desc = font_desc;
694 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
695 normal_color_style = gtk_style_copy(normal_style);
696 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
700 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
701 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
702 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
703 PangoFontDescription *font_desc;
704 font_desc = pango_font_description_from_string(NORMAL_FONT);
706 pango_font_description_free(bold_style->font_desc);
707 bold_style->font_desc = font_desc;
709 pango_font_description_set_weight
710 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
712 PangoFontDescription *font_desc;
713 font_desc = pango_font_description_from_string(BOLD_FONT);
715 if (bold_style->font_desc)
716 pango_font_description_free
717 (bold_style->font_desc);
718 bold_style->font_desc = font_desc;
721 bold_color_style = gtk_style_copy(bold_style);
722 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
724 bold_tgtfold_style = gtk_style_copy(bold_style);
725 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
729 static gboolean folderview_defer_set(gpointer data)
731 FolderView *folderview = (FolderView *)data;
732 MainWindow *mainwin = folderview->mainwin;
736 if (mainwin->lock_count)
739 debug_print("doing deferred folderview_set now\n");
740 folderview_set(folderview);
742 folderview->deferred_refresh_id = 0;
746 void folderview_set(FolderView *folderview)
748 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
749 MainWindow *mainwin = folderview->mainwin;
750 FolderItem *sel_item = NULL, *op_item = NULL;
755 if (mainwin->lock_count) {
756 if (folderview->deferred_refresh_id == 0)
757 folderview->deferred_refresh_id =
758 g_timeout_add(500, folderview_defer_set, folderview);
759 debug_print("deferred folderview_set\n");
764 debug_print("Setting folder info...\n");
765 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
767 main_window_cursor_wait(mainwin);
769 if (folderview->selected)
770 sel_item = folderview_get_selected_item(folderview);
771 if (folderview->opened)
772 op_item = folderview_get_opened_item(folderview);
774 folderview->selected = NULL;
775 folderview->opened = NULL;
777 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
778 gtk_cmclist_clear(GTK_CMCLIST(ctree));
780 folderview_set_folders(folderview);
783 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
785 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
787 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
788 main_window_cursor_normal(mainwin);
789 STATUSBAR_POP(mainwin);
793 void folderview_set_all(void)
797 for (list = folderview_list; list != NULL; list = list->next)
798 folderview_set((FolderView *)list->data);
801 void folderview_select(FolderView *folderview, FolderItem *item)
803 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
804 GtkCMCTreeNode *node;
805 GtkCMCTreeNode *old_selected = folderview->selected;
809 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
810 if (node) folderview_select_node(folderview, node);
812 if (old_selected != node)
813 folder_update_op_count();
816 static void mark_all_read_cb(GtkAction *action, gpointer data)
818 mark_all_read_handler(action, data, FALSE);
821 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
823 mark_all_read_handler(action, data, TRUE);
826 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
828 FolderView *folderview = (FolderView *)data;
833 item = folderview_get_selected_item(folderview);
837 message = recursive? _("Do you really want to mark all mails in this "
838 "folder and its sub-folders as read?") :
839 _("Do you really want to mark all mails in this "
841 if (folderview->summaryview->folder_item != item &&
842 prefs_common.ask_mark_all_read) {
843 val = alertpanel_full(_("Mark all as read"),
844 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
845 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
847 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
849 else if (val & G_ALERTDISABLE)
850 prefs_common.ask_mark_all_read = FALSE;
853 folder_item_update_freeze();
854 if (folderview->summaryview->folder_item != item && !recursive)
855 summary_lock(folderview->summaryview);
857 summary_freeze(folderview->summaryview);
860 folderutils_mark_all_read_recursive(item);
862 folderutils_mark_all_read(item);
864 if (folderview->summaryview->folder_item != item && !recursive)
865 summary_unlock(folderview->summaryview);
867 summary_thaw(folderview->summaryview);
868 folder_item_update_thaw();
871 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
873 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
875 cm_return_if_fail(node != NULL);
877 if (folderview->open_folder) {
881 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
882 gtkut_ctree_expand_parent_all(ctree, node);
884 folderview->open_folder = TRUE;
885 gtkut_ctree_set_focus_row(ctree, node);
886 gtk_cmctree_select(ctree, node);
887 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
888 if ((folderview->summaryview->folder_item &&
889 folderview->summaryview->folder_item->total_msgs > 0) ||
890 prefs_common.layout_mode == SMALL_LAYOUT)
891 summary_grab_focus(folderview->summaryview);
893 gtk_widget_grab_focus(folderview->ctree);
896 void folderview_unselect(FolderView *folderview)
898 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
900 (GTK_CMCTREE(folderview->ctree), folderview->opened);
902 folderview->selected = folderview->opened = NULL;
905 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
906 GtkCMCTreeNode *node,
912 node = gtkut_ctree_node_next(ctree, node);
914 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
916 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
917 item = gtk_cmctree_node_get_row_data(ctree, node);
920 if (item->stype == F_TRASH || item->stype == F_DRAFT)
924 if(item->unread_msgs > 0)
928 if(item->new_msgs > 0)
932 if(item->marked_msgs > 0)
941 void folderview_select_next_with_flag(FolderView *folderview,
945 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
946 GtkCMCTreeNode *node = NULL;
947 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
948 gboolean last_open = prefs_common.always_show_msg;
952 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
955 prefs_common.summary_select_prio[0] = ACTION_NEW;
958 prefs_common.summary_select_prio[0] = ACTION_MARKED;
961 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
963 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
965 folderview_select_node(folderview, node);
969 if (!folderview->opened ||
970 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
974 /* search again from the first node */
975 node = folderview_find_next_with_flag(ctree, NULL, flag);
977 folderview_select_node(folderview, node);
980 prefs_common.summary_select_prio[0] = last_summary_select_prio;
981 prefs_common.always_show_msg = last_open;
984 FolderItem *folderview_get_selected_item(FolderView *folderview)
986 g_return_val_if_fail(folderview != NULL, NULL);
987 g_return_val_if_fail(folderview->ctree != NULL, NULL);
989 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
991 if (!folderview->selected) return NULL;
992 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
995 FolderItem *folderview_get_opened_item(FolderView *folderview)
997 g_return_val_if_fail(folderview != NULL, NULL);
998 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1000 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1002 if (!folderview->opened) return NULL;
1003 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1006 static void folderview_set_folders(FolderView *folderview)
1009 list = folder_get_list();
1011 for (; list != NULL; list = list->next) {
1012 folderview_append_folder(folderview, FOLDER(list->data));
1016 static gchar *get_scan_str(FolderItem *item)
1019 return g_strdup_printf(_("Scanning folder %s/%s..."),
1020 item->folder->name, item->path);
1022 return g_strdup_printf(_("Scanning folder %s..."),
1023 item->folder->name);
1025 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1029 for (list = folderview_list; list != NULL; list = list->next) {
1030 FolderView *folderview = (FolderView *)list->data;
1031 MainWindow *mainwin = folderview->mainwin;
1032 gchar *str = get_scan_str(item);
1034 STATUSBAR_PUSH(mainwin, str);
1035 STATUSBAR_POP(mainwin);
1040 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1043 MainWindow *mainwin = mainwindow_get_mainwindow();
1044 FolderView *folderview = NULL;
1045 GtkAdjustment *pos = NULL;
1048 cm_return_if_fail(folder != NULL);
1050 if (!folder->klass->scan_tree) return;
1053 alertpanel_full(_("Rebuild folder tree"),
1054 _("Rebuilding the folder tree will remove "
1055 "local caches. Do you want to continue?"),
1056 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1057 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1058 != G_ALERTALTERNATE) {
1064 window = label_window_create(_("Rebuilding folder tree..."));
1066 window = label_window_create(_("Scanning folder tree..."));
1069 folderview = mainwin->folderview;
1072 pos = gtk_scrolled_window_get_vadjustment(
1073 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1074 height = gtk_adjustment_get_value(pos);
1077 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1078 folder_scan_tree(folder, rebuild);
1079 folder_set_ui_func(folder, NULL, NULL);
1081 folderview_set_all();
1084 pos = gtk_scrolled_window_get_vadjustment(
1085 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1086 gtk_adjustment_set_value(pos, height);
1087 gtk_adjustment_changed(pos);
1089 label_window_destroy(window);
1093 /** folderview_check_new()
1094 * Scan and update the folder and return the
1095 * count the number of new messages since last check.
1096 * \param folder the folder to check for new messages
1097 * \return the number of new messages since last check
1099 gint folderview_check_new(Folder *folder)
1103 FolderView *folderview;
1105 GtkCMCTreeNode *node;
1107 gint former_new_msgs = 0;
1108 gint former_new = 0, former_unread = 0, former_total;
1110 for (list = folderview_list; list != NULL; list = list->next) {
1111 folderview = (FolderView *)list->data;
1112 ctree = GTK_CMCTREE(folderview->ctree);
1113 folderview->scanning_folder = folder;
1115 main_window_lock(folderview->mainwin);
1117 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1118 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1120 item = gtk_cmctree_node_get_row_data(ctree, node);
1121 if (!item || !item->path || !item->folder) continue;
1122 if (item->no_select) continue;
1123 if (folder && folder != item->folder) continue;
1124 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1125 if (!item->prefs->newmailcheck) continue;
1126 if (item->processing_pending == TRUE) {
1127 debug_print("skipping %s, processing pending\n",
1128 item->path ? item->path : item->name);
1131 if (item->scanning != ITEM_NOT_SCANNING) {
1132 debug_print("skipping %s, scanning\n",
1133 item->path ? item->path : item->name);
1137 str = get_scan_str(item);
1139 STATUSBAR_PUSH(folderview->mainwin, str);
1143 folderview_scan_tree_func(item->folder, item, NULL);
1144 former_new = item->new_msgs;
1145 former_unread = item->unread_msgs;
1146 former_total = item->total_msgs;
1148 if (item->folder->klass->scan_required &&
1149 (item->folder->klass->scan_required(item->folder, item) ||
1150 item->folder->inbox == item ||
1151 item->opened == TRUE ||
1152 item->processing_pending == TRUE)) {
1153 if (folder_item_scan(item) < 0) {
1155 summaryview_unlock(folderview->summaryview, item);
1156 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1157 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1158 item->path ? item->path:item->name);
1159 STATUSBAR_POP(folderview->mainwin);
1161 } else if (!FOLDER_IS_LOCAL(folder)) {
1162 STATUSBAR_POP(folderview->mainwin);
1167 } else if (!item->folder->klass->scan_required) {
1168 if (folder_item_scan(item) < 0) {
1169 summaryview_unlock(folderview->summaryview, item);
1170 if (folder && !FOLDER_IS_LOCAL(folder)) {
1171 STATUSBAR_POP(folderview->mainwin);
1176 if (former_new != item->new_msgs ||
1177 former_unread != item->unread_msgs ||
1178 former_total != item->total_msgs)
1179 folderview_update_node(folderview, node);
1181 new_msgs += item->new_msgs;
1182 former_new_msgs += former_new;
1183 STATUSBAR_POP(folderview->mainwin);
1185 folderview->scanning_folder = NULL;
1186 main_window_unlock(folderview->mainwin);
1190 folder_write_list();
1191 /* Number of new messages since last check is the just the difference
1192 * between former_new_msgs and new_msgs. If new_msgs is less than
1193 * former_new_msgs, that would mean another session accessed the folder
1194 * and the result is not well defined.
1196 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1200 void folderview_check_new_all(void)
1204 FolderView *folderview;
1206 folderview = (FolderView *)folderview_list->data;
1209 main_window_lock(folderview->mainwin);
1210 window = label_window_create
1211 (_("Checking for new messages in all folders..."));
1213 list = folder_get_list();
1214 for (; list != NULL; list = list->next) {
1215 Folder *folder = list->data;
1217 folderview_check_new(folder);
1220 folder_write_list();
1221 folderview_set_all();
1223 label_window_destroy(window);
1224 main_window_unlock(folderview->mainwin);
1228 static gboolean folderview_have_children_sub(FolderView *folderview,
1234 if (!item || !item->folder || !item->folder->node)
1237 node = item->folder->node;
1239 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1240 node = node->children;
1242 if (in_sub && item->total_msgs > 0) {
1246 while (node != NULL) {
1247 if (node && node->data) {
1248 FolderItem *next_item = (FolderItem*) node->data;
1250 if (folderview_have_children_sub(folderview,
1259 static gboolean folderview_have_children(FolderView *folderview,
1262 return folderview_have_children_sub(folderview, item, FALSE);
1265 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1271 if (!item || !item->folder || !item->folder->node)
1274 node = item->folder->node;
1276 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1277 node = node->children;
1280 (item->new_msgs > 0 ||
1281 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1285 while (node != NULL) {
1286 if (node && node->data) {
1287 FolderItem *next_item = (FolderItem*) node->data;
1289 if (folderview_have_new_children_sub(folderview,
1298 static gboolean folderview_have_new_children(FolderView *folderview,
1301 return folderview_have_new_children_sub(folderview, item, FALSE);
1304 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1310 if (!item || !item->folder || !item->folder->node)
1313 node = item->folder->node;
1315 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1316 node = node->children;
1319 (item->unread_msgs > 0 ||
1320 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1324 while (node != NULL) {
1325 if (node && node->data) {
1326 FolderItem *next_item = (FolderItem*) node->data;
1328 if (folderview_have_unread_children_sub(folderview,
1338 static gboolean folderview_have_unread_children(FolderView *folderview,
1341 return folderview_have_unread_children_sub(folderview, item, FALSE);
1344 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1350 if (!item || !item->folder || !item->folder->node)
1353 node = item->folder->node;
1355 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1356 node = node->children;
1358 if (in_sub && item->search_match){
1362 while (node != NULL) {
1363 if (node && node->data) {
1364 FolderItem *next_item = (FolderItem*) node->data;
1366 if (folderview_have_matching_children_sub(folderview,
1376 static gboolean folderview_have_matching_children(FolderView *folderview,
1379 return folderview_have_matching_children_sub(folderview, item, FALSE);
1382 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1388 if (!item || !item->folder || !item->folder->node)
1391 node = item->folder->node;
1393 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1394 node = node->children;
1396 if (item->marked_msgs != 0) {
1400 while (node != NULL) {
1401 if (node && node->data) {
1402 FolderItem *next_item = (FolderItem*) node->data;
1404 if (folderview_have_marked_children_sub(folderview,
1413 static gboolean folderview_have_marked_children(FolderView *folderview,
1416 return folderview_have_marked_children_sub(folderview, item, FALSE);
1419 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1421 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1422 GtkStyle *style = NULL;
1423 GtkStyle *color_style = NULL;
1425 GdkPixbuf *xpm, *openxpm;
1426 static GdkPixbuf *searchicon;
1427 gboolean mark = FALSE;
1430 gboolean add_unread_mark;
1431 gboolean add_sub_match_mark;
1432 gboolean use_bold, use_color;
1433 gint *col_pos = folderview->col_pos;
1434 SpecialFolderItemType stype;
1436 item = gtk_cmctree_node_get_row_data(ctree, node);
1437 cm_return_if_fail(item != NULL);
1439 if (!GTK_CMCTREE_ROW(node)->expanded)
1440 mark = folderview_have_marked_children(folderview, item);
1442 mark = (item->marked_msgs != 0);
1444 stype = item->stype;
1445 if (stype == F_NORMAL) {
1446 if (folder_has_parent_of_type(item, F_TRASH))
1448 else if (folder_has_parent_of_type(item, F_DRAFT))
1450 else if (folder_has_parent_of_type(item, F_OUTBOX))
1452 else if (folder_has_parent_of_type(item, F_QUEUE))
1457 if (item->hide_read_msgs || item->hide_read_threads) {
1458 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1459 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1461 xpm = mark?m_inboxxpm:inboxxpm;
1462 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1466 if (item->hide_read_msgs || item->hide_read_threads) {
1467 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1468 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1470 xpm = mark?m_outboxxpm:outboxxpm;
1471 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1475 if (item->hide_read_msgs || item->hide_read_threads) {
1476 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1477 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1479 xpm = mark?m_queuexpm:queuexpm;
1480 openxpm = mark?m_queueopenxpm:queueopenxpm;
1484 if (item->hide_read_msgs || item->hide_read_threads) {
1485 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1486 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1488 xpm = mark?m_trashxpm:trashxpm;
1489 openxpm = mark?m_trashopenxpm:trashopenxpm;
1493 xpm = mark?m_draftsxpm:draftsxpm;
1494 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1497 if (item->hide_read_msgs || item->hide_read_threads) {
1498 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1499 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1501 xpm = mark?m_folderxpm:folderxpm;
1502 openxpm = mark?m_folderopenxpm:folderopenxpm;
1506 if (item->no_select) {
1507 xpm = openxpm = noselectxpm;
1510 name = folder_item_get_name(item);
1512 if (!GTK_CMCTREE_ROW(node)->expanded) {
1513 add_unread_mark = folderview_have_unread_children(
1515 add_sub_match_mark = folderview_have_matching_children(
1518 add_unread_mark = FALSE;
1519 add_sub_match_mark = FALSE;
1522 if (item->search_match) {
1524 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1527 xpm = openxpm = searchicon;
1531 if (prefs_common.display_folder_unread) {
1532 if (folder_has_parent_of_type(item, F_QUEUE)) {
1533 /* only total_msgs matters here */
1534 if (item->total_msgs > 0) {
1535 /* show total number (should be equal to the unread number)
1537 str = g_strdup_printf("%s (%d%s%s)",
1538 name, item->total_msgs,
1539 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1540 (item->unreadmarked_msgs > 0) ? "!" : "");
1543 if (prefs_common.display_folder_unread == 1) {
1544 if (item->unread_msgs > 0) {
1545 /* show unread number and signs */
1546 str = g_strdup_printf("%s (%d%s%s)",
1547 name, item->unread_msgs,
1548 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1549 (item->unreadmarked_msgs > 0) ? "!" : "");
1552 if (item->total_msgs > 0) {
1553 /* show unread number, total number and signs if any */
1554 str = g_strdup_printf("%s (%d/%d%s%s)",
1555 name, item->unread_msgs, item->total_msgs,
1556 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1557 (item->unreadmarked_msgs > 0) ? "!" : "");
1561 if ((str == NULL) &&
1562 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1563 /* no unread/total numbers, but at least one sign */
1564 str = g_strdup_printf("%s (%s%s)",
1566 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1567 (item->unreadmarked_msgs > 0) ? "!" : "");
1571 /* last fallback, folder name only or with +! sign */
1572 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1573 str = g_strdup_printf("%s%s",
1575 } else if (item->unreadmarked_msgs > 0) {
1576 str = g_strdup_printf("%s%s",
1578 } else if (add_sub_match_mark) {
1579 str = g_strdup_printf("%s%s",
1582 str = g_strdup_printf("%s", name);
1585 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1587 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1591 if (!folder_item_parent(item)) {
1592 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1593 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1594 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1596 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1597 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1598 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1601 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1602 folder_has_parent_of_type(item, F_TRASH)) {
1603 use_bold = use_color = FALSE;
1604 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1605 GSList *list = folder_item_get_msg_list(item);
1607 use_bold = use_color = FALSE;
1608 for (cur = list; cur; cur = cur->next) {
1609 MsgInfo *msginfo = (MsgInfo *)cur->data;
1610 if (!MSG_IS_DELETED(msginfo->flags)) {
1611 /* highlight queue folder if there are any messages */
1612 use_bold = use_color = TRUE;
1616 if (!GTK_CMCTREE_ROW(node)->expanded &&
1617 use_bold == FALSE &&
1618 folderview_have_children(folderview, item))
1619 use_bold = use_color = TRUE;
1620 procmsg_msg_list_free(list);
1622 /* if unread messages exist, print with bold font */
1623 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1625 /* if new messages exist, print with colored letter */
1627 (item->new_msgs > 0) ||
1629 folderview_have_new_children(folderview, item));
1632 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1637 if (item->prefs->color > 0 && !use_color) {
1638 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1639 color_style = gtk_style_copy(bold_style);
1640 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1641 style = color_style;
1642 } else if (use_color) {
1643 style = bold_color_style;
1646 if (item->op_count > 0) {
1647 style = bold_tgtfold_style;
1649 } else if (use_color) {
1650 style = normal_color_style;
1651 gtk_cmctree_node_set_foreground(ctree, node,
1652 &folderview->color_new);
1653 } else if (item->op_count > 0) {
1654 style = bold_tgtfold_style;
1655 } else if (item->prefs->color > 0) {
1657 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1658 color_style = gtk_style_copy(normal_style);
1659 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1660 style = color_style;
1662 style = normal_style;
1665 gtk_cmctree_node_set_row_style(ctree, node, style);
1667 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1668 folderview_update_node(folderview, node);
1671 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1674 FolderView *folderview;
1676 GtkCMCTreeNode *node;
1678 cm_return_if_fail(item != NULL);
1680 for (list = folderview_list; list != NULL; list = list->next) {
1681 folderview = (FolderView *)list->data;
1682 ctree = GTK_CMCTREE(folderview->ctree);
1684 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1685 if (node && item->search_match != matches) {
1686 item->search_match = matches;
1687 folderview_update_node(folderview, node);
1692 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1694 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1695 FolderView *folderview = (FolderView *)data;
1697 GtkCMCTreeNode *node;
1698 cm_return_val_if_fail(update_info != NULL, TRUE);
1699 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1700 cm_return_val_if_fail(folderview != NULL, FALSE);
1702 ctree = GTK_CMCTREE(folderview->ctree);
1704 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1707 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1708 folderview_update_node(folderview, node);
1710 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1711 update_info->item == folderview->summaryview->folder_item &&
1712 update_info->item != NULL)
1713 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1714 summary_show(folderview->summaryview, update_info->item);
1720 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1721 GNode *gnode, GtkCMCTreeNode *cnode,
1724 FolderView *folderview = (FolderView *)data;
1725 FolderItem *item = FOLDER_ITEM(gnode->data);
1727 cm_return_val_if_fail(item != NULL, FALSE);
1729 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1730 folderview_update_node(folderview, cnode);
1735 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1738 FolderView *folderview = (FolderView *)data;
1741 if (GTK_CMCTREE_ROW(node)->children) {
1742 item = gtk_cmctree_node_get_row_data(ctree, node);
1743 cm_return_if_fail(item != NULL);
1745 if (!item->collapsed)
1746 gtk_cmctree_expand(ctree, node);
1748 folderview_update_node(folderview, node);
1752 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1753 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1756 GtkCMCTreeNode *node, *parent, *sibling;
1758 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1760 g_warning("%s not found.", item->path);
1762 parent = GTK_CMCTREE_ROW(node)->parent;
1763 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1764 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1766 sibling = GTK_CMCTREE_ROW(parent)->children;
1770 tmp = gtk_cmctree_node_get_row_data
1772 if (tmp && tmp->stype != F_NORMAL)
1773 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1777 if (node != sibling)
1778 gtk_cmctree_move(ctree, node, parent, sibling);
1785 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1788 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1789 GtkCMCTreeNode *prev = NULL;
1791 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1792 gtk_sctree_sort_recursive(ctree, root);
1793 if (root && GTK_CMCTREE_ROW(root)->parent) {
1794 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1797 set_special_folder(ctree, folder->inbox, root, &prev);
1798 set_special_folder(ctree, folder->outbox, root, &prev);
1799 set_special_folder(ctree, folder->draft, root, &prev);
1800 set_special_folder(ctree, folder->queue, root, &prev);
1801 set_special_folder(ctree, folder->trash, root, &prev);
1802 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1805 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1807 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1808 GtkCMCTreeNode *root;
1810 cm_return_if_fail(folder != NULL);
1812 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1813 folderview_gnode_func, folderview);
1814 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1816 folderview_sort_folders(folderview, root, folder);
1819 /* callback functions */
1820 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1821 GdkEventButton *event)
1825 FolderViewPopup *fpopup;
1826 GtkActionGroup *action_group;
1828 FolderItem *special_trash = NULL, *special_queue = NULL;
1830 GtkUIManager *ui_manager = gtk_ui_manager_new();
1832 if (folderview->ui_manager)
1833 g_object_unref(folderview->ui_manager);
1835 folderview->ui_manager = ui_manager;
1836 item = folderview_get_selected_item(folderview);
1838 cm_return_if_fail(item != NULL);
1839 cm_return_if_fail(item->folder != NULL);
1840 folder = item->folder;
1842 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1845 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1847 fpopup = g_hash_table_lookup(folderview_popups, "common");
1848 action_group = g_hash_table_lookup(folderview->popups, "common");
1851 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1855 if (fpopup->add_menuitems)
1856 fpopup->add_menuitems(ui_manager, item);
1858 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1864 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1866 if (fpopup->set_sensitivity != NULL)
1867 fpopup->set_sensitivity(ui_manager, item);
1869 if (NULL != (ac = account_find_from_item(item))) {
1870 special_trash = account_get_special_folder(ac, F_TRASH);
1871 special_queue = account_get_special_folder(ac, F_QUEUE);
1874 if ((item == folder->trash || item == special_trash
1875 || folder_has_parent_of_type(item, F_TRASH))) {
1876 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1877 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1880 if ((item == folder->queue || item == special_queue
1881 || folder_has_parent_of_type(item, F_QUEUE))) {
1882 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1883 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1886 #define SET_SENS(name, sens) \
1887 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1889 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1890 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1891 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1892 folderview->selected == folderview->opened);
1893 SET_SENS("FolderViewPopup/Properties", TRUE);
1895 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1896 item->total_msgs >= 1 && !item->processing_pending);
1897 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1898 !item->no_select && !item->processing_pending);
1900 if (item == folder->trash || item == special_trash
1901 || folder_has_parent_of_type(item, F_TRASH)) {
1902 GSList *msglist = folder_item_get_msg_list(item);
1903 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1904 procmsg_msg_list_free(msglist);
1906 if (item == folder->queue || item == special_queue
1907 || folder_has_parent_of_type(item, F_QUEUE)) {
1908 GSList *msglist = folder_item_get_msg_list(item);
1909 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1910 procmsg_msg_list_free(msglist);
1914 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1915 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1916 g_signal_connect(G_OBJECT(popup), "selection_done",
1917 G_CALLBACK(folderview_popup_close),
1919 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1920 event->button, event->time);
1923 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1924 FolderView *folderview)
1926 GtkCMCList *clist = GTK_CMCLIST(ctree);
1927 gint prev_row = -1, row = -1, column = -1;
1929 if (!event) return FALSE;
1931 if (event->button == 1 || event->button == 2) {
1932 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1933 folderview->open_folder = TRUE;
1935 if (event->type == GDK_2BUTTON_PRESS) {
1936 if (clist->selection) {
1937 GtkCMCTreeNode *node;
1939 node = GTK_CMCTREE_NODE(clist->selection->data);
1941 gtk_cmctree_toggle_expansion(
1944 folderview->open_folder = FALSE;
1951 if (event->button == 2 || event->button == 3) {
1953 if (clist->selection) {
1954 GtkCMCTreeNode *node;
1956 node = GTK_CMCTREE_NODE(clist->selection->data);
1958 prev_row = gtkut_ctree_get_nth_from_node
1959 (GTK_CMCTREE(ctree), node);
1962 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1965 if (prev_row != row) {
1966 gtk_cmclist_unselect_all(clist);
1967 if (event->button == 2)
1968 folderview_select_node
1970 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1973 gtk_cmclist_select_row(clist, row, column);
1977 if (event->button != 3) return FALSE;
1979 folderview_set_sens_and_popup_menu(folderview, row, event);
1983 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1984 FolderView *folderview)
1986 int row = -1, column = -1;
1988 if (!event) return FALSE;
1990 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1993 if (event->button == 1 && folderview->open_folder == FALSE &&
1994 folderview->opened != NULL) {
1995 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1996 folderview->opened);
1997 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2003 #define BREAK_ON_MODIFIER_KEY() \
2004 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2006 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2007 FolderView *folderview)
2009 GtkCMCTreeNode *node;
2012 if (!event) return FALSE;
2014 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2017 switch (event->keyval) {
2019 if (folderview->selected) {
2020 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2021 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2022 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2023 folderview->selected);
2025 folderview_select_node(folderview,
2026 folderview->selected);
2030 case GDK_KEY_Return:
2031 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2032 gtk_cmctree_toggle_expansion(
2033 GTK_CMCTREE(folderview->ctree),
2034 folderview->selected);
2038 case GDK_KEY_Return:
2039 case GDK_KEY_KP_Enter:
2040 if (folderview->selected)
2041 folderview_select_node(folderview, folderview->selected);
2045 BREAK_ON_MODIFIER_KEY();
2046 if (folderview->selected) {
2047 if (folderview->opened == folderview->selected &&
2048 (!folderview->summaryview->folder_item ||
2049 folderview->summaryview->folder_item->total_msgs == 0))
2050 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2052 folderview_select_node(folderview,
2053 folderview->selected);
2057 if (folderview->selected) {
2058 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2059 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2060 folderview->selected);
2062 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2063 folderview->selected))) {
2064 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2065 NULL, folder_item_parent(item)))) {
2066 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2067 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2068 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2077 if (event->keyval == GDK_KEY_Home)
2078 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2080 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2081 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2083 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2085 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2086 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2096 typedef struct _PostponedSelectData
2099 GtkCMCTreeNode *row;
2101 FolderView *folderview;
2102 } PostponedSelectData;
2104 static gboolean postpone_select(void *data)
2106 PostponedSelectData *psdata = (PostponedSelectData *)data;
2107 debug_print("trying again\n");
2109 psdata->folderview->postpone_select_id = 0;
2110 psdata->folderview->open_folder = TRUE;
2111 main_window_cursor_normal(psdata->folderview->mainwin);
2112 STATUSBAR_POP(psdata->folderview->mainwin);
2113 folderview_selected(psdata->ctree, psdata->row,
2114 psdata->column, psdata->folderview);
2119 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2121 if (folderview->opened) {
2123 folderview->opened = NULL;
2127 FolderItem *olditem =
2128 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2129 folderview->opened);
2131 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2132 olditem->path ? olditem->path:olditem->name);
2133 /* will be null if we just moved the previously opened folder */
2134 STATUSBAR_PUSH(folderview->mainwin, buf);
2135 main_window_cursor_wait(folderview->mainwin);
2137 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2138 summary_show(folderview->summaryview, NULL);
2139 folder_item_close(olditem);
2140 main_window_cursor_normal(folderview->mainwin);
2141 STATUSBAR_POP(folderview->mainwin);
2142 if (olditem->folder->klass->item_closed)
2143 olditem->folder->klass->item_closed(olditem);
2148 if (folderview->opened &&
2149 !GTK_CMCTREE_ROW(folderview->opened)->children)
2150 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2152 folderview->opened = NULL;
2154 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2155 gint column, FolderView *folderview)
2157 static gboolean can_select = TRUE; /* exclusive lock */
2162 GtkCMCTreeNode *old_opened = folderview->opened;
2164 folderview->selected = row;
2166 debug_print("newly selected %p, opened %p\n", folderview->selected,
2167 folderview->opened);
2168 if (folderview->opened == row) {
2169 folderview->open_folder = FALSE;
2174 item = gtk_cmctree_node_get_row_data(ctree, row);
2177 folderview->open_folder = FALSE;
2181 if (!can_select || summary_is_locked(folderview->summaryview)) {
2182 if (folderview->opened) {
2183 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2184 gtk_cmctree_select(ctree, folderview->opened);
2186 folderview->open_folder = FALSE;
2191 if (!folderview->open_folder) {
2198 /* Save cache for old folder */
2199 /* We don't want to lose all caches if sylpheed crashed */
2200 /* resets folderview->opened to NULL */
2201 folderview_close_opened(folderview, FALSE);
2203 /* CLAWS: set compose button type: news folder items
2204 * always have a news folder as parent */
2206 toolbar_set_compose_button
2207 (folderview->mainwin->toolbar,
2208 FOLDER_TYPE(item->folder) == F_NEWS ?
2209 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2212 debug_print("Folder %s is selected\n", item->path);
2214 if (!GTK_CMCTREE_ROW(row)->children)
2215 gtk_cmctree_expand(ctree, row);
2217 /* ungrab the mouse event */
2218 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2219 gtk_grab_remove(GTK_WIDGET(ctree));
2220 if (gdk_pointer_is_grabbed())
2221 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2225 /* TODO: wwp: avoid displaying (null) in the status bar */
2226 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2227 item->path : "(null)");
2228 debug_print("%s\n", buf);
2229 STATUSBAR_PUSH(folderview->mainwin, buf);
2232 main_window_cursor_wait(folderview->mainwin);
2234 if (folderview->scanning_folder == item->folder) {
2237 res = folder_item_open(item);
2240 if (res == -1 && item->no_select == FALSE) {
2241 main_window_cursor_normal(folderview->mainwin);
2242 STATUSBAR_POP(folderview->mainwin);
2244 alertpanel_error(_("Folder could not be opened."));
2246 folderview->open_folder = FALSE;
2250 } else if (res == -2 && item->no_select == FALSE) {
2251 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2252 data->ctree = ctree;
2254 data->column = column;
2255 data->folderview = folderview;
2256 debug_print("postponing open of %s till end of scan\n",
2257 item->path ? item->path:item->name);
2258 folderview->open_folder = FALSE;
2260 if (folderview->postpone_select_id != 0)
2261 g_source_remove(folderview->postpone_select_id);
2262 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2267 main_window_cursor_normal(folderview->mainwin);
2270 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2271 opened = summary_show(folderview->summaryview, item);
2273 folder_clean_cache_memory(item);
2276 gtkut_ctree_set_focus_row(ctree, old_opened);
2277 gtk_cmctree_select(ctree, old_opened);
2278 folderview->opened = old_opened;
2280 folderview->opened = row;
2281 if (gtk_cmctree_node_is_visible(ctree, row)
2282 != GTK_VISIBILITY_FULL)
2283 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2286 STATUSBAR_POP(folderview->mainwin);
2288 folderview->open_folder = FALSE;
2293 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2294 FolderView *folderview)
2298 item = gtk_cmctree_node_get_row_data(ctree, node);
2299 cm_return_if_fail(item != NULL);
2300 item->collapsed = FALSE;
2301 folderview_update_node(folderview, node);
2304 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2305 FolderView *folderview)
2309 item = gtk_cmctree_node_get_row_data(ctree, node);
2310 cm_return_if_fail(item != NULL);
2311 item->collapsed = TRUE;
2312 folderview_update_node(folderview, node);
2315 static void folderview_popup_close(GtkMenuShell *menu_shell,
2316 FolderView *folderview)
2318 if (!folderview->opened) return;
2320 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2323 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2324 FolderView *folderview)
2326 FolderColumnType type = folderview->col_state[column].type;
2328 prefs_common.folder_col_size[type] = width;
2331 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2333 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2334 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2335 GtkCMCTreeNode *node, *parent_node;
2336 gint *col_pos = folderview->col_pos;
2337 FolderItemUpdateData hookdata;
2339 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2340 if (parent_node == NULL)
2343 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2345 text[col_pos[F_COL_FOLDER]] = item->name;
2346 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2351 gtk_cmctree_expand(ctree, parent_node);
2352 gtk_cmctree_node_set_row_data(ctree, node, item);
2354 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2355 folderview_sort_folders(folderview, parent_node, item->folder);
2357 hookdata.item = item;
2358 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2359 hookdata.msg = NULL;
2360 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2362 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2365 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2367 FolderView *folderview = (FolderView *)data;
2369 GSList *mlist = NULL;
2371 FolderItem *special_trash = NULL;
2374 if (!folderview->selected) return;
2375 item = folderview_get_selected_item(folderview);
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_trash = account_get_special_folder(ac, F_TRASH);
2382 if (item != item->folder->trash && item != special_trash
2383 && !folder_has_parent_of_type(item, F_TRASH)) return;
2385 if (prefs_common.ask_on_clean) {
2386 if (alertpanel(_("Empty trash"),
2387 _("Delete all messages in trash?"),
2388 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2392 mlist = folder_item_get_msg_list(item);
2394 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2395 MsgInfo * msginfo = (MsgInfo *) cur->data;
2396 if (MSG_IS_LOCKED(msginfo->flags))
2398 /* is it partially received? (partial_recv isn't cached) */
2399 if (msginfo->total_size != 0 &&
2400 msginfo->size != (off_t)msginfo->total_size)
2401 partial_mark_for_delete(msginfo);
2403 procmsg_msg_list_free(mlist);
2405 folder_item_remove_all_msg(item);
2408 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2410 FolderView *folderview = (FolderView *)data;
2412 FolderItem *special_queue = NULL;
2414 gchar *errstr = NULL;
2416 if (!folderview->selected) return;
2417 item = folderview_get_selected_item(folderview);
2418 cm_return_if_fail(item != NULL);
2419 cm_return_if_fail(item->folder != NULL);
2421 if (NULL != (ac = account_find_from_item(item)))
2422 special_queue = account_get_special_folder(ac, F_QUEUE);
2424 if (item != item->folder->queue && item != special_queue
2425 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2427 if (procmsg_queue_is_empty(item))
2430 if (prefs_common.work_offline)
2431 if (alertpanel(_("Offline warning"),
2432 _("You're working offline. Override?"),
2433 GTK_STOCK_NO, GTK_STOCK_YES,
2434 NULL) != G_ALERTALTERNATE)
2437 /* ask for confirmation before sending queued messages only
2438 in online mode and if there is at least one message queued
2439 in any of the folder queue
2441 if (prefs_common.confirm_send_queued_messages) {
2442 if (!prefs_common.work_offline) {
2443 if (alertpanel(_("Send queued messages"),
2444 _("Send all queued messages?"),
2445 GTK_STOCK_CANCEL, _("_Send"),
2446 NULL) != G_ALERTALTERNATE)
2451 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2453 alertpanel_error_log(_("Some errors occurred while "
2454 "sending queued messages."));
2456 alertpanel_error_log(_("Some errors occurred "
2457 "while sending queued messages:\n%s"), errstr);
2463 static void folderview_search_cb(GtkAction *action, gpointer data)
2465 FolderView *folderview = (FolderView *)data;
2466 summary_search(folderview->summaryview);
2469 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2471 FolderView *folderview = (FolderView *)data;
2474 if (!folderview->selected) return;
2476 item = folderview_get_selected_item(folderview);
2477 cm_return_if_fail(item != NULL);
2478 cm_return_if_fail(item->folder != NULL);
2480 item->processing_pending = TRUE;
2481 folder_item_apply_processing(item);
2482 item->processing_pending = FALSE;
2485 static void folderview_property_cb(GtkAction *action, gpointer data)
2487 FolderView *folderview = (FolderView *)data;
2490 if (!folderview->selected) return;
2492 item = folderview_get_selected_item(folderview);
2493 cm_return_if_fail(item != NULL);
2494 cm_return_if_fail(item->folder != NULL);
2496 prefs_folder_item_open(item);
2499 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2501 GSList *list = NULL;
2502 GSList *done = NULL;
2503 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2505 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2506 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2507 && list->data != node) {
2508 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2509 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2512 for (list = done; list != NULL; list = g_slist_next(list)) {
2513 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2519 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2520 FolderItem *to_folder, gboolean copy)
2522 FolderItem *new_folder = NULL;
2526 cm_return_if_fail(folderview != NULL);
2527 cm_return_if_fail(from_folder != NULL);
2528 cm_return_if_fail(to_folder != NULL);
2530 if (prefs_common.warn_dnd) {
2531 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2532 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2533 from_folder->name, to_folder->name);
2534 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2535 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2536 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2539 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2541 else if (status & G_ALERTDISABLE)
2542 prefs_common.warn_dnd = FALSE;
2545 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2546 from_folder->name, to_folder->name);
2547 STATUSBAR_PUSH(folderview->mainwin, buf);
2549 summary_clear_all(folderview->summaryview);
2550 folderview->opened = NULL;
2551 folderview->selected = NULL;
2552 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2554 main_window_cursor_wait(folderview->mainwin);
2556 statusbar_verbosity_set(FALSE);
2557 folder_item_update_freeze();
2558 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2559 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2560 statusbar_verbosity_set(FALSE);
2561 main_window_cursor_normal(folderview->mainwin);
2562 STATUSBAR_POP(folderview->mainwin);
2563 folder_item_update_thaw();
2564 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2566 folderview_sort_folders(folderview,
2567 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2568 NULL, to_folder), new_folder->folder);
2569 folderview_select(folderview, new_folder);
2570 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2572 statusbar_verbosity_set(FALSE);
2573 main_window_cursor_normal(folderview->mainwin);
2574 STATUSBAR_POP(folderview->mainwin);
2575 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2576 folder_item_update_thaw();
2578 case F_MOVE_FAILED_DEST_IS_PARENT:
2579 alertpanel_error(_("Source and destination are the same."));
2581 case F_MOVE_FAILED_DEST_IS_CHILD:
2582 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2583 _("Can't move a folder to one of its children."));
2585 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2586 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2589 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2594 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2597 static gint folderview_clist_compare(GtkCMCList *clist,
2598 gconstpointer ptr1, gconstpointer ptr2)
2600 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2601 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2603 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2605 return item1->order - item2->order;
2608 // if only one folder has an order it comes first
2609 if (item1->order > 0)
2613 if (item2->order > 0)
2619 return (item2->name != NULL);
2623 return g_utf8_collate(item1->name, item2->name);
2626 static void folderview_processing_cb(GtkAction *action, gpointer data)
2628 FolderView *folderview = (FolderView *)data;
2632 if (!folderview->selected) return;
2634 item = folderview_get_selected_item(folderview);
2635 cm_return_if_fail(item != NULL);
2636 cm_return_if_fail(item->folder != NULL);
2638 id = folder_item_get_identifier(item);
2639 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2642 prefs_filtering_open(&item->prefs->processing, title,
2643 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2647 void folderview_set_target_folder_color(gint color_op)
2651 FolderView *folderview;
2653 for (list = folderview_list; list != NULL; list = list->next) {
2654 folderview = (FolderView *)list->data;
2655 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2657 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2658 folderview->color_op;
2664 static gchar *last_smallfont = NULL;
2665 static gchar *last_normalfont = NULL;
2666 static gchar *last_boldfont = NULL;
2667 static gboolean last_derive = 0;
2669 void folderview_reinit_fonts(FolderView *folderview)
2672 g_free(last_smallfont);
2673 last_smallfont = NULL;
2674 g_free(last_normalfont);
2675 last_normalfont = NULL;
2676 g_free(last_boldfont);
2677 last_boldfont = NULL;
2680 void folderview_reflect_prefs(void)
2682 gboolean update_font = FALSE;
2683 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2684 FolderItem *item = folderview_get_selected_item(folderview);
2685 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2686 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2687 gint height = gtk_adjustment_get_value(pos);
2689 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2690 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2691 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2692 last_derive != prefs_common.derive_from_normal_font)
2698 g_free(last_smallfont);
2699 last_smallfont = g_strdup(SMALL_FONT);
2700 g_free(last_normalfont);
2701 last_normalfont = g_strdup(NORMAL_FONT);
2702 g_free(last_boldfont);
2703 last_boldfont = g_strdup(BOLD_FONT);
2704 last_derive = prefs_common.derive_from_normal_font;
2706 normal_style = normal_color_style = bold_style =
2707 bold_color_style = bold_tgtfold_style = NULL;
2709 folderview_init(folderview);
2710 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2711 folderview_column_set_titles(folderview);
2712 folderview_set_all();
2714 g_signal_handlers_block_by_func
2715 (G_OBJECT(folderview->ctree),
2716 G_CALLBACK(folderview_selected), folderview);
2719 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2720 GTK_CMCTREE(folderview->ctree), NULL, item);
2722 folderview_select(folderview, item);
2723 folderview->open_folder = FALSE;
2724 folderview->selected = node;
2727 g_signal_handlers_unblock_by_func
2728 (G_OBJECT(folderview->ctree),
2729 G_CALLBACK(folderview_selected), folderview);
2731 pos = gtk_scrolled_window_get_vadjustment(
2732 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2733 gtk_adjustment_set_value(pos, height);
2734 gtk_adjustment_changed(pos);
2735 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2738 static void drag_state_stop(FolderView *folderview)
2740 if (folderview->drag_timer_id)
2741 g_source_remove(folderview->drag_timer_id);
2742 folderview->drag_timer_id = 0;
2743 folderview->drag_node = NULL;
2746 static gboolean folderview_defer_expand(FolderView *folderview)
2748 if (folderview->drag_node) {
2749 folderview_recollapse_nodes(folderview, folderview->drag_node);
2750 if (folderview->drag_item->collapsed) {
2751 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2752 folderview->nodes_to_recollapse = g_slist_append
2753 (folderview->nodes_to_recollapse, folderview->drag_node);
2756 folderview->drag_item = NULL;
2757 folderview->drag_timer_id = 0;
2761 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2763 /* the idea is that we call drag_state_start() whenever we want expansion to
2764 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2765 * we need to call drag_state_stop() */
2766 drag_state_stop(folderview);
2767 /* request expansion */
2768 if (0 != (folderview->drag_timer_id = g_timeout_add
2769 (prefs_common.hover_timeout,
2770 (GSourceFunc)folderview_defer_expand,
2772 folderview->drag_node = node;
2773 folderview->drag_item = item;
2776 #ifndef GENERIC_UMPC
2777 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2778 FolderView *folderview)
2780 GdkDragContext *context;
2782 cm_return_if_fail(folderview != NULL);
2783 if (folderview->selected == NULL) return;
2784 if (folderview->nodes_to_recollapse)
2785 g_slist_free(folderview->nodes_to_recollapse);
2786 folderview->nodes_to_recollapse = NULL;
2787 context = gtk_drag_begin(widget, folderview->target_list,
2788 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2789 gtk_drag_set_icon_default(context);
2792 static void folderview_drag_data_get(GtkWidget *widget,
2793 GdkDragContext *drag_context,
2794 GtkSelectionData *selection_data,
2797 FolderView *folderview)
2801 gchar *source = NULL;
2802 if (info == TARGET_DUMMY) {
2803 sel = GTK_CMCLIST(folderview->ctree)->selection;
2807 item = gtk_cmctree_node_get_row_data
2808 (GTK_CMCTREE(folderview->ctree),
2809 GTK_CMCTREE_NODE(sel->data));
2811 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2812 gtk_selection_data_set(selection_data,
2813 gtk_selection_data_get_target(selection_data), 8,
2814 source, strlen(source));
2817 g_warning("unknown info %d", info);
2821 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2823 FolderUpdateData *hookdata;
2824 FolderView *folderview;
2828 folderview = (FolderView *) userdata;
2829 cm_return_val_if_fail(hookdata != NULL, FALSE);
2830 cm_return_val_if_fail(folderview != NULL, FALSE);
2832 ctree = folderview->ctree;
2833 cm_return_val_if_fail(ctree != NULL, FALSE);
2835 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2836 folderview_create_folder_node(folderview, hookdata->item);
2837 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2838 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2839 NULL, folder_item_parent(hookdata->item));
2840 folderview_sort_folders(folderview, node, hookdata->folder);
2841 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2842 GtkCMCTreeNode *node;
2844 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2846 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2847 if (folderview->selected == node)
2848 folderview->selected = NULL;
2849 if (folderview->opened == node)
2850 folderview->opened = NULL;
2852 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2853 /* do nothing, it's done by the ADD and REMOVE) */
2854 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2855 folderview_set(folderview);
2860 static gboolean folderview_dnd_scroll_cb(gpointer data)
2862 FolderView *folderview = (FolderView *)data;
2863 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2864 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2865 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2866 gint max = (int)gtk_adjustment_get_upper(pos) -
2867 (int)gtk_adjustment_get_page_size(pos);
2869 if (folderview->scroll_value == 0) {
2870 folderview->scroll_timeout_id = 0;
2874 if (folderview->scroll_value > 0 && new_val > max) {
2876 } else if (folderview->scroll_value < 0 && new_val < 0) {
2879 gtk_adjustment_set_value(pos, new_val);
2884 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2885 GdkDragContext *context,
2889 FolderView *folderview)
2892 FolderItem *item = NULL, *src_item = NULL;
2893 GtkCMCTreeNode *node = NULL;
2894 gboolean acceptable = FALSE;
2895 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2896 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2897 int height = (int)gtk_adjustment_get_page_size(pos);
2898 int total_height = (int)gtk_adjustment_get_upper(pos);
2899 int vpos = (int)gtk_adjustment_get_value(pos);
2900 int offset = prefs_common.show_col_headers ? 24:0;
2903 if (gtk_cmclist_get_selection_info
2904 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2905 GtkWidget *srcwidget;
2907 if (y > height - (48 - offset) && height + vpos < total_height) {
2908 dist = -(height - (48 - offset) - y);
2909 folderview->scroll_value = 1.41f * (1+(dist / 6));
2910 } else if (y < 72 - (24 - offset) && y >= 0) {
2911 dist = 72 - (24 - offset) - y;
2912 folderview->scroll_value = -1.41f * (1+(dist / 6));
2914 folderview->scroll_value = 0;
2916 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2917 folderview->scroll_timeout_id =
2918 g_timeout_add(30, folderview_dnd_scroll_cb,
2922 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2923 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2924 src_item = folderview->summaryview->folder_item;
2926 srcwidget = gtk_drag_get_source_widget(context);
2927 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2928 /* comes from summaryview */
2929 /* we are copying messages, so only accept folder items that are not
2930 the source item, are no root items and can copy messages */
2931 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2932 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2933 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2935 } else if (srcwidget == folderview->ctree) {
2936 /* comes from folderview */
2937 /* we are moving folder items, only accept folders that are not
2938 the source items and can copy messages and create folder items */
2939 if (item && item->folder && src_item && src_item != item &&
2940 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2941 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2942 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2943 || item->folder == src_item->folder))
2946 /* comes from another app */
2947 /* we are adding messages, so only accept folder items that are
2948 no root items and can copy messages */
2949 if (item && item->folder && folder_item_parent(item) != NULL
2950 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2951 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2956 if (acceptable || (src_item && src_item == item))
2957 drag_state_start(folderview, node, item);
2960 g_signal_handlers_block_by_func
2962 G_CALLBACK(folderview_selected), folderview);
2963 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2964 g_signal_handlers_unblock_by_func
2966 G_CALLBACK(folderview_selected), folderview);
2967 gdk_drag_status(context,
2968 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2969 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2971 if (folderview->opened)
2972 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2973 gdk_drag_status(context, 0, time);
2979 static void folderview_drag_leave_cb(GtkWidget *widget,
2980 GdkDragContext *context,
2982 FolderView *folderview)
2984 drag_state_stop(folderview);
2985 folderview->scroll_value = 0;
2986 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2989 static void free_info (gpointer stuff, gpointer data)
2994 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2995 guint time, FolderItem *item)
2998 GSList *msglist = NULL;
2999 list = uri_list_extract_filenames(data);
3000 if (!(item && item->folder && folder_item_parent(item) != NULL
3001 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3003 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3004 debug_print("item doesn't fit\n");
3008 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3009 debug_print("list is empty\n");
3012 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3013 MsgFileInfo *info = NULL;
3015 if (file_is_email((gchar *)tmp->data)) {
3016 info = g_new0(MsgFileInfo, 1);
3017 info->msginfo = NULL;
3018 info->file = (gchar *)tmp->data;
3019 msglist = g_slist_prepend(msglist, info);
3020 debug_print("file is a mail\n");
3022 debug_print("file isn't a mail\n");
3026 msglist = g_slist_reverse(msglist);
3027 folder_item_add_msgs(item, msglist, FALSE);
3028 g_slist_foreach(msglist, free_info, NULL);
3029 g_slist_free(msglist);
3030 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3032 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3034 list_free_strings(list);
3038 static void folderview_drag_received_cb(GtkWidget *widget,
3039 GdkDragContext *drag_context,
3042 GtkSelectionData *data,
3045 FolderView *folderview)
3048 FolderItem *item = NULL, *src_item;
3049 GtkCMCTreeNode *node;
3050 int offset = prefs_common.show_col_headers ? 24:0;
3052 folderview->scroll_value = 0;
3054 if (info == TARGET_DUMMY) {
3055 drag_state_stop(folderview);
3056 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3057 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3058 /* comes from summaryview */
3059 if (gtk_cmclist_get_selection_info
3060 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3063 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3064 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3065 src_item = folderview->summaryview->folder_item;
3067 if (item->no_select) {
3068 alertpanel_error(_("The destination folder can only be used to "
3069 "store subfolders."));
3072 /* re-check (due to acceptable possibly set for folder moves */
3073 if (!(item && item->folder && item->path && !item->no_select &&
3074 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3077 if (item && src_item) {
3078 switch (gdk_drag_context_get_selected_action(drag_context)) {
3079 case GDK_ACTION_COPY:
3080 summary_copy_selected_to(folderview->summaryview, item);
3081 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3083 case GDK_ACTION_MOVE:
3084 case GDK_ACTION_DEFAULT:
3086 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3087 summary_copy_selected_to(folderview->summaryview, item);
3089 summary_move_selected_to(folderview->summaryview, item);
3090 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3093 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3095 /* comes from folderview */
3097 gboolean folder_is_normal = TRUE;
3098 gboolean copy = (GDK_ACTION_COPY ==
3099 gdk_drag_context_get_selected_action(drag_context));
3101 source = (char *)gtk_selection_data_get_data(data) + 17;
3102 if (gtk_cmclist_get_selection_info
3103 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3105 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3108 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3109 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3110 src_item = folder_find_item_from_identifier(source);
3114 src_item->stype == F_NORMAL &&
3115 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3116 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3117 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3118 !folder_has_parent_of_type(src_item, F_TRASH);
3119 if (!item || !src_item || !folder_is_normal) {
3120 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3124 folderview_move_folder(folderview, src_item, item, copy);
3125 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3127 folderview->nodes_to_recollapse = NULL;
3128 } else if (info == TARGET_MAIL_URI_LIST) {
3129 if (gtk_cmclist_get_selection_info
3130 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3133 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3135 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3136 debug_print("no node\n");
3139 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3141 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3142 debug_print("no item\n");
3145 folderview_finish_dnd(gtk_selection_data_get_data(data),
3146 drag_context, time, item);
3150 static void folderview_drag_end_cb(GtkWidget *widget,
3151 GdkDragContext *drag_context,
3152 FolderView *folderview)
3154 drag_state_stop(folderview);
3155 folderview->scroll_value = 0;
3156 g_slist_free(folderview->nodes_to_recollapse);
3157 folderview->nodes_to_recollapse = NULL;
3160 void folderview_register_popup(FolderViewPopup *fpopup)
3164 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3165 FolderView *folderview = folderviews->data;
3166 GtkActionGroup *factory;
3168 factory = create_action_group(folderview, fpopup);
3169 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3171 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3174 void folderview_unregister_popup(FolderViewPopup *fpopup)
3179 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3180 FolderView *folderview = folderviews->data;
3182 g_hash_table_remove(folderview->popups, fpopup->klass);
3184 g_hash_table_remove(folderview_popups, fpopup->klass);
3187 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3189 g_return_if_fail(folderview != NULL);
3190 g_return_if_fail(item != NULL);
3192 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3193 g_return_if_fail(ctree != NULL);
3195 GtkCMCTreeNode *node =
3196 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3197 g_return_if_fail(node != NULL);
3199 gtk_cmctree_remove_node(ctree, node);
3202 void folderview_freeze(FolderView *folderview)
3205 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3208 void folderview_thaw(FolderView *folderview)
3211 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3214 void folderview_grab_focus(FolderView *folderview)
3217 gtk_widget_grab_focus(folderview->ctree);