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"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_cb (GtkAction *action,
169 static void folderview_empty_trash_cb (GtkAction *action,
172 static void folderview_send_queue_cb (GtkAction *action,
175 static void folderview_search_cb (GtkAction *action,
177 static void folderview_run_processing_cb(GtkAction *action,
180 static void folderview_property_cb (GtkAction *action,
183 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
184 GdkDragContext *context,
188 FolderView *folderview);
189 static void folderview_drag_leave_cb (GtkWidget *widget,
190 GdkDragContext *context,
192 FolderView *folderview);
193 static void folderview_drag_received_cb (GtkWidget *widget,
194 GdkDragContext *drag_context,
197 GtkSelectionData *data,
200 FolderView *folderview);
202 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
203 FolderView *folderview);
205 static void folderview_drag_data_get (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 GtkSelectionData *selection_data,
210 FolderView *folderview);
211 static void folderview_drag_end_cb (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 FolderView *folderview);
215 static void folderview_create_folder_node (FolderView *folderview,
217 static gboolean folderview_update_folder (gpointer source,
219 static gboolean folderview_update_item_claws (gpointer source,
221 static void folderview_processing_cb(GtkAction *action, gpointer data);
222 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
223 GdkEventButton *event);
225 GHashTable *folderview_popups;
227 static GtkActionEntry folderview_common_popup_entries[] =
229 {"FolderViewPopup", NULL, "FolderViewPopup" },
230 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
231 {"FolderViewPopup/---", NULL, "---" },
232 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
233 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
234 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
235 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
236 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
237 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
241 GtkTargetEntry folderview_drag_types[] =
243 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
244 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
247 void folderview_initialize(void)
249 FolderViewPopup *fpopup;
251 fpopup = g_new0(FolderViewPopup, 1);
253 fpopup->klass = "common";
254 fpopup->path = "<CommonFolder>";
255 fpopup->entries = folderview_common_popup_entries;
256 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
257 fpopup->set_sensitivity = NULL;
259 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
260 g_hash_table_insert(folderview_popups, "common", fpopup);
263 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
265 FolderViewPopup *fpopup_common;
266 GtkActionGroup *action_group;
268 action_group = cm_menu_create_action_group(
270 fpopup->entries, fpopup->n_entries,
271 (gpointer)folderview);
273 if (fpopup->toggle_entries)
274 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
275 fpopup->n_toggle_entries,
276 (gpointer)folderview);
277 if (fpopup->radio_entries)
278 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
279 fpopup->n_radio_entries, fpopup->radio_default,
280 G_CALLBACK(fpopup->radio_callback),
281 (gpointer)folderview);
283 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
284 if (fpopup_common != fpopup) {
285 gtk_action_group_add_actions(action_group, fpopup_common->entries,
286 fpopup_common->n_entries,
287 (gpointer)folderview);
288 if (fpopup_common->toggle_entries)
289 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
290 fpopup_common->n_toggle_entries,
291 (gpointer)folderview);
292 if (fpopup_common->radio_entries)
293 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
294 fpopup_common->n_radio_entries, fpopup_common->radio_default,
295 G_CALLBACK(fpopup_common->radio_callback),
296 (gpointer)folderview);
302 static void create_action_groups(gpointer key, gpointer value, gpointer data)
304 FolderView *folderview = data;
305 FolderViewPopup *fpopup = value;
306 GtkActionGroup *group;
308 group = create_action_group(folderview, fpopup);
309 g_hash_table_insert(folderview->popups, fpopup->klass, group);
312 static void folderview_column_set_titles(FolderView *folderview)
314 GtkWidget *ctree = folderview->ctree;
315 GtkWidget *label_folder;
316 GtkWidget *label_new;
317 GtkWidget *label_unread;
318 GtkWidget *label_total;
319 GtkWidget *hbox_folder;
321 GtkWidget *hbox_unread;
322 GtkWidget *hbox_total;
323 gint *col_pos = folderview->col_pos;
325 debug_print("setting titles...\n");
326 gtk_widget_realize(folderview->ctree);
327 gtk_widget_show_all(folderview->scrolledwin);
329 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
330 * instead text (text overflows making them unreadable and ugly) */
331 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
333 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
335 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
338 label_folder = gtk_label_new(_("Folder"));
339 label_new = gtk_image_new_from_pixbuf(newxpm);
340 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
341 label_total = gtk_image_new_from_pixbuf(readxpm);
343 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
345 hbox_folder = gtk_hbox_new(FALSE, 4);
346 hbox_new = gtk_hbox_new(FALSE, 4);
347 hbox_unread = gtk_hbox_new(FALSE, 4);
348 hbox_total = gtk_hbox_new(FALSE, 4);
351 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
352 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
353 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
354 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
355 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
360 gtk_widget_show_all(hbox_folder);
361 gtk_widget_show_all(hbox_new);
362 gtk_widget_show_all(hbox_unread);
363 gtk_widget_show_all(hbox_total);
366 gtk_widget_set_size_request(hbox_new, -1, 20);
367 gtk_widget_set_size_request(hbox_unread, -1, 20);
368 gtk_widget_set_size_request(hbox_total, -1, 20);
371 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
372 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
373 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
374 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
381 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
382 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
385 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
387 FolderView *folderview = (FolderView *)data;
388 GdkEventButton event;
389 if (folderview_get_selected_item(folderview) == NULL)
393 event.time = gtk_get_current_event_time();
395 folderview_set_sens_and_popup_menu(folderview, -1,
402 static GtkWidget *folderview_ctree_create(FolderView *folderview)
406 FolderColumnState *col_state;
407 FolderColumnType type;
408 gchar *titles[N_FOLDER_COLS];
410 GtkWidget *scrolledwin = folderview->scrolledwin;
412 debug_print("creating tree...\n");
413 memset(titles, 0, sizeof(titles));
415 col_state = prefs_folder_column_get_config();
416 memset(titles, 0, sizeof(titles));
418 col_pos = folderview->col_pos;
420 for (i = 0; i < N_FOLDER_COLS; i++) {
421 folderview->col_state[i] = col_state[i];
422 type = col_state[i].type;
426 titles[col_pos[F_COL_FOLDER]] = _("Folder");
427 titles[col_pos[F_COL_NEW]] = _("New");
428 titles[col_pos[F_COL_UNREAD]] = _("Unread");
429 /* TRANSLATORS: This in Number sign in American style */
430 titles[col_pos[F_COL_TOTAL]] = _("#");
432 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
435 if (prefs_common.show_col_headers == FALSE)
436 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
439 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
440 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
442 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
443 col_pos[F_COL_UNREAD],
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
446 col_pos[F_COL_TOTAL],
448 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
449 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
450 GTK_CMCTREE_EXPANDER_TRIANGLE);
452 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
453 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
455 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
456 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
458 /* don't let title buttons take key focus */
459 for (i = 0; i < N_FOLDER_COLS; i++) {
460 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
461 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
462 prefs_common.folder_col_size[i]);
463 gtk_cmclist_set_column_visibility
464 (GTK_CMCLIST(ctree), i, col_state[i].visible);
467 g_signal_connect(G_OBJECT(ctree), "key_press_event",
468 G_CALLBACK(folderview_key_pressed),
470 g_signal_connect(G_OBJECT(ctree), "button_press_event",
471 G_CALLBACK(folderview_button_pressed),
473 g_signal_connect(G_OBJECT(ctree), "popup-menu",
474 G_CALLBACK(folderview_popup_menu), folderview);
475 g_signal_connect(G_OBJECT(ctree), "button_release_event",
476 G_CALLBACK(folderview_button_released),
478 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
479 G_CALLBACK(folderview_selected), folderview);
481 /* drag-n-dropping folders on maemo is impractical as this
482 * opens the folder almost everytime */
483 g_signal_connect(G_OBJECT(ctree), "start_drag",
484 G_CALLBACK(folderview_start_drag), folderview);
486 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
487 G_CALLBACK(folderview_drag_data_get),
490 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
491 G_CALLBACK(folderview_tree_expanded),
493 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
494 G_CALLBACK(folderview_tree_collapsed),
497 g_signal_connect(G_OBJECT(ctree), "resize_column",
498 G_CALLBACK(folderview_col_resized),
502 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
503 folderview_drag_types, 2,
504 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
505 g_signal_connect(G_OBJECT(ctree), "drag_motion",
506 G_CALLBACK(folderview_drag_motion_cb),
508 g_signal_connect(G_OBJECT(ctree), "drag_leave",
509 G_CALLBACK(folderview_drag_leave_cb),
511 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
512 G_CALLBACK(folderview_drag_received_cb),
514 g_signal_connect(G_OBJECT(ctree), "drag_end",
515 G_CALLBACK(folderview_drag_end_cb),
518 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
523 void folderview_set_column_order(FolderView *folderview)
525 GtkWidget *ctree = folderview->ctree;
526 FolderItem *item = folderview_get_selected_item(folderview);
527 FolderItem *sel_item = NULL, *op_item = NULL;
528 GtkWidget *scrolledwin = folderview->scrolledwin;
530 if (folderview->selected)
531 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
532 if (folderview->opened)
533 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
535 debug_print("recreating tree...\n");
536 gtk_widget_destroy(folderview->ctree);
539 folderview->ctree = ctree = folderview_ctree_create(folderview);
540 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
541 GTK_CMCLIST(ctree)->hadjustment);
542 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
543 GTK_CMCLIST(ctree)->vadjustment);
544 gtk_widget_show(ctree);
547 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
549 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
551 folderview_set(folderview);
552 folderview_column_set_titles(folderview);
554 folderview_select(folderview,item);
557 FolderView *folderview_create(void)
559 FolderView *folderview;
560 GtkWidget *scrolledwin;
563 debug_print("Creating folder view...\n");
564 folderview = g_new0(FolderView, 1);
566 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
567 gtk_scrolled_window_set_policy
568 (GTK_SCROLLED_WINDOW(scrolledwin),
569 GTK_POLICY_AUTOMATIC,
570 prefs_common.folderview_vscrollbar_policy);
571 gtk_widget_set_size_request(scrolledwin,
572 prefs_common.folderview_width,
573 prefs_common.folderview_height);
575 folderview->scrolledwin = scrolledwin;
576 ctree = folderview_ctree_create(folderview);
578 /* create popup factories */
579 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
580 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
582 folderview->ctree = ctree;
584 folderview->folder_update_callback_id =
585 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
586 folderview->folder_item_update_callback_id =
587 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
589 gtk_widget_show_all(scrolledwin);
591 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
592 folderview_list = g_list_append(folderview_list, folderview);
593 folderview->deferred_refresh_id = -1;
594 folderview->scroll_timeout_id = -1;
598 void folderview_init(FolderView *folderview)
600 GtkWidget *ctree = folderview->ctree;
603 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
604 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
605 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
606 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
607 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
608 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
609 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
610 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
611 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
612 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
613 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
614 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
615 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
616 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
617 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
618 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
619 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
620 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
621 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
622 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
623 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
651 PangoFontDescription *font_desc;
652 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
653 font_desc = pango_font_description_from_string(NORMAL_FONT);
655 if (normal_style->font_desc)
656 pango_font_description_free
657 (normal_style->font_desc);
658 normal_style->font_desc = font_desc;
660 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
661 normal_color_style = gtk_style_copy(normal_style);
662 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
664 gtk_widget_set_style(ctree, normal_style);
668 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
669 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
670 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
671 pango_font_description_set_weight
672 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
674 PangoFontDescription *font_desc;
675 font_desc = pango_font_description_from_string(BOLD_FONT);
677 if (bold_style->font_desc)
678 pango_font_description_free
679 (bold_style->font_desc);
680 bold_style->font_desc = font_desc;
683 bold_color_style = gtk_style_copy(bold_style);
684 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
686 bold_tgtfold_style = gtk_style_copy(bold_style);
687 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
691 static gboolean folderview_defer_set(gpointer data)
693 FolderView *folderview = (FolderView *)data;
694 MainWindow *mainwin = folderview->mainwin;
698 if (mainwin->lock_count)
701 debug_print("doing deferred folderview_set now\n");
702 folderview_set(folderview);
704 folderview->deferred_refresh_id = -1;
708 void folderview_set(FolderView *folderview)
710 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
711 MainWindow *mainwin = folderview->mainwin;
712 FolderItem *sel_item = NULL, *op_item = NULL;
717 if (mainwin->lock_count) {
718 if (folderview->deferred_refresh_id == -1)
719 folderview->deferred_refresh_id =
720 g_timeout_add(500, folderview_defer_set, folderview);
721 debug_print("deferred folderview_set\n");
726 debug_print("Setting folder info...\n");
727 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
729 main_window_cursor_wait(mainwin);
731 if (folderview->selected)
732 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
733 if (folderview->opened)
734 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
736 folderview->selected = NULL;
737 folderview->opened = NULL;
739 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
740 gtk_cmclist_clear(GTK_CMCLIST(ctree));
742 folderview_set_folders(folderview);
745 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
747 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
749 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
750 main_window_cursor_normal(mainwin);
751 STATUSBAR_POP(mainwin);
755 void folderview_set_all(void)
759 for (list = folderview_list; list != NULL; list = list->next)
760 folderview_set((FolderView *)list->data);
763 void folderview_select(FolderView *folderview, FolderItem *item)
765 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
766 GtkCMCTreeNode *node;
767 GtkCMCTreeNode *old_selected = folderview->selected;
771 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
772 if (node) folderview_select_node(folderview, node);
774 if (old_selected != node)
775 folder_update_op_count();
778 static void mark_all_read_cb(GtkAction *action, gpointer data)
780 FolderView *folderview = (FolderView *)data;
784 item = folderview_get_selected_item(folderview);
788 if (folderview->summaryview->folder_item != item
789 && prefs_common.ask_mark_all_read) {
790 val = alertpanel_full(_("Mark all as read"),
791 _("Do you really want to mark all mails in this "
792 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
793 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
795 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
797 else if (val & G_ALERTDISABLE)
798 prefs_common.ask_mark_all_read = FALSE;
802 folder_item_update_freeze();
803 if (folderview->summaryview->folder_item != item)
804 summary_lock(folderview->summaryview);
806 summary_freeze(folderview->summaryview);
808 folderutils_mark_all_read(item);
810 if (folderview->summaryview->folder_item != item)
811 summary_unlock(folderview->summaryview);
813 summary_thaw(folderview->summaryview);
814 folder_item_update_thaw();
817 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
819 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
821 cm_return_if_fail(node != NULL);
823 if (folderview->open_folder) {
827 folderview->open_folder = TRUE;
828 gtkut_ctree_set_focus_row(ctree, node);
829 gtk_cmctree_select(ctree, node);
830 if ((folderview->summaryview->folder_item &&
831 folderview->summaryview->folder_item->total_msgs > 0) ||
832 prefs_common.layout_mode == SMALL_LAYOUT)
833 summary_grab_focus(folderview->summaryview);
835 gtk_widget_grab_focus(folderview->ctree);
837 gtkut_ctree_expand_parent_all(ctree, node);
840 void folderview_unselect(FolderView *folderview)
842 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
844 (GTK_CMCTREE(folderview->ctree), folderview->opened);
846 folderview->selected = folderview->opened = NULL;
849 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
850 GtkCMCTreeNode *node)
855 node = gtkut_ctree_node_next(ctree, node);
857 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
859 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
860 item = gtk_cmctree_node_get_row_data(ctree, node);
861 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
868 void folderview_select_next_marked(FolderView *folderview)
870 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
871 GtkCMCTreeNode *node = NULL;
872 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
873 gboolean last_open = prefs_common.always_show_msg;
875 prefs_common.summary_select_prio[0] = ACTION_MARKED;
876 prefs_common.always_show_msg = OPENMSG_ALWAYS;
878 if ((node = folderview_find_next_marked(ctree, folderview->opened))
880 folderview_select_node(folderview, node);
884 if (!folderview->opened ||
885 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
888 /* search again from the first node */
889 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
890 folderview_select_node(folderview, node);
893 prefs_common.summary_select_prio[0] = last_summary_select_prio;
894 prefs_common.always_show_msg = last_open;
897 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
898 GtkCMCTreeNode *node)
903 node = gtkut_ctree_node_next(ctree, node);
905 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
907 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
908 item = gtk_cmctree_node_get_row_data(ctree, node);
909 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
916 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
918 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
919 GtkCMCTreeNode *node = NULL;
920 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
921 gboolean last_open = prefs_common.always_show_msg;
923 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
924 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
926 if ((node = folderview_find_next_unread(ctree, folderview->opened))
928 folderview_select_node(folderview, node);
932 if (!folderview->opened ||
933 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
936 /* search again from the first node */
937 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
938 folderview_select_node(folderview, node);
941 prefs_common.summary_select_prio[0] = last_summary_select_prio;
942 prefs_common.always_show_msg = last_open;
945 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
946 GtkCMCTreeNode *node)
951 node = gtkut_ctree_node_next(ctree, node);
953 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
955 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
956 item = gtk_cmctree_node_get_row_data(ctree, node);
957 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
964 void folderview_select_next_new(FolderView *folderview)
966 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
967 GtkCMCTreeNode *node = NULL;
968 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
969 gboolean last_open = prefs_common.always_show_msg;
971 prefs_common.summary_select_prio[0] = ACTION_NEW;
972 prefs_common.always_show_msg = OPENMSG_ALWAYS;
974 if ((node = folderview_find_next_new(ctree, folderview->opened))
976 folderview_select_node(folderview, node);
980 if (!folderview->opened ||
981 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
984 /* search again from the first node */
985 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
986 folderview_select_node(folderview, node);
989 prefs_common.summary_select_prio[0] = last_summary_select_prio;
990 prefs_common.always_show_msg = last_open;
993 FolderItem *folderview_get_selected_item(FolderView *folderview)
995 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
997 if (!folderview->selected) return NULL;
998 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1001 static void folderview_set_folders(FolderView *folderview)
1004 list = folder_get_list();
1006 for (; list != NULL; list = list->next) {
1007 folderview_append_folder(folderview, FOLDER(list->data));
1011 static gchar *get_scan_str(FolderItem *item)
1014 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1015 item->folder->name, G_DIR_SEPARATOR,
1018 return g_strdup_printf(_("Scanning folder %s..."),
1019 item->folder->name);
1021 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1025 for (list = folderview_list; list != NULL; list = list->next) {
1026 FolderView *folderview = (FolderView *)list->data;
1027 MainWindow *mainwin = folderview->mainwin;
1028 gchar *str = get_scan_str(item);
1030 STATUSBAR_PUSH(mainwin, str);
1031 STATUSBAR_POP(mainwin);
1036 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1039 MainWindow *mainwin = mainwindow_get_mainwindow();
1040 FolderView *folderview = NULL;
1041 GtkAdjustment *pos = NULL;
1044 cm_return_if_fail(folder != NULL);
1046 if (!folder->klass->scan_tree) return;
1049 alertpanel_full(_("Rebuild folder tree"),
1050 _("Rebuilding the folder tree will remove "
1051 "local caches. Do you want to continue?"),
1052 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1053 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1054 != G_ALERTALTERNATE) {
1060 window = label_window_create(_("Rebuilding folder tree..."));
1062 window = label_window_create(_("Scanning folder tree..."));
1065 folderview = mainwin->folderview;
1068 pos = gtk_scrolled_window_get_vadjustment(
1069 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1070 height = gtk_adjustment_get_value(pos);
1073 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1074 folder_scan_tree(folder, rebuild);
1075 folder_set_ui_func(folder, NULL, NULL);
1077 folderview_set_all();
1080 pos = gtk_scrolled_window_get_vadjustment(
1081 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1082 gtk_adjustment_set_value(pos, height);
1083 gtk_adjustment_changed(pos);
1085 label_window_destroy(window);
1089 /** folderview_check_new()
1090 * Scan and update the folder and return the
1091 * count the number of new messages since last check.
1092 * \param folder the folder to check for new messages
1093 * \return the number of new messages since last check
1095 gint folderview_check_new(Folder *folder)
1099 FolderView *folderview;
1101 GtkCMCTreeNode *node;
1103 gint former_new_msgs = 0;
1104 gint former_new = 0, former_unread = 0, former_total;
1106 for (list = folderview_list; list != NULL; list = list->next) {
1107 folderview = (FolderView *)list->data;
1108 ctree = GTK_CMCTREE(folderview->ctree);
1109 folderview->scanning_folder = folder;
1111 main_window_lock(folderview->mainwin);
1113 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1114 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1116 item = gtk_cmctree_node_get_row_data(ctree, node);
1117 if (!item || !item->path || !item->folder) continue;
1118 if (item->no_select) continue;
1119 if (folder && folder != item->folder) continue;
1120 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1121 if (!item->prefs->newmailcheck) continue;
1122 if (item->processing_pending == TRUE) {
1123 debug_print("skipping %s, processing pending\n",
1124 item->path ? item->path : item->name);
1127 if (item->scanning != ITEM_NOT_SCANNING) {
1128 debug_print("skipping %s, scanning\n",
1129 item->path ? item->path : item->name);
1133 str = get_scan_str(item);
1135 STATUSBAR_PUSH(folderview->mainwin, str);
1139 folderview_scan_tree_func(item->folder, item, NULL);
1140 former_new = item->new_msgs;
1141 former_unread = item->unread_msgs;
1142 former_total = item->total_msgs;
1144 if (item->folder->klass->scan_required &&
1145 (item->folder->klass->scan_required(item->folder, item) ||
1146 item->folder->inbox == item ||
1147 item->opened == TRUE ||
1148 item->processing_pending == TRUE)) {
1149 if (folder_item_scan(item) < 0) {
1151 summaryview_unlock(folderview->summaryview, item);
1152 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1153 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1154 item->path ? item->path:item->name);
1155 STATUSBAR_POP(folderview->mainwin);
1157 } else if (!FOLDER_IS_LOCAL(folder)) {
1158 STATUSBAR_POP(folderview->mainwin);
1163 } else if (!item->folder->klass->scan_required) {
1164 if (folder_item_scan(item) < 0) {
1165 summaryview_unlock(folderview->summaryview, item);
1166 if (folder && !FOLDER_IS_LOCAL(folder)) {
1167 STATUSBAR_POP(folderview->mainwin);
1172 if (former_new != item->new_msgs ||
1173 former_unread != item->unread_msgs ||
1174 former_total != item->total_msgs)
1175 folderview_update_node(folderview, node);
1177 new_msgs += item->new_msgs;
1178 former_new_msgs += former_new;
1179 STATUSBAR_POP(folderview->mainwin);
1181 folderview->scanning_folder = NULL;
1182 main_window_unlock(folderview->mainwin);
1186 folder_write_list();
1187 /* Number of new messages since last check is the just the difference
1188 * between former_new_msgs and new_msgs. If new_msgs is less than
1189 * former_new_msgs, that would mean another session accessed the folder
1190 * and the result is not well defined.
1192 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1196 void folderview_check_new_all(void)
1200 FolderView *folderview;
1202 folderview = (FolderView *)folderview_list->data;
1205 main_window_lock(folderview->mainwin);
1206 window = label_window_create
1207 (_("Checking for new messages in all folders..."));
1209 list = folder_get_list();
1210 for (; list != NULL; list = list->next) {
1211 Folder *folder = list->data;
1213 folderview_check_new(folder);
1216 folder_write_list();
1217 folderview_set_all();
1219 label_window_destroy(window);
1220 main_window_unlock(folderview->mainwin);
1224 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1230 if (!item || !item->folder || !item->folder->node)
1233 node = item->folder->node;
1235 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1236 node = node->children;
1239 (item->new_msgs > 0 ||
1240 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1244 while (node != NULL) {
1245 if (node && node->data) {
1246 FolderItem *next_item = (FolderItem*) node->data;
1248 if (folderview_have_new_children_sub(folderview,
1257 static gboolean folderview_have_new_children(FolderView *folderview,
1260 return folderview_have_new_children_sub(folderview, item, FALSE);
1263 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1269 if (!item || !item->folder || !item->folder->node)
1272 node = item->folder->node;
1274 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1275 node = node->children;
1278 (item->unread_msgs > 0 ||
1279 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1283 while (node != NULL) {
1284 if (node && node->data) {
1285 FolderItem *next_item = (FolderItem*) node->data;
1287 if (folderview_have_unread_children_sub(folderview,
1297 static gboolean folderview_have_unread_children(FolderView *folderview,
1300 return folderview_have_unread_children_sub(folderview, item, FALSE);
1303 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1309 if (!item || !item->folder || !item->folder->node)
1312 node = item->folder->node;
1314 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1315 node = node->children;
1317 if (in_sub && item->search_match){
1321 while (node != NULL) {
1322 if (node && node->data) {
1323 FolderItem *next_item = (FolderItem*) node->data;
1325 if (folderview_have_matching_children_sub(folderview,
1335 static gboolean folderview_have_matching_children(FolderView *folderview,
1338 return folderview_have_matching_children_sub(folderview, item, FALSE);
1341 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1347 if (!item || !item->folder || !item->folder->node)
1350 node = item->folder->node;
1352 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1353 node = node->children;
1355 if (item->marked_msgs != 0) {
1359 while (node != NULL) {
1360 if (node && node->data) {
1361 FolderItem *next_item = (FolderItem*) node->data;
1363 if (folderview_have_marked_children_sub(folderview,
1372 static gboolean folderview_have_marked_children(FolderView *folderview,
1375 return folderview_have_marked_children_sub(folderview, item, FALSE);
1378 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1380 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1381 GtkStyle *style = NULL;
1382 GtkStyle *color_style = NULL;
1384 GdkPixbuf *xpm, *openxpm;
1385 static GdkPixbuf *searchicon;
1386 gboolean mark = FALSE;
1389 gboolean add_unread_mark;
1390 gboolean add_sub_match_mark;
1391 gboolean use_bold, use_color;
1392 gint *col_pos = folderview->col_pos;
1393 SpecialFolderItemType stype;
1395 item = gtk_cmctree_node_get_row_data(ctree, node);
1396 cm_return_if_fail(item != NULL);
1398 if (!GTK_CMCTREE_ROW(node)->expanded)
1399 mark = folderview_have_marked_children(folderview, item);
1401 mark = (item->marked_msgs != 0);
1403 stype = item->stype;
1404 if (stype == F_NORMAL) {
1405 if (folder_has_parent_of_type(item, F_TRASH))
1407 else if (folder_has_parent_of_type(item, F_DRAFT))
1409 else if (folder_has_parent_of_type(item, F_OUTBOX))
1411 else if (folder_has_parent_of_type(item, F_QUEUE))
1416 if (item->hide_read_msgs || item->hide_read_threads) {
1417 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1418 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1420 xpm = mark?m_inboxxpm:inboxxpm;
1421 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1425 if (item->hide_read_msgs || item->hide_read_threads) {
1426 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1427 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1429 xpm = mark?m_outboxxpm:outboxxpm;
1430 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1434 if (item->hide_read_msgs || item->hide_read_threads) {
1435 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1436 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1438 xpm = mark?m_queuexpm:queuexpm;
1439 openxpm = mark?m_queueopenxpm:queueopenxpm;
1443 if (item->hide_read_msgs || item->hide_read_threads) {
1444 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1445 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1447 xpm = mark?m_trashxpm:trashxpm;
1448 openxpm = mark?m_trashopenxpm:trashopenxpm;
1452 xpm = mark?m_draftsxpm:draftsxpm;
1453 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1456 if (item->hide_read_msgs || item->hide_read_threads) {
1457 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1458 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1460 xpm = mark?m_folderxpm:folderxpm;
1461 openxpm = mark?m_folderopenxpm:folderopenxpm;
1465 if (item->no_select) {
1466 xpm = openxpm = noselectxpm;
1469 name = folder_item_get_name(item);
1471 if (!GTK_CMCTREE_ROW(node)->expanded) {
1472 add_unread_mark = folderview_have_unread_children(
1474 add_sub_match_mark = folderview_have_matching_children(
1477 add_unread_mark = FALSE;
1478 add_sub_match_mark = FALSE;
1481 if (item->search_match) {
1483 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1486 xpm = openxpm = searchicon;
1490 if (prefs_common.display_folder_unread) {
1491 if (folder_has_parent_of_type(item, F_QUEUE)) {
1492 /* only total_msgs matters here */
1493 if (item->total_msgs > 0) {
1494 /* show total number (should be equal to the unread number)
1496 str = g_strdup_printf("%s (%d%s%s)",
1497 name, item->total_msgs,
1498 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1499 (item->unreadmarked_msgs > 0) ? "!" : "");
1502 if (prefs_common.display_folder_unread == 1) {
1503 if (item->unread_msgs > 0) {
1504 /* show unread number and signs */
1505 str = g_strdup_printf("%s (%d%s%s)",
1506 name, item->unread_msgs,
1507 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1508 (item->unreadmarked_msgs > 0) ? "!" : "");
1511 if (item->total_msgs > 0) {
1512 /* show unread number, total number and signs if any */
1513 str = g_strdup_printf("%s (%d/%d%s%s)",
1514 name, item->unread_msgs, item->total_msgs,
1515 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1516 (item->unreadmarked_msgs > 0) ? "!" : "");
1520 if ((str == NULL) &&
1521 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1522 /* no unread/total numbers, but at least one sign */
1523 str = g_strdup_printf("%s (%s%s)",
1525 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1526 (item->unreadmarked_msgs > 0) ? "!" : "");
1530 /* last fallback, folder name only or with +! sign */
1531 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1532 str = g_strdup_printf("%s%s",
1534 } else if (item->unreadmarked_msgs > 0) {
1535 str = g_strdup_printf("%s%s",
1537 } else if (add_sub_match_mark) {
1538 str = g_strdup_printf("%s%s",
1541 str = g_strdup_printf("%s", name);
1544 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1546 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1550 if (!folder_item_parent(item)) {
1551 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1552 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1553 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1555 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1556 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1557 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1560 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1561 folder_has_parent_of_type(item, F_TRASH)) {
1562 use_bold = use_color = FALSE;
1563 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1564 GSList *list = folder_item_get_msg_list(item);
1566 use_bold = use_color = FALSE;
1567 for (cur = list; cur; cur = cur->next) {
1568 MsgInfo *msginfo = (MsgInfo *)cur->data;
1569 if (!MSG_IS_DELETED(msginfo->flags)) {
1570 /* highlight queue folder if there are any messages */
1571 use_bold = use_color = TRUE;
1575 procmsg_msg_list_free(list);
1577 /* if unread messages exist, print with bold font */
1578 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1580 /* if new messages exist, print with colored letter */
1582 (item->new_msgs > 0) ||
1584 folderview_have_new_children(folderview, item));
1587 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1592 if (item->prefs->color > 0 && !use_color) {
1593 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1594 color_style = gtk_style_copy(bold_style);
1595 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1596 style = color_style;
1597 } else if (use_color) {
1598 style = bold_color_style;
1601 if (item->op_count > 0) {
1602 style = bold_tgtfold_style;
1604 } else if (use_color) {
1605 style = normal_color_style;
1606 gtk_cmctree_node_set_foreground(ctree, node,
1607 &folderview->color_new);
1608 } else if (item->op_count > 0) {
1609 style = bold_tgtfold_style;
1610 } else if (item->prefs->color > 0) {
1612 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1613 color_style = gtk_style_copy(normal_style);
1614 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1615 style = color_style;
1617 style = normal_style;
1620 gtk_cmctree_node_set_row_style(ctree, node, style);
1622 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1623 folderview_update_node(folderview, node);
1626 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1629 FolderView *folderview;
1631 GtkCMCTreeNode *node;
1633 cm_return_if_fail(item != NULL);
1635 for (list = folderview_list; list != NULL; list = list->next) {
1636 folderview = (FolderView *)list->data;
1637 ctree = GTK_CMCTREE(folderview->ctree);
1639 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1640 if (node && item->search_match != matches) {
1641 item->search_match = matches;
1642 folderview_update_node(folderview, node);
1647 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1649 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1650 FolderView *folderview = (FolderView *)data;
1652 GtkCMCTreeNode *node;
1653 cm_return_val_if_fail(update_info != NULL, TRUE);
1654 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1655 cm_return_val_if_fail(folderview != NULL, FALSE);
1657 ctree = GTK_CMCTREE(folderview->ctree);
1659 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1662 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1663 folderview_update_node(folderview, node);
1665 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1666 update_info->item == folderview->summaryview->folder_item &&
1667 update_info->item != NULL)
1668 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1669 summary_show(folderview->summaryview, update_info->item);
1675 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1676 GNode *gnode, GtkCMCTreeNode *cnode,
1679 FolderView *folderview = (FolderView *)data;
1680 FolderItem *item = FOLDER_ITEM(gnode->data);
1682 cm_return_val_if_fail(item != NULL, FALSE);
1684 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1685 folderview_update_node(folderview, cnode);
1690 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1693 FolderView *folderview = (FolderView *)data;
1696 if (GTK_CMCTREE_ROW(node)->children) {
1697 item = gtk_cmctree_node_get_row_data(ctree, node);
1698 cm_return_if_fail(item != NULL);
1700 if (!item->collapsed)
1701 gtk_cmctree_expand(ctree, node);
1703 folderview_update_node(folderview, node);
1707 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1708 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1711 GtkCMCTreeNode *node, *parent, *sibling;
1713 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1715 g_warning("%s not found.\n", item->path);
1717 parent = GTK_CMCTREE_ROW(node)->parent;
1718 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1719 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1721 sibling = GTK_CMCTREE_ROW(parent)->children;
1725 tmp = gtk_cmctree_node_get_row_data
1727 if (tmp->stype != F_NORMAL)
1728 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1732 if (node != sibling)
1733 gtk_cmctree_move(ctree, node, parent, sibling);
1740 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1743 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1744 GtkCMCTreeNode *prev = NULL;
1746 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1747 gtk_sctree_sort_recursive(ctree, root);
1748 if (root && GTK_CMCTREE_ROW(root)->parent) {
1749 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1752 set_special_folder(ctree, folder->inbox, root, &prev);
1753 set_special_folder(ctree, folder->outbox, root, &prev);
1754 set_special_folder(ctree, folder->draft, root, &prev);
1755 set_special_folder(ctree, folder->queue, root, &prev);
1756 set_special_folder(ctree, folder->trash, root, &prev);
1757 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1760 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1762 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1763 GtkCMCTreeNode *root;
1765 cm_return_if_fail(folder != NULL);
1767 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1768 folderview_gnode_func, folderview);
1769 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1771 folderview_sort_folders(folderview, root, folder);
1774 /* callback functions */
1775 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1776 GdkEventButton *event)
1780 FolderViewPopup *fpopup;
1781 GtkActionGroup *action_group;
1783 FolderItem *special_trash = NULL, *special_queue = NULL;
1785 GtkUIManager *ui_manager = gtk_ui_manager_new();
1787 if (folderview->ui_manager)
1788 g_object_unref(folderview->ui_manager);
1790 folderview->ui_manager = ui_manager;
1791 item = folderview_get_selected_item(folderview);
1793 cm_return_if_fail(item != NULL);
1794 cm_return_if_fail(item->folder != NULL);
1795 folder = item->folder;
1797 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1800 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1802 fpopup = g_hash_table_lookup(folderview_popups, "common");
1803 action_group = g_hash_table_lookup(folderview->popups, "common");
1806 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1807 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1808 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1810 if (fpopup->add_menuitems)
1811 fpopup->add_menuitems(ui_manager, item);
1813 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1814 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1815 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1816 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1817 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1818 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1820 if (fpopup->set_sensitivity != NULL)
1821 fpopup->set_sensitivity(ui_manager, item);
1823 if (NULL != (ac = account_find_from_item(item))) {
1824 special_trash = account_get_special_folder(ac, F_TRASH);
1825 special_queue = account_get_special_folder(ac, F_QUEUE);
1828 if ((item == folder->trash || item == special_trash
1829 || folder_has_parent_of_type(item, F_TRASH))) {
1830 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1831 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1834 if ((item == folder->queue || item == special_queue
1835 || folder_has_parent_of_type(item, F_QUEUE))) {
1836 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1837 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1840 #define SET_SENS(name, sens) \
1841 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1843 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1844 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1845 folderview->selected == folderview->opened);
1846 SET_SENS("FolderViewPopup/Properties", TRUE);
1848 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1849 item->total_msgs >= 1 && !item->processing_pending);
1850 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1851 !item->no_select && !item->processing_pending);
1853 if (item == folder->trash || item == special_trash
1854 || folder_has_parent_of_type(item, F_TRASH)) {
1855 GSList *msglist = folder_item_get_msg_list(item);
1856 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1857 procmsg_msg_list_free(msglist);
1859 if (item == folder->queue || item == special_queue
1860 || folder_has_parent_of_type(item, F_QUEUE)) {
1861 GSList *msglist = folder_item_get_msg_list(item);
1862 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1863 procmsg_msg_list_free(msglist);
1867 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1868 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1869 g_signal_connect(G_OBJECT(popup), "selection_done",
1870 G_CALLBACK(folderview_popup_close),
1872 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1873 event->button, event->time);
1876 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1877 FolderView *folderview)
1879 GtkCMCList *clist = GTK_CMCLIST(ctree);
1880 gint prev_row = -1, row = -1, column = -1;
1882 if (!event) return FALSE;
1884 if (event->button == 1 || event->button == 2) {
1885 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1886 folderview->open_folder = TRUE;
1888 if (event->type == GDK_2BUTTON_PRESS) {
1889 if (clist->selection) {
1890 GtkCMCTreeNode *node;
1892 node = GTK_CMCTREE_NODE(clist->selection->data);
1894 gtk_cmctree_toggle_expansion(
1897 folderview->open_folder = FALSE;
1904 if (event->button == 2 || event->button == 3) {
1906 if (clist->selection) {
1907 GtkCMCTreeNode *node;
1909 node = GTK_CMCTREE_NODE(clist->selection->data);
1911 prev_row = gtkut_ctree_get_nth_from_node
1912 (GTK_CMCTREE(ctree), node);
1915 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1918 if (prev_row != row) {
1919 gtk_cmclist_unselect_all(clist);
1920 if (event->button == 2)
1921 folderview_select_node
1923 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1926 gtk_cmclist_select_row(clist, row, column);
1930 if (event->button != 3) return FALSE;
1932 folderview_set_sens_and_popup_menu(folderview, row, event);
1936 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1937 FolderView *folderview)
1939 int row = -1, column = -1;
1941 if (!event) return FALSE;
1943 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1946 if (event->button == 1 && folderview->open_folder == FALSE &&
1947 folderview->opened != NULL) {
1948 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1949 folderview->opened);
1950 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1956 #define BREAK_ON_MODIFIER_KEY() \
1957 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1959 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1960 FolderView *folderview)
1962 if (!event) return FALSE;
1964 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1967 switch (event->keyval) {
1969 #ifndef GENERIC_UMPC
1970 case GDK_KEY_Return:
1971 case GDK_KEY_KP_Enter:
1973 if (folderview->selected) {
1974 folderview_select_node(folderview,
1975 folderview->selected);
1979 case GDK_KEY_Return:
1980 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
1981 gtk_cmctree_toggle_expansion(
1982 GTK_CMCTREE(folderview->ctree),
1983 folderview->selected);
1988 BREAK_ON_MODIFIER_KEY();
1989 if (folderview->selected) {
1990 if (folderview->opened == folderview->selected &&
1991 (!folderview->summaryview->folder_item ||
1992 folderview->summaryview->folder_item->total_msgs == 0))
1993 folderview_select_next_unread(folderview, TRUE);
1995 folderview_select_node(folderview,
1996 folderview->selected);
2006 typedef struct _PostponedSelectData
2009 GtkCMCTreeNode *row;
2011 FolderView *folderview;
2012 } PostponedSelectData;
2014 static gboolean postpone_select(void *data)
2016 PostponedSelectData *psdata = (PostponedSelectData *)data;
2017 debug_print("trying again\n");
2018 psdata->folderview->open_folder = TRUE;
2019 main_window_cursor_normal(psdata->folderview->mainwin);
2020 STATUSBAR_POP(psdata->folderview->mainwin);
2021 folderview_selected(psdata->ctree, psdata->row,
2022 psdata->column, psdata->folderview);
2027 void folderview_close_opened(FolderView *folderview)
2029 if (folderview->opened) {
2030 FolderItem *olditem;
2032 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2033 folderview->opened);
2035 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2036 olditem->path ? olditem->path:olditem->name);
2037 /* will be null if we just moved the previously opened folder */
2038 STATUSBAR_PUSH(folderview->mainwin, buf);
2039 main_window_cursor_wait(folderview->mainwin);
2041 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2042 summary_show(folderview->summaryview, NULL);
2043 folder_item_close(olditem);
2044 main_window_cursor_normal(folderview->mainwin);
2045 STATUSBAR_POP(folderview->mainwin);
2046 if (olditem->folder->klass->item_closed)
2047 olditem->folder->klass->item_closed(olditem);
2052 if (folderview->opened &&
2053 !GTK_CMCTREE_ROW(folderview->opened)->children)
2054 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2056 folderview->opened = NULL;
2058 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2059 gint column, FolderView *folderview)
2061 static gboolean can_select = TRUE; /* exclusive lock */
2066 GtkCMCTreeNode *old_opened = folderview->opened;
2068 folderview->selected = row;
2070 debug_print("newly selected %p, opened %p\n", folderview->selected,
2071 folderview->opened);
2072 if (folderview->opened == row) {
2073 folderview->open_folder = FALSE;
2078 item = gtk_cmctree_node_get_row_data(ctree, row);
2081 folderview->open_folder = FALSE;
2085 if (!can_select || summary_is_locked(folderview->summaryview)) {
2086 if (folderview->opened) {
2087 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2088 gtk_cmctree_select(ctree, folderview->opened);
2090 folderview->open_folder = FALSE;
2095 if (!folderview->open_folder) {
2102 /* Save cache for old folder */
2103 /* We don't want to lose all caches if sylpheed crashed */
2104 /* resets folderview->opened to NULL */
2105 folderview_close_opened(folderview);
2107 /* CLAWS: set compose button type: news folder items
2108 * always have a news folder as parent */
2110 toolbar_set_compose_button
2111 (folderview->mainwin->toolbar,
2112 FOLDER_TYPE(item->folder) == F_NEWS ?
2113 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2116 debug_print("Folder %s is selected\n", item->path);
2118 if (!GTK_CMCTREE_ROW(row)->children)
2119 gtk_cmctree_expand(ctree, row);
2121 /* ungrab the mouse event */
2122 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2123 gtk_grab_remove(GTK_WIDGET(ctree));
2124 if (gdk_pointer_is_grabbed())
2125 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2129 /* TODO: wwp: avoid displaying (null) in the status bar */
2130 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2131 item->path : "(null)");
2132 debug_print("%s\n", buf);
2133 STATUSBAR_PUSH(folderview->mainwin, buf);
2136 main_window_cursor_wait(folderview->mainwin);
2138 if (folderview->scanning_folder == item->folder) {
2141 res = folder_item_open(item);
2144 if (res == -1 && item->no_select == FALSE) {
2145 main_window_cursor_normal(folderview->mainwin);
2146 STATUSBAR_POP(folderview->mainwin);
2148 alertpanel_error(_("Folder could not be opened."));
2150 folderview->open_folder = FALSE;
2154 } else if (res == -2 && item->no_select == FALSE) {
2155 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2156 data->ctree = ctree;
2158 data->column = column;
2159 data->folderview = folderview;
2160 debug_print("postponing open of %s till end of scan\n",
2161 item->path ? item->path:item->name);
2162 folderview->open_folder = FALSE;
2164 g_timeout_add(500, postpone_select, data);
2169 main_window_cursor_normal(folderview->mainwin);
2172 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2173 opened = summary_show(folderview->summaryview, item);
2175 folder_clean_cache_memory(item);
2178 gtkut_ctree_set_focus_row(ctree, old_opened);
2179 gtk_cmctree_select(ctree, old_opened);
2180 folderview->opened = old_opened;
2182 folderview->opened = row;
2183 if (gtk_cmctree_node_is_visible(ctree, row)
2184 != GTK_VISIBILITY_FULL)
2185 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2188 STATUSBAR_POP(folderview->mainwin);
2190 folderview->open_folder = FALSE;
2195 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2196 FolderView *folderview)
2200 item = gtk_cmctree_node_get_row_data(ctree, node);
2201 cm_return_if_fail(item != NULL);
2202 item->collapsed = FALSE;
2203 folderview_update_node(folderview, node);
2206 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2207 FolderView *folderview)
2211 item = gtk_cmctree_node_get_row_data(ctree, node);
2212 cm_return_if_fail(item != NULL);
2213 item->collapsed = TRUE;
2214 folderview_update_node(folderview, node);
2217 static void folderview_popup_close(GtkMenuShell *menu_shell,
2218 FolderView *folderview)
2220 if (!folderview->opened) return;
2222 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2225 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2226 FolderView *folderview)
2228 FolderColumnType type = folderview->col_state[column].type;
2230 prefs_common.folder_col_size[type] = width;
2233 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2235 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2236 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2237 GtkCMCTreeNode *node, *parent_node;
2238 gint *col_pos = folderview->col_pos;
2239 FolderItemUpdateData hookdata;
2241 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2242 if (parent_node == NULL)
2245 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2247 text[col_pos[F_COL_FOLDER]] = item->name;
2248 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2253 gtk_cmctree_expand(ctree, parent_node);
2254 gtk_cmctree_node_set_row_data(ctree, node, item);
2256 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2257 folderview_sort_folders(folderview, parent_node, item->folder);
2259 hookdata.item = item;
2260 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2261 hookdata.msg = NULL;
2262 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2264 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2267 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2269 FolderView *folderview = (FolderView *)data;
2270 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2272 GSList *mlist = NULL;
2274 FolderItem *special_trash = NULL;
2277 if (!folderview->selected) return;
2278 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2279 cm_return_if_fail(item != NULL);
2280 cm_return_if_fail(item->folder != NULL);
2282 if (NULL != (ac = account_find_from_item(item)))
2283 special_trash = account_get_special_folder(ac, F_TRASH);
2285 if (item != item->folder->trash && item != special_trash
2286 && !folder_has_parent_of_type(item, F_TRASH)) return;
2288 if (prefs_common.ask_on_clean) {
2289 if (alertpanel(_("Empty trash"),
2290 _("Delete all messages in trash?"),
2291 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2295 mlist = folder_item_get_msg_list(item);
2297 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2298 MsgInfo * msginfo = (MsgInfo *) cur->data;
2299 if (MSG_IS_LOCKED(msginfo->flags))
2301 /* is it partially received? (partial_recv isn't cached) */
2302 if (msginfo->total_size != 0 &&
2303 msginfo->size != (off_t)msginfo->total_size)
2304 partial_mark_for_delete(msginfo);
2306 procmsg_msg_list_free(mlist);
2308 folder_item_remove_all_msg(item);
2311 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2313 FolderView *folderview = (FolderView *)data;
2314 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2316 FolderItem *special_queue = NULL;
2318 gchar *errstr = NULL;
2320 if (!folderview->selected) return;
2321 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2322 cm_return_if_fail(item != NULL);
2323 cm_return_if_fail(item->folder != NULL);
2325 if (NULL != (ac = account_find_from_item(item)))
2326 special_queue = account_get_special_folder(ac, F_QUEUE);
2328 if (item != item->folder->queue && item != special_queue
2329 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2331 if (procmsg_queue_is_empty(item))
2334 if (prefs_common.work_offline)
2335 if (alertpanel(_("Offline warning"),
2336 _("You're working offline. Override?"),
2337 GTK_STOCK_NO, GTK_STOCK_YES,
2338 NULL) != G_ALERTALTERNATE)
2341 /* ask for confirmation before sending queued messages only
2342 in online mode and if there is at least one message queued
2343 in any of the folder queue
2345 if (prefs_common.confirm_send_queued_messages) {
2346 if (!prefs_common.work_offline) {
2347 if (alertpanel(_("Send queued messages"),
2348 _("Send all queued messages?"),
2349 GTK_STOCK_CANCEL, _("_Send"),
2350 NULL) != G_ALERTALTERNATE)
2355 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2357 alertpanel_error_log(_("Some errors occurred while "
2358 "sending queued messages."));
2360 alertpanel_error_log(_("Some errors occurred "
2361 "while sending queued messages:\n%s"), errstr);
2367 static void folderview_search_cb(GtkAction *action, gpointer data)
2369 FolderView *folderview = (FolderView *)data;
2370 summary_search(folderview->summaryview);
2373 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2375 FolderView *folderview = (FolderView *)data;
2376 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2379 if (!folderview->selected) return;
2381 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2382 cm_return_if_fail(item != NULL);
2383 cm_return_if_fail(item->folder != NULL);
2385 item->processing_pending = TRUE;
2386 folder_item_apply_processing(item);
2387 item->processing_pending = FALSE;
2390 static void folderview_property_cb(GtkAction *action, gpointer data)
2392 FolderView *folderview = (FolderView *)data;
2393 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2396 if (!folderview->selected) return;
2398 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2399 cm_return_if_fail(item != NULL);
2400 cm_return_if_fail(item->folder != NULL);
2402 prefs_folder_item_open(item);
2405 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2407 GSList *list = NULL;
2408 GSList *done = NULL;
2409 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2411 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2412 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2413 && list->data != node) {
2414 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2415 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2418 for (list = done; list != NULL; list = g_slist_next(list)) {
2419 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2425 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2426 FolderItem *to_folder, gboolean copy)
2428 FolderItem *new_folder = NULL;
2432 cm_return_if_fail(folderview != NULL);
2433 cm_return_if_fail(from_folder != NULL);
2434 cm_return_if_fail(to_folder != NULL);
2436 if (prefs_common.warn_dnd) {
2437 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2438 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2439 from_folder->name, to_folder->name);
2440 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2441 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2442 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2445 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2447 else if (status & G_ALERTDISABLE)
2448 prefs_common.warn_dnd = FALSE;
2451 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2452 from_folder->name, to_folder->name);
2453 STATUSBAR_PUSH(folderview->mainwin, buf);
2455 summary_clear_all(folderview->summaryview);
2456 folderview->opened = NULL;
2457 folderview->selected = NULL;
2458 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2460 main_window_cursor_wait(folderview->mainwin);
2462 statusbar_verbosity_set(FALSE);
2463 folder_item_update_freeze();
2464 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2465 statusbar_verbosity_set(FALSE);
2466 main_window_cursor_normal(folderview->mainwin);
2467 STATUSBAR_POP(folderview->mainwin);
2468 folder_item_update_thaw();
2469 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2471 folderview_sort_folders(folderview,
2472 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2473 NULL, to_folder), new_folder->folder);
2474 folderview_select(folderview, new_folder);
2476 statusbar_verbosity_set(FALSE);
2477 main_window_cursor_normal(folderview->mainwin);
2478 STATUSBAR_POP(folderview->mainwin);
2479 folder_item_update_thaw();
2481 case F_MOVE_FAILED_DEST_IS_PARENT:
2482 alertpanel_error(_("Source and destination are the same."));
2484 case F_MOVE_FAILED_DEST_IS_CHILD:
2485 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2486 _("Can't move a folder to one of its children."));
2488 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2489 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2492 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2497 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2500 static gint folderview_clist_compare(GtkCMCList *clist,
2501 gconstpointer ptr1, gconstpointer ptr2)
2503 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2504 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2506 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2508 return item1->order - item2->order;
2511 // if only one folder has an order it comes first
2512 if (item1->order > 0)
2516 if (item2->order > 0)
2522 return (item2->name != NULL);
2526 return g_utf8_collate(item1->name, item2->name);
2529 static void folderview_processing_cb(GtkAction *action, gpointer data)
2531 FolderView *folderview = (FolderView *)data;
2532 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2536 if (!folderview->selected) return;
2538 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2539 cm_return_if_fail(item != NULL);
2540 cm_return_if_fail(item->folder != NULL);
2542 id = folder_item_get_identifier(item);
2543 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2546 prefs_filtering_open(&item->prefs->processing, title,
2547 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2551 void folderview_set_target_folder_color(gint color_op)
2555 FolderView *folderview;
2557 for (list = folderview_list; list != NULL; list = list->next) {
2558 folderview = (FolderView *)list->data;
2559 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2561 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2562 folderview->color_op;
2568 static gchar *last_smallfont = NULL;
2569 static gchar *last_normalfont = NULL;
2570 static gchar *last_boldfont = NULL;
2571 static gboolean last_derive = 0;
2573 void folderview_reinit_fonts(FolderView *folderview)
2576 g_free(last_smallfont);
2577 last_smallfont = NULL;
2578 g_free(last_normalfont);
2579 last_normalfont = NULL;
2580 g_free(last_boldfont);
2581 last_boldfont = NULL;
2584 void folderview_reflect_prefs(void)
2586 gboolean update_font = FALSE;
2587 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2588 FolderItem *item = folderview_get_selected_item(folderview);
2589 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2590 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2591 gint height = gtk_adjustment_get_value(pos);
2593 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2594 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2595 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2596 last_derive != prefs_common.derive_from_normal_font)
2599 g_free(last_smallfont);
2600 last_smallfont = g_strdup(SMALL_FONT);
2601 g_free(last_normalfont);
2602 last_normalfont = g_strdup(NORMAL_FONT);
2603 g_free(last_boldfont);
2604 last_boldfont = g_strdup(BOLD_FONT);
2605 last_derive = prefs_common.derive_from_normal_font;
2608 normal_style = normal_color_style = bold_style =
2609 bold_color_style = bold_tgtfold_style = NULL;
2611 folderview_init(folderview);
2613 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2614 folderview_column_set_titles(folderview);
2615 folderview_set_all();
2617 g_signal_handlers_block_by_func
2618 (G_OBJECT(folderview->ctree),
2619 G_CALLBACK(folderview_selected), folderview);
2622 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2623 GTK_CMCTREE(folderview->ctree), NULL, item);
2625 folderview_select(folderview, item);
2626 folderview->open_folder = FALSE;
2627 folderview->selected = node;
2630 g_signal_handlers_unblock_by_func
2631 (G_OBJECT(folderview->ctree),
2632 G_CALLBACK(folderview_selected), folderview);
2634 pos = gtk_scrolled_window_get_vadjustment(
2635 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2636 gtk_adjustment_set_value(pos, height);
2637 gtk_adjustment_changed(pos);
2638 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2641 static void drag_state_stop(FolderView *folderview)
2643 if (folderview->drag_timer)
2644 g_source_remove(folderview->drag_timer);
2645 folderview->drag_timer = 0;
2646 folderview->drag_node = NULL;
2649 static gboolean folderview_defer_expand(FolderView *folderview)
2651 if (folderview->drag_node) {
2652 folderview_recollapse_nodes(folderview, folderview->drag_node);
2653 if (folderview->drag_item->collapsed) {
2654 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2655 folderview->nodes_to_recollapse = g_slist_append
2656 (folderview->nodes_to_recollapse, folderview->drag_node);
2659 folderview->drag_item = NULL;
2660 folderview->drag_timer = 0;
2664 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2666 /* the idea is that we call drag_state_start() whenever we want expansion to
2667 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2668 * we need to call drag_state_stop() */
2669 drag_state_stop(folderview);
2670 /* request expansion */
2671 if (0 != (folderview->drag_timer = g_timeout_add
2672 (prefs_common.hover_timeout,
2673 (GSourceFunc)folderview_defer_expand,
2675 folderview->drag_node = node;
2676 folderview->drag_item = item;
2679 #ifndef GENERIC_UMPC
2680 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2681 FolderView *folderview)
2683 GdkDragContext *context;
2685 cm_return_if_fail(folderview != NULL);
2686 if (folderview->selected == NULL) return;
2687 if (folderview->nodes_to_recollapse)
2688 g_slist_free(folderview->nodes_to_recollapse);
2689 folderview->nodes_to_recollapse = NULL;
2690 context = gtk_drag_begin(widget, folderview->target_list,
2691 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2692 gtk_drag_set_icon_default(context);
2695 static void folderview_drag_data_get(GtkWidget *widget,
2696 GdkDragContext *drag_context,
2697 GtkSelectionData *selection_data,
2700 FolderView *folderview)
2704 gchar *source = NULL;
2705 if (info == TARGET_DUMMY) {
2706 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2707 cur != NULL; cur = cur->next) {
2708 item = gtk_cmctree_node_get_row_data
2709 (GTK_CMCTREE(folderview->ctree),
2710 GTK_CMCTREE_NODE(cur->data));
2712 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2713 gtk_selection_data_set(selection_data,
2714 gtk_selection_data_get_target(selection_data), 8,
2715 source, strlen(source));
2721 g_warning("unknown info %d\n", info);
2725 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2727 FolderUpdateData *hookdata;
2728 FolderView *folderview;
2732 folderview = (FolderView *) userdata;
2733 cm_return_val_if_fail(hookdata != NULL, FALSE);
2734 cm_return_val_if_fail(folderview != NULL, FALSE);
2736 ctree = folderview->ctree;
2737 cm_return_val_if_fail(ctree != NULL, FALSE);
2739 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2740 folderview_create_folder_node(folderview, hookdata->item);
2741 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2742 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2743 NULL, folder_item_parent(hookdata->item));
2744 folderview_sort_folders(folderview, node, hookdata->folder);
2745 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2746 GtkCMCTreeNode *node;
2748 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2750 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2751 if (folderview->selected == node)
2752 folderview->selected = NULL;
2753 if (folderview->opened == node)
2754 folderview->opened = NULL;
2756 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2757 folderview_set(folderview);
2762 static gboolean folderview_dnd_scroll_cb(gpointer data)
2764 FolderView *folderview = (FolderView *)data;
2765 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2766 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2767 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2768 gint max = (int)gtk_adjustment_get_upper(pos) -
2769 (int)gtk_adjustment_get_page_size(pos);
2771 if (folderview->scroll_value == 0) {
2772 folderview->scroll_timeout_id = -1;
2776 if (folderview->scroll_value > 0 && new_val > max) {
2778 } else if (folderview->scroll_value < 0 && new_val < 0) {
2781 gtk_adjustment_set_value(pos, new_val);
2786 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2787 GdkDragContext *context,
2791 FolderView *folderview)
2794 FolderItem *item = NULL, *src_item = NULL;
2795 GtkCMCTreeNode *node = NULL;
2796 gboolean acceptable = FALSE;
2797 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2798 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2799 int height = (int)gtk_adjustment_get_page_size(pos);
2800 int total_height = (int)gtk_adjustment_get_upper(pos);
2801 int vpos = (int)gtk_adjustment_get_value(pos);
2802 int offset = prefs_common.show_col_headers ? 24:0;
2805 if (gtk_cmclist_get_selection_info
2806 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2807 GtkWidget *srcwidget;
2809 if (y > height - (48 - offset) && height + vpos < total_height) {
2810 dist = -(height - (48 - offset) - y);
2811 folderview->scroll_value = 1.41f * (1+(dist / 6));
2812 } else if (y < 72 - (24 - offset) && y >= 0) {
2813 dist = 72 - (24 - offset) - y;
2814 folderview->scroll_value = -1.41f * (1+(dist / 6));
2816 folderview->scroll_value = 0;
2818 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == -1) {
2819 folderview->scroll_timeout_id =
2820 g_timeout_add(30, folderview_dnd_scroll_cb,
2824 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2825 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2826 src_item = folderview->summaryview->folder_item;
2828 srcwidget = gtk_drag_get_source_widget(context);
2829 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2830 /* comes from summaryview */
2831 /* we are copying messages, so only accept folder items that are not
2832 the source item, are no root items and can copy messages */
2833 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2834 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2835 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2837 } else if (srcwidget == folderview->ctree) {
2838 /* comes from folderview */
2839 /* we are moving folder items, only accept folders that are not
2840 the source items and can copy messages and create folder items */
2841 if (item && item->folder && src_item && src_item != item &&
2842 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2843 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2844 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2845 || item->folder == src_item->folder))
2848 /* comes from another app */
2849 /* we are adding messages, so only accept folder items that are
2850 no root items and can copy messages */
2851 if (item && item->folder && folder_item_parent(item) != NULL
2852 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2853 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2858 if (acceptable || (src_item && src_item == item))
2859 drag_state_start(folderview, node, item);
2862 g_signal_handlers_block_by_func
2864 G_CALLBACK(folderview_selected), folderview);
2865 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2866 g_signal_handlers_unblock_by_func
2868 G_CALLBACK(folderview_selected), folderview);
2869 gdk_drag_status(context,
2870 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2871 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2873 if (folderview->opened)
2874 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2875 gdk_drag_status(context, 0, time);
2881 static void folderview_drag_leave_cb(GtkWidget *widget,
2882 GdkDragContext *context,
2884 FolderView *folderview)
2886 drag_state_stop(folderview);
2887 folderview->scroll_value = 0;
2888 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2891 static void free_info (gpointer stuff, gpointer data)
2896 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2897 guint time, FolderItem *item)
2900 GSList *msglist = NULL;
2901 list = uri_list_extract_filenames(data);
2902 if (!(item && item->folder && folder_item_parent(item) != NULL
2903 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2905 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2906 debug_print("item doesn't fit\n");
2910 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2911 debug_print("list is empty\n");
2914 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2915 MsgFileInfo *info = NULL;
2917 if (file_is_email((gchar *)tmp->data)) {
2918 info = g_new0(MsgFileInfo, 1);
2919 info->msginfo = NULL;
2920 info->file = (gchar *)tmp->data;
2921 msglist = g_slist_prepend(msglist, info);
2922 debug_print("file is a mail\n");
2924 debug_print("file isn't a mail\n");
2928 msglist = g_slist_reverse(msglist);
2929 folder_item_add_msgs(item, msglist, FALSE);
2930 g_slist_foreach(msglist, free_info, NULL);
2931 g_slist_free(msglist);
2932 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2934 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2936 list_free_strings(list);
2940 static void folderview_drag_received_cb(GtkWidget *widget,
2941 GdkDragContext *drag_context,
2944 GtkSelectionData *data,
2947 FolderView *folderview)
2950 FolderItem *item = NULL, *src_item;
2951 GtkCMCTreeNode *node;
2952 int offset = prefs_common.show_col_headers ? 24:0;
2954 folderview->scroll_value = 0;
2956 if (info == TARGET_DUMMY) {
2957 drag_state_stop(folderview);
2958 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2959 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2960 /* comes from summaryview */
2961 if (gtk_cmclist_get_selection_info
2962 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2965 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2966 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2967 src_item = folderview->summaryview->folder_item;
2969 if (item->no_select) {
2970 alertpanel_error(_("The destination folder can only be used to "
2971 "store subfolders."));
2974 /* re-check (due to acceptable possibly set for folder moves */
2975 if (!(item && item->folder && item->path && !item->no_select &&
2976 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2979 if (item && src_item) {
2980 switch (gdk_drag_context_get_selected_action(drag_context)) {
2981 case GDK_ACTION_COPY:
2982 summary_copy_selected_to(folderview->summaryview, item);
2983 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2985 case GDK_ACTION_MOVE:
2986 case GDK_ACTION_DEFAULT:
2988 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2989 summary_copy_selected_to(folderview->summaryview, item);
2991 summary_move_selected_to(folderview->summaryview, item);
2992 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2995 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2997 /* comes from folderview */
2999 gboolean folder_is_normal = TRUE;
3000 gboolean copy = (GDK_ACTION_COPY ==
3001 gdk_drag_context_get_selected_action(drag_context));
3003 source = (char *)gtk_selection_data_get_data(data) + 17;
3004 if (gtk_cmclist_get_selection_info
3005 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3007 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3010 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3011 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3012 src_item = folder_find_item_from_identifier(source);
3016 src_item->stype == F_NORMAL &&
3017 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3018 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3019 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3020 !folder_has_parent_of_type(src_item, F_TRASH);
3021 if (!item || !src_item || !folder_is_normal) {
3022 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3026 folderview_move_folder(folderview, src_item, item, copy);
3027 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3029 folderview->nodes_to_recollapse = NULL;
3030 } else if (info == TARGET_MAIL_URI_LIST) {
3031 if (gtk_cmclist_get_selection_info
3032 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3035 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3037 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3038 debug_print("no node\n");
3041 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3043 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3044 debug_print("no item\n");
3047 folderview_finish_dnd(gtk_selection_data_get_data(data),
3048 drag_context, time, item);
3052 static void folderview_drag_end_cb(GtkWidget *widget,
3053 GdkDragContext *drag_context,
3054 FolderView *folderview)
3056 drag_state_stop(folderview);
3057 folderview->scroll_value = 0;
3058 g_slist_free(folderview->nodes_to_recollapse);
3059 folderview->nodes_to_recollapse = NULL;
3062 void folderview_register_popup(FolderViewPopup *fpopup)
3066 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3067 FolderView *folderview = folderviews->data;
3068 GtkActionGroup *factory;
3070 factory = create_action_group(folderview, fpopup);
3071 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3073 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3076 void folderview_unregister_popup(FolderViewPopup *fpopup)
3081 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3082 FolderView *folderview = folderviews->data;
3084 g_hash_table_remove(folderview->popups, fpopup->klass);
3086 g_hash_table_remove(folderview_popups, fpopup->klass);