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_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;
1279 if (in_sub && item->total_msgs > 0) {
1283 while (node != NULL) {
1284 if (node && node->data) {
1285 FolderItem *next_item = (FolderItem*) node->data;
1287 if (folderview_have_children_sub(folderview,
1296 static gboolean folderview_have_children(FolderView *folderview,
1299 return folderview_have_children_sub(folderview, item, FALSE);
1302 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1308 if (!item || !item->folder || !item->folder->node)
1311 node = item->folder->node;
1313 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1314 node = node->children;
1317 (item->new_msgs > 0 ||
1318 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1322 while (node != NULL) {
1323 if (node && node->data) {
1324 FolderItem *next_item = (FolderItem*) node->data;
1326 if (folderview_have_new_children_sub(folderview,
1335 static gboolean folderview_have_new_children(FolderView *folderview,
1338 return folderview_have_new_children_sub(folderview, item, FALSE);
1341 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1347 if (!item || !item->folder || !item->folder->node)
1350 node = item->folder->node;
1352 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1353 node = node->children;
1356 (item->unread_msgs > 0 ||
1357 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1361 while (node != NULL) {
1362 if (node && node->data) {
1363 FolderItem *next_item = (FolderItem*) node->data;
1365 if (folderview_have_unread_children_sub(folderview,
1375 static gboolean folderview_have_unread_children(FolderView *folderview,
1378 return folderview_have_unread_children_sub(folderview, item, FALSE);
1381 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1387 if (!item || !item->folder || !item->folder->node)
1390 node = item->folder->node;
1392 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1393 node = node->children;
1395 if (in_sub && item->search_match){
1399 while (node != NULL) {
1400 if (node && node->data) {
1401 FolderItem *next_item = (FolderItem*) node->data;
1403 if (folderview_have_matching_children_sub(folderview,
1413 static gboolean folderview_have_matching_children(FolderView *folderview,
1416 return folderview_have_matching_children_sub(folderview, item, FALSE);
1419 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1425 if (!item || !item->folder || !item->folder->node)
1428 node = item->folder->node;
1430 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1431 node = node->children;
1433 if (item->marked_msgs != 0) {
1437 while (node != NULL) {
1438 if (node && node->data) {
1439 FolderItem *next_item = (FolderItem*) node->data;
1441 if (folderview_have_marked_children_sub(folderview,
1450 static gboolean folderview_have_marked_children(FolderView *folderview,
1453 return folderview_have_marked_children_sub(folderview, item, FALSE);
1456 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1458 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1459 GtkStyle *style = NULL;
1460 GtkStyle *color_style = NULL;
1462 GdkPixbuf *xpm, *openxpm;
1463 static GdkPixbuf *searchicon;
1464 gboolean mark = FALSE;
1467 gboolean add_unread_mark;
1468 gboolean add_sub_match_mark;
1469 gboolean use_bold, use_color;
1470 gint *col_pos = folderview->col_pos;
1471 SpecialFolderItemType stype;
1473 item = gtk_cmctree_node_get_row_data(ctree, node);
1474 cm_return_if_fail(item != NULL);
1476 if (!GTK_CMCTREE_ROW(node)->expanded)
1477 mark = folderview_have_marked_children(folderview, item);
1479 mark = (item->marked_msgs != 0);
1481 stype = item->stype;
1482 if (stype == F_NORMAL) {
1483 if (folder_has_parent_of_type(item, F_TRASH))
1485 else if (folder_has_parent_of_type(item, F_DRAFT))
1487 else if (folder_has_parent_of_type(item, F_OUTBOX))
1489 else if (folder_has_parent_of_type(item, F_QUEUE))
1494 if (item->hide_read_msgs || item->hide_read_threads) {
1495 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1496 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1498 xpm = mark?m_inboxxpm:inboxxpm;
1499 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1503 if (item->hide_read_msgs || item->hide_read_threads) {
1504 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1505 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1507 xpm = mark?m_outboxxpm:outboxxpm;
1508 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1512 if (item->hide_read_msgs || item->hide_read_threads) {
1513 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1514 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1516 xpm = mark?m_queuexpm:queuexpm;
1517 openxpm = mark?m_queueopenxpm:queueopenxpm;
1521 if (item->hide_read_msgs || item->hide_read_threads) {
1522 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1523 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1525 xpm = mark?m_trashxpm:trashxpm;
1526 openxpm = mark?m_trashopenxpm:trashopenxpm;
1530 xpm = mark?m_draftsxpm:draftsxpm;
1531 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1534 if (item->hide_read_msgs || item->hide_read_threads) {
1535 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1536 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1538 xpm = mark?m_folderxpm:folderxpm;
1539 openxpm = mark?m_folderopenxpm:folderopenxpm;
1543 if (item->no_select) {
1544 xpm = openxpm = noselectxpm;
1547 name = folder_item_get_name(item);
1549 if (!GTK_CMCTREE_ROW(node)->expanded) {
1550 add_unread_mark = folderview_have_unread_children(
1552 add_sub_match_mark = folderview_have_matching_children(
1555 add_unread_mark = FALSE;
1556 add_sub_match_mark = FALSE;
1559 if (item->search_match) {
1561 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1564 xpm = openxpm = searchicon;
1568 if (prefs_common.display_folder_unread) {
1569 if (folder_has_parent_of_type(item, F_QUEUE)) {
1570 /* only total_msgs matters here */
1571 if (item->total_msgs > 0) {
1572 /* show total number (should be equal to the unread number)
1574 str = g_strdup_printf("%s (%d%s%s)",
1575 name, item->total_msgs,
1576 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1577 (item->unreadmarked_msgs > 0) ? "!" : "");
1580 if (prefs_common.display_folder_unread == 1) {
1581 if (item->unread_msgs > 0) {
1582 /* show unread number and signs */
1583 str = g_strdup_printf("%s (%d%s%s)",
1584 name, item->unread_msgs,
1585 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1586 (item->unreadmarked_msgs > 0) ? "!" : "");
1589 if (item->total_msgs > 0) {
1590 /* show unread number, total number and signs if any */
1591 str = g_strdup_printf("%s (%d/%d%s%s)",
1592 name, item->unread_msgs, item->total_msgs,
1593 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1594 (item->unreadmarked_msgs > 0) ? "!" : "");
1598 if ((str == NULL) &&
1599 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1600 /* no unread/total numbers, but at least one sign */
1601 str = g_strdup_printf("%s (%s%s)",
1603 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1604 (item->unreadmarked_msgs > 0) ? "!" : "");
1608 /* last fallback, folder name only or with +! sign */
1609 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1610 str = g_strdup_printf("%s%s",
1612 } else if (item->unreadmarked_msgs > 0) {
1613 str = g_strdup_printf("%s%s",
1615 } else if (add_sub_match_mark) {
1616 str = g_strdup_printf("%s%s",
1619 str = g_strdup_printf("%s", name);
1622 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1624 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1628 if (!folder_item_parent(item)) {
1629 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1630 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1631 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1633 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1634 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1635 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1638 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1639 folder_has_parent_of_type(item, F_TRASH)) {
1640 use_bold = use_color = FALSE;
1641 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1642 GSList *list = folder_item_get_msg_list(item);
1644 use_bold = use_color = FALSE;
1645 for (cur = list; cur; cur = cur->next) {
1646 MsgInfo *msginfo = (MsgInfo *)cur->data;
1647 if (!MSG_IS_DELETED(msginfo->flags)) {
1648 /* highlight queue folder if there are any messages */
1649 use_bold = use_color = TRUE;
1653 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
1654 use_bold = use_color = item->total_msgs > 0 ||
1655 (!GTK_CMCTREE_ROW(node)->expanded &&
1656 folderview_have_children(folderview, item));
1658 /* if unread messages exist, print with bold font */
1659 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1661 /* if new messages exist, print with colored letter */
1663 (item->new_msgs > 0) ||
1665 folderview_have_new_children(folderview, item));
1668 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1673 if (item->prefs->color > 0 && !use_color) {
1674 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1675 color_style = gtk_style_copy(bold_style);
1676 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1677 style = color_style;
1678 } else if (use_color) {
1679 style = bold_color_style;
1682 if (item->op_count > 0) {
1683 style = bold_tgtfold_style;
1685 } else if (use_color) {
1686 style = normal_color_style;
1687 gtk_cmctree_node_set_foreground(ctree, node,
1688 &folderview->color_new);
1689 } else if (item->op_count > 0) {
1690 style = bold_tgtfold_style;
1691 } else if (item->prefs->color > 0) {
1693 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1694 color_style = gtk_style_copy(normal_style);
1695 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1696 style = color_style;
1698 style = normal_style;
1701 gtk_cmctree_node_set_row_style(ctree, node, style);
1703 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1704 folderview_update_node(folderview, node);
1707 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1710 FolderView *folderview;
1712 GtkCMCTreeNode *node;
1714 cm_return_if_fail(item != NULL);
1716 for (list = folderview_list; list != NULL; list = list->next) {
1717 folderview = (FolderView *)list->data;
1718 ctree = GTK_CMCTREE(folderview->ctree);
1720 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1721 if (node && item->search_match != matches) {
1722 item->search_match = matches;
1723 folderview_update_node(folderview, node);
1728 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1730 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1731 FolderView *folderview = (FolderView *)data;
1733 GtkCMCTreeNode *node;
1734 cm_return_val_if_fail(update_info != NULL, TRUE);
1735 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1736 cm_return_val_if_fail(folderview != NULL, FALSE);
1738 ctree = GTK_CMCTREE(folderview->ctree);
1740 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1743 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1744 folderview_update_node(folderview, node);
1746 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1747 update_info->item == folderview->summaryview->folder_item &&
1748 update_info->item != NULL)
1749 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1750 summary_show(folderview->summaryview, update_info->item);
1756 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1757 GNode *gnode, GtkCMCTreeNode *cnode,
1760 FolderView *folderview = (FolderView *)data;
1761 FolderItem *item = FOLDER_ITEM(gnode->data);
1763 cm_return_val_if_fail(item != NULL, FALSE);
1765 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1766 folderview_update_node(folderview, cnode);
1771 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1774 FolderView *folderview = (FolderView *)data;
1777 if (GTK_CMCTREE_ROW(node)->children) {
1778 item = gtk_cmctree_node_get_row_data(ctree, node);
1779 cm_return_if_fail(item != NULL);
1781 if (!item->collapsed)
1782 gtk_cmctree_expand(ctree, node);
1784 folderview_update_node(folderview, node);
1788 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1789 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1792 GtkCMCTreeNode *node, *parent, *sibling;
1794 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1796 g_warning("%s not found.\n", item->path);
1798 parent = GTK_CMCTREE_ROW(node)->parent;
1799 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1800 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1802 sibling = GTK_CMCTREE_ROW(parent)->children;
1806 tmp = gtk_cmctree_node_get_row_data
1808 if (tmp->stype != F_NORMAL)
1809 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1813 if (node != sibling)
1814 gtk_cmctree_move(ctree, node, parent, sibling);
1821 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1824 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1825 GtkCMCTreeNode *prev = NULL;
1827 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1828 gtk_sctree_sort_recursive(ctree, root);
1829 if (root && GTK_CMCTREE_ROW(root)->parent) {
1830 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1833 set_special_folder(ctree, folder->inbox, root, &prev);
1834 set_special_folder(ctree, folder->outbox, root, &prev);
1835 set_special_folder(ctree, folder->draft, root, &prev);
1836 set_special_folder(ctree, folder->queue, root, &prev);
1837 set_special_folder(ctree, folder->trash, root, &prev);
1838 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1841 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1843 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1844 GtkCMCTreeNode *root;
1846 cm_return_if_fail(folder != NULL);
1848 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1849 folderview_gnode_func, folderview);
1850 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1852 folderview_sort_folders(folderview, root, folder);
1855 /* callback functions */
1856 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1857 GdkEventButton *event)
1861 FolderViewPopup *fpopup;
1862 GtkActionGroup *action_group;
1864 FolderItem *special_trash = NULL, *special_queue = NULL;
1866 GtkUIManager *ui_manager = gtk_ui_manager_new();
1868 if (folderview->ui_manager)
1869 g_object_unref(folderview->ui_manager);
1871 folderview->ui_manager = ui_manager;
1872 item = folderview_get_selected_item(folderview);
1874 cm_return_if_fail(item != NULL);
1875 cm_return_if_fail(item->folder != NULL);
1876 folder = item->folder;
1878 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1881 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1883 fpopup = g_hash_table_lookup(folderview_popups, "common");
1884 action_group = g_hash_table_lookup(folderview->popups, "common");
1887 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1888 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1889 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1891 if (fpopup->add_menuitems)
1892 fpopup->add_menuitems(ui_manager, item);
1894 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1895 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1896 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1897 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1898 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1899 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1901 if (fpopup->set_sensitivity != NULL)
1902 fpopup->set_sensitivity(ui_manager, item);
1904 if (NULL != (ac = account_find_from_item(item))) {
1905 special_trash = account_get_special_folder(ac, F_TRASH);
1906 special_queue = account_get_special_folder(ac, F_QUEUE);
1909 if ((item == folder->trash || item == special_trash
1910 || folder_has_parent_of_type(item, F_TRASH))) {
1911 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1912 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1915 if ((item == folder->queue || item == special_queue
1916 || folder_has_parent_of_type(item, F_QUEUE))) {
1917 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1918 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1921 #define SET_SENS(name, sens) \
1922 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1924 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1925 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1926 folderview->selected == folderview->opened);
1927 SET_SENS("FolderViewPopup/Properties", TRUE);
1929 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1930 item->total_msgs >= 1 && !item->processing_pending);
1931 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1932 !item->no_select && !item->processing_pending);
1934 if (item == folder->trash || item == special_trash
1935 || folder_has_parent_of_type(item, F_TRASH)) {
1936 GSList *msglist = folder_item_get_msg_list(item);
1937 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1938 procmsg_msg_list_free(msglist);
1940 if (item == folder->queue || item == special_queue
1941 || folder_has_parent_of_type(item, F_QUEUE)) {
1942 GSList *msglist = folder_item_get_msg_list(item);
1943 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1944 procmsg_msg_list_free(msglist);
1948 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1949 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1950 g_signal_connect(G_OBJECT(popup), "selection_done",
1951 G_CALLBACK(folderview_popup_close),
1953 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1954 event->button, event->time);
1957 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1958 FolderView *folderview)
1960 GtkCMCList *clist = GTK_CMCLIST(ctree);
1961 gint prev_row = -1, row = -1, column = -1;
1963 if (!event) return FALSE;
1965 if (event->button == 1 || event->button == 2) {
1966 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1967 folderview->open_folder = TRUE;
1969 if (event->type == GDK_2BUTTON_PRESS) {
1970 if (clist->selection) {
1971 GtkCMCTreeNode *node;
1973 node = GTK_CMCTREE_NODE(clist->selection->data);
1975 gtk_cmctree_toggle_expansion(
1978 folderview->open_folder = FALSE;
1985 if (event->button == 2 || event->button == 3) {
1987 if (clist->selection) {
1988 GtkCMCTreeNode *node;
1990 node = GTK_CMCTREE_NODE(clist->selection->data);
1992 prev_row = gtkut_ctree_get_nth_from_node
1993 (GTK_CMCTREE(ctree), node);
1996 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1999 if (prev_row != row) {
2000 gtk_cmclist_unselect_all(clist);
2001 if (event->button == 2)
2002 folderview_select_node
2004 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2007 gtk_cmclist_select_row(clist, row, column);
2011 if (event->button != 3) return FALSE;
2013 folderview_set_sens_and_popup_menu(folderview, row, event);
2017 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2018 FolderView *folderview)
2020 int row = -1, column = -1;
2022 if (!event) return FALSE;
2024 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2027 if (event->button == 1 && folderview->open_folder == FALSE &&
2028 folderview->opened != NULL) {
2029 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2030 folderview->opened);
2031 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2037 #define BREAK_ON_MODIFIER_KEY() \
2038 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2040 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2041 FolderView *folderview)
2043 if (!event) return FALSE;
2045 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2048 switch (event->keyval) {
2050 #ifndef GENERIC_UMPC
2051 case GDK_KEY_Return:
2052 case GDK_KEY_KP_Enter:
2054 if (folderview->selected) {
2055 folderview_select_node(folderview,
2056 folderview->selected);
2060 case GDK_KEY_Return:
2061 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2062 gtk_cmctree_toggle_expansion(
2063 GTK_CMCTREE(folderview->ctree),
2064 folderview->selected);
2069 BREAK_ON_MODIFIER_KEY();
2070 if (folderview->selected) {
2071 if (folderview->opened == folderview->selected &&
2072 (!folderview->summaryview->folder_item ||
2073 folderview->summaryview->folder_item->total_msgs == 0))
2074 folderview_select_next_unread(folderview, TRUE);
2076 folderview_select_node(folderview,
2077 folderview->selected);
2087 typedef struct _PostponedSelectData
2090 GtkCMCTreeNode *row;
2092 FolderView *folderview;
2093 } PostponedSelectData;
2095 static gboolean postpone_select(void *data)
2097 PostponedSelectData *psdata = (PostponedSelectData *)data;
2098 debug_print("trying again\n");
2099 psdata->folderview->open_folder = TRUE;
2100 main_window_cursor_normal(psdata->folderview->mainwin);
2101 STATUSBAR_POP(psdata->folderview->mainwin);
2102 folderview_selected(psdata->ctree, psdata->row,
2103 psdata->column, psdata->folderview);
2108 void folderview_close_opened(FolderView *folderview)
2110 if (folderview->opened) {
2111 FolderItem *olditem;
2113 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2114 folderview->opened);
2116 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2117 olditem->path ? olditem->path:olditem->name);
2118 /* will be null if we just moved the previously opened folder */
2119 STATUSBAR_PUSH(folderview->mainwin, buf);
2120 main_window_cursor_wait(folderview->mainwin);
2122 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2123 summary_show(folderview->summaryview, NULL);
2124 folder_item_close(olditem);
2125 main_window_cursor_normal(folderview->mainwin);
2126 STATUSBAR_POP(folderview->mainwin);
2127 if (olditem->folder->klass->item_closed)
2128 olditem->folder->klass->item_closed(olditem);
2133 if (folderview->opened &&
2134 !GTK_CMCTREE_ROW(folderview->opened)->children)
2135 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2137 folderview->opened = NULL;
2139 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2140 gint column, FolderView *folderview)
2142 static gboolean can_select = TRUE; /* exclusive lock */
2147 GtkCMCTreeNode *old_opened = folderview->opened;
2149 folderview->selected = row;
2151 debug_print("newly selected %p, opened %p\n", folderview->selected,
2152 folderview->opened);
2153 if (folderview->opened == row) {
2154 folderview->open_folder = FALSE;
2159 item = gtk_cmctree_node_get_row_data(ctree, row);
2162 folderview->open_folder = FALSE;
2166 if (!can_select || summary_is_locked(folderview->summaryview)) {
2167 if (folderview->opened) {
2168 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2169 gtk_cmctree_select(ctree, folderview->opened);
2171 folderview->open_folder = FALSE;
2176 if (!folderview->open_folder) {
2183 /* Save cache for old folder */
2184 /* We don't want to lose all caches if sylpheed crashed */
2185 /* resets folderview->opened to NULL */
2186 folderview_close_opened(folderview);
2188 /* CLAWS: set compose button type: news folder items
2189 * always have a news folder as parent */
2191 toolbar_set_compose_button
2192 (folderview->mainwin->toolbar,
2193 FOLDER_TYPE(item->folder) == F_NEWS ?
2194 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2197 debug_print("Folder %s is selected\n", item->path);
2199 if (!GTK_CMCTREE_ROW(row)->children)
2200 gtk_cmctree_expand(ctree, row);
2202 /* ungrab the mouse event */
2203 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2204 gtk_grab_remove(GTK_WIDGET(ctree));
2205 if (gdk_pointer_is_grabbed())
2206 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2210 /* TODO: wwp: avoid displaying (null) in the status bar */
2211 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2212 item->path : "(null)");
2213 debug_print("%s\n", buf);
2214 STATUSBAR_PUSH(folderview->mainwin, buf);
2217 main_window_cursor_wait(folderview->mainwin);
2219 if (folderview->scanning_folder == item->folder) {
2222 res = folder_item_open(item);
2225 if (res == -1 && item->no_select == FALSE) {
2226 main_window_cursor_normal(folderview->mainwin);
2227 STATUSBAR_POP(folderview->mainwin);
2229 alertpanel_error(_("Folder could not be opened."));
2231 folderview->open_folder = FALSE;
2235 } else if (res == -2 && item->no_select == FALSE) {
2236 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2237 data->ctree = ctree;
2239 data->column = column;
2240 data->folderview = folderview;
2241 debug_print("postponing open of %s till end of scan\n",
2242 item->path ? item->path:item->name);
2243 folderview->open_folder = FALSE;
2245 g_timeout_add(500, postpone_select, data);
2250 main_window_cursor_normal(folderview->mainwin);
2253 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2254 opened = summary_show(folderview->summaryview, item);
2256 folder_clean_cache_memory(item);
2259 gtkut_ctree_set_focus_row(ctree, old_opened);
2260 gtk_cmctree_select(ctree, old_opened);
2261 folderview->opened = old_opened;
2263 folderview->opened = row;
2264 if (gtk_cmctree_node_is_visible(ctree, row)
2265 != GTK_VISIBILITY_FULL)
2266 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2269 STATUSBAR_POP(folderview->mainwin);
2271 folderview->open_folder = FALSE;
2276 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2277 FolderView *folderview)
2281 item = gtk_cmctree_node_get_row_data(ctree, node);
2282 cm_return_if_fail(item != NULL);
2283 item->collapsed = FALSE;
2284 folderview_update_node(folderview, node);
2287 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2288 FolderView *folderview)
2292 item = gtk_cmctree_node_get_row_data(ctree, node);
2293 cm_return_if_fail(item != NULL);
2294 item->collapsed = TRUE;
2295 folderview_update_node(folderview, node);
2298 static void folderview_popup_close(GtkMenuShell *menu_shell,
2299 FolderView *folderview)
2301 if (!folderview->opened) return;
2303 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2306 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2307 FolderView *folderview)
2309 FolderColumnType type = folderview->col_state[column].type;
2311 prefs_common.folder_col_size[type] = width;
2314 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2316 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2317 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2318 GtkCMCTreeNode *node, *parent_node;
2319 gint *col_pos = folderview->col_pos;
2320 FolderItemUpdateData hookdata;
2322 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2323 if (parent_node == NULL)
2326 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2328 text[col_pos[F_COL_FOLDER]] = item->name;
2329 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2334 gtk_cmctree_expand(ctree, parent_node);
2335 gtk_cmctree_node_set_row_data(ctree, node, item);
2337 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2338 folderview_sort_folders(folderview, parent_node, item->folder);
2340 hookdata.item = item;
2341 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2342 hookdata.msg = NULL;
2343 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2345 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2348 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2350 FolderView *folderview = (FolderView *)data;
2351 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2353 GSList *mlist = NULL;
2355 FolderItem *special_trash = NULL;
2358 if (!folderview->selected) return;
2359 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2360 cm_return_if_fail(item != NULL);
2361 cm_return_if_fail(item->folder != NULL);
2363 if (NULL != (ac = account_find_from_item(item)))
2364 special_trash = account_get_special_folder(ac, F_TRASH);
2366 if (item != item->folder->trash && item != special_trash
2367 && !folder_has_parent_of_type(item, F_TRASH)) return;
2369 if (prefs_common.ask_on_clean) {
2370 if (alertpanel(_("Empty trash"),
2371 _("Delete all messages in trash?"),
2372 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2376 mlist = folder_item_get_msg_list(item);
2378 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2379 MsgInfo * msginfo = (MsgInfo *) cur->data;
2380 if (MSG_IS_LOCKED(msginfo->flags))
2382 /* is it partially received? (partial_recv isn't cached) */
2383 if (msginfo->total_size != 0 &&
2384 msginfo->size != (off_t)msginfo->total_size)
2385 partial_mark_for_delete(msginfo);
2387 procmsg_msg_list_free(mlist);
2389 folder_item_remove_all_msg(item);
2392 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2394 FolderView *folderview = (FolderView *)data;
2395 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2397 FolderItem *special_queue = NULL;
2399 gchar *errstr = NULL;
2401 if (!folderview->selected) return;
2402 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2403 cm_return_if_fail(item != NULL);
2404 cm_return_if_fail(item->folder != NULL);
2406 if (NULL != (ac = account_find_from_item(item)))
2407 special_queue = account_get_special_folder(ac, F_QUEUE);
2409 if (item != item->folder->queue && item != special_queue
2410 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2412 if (procmsg_queue_is_empty(item))
2415 if (prefs_common.work_offline)
2416 if (alertpanel(_("Offline warning"),
2417 _("You're working offline. Override?"),
2418 GTK_STOCK_NO, GTK_STOCK_YES,
2419 NULL) != G_ALERTALTERNATE)
2422 /* ask for confirmation before sending queued messages only
2423 in online mode and if there is at least one message queued
2424 in any of the folder queue
2426 if (prefs_common.confirm_send_queued_messages) {
2427 if (!prefs_common.work_offline) {
2428 if (alertpanel(_("Send queued messages"),
2429 _("Send all queued messages?"),
2430 GTK_STOCK_CANCEL, _("_Send"),
2431 NULL) != G_ALERTALTERNATE)
2436 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2438 alertpanel_error_log(_("Some errors occurred while "
2439 "sending queued messages."));
2441 alertpanel_error_log(_("Some errors occurred "
2442 "while sending queued messages:\n%s"), errstr);
2448 static void folderview_search_cb(GtkAction *action, gpointer data)
2450 FolderView *folderview = (FolderView *)data;
2451 summary_search(folderview->summaryview);
2454 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2456 FolderView *folderview = (FolderView *)data;
2457 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2460 if (!folderview->selected) return;
2462 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2463 cm_return_if_fail(item != NULL);
2464 cm_return_if_fail(item->folder != NULL);
2466 item->processing_pending = TRUE;
2467 folder_item_apply_processing(item);
2468 item->processing_pending = FALSE;
2471 static void folderview_property_cb(GtkAction *action, gpointer data)
2473 FolderView *folderview = (FolderView *)data;
2474 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2477 if (!folderview->selected) return;
2479 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2480 cm_return_if_fail(item != NULL);
2481 cm_return_if_fail(item->folder != NULL);
2483 prefs_folder_item_open(item);
2486 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2488 GSList *list = NULL;
2489 GSList *done = NULL;
2490 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2492 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2493 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2494 && list->data != node) {
2495 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2496 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2499 for (list = done; list != NULL; list = g_slist_next(list)) {
2500 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2506 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2507 FolderItem *to_folder, gboolean copy)
2509 FolderItem *new_folder = NULL;
2513 cm_return_if_fail(folderview != NULL);
2514 cm_return_if_fail(from_folder != NULL);
2515 cm_return_if_fail(to_folder != NULL);
2517 if (prefs_common.warn_dnd) {
2518 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2519 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2520 from_folder->name, to_folder->name);
2521 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2522 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2523 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2526 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2528 else if (status & G_ALERTDISABLE)
2529 prefs_common.warn_dnd = FALSE;
2532 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2533 from_folder->name, to_folder->name);
2534 STATUSBAR_PUSH(folderview->mainwin, buf);
2536 summary_clear_all(folderview->summaryview);
2537 folderview->opened = NULL;
2538 folderview->selected = NULL;
2539 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2541 main_window_cursor_wait(folderview->mainwin);
2543 statusbar_verbosity_set(FALSE);
2544 folder_item_update_freeze();
2545 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2546 statusbar_verbosity_set(FALSE);
2547 main_window_cursor_normal(folderview->mainwin);
2548 STATUSBAR_POP(folderview->mainwin);
2549 folder_item_update_thaw();
2550 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2552 folderview_sort_folders(folderview,
2553 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2554 NULL, to_folder), new_folder->folder);
2555 folderview_select(folderview, new_folder);
2557 statusbar_verbosity_set(FALSE);
2558 main_window_cursor_normal(folderview->mainwin);
2559 STATUSBAR_POP(folderview->mainwin);
2560 folder_item_update_thaw();
2562 case F_MOVE_FAILED_DEST_IS_PARENT:
2563 alertpanel_error(_("Source and destination are the same."));
2565 case F_MOVE_FAILED_DEST_IS_CHILD:
2566 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2567 _("Can't move a folder to one of its children."));
2569 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2570 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2573 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2578 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2581 static gint folderview_clist_compare(GtkCMCList *clist,
2582 gconstpointer ptr1, gconstpointer ptr2)
2584 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2585 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2587 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2589 return item1->order - item2->order;
2592 // if only one folder has an order it comes first
2593 if (item1->order > 0)
2597 if (item2->order > 0)
2603 return (item2->name != NULL);
2607 return g_utf8_collate(item1->name, item2->name);
2610 static void folderview_processing_cb(GtkAction *action, gpointer data)
2612 FolderView *folderview = (FolderView *)data;
2613 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2617 if (!folderview->selected) return;
2619 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2620 cm_return_if_fail(item != NULL);
2621 cm_return_if_fail(item->folder != NULL);
2623 id = folder_item_get_identifier(item);
2624 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2627 prefs_filtering_open(&item->prefs->processing, title,
2628 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2632 void folderview_set_target_folder_color(gint color_op)
2636 FolderView *folderview;
2638 for (list = folderview_list; list != NULL; list = list->next) {
2639 folderview = (FolderView *)list->data;
2640 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2642 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2643 folderview->color_op;
2649 static gchar *last_smallfont = NULL;
2650 static gchar *last_normalfont = NULL;
2651 static gchar *last_boldfont = NULL;
2652 static gboolean last_derive = 0;
2654 void folderview_reinit_fonts(FolderView *folderview)
2657 g_free(last_smallfont);
2658 last_smallfont = NULL;
2659 g_free(last_normalfont);
2660 last_normalfont = NULL;
2661 g_free(last_boldfont);
2662 last_boldfont = NULL;
2665 void folderview_reflect_prefs(void)
2667 gboolean update_font = FALSE;
2668 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2669 FolderItem *item = folderview_get_selected_item(folderview);
2670 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2671 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2672 gint height = gtk_adjustment_get_value(pos);
2674 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2675 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2676 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2677 last_derive != prefs_common.derive_from_normal_font)
2680 g_free(last_smallfont);
2681 last_smallfont = g_strdup(SMALL_FONT);
2682 g_free(last_normalfont);
2683 last_normalfont = g_strdup(NORMAL_FONT);
2684 g_free(last_boldfont);
2685 last_boldfont = g_strdup(BOLD_FONT);
2686 last_derive = prefs_common.derive_from_normal_font;
2689 normal_style = normal_color_style = bold_style =
2690 bold_color_style = bold_tgtfold_style = NULL;
2692 folderview_init(folderview);
2694 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2695 folderview_column_set_titles(folderview);
2696 folderview_set_all();
2698 g_signal_handlers_block_by_func
2699 (G_OBJECT(folderview->ctree),
2700 G_CALLBACK(folderview_selected), folderview);
2703 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2704 GTK_CMCTREE(folderview->ctree), NULL, item);
2706 folderview_select(folderview, item);
2707 folderview->open_folder = FALSE;
2708 folderview->selected = node;
2711 g_signal_handlers_unblock_by_func
2712 (G_OBJECT(folderview->ctree),
2713 G_CALLBACK(folderview_selected), folderview);
2715 pos = gtk_scrolled_window_get_vadjustment(
2716 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2717 gtk_adjustment_set_value(pos, height);
2718 gtk_adjustment_changed(pos);
2719 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2722 static void drag_state_stop(FolderView *folderview)
2724 if (folderview->drag_timer)
2725 g_source_remove(folderview->drag_timer);
2726 folderview->drag_timer = 0;
2727 folderview->drag_node = NULL;
2730 static gboolean folderview_defer_expand(FolderView *folderview)
2732 if (folderview->drag_node) {
2733 folderview_recollapse_nodes(folderview, folderview->drag_node);
2734 if (folderview->drag_item->collapsed) {
2735 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2736 folderview->nodes_to_recollapse = g_slist_append
2737 (folderview->nodes_to_recollapse, folderview->drag_node);
2740 folderview->drag_item = NULL;
2741 folderview->drag_timer = 0;
2745 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2747 /* the idea is that we call drag_state_start() whenever we want expansion to
2748 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2749 * we need to call drag_state_stop() */
2750 drag_state_stop(folderview);
2751 /* request expansion */
2752 if (0 != (folderview->drag_timer = g_timeout_add
2753 (prefs_common.hover_timeout,
2754 (GSourceFunc)folderview_defer_expand,
2756 folderview->drag_node = node;
2757 folderview->drag_item = item;
2760 #ifndef GENERIC_UMPC
2761 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2762 FolderView *folderview)
2764 GdkDragContext *context;
2766 cm_return_if_fail(folderview != NULL);
2767 if (folderview->selected == NULL) return;
2768 if (folderview->nodes_to_recollapse)
2769 g_slist_free(folderview->nodes_to_recollapse);
2770 folderview->nodes_to_recollapse = NULL;
2771 context = gtk_drag_begin(widget, folderview->target_list,
2772 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2773 gtk_drag_set_icon_default(context);
2776 static void folderview_drag_data_get(GtkWidget *widget,
2777 GdkDragContext *drag_context,
2778 GtkSelectionData *selection_data,
2781 FolderView *folderview)
2785 gchar *source = NULL;
2786 if (info == TARGET_DUMMY) {
2787 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2788 cur != NULL; cur = cur->next) {
2789 item = gtk_cmctree_node_get_row_data
2790 (GTK_CMCTREE(folderview->ctree),
2791 GTK_CMCTREE_NODE(cur->data));
2793 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2794 gtk_selection_data_set(selection_data,
2795 gtk_selection_data_get_target(selection_data), 8,
2796 source, strlen(source));
2802 g_warning("unknown info %d\n", info);
2806 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2808 FolderUpdateData *hookdata;
2809 FolderView *folderview;
2813 folderview = (FolderView *) userdata;
2814 cm_return_val_if_fail(hookdata != NULL, FALSE);
2815 cm_return_val_if_fail(folderview != NULL, FALSE);
2817 ctree = folderview->ctree;
2818 cm_return_val_if_fail(ctree != NULL, FALSE);
2820 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2821 folderview_create_folder_node(folderview, hookdata->item);
2822 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2823 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2824 NULL, folder_item_parent(hookdata->item));
2825 folderview_sort_folders(folderview, node, hookdata->folder);
2826 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2827 GtkCMCTreeNode *node;
2829 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2831 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2832 if (folderview->selected == node)
2833 folderview->selected = NULL;
2834 if (folderview->opened == node)
2835 folderview->opened = NULL;
2837 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2838 folderview_set(folderview);
2843 static gboolean folderview_dnd_scroll_cb(gpointer data)
2845 FolderView *folderview = (FolderView *)data;
2846 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2847 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2848 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2849 gint max = (int)gtk_adjustment_get_upper(pos) -
2850 (int)gtk_adjustment_get_page_size(pos);
2852 if (folderview->scroll_value == 0) {
2853 folderview->scroll_timeout_id = -1;
2857 if (folderview->scroll_value > 0 && new_val > max) {
2859 } else if (folderview->scroll_value < 0 && new_val < 0) {
2862 gtk_adjustment_set_value(pos, new_val);
2867 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2868 GdkDragContext *context,
2872 FolderView *folderview)
2875 FolderItem *item = NULL, *src_item = NULL;
2876 GtkCMCTreeNode *node = NULL;
2877 gboolean acceptable = FALSE;
2878 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2879 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2880 int height = (int)gtk_adjustment_get_page_size(pos);
2881 int total_height = (int)gtk_adjustment_get_upper(pos);
2882 int vpos = (int)gtk_adjustment_get_value(pos);
2883 int offset = prefs_common.show_col_headers ? 24:0;
2886 if (gtk_cmclist_get_selection_info
2887 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2888 GtkWidget *srcwidget;
2890 if (y > height - (48 - offset) && height + vpos < total_height) {
2891 dist = -(height - (48 - offset) - y);
2892 folderview->scroll_value = 1.41f * (1+(dist / 6));
2893 } else if (y < 72 - (24 - offset) && y >= 0) {
2894 dist = 72 - (24 - offset) - y;
2895 folderview->scroll_value = -1.41f * (1+(dist / 6));
2897 folderview->scroll_value = 0;
2899 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == -1) {
2900 folderview->scroll_timeout_id =
2901 g_timeout_add(30, folderview_dnd_scroll_cb,
2905 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2906 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2907 src_item = folderview->summaryview->folder_item;
2909 srcwidget = gtk_drag_get_source_widget(context);
2910 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2911 /* comes from summaryview */
2912 /* we are copying messages, so only accept folder items that are not
2913 the source item, are no root items and can copy messages */
2914 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2915 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2916 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2918 } else if (srcwidget == folderview->ctree) {
2919 /* comes from folderview */
2920 /* we are moving folder items, only accept folders that are not
2921 the source items and can copy messages and create folder items */
2922 if (item && item->folder && src_item && src_item != item &&
2923 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2924 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2925 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2926 || item->folder == src_item->folder))
2929 /* comes from another app */
2930 /* we are adding messages, so only accept folder items that are
2931 no root items and can copy messages */
2932 if (item && item->folder && folder_item_parent(item) != NULL
2933 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2934 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2939 if (acceptable || (src_item && src_item == item))
2940 drag_state_start(folderview, node, item);
2943 g_signal_handlers_block_by_func
2945 G_CALLBACK(folderview_selected), folderview);
2946 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2947 g_signal_handlers_unblock_by_func
2949 G_CALLBACK(folderview_selected), folderview);
2950 gdk_drag_status(context,
2951 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2952 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2954 if (folderview->opened)
2955 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2956 gdk_drag_status(context, 0, time);
2962 static void folderview_drag_leave_cb(GtkWidget *widget,
2963 GdkDragContext *context,
2965 FolderView *folderview)
2967 drag_state_stop(folderview);
2968 folderview->scroll_value = 0;
2969 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2972 static void free_info (gpointer stuff, gpointer data)
2977 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2978 guint time, FolderItem *item)
2981 GSList *msglist = NULL;
2982 list = uri_list_extract_filenames(data);
2983 if (!(item && item->folder && folder_item_parent(item) != NULL
2984 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2986 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2987 debug_print("item doesn't fit\n");
2991 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2992 debug_print("list is empty\n");
2995 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2996 MsgFileInfo *info = NULL;
2998 if (file_is_email((gchar *)tmp->data)) {
2999 info = g_new0(MsgFileInfo, 1);
3000 info->msginfo = NULL;
3001 info->file = (gchar *)tmp->data;
3002 msglist = g_slist_prepend(msglist, info);
3003 debug_print("file is a mail\n");
3005 debug_print("file isn't a mail\n");
3009 msglist = g_slist_reverse(msglist);
3010 folder_item_add_msgs(item, msglist, FALSE);
3011 g_slist_foreach(msglist, free_info, NULL);
3012 g_slist_free(msglist);
3013 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3015 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3017 list_free_strings(list);
3021 static void folderview_drag_received_cb(GtkWidget *widget,
3022 GdkDragContext *drag_context,
3025 GtkSelectionData *data,
3028 FolderView *folderview)
3031 FolderItem *item = NULL, *src_item;
3032 GtkCMCTreeNode *node;
3033 int offset = prefs_common.show_col_headers ? 24:0;
3035 folderview->scroll_value = 0;
3037 if (info == TARGET_DUMMY) {
3038 drag_state_stop(folderview);
3039 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3040 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3041 /* comes from summaryview */
3042 if (gtk_cmclist_get_selection_info
3043 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3046 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3047 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3048 src_item = folderview->summaryview->folder_item;
3050 if (item->no_select) {
3051 alertpanel_error(_("The destination folder can only be used to "
3052 "store subfolders."));
3055 /* re-check (due to acceptable possibly set for folder moves */
3056 if (!(item && item->folder && item->path && !item->no_select &&
3057 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3060 if (item && src_item) {
3061 switch (gdk_drag_context_get_selected_action(drag_context)) {
3062 case GDK_ACTION_COPY:
3063 summary_copy_selected_to(folderview->summaryview, item);
3064 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3066 case GDK_ACTION_MOVE:
3067 case GDK_ACTION_DEFAULT:
3069 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3070 summary_copy_selected_to(folderview->summaryview, item);
3072 summary_move_selected_to(folderview->summaryview, item);
3073 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3076 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3078 /* comes from folderview */
3080 gboolean folder_is_normal = TRUE;
3081 gboolean copy = (GDK_ACTION_COPY ==
3082 gdk_drag_context_get_selected_action(drag_context));
3084 source = (char *)gtk_selection_data_get_data(data) + 17;
3085 if (gtk_cmclist_get_selection_info
3086 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3088 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3091 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3092 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3093 src_item = folder_find_item_from_identifier(source);
3097 src_item->stype == F_NORMAL &&
3098 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3099 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3100 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3101 !folder_has_parent_of_type(src_item, F_TRASH);
3102 if (!item || !src_item || !folder_is_normal) {
3103 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3107 folderview_move_folder(folderview, src_item, item, copy);
3108 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3110 folderview->nodes_to_recollapse = NULL;
3111 } else if (info == TARGET_MAIL_URI_LIST) {
3112 if (gtk_cmclist_get_selection_info
3113 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3116 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3118 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3119 debug_print("no node\n");
3122 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3124 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3125 debug_print("no item\n");
3128 folderview_finish_dnd(gtk_selection_data_get_data(data),
3129 drag_context, time, item);
3133 static void folderview_drag_end_cb(GtkWidget *widget,
3134 GdkDragContext *drag_context,
3135 FolderView *folderview)
3137 drag_state_stop(folderview);
3138 folderview->scroll_value = 0;
3139 g_slist_free(folderview->nodes_to_recollapse);
3140 folderview->nodes_to_recollapse = NULL;
3143 void folderview_register_popup(FolderViewPopup *fpopup)
3147 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3148 FolderView *folderview = folderviews->data;
3149 GtkActionGroup *factory;
3151 factory = create_action_group(folderview, fpopup);
3152 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3154 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3157 void folderview_unregister_popup(FolderViewPopup *fpopup)
3162 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3163 FolderView *folderview = folderviews->data;
3165 g_hash_table_remove(folderview->popups, fpopup->klass);
3167 g_hash_table_remove(folderview_popups, fpopup->klass);