2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_cb (GtkAction *action,
169 static void folderview_empty_trash_cb (GtkAction *action,
172 static void folderview_send_queue_cb (GtkAction *action,
175 static void folderview_search_cb (GtkAction *action,
177 static void folderview_run_processing_cb(GtkAction *action,
180 static void folderview_property_cb (GtkAction *action,
183 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
184 GdkDragContext *context,
188 FolderView *folderview);
189 static void folderview_drag_leave_cb (GtkWidget *widget,
190 GdkDragContext *context,
192 FolderView *folderview);
193 static void folderview_drag_received_cb (GtkWidget *widget,
194 GdkDragContext *drag_context,
197 GtkSelectionData *data,
200 FolderView *folderview);
202 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
203 FolderView *folderview);
205 static void folderview_drag_data_get (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 GtkSelectionData *selection_data,
210 FolderView *folderview);
211 static void folderview_drag_end_cb (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 FolderView *folderview);
215 static void folderview_create_folder_node (FolderView *folderview,
217 static gboolean folderview_update_folder (gpointer source,
219 static gboolean folderview_update_item_claws (gpointer source,
221 static void folderview_processing_cb(GtkAction *action, gpointer data);
222 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
223 GdkEventButton *event);
225 GHashTable *folderview_popups;
227 static GtkActionEntry folderview_common_popup_entries[] =
229 {"FolderViewPopup", NULL, "FolderViewPopup" },
230 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
231 {"FolderViewPopup/---", NULL, "---" },
232 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
233 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
234 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
235 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
236 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
237 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
241 GtkTargetEntry folderview_drag_types[] =
243 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
244 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
247 void folderview_initialize(void)
249 FolderViewPopup *fpopup;
251 fpopup = g_new0(FolderViewPopup, 1);
253 fpopup->klass = "common";
254 fpopup->path = "<CommonFolder>";
255 fpopup->entries = folderview_common_popup_entries;
256 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
257 fpopup->set_sensitivity = NULL;
259 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
260 g_hash_table_insert(folderview_popups, "common", fpopup);
263 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
265 FolderViewPopup *fpopup_common;
266 GtkActionGroup *action_group;
268 action_group = cm_menu_create_action_group(
270 fpopup->entries, fpopup->n_entries,
271 (gpointer)folderview);
273 if (fpopup->toggle_entries)
274 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
275 fpopup->n_toggle_entries,
276 (gpointer)folderview);
277 if (fpopup->radio_entries)
278 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
279 fpopup->n_radio_entries, fpopup->radio_default,
280 G_CALLBACK(fpopup->radio_callback),
281 (gpointer)folderview);
283 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
284 if (fpopup_common != fpopup) {
285 gtk_action_group_add_actions(action_group, fpopup_common->entries,
286 fpopup_common->n_entries,
287 (gpointer)folderview);
288 if (fpopup_common->toggle_entries)
289 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
290 fpopup_common->n_toggle_entries,
291 (gpointer)folderview);
292 if (fpopup_common->radio_entries)
293 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
294 fpopup_common->n_radio_entries, fpopup_common->radio_default,
295 G_CALLBACK(fpopup_common->radio_callback),
296 (gpointer)folderview);
302 static void create_action_groups(gpointer key, gpointer value, gpointer data)
304 FolderView *folderview = data;
305 FolderViewPopup *fpopup = value;
306 GtkActionGroup *group;
308 group = create_action_group(folderview, fpopup);
309 g_hash_table_insert(folderview->popups, fpopup->klass, group);
312 static void folderview_column_set_titles(FolderView *folderview)
314 GtkWidget *ctree = folderview->ctree;
315 GtkWidget *label_folder;
316 GtkWidget *label_new;
317 GtkWidget *label_unread;
318 GtkWidget *label_total;
319 GtkWidget *hbox_folder;
321 GtkWidget *hbox_unread;
322 GtkWidget *hbox_total;
323 gint *col_pos = folderview->col_pos;
325 debug_print("setting titles...\n");
326 gtk_widget_realize(folderview->ctree);
327 gtk_widget_show_all(folderview->scrolledwin);
329 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
330 * instead text (text overflows making them unreadable and ugly) */
331 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
333 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
335 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
338 label_folder = gtk_label_new(_("Folder"));
339 label_new = gtk_image_new_from_pixbuf(newxpm);
340 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
341 label_total = gtk_image_new_from_pixbuf(readxpm);
343 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
345 hbox_folder = gtk_hbox_new(FALSE, 4);
346 hbox_new = gtk_hbox_new(FALSE, 4);
347 hbox_unread = gtk_hbox_new(FALSE, 4);
348 hbox_total = gtk_hbox_new(FALSE, 4);
351 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
352 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
353 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
354 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
355 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
360 gtk_widget_show_all(hbox_folder);
361 gtk_widget_show_all(hbox_new);
362 gtk_widget_show_all(hbox_unread);
363 gtk_widget_show_all(hbox_total);
366 gtk_widget_set_size_request(hbox_new, -1, 20);
367 gtk_widget_set_size_request(hbox_unread, -1, 20);
368 gtk_widget_set_size_request(hbox_total, -1, 20);
371 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
372 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
373 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
374 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
381 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
382 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
385 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
387 FolderView *folderview = (FolderView *)data;
388 GdkEventButton event;
389 if (folderview_get_selected_item(folderview) == NULL)
393 event.time = gtk_get_current_event_time();
395 folderview_set_sens_and_popup_menu(folderview, -1,
402 static GtkWidget *folderview_ctree_create(FolderView *folderview)
406 FolderColumnState *col_state;
407 FolderColumnType type;
408 gchar *titles[N_FOLDER_COLS];
410 GtkWidget *scrolledwin = folderview->scrolledwin;
412 debug_print("creating tree...\n");
413 memset(titles, 0, sizeof(titles));
415 col_state = prefs_folder_column_get_config();
416 memset(titles, 0, sizeof(titles));
418 col_pos = folderview->col_pos;
420 for (i = 0; i < N_FOLDER_COLS; i++) {
421 folderview->col_state[i] = col_state[i];
422 type = col_state[i].type;
426 titles[col_pos[F_COL_FOLDER]] = _("Folder");
427 titles[col_pos[F_COL_NEW]] = _("New");
428 titles[col_pos[F_COL_UNREAD]] = _("Unread");
429 /* TRANSLATORS: This in Number sign in American style */
430 titles[col_pos[F_COL_TOTAL]] = _("#");
432 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
435 if (prefs_common.show_col_headers == FALSE)
436 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
439 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
440 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
442 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
443 col_pos[F_COL_UNREAD],
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
446 col_pos[F_COL_TOTAL],
448 if (prefs_common.enable_dotted_lines) {
449 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_DOTTED);
450 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
451 GTK_CMCTREE_EXPANDER_SQUARE);
453 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
454 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
455 GTK_CMCTREE_EXPANDER_TRIANGLE);
458 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
459 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
461 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
462 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
464 /* don't let title buttons take key focus */
465 for (i = 0; i < N_FOLDER_COLS; i++) {
466 GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(ctree)->column[i].button,
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
481 g_signal_connect(G_OBJECT(ctree), "popup-menu",
482 G_CALLBACK(folderview_popup_menu), folderview);
484 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
485 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
486 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
487 G_CALLBACK(folderview_popup_menu), folderview);
489 g_signal_connect(G_OBJECT(ctree), "button_release_event",
490 G_CALLBACK(folderview_button_released),
492 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
493 G_CALLBACK(folderview_selected), folderview);
495 /* drag-n-dropping folders on maemo is impractical as this
496 * opens the folder almost everytime */
497 g_signal_connect(G_OBJECT(ctree), "start_drag",
498 G_CALLBACK(folderview_start_drag), folderview);
500 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
501 G_CALLBACK(folderview_drag_data_get),
504 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
505 G_CALLBACK(folderview_tree_expanded),
507 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
508 G_CALLBACK(folderview_tree_collapsed),
511 g_signal_connect(G_OBJECT(ctree), "resize_column",
512 G_CALLBACK(folderview_col_resized),
516 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
517 folderview_drag_types, 2,
518 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
519 g_signal_connect(G_OBJECT(ctree), "drag_motion",
520 G_CALLBACK(folderview_drag_motion_cb),
522 g_signal_connect(G_OBJECT(ctree), "drag_leave",
523 G_CALLBACK(folderview_drag_leave_cb),
525 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
526 G_CALLBACK(folderview_drag_received_cb),
528 g_signal_connect(G_OBJECT(ctree), "drag_end",
529 G_CALLBACK(folderview_drag_end_cb),
532 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
537 void folderview_set_column_order(FolderView *folderview)
539 GtkWidget *ctree = folderview->ctree;
540 FolderItem *item = folderview_get_selected_item(folderview);
541 FolderItem *sel_item = NULL, *op_item = NULL;
542 GtkWidget *scrolledwin = folderview->scrolledwin;
544 if (folderview->selected)
545 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
546 if (folderview->opened)
547 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
549 debug_print("recreating tree...\n");
550 gtk_widget_destroy(folderview->ctree);
553 folderview->ctree = ctree = folderview_ctree_create(folderview);
554 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
555 GTK_CMCLIST(ctree)->hadjustment);
556 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
557 GTK_CMCLIST(ctree)->vadjustment);
558 gtk_widget_show(ctree);
561 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
563 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
565 folderview_set(folderview);
566 folderview_column_set_titles(folderview);
568 folderview_select(folderview,item);
571 FolderView *folderview_create(void)
573 FolderView *folderview;
574 GtkWidget *scrolledwin;
577 debug_print("Creating folder view...\n");
578 folderview = g_new0(FolderView, 1);
580 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
581 gtk_scrolled_window_set_policy
582 (GTK_SCROLLED_WINDOW(scrolledwin),
583 GTK_POLICY_AUTOMATIC,
584 prefs_common.folderview_vscrollbar_policy);
585 gtk_widget_set_size_request(scrolledwin,
586 prefs_common.folderview_width,
587 prefs_common.folderview_height);
589 folderview->scrolledwin = scrolledwin;
590 ctree = folderview_ctree_create(folderview);
592 /* create popup factories */
593 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
594 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
596 folderview->ctree = ctree;
598 folderview->folder_update_callback_id =
599 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
600 folderview->folder_item_update_callback_id =
601 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
603 gtk_widget_show_all(scrolledwin);
605 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
606 folderview_list = g_list_append(folderview_list, folderview);
607 folderview->deferred_refresh_id = -1;
612 void folderview_init(FolderView *folderview)
614 GtkWidget *ctree = folderview->ctree;
617 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
618 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
619 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
620 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
621 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
622 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
623 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
624 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
665 PangoFontDescription *font_desc;
666 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
667 font_desc = pango_font_description_from_string(NORMAL_FONT);
669 if (normal_style->font_desc)
670 pango_font_description_free
671 (normal_style->font_desc);
672 normal_style->font_desc = font_desc;
674 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
675 normal_color_style = gtk_style_copy(normal_style);
676 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
678 gtk_widget_set_style(ctree, normal_style);
682 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
683 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
684 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
685 pango_font_description_set_weight
686 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
688 PangoFontDescription *font_desc;
689 font_desc = pango_font_description_from_string(BOLD_FONT);
691 if (bold_style->font_desc)
692 pango_font_description_free
693 (bold_style->font_desc);
694 bold_style->font_desc = font_desc;
697 bold_color_style = gtk_style_copy(bold_style);
698 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
700 bold_tgtfold_style = gtk_style_copy(bold_style);
701 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
705 static gboolean folderview_defer_set(gpointer data)
707 FolderView *folderview = (FolderView *)data;
708 MainWindow *mainwin = folderview->mainwin;
712 if (mainwin->lock_count)
715 debug_print("doing deferred folderview_set now\n");
716 folderview_set(folderview);
718 folderview->deferred_refresh_id = -1;
722 void folderview_set(FolderView *folderview)
724 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
725 MainWindow *mainwin = folderview->mainwin;
726 FolderItem *sel_item = NULL, *op_item = NULL;
731 if (mainwin->lock_count) {
732 if (folderview->deferred_refresh_id == -1)
733 folderview->deferred_refresh_id =
734 g_timeout_add(500, folderview_defer_set, folderview);
735 debug_print("deferred folderview_set\n");
740 debug_print("Setting folder info...\n");
741 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
743 main_window_cursor_wait(mainwin);
745 if (folderview->selected)
746 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
747 if (folderview->opened)
748 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
750 folderview->selected = NULL;
751 folderview->opened = NULL;
753 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
754 gtk_cmclist_clear(GTK_CMCLIST(ctree));
756 folderview_set_folders(folderview);
759 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
761 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
763 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
764 main_window_cursor_normal(mainwin);
765 STATUSBAR_POP(mainwin);
769 void folderview_set_all(void)
773 for (list = folderview_list; list != NULL; list = list->next)
774 folderview_set((FolderView *)list->data);
777 void folderview_select(FolderView *folderview, FolderItem *item)
779 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
780 GtkCMCTreeNode *node;
781 GtkCMCTreeNode *old_selected = folderview->selected;
785 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
786 if (node) folderview_select_node(folderview, node);
788 if (old_selected != node)
789 folder_update_op_count();
792 static void mark_all_read_cb(GtkAction *action, gpointer data)
794 FolderView *folderview = (FolderView *)data;
798 item = folderview_get_selected_item(folderview);
802 if (folderview->summaryview->folder_item != item
803 && prefs_common.ask_mark_all_read) {
804 val = alertpanel_full(_("Mark all as read"),
805 _("Do you really want to mark all mails in this "
806 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
807 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
809 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
811 else if (val & G_ALERTDISABLE)
812 prefs_common.ask_mark_all_read = FALSE;
816 folder_item_update_freeze();
817 if (folderview->summaryview->folder_item != item)
818 summary_lock(folderview->summaryview);
820 summary_freeze(folderview->summaryview);
822 folderutils_mark_all_read(item);
824 if (folderview->summaryview->folder_item != item)
825 summary_unlock(folderview->summaryview);
827 summary_thaw(folderview->summaryview);
828 folder_item_update_thaw();
831 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
833 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
835 g_return_if_fail(node != NULL);
837 if (folderview->open_folder) {
841 folderview->open_folder = TRUE;
842 gtkut_ctree_set_focus_row(ctree, node);
843 gtk_cmctree_select(ctree, node);
844 if ((folderview->summaryview->folder_item &&
845 folderview->summaryview->folder_item->total_msgs > 0) ||
846 prefs_common.layout_mode == SMALL_LAYOUT)
847 summary_grab_focus(folderview->summaryview);
849 gtk_widget_grab_focus(folderview->ctree);
851 gtkut_ctree_expand_parent_all(ctree, node);
854 void folderview_unselect(FolderView *folderview)
856 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
858 (GTK_CMCTREE(folderview->ctree), folderview->opened);
860 folderview->selected = folderview->opened = NULL;
863 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
864 GtkCMCTreeNode *node)
869 node = gtkut_ctree_node_next(ctree, node);
871 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
873 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
874 item = gtk_cmctree_node_get_row_data(ctree, node);
875 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
882 void folderview_select_next_marked(FolderView *folderview)
884 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
885 GtkCMCTreeNode *node = NULL;
886 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
887 gboolean last_open = prefs_common.always_show_msg;
889 prefs_common.summary_select_prio[0] = ACTION_MARKED;
890 prefs_common.always_show_msg = OPENMSG_ALWAYS;
892 if ((node = folderview_find_next_marked(ctree, folderview->opened))
894 folderview_select_node(folderview, node);
898 if (!folderview->opened ||
899 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
902 /* search again from the first node */
903 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
904 folderview_select_node(folderview, node);
907 prefs_common.summary_select_prio[0] = last_summary_select_prio;
908 prefs_common.always_show_msg = last_open;
911 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
912 GtkCMCTreeNode *node)
917 node = gtkut_ctree_node_next(ctree, node);
919 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
921 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
922 item = gtk_cmctree_node_get_row_data(ctree, node);
923 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
930 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
932 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
933 GtkCMCTreeNode *node = NULL;
934 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
935 gboolean last_open = prefs_common.always_show_msg;
937 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
938 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
940 if ((node = folderview_find_next_unread(ctree, folderview->opened))
942 folderview_select_node(folderview, node);
946 if (!folderview->opened ||
947 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
950 /* search again from the first node */
951 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
952 folderview_select_node(folderview, node);
955 prefs_common.summary_select_prio[0] = last_summary_select_prio;
956 prefs_common.always_show_msg = last_open;
959 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
960 GtkCMCTreeNode *node)
965 node = gtkut_ctree_node_next(ctree, node);
967 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
969 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
970 item = gtk_cmctree_node_get_row_data(ctree, node);
971 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
978 void folderview_select_next_new(FolderView *folderview)
980 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
981 GtkCMCTreeNode *node = NULL;
982 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
983 gboolean last_open = prefs_common.always_show_msg;
985 prefs_common.summary_select_prio[0] = ACTION_NEW;
986 prefs_common.always_show_msg = OPENMSG_ALWAYS;
988 if ((node = folderview_find_next_new(ctree, folderview->opened))
990 folderview_select_node(folderview, node);
994 if (!folderview->opened ||
995 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
998 /* search again from the first node */
999 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1000 folderview_select_node(folderview, node);
1003 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1004 prefs_common.always_show_msg = last_open;
1007 FolderItem *folderview_get_selected_item(FolderView *folderview)
1009 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1011 if (!folderview->selected) return NULL;
1012 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1015 static void folderview_set_folders(FolderView *folderview)
1018 list = folder_get_list();
1020 for (; list != NULL; list = list->next) {
1021 folderview_append_folder(folderview, FOLDER(list->data));
1025 static gchar *get_scan_str(FolderItem *item)
1028 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1029 item->folder->name, G_DIR_SEPARATOR,
1032 return g_strdup_printf(_("Scanning folder %s ..."),
1033 item->folder->name);
1035 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1039 for (list = folderview_list; list != NULL; list = list->next) {
1040 FolderView *folderview = (FolderView *)list->data;
1041 MainWindow *mainwin = folderview->mainwin;
1042 gchar *str = get_scan_str(item);
1044 STATUSBAR_PUSH(mainwin, str);
1045 STATUSBAR_POP(mainwin);
1050 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1053 MainWindow *mainwin = mainwindow_get_mainwindow();
1054 FolderView *folderview = NULL;
1055 GtkAdjustment *pos = NULL;
1058 g_return_if_fail(folder != NULL);
1060 if (!folder->klass->scan_tree) return;
1063 alertpanel_full(_("Rebuild folder tree"),
1064 _("Rebuilding the folder tree will remove "
1065 "local caches. Do you want to continue?"),
1066 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1067 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1068 != G_ALERTALTERNATE) {
1074 window = label_window_create(_("Rebuilding folder tree..."));
1076 window = label_window_create(_("Scanning folder tree..."));
1079 folderview = mainwin->folderview;
1082 pos = gtk_scrolled_window_get_vadjustment(
1083 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1084 height = pos->value;
1087 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1088 folder_scan_tree(folder, rebuild);
1089 folder_set_ui_func(folder, NULL, NULL);
1091 folderview_set_all();
1094 pos = gtk_scrolled_window_get_vadjustment(
1095 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1096 gtk_adjustment_set_value(pos, height);
1097 gtk_adjustment_changed(pos);
1099 label_window_destroy(window);
1103 void folderview_fast_rescan_tree(Folder *folder)
1106 MainWindow *mainwin = mainwindow_get_mainwindow();
1107 FolderView *folderview = NULL;
1108 GtkAdjustment *pos = NULL;
1111 g_return_if_fail(folder != NULL);
1113 if (!folder->klass->scan_tree) return;
1117 window = label_window_create(_("Scanning folder tree..."));
1120 folderview = mainwin->folderview;
1123 pos = gtk_scrolled_window_get_vadjustment(
1124 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1125 height = pos->value;
1128 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1129 folder_fast_scan_tree(folder);
1130 folder_set_ui_func(folder, NULL, NULL);
1132 folderview_set_all();
1135 pos = gtk_scrolled_window_get_vadjustment(
1136 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1137 gtk_adjustment_set_value(pos, height);
1138 gtk_adjustment_changed(pos);
1140 label_window_destroy(window);
1144 /** folderview_check_new()
1145 * Scan and update the folder and return the
1146 * count the number of new messages since last check.
1147 * \param folder the folder to check for new messages
1148 * \return the number of new messages since last check
1150 gint folderview_check_new(Folder *folder)
1154 FolderView *folderview;
1156 GtkCMCTreeNode *node;
1158 gint former_new_msgs = 0;
1159 gint former_new = 0, former_unread = 0, former_total;
1161 for (list = folderview_list; list != NULL; list = list->next) {
1162 folderview = (FolderView *)list->data;
1163 ctree = GTK_CMCTREE(folderview->ctree);
1164 folderview->scanning_folder = folder;
1166 main_window_lock(folderview->mainwin);
1168 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1169 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1171 item = gtk_cmctree_node_get_row_data(ctree, node);
1172 if (!item || !item->path || !item->folder) continue;
1173 if (item->no_select) continue;
1174 if (folder && folder != item->folder) continue;
1175 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1176 if (!item->prefs->newmailcheck) continue;
1177 if (item->processing_pending == TRUE) {
1178 debug_print("skipping %s, processing pending\n",
1179 item->path ? item->path : item->name);
1182 if (item->scanning != ITEM_NOT_SCANNING) {
1183 debug_print("skipping %s, scanning\n",
1184 item->path ? item->path : item->name);
1188 str = get_scan_str(item);
1190 STATUSBAR_PUSH(folderview->mainwin, str);
1194 folderview_scan_tree_func(item->folder, item, NULL);
1195 former_new = item->new_msgs;
1196 former_unread = item->unread_msgs;
1197 former_total = item->total_msgs;
1199 if (item->folder->klass->scan_required &&
1200 (item->folder->klass->scan_required(item->folder, item) ||
1201 item->folder->inbox == item ||
1202 item->opened == TRUE ||
1203 item->processing_pending == TRUE)) {
1204 if (folder_item_scan(item) < 0) {
1206 summaryview_unlock(folderview->summaryview, item);
1207 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1208 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1209 item->path ? item->path:item->name);
1210 STATUSBAR_POP(folderview->mainwin);
1212 } else if (!FOLDER_IS_LOCAL(folder)) {
1213 STATUSBAR_POP(folderview->mainwin);
1218 } else if (!item->folder->klass->scan_required) {
1219 if (folder_item_scan(item) < 0) {
1220 summaryview_unlock(folderview->summaryview, item);
1221 if (folder && !FOLDER_IS_LOCAL(folder)) {
1222 STATUSBAR_POP(folderview->mainwin);
1227 if (former_new != item->new_msgs ||
1228 former_unread != item->unread_msgs ||
1229 former_total != item->total_msgs)
1230 folderview_update_node(folderview, node);
1232 new_msgs += item->new_msgs;
1233 former_new_msgs += former_new;
1234 STATUSBAR_POP(folderview->mainwin);
1236 folderview->scanning_folder = NULL;
1237 main_window_unlock(folderview->mainwin);
1241 folder_write_list();
1242 /* Number of new messages since last check is the just the difference
1243 * between former_new_msgs and new_msgs. If new_msgs is less than
1244 * former_new_msgs, that would mean another session accessed the folder
1245 * and the result is not well defined.
1247 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1251 void folderview_check_new_all(void)
1255 FolderView *folderview;
1257 folderview = (FolderView *)folderview_list->data;
1260 main_window_lock(folderview->mainwin);
1261 window = label_window_create
1262 (_("Checking for new messages in all folders..."));
1264 list = folder_get_list();
1265 for (; list != NULL; list = list->next) {
1266 Folder *folder = list->data;
1268 folderview_check_new(folder);
1271 folder_write_list();
1272 folderview_set_all();
1274 label_window_destroy(window);
1275 main_window_unlock(folderview->mainwin);
1279 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1285 if (!item || !item->folder || !item->folder->node)
1288 node = item->folder->node;
1290 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1291 node = node->children;
1294 (item->new_msgs > 0 ||
1295 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1299 while (node != NULL) {
1300 if (node && node->data) {
1301 FolderItem *next_item = (FolderItem*) node->data;
1303 if (folderview_have_new_children_sub(folderview,
1312 static gboolean folderview_have_new_children(FolderView *folderview,
1315 return folderview_have_new_children_sub(folderview, item, FALSE);
1318 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1324 if (!item || !item->folder || !item->folder->node)
1327 node = item->folder->node;
1329 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1330 node = node->children;
1333 (item->unread_msgs > 0 ||
1334 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1338 while (node != NULL) {
1339 if (node && node->data) {
1340 FolderItem *next_item = (FolderItem*) node->data;
1342 if (folderview_have_unread_children_sub(folderview,
1352 static gboolean folderview_have_unread_children(FolderView *folderview,
1355 return folderview_have_unread_children_sub(folderview, item, FALSE);
1358 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1364 if (!item || !item->folder || !item->folder->node)
1367 node = item->folder->node;
1369 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1370 node = node->children;
1372 if (in_sub && item->search_match){
1376 while (node != NULL) {
1377 if (node && node->data) {
1378 FolderItem *next_item = (FolderItem*) node->data;
1380 if (folderview_have_matching_children_sub(folderview,
1390 static gboolean folderview_have_matching_children(FolderView *folderview,
1393 return folderview_have_matching_children_sub(folderview, item, FALSE);
1396 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1402 if (!item || !item->folder || !item->folder->node)
1405 node = item->folder->node;
1407 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1408 node = node->children;
1410 if (item->marked_msgs != 0) {
1414 while (node != NULL) {
1415 if (node && node->data) {
1416 FolderItem *next_item = (FolderItem*) node->data;
1418 if (folderview_have_marked_children_sub(folderview,
1427 static gboolean folderview_have_marked_children(FolderView *folderview,
1430 return folderview_have_marked_children_sub(folderview, item, FALSE);
1433 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1435 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1436 GtkStyle *style = NULL;
1437 GtkStyle *color_style = NULL;
1439 GdkPixbuf *xpm, *openxpm;
1440 static GdkPixbuf *searchicon;
1441 gboolean mark = FALSE;
1444 gboolean add_unread_mark;
1445 gboolean add_sub_match_mark;
1446 gboolean use_bold, use_color;
1447 gint *col_pos = folderview->col_pos;
1448 SpecialFolderItemType stype;
1450 item = gtk_cmctree_node_get_row_data(ctree, node);
1451 g_return_if_fail(item != NULL);
1453 if (!GTK_CMCTREE_ROW(node)->expanded)
1454 mark = folderview_have_marked_children(folderview, item);
1456 mark = (item->marked_msgs != 0);
1458 stype = item->stype;
1459 if (stype == F_NORMAL) {
1460 if (folder_has_parent_of_type(item, F_TRASH))
1462 else if (folder_has_parent_of_type(item, F_DRAFT))
1464 else if (folder_has_parent_of_type(item, F_OUTBOX))
1466 else if (folder_has_parent_of_type(item, F_QUEUE))
1471 if (item->hide_read_msgs) {
1472 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1473 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1475 xpm = mark?m_inboxxpm:inboxxpm;
1476 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1480 if (item->hide_read_msgs) {
1481 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1482 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1484 xpm = mark?m_outboxxpm:outboxxpm;
1485 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1489 if (item->hide_read_msgs) {
1490 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1491 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1493 xpm = mark?m_queuexpm:queuexpm;
1494 openxpm = mark?m_queueopenxpm:queueopenxpm;
1498 if (item->hide_read_msgs) {
1499 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1500 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1502 xpm = mark?m_trashxpm:trashxpm;
1503 openxpm = mark?m_trashopenxpm:trashopenxpm;
1507 xpm = mark?m_draftsxpm:draftsxpm;
1508 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1511 if (item->hide_read_msgs) {
1512 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1513 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1515 xpm = mark?m_folderxpm:folderxpm;
1516 openxpm = mark?m_folderopenxpm:folderopenxpm;
1520 if (item->no_select) {
1521 xpm = openxpm = noselectxpm;
1524 name = folder_item_get_name(item);
1526 if (!GTK_CMCTREE_ROW(node)->expanded) {
1527 add_unread_mark = folderview_have_unread_children(
1529 add_sub_match_mark = folderview_have_matching_children(
1532 add_unread_mark = FALSE;
1533 add_sub_match_mark = FALSE;
1536 if (item->search_match) {
1538 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1541 xpm = openxpm = searchicon;
1545 if (prefs_common.display_folder_unread) {
1546 if (folder_has_parent_of_type(item, F_QUEUE)) {
1547 /* only total_msgs matters here */
1548 if (item->total_msgs > 0) {
1549 /* show total number (should be equal to the unread number)
1551 str = g_strdup_printf("%s (%d%s%s)",
1552 name, item->total_msgs,
1553 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1554 (item->unreadmarked_msgs > 0) ? "!" : "");
1557 if (prefs_common.display_folder_unread == 1) {
1558 if (item->unread_msgs > 0) {
1559 /* show unread number and signs */
1560 str = g_strdup_printf("%s (%d%s%s)",
1561 name, item->unread_msgs,
1562 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1563 (item->unreadmarked_msgs > 0) ? "!" : "");
1566 if (item->total_msgs > 0) {
1567 /* show unread number, total number and signs if any */
1568 str = g_strdup_printf("%s (%d/%d%s%s)",
1569 name, item->unread_msgs, item->total_msgs,
1570 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1571 (item->unreadmarked_msgs > 0) ? "!" : "");
1575 if ((str == NULL) &&
1576 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1577 /* no unread/total numbers, but at least one sign */
1578 str = g_strdup_printf("%s (%s%s)",
1580 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1581 (item->unreadmarked_msgs > 0) ? "!" : "");
1585 /* last fallback, folder name only or with +! sign */
1586 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1587 str = g_strdup_printf("%s%s",
1589 } else if (item->unreadmarked_msgs > 0) {
1590 str = g_strdup_printf("%s%s",
1592 } else if (add_sub_match_mark) {
1593 str = g_strdup_printf("%s%s",
1596 str = g_strdup_printf("%s", name);
1599 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1601 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1605 if (!folder_item_parent(item)) {
1606 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1607 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1608 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1610 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1611 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1612 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1615 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1616 folder_has_parent_of_type(item, F_DRAFT) ||
1617 folder_has_parent_of_type(item, F_TRASH)) {
1618 use_bold = use_color = FALSE;
1619 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1620 /* highlight queue folder if there are any messages */
1621 use_bold = use_color = (item->total_msgs > 0);
1623 /* if unread messages exist, print with bold font */
1624 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1626 /* if new messages exist, print with colored letter */
1628 (item->new_msgs > 0) ||
1630 folderview_have_new_children(folderview, item));
1633 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1638 if (item->prefs->color > 0 && !use_color) {
1639 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1640 color_style = gtk_style_copy(bold_style);
1641 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1642 style = color_style;
1643 } else if (use_color) {
1644 style = bold_color_style;
1647 if (item->op_count > 0) {
1648 style = bold_tgtfold_style;
1650 } else if (use_color) {
1651 style = normal_color_style;
1652 gtk_cmctree_node_set_foreground(ctree, node,
1653 &folderview->color_new);
1654 } else if (item->op_count > 0) {
1655 style = bold_tgtfold_style;
1656 } else if (item->prefs->color > 0) {
1658 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1659 color_style = gtk_style_copy(normal_style);
1660 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1661 style = color_style;
1663 style = normal_style;
1666 gtk_cmctree_node_set_row_style(ctree, node, style);
1668 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1669 folderview_update_node(folderview, node);
1672 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1675 FolderView *folderview;
1677 GtkCMCTreeNode *node;
1679 g_return_if_fail(item != NULL);
1681 for (list = folderview_list; list != NULL; list = list->next) {
1682 folderview = (FolderView *)list->data;
1683 ctree = GTK_CMCTREE(folderview->ctree);
1685 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1687 item->search_match = matches;
1688 folderview_update_node(folderview, node);
1693 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1695 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1696 FolderView *folderview = (FolderView *)data;
1698 GtkCMCTreeNode *node;
1699 g_return_val_if_fail(update_info != NULL, TRUE);
1700 g_return_val_if_fail(update_info->item != NULL, TRUE);
1701 g_return_val_if_fail(folderview != NULL, FALSE);
1703 ctree = GTK_CMCTREE(folderview->ctree);
1705 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1708 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1709 folderview_update_node(folderview, node);
1711 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1712 update_info->item == folderview->summaryview->folder_item &&
1713 update_info->item != NULL)
1714 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1715 summary_show(folderview->summaryview, update_info->item);
1721 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1722 GNode *gnode, GtkCMCTreeNode *cnode,
1725 FolderView *folderview = (FolderView *)data;
1726 FolderItem *item = FOLDER_ITEM(gnode->data);
1728 g_return_val_if_fail(item != NULL, FALSE);
1730 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1731 folderview_update_node(folderview, cnode);
1736 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1739 FolderView *folderview = (FolderView *)data;
1742 if (GTK_CMCTREE_ROW(node)->children) {
1743 item = gtk_cmctree_node_get_row_data(ctree, node);
1744 g_return_if_fail(item != NULL);
1746 if (!item->collapsed)
1747 gtk_cmctree_expand(ctree, node);
1749 folderview_update_node(folderview, node);
1753 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1754 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1757 GtkCMCTreeNode *node, *parent, *sibling;
1759 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1761 g_warning("%s not found.\n", item->path);
1763 parent = GTK_CMCTREE_ROW(node)->parent;
1764 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1765 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1767 sibling = GTK_CMCTREE_ROW(parent)->children;
1771 tmp = gtk_cmctree_node_get_row_data
1773 if (tmp->stype != F_NORMAL)
1774 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1778 if (node != sibling)
1779 gtk_cmctree_move(ctree, node, parent, sibling);
1786 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1789 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1790 GtkCMCTreeNode *prev = NULL;
1792 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1793 gtk_sctree_sort_recursive(ctree, root);
1794 if (root && GTK_CMCTREE_ROW(root)->parent) {
1795 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1798 set_special_folder(ctree, folder->inbox, root, &prev);
1799 set_special_folder(ctree, folder->outbox, root, &prev);
1800 set_special_folder(ctree, folder->draft, root, &prev);
1801 set_special_folder(ctree, folder->queue, root, &prev);
1802 set_special_folder(ctree, folder->trash, root, &prev);
1803 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1806 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1808 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1809 GtkCMCTreeNode *root;
1811 g_return_if_fail(folder != NULL);
1813 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1814 folderview_gnode_func, folderview);
1815 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1817 folderview_sort_folders(folderview, root, folder);
1820 /* callback functions */
1821 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1822 GdkEventButton *event)
1826 FolderViewPopup *fpopup;
1827 GtkActionGroup *action_group;
1829 FolderItem *special_trash = NULL, *special_queue = NULL;
1831 GtkUIManager *ui_manager = gtk_ui_manager_new();
1833 if (folderview->ui_manager)
1834 g_object_unref(folderview->ui_manager);
1836 folderview->ui_manager = ui_manager;
1837 item = folderview_get_selected_item(folderview);
1839 g_return_if_fail(item != NULL);
1840 g_return_if_fail(item->folder != NULL);
1841 folder = item->folder;
1843 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1846 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1848 fpopup = g_hash_table_lookup(folderview_popups, "common");
1849 action_group = g_hash_table_lookup(folderview->popups, "common");
1852 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1856 if (fpopup->add_menuitems)
1857 fpopup->add_menuitems(ui_manager, item);
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1862 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1863 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1864 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1866 if (fpopup->set_sensitivity != NULL)
1867 fpopup->set_sensitivity(ui_manager, item);
1869 if (NULL != (ac = account_find_from_item(item))) {
1870 special_trash = account_get_special_folder(ac, F_TRASH);
1871 special_queue = account_get_special_folder(ac, F_QUEUE);
1874 if ((item == folder->trash || item == special_trash
1875 || folder_has_parent_of_type(item, F_TRASH))) {
1876 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1877 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1880 if ((item == folder->queue || item == special_queue
1881 || folder_has_parent_of_type(item, F_QUEUE))) {
1882 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1883 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1886 #define SET_SENS(name, sens) \
1887 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1889 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1890 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1891 folderview->selected == folderview->opened);
1892 SET_SENS("FolderViewPopup/Properties", TRUE);
1894 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1895 item->total_msgs >= 1 && !item->processing_pending);
1896 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1897 !item->no_select && !item->processing_pending);
1899 if (item == folder->trash || item == special_trash
1900 || folder_has_parent_of_type(item, F_TRASH)) {
1901 GSList *msglist = folder_item_get_msg_list(item);
1902 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1903 procmsg_msg_list_free(msglist);
1905 if (item == folder->queue || item == special_queue
1906 || folder_has_parent_of_type(item, F_QUEUE)) {
1907 GSList *msglist = folder_item_get_msg_list(item);
1908 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1909 procmsg_msg_list_free(msglist);
1913 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1914 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1915 g_signal_connect(G_OBJECT(popup), "selection_done",
1916 G_CALLBACK(folderview_popup_close),
1918 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1919 event->button, event->time);
1922 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1923 FolderView *folderview)
1925 GtkCMCList *clist = GTK_CMCLIST(ctree);
1926 gint prev_row = -1, row = -1, column = -1;
1928 if (!event) return FALSE;
1930 if (event->button == 1 || event->button == 2) {
1931 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1932 folderview->open_folder = TRUE;
1934 if (event->type == GDK_2BUTTON_PRESS) {
1935 if (clist->selection) {
1936 GtkCMCTreeNode *node;
1938 node = GTK_CMCTREE_NODE(clist->selection->data);
1940 gtk_cmctree_toggle_expansion(
1943 folderview->open_folder = FALSE;
1950 if (event->button == 2 || event->button == 3) {
1952 if (clist->selection) {
1953 GtkCMCTreeNode *node;
1955 node = GTK_CMCTREE_NODE(clist->selection->data);
1957 prev_row = gtkut_ctree_get_nth_from_node
1958 (GTK_CMCTREE(ctree), node);
1961 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1964 if (prev_row != row) {
1965 gtk_cmclist_unselect_all(clist);
1966 if (event->button == 2)
1967 folderview_select_node
1969 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1972 gtk_cmclist_select_row(clist, row, column);
1976 if (event->button != 3) return FALSE;
1978 folderview_set_sens_and_popup_menu(folderview, row, event);
1982 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1983 FolderView *folderview)
1985 int row = -1, column = -1;
1987 if (!event) return FALSE;
1989 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1992 if (event->button == 1 && folderview->open_folder == FALSE &&
1993 folderview->opened != NULL) {
1994 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1995 folderview->opened);
1996 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2002 #define BREAK_ON_MODIFIER_KEY() \
2003 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2005 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2006 FolderView *folderview)
2008 if (!event) return FALSE;
2010 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2013 switch (event->keyval) {
2015 #ifndef GENERIC_UMPC
2019 if (folderview->selected) {
2020 folderview_select_node(folderview,
2021 folderview->selected);
2026 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2027 gtk_cmctree_toggle_expansion(
2028 GTK_CMCTREE(folderview->ctree),
2029 folderview->selected);
2034 BREAK_ON_MODIFIER_KEY();
2035 if (folderview->selected) {
2036 if (folderview->opened == folderview->selected &&
2037 (!folderview->summaryview->folder_item ||
2038 folderview->summaryview->folder_item->total_msgs == 0))
2039 folderview_select_next_unread(folderview, TRUE);
2041 folderview_select_node(folderview,
2042 folderview->selected);
2052 typedef struct _PostponedSelectData
2055 GtkCMCTreeNode *row;
2057 FolderView *folderview;
2058 } PostponedSelectData;
2060 static gboolean postpone_select(void *data)
2062 PostponedSelectData *psdata = (PostponedSelectData *)data;
2063 debug_print("trying again\n");
2064 psdata->folderview->open_folder = TRUE;
2065 main_window_cursor_normal(psdata->folderview->mainwin);
2066 STATUSBAR_POP(psdata->folderview->mainwin);
2067 folderview_selected(psdata->ctree, psdata->row,
2068 psdata->column, psdata->folderview);
2073 void folderview_close_opened(FolderView *folderview)
2075 if (folderview->opened) {
2076 FolderItem *olditem;
2078 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2079 folderview->opened);
2081 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2082 olditem->path ? olditem->path:olditem->name);
2083 /* will be null if we just moved the previously opened folder */
2084 STATUSBAR_PUSH(folderview->mainwin, buf);
2085 main_window_cursor_wait(folderview->mainwin);
2087 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2088 summary_show(folderview->summaryview, NULL);
2089 folder_item_close(olditem);
2090 main_window_cursor_normal(folderview->mainwin);
2091 STATUSBAR_POP(folderview->mainwin);
2092 if (olditem->folder->klass->item_closed)
2093 olditem->folder->klass->item_closed(olditem);
2098 if (folderview->opened &&
2099 !GTK_CMCTREE_ROW(folderview->opened)->children)
2100 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2102 folderview->opened = NULL;
2104 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2105 gint column, FolderView *folderview)
2107 static gboolean can_select = TRUE; /* exclusive lock */
2112 GtkCMCTreeNode *old_opened = folderview->opened;
2114 folderview->selected = row;
2116 debug_print("newly selected %p, opened %p\n", folderview->selected,
2117 folderview->opened);
2118 if (folderview->opened == row) {
2119 folderview->open_folder = FALSE;
2124 item = gtk_cmctree_node_get_row_data(ctree, row);
2127 folderview->open_folder = FALSE;
2131 if (!can_select || summary_is_locked(folderview->summaryview)) {
2132 if (folderview->opened) {
2133 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2134 gtk_cmctree_select(ctree, folderview->opened);
2136 folderview->open_folder = FALSE;
2141 if (!folderview->open_folder) {
2148 /* Save cache for old folder */
2149 /* We don't want to lose all caches if sylpheed crashed */
2150 /* resets folderview->opened to NULL */
2151 folderview_close_opened(folderview);
2153 /* CLAWS: set compose button type: news folder items
2154 * always have a news folder as parent */
2156 toolbar_set_compose_button
2157 (folderview->mainwin->toolbar,
2158 FOLDER_TYPE(item->folder) == F_NEWS ?
2159 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2162 debug_print("Folder %s is selected\n", item->path);
2164 if (!GTK_CMCTREE_ROW(row)->children)
2165 gtk_cmctree_expand(ctree, row);
2167 /* ungrab the mouse event */
2168 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2169 gtk_grab_remove(GTK_WIDGET(ctree));
2170 if (gdk_pointer_is_grabbed())
2171 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2175 /* TODO: wwp: avoid displaying (null) in the status bar */
2176 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2177 item->path : "(null)");
2178 debug_print("%s\n", buf);
2179 STATUSBAR_PUSH(folderview->mainwin, buf);
2182 main_window_cursor_wait(folderview->mainwin);
2184 if (folderview->scanning_folder == item->folder) {
2187 res = folder_item_open(item);
2190 if (res == -1 && item->no_select == FALSE) {
2191 main_window_cursor_normal(folderview->mainwin);
2192 STATUSBAR_POP(folderview->mainwin);
2194 alertpanel_error(_("Folder could not be opened."));
2196 folderview->open_folder = FALSE;
2200 } else if (res == -2 && item->no_select == FALSE) {
2201 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2202 data->ctree = ctree;
2204 data->column = column;
2205 data->folderview = folderview;
2206 debug_print("postponing open of %s till end of scan\n",
2207 item->path ? item->path:item->name);
2208 folderview->open_folder = FALSE;
2210 g_timeout_add(500, postpone_select, data);
2215 main_window_cursor_normal(folderview->mainwin);
2218 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2219 opened = summary_show(folderview->summaryview, item);
2221 folder_clean_cache_memory(item);
2224 gtkut_ctree_set_focus_row(ctree, old_opened);
2225 gtk_cmctree_select(ctree, old_opened);
2226 folderview->opened = old_opened;
2228 folderview->opened = row;
2229 if (gtk_cmctree_node_is_visible(ctree, row)
2230 != GTK_VISIBILITY_FULL)
2231 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2234 STATUSBAR_POP(folderview->mainwin);
2236 folderview->open_folder = FALSE;
2241 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2242 FolderView *folderview)
2246 item = gtk_cmctree_node_get_row_data(ctree, node);
2247 g_return_if_fail(item != NULL);
2248 item->collapsed = FALSE;
2249 folderview_update_node(folderview, node);
2252 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2253 FolderView *folderview)
2257 item = gtk_cmctree_node_get_row_data(ctree, node);
2258 g_return_if_fail(item != NULL);
2259 item->collapsed = TRUE;
2260 folderview_update_node(folderview, node);
2263 static void folderview_popup_close(GtkMenuShell *menu_shell,
2264 FolderView *folderview)
2266 if (!folderview->opened) return;
2268 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2271 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2272 FolderView *folderview)
2274 FolderColumnType type = folderview->col_state[column].type;
2276 prefs_common.folder_col_size[type] = width;
2279 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2283 folderview_create_folder_node(folderview, item);
2285 if (!item || !item->folder || !item->folder->node)
2288 srcnode = item->folder->node;
2289 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2290 srcnode = srcnode->children;
2291 while (srcnode != NULL) {
2292 if (srcnode && srcnode->data) {
2293 FolderItem *next_item = (FolderItem*) srcnode->data;
2294 folderview_create_folder_node_recursive(folderview, next_item);
2296 srcnode = srcnode->next;
2300 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2302 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2303 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2304 GtkCMCTreeNode *node, *parent_node;
2305 gint *col_pos = folderview->col_pos;
2306 FolderItemUpdateData hookdata;
2308 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2309 if (parent_node == NULL)
2312 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2314 text[col_pos[F_COL_FOLDER]] = item->name;
2315 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2320 gtk_cmctree_expand(ctree, parent_node);
2321 gtk_cmctree_node_set_row_data(ctree, node, item);
2323 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2324 folderview_sort_folders(folderview, parent_node, item->folder);
2326 hookdata.item = item;
2327 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2328 hookdata.msg = NULL;
2329 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2331 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2334 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2336 FolderView *folderview = (FolderView *)data;
2337 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2339 GSList *mlist = NULL;
2341 FolderItem *special_trash = NULL;
2344 if (!folderview->selected) return;
2345 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2346 g_return_if_fail(item != NULL);
2347 g_return_if_fail(item->folder != NULL);
2349 if (NULL != (ac = account_find_from_item(item)))
2350 special_trash = account_get_special_folder(ac, F_TRASH);
2352 if (item != item->folder->trash && item != special_trash
2353 && !folder_has_parent_of_type(item, F_TRASH)) return;
2355 if (prefs_common.ask_on_clean) {
2356 if (alertpanel(_("Empty trash"),
2357 _("Delete all messages in trash?"),
2358 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2362 mlist = folder_item_get_msg_list(item);
2364 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2365 MsgInfo * msginfo = (MsgInfo *) cur->data;
2366 if (MSG_IS_LOCKED(msginfo->flags))
2368 /* is it partially received? (partial_recv isn't cached) */
2369 if (msginfo->total_size != 0 &&
2370 msginfo->size != (off_t)msginfo->total_size)
2371 partial_mark_for_delete(msginfo);
2373 procmsg_msg_list_free(mlist);
2375 folder_item_remove_all_msg(item);
2378 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2380 FolderView *folderview = (FolderView *)data;
2381 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2383 FolderItem *special_queue = NULL;
2385 gchar *errstr = NULL;
2387 if (!folderview->selected) return;
2388 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2389 g_return_if_fail(item != NULL);
2390 g_return_if_fail(item->folder != NULL);
2392 if (NULL != (ac = account_find_from_item(item)))
2393 special_queue = account_get_special_folder(ac, F_QUEUE);
2395 if (item != item->folder->queue && item != special_queue
2396 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2398 if (procmsg_queue_is_empty(item))
2401 if (prefs_common.work_offline)
2402 if (alertpanel(_("Offline warning"),
2403 _("You're working offline. Override?"),
2404 GTK_STOCK_NO, GTK_STOCK_YES,
2405 NULL) != G_ALERTALTERNATE)
2408 /* ask for confirmation before sending queued messages only
2409 in online mode and if there is at least one message queued
2410 in any of the folder queue
2412 if (prefs_common.confirm_send_queued_messages) {
2413 if (!prefs_common.work_offline) {
2414 if (alertpanel(_("Send queued messages"),
2415 _("Send all queued messages?"),
2416 GTK_STOCK_CANCEL, _("_Send"),
2417 NULL) != G_ALERTALTERNATE)
2422 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2424 alertpanel_error_log(_("Some errors occurred while "
2425 "sending queued messages."));
2427 alertpanel_error_log(_("Some errors occurred "
2428 "while sending queued messages:\n%s"), errstr);
2434 static void folderview_search_cb(GtkAction *action, gpointer data)
2436 FolderView *folderview = (FolderView *)data;
2437 summary_search(folderview->summaryview);
2440 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2442 FolderView *folderview = (FolderView *)data;
2443 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2446 if (!folderview->selected) return;
2448 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2449 g_return_if_fail(item != NULL);
2450 g_return_if_fail(item->folder != NULL);
2452 item->processing_pending = TRUE;
2453 folder_item_apply_processing(item);
2454 item->processing_pending = FALSE;
2457 static void folderview_property_cb(GtkAction *action, gpointer data)
2459 FolderView *folderview = (FolderView *)data;
2460 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2463 if (!folderview->selected) return;
2465 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2466 g_return_if_fail(item != NULL);
2467 g_return_if_fail(item->folder != NULL);
2469 prefs_folder_item_open(item);
2472 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2474 GSList *list = NULL;
2475 GSList *done = NULL;
2476 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2478 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2479 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2480 && list->data != node) {
2481 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2482 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2485 for (list = done; list != NULL; list = g_slist_next(list)) {
2486 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2492 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2493 FolderItem *to_folder, gboolean copy)
2495 FolderItem *from_parent = NULL;
2496 FolderItem *new_folder = NULL;
2497 GtkCMCTreeNode *src_node = NULL;
2501 g_return_if_fail(folderview != NULL);
2502 g_return_if_fail(from_folder != NULL);
2503 g_return_if_fail(to_folder != NULL);
2505 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2506 from_parent = folder_item_parent(from_folder);
2508 if (prefs_common.warn_dnd) {
2509 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2510 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2511 from_folder->name, to_folder->name);
2512 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2513 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2514 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2517 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2519 else if (status & G_ALERTDISABLE)
2520 prefs_common.warn_dnd = FALSE;
2523 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2524 from_folder->name, to_folder->name);
2525 STATUSBAR_PUSH(folderview->mainwin, buf);
2527 summary_clear_all(folderview->summaryview);
2528 folderview->opened = NULL;
2529 folderview->selected = NULL;
2530 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2532 main_window_cursor_wait(folderview->mainwin);
2534 statusbar_verbosity_set(FALSE);
2535 folder_item_update_freeze();
2536 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2537 statusbar_verbosity_set(FALSE);
2538 main_window_cursor_normal(folderview->mainwin);
2539 STATUSBAR_POP(folderview->mainwin);
2540 folder_item_update_thaw();
2541 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2543 folderview_sort_folders(folderview,
2544 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2545 NULL, to_folder), new_folder->folder);
2546 folderview_select(folderview, new_folder);
2548 statusbar_verbosity_set(FALSE);
2549 main_window_cursor_normal(folderview->mainwin);
2550 STATUSBAR_POP(folderview->mainwin);
2551 folder_item_update_thaw();
2553 case F_MOVE_FAILED_DEST_IS_PARENT:
2554 alertpanel_error(_("Source and destination are the same."));
2556 case F_MOVE_FAILED_DEST_IS_CHILD:
2557 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2558 _("Can't move a folder to one of its children."));
2560 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2561 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2564 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2569 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2572 static gint folderview_clist_compare(GtkCMCList *clist,
2573 gconstpointer ptr1, gconstpointer ptr2)
2575 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2576 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2578 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2580 return item1->order - item2->order;
2583 // if only one folder has an order it comes first
2584 if (item1->order > 0)
2588 if (item2->order > 0)
2594 return (item2->name != NULL);
2598 return g_utf8_collate(item1->name, item2->name);
2601 static void folderview_processing_cb(GtkAction *action, gpointer data)
2603 FolderView *folderview = (FolderView *)data;
2604 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2608 if (!folderview->selected) return;
2610 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2611 g_return_if_fail(item != NULL);
2612 g_return_if_fail(item->folder != NULL);
2614 id = folder_item_get_identifier(item);
2615 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2618 prefs_filtering_open(&item->prefs->processing, title,
2619 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2623 void folderview_set_target_folder_color(gint color_op)
2627 FolderView *folderview;
2629 for (list = folderview_list; list != NULL; list = list->next) {
2630 folderview = (FolderView *)list->data;
2631 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2633 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2634 folderview->color_op;
2640 static gchar *last_smallfont = NULL;
2641 static gchar *last_normalfont = NULL;
2642 static gchar *last_boldfont = NULL;
2643 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2646 g_free(last_smallfont);
2647 last_smallfont = NULL;
2648 g_free(last_normalfont);
2649 last_normalfont = NULL;
2650 g_free(last_boldfont);
2651 last_boldfont = NULL;
2654 void folderview_reflect_prefs(void)
2656 gboolean update_font = FALSE;
2657 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2658 FolderItem *item = folderview_get_selected_item(folderview);
2659 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2660 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2661 gint height = pos->value;
2663 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2664 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2665 !last_boldfont || strcmp(last_boldfont, BOLD_FONT))
2668 g_free(last_smallfont);
2669 last_smallfont = g_strdup(SMALL_FONT);
2670 g_free(last_normalfont);
2671 last_normalfont = g_strdup(NORMAL_FONT);
2672 g_free(last_boldfont);
2673 last_boldfont = g_strdup(BOLD_FONT);
2676 normal_style = normal_color_style = bold_style =
2677 bold_color_style = bold_tgtfold_style = NULL;
2679 folderview_init(folderview);
2681 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2682 folderview_column_set_titles(folderview);
2683 folderview_set_all();
2685 g_signal_handlers_block_by_func
2686 (G_OBJECT(folderview->ctree),
2687 G_CALLBACK(folderview_selected), folderview);
2690 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2691 GTK_CMCTREE(folderview->ctree), NULL, item);
2693 folderview_select(folderview, item);
2694 folderview->open_folder = FALSE;
2695 folderview->selected = node;
2698 g_signal_handlers_unblock_by_func
2699 (G_OBJECT(folderview->ctree),
2700 G_CALLBACK(folderview_selected), folderview);
2702 pos = gtk_scrolled_window_get_vadjustment(
2703 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2704 gtk_adjustment_set_value(pos, height);
2705 gtk_adjustment_changed(pos);
2706 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2709 static void drag_state_stop(FolderView *folderview)
2711 if (folderview->drag_timer)
2712 g_source_remove(folderview->drag_timer);
2713 folderview->drag_timer = 0;
2714 folderview->drag_node = NULL;
2717 static gint folderview_defer_expand(FolderView *folderview)
2719 if (folderview->drag_node) {
2720 folderview_recollapse_nodes(folderview, folderview->drag_node);
2721 if (folderview->drag_item->collapsed) {
2722 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2723 folderview->nodes_to_recollapse = g_slist_append
2724 (folderview->nodes_to_recollapse, folderview->drag_node);
2727 folderview->drag_item = NULL;
2728 folderview->drag_timer = 0;
2732 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2734 /* the idea is that we call drag_state_start() whenever we want expansion to
2735 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2736 * we need to call drag_state_stop() */
2737 drag_state_stop(folderview);
2738 /* request expansion */
2739 if (0 != (folderview->drag_timer = g_timeout_add
2740 (prefs_common.hover_timeout,
2741 (GtkFunction)folderview_defer_expand,
2743 folderview->drag_node = node;
2744 folderview->drag_item = item;
2747 #ifndef GENERIC_UMPC
2748 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2749 FolderView *folderview)
2751 GdkDragContext *context;
2753 g_return_if_fail(folderview != NULL);
2754 if (folderview->selected == NULL) return;
2755 if (folderview->nodes_to_recollapse)
2756 g_slist_free(folderview->nodes_to_recollapse);
2757 folderview->nodes_to_recollapse = NULL;
2758 context = gtk_drag_begin(widget, folderview->target_list,
2759 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2760 gtk_drag_set_icon_default(context);
2763 static void folderview_drag_data_get(GtkWidget *widget,
2764 GdkDragContext *drag_context,
2765 GtkSelectionData *selection_data,
2768 FolderView *folderview)
2772 gchar *source = NULL;
2773 if (info == TARGET_DUMMY) {
2774 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2775 cur != NULL; cur = cur->next) {
2776 item = gtk_cmctree_node_get_row_data
2777 (GTK_CMCTREE(folderview->ctree),
2778 GTK_CMCTREE_NODE(cur->data));
2780 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2781 gtk_selection_data_set(selection_data,
2782 selection_data->target, 8,
2783 source, strlen(source));
2789 g_warning("unknown info %d\n", info);
2793 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2795 FolderUpdateData *hookdata;
2796 FolderView *folderview;
2800 folderview = (FolderView *) userdata;
2801 g_return_val_if_fail(hookdata != NULL, FALSE);
2802 g_return_val_if_fail(folderview != NULL, FALSE);
2804 ctree = folderview->ctree;
2805 g_return_val_if_fail(ctree != NULL, FALSE);
2807 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2808 folderview_create_folder_node(folderview, hookdata->item);
2809 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2810 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2811 NULL, folder_item_parent(hookdata->item));
2812 folderview_sort_folders(folderview, node, hookdata->folder);
2813 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2814 GtkCMCTreeNode *node;
2816 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2818 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2819 if (folderview->selected == node)
2820 folderview->selected = NULL;
2821 if (folderview->opened == node)
2822 folderview->opened = NULL;
2824 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2825 folderview_set(folderview);
2830 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2831 GdkDragContext *context,
2835 FolderView *folderview)
2838 FolderItem *item = NULL, *src_item = NULL;
2839 GtkCMCTreeNode *node = NULL;
2840 gboolean acceptable = FALSE;
2841 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2842 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2843 int height = (int)pos->page_size;
2844 int total_height = (int)pos->upper;
2845 int vpos = (int) pos->value;
2846 int offset = prefs_common.show_col_headers ? 24:0;
2848 if (gtk_cmclist_get_selection_info
2849 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2850 GtkWidget *srcwidget;
2852 if (y > height - 24 && height + vpos < total_height) {
2853 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2854 gtk_adjustment_changed(pos);
2856 if (y < 48 && y > 0) {
2857 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2858 gtk_adjustment_changed(pos);
2861 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2862 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2863 src_item = folderview->summaryview->folder_item;
2865 srcwidget = gtk_drag_get_source_widget(context);
2866 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2867 /* comes from summaryview */
2868 /* we are copying messages, so only accept folder items that are not
2869 the source item, are no root items and can copy messages */
2870 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2871 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2872 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2874 } else if (srcwidget == folderview->ctree) {
2875 /* comes from folderview */
2876 /* we are moving folder items, only accept folders that are not
2877 the source items and can copy messages and create folder items */
2878 if (item && item->folder && src_item && src_item != item &&
2879 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2880 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2881 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2882 || item->folder == src_item->folder))
2885 /* comes from another app */
2886 /* we are adding messages, so only accept folder items that are
2887 no root items and can copy messages */
2888 if (item && item->folder && folder_item_parent(item) != NULL
2889 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2890 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2895 if (acceptable || (src_item && src_item == item))
2896 drag_state_start(folderview, node, item);
2899 g_signal_handlers_block_by_func
2901 G_CALLBACK(folderview_selected), folderview);
2902 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2903 g_signal_handlers_unblock_by_func
2905 G_CALLBACK(folderview_selected), folderview);
2906 gdk_drag_status(context,
2907 (context->actions == GDK_ACTION_COPY ?
2908 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2910 if (folderview->opened)
2911 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2912 gdk_drag_status(context, 0, time);
2918 static void folderview_drag_leave_cb(GtkWidget *widget,
2919 GdkDragContext *context,
2921 FolderView *folderview)
2923 drag_state_stop(folderview);
2924 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2927 static void free_info (gpointer stuff, gpointer data)
2932 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2933 guint time, FolderItem *item)
2936 GSList *msglist = NULL;
2937 list = uri_list_extract_filenames(data);
2938 if (!(item && item->folder && folder_item_parent(item) != NULL
2939 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2941 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2942 debug_print("item doesn't fit\n");
2946 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2947 debug_print("list is empty\n");
2950 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2951 MsgFileInfo *info = NULL;
2953 if (file_is_email((gchar *)tmp->data)) {
2954 info = g_new0(MsgFileInfo, 1);
2955 info->msginfo = NULL;
2956 info->file = (gchar *)tmp->data;
2957 msglist = g_slist_prepend(msglist, info);
2958 debug_print("file is a mail\n");
2960 debug_print("file isn't a mail\n");
2964 msglist = g_slist_reverse(msglist);
2965 folder_item_add_msgs(item, msglist, FALSE);
2966 g_slist_foreach(msglist, free_info, NULL);
2967 g_slist_free(msglist);
2968 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2970 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2972 list_free_strings(list);
2976 static void folderview_drag_received_cb(GtkWidget *widget,
2977 GdkDragContext *drag_context,
2980 GtkSelectionData *data,
2983 FolderView *folderview)
2986 FolderItem *item = NULL, *src_item;
2987 GtkCMCTreeNode *node;
2988 int offset = prefs_common.show_col_headers ? 24:0;
2990 if (info == TARGET_DUMMY) {
2991 drag_state_stop(folderview);
2992 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2993 /* comes from summaryview */
2994 if (gtk_cmclist_get_selection_info
2995 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2998 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2999 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3000 src_item = folderview->summaryview->folder_item;
3002 if (item->no_select) {
3003 alertpanel_error(_("The destination folder can only be used to "
3004 "store subfolders."));
3007 /* re-check (due to acceptable possibly set for folder moves */
3008 if (!(item && item->folder && item->path && !item->no_select &&
3009 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3012 if (item && src_item) {
3013 switch (drag_context->action) {
3014 case GDK_ACTION_COPY:
3015 summary_copy_selected_to(folderview->summaryview, item);
3016 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3018 case GDK_ACTION_MOVE:
3019 case GDK_ACTION_DEFAULT:
3021 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3022 summary_copy_selected_to(folderview->summaryview, item);
3024 summary_move_selected_to(folderview->summaryview, item);
3025 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3028 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3030 /* comes from folderview */
3032 gboolean folder_is_normal = TRUE;
3033 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3035 source = data->data + 17;
3036 if (gtk_cmclist_get_selection_info
3037 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3039 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3042 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3043 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3044 src_item = folder_find_item_from_identifier(source);
3048 src_item->stype == F_NORMAL &&
3049 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3050 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3051 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3052 !folder_has_parent_of_type(src_item, F_TRASH);
3053 if (!item || !src_item || !folder_is_normal) {
3054 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3058 folderview_move_folder(folderview, src_item, item, copy);
3059 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3061 folderview->nodes_to_recollapse = NULL;
3062 } else if (info == TARGET_MAIL_URI_LIST) {
3063 if (gtk_cmclist_get_selection_info
3064 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3067 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3069 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3070 debug_print("no node\n");
3073 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3075 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3076 debug_print("no item\n");
3079 folderview_finish_dnd(data->data, drag_context, time, item);
3083 static void folderview_drag_end_cb(GtkWidget *widget,
3084 GdkDragContext *drag_context,
3085 FolderView *folderview)
3087 drag_state_stop(folderview);
3088 g_slist_free(folderview->nodes_to_recollapse);
3089 folderview->nodes_to_recollapse = NULL;
3092 void folderview_register_popup(FolderViewPopup *fpopup)
3096 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3097 FolderView *folderview = folderviews->data;
3098 GtkActionGroup *factory;
3100 factory = create_action_group(folderview, fpopup);
3101 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3103 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3106 void folderview_unregister_popup(FolderViewPopup *fpopup)
3111 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3112 FolderView *folderview = folderviews->data;
3114 g_hash_table_remove(folderview->popups, fpopup->klass);
3116 g_hash_table_remove(folderview_popups, fpopup->klass);