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 void folderview_fast_rescan_tree(Folder *folder)
1092 MainWindow *mainwin = mainwindow_get_mainwindow();
1093 FolderView *folderview = NULL;
1094 GtkAdjustment *pos = NULL;
1097 cm_return_if_fail(folder != NULL);
1099 if (!folder->klass->scan_tree) return;
1103 window = label_window_create(_("Scanning folder tree..."));
1106 folderview = mainwin->folderview;
1109 pos = gtk_scrolled_window_get_vadjustment(
1110 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1111 height = gtk_adjustment_get_value(pos);
1114 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1115 folder_fast_scan_tree(folder);
1116 folder_set_ui_func(folder, NULL, NULL);
1118 folderview_set_all();
1121 pos = gtk_scrolled_window_get_vadjustment(
1122 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1123 gtk_adjustment_set_value(pos, height);
1124 gtk_adjustment_changed(pos);
1126 label_window_destroy(window);
1130 /** folderview_check_new()
1131 * Scan and update the folder and return the
1132 * count the number of new messages since last check.
1133 * \param folder the folder to check for new messages
1134 * \return the number of new messages since last check
1136 gint folderview_check_new(Folder *folder)
1140 FolderView *folderview;
1142 GtkCMCTreeNode *node;
1144 gint former_new_msgs = 0;
1145 gint former_new = 0, former_unread = 0, former_total;
1147 for (list = folderview_list; list != NULL; list = list->next) {
1148 folderview = (FolderView *)list->data;
1149 ctree = GTK_CMCTREE(folderview->ctree);
1150 folderview->scanning_folder = folder;
1152 main_window_lock(folderview->mainwin);
1154 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1155 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1157 item = gtk_cmctree_node_get_row_data(ctree, node);
1158 if (!item || !item->path || !item->folder) continue;
1159 if (item->no_select) continue;
1160 if (folder && folder != item->folder) continue;
1161 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1162 if (!item->prefs->newmailcheck) continue;
1163 if (item->processing_pending == TRUE) {
1164 debug_print("skipping %s, processing pending\n",
1165 item->path ? item->path : item->name);
1168 if (item->scanning != ITEM_NOT_SCANNING) {
1169 debug_print("skipping %s, scanning\n",
1170 item->path ? item->path : item->name);
1174 str = get_scan_str(item);
1176 STATUSBAR_PUSH(folderview->mainwin, str);
1180 folderview_scan_tree_func(item->folder, item, NULL);
1181 former_new = item->new_msgs;
1182 former_unread = item->unread_msgs;
1183 former_total = item->total_msgs;
1185 if (item->folder->klass->scan_required &&
1186 (item->folder->klass->scan_required(item->folder, item) ||
1187 item->folder->inbox == item ||
1188 item->opened == TRUE ||
1189 item->processing_pending == TRUE)) {
1190 if (folder_item_scan(item) < 0) {
1192 summaryview_unlock(folderview->summaryview, item);
1193 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1194 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1195 item->path ? item->path:item->name);
1196 STATUSBAR_POP(folderview->mainwin);
1198 } else if (!FOLDER_IS_LOCAL(folder)) {
1199 STATUSBAR_POP(folderview->mainwin);
1204 } else if (!item->folder->klass->scan_required) {
1205 if (folder_item_scan(item) < 0) {
1206 summaryview_unlock(folderview->summaryview, item);
1207 if (folder && !FOLDER_IS_LOCAL(folder)) {
1208 STATUSBAR_POP(folderview->mainwin);
1213 if (former_new != item->new_msgs ||
1214 former_unread != item->unread_msgs ||
1215 former_total != item->total_msgs)
1216 folderview_update_node(folderview, node);
1218 new_msgs += item->new_msgs;
1219 former_new_msgs += former_new;
1220 STATUSBAR_POP(folderview->mainwin);
1222 folderview->scanning_folder = NULL;
1223 main_window_unlock(folderview->mainwin);
1227 folder_write_list();
1228 /* Number of new messages since last check is the just the difference
1229 * between former_new_msgs and new_msgs. If new_msgs is less than
1230 * former_new_msgs, that would mean another session accessed the folder
1231 * and the result is not well defined.
1233 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1237 void folderview_check_new_all(void)
1241 FolderView *folderview;
1243 folderview = (FolderView *)folderview_list->data;
1246 main_window_lock(folderview->mainwin);
1247 window = label_window_create
1248 (_("Checking for new messages in all folders..."));
1250 list = folder_get_list();
1251 for (; list != NULL; list = list->next) {
1252 Folder *folder = list->data;
1254 folderview_check_new(folder);
1257 folder_write_list();
1258 folderview_set_all();
1260 label_window_destroy(window);
1261 main_window_unlock(folderview->mainwin);
1265 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1271 if (!item || !item->folder || !item->folder->node)
1274 node = item->folder->node;
1276 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1277 node = node->children;
1280 (item->new_msgs > 0 ||
1281 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1285 while (node != NULL) {
1286 if (node && node->data) {
1287 FolderItem *next_item = (FolderItem*) node->data;
1289 if (folderview_have_new_children_sub(folderview,
1298 static gboolean folderview_have_new_children(FolderView *folderview,
1301 return folderview_have_new_children_sub(folderview, item, FALSE);
1304 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1310 if (!item || !item->folder || !item->folder->node)
1313 node = item->folder->node;
1315 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1316 node = node->children;
1319 (item->unread_msgs > 0 ||
1320 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1324 while (node != NULL) {
1325 if (node && node->data) {
1326 FolderItem *next_item = (FolderItem*) node->data;
1328 if (folderview_have_unread_children_sub(folderview,
1338 static gboolean folderview_have_unread_children(FolderView *folderview,
1341 return folderview_have_unread_children_sub(folderview, item, FALSE);
1344 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1350 if (!item || !item->folder || !item->folder->node)
1353 node = item->folder->node;
1355 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1356 node = node->children;
1358 if (in_sub && item->search_match){
1362 while (node != NULL) {
1363 if (node && node->data) {
1364 FolderItem *next_item = (FolderItem*) node->data;
1366 if (folderview_have_matching_children_sub(folderview,
1376 static gboolean folderview_have_matching_children(FolderView *folderview,
1379 return folderview_have_matching_children_sub(folderview, item, FALSE);
1382 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1388 if (!item || !item->folder || !item->folder->node)
1391 node = item->folder->node;
1393 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1394 node = node->children;
1396 if (item->marked_msgs != 0) {
1400 while (node != NULL) {
1401 if (node && node->data) {
1402 FolderItem *next_item = (FolderItem*) node->data;
1404 if (folderview_have_marked_children_sub(folderview,
1413 static gboolean folderview_have_marked_children(FolderView *folderview,
1416 return folderview_have_marked_children_sub(folderview, item, FALSE);
1419 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1421 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1422 GtkStyle *style = NULL;
1423 GtkStyle *color_style = NULL;
1425 GdkPixbuf *xpm, *openxpm;
1426 static GdkPixbuf *searchicon;
1427 gboolean mark = FALSE;
1430 gboolean add_unread_mark;
1431 gboolean add_sub_match_mark;
1432 gboolean use_bold, use_color;
1433 gint *col_pos = folderview->col_pos;
1434 SpecialFolderItemType stype;
1436 item = gtk_cmctree_node_get_row_data(ctree, node);
1437 cm_return_if_fail(item != NULL);
1439 if (!GTK_CMCTREE_ROW(node)->expanded)
1440 mark = folderview_have_marked_children(folderview, item);
1442 mark = (item->marked_msgs != 0);
1444 stype = item->stype;
1445 if (stype == F_NORMAL) {
1446 if (folder_has_parent_of_type(item, F_TRASH))
1448 else if (folder_has_parent_of_type(item, F_DRAFT))
1450 else if (folder_has_parent_of_type(item, F_OUTBOX))
1452 else if (folder_has_parent_of_type(item, F_QUEUE))
1457 if (item->hide_read_msgs || item->hide_read_threads) {
1458 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1459 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1461 xpm = mark?m_inboxxpm:inboxxpm;
1462 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1466 if (item->hide_read_msgs || item->hide_read_threads) {
1467 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1468 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1470 xpm = mark?m_outboxxpm:outboxxpm;
1471 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1475 if (item->hide_read_msgs || item->hide_read_threads) {
1476 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1477 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1479 xpm = mark?m_queuexpm:queuexpm;
1480 openxpm = mark?m_queueopenxpm:queueopenxpm;
1484 if (item->hide_read_msgs || item->hide_read_threads) {
1485 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1486 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1488 xpm = mark?m_trashxpm:trashxpm;
1489 openxpm = mark?m_trashopenxpm:trashopenxpm;
1493 xpm = mark?m_draftsxpm:draftsxpm;
1494 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1497 if (item->hide_read_msgs || item->hide_read_threads) {
1498 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1499 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1501 xpm = mark?m_folderxpm:folderxpm;
1502 openxpm = mark?m_folderopenxpm:folderopenxpm;
1506 if (item->no_select) {
1507 xpm = openxpm = noselectxpm;
1510 name = folder_item_get_name(item);
1512 if (!GTK_CMCTREE_ROW(node)->expanded) {
1513 add_unread_mark = folderview_have_unread_children(
1515 add_sub_match_mark = folderview_have_matching_children(
1518 add_unread_mark = FALSE;
1519 add_sub_match_mark = FALSE;
1522 if (item->search_match) {
1524 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1527 xpm = openxpm = searchicon;
1531 if (prefs_common.display_folder_unread) {
1532 if (folder_has_parent_of_type(item, F_QUEUE)) {
1533 /* only total_msgs matters here */
1534 if (item->total_msgs > 0) {
1535 /* show total number (should be equal to the unread number)
1537 str = g_strdup_printf("%s (%d%s%s)",
1538 name, item->total_msgs,
1539 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1540 (item->unreadmarked_msgs > 0) ? "!" : "");
1543 if (prefs_common.display_folder_unread == 1) {
1544 if (item->unread_msgs > 0) {
1545 /* show unread number and signs */
1546 str = g_strdup_printf("%s (%d%s%s)",
1547 name, item->unread_msgs,
1548 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1549 (item->unreadmarked_msgs > 0) ? "!" : "");
1552 if (item->total_msgs > 0) {
1553 /* show unread number, total number and signs if any */
1554 str = g_strdup_printf("%s (%d/%d%s%s)",
1555 name, item->unread_msgs, item->total_msgs,
1556 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1557 (item->unreadmarked_msgs > 0) ? "!" : "");
1561 if ((str == NULL) &&
1562 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1563 /* no unread/total numbers, but at least one sign */
1564 str = g_strdup_printf("%s (%s%s)",
1566 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1567 (item->unreadmarked_msgs > 0) ? "!" : "");
1571 /* last fallback, folder name only or with +! sign */
1572 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1573 str = g_strdup_printf("%s%s",
1575 } else if (item->unreadmarked_msgs > 0) {
1576 str = g_strdup_printf("%s%s",
1578 } else if (add_sub_match_mark) {
1579 str = g_strdup_printf("%s%s",
1582 str = g_strdup_printf("%s", name);
1585 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1587 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1591 if (!folder_item_parent(item)) {
1592 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1593 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1594 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1596 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1597 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1598 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1601 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1602 folder_has_parent_of_type(item, F_TRASH)) {
1603 use_bold = use_color = FALSE;
1604 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1605 GSList *list = folder_item_get_msg_list(item);
1607 use_bold = use_color = FALSE;
1608 for (cur = list; cur; cur = cur->next) {
1609 MsgInfo *msginfo = (MsgInfo *)cur->data;
1610 if (!MSG_IS_DELETED(msginfo->flags)) {
1611 /* highlight queue folder if there are any messages */
1612 use_bold = use_color = TRUE;
1616 procmsg_msg_list_free(list);
1618 /* if unread messages exist, print with bold font */
1619 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1621 /* if new messages exist, print with colored letter */
1623 (item->new_msgs > 0) ||
1625 folderview_have_new_children(folderview, item));
1628 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1633 if (item->prefs->color > 0 && !use_color) {
1634 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1635 color_style = gtk_style_copy(bold_style);
1636 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1637 style = color_style;
1638 } else if (use_color) {
1639 style = bold_color_style;
1642 if (item->op_count > 0) {
1643 style = bold_tgtfold_style;
1645 } else if (use_color) {
1646 style = normal_color_style;
1647 gtk_cmctree_node_set_foreground(ctree, node,
1648 &folderview->color_new);
1649 } else if (item->op_count > 0) {
1650 style = bold_tgtfold_style;
1651 } else if (item->prefs->color > 0) {
1653 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1654 color_style = gtk_style_copy(normal_style);
1655 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1656 style = color_style;
1658 style = normal_style;
1661 gtk_cmctree_node_set_row_style(ctree, node, style);
1663 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1664 folderview_update_node(folderview, node);
1667 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1670 FolderView *folderview;
1672 GtkCMCTreeNode *node;
1674 cm_return_if_fail(item != NULL);
1676 for (list = folderview_list; list != NULL; list = list->next) {
1677 folderview = (FolderView *)list->data;
1678 ctree = GTK_CMCTREE(folderview->ctree);
1680 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1681 if (node && item->search_match != matches) {
1682 item->search_match = matches;
1683 folderview_update_node(folderview, node);
1688 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1690 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1691 FolderView *folderview = (FolderView *)data;
1693 GtkCMCTreeNode *node;
1694 cm_return_val_if_fail(update_info != NULL, TRUE);
1695 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1696 cm_return_val_if_fail(folderview != NULL, FALSE);
1698 ctree = GTK_CMCTREE(folderview->ctree);
1700 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1703 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1704 folderview_update_node(folderview, node);
1706 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1707 update_info->item == folderview->summaryview->folder_item &&
1708 update_info->item != NULL)
1709 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1710 summary_show(folderview->summaryview, update_info->item);
1716 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1717 GNode *gnode, GtkCMCTreeNode *cnode,
1720 FolderView *folderview = (FolderView *)data;
1721 FolderItem *item = FOLDER_ITEM(gnode->data);
1723 cm_return_val_if_fail(item != NULL, FALSE);
1725 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1726 folderview_update_node(folderview, cnode);
1731 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1734 FolderView *folderview = (FolderView *)data;
1737 if (GTK_CMCTREE_ROW(node)->children) {
1738 item = gtk_cmctree_node_get_row_data(ctree, node);
1739 cm_return_if_fail(item != NULL);
1741 if (!item->collapsed)
1742 gtk_cmctree_expand(ctree, node);
1744 folderview_update_node(folderview, node);
1748 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1749 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1752 GtkCMCTreeNode *node, *parent, *sibling;
1754 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1756 g_warning("%s not found.\n", item->path);
1758 parent = GTK_CMCTREE_ROW(node)->parent;
1759 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1760 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1762 sibling = GTK_CMCTREE_ROW(parent)->children;
1766 tmp = gtk_cmctree_node_get_row_data
1768 if (tmp->stype != F_NORMAL)
1769 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1773 if (node != sibling)
1774 gtk_cmctree_move(ctree, node, parent, sibling);
1781 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1784 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1785 GtkCMCTreeNode *prev = NULL;
1787 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1788 gtk_sctree_sort_recursive(ctree, root);
1789 if (root && GTK_CMCTREE_ROW(root)->parent) {
1790 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1793 set_special_folder(ctree, folder->inbox, root, &prev);
1794 set_special_folder(ctree, folder->outbox, root, &prev);
1795 set_special_folder(ctree, folder->draft, root, &prev);
1796 set_special_folder(ctree, folder->queue, root, &prev);
1797 set_special_folder(ctree, folder->trash, root, &prev);
1798 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1801 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1803 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1804 GtkCMCTreeNode *root;
1806 cm_return_if_fail(folder != NULL);
1808 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1809 folderview_gnode_func, folderview);
1810 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1812 folderview_sort_folders(folderview, root, folder);
1815 /* callback functions */
1816 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1817 GdkEventButton *event)
1821 FolderViewPopup *fpopup;
1822 GtkActionGroup *action_group;
1824 FolderItem *special_trash = NULL, *special_queue = NULL;
1826 GtkUIManager *ui_manager = gtk_ui_manager_new();
1828 if (folderview->ui_manager)
1829 g_object_unref(folderview->ui_manager);
1831 folderview->ui_manager = ui_manager;
1832 item = folderview_get_selected_item(folderview);
1834 cm_return_if_fail(item != NULL);
1835 cm_return_if_fail(item->folder != NULL);
1836 folder = item->folder;
1838 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1841 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1843 fpopup = g_hash_table_lookup(folderview_popups, "common");
1844 action_group = g_hash_table_lookup(folderview->popups, "common");
1847 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1851 if (fpopup->add_menuitems)
1852 fpopup->add_menuitems(ui_manager, item);
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1855 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1856 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1857 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1858 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1861 if (fpopup->set_sensitivity != NULL)
1862 fpopup->set_sensitivity(ui_manager, item);
1864 if (NULL != (ac = account_find_from_item(item))) {
1865 special_trash = account_get_special_folder(ac, F_TRASH);
1866 special_queue = account_get_special_folder(ac, F_QUEUE);
1869 if ((item == folder->trash || item == special_trash
1870 || folder_has_parent_of_type(item, F_TRASH))) {
1871 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1875 if ((item == folder->queue || item == special_queue
1876 || folder_has_parent_of_type(item, F_QUEUE))) {
1877 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1878 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1881 #define SET_SENS(name, sens) \
1882 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1884 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1885 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1886 folderview->selected == folderview->opened);
1887 SET_SENS("FolderViewPopup/Properties", TRUE);
1889 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1890 item->total_msgs >= 1 && !item->processing_pending);
1891 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1892 !item->no_select && !item->processing_pending);
1894 if (item == folder->trash || item == special_trash
1895 || folder_has_parent_of_type(item, F_TRASH)) {
1896 GSList *msglist = folder_item_get_msg_list(item);
1897 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1898 procmsg_msg_list_free(msglist);
1900 if (item == folder->queue || item == special_queue
1901 || folder_has_parent_of_type(item, F_QUEUE)) {
1902 GSList *msglist = folder_item_get_msg_list(item);
1903 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1904 procmsg_msg_list_free(msglist);
1908 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1909 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1910 g_signal_connect(G_OBJECT(popup), "selection_done",
1911 G_CALLBACK(folderview_popup_close),
1913 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1914 event->button, event->time);
1917 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1918 FolderView *folderview)
1920 GtkCMCList *clist = GTK_CMCLIST(ctree);
1921 gint prev_row = -1, row = -1, column = -1;
1923 if (!event) return FALSE;
1925 if (event->button == 1 || event->button == 2) {
1926 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1927 folderview->open_folder = TRUE;
1929 if (event->type == GDK_2BUTTON_PRESS) {
1930 if (clist->selection) {
1931 GtkCMCTreeNode *node;
1933 node = GTK_CMCTREE_NODE(clist->selection->data);
1935 gtk_cmctree_toggle_expansion(
1938 folderview->open_folder = FALSE;
1945 if (event->button == 2 || event->button == 3) {
1947 if (clist->selection) {
1948 GtkCMCTreeNode *node;
1950 node = GTK_CMCTREE_NODE(clist->selection->data);
1952 prev_row = gtkut_ctree_get_nth_from_node
1953 (GTK_CMCTREE(ctree), node);
1956 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1959 if (prev_row != row) {
1960 gtk_cmclist_unselect_all(clist);
1961 if (event->button == 2)
1962 folderview_select_node
1964 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1967 gtk_cmclist_select_row(clist, row, column);
1971 if (event->button != 3) return FALSE;
1973 folderview_set_sens_and_popup_menu(folderview, row, event);
1977 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1978 FolderView *folderview)
1980 int row = -1, column = -1;
1982 if (!event) return FALSE;
1984 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1987 if (event->button == 1 && folderview->open_folder == FALSE &&
1988 folderview->opened != NULL) {
1989 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1990 folderview->opened);
1991 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1997 #define BREAK_ON_MODIFIER_KEY() \
1998 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2000 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2001 FolderView *folderview)
2003 if (!event) return FALSE;
2005 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2008 switch (event->keyval) {
2010 #ifndef GENERIC_UMPC
2011 case GDK_KEY_Return:
2012 case GDK_KEY_KP_Enter:
2014 if (folderview->selected) {
2015 folderview_select_node(folderview,
2016 folderview->selected);
2020 case GDK_KEY_Return:
2021 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2022 gtk_cmctree_toggle_expansion(
2023 GTK_CMCTREE(folderview->ctree),
2024 folderview->selected);
2029 BREAK_ON_MODIFIER_KEY();
2030 if (folderview->selected) {
2031 if (folderview->opened == folderview->selected &&
2032 (!folderview->summaryview->folder_item ||
2033 folderview->summaryview->folder_item->total_msgs == 0))
2034 folderview_select_next_unread(folderview, TRUE);
2036 folderview_select_node(folderview,
2037 folderview->selected);
2047 typedef struct _PostponedSelectData
2050 GtkCMCTreeNode *row;
2052 FolderView *folderview;
2053 } PostponedSelectData;
2055 static gboolean postpone_select(void *data)
2057 PostponedSelectData *psdata = (PostponedSelectData *)data;
2058 debug_print("trying again\n");
2059 psdata->folderview->open_folder = TRUE;
2060 main_window_cursor_normal(psdata->folderview->mainwin);
2061 STATUSBAR_POP(psdata->folderview->mainwin);
2062 folderview_selected(psdata->ctree, psdata->row,
2063 psdata->column, psdata->folderview);
2068 void folderview_close_opened(FolderView *folderview)
2070 if (folderview->opened) {
2071 FolderItem *olditem;
2073 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2074 folderview->opened);
2076 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2077 olditem->path ? olditem->path:olditem->name);
2078 /* will be null if we just moved the previously opened folder */
2079 STATUSBAR_PUSH(folderview->mainwin, buf);
2080 main_window_cursor_wait(folderview->mainwin);
2082 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2083 summary_show(folderview->summaryview, NULL);
2084 folder_item_close(olditem);
2085 main_window_cursor_normal(folderview->mainwin);
2086 STATUSBAR_POP(folderview->mainwin);
2087 if (olditem->folder->klass->item_closed)
2088 olditem->folder->klass->item_closed(olditem);
2093 if (folderview->opened &&
2094 !GTK_CMCTREE_ROW(folderview->opened)->children)
2095 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2097 folderview->opened = NULL;
2099 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2100 gint column, FolderView *folderview)
2102 static gboolean can_select = TRUE; /* exclusive lock */
2107 GtkCMCTreeNode *old_opened = folderview->opened;
2109 folderview->selected = row;
2111 debug_print("newly selected %p, opened %p\n", folderview->selected,
2112 folderview->opened);
2113 if (folderview->opened == row) {
2114 folderview->open_folder = FALSE;
2119 item = gtk_cmctree_node_get_row_data(ctree, row);
2122 folderview->open_folder = FALSE;
2126 if (!can_select || summary_is_locked(folderview->summaryview)) {
2127 if (folderview->opened) {
2128 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2129 gtk_cmctree_select(ctree, folderview->opened);
2131 folderview->open_folder = FALSE;
2136 if (!folderview->open_folder) {
2143 /* Save cache for old folder */
2144 /* We don't want to lose all caches if sylpheed crashed */
2145 /* resets folderview->opened to NULL */
2146 folderview_close_opened(folderview);
2148 /* CLAWS: set compose button type: news folder items
2149 * always have a news folder as parent */
2151 toolbar_set_compose_button
2152 (folderview->mainwin->toolbar,
2153 FOLDER_TYPE(item->folder) == F_NEWS ?
2154 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2157 debug_print("Folder %s is selected\n", item->path);
2159 if (!GTK_CMCTREE_ROW(row)->children)
2160 gtk_cmctree_expand(ctree, row);
2162 /* ungrab the mouse event */
2163 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2164 gtk_grab_remove(GTK_WIDGET(ctree));
2165 if (gdk_pointer_is_grabbed())
2166 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2170 /* TODO: wwp: avoid displaying (null) in the status bar */
2171 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2172 item->path : "(null)");
2173 debug_print("%s\n", buf);
2174 STATUSBAR_PUSH(folderview->mainwin, buf);
2177 main_window_cursor_wait(folderview->mainwin);
2179 if (folderview->scanning_folder == item->folder) {
2182 res = folder_item_open(item);
2185 if (res == -1 && item->no_select == FALSE) {
2186 main_window_cursor_normal(folderview->mainwin);
2187 STATUSBAR_POP(folderview->mainwin);
2189 alertpanel_error(_("Folder could not be opened."));
2191 folderview->open_folder = FALSE;
2195 } else if (res == -2 && item->no_select == FALSE) {
2196 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2197 data->ctree = ctree;
2199 data->column = column;
2200 data->folderview = folderview;
2201 debug_print("postponing open of %s till end of scan\n",
2202 item->path ? item->path:item->name);
2203 folderview->open_folder = FALSE;
2205 g_timeout_add(500, postpone_select, data);
2210 main_window_cursor_normal(folderview->mainwin);
2213 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2214 opened = summary_show(folderview->summaryview, item);
2216 folder_clean_cache_memory(item);
2219 gtkut_ctree_set_focus_row(ctree, old_opened);
2220 gtk_cmctree_select(ctree, old_opened);
2221 folderview->opened = old_opened;
2223 folderview->opened = row;
2224 if (gtk_cmctree_node_is_visible(ctree, row)
2225 != GTK_VISIBILITY_FULL)
2226 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2229 STATUSBAR_POP(folderview->mainwin);
2231 folderview->open_folder = FALSE;
2236 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2237 FolderView *folderview)
2241 item = gtk_cmctree_node_get_row_data(ctree, node);
2242 cm_return_if_fail(item != NULL);
2243 item->collapsed = FALSE;
2244 folderview_update_node(folderview, node);
2247 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2248 FolderView *folderview)
2252 item = gtk_cmctree_node_get_row_data(ctree, node);
2253 cm_return_if_fail(item != NULL);
2254 item->collapsed = TRUE;
2255 folderview_update_node(folderview, node);
2258 static void folderview_popup_close(GtkMenuShell *menu_shell,
2259 FolderView *folderview)
2261 if (!folderview->opened) return;
2263 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2266 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2267 FolderView *folderview)
2269 FolderColumnType type = folderview->col_state[column].type;
2271 prefs_common.folder_col_size[type] = width;
2274 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2276 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2277 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2278 GtkCMCTreeNode *node, *parent_node;
2279 gint *col_pos = folderview->col_pos;
2280 FolderItemUpdateData hookdata;
2282 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2283 if (parent_node == NULL)
2286 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2288 text[col_pos[F_COL_FOLDER]] = item->name;
2289 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2294 gtk_cmctree_expand(ctree, parent_node);
2295 gtk_cmctree_node_set_row_data(ctree, node, item);
2297 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2298 folderview_sort_folders(folderview, parent_node, item->folder);
2300 hookdata.item = item;
2301 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2302 hookdata.msg = NULL;
2303 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2305 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2308 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2310 FolderView *folderview = (FolderView *)data;
2311 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2313 GSList *mlist = NULL;
2315 FolderItem *special_trash = NULL;
2318 if (!folderview->selected) return;
2319 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2320 cm_return_if_fail(item != NULL);
2321 cm_return_if_fail(item->folder != NULL);
2323 if (NULL != (ac = account_find_from_item(item)))
2324 special_trash = account_get_special_folder(ac, F_TRASH);
2326 if (item != item->folder->trash && item != special_trash
2327 && !folder_has_parent_of_type(item, F_TRASH)) return;
2329 if (prefs_common.ask_on_clean) {
2330 if (alertpanel(_("Empty trash"),
2331 _("Delete all messages in trash?"),
2332 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2336 mlist = folder_item_get_msg_list(item);
2338 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2339 MsgInfo * msginfo = (MsgInfo *) cur->data;
2340 if (MSG_IS_LOCKED(msginfo->flags))
2342 /* is it partially received? (partial_recv isn't cached) */
2343 if (msginfo->total_size != 0 &&
2344 msginfo->size != (off_t)msginfo->total_size)
2345 partial_mark_for_delete(msginfo);
2347 procmsg_msg_list_free(mlist);
2349 folder_item_remove_all_msg(item);
2352 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2354 FolderView *folderview = (FolderView *)data;
2355 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2357 FolderItem *special_queue = NULL;
2359 gchar *errstr = NULL;
2361 if (!folderview->selected) return;
2362 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2363 cm_return_if_fail(item != NULL);
2364 cm_return_if_fail(item->folder != NULL);
2366 if (NULL != (ac = account_find_from_item(item)))
2367 special_queue = account_get_special_folder(ac, F_QUEUE);
2369 if (item != item->folder->queue && item != special_queue
2370 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2372 if (procmsg_queue_is_empty(item))
2375 if (prefs_common.work_offline)
2376 if (alertpanel(_("Offline warning"),
2377 _("You're working offline. Override?"),
2378 GTK_STOCK_NO, GTK_STOCK_YES,
2379 NULL) != G_ALERTALTERNATE)
2382 /* ask for confirmation before sending queued messages only
2383 in online mode and if there is at least one message queued
2384 in any of the folder queue
2386 if (prefs_common.confirm_send_queued_messages) {
2387 if (!prefs_common.work_offline) {
2388 if (alertpanel(_("Send queued messages"),
2389 _("Send all queued messages?"),
2390 GTK_STOCK_CANCEL, _("_Send"),
2391 NULL) != G_ALERTALTERNATE)
2396 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2398 alertpanel_error_log(_("Some errors occurred while "
2399 "sending queued messages."));
2401 alertpanel_error_log(_("Some errors occurred "
2402 "while sending queued messages:\n%s"), errstr);
2408 static void folderview_search_cb(GtkAction *action, gpointer data)
2410 FolderView *folderview = (FolderView *)data;
2411 summary_search(folderview->summaryview);
2414 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2416 FolderView *folderview = (FolderView *)data;
2417 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2420 if (!folderview->selected) return;
2422 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2423 cm_return_if_fail(item != NULL);
2424 cm_return_if_fail(item->folder != NULL);
2426 item->processing_pending = TRUE;
2427 folder_item_apply_processing(item);
2428 item->processing_pending = FALSE;
2431 static void folderview_property_cb(GtkAction *action, gpointer data)
2433 FolderView *folderview = (FolderView *)data;
2434 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2437 if (!folderview->selected) return;
2439 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2440 cm_return_if_fail(item != NULL);
2441 cm_return_if_fail(item->folder != NULL);
2443 prefs_folder_item_open(item);
2446 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2448 GSList *list = NULL;
2449 GSList *done = NULL;
2450 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2452 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2453 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2454 && list->data != node) {
2455 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2456 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2459 for (list = done; list != NULL; list = g_slist_next(list)) {
2460 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2466 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2467 FolderItem *to_folder, gboolean copy)
2469 FolderItem *new_folder = NULL;
2473 cm_return_if_fail(folderview != NULL);
2474 cm_return_if_fail(from_folder != NULL);
2475 cm_return_if_fail(to_folder != NULL);
2477 if (prefs_common.warn_dnd) {
2478 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2479 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2480 from_folder->name, to_folder->name);
2481 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2482 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2483 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2486 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2488 else if (status & G_ALERTDISABLE)
2489 prefs_common.warn_dnd = FALSE;
2492 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2493 from_folder->name, to_folder->name);
2494 STATUSBAR_PUSH(folderview->mainwin, buf);
2496 summary_clear_all(folderview->summaryview);
2497 folderview->opened = NULL;
2498 folderview->selected = NULL;
2499 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2501 main_window_cursor_wait(folderview->mainwin);
2503 statusbar_verbosity_set(FALSE);
2504 folder_item_update_freeze();
2505 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2506 statusbar_verbosity_set(FALSE);
2507 main_window_cursor_normal(folderview->mainwin);
2508 STATUSBAR_POP(folderview->mainwin);
2509 folder_item_update_thaw();
2510 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2512 folderview_sort_folders(folderview,
2513 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2514 NULL, to_folder), new_folder->folder);
2515 folderview_select(folderview, new_folder);
2517 statusbar_verbosity_set(FALSE);
2518 main_window_cursor_normal(folderview->mainwin);
2519 STATUSBAR_POP(folderview->mainwin);
2520 folder_item_update_thaw();
2522 case F_MOVE_FAILED_DEST_IS_PARENT:
2523 alertpanel_error(_("Source and destination are the same."));
2525 case F_MOVE_FAILED_DEST_IS_CHILD:
2526 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2527 _("Can't move a folder to one of its children."));
2529 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2530 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2533 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2538 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2541 static gint folderview_clist_compare(GtkCMCList *clist,
2542 gconstpointer ptr1, gconstpointer ptr2)
2544 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2545 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2547 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2549 return item1->order - item2->order;
2552 // if only one folder has an order it comes first
2553 if (item1->order > 0)
2557 if (item2->order > 0)
2563 return (item2->name != NULL);
2567 return g_utf8_collate(item1->name, item2->name);
2570 static void folderview_processing_cb(GtkAction *action, gpointer data)
2572 FolderView *folderview = (FolderView *)data;
2573 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2577 if (!folderview->selected) return;
2579 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2580 cm_return_if_fail(item != NULL);
2581 cm_return_if_fail(item->folder != NULL);
2583 id = folder_item_get_identifier(item);
2584 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2587 prefs_filtering_open(&item->prefs->processing, title,
2588 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2592 void folderview_set_target_folder_color(gint color_op)
2596 FolderView *folderview;
2598 for (list = folderview_list; list != NULL; list = list->next) {
2599 folderview = (FolderView *)list->data;
2600 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2602 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2603 folderview->color_op;
2609 static gchar *last_smallfont = NULL;
2610 static gchar *last_normalfont = NULL;
2611 static gchar *last_boldfont = NULL;
2612 static gboolean last_derive = 0;
2614 void folderview_reinit_fonts(FolderView *folderview)
2617 g_free(last_smallfont);
2618 last_smallfont = NULL;
2619 g_free(last_normalfont);
2620 last_normalfont = NULL;
2621 g_free(last_boldfont);
2622 last_boldfont = NULL;
2625 void folderview_reflect_prefs(void)
2627 gboolean update_font = FALSE;
2628 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2629 FolderItem *item = folderview_get_selected_item(folderview);
2630 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2631 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2632 gint height = gtk_adjustment_get_value(pos);
2634 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2635 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2636 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2637 last_derive != prefs_common.derive_from_normal_font)
2640 g_free(last_smallfont);
2641 last_smallfont = g_strdup(SMALL_FONT);
2642 g_free(last_normalfont);
2643 last_normalfont = g_strdup(NORMAL_FONT);
2644 g_free(last_boldfont);
2645 last_boldfont = g_strdup(BOLD_FONT);
2646 last_derive = prefs_common.derive_from_normal_font;
2649 normal_style = normal_color_style = bold_style =
2650 bold_color_style = bold_tgtfold_style = NULL;
2652 folderview_init(folderview);
2654 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2655 folderview_column_set_titles(folderview);
2656 folderview_set_all();
2658 g_signal_handlers_block_by_func
2659 (G_OBJECT(folderview->ctree),
2660 G_CALLBACK(folderview_selected), folderview);
2663 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2664 GTK_CMCTREE(folderview->ctree), NULL, item);
2666 folderview_select(folderview, item);
2667 folderview->open_folder = FALSE;
2668 folderview->selected = node;
2671 g_signal_handlers_unblock_by_func
2672 (G_OBJECT(folderview->ctree),
2673 G_CALLBACK(folderview_selected), folderview);
2675 pos = gtk_scrolled_window_get_vadjustment(
2676 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2677 gtk_adjustment_set_value(pos, height);
2678 gtk_adjustment_changed(pos);
2679 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2682 static void drag_state_stop(FolderView *folderview)
2684 if (folderview->drag_timer)
2685 g_source_remove(folderview->drag_timer);
2686 folderview->drag_timer = 0;
2687 folderview->drag_node = NULL;
2690 static gboolean folderview_defer_expand(FolderView *folderview)
2692 if (folderview->drag_node) {
2693 folderview_recollapse_nodes(folderview, folderview->drag_node);
2694 if (folderview->drag_item->collapsed) {
2695 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2696 folderview->nodes_to_recollapse = g_slist_append
2697 (folderview->nodes_to_recollapse, folderview->drag_node);
2700 folderview->drag_item = NULL;
2701 folderview->drag_timer = 0;
2705 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2707 /* the idea is that we call drag_state_start() whenever we want expansion to
2708 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2709 * we need to call drag_state_stop() */
2710 drag_state_stop(folderview);
2711 /* request expansion */
2712 if (0 != (folderview->drag_timer = g_timeout_add
2713 (prefs_common.hover_timeout,
2714 (GSourceFunc)folderview_defer_expand,
2716 folderview->drag_node = node;
2717 folderview->drag_item = item;
2720 #ifndef GENERIC_UMPC
2721 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2722 FolderView *folderview)
2724 GdkDragContext *context;
2726 cm_return_if_fail(folderview != NULL);
2727 if (folderview->selected == NULL) return;
2728 if (folderview->nodes_to_recollapse)
2729 g_slist_free(folderview->nodes_to_recollapse);
2730 folderview->nodes_to_recollapse = NULL;
2731 context = gtk_drag_begin(widget, folderview->target_list,
2732 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2733 gtk_drag_set_icon_default(context);
2736 static void folderview_drag_data_get(GtkWidget *widget,
2737 GdkDragContext *drag_context,
2738 GtkSelectionData *selection_data,
2741 FolderView *folderview)
2745 gchar *source = NULL;
2746 if (info == TARGET_DUMMY) {
2747 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2748 cur != NULL; cur = cur->next) {
2749 item = gtk_cmctree_node_get_row_data
2750 (GTK_CMCTREE(folderview->ctree),
2751 GTK_CMCTREE_NODE(cur->data));
2753 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2754 gtk_selection_data_set(selection_data,
2755 gtk_selection_data_get_target(selection_data), 8,
2756 source, strlen(source));
2762 g_warning("unknown info %d\n", info);
2766 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2768 FolderUpdateData *hookdata;
2769 FolderView *folderview;
2773 folderview = (FolderView *) userdata;
2774 cm_return_val_if_fail(hookdata != NULL, FALSE);
2775 cm_return_val_if_fail(folderview != NULL, FALSE);
2777 ctree = folderview->ctree;
2778 cm_return_val_if_fail(ctree != NULL, FALSE);
2780 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2781 folderview_create_folder_node(folderview, hookdata->item);
2782 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2783 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2784 NULL, folder_item_parent(hookdata->item));
2785 folderview_sort_folders(folderview, node, hookdata->folder);
2786 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2787 GtkCMCTreeNode *node;
2789 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2791 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2792 if (folderview->selected == node)
2793 folderview->selected = NULL;
2794 if (folderview->opened == node)
2795 folderview->opened = NULL;
2797 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2798 folderview_set(folderview);
2803 static gboolean folderview_dnd_scroll_cb(gpointer data)
2805 FolderView *folderview = (FolderView *)data;
2806 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2807 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2808 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2809 gint max = (int)gtk_adjustment_get_upper(pos) -
2810 (int)gtk_adjustment_get_page_size(pos);
2812 if (folderview->scroll_value == 0) {
2813 folderview->scroll_timeout_id = -1;
2817 if (folderview->scroll_value > 0 && new_val > max) {
2819 } else if (folderview->scroll_value < 0 && new_val < 0) {
2822 gtk_adjustment_set_value(pos, new_val);
2827 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2828 GdkDragContext *context,
2832 FolderView *folderview)
2835 FolderItem *item = NULL, *src_item = NULL;
2836 GtkCMCTreeNode *node = NULL;
2837 gboolean acceptable = FALSE;
2838 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2839 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2840 int height = (int)gtk_adjustment_get_page_size(pos);
2841 int total_height = (int)gtk_adjustment_get_upper(pos);
2842 int vpos = (int)gtk_adjustment_get_value(pos);
2843 int offset = prefs_common.show_col_headers ? 24:0;
2846 if (gtk_cmclist_get_selection_info
2847 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2848 GtkWidget *srcwidget;
2850 if (y > height - (48 - offset) && height + vpos < total_height) {
2851 dist = -(height - (48 - offset) - y);
2852 folderview->scroll_value = 1.41f * (1+(dist / 6));
2853 } else if (y < 72 - (24 - offset) && y >= 0) {
2854 dist = 72 - (24 - offset) - y;
2855 folderview->scroll_value = -1.41f * (1+(dist / 6));
2857 folderview->scroll_value = 0;
2859 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == -1) {
2860 folderview->scroll_timeout_id =
2861 g_timeout_add(30, folderview_dnd_scroll_cb,
2865 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2866 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2867 src_item = folderview->summaryview->folder_item;
2869 srcwidget = gtk_drag_get_source_widget(context);
2870 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2871 /* comes from summaryview */
2872 /* we are copying messages, so only accept folder items that are not
2873 the source item, are no root items and can copy messages */
2874 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2875 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2876 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2878 } else if (srcwidget == folderview->ctree) {
2879 /* comes from folderview */
2880 /* we are moving folder items, only accept folders that are not
2881 the source items and can copy messages and create folder items */
2882 if (item && item->folder && src_item && src_item != item &&
2883 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2884 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2885 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2886 || item->folder == src_item->folder))
2889 /* comes from another app */
2890 /* we are adding messages, so only accept folder items that are
2891 no root items and can copy messages */
2892 if (item && item->folder && folder_item_parent(item) != NULL
2893 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2894 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2899 if (acceptable || (src_item && src_item == item))
2900 drag_state_start(folderview, node, item);
2903 g_signal_handlers_block_by_func
2905 G_CALLBACK(folderview_selected), folderview);
2906 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2907 g_signal_handlers_unblock_by_func
2909 G_CALLBACK(folderview_selected), folderview);
2910 gdk_drag_status(context,
2911 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2912 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2914 if (folderview->opened)
2915 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2916 gdk_drag_status(context, 0, time);
2922 static void folderview_drag_leave_cb(GtkWidget *widget,
2923 GdkDragContext *context,
2925 FolderView *folderview)
2927 drag_state_stop(folderview);
2928 folderview->scroll_value = 0;
2929 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2932 static void free_info (gpointer stuff, gpointer data)
2937 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2938 guint time, FolderItem *item)
2941 GSList *msglist = NULL;
2942 list = uri_list_extract_filenames(data);
2943 if (!(item && item->folder && folder_item_parent(item) != NULL
2944 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2946 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2947 debug_print("item doesn't fit\n");
2951 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2952 debug_print("list is empty\n");
2955 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2956 MsgFileInfo *info = NULL;
2958 if (file_is_email((gchar *)tmp->data)) {
2959 info = g_new0(MsgFileInfo, 1);
2960 info->msginfo = NULL;
2961 info->file = (gchar *)tmp->data;
2962 msglist = g_slist_prepend(msglist, info);
2963 debug_print("file is a mail\n");
2965 debug_print("file isn't a mail\n");
2969 msglist = g_slist_reverse(msglist);
2970 folder_item_add_msgs(item, msglist, FALSE);
2971 g_slist_foreach(msglist, free_info, NULL);
2972 g_slist_free(msglist);
2973 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2975 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2977 list_free_strings(list);
2981 static void folderview_drag_received_cb(GtkWidget *widget,
2982 GdkDragContext *drag_context,
2985 GtkSelectionData *data,
2988 FolderView *folderview)
2991 FolderItem *item = NULL, *src_item;
2992 GtkCMCTreeNode *node;
2993 int offset = prefs_common.show_col_headers ? 24:0;
2995 folderview->scroll_value = 0;
2997 if (info == TARGET_DUMMY) {
2998 drag_state_stop(folderview);
2999 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3000 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3001 /* comes from summaryview */
3002 if (gtk_cmclist_get_selection_info
3003 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3006 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3007 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3008 src_item = folderview->summaryview->folder_item;
3010 if (item->no_select) {
3011 alertpanel_error(_("The destination folder can only be used to "
3012 "store subfolders."));
3015 /* re-check (due to acceptable possibly set for folder moves */
3016 if (!(item && item->folder && item->path && !item->no_select &&
3017 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3020 if (item && src_item) {
3021 switch (gdk_drag_context_get_selected_action(drag_context)) {
3022 case GDK_ACTION_COPY:
3023 summary_copy_selected_to(folderview->summaryview, item);
3024 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3026 case GDK_ACTION_MOVE:
3027 case GDK_ACTION_DEFAULT:
3029 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3030 summary_copy_selected_to(folderview->summaryview, item);
3032 summary_move_selected_to(folderview->summaryview, item);
3033 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3036 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3038 /* comes from folderview */
3040 gboolean folder_is_normal = TRUE;
3041 gboolean copy = (GDK_ACTION_COPY ==
3042 gdk_drag_context_get_selected_action(drag_context));
3044 source = (char *)gtk_selection_data_get_data(data) + 17;
3045 if (gtk_cmclist_get_selection_info
3046 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3048 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3051 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3052 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3053 src_item = folder_find_item_from_identifier(source);
3057 src_item->stype == F_NORMAL &&
3058 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3059 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3060 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3061 !folder_has_parent_of_type(src_item, F_TRASH);
3062 if (!item || !src_item || !folder_is_normal) {
3063 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3067 folderview_move_folder(folderview, src_item, item, copy);
3068 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3070 folderview->nodes_to_recollapse = NULL;
3071 } else if (info == TARGET_MAIL_URI_LIST) {
3072 if (gtk_cmclist_get_selection_info
3073 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3076 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3078 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3079 debug_print("no node\n");
3082 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3084 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3085 debug_print("no item\n");
3088 folderview_finish_dnd(gtk_selection_data_get_data(data),
3089 drag_context, time, item);
3093 static void folderview_drag_end_cb(GtkWidget *widget,
3094 GdkDragContext *drag_context,
3095 FolderView *folderview)
3097 drag_state_stop(folderview);
3098 folderview->scroll_value = 0;
3099 g_slist_free(folderview->nodes_to_recollapse);
3100 folderview->nodes_to_recollapse = NULL;
3103 void folderview_register_popup(FolderViewPopup *fpopup)
3107 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3108 FolderView *folderview = folderviews->data;
3109 GtkActionGroup *factory;
3111 factory = create_action_group(folderview, fpopup);
3112 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3114 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3117 void folderview_unregister_popup(FolderViewPopup *fpopup)
3122 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3123 FolderView *folderview = folderviews->data;
3125 g_hash_table_remove(folderview->popups, fpopup->klass);
3127 g_hash_table_remove(folderview_popups, fpopup->klass);