2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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 GdkPixmap *inboxxpm;
74 static GdkBitmap *inboxxpmmask;
75 static GdkPixmap *inboxhrmxpm;
76 static GdkBitmap *inboxhrmxpmmask;
77 static GdkPixmap *inboxopenxpm;
78 static GdkBitmap *inboxopenxpmmask;
79 static GdkPixmap *inboxopenhrmxpm;
80 static GdkBitmap *inboxopenhrmxpmmask;
81 static GdkPixmap *outboxxpm;
82 static GdkBitmap *outboxxpmmask;
83 static GdkPixmap *outboxhrmxpm;
84 static GdkBitmap *outboxhrmxpmmask;
85 static GdkPixmap *outboxopenxpm;
86 static GdkBitmap *outboxopenxpmmask;
87 static GdkPixmap *outboxopenhrmxpm;
88 static GdkBitmap *outboxopenhrmxpmmask;
89 static GdkPixmap *folderxpm;
90 static GdkBitmap *folderxpmmask;
91 static GdkPixmap *folderhrmxpm;
92 static GdkBitmap *folderhrmxpmmask;
93 static GdkPixmap *folderopenxpm;
94 static GdkBitmap *folderopenxpmmask;
95 static GdkPixmap *folderopenhrmxpm;
96 static GdkBitmap *folderopenhrmxpmmask;
97 static GdkPixmap *trashopenxpm;
98 static GdkBitmap *trashopenxpmmask;
99 static GdkPixmap *trashopenhrmxpm;
100 static GdkBitmap *trashopenhrmxpmmask;
101 static GdkPixmap *trashxpm;
102 static GdkBitmap *trashxpmmask;
103 static GdkPixmap *trashhrmxpm;
104 static GdkBitmap *trashhrmxpmmask;
105 static GdkPixmap *queuexpm;
106 static GdkBitmap *queuexpmmask;
107 static GdkPixmap *queuehrmxpm;
108 static GdkBitmap *queuehrmxpmmask;
109 static GdkPixmap *queueopenxpm;
110 static GdkBitmap *queueopenxpmmask;
111 static GdkPixmap *queueopenhrmxpm;
112 static GdkBitmap *queueopenhrmxpmmask;
113 static GdkPixmap *draftsxpm;
114 static GdkBitmap *draftsxpmmask;
115 static GdkPixmap *draftsopenxpm;
116 static GdkBitmap *draftsopenxpmmask;
117 static GdkPixmap *noselectxpm;
118 static GdkBitmap *noselectxpmmask;
120 static GdkPixmap *m_inboxxpm;
121 static GdkBitmap *m_inboxxpmmask;
122 static GdkPixmap *m_inboxhrmxpm;
123 static GdkBitmap *m_inboxhrmxpmmask;
124 static GdkPixmap *m_inboxopenxpm;
125 static GdkBitmap *m_inboxopenxpmmask;
126 static GdkPixmap *m_inboxopenhrmxpm;
127 static GdkBitmap *m_inboxopenhrmxpmmask;
128 static GdkPixmap *m_outboxxpm;
129 static GdkBitmap *m_outboxxpmmask;
130 static GdkPixmap *m_outboxhrmxpm;
131 static GdkBitmap *m_outboxhrmxpmmask;
132 static GdkPixmap *m_outboxopenxpm;
133 static GdkBitmap *m_outboxopenxpmmask;
134 static GdkPixmap *m_outboxopenhrmxpm;
135 static GdkBitmap *m_outboxopenhrmxpmmask;
136 static GdkPixmap *m_folderxpm;
137 static GdkBitmap *m_folderxpmmask;
138 static GdkPixmap *m_folderhrmxpm;
139 static GdkBitmap *m_folderhrmxpmmask;
140 static GdkPixmap *m_folderopenxpm;
141 static GdkBitmap *m_folderopenxpmmask;
142 static GdkPixmap *m_folderopenhrmxpm;
143 static GdkBitmap *m_folderopenhrmxpmmask;
144 static GdkPixmap *m_trashopenxpm;
145 static GdkBitmap *m_trashopenxpmmask;
146 static GdkPixmap *m_trashopenhrmxpm;
147 static GdkBitmap *m_trashopenhrmxpmmask;
148 static GdkPixmap *m_trashxpm;
149 static GdkBitmap *m_trashxpmmask;
150 static GdkPixmap *m_trashhrmxpm;
151 static GdkBitmap *m_trashhrmxpmmask;
152 static GdkPixmap *m_queuexpm;
153 static GdkBitmap *m_queuexpmmask;
154 static GdkPixmap *m_queuehrmxpm;
155 static GdkBitmap *m_queuehrmxpmmask;
156 static GdkPixmap *m_queueopenxpm;
157 static GdkBitmap *m_queueopenxpmmask;
158 static GdkPixmap *m_queueopenhrmxpm;
159 static GdkBitmap *m_queueopenhrmxpmmask;
160 static GdkPixmap *m_draftsxpm;
161 static GdkBitmap *m_draftsxpmmask;
162 static GdkPixmap *m_draftsopenxpm;
163 static GdkBitmap *m_draftsopenxpmmask;
165 static GdkPixmap *newxpm;
166 static GdkBitmap *newxpmmask;
167 static GdkPixmap *unreadxpm;
168 static GdkBitmap *unreadxpmmask;
169 static GdkPixmap *readxpm;
170 static GdkBitmap *readxpmmask;
172 static void folderview_select_node (FolderView *folderview,
173 GtkCMCTreeNode *node);
174 static void folderview_set_folders (FolderView *folderview);
175 static void folderview_sort_folders (FolderView *folderview,
176 GtkCMCTreeNode *root,
178 static void folderview_append_folder (FolderView *folderview,
180 static void folderview_update_node (FolderView *folderview,
181 GtkCMCTreeNode *node);
183 static gint folderview_clist_compare (GtkCMCList *clist,
187 /* callback functions */
188 static gboolean folderview_button_pressed (GtkWidget *ctree,
189 GdkEventButton *event,
190 FolderView *folderview);
191 static gboolean folderview_button_released (GtkWidget *ctree,
192 GdkEventButton *event,
193 FolderView *folderview);
194 static gboolean folderview_key_pressed (GtkWidget *widget,
196 FolderView *folderview);
197 static void folderview_selected (GtkCMCTree *ctree,
200 FolderView *folderview);
201 static void folderview_tree_expanded (GtkCMCTree *ctree,
202 GtkCMCTreeNode *node,
203 FolderView *folderview);
204 static void folderview_tree_collapsed (GtkCMCTree *ctree,
205 GtkCMCTreeNode *node,
206 FolderView *folderview);
207 static void folderview_popup_close (GtkMenuShell *menu_shell,
208 FolderView *folderview);
209 static void folderview_col_resized (GtkCMCList *clist,
212 FolderView *folderview);
214 static void mark_all_read_cb (GtkAction *action,
217 static void folderview_empty_trash_cb (GtkAction *action,
220 static void folderview_send_queue_cb (GtkAction *action,
223 static void folderview_search_cb (GtkAction *action,
225 static void folderview_run_processing_cb(GtkAction *action,
228 static void folderview_property_cb (GtkAction *action,
231 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
232 GdkDragContext *context,
236 FolderView *folderview);
237 static void folderview_drag_leave_cb (GtkWidget *widget,
238 GdkDragContext *context,
240 FolderView *folderview);
241 static void folderview_drag_received_cb (GtkWidget *widget,
242 GdkDragContext *drag_context,
245 GtkSelectionData *data,
248 FolderView *folderview);
250 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
251 FolderView *folderview);
253 static void folderview_drag_data_get (GtkWidget *widget,
254 GdkDragContext *drag_context,
255 GtkSelectionData *selection_data,
258 FolderView *folderview);
259 static void folderview_drag_end_cb (GtkWidget *widget,
260 GdkDragContext *drag_context,
261 FolderView *folderview);
263 static void folderview_create_folder_node (FolderView *folderview,
265 static gboolean folderview_update_folder (gpointer source,
267 static gboolean folderview_update_item_claws (gpointer source,
269 static void folderview_processing_cb(GtkAction *action, gpointer data);
270 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
271 GdkEventButton *event);
273 GHashTable *folderview_popups;
275 static GtkActionEntry folderview_common_popup_entries[] =
277 {"FolderViewPopup", NULL, "FolderViewPopup" },
278 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
279 {"FolderViewPopup/---", NULL, "---" },
280 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
281 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
282 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
283 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
284 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
285 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
289 GtkTargetEntry folderview_drag_types[] =
291 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
292 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
295 void folderview_initialize(void)
297 FolderViewPopup *fpopup;
299 fpopup = g_new0(FolderViewPopup, 1);
301 fpopup->klass = "common";
302 fpopup->path = "<CommonFolder>";
303 fpopup->entries = folderview_common_popup_entries;
304 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
305 fpopup->set_sensitivity = NULL;
307 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
308 g_hash_table_insert(folderview_popups, "common", fpopup);
311 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
313 FolderViewPopup *fpopup_common;
314 GtkActionGroup *action_group;
316 action_group = cm_menu_create_action_group(
318 fpopup->entries, fpopup->n_entries,
319 (gpointer)folderview);
321 if (fpopup->toggle_entries)
322 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
323 fpopup->n_toggle_entries,
324 (gpointer)folderview);
325 if (fpopup->radio_entries)
326 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
327 fpopup->n_radio_entries, fpopup->radio_default,
328 G_CALLBACK(fpopup->radio_callback),
329 (gpointer)folderview);
331 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
332 if (fpopup_common != fpopup) {
333 gtk_action_group_add_actions(action_group, fpopup_common->entries,
334 fpopup_common->n_entries,
335 (gpointer)folderview);
336 if (fpopup_common->toggle_entries)
337 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
338 fpopup_common->n_toggle_entries,
339 (gpointer)folderview);
340 if (fpopup_common->radio_entries)
341 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
342 fpopup_common->n_radio_entries, fpopup_common->radio_default,
343 G_CALLBACK(fpopup_common->radio_callback),
344 (gpointer)folderview);
350 static void create_action_groups(gpointer key, gpointer value, gpointer data)
352 FolderView *folderview = data;
353 FolderViewPopup *fpopup = value;
354 GtkActionGroup *group;
356 group = create_action_group(folderview, fpopup);
357 g_hash_table_insert(folderview->popups, fpopup->klass, group);
360 static void folderview_column_set_titles(FolderView *folderview)
362 GtkWidget *ctree = folderview->ctree;
363 GtkWidget *label_folder;
364 GtkWidget *label_new;
365 GtkWidget *label_unread;
366 GtkWidget *label_total;
367 GtkWidget *hbox_folder;
369 GtkWidget *hbox_unread;
370 GtkWidget *hbox_total;
371 gint *col_pos = folderview->col_pos;
373 debug_print("setting titles...\n");
374 gtk_widget_realize(folderview->ctree);
375 gtk_widget_show_all(folderview->scrolledwin);
377 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
378 * instead text (text overflows making them unreadable and ugly) */
379 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
380 &newxpm, &newxpmmask);
381 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
382 &unreadxpm, &unreadxpmmask);
383 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
384 &readxpm, &readxpmmask);
386 label_folder = gtk_label_new(_("Folder"));
387 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
388 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
389 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
391 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
393 hbox_folder = gtk_hbox_new(FALSE, 4);
394 hbox_new = gtk_hbox_new(FALSE, 4);
395 hbox_unread = gtk_hbox_new(FALSE, 4);
396 hbox_total = gtk_hbox_new(FALSE, 4);
399 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
400 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
401 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
402 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
403 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
404 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
405 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
406 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
408 gtk_widget_show_all(hbox_folder);
409 gtk_widget_show_all(hbox_new);
410 gtk_widget_show_all(hbox_unread);
411 gtk_widget_show_all(hbox_total);
414 gtk_widget_set_size_request(hbox_new, -1, 20);
415 gtk_widget_set_size_request(hbox_unread, -1, 20);
416 gtk_widget_set_size_request(hbox_total, -1, 20);
419 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
420 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
421 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
422 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
428 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
429 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
430 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
433 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
435 FolderView *folderview = (FolderView *)data;
436 GdkEventButton event;
437 if (folderview_get_selected_item(folderview) == NULL)
441 event.time = gtk_get_current_event_time();
443 folderview_set_sens_and_popup_menu(folderview, -1,
450 static GtkWidget *folderview_ctree_create(FolderView *folderview)
454 FolderColumnState *col_state;
455 FolderColumnType type;
456 gchar *titles[N_FOLDER_COLS];
458 GtkWidget *scrolledwin = folderview->scrolledwin;
460 debug_print("creating tree...\n");
461 memset(titles, 0, sizeof(titles));
463 col_state = prefs_folder_column_get_config();
464 memset(titles, 0, sizeof(titles));
466 col_pos = folderview->col_pos;
468 for (i = 0; i < N_FOLDER_COLS; i++) {
469 folderview->col_state[i] = col_state[i];
470 type = col_state[i].type;
474 titles[col_pos[F_COL_FOLDER]] = _("Folder");
475 titles[col_pos[F_COL_NEW]] = _("New");
476 titles[col_pos[F_COL_UNREAD]] = _("Unread");
477 /* TRANSLATORS: This in Number sign in American style */
478 titles[col_pos[F_COL_TOTAL]] = _("#");
480 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
483 if (prefs_common.show_col_headers == FALSE)
484 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
487 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
488 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
490 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
491 col_pos[F_COL_UNREAD],
493 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
494 col_pos[F_COL_TOTAL],
496 if (prefs_common.enable_dotted_lines) {
497 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_DOTTED);
498 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
499 GTK_CMCTREE_EXPANDER_SQUARE);
501 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
502 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
503 GTK_CMCTREE_EXPANDER_TRIANGLE);
506 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
507 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
509 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
510 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
512 /* don't let title buttons take key focus */
513 for (i = 0; i < N_FOLDER_COLS; i++) {
514 GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(ctree)->column[i].button,
516 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
517 prefs_common.folder_col_size[i]);
518 gtk_cmclist_set_column_visibility
519 (GTK_CMCLIST(ctree), i, col_state[i].visible);
522 g_signal_connect(G_OBJECT(ctree), "key_press_event",
523 G_CALLBACK(folderview_key_pressed),
525 g_signal_connect(G_OBJECT(ctree), "button_press_event",
526 G_CALLBACK(folderview_button_pressed),
529 g_signal_connect(G_OBJECT(ctree), "popup-menu",
530 G_CALLBACK(folderview_popup_menu), folderview);
532 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
533 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
534 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
535 G_CALLBACK(folderview_popup_menu), folderview);
537 g_signal_connect(G_OBJECT(ctree), "button_release_event",
538 G_CALLBACK(folderview_button_released),
540 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
541 G_CALLBACK(folderview_selected), folderview);
543 /* drag-n-dropping folders on maemo is impractical as this
544 * opens the folder almost everytime */
545 g_signal_connect(G_OBJECT(ctree), "start_drag",
546 G_CALLBACK(folderview_start_drag), folderview);
548 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
549 G_CALLBACK(folderview_drag_data_get),
552 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
553 G_CALLBACK(folderview_tree_expanded),
555 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
556 G_CALLBACK(folderview_tree_collapsed),
559 g_signal_connect(G_OBJECT(ctree), "resize_column",
560 G_CALLBACK(folderview_col_resized),
564 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
565 folderview_drag_types, 2,
566 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
567 g_signal_connect(G_OBJECT(ctree), "drag_motion",
568 G_CALLBACK(folderview_drag_motion_cb),
570 g_signal_connect(G_OBJECT(ctree), "drag_leave",
571 G_CALLBACK(folderview_drag_leave_cb),
573 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
574 G_CALLBACK(folderview_drag_received_cb),
576 g_signal_connect(G_OBJECT(ctree), "drag_end",
577 G_CALLBACK(folderview_drag_end_cb),
580 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
585 void folderview_set_column_order(FolderView *folderview)
587 GtkWidget *ctree = folderview->ctree;
588 FolderItem *item = folderview_get_selected_item(folderview);
589 FolderItem *sel_item = NULL, *op_item = NULL;
590 GtkWidget *scrolledwin = folderview->scrolledwin;
592 if (folderview->selected)
593 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
594 if (folderview->opened)
595 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
597 debug_print("recreating tree...\n");
598 gtk_widget_destroy(folderview->ctree);
601 folderview->ctree = ctree = folderview_ctree_create(folderview);
602 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
603 GTK_CMCLIST(ctree)->hadjustment);
604 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
605 GTK_CMCLIST(ctree)->vadjustment);
606 gtk_widget_show(ctree);
609 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
611 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
613 folderview_set(folderview);
614 folderview_column_set_titles(folderview);
616 folderview_select(folderview,item);
619 FolderView *folderview_create(void)
621 FolderView *folderview;
622 GtkWidget *scrolledwin;
625 debug_print("Creating folder view...\n");
626 folderview = g_new0(FolderView, 1);
628 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
629 gtk_scrolled_window_set_policy
630 (GTK_SCROLLED_WINDOW(scrolledwin),
631 GTK_POLICY_AUTOMATIC,
632 prefs_common.folderview_vscrollbar_policy);
633 gtk_widget_set_size_request(scrolledwin,
634 prefs_common.folderview_width,
635 prefs_common.folderview_height);
637 folderview->scrolledwin = scrolledwin;
638 ctree = folderview_ctree_create(folderview);
640 /* create popup factories */
641 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
642 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
644 folderview->ctree = ctree;
646 folderview->folder_update_callback_id =
647 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
648 folderview->folder_item_update_callback_id =
649 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
651 gtk_widget_show_all(scrolledwin);
653 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
654 folderview_list = g_list_append(folderview_list, folderview);
655 folderview->deferred_refresh_id = -1;
660 void folderview_init(FolderView *folderview)
662 GtkWidget *ctree = folderview->ctree;
665 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
666 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
667 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
668 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
669 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
670 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
671 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
675 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
693 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
695 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
698 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
699 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
700 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
701 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
702 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
703 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
704 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
705 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
706 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
707 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
708 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
709 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
710 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
713 PangoFontDescription *font_desc;
714 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
715 font_desc = pango_font_description_from_string(NORMAL_FONT);
717 if (normal_style->font_desc)
718 pango_font_description_free
719 (normal_style->font_desc);
720 normal_style->font_desc = font_desc;
722 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
723 normal_color_style = gtk_style_copy(normal_style);
724 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
726 gtk_widget_set_style(ctree, normal_style);
730 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
731 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
732 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
733 pango_font_description_set_weight
734 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
736 PangoFontDescription *font_desc;
737 font_desc = pango_font_description_from_string(BOLD_FONT);
739 if (bold_style->font_desc)
740 pango_font_description_free
741 (bold_style->font_desc);
742 bold_style->font_desc = font_desc;
745 bold_color_style = gtk_style_copy(bold_style);
746 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
748 bold_tgtfold_style = gtk_style_copy(bold_style);
749 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
753 static gboolean folderview_defer_set(gpointer data)
755 FolderView *folderview = (FolderView *)data;
756 MainWindow *mainwin = folderview->mainwin;
760 if (mainwin->lock_count)
763 debug_print("doing deferred folderview_set now\n");
764 folderview_set(folderview);
766 folderview->deferred_refresh_id = -1;
770 void folderview_set(FolderView *folderview)
772 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
773 MainWindow *mainwin = folderview->mainwin;
774 FolderItem *sel_item = NULL, *op_item = NULL;
779 if (mainwin->lock_count) {
780 if (folderview->deferred_refresh_id == -1)
781 folderview->deferred_refresh_id =
782 g_timeout_add(500, folderview_defer_set, folderview);
783 debug_print("deferred folderview_set\n");
788 debug_print("Setting folder info...\n");
789 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
791 main_window_cursor_wait(mainwin);
793 if (folderview->selected)
794 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
795 if (folderview->opened)
796 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
798 folderview->selected = NULL;
799 folderview->opened = NULL;
801 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
802 gtk_cmclist_clear(GTK_CMCLIST(ctree));
804 folderview_set_folders(folderview);
807 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
809 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
811 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
812 main_window_cursor_normal(mainwin);
813 STATUSBAR_POP(mainwin);
817 void folderview_set_all(void)
821 for (list = folderview_list; list != NULL; list = list->next)
822 folderview_set((FolderView *)list->data);
825 void folderview_select(FolderView *folderview, FolderItem *item)
827 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
828 GtkCMCTreeNode *node;
829 GtkCMCTreeNode *old_selected = folderview->selected;
833 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
834 if (node) folderview_select_node(folderview, node);
836 if (old_selected != node)
837 folder_update_op_count();
840 static void mark_all_read_cb(GtkAction *action, gpointer data)
842 FolderView *folderview = (FolderView *)data;
846 item = folderview_get_selected_item(folderview);
850 if (folderview->summaryview->folder_item != item
851 && prefs_common.ask_mark_all_read) {
852 val = alertpanel_full(_("Mark all as read"),
853 _("Do you really want to mark all mails in this "
854 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
855 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
857 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
859 else if (val & G_ALERTDISABLE)
860 prefs_common.ask_mark_all_read = FALSE;
864 folder_item_update_freeze();
865 if (folderview->summaryview->folder_item != item)
866 summary_lock(folderview->summaryview);
868 summary_freeze(folderview->summaryview);
870 folderutils_mark_all_read(item);
872 if (folderview->summaryview->folder_item != item)
873 summary_unlock(folderview->summaryview);
875 summary_thaw(folderview->summaryview);
876 folder_item_update_thaw();
879 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
881 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
883 g_return_if_fail(node != NULL);
885 if (folderview->open_folder) {
889 folderview->open_folder = TRUE;
890 gtkut_ctree_set_focus_row(ctree, node);
891 gtk_cmctree_select(ctree, node);
892 if (folderview->summaryview->folder_item &&
893 folderview->summaryview->folder_item->total_msgs > 0)
894 summary_grab_focus(folderview->summaryview);
896 gtk_widget_grab_focus(folderview->ctree);
898 gtkut_ctree_expand_parent_all(ctree, node);
901 void folderview_unselect(FolderView *folderview)
903 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
905 (GTK_CMCTREE(folderview->ctree), folderview->opened);
907 folderview->selected = folderview->opened = NULL;
910 static GtkCMCTreeNode *folderview_find_next_marked(GtkCMCTree *ctree,
911 GtkCMCTreeNode *node)
916 node = gtkut_ctree_node_next(ctree, node);
918 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
920 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
921 item = gtk_cmctree_node_get_row_data(ctree, node);
922 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
929 void folderview_select_next_marked(FolderView *folderview)
931 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
932 GtkCMCTreeNode *node = NULL;
933 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
934 gboolean last_open = prefs_common.always_show_msg;
936 prefs_common.summary_select_prio[0] = ACTION_MARKED;
937 prefs_common.always_show_msg = OPENMSG_ALWAYS;
939 if ((node = folderview_find_next_marked(ctree, folderview->opened))
941 folderview_select_node(folderview, node);
945 if (!folderview->opened ||
946 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
949 /* search again from the first node */
950 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
951 folderview_select_node(folderview, node);
954 prefs_common.summary_select_prio[0] = last_summary_select_prio;
955 prefs_common.always_show_msg = last_open;
958 static GtkCMCTreeNode *folderview_find_next_unread(GtkCMCTree *ctree,
959 GtkCMCTreeNode *node)
964 node = gtkut_ctree_node_next(ctree, node);
966 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
968 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
969 item = gtk_cmctree_node_get_row_data(ctree, node);
970 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
977 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
979 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
980 GtkCMCTreeNode *node = NULL;
981 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
982 gboolean last_open = prefs_common.always_show_msg;
984 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
985 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
987 if ((node = folderview_find_next_unread(ctree, folderview->opened))
989 folderview_select_node(folderview, node);
993 if (!folderview->opened ||
994 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
997 /* search again from the first node */
998 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
999 folderview_select_node(folderview, node);
1002 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1003 prefs_common.always_show_msg = last_open;
1006 static GtkCMCTreeNode *folderview_find_next_new(GtkCMCTree *ctree,
1007 GtkCMCTreeNode *node)
1012 node = gtkut_ctree_node_next(ctree, node);
1014 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1016 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1017 item = gtk_cmctree_node_get_row_data(ctree, node);
1018 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1025 void folderview_select_next_new(FolderView *folderview)
1027 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1028 GtkCMCTreeNode *node = NULL;
1029 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1030 gboolean last_open = prefs_common.always_show_msg;
1032 prefs_common.summary_select_prio[0] = ACTION_NEW;
1033 prefs_common.always_show_msg = OPENMSG_ALWAYS;
1035 if ((node = folderview_find_next_new(ctree, folderview->opened))
1037 folderview_select_node(folderview, node);
1041 if (!folderview->opened ||
1042 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1045 /* search again from the first node */
1046 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1047 folderview_select_node(folderview, node);
1050 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1051 prefs_common.always_show_msg = last_open;
1054 FolderItem *folderview_get_selected_item(FolderView *folderview)
1056 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1058 if (!folderview->selected) return NULL;
1059 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1062 static void folderview_set_folders(FolderView *folderview)
1065 list = folder_get_list();
1067 for (; list != NULL; list = list->next) {
1068 folderview_append_folder(folderview, FOLDER(list->data));
1072 static gchar *get_scan_str(FolderItem *item)
1075 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1076 item->folder->name, G_DIR_SEPARATOR,
1079 return g_strdup_printf(_("Scanning folder %s ..."),
1080 item->folder->name);
1082 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1086 for (list = folderview_list; list != NULL; list = list->next) {
1087 FolderView *folderview = (FolderView *)list->data;
1088 MainWindow *mainwin = folderview->mainwin;
1089 gchar *str = get_scan_str(item);
1091 STATUSBAR_PUSH(mainwin, str);
1092 STATUSBAR_POP(mainwin);
1097 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1100 MainWindow *mainwin = mainwindow_get_mainwindow();
1101 FolderView *folderview = NULL;
1102 GtkAdjustment *pos = NULL;
1105 g_return_if_fail(folder != NULL);
1107 if (!folder->klass->scan_tree) return;
1110 alertpanel_full(_("Rebuild folder tree"),
1111 _("Rebuilding the folder tree will remove "
1112 "local caches. Do you want to continue?"),
1113 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1114 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1115 != G_ALERTALTERNATE) {
1121 window = label_window_create(_("Rebuilding folder tree..."));
1123 window = label_window_create(_("Scanning folder tree..."));
1126 folderview = mainwin->folderview;
1129 pos = gtk_scrolled_window_get_vadjustment(
1130 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1131 height = pos->value;
1134 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1135 folder_scan_tree(folder, rebuild);
1136 folder_set_ui_func(folder, NULL, NULL);
1138 folderview_set_all();
1141 pos = gtk_scrolled_window_get_vadjustment(
1142 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1143 gtk_adjustment_set_value(pos, height);
1144 gtk_adjustment_changed(pos);
1146 label_window_destroy(window);
1150 void folderview_fast_rescan_tree(Folder *folder)
1153 MainWindow *mainwin = mainwindow_get_mainwindow();
1154 FolderView *folderview = NULL;
1155 GtkAdjustment *pos = NULL;
1158 g_return_if_fail(folder != NULL);
1160 if (!folder->klass->scan_tree) return;
1164 window = label_window_create(_("Scanning folder tree..."));
1167 folderview = mainwin->folderview;
1170 pos = gtk_scrolled_window_get_vadjustment(
1171 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1172 height = pos->value;
1175 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1176 folder_fast_scan_tree(folder);
1177 folder_set_ui_func(folder, NULL, NULL);
1179 folderview_set_all();
1182 pos = gtk_scrolled_window_get_vadjustment(
1183 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1184 gtk_adjustment_set_value(pos, height);
1185 gtk_adjustment_changed(pos);
1187 label_window_destroy(window);
1191 /** folderview_check_new()
1192 * Scan and update the folder and return the
1193 * count the number of new messages since last check.
1194 * \param folder the folder to check for new messages
1195 * \return the number of new messages since last check
1197 gint folderview_check_new(Folder *folder)
1201 FolderView *folderview;
1203 GtkCMCTreeNode *node;
1205 gint former_new_msgs = 0;
1206 gint former_new = 0, former_unread = 0, former_total;
1208 for (list = folderview_list; list != NULL; list = list->next) {
1209 folderview = (FolderView *)list->data;
1210 ctree = GTK_CMCTREE(folderview->ctree);
1211 folderview->scanning_folder = folder;
1213 main_window_lock(folderview->mainwin);
1215 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1216 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1218 item = gtk_cmctree_node_get_row_data(ctree, node);
1219 if (!item || !item->path || !item->folder) continue;
1220 if (item->no_select) continue;
1221 if (folder && folder != item->folder) continue;
1222 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1223 if (!item->prefs->newmailcheck) continue;
1224 if (item->processing_pending == TRUE) {
1225 debug_print("skipping %s, processing pending\n",
1226 item->path ? item->path : item->name);
1229 if (item->scanning != ITEM_NOT_SCANNING) {
1230 debug_print("skipping %s, scanning\n",
1231 item->path ? item->path : item->name);
1235 str = get_scan_str(item);
1237 STATUSBAR_PUSH(folderview->mainwin, str);
1241 folderview_scan_tree_func(item->folder, item, NULL);
1242 former_new = item->new_msgs;
1243 former_unread = item->unread_msgs;
1244 former_total = item->total_msgs;
1246 if (item->folder->klass->scan_required &&
1247 (item->folder->klass->scan_required(item->folder, item) ||
1248 item->folder->inbox == item ||
1249 item->opened == TRUE ||
1250 item->processing_pending == TRUE)) {
1251 if (folder_item_scan(item) < 0) {
1253 summaryview_unlock(folderview->summaryview, item);
1254 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1255 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1256 item->path ? item->path:item->name);
1257 STATUSBAR_POP(folderview->mainwin);
1259 } else if (!FOLDER_IS_LOCAL(folder)) {
1260 STATUSBAR_POP(folderview->mainwin);
1265 } else if (!item->folder->klass->scan_required) {
1266 if (folder_item_scan(item) < 0) {
1267 summaryview_unlock(folderview->summaryview, item);
1268 if (folder && !FOLDER_IS_LOCAL(folder)) {
1269 STATUSBAR_POP(folderview->mainwin);
1274 if (former_new != item->new_msgs ||
1275 former_unread != item->unread_msgs ||
1276 former_total != item->total_msgs)
1277 folderview_update_node(folderview, node);
1279 new_msgs += item->new_msgs;
1280 former_new_msgs += former_new;
1281 STATUSBAR_POP(folderview->mainwin);
1283 folderview->scanning_folder = NULL;
1284 main_window_unlock(folderview->mainwin);
1288 folder_write_list();
1289 /* Number of new messages since last check is the just the difference
1290 * between former_new_msgs and new_msgs. If new_msgs is less than
1291 * former_new_msgs, that would mean another session accessed the folder
1292 * and the result is not well defined.
1294 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1298 void folderview_check_new_all(void)
1302 FolderView *folderview;
1304 folderview = (FolderView *)folderview_list->data;
1307 main_window_lock(folderview->mainwin);
1308 window = label_window_create
1309 (_("Checking for new messages in all folders..."));
1311 list = folder_get_list();
1312 for (; list != NULL; list = list->next) {
1313 Folder *folder = list->data;
1315 folderview_check_new(folder);
1318 folder_write_list();
1319 folderview_set_all();
1321 label_window_destroy(window);
1322 main_window_unlock(folderview->mainwin);
1326 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1332 if (!item || !item->folder || !item->folder->node)
1335 node = item->folder->node;
1337 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1338 node = node->children;
1341 (item->new_msgs > 0 ||
1342 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1346 while (node != NULL) {
1347 if (node && node->data) {
1348 FolderItem *next_item = (FolderItem*) node->data;
1350 if (folderview_have_new_children_sub(folderview,
1359 static gboolean folderview_have_new_children(FolderView *folderview,
1362 return folderview_have_new_children_sub(folderview, item, FALSE);
1365 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1371 if (!item || !item->folder || !item->folder->node)
1374 node = item->folder->node;
1376 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1377 node = node->children;
1380 (item->unread_msgs > 0 ||
1381 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1385 while (node != NULL) {
1386 if (node && node->data) {
1387 FolderItem *next_item = (FolderItem*) node->data;
1389 if (folderview_have_unread_children_sub(folderview,
1399 static gboolean folderview_have_unread_children(FolderView *folderview,
1402 return folderview_have_unread_children_sub(folderview, item, FALSE);
1405 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1411 if (!item || !item->folder || !item->folder->node)
1414 node = item->folder->node;
1416 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1417 node = node->children;
1419 if (in_sub && item->search_match){
1423 while (node != NULL) {
1424 if (node && node->data) {
1425 FolderItem *next_item = (FolderItem*) node->data;
1427 if (folderview_have_matching_children_sub(folderview,
1437 static gboolean folderview_have_matching_children(FolderView *folderview,
1440 return folderview_have_matching_children_sub(folderview, item, FALSE);
1443 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1449 if (!item || !item->folder || !item->folder->node)
1452 node = item->folder->node;
1454 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1455 node = node->children;
1457 if (item->marked_msgs != 0) {
1461 while (node != NULL) {
1462 if (node && node->data) {
1463 FolderItem *next_item = (FolderItem*) node->data;
1465 if (folderview_have_marked_children_sub(folderview,
1474 static gboolean folderview_have_marked_children(FolderView *folderview,
1477 return folderview_have_marked_children_sub(folderview, item, FALSE);
1480 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1482 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1483 GtkStyle *style = NULL;
1484 GtkStyle *color_style = NULL;
1486 GdkPixmap *xpm, *openxpm;
1487 GdkBitmap *mask, *openmask;
1488 static GdkPixmap *searchicon;
1489 static GdkBitmap *searchmask;
1490 gboolean mark = FALSE;
1493 gboolean add_unread_mark;
1494 gboolean add_sub_match_mark;
1495 gboolean use_bold, use_color;
1496 gint *col_pos = folderview->col_pos;
1497 SpecialFolderItemType stype;
1499 item = gtk_cmctree_node_get_row_data(ctree, node);
1500 g_return_if_fail(item != NULL);
1502 if (!GTK_CMCTREE_ROW(node)->expanded)
1503 mark = folderview_have_marked_children(folderview, item);
1505 mark = (item->marked_msgs != 0);
1507 stype = item->stype;
1508 if (stype == F_NORMAL) {
1509 if (folder_has_parent_of_type(item, F_TRASH))
1511 else if (folder_has_parent_of_type(item, F_DRAFT))
1513 else if (folder_has_parent_of_type(item, F_OUTBOX))
1515 else if (folder_has_parent_of_type(item, F_QUEUE))
1520 if (item->hide_read_msgs) {
1521 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1522 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1523 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1524 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1526 xpm = mark?m_inboxxpm:inboxxpm;
1527 mask = mark?m_inboxxpmmask:inboxxpmmask;
1528 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1529 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1533 if (item->hide_read_msgs) {
1534 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1535 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1536 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1537 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1539 xpm = mark?m_outboxxpm:outboxxpm;
1540 mask = mark?m_outboxxpmmask:outboxxpmmask;
1541 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1542 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1546 if (item->hide_read_msgs) {
1547 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1548 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1549 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1550 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1552 xpm = mark?m_queuexpm:queuexpm;
1553 mask = mark?m_queuexpmmask:queuexpmmask;
1554 openxpm = mark?m_queueopenxpm:queueopenxpm;
1555 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1559 if (item->hide_read_msgs) {
1560 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1561 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1562 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1563 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1565 xpm = mark?m_trashxpm:trashxpm;
1566 mask = mark?m_trashxpmmask:trashxpmmask;
1567 openxpm = mark?m_trashopenxpm:trashopenxpm;
1568 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1572 xpm = mark?m_draftsxpm:draftsxpm;
1573 mask = mark?m_draftsxpmmask:draftsxpmmask;
1574 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1575 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1578 if (item->hide_read_msgs) {
1579 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1580 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1581 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1582 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1584 xpm = mark?m_folderxpm:folderxpm;
1585 mask = mark?m_folderxpmmask:folderxpmmask;
1586 openxpm = mark?m_folderopenxpm:folderopenxpm;
1587 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1591 if (item->no_select) {
1592 xpm = openxpm = noselectxpm;
1593 mask = openmask = noselectxpmmask;
1596 name = folder_item_get_name(item);
1598 if (!GTK_CMCTREE_ROW(node)->expanded) {
1599 add_unread_mark = folderview_have_unread_children(
1601 add_sub_match_mark = folderview_have_matching_children(
1604 add_unread_mark = FALSE;
1605 add_sub_match_mark = FALSE;
1608 if (item->search_match) {
1610 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1611 &searchicon, &searchmask);
1613 xpm = openxpm = searchicon;
1614 mask = openmask = searchmask;
1618 if (prefs_common.display_folder_unread) {
1619 if (folder_has_parent_of_type(item, F_QUEUE)) {
1620 /* only total_msgs matters here */
1621 if (item->total_msgs > 0) {
1622 /* show total number (should be equal to the unread number)
1624 str = g_strdup_printf("%s (%d%s%s)",
1625 name, item->total_msgs,
1626 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1627 (item->unreadmarked_msgs > 0) ? "!" : "");
1630 if (prefs_common.display_folder_unread == 1) {
1631 if (item->unread_msgs > 0) {
1632 /* show unread number and signs */
1633 str = g_strdup_printf("%s (%d%s%s)",
1634 name, item->unread_msgs,
1635 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1636 (item->unreadmarked_msgs > 0) ? "!" : "");
1639 if (item->total_msgs > 0) {
1640 /* show unread number, total number and signs if any */
1641 str = g_strdup_printf("%s (%d/%d%s%s)",
1642 name, item->unread_msgs, item->total_msgs,
1643 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1644 (item->unreadmarked_msgs > 0) ? "!" : "");
1648 if ((str == NULL) &&
1649 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1650 /* no unread/total numbers, but at least one sign */
1651 str = g_strdup_printf("%s (%s%s)",
1653 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1654 (item->unreadmarked_msgs > 0) ? "!" : "");
1658 /* last fallback, folder name only or with ! sign */
1659 str = g_strdup_printf("%s%s",
1660 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1662 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1663 xpm, mask, openxpm, openmask,
1664 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1668 if (!folder_item_parent(item)) {
1669 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1670 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1671 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1673 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1674 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1675 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1678 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1679 folder_has_parent_of_type(item, F_DRAFT) ||
1680 folder_has_parent_of_type(item, F_TRASH)) {
1681 use_bold = use_color = FALSE;
1682 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1683 /* highlight queue folder if there are any messages */
1684 use_bold = use_color = (item->total_msgs > 0);
1686 /* if unread messages exist, print with bold font */
1687 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1689 /* if new messages exist, print with colored letter */
1691 (item->new_msgs > 0) ||
1693 folderview_have_new_children(folderview, item));
1696 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1701 if (item->prefs->color > 0 && !use_color) {
1702 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1703 color_style = gtk_style_copy(bold_style);
1704 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1705 style = color_style;
1706 } else if (use_color) {
1707 style = bold_color_style;
1710 if (item->op_count > 0) {
1711 style = bold_tgtfold_style;
1713 } else if (use_color) {
1714 style = normal_color_style;
1715 gtk_cmctree_node_set_foreground(ctree, node,
1716 &folderview->color_new);
1717 } else if (item->op_count > 0) {
1718 style = bold_tgtfold_style;
1719 } else if (item->prefs->color > 0) {
1721 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1722 color_style = gtk_style_copy(normal_style);
1723 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1724 style = color_style;
1726 style = normal_style;
1729 gtk_cmctree_node_set_row_style(ctree, node, style);
1731 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1732 folderview_update_node(folderview, node);
1735 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1738 FolderView *folderview;
1740 GtkCMCTreeNode *node;
1742 g_return_if_fail(item != NULL);
1744 for (list = folderview_list; list != NULL; list = list->next) {
1745 folderview = (FolderView *)list->data;
1746 ctree = GTK_CMCTREE(folderview->ctree);
1748 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1750 item->search_match = matches;
1751 folderview_update_node(folderview, node);
1756 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1758 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1759 FolderView *folderview = (FolderView *)data;
1761 GtkCMCTreeNode *node;
1762 g_return_val_if_fail(update_info != NULL, TRUE);
1763 g_return_val_if_fail(update_info->item != NULL, TRUE);
1764 g_return_val_if_fail(folderview != NULL, FALSE);
1766 ctree = GTK_CMCTREE(folderview->ctree);
1768 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1771 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1772 folderview_update_node(folderview, node);
1774 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1775 update_info->item == folderview->summaryview->folder_item &&
1776 update_info->item != NULL)
1777 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1778 summary_show(folderview->summaryview, update_info->item);
1784 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1785 GNode *gnode, GtkCMCTreeNode *cnode,
1788 FolderView *folderview = (FolderView *)data;
1789 FolderItem *item = FOLDER_ITEM(gnode->data);
1791 g_return_val_if_fail(item != NULL, FALSE);
1793 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1794 folderview_update_node(folderview, cnode);
1799 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1802 FolderView *folderview = (FolderView *)data;
1805 if (GTK_CMCTREE_ROW(node)->children) {
1806 item = gtk_cmctree_node_get_row_data(ctree, node);
1807 g_return_if_fail(item != NULL);
1809 if (!item->collapsed)
1810 gtk_cmctree_expand(ctree, node);
1812 folderview_update_node(folderview, node);
1816 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1817 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1820 GtkCMCTreeNode *node, *parent, *sibling;
1822 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1824 g_warning("%s not found.\n", item->path);
1826 parent = GTK_CMCTREE_ROW(node)->parent;
1827 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1828 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1830 sibling = GTK_CMCTREE_ROW(parent)->children;
1834 tmp = gtk_cmctree_node_get_row_data
1836 if (tmp->stype != F_NORMAL)
1837 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1841 if (node != sibling)
1842 gtk_cmctree_move(ctree, node, parent, sibling);
1849 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1852 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1853 GtkCMCTreeNode *prev = NULL;
1855 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1856 gtk_sctree_sort_recursive(ctree, root);
1857 if (root && GTK_CMCTREE_ROW(root)->parent) {
1858 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1861 set_special_folder(ctree, folder->inbox, root, &prev);
1862 set_special_folder(ctree, folder->outbox, root, &prev);
1863 set_special_folder(ctree, folder->draft, root, &prev);
1864 set_special_folder(ctree, folder->queue, root, &prev);
1865 set_special_folder(ctree, folder->trash, root, &prev);
1866 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1869 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1871 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1872 GtkCMCTreeNode *root;
1874 g_return_if_fail(folder != NULL);
1876 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1877 folderview_gnode_func, folderview);
1878 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1880 folderview_sort_folders(folderview, root, folder);
1883 /* callback functions */
1884 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1885 GdkEventButton *event)
1889 FolderViewPopup *fpopup;
1890 GtkActionGroup *action_group;
1892 FolderItem *special_trash = NULL, *special_queue = NULL;
1894 GtkUIManager *ui_manager = gtk_ui_manager_new();
1896 if (folderview->ui_manager)
1897 g_object_unref(folderview->ui_manager);
1899 folderview->ui_manager = ui_manager;
1900 item = folderview_get_selected_item(folderview);
1902 g_return_if_fail(item != NULL);
1903 g_return_if_fail(item->folder != NULL);
1904 folder = item->folder;
1906 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1909 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1911 fpopup = g_hash_table_lookup(folderview_popups, "common");
1912 action_group = g_hash_table_lookup(folderview->popups, "common");
1915 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1916 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1917 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1919 if (fpopup->add_menuitems)
1920 fpopup->add_menuitems(ui_manager, item);
1922 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1923 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1924 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1925 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1926 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1927 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1929 if (fpopup->set_sensitivity != NULL)
1930 fpopup->set_sensitivity(ui_manager, item);
1932 if (NULL != (ac = account_find_from_item(item))) {
1933 special_trash = account_get_special_folder(ac, F_TRASH);
1934 special_queue = account_get_special_folder(ac, F_QUEUE);
1937 if ((item == folder->trash || item == special_trash
1938 || folder_has_parent_of_type(item, F_TRASH))) {
1939 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1940 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1943 if ((item == folder->queue || item == special_queue
1944 || folder_has_parent_of_type(item, F_QUEUE))) {
1945 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1946 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1949 #define SET_SENS(name, sens) \
1950 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1952 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1953 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1954 folderview->selected == folderview->opened);
1955 SET_SENS("FolderViewPopup/Properties", TRUE);
1957 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1958 item->total_msgs >= 1 && !item->processing_pending);
1959 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1960 !item->no_select && !item->processing_pending);
1962 if (item == folder->trash || item == special_trash
1963 || folder_has_parent_of_type(item, F_TRASH)) {
1964 GSList *msglist = folder_item_get_msg_list(item);
1965 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1966 procmsg_msg_list_free(msglist);
1968 if (item == folder->queue || item == special_queue
1969 || folder_has_parent_of_type(item, F_QUEUE)) {
1970 GSList *msglist = folder_item_get_msg_list(item);
1971 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1972 procmsg_msg_list_free(msglist);
1976 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1977 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1978 g_signal_connect(G_OBJECT(popup), "selection_done",
1979 G_CALLBACK(folderview_popup_close),
1981 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1982 event->button, event->time);
1985 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1986 FolderView *folderview)
1988 GtkCMCList *clist = GTK_CMCLIST(ctree);
1989 gint prev_row = -1, row = -1, column = -1;
1991 if (!event) return FALSE;
1993 if (event->button == 1 || event->button == 2) {
1994 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1995 folderview->open_folder = TRUE;
1997 if (event->type == GDK_2BUTTON_PRESS) {
1998 if (clist->selection) {
1999 GtkCMCTreeNode *node;
2001 node = GTK_CMCTREE_NODE(clist->selection->data);
2003 gtk_cmctree_toggle_expansion(
2006 folderview->open_folder = FALSE;
2013 if (event->button == 2 || event->button == 3) {
2015 if (clist->selection) {
2016 GtkCMCTreeNode *node;
2018 node = GTK_CMCTREE_NODE(clist->selection->data);
2020 prev_row = gtkut_ctree_get_nth_from_node
2021 (GTK_CMCTREE(ctree), node);
2024 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2027 if (prev_row != row) {
2028 gtk_cmclist_unselect_all(clist);
2029 if (event->button == 2)
2030 folderview_select_node
2032 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2035 gtk_cmclist_select_row(clist, row, column);
2039 if (event->button != 3) return FALSE;
2041 folderview_set_sens_and_popup_menu(folderview, row, event);
2045 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2046 FolderView *folderview)
2048 int row = -1, column = -1;
2050 if (!event) return FALSE;
2052 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2055 if (event->button == 1 && folderview->open_folder == FALSE &&
2056 folderview->opened != NULL) {
2057 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2058 folderview->opened);
2059 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2065 #define BREAK_ON_MODIFIER_KEY() \
2066 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2068 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2069 FolderView *folderview)
2071 if (!event) return FALSE;
2073 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2076 switch (event->keyval) {
2078 #ifndef GENERIC_UMPC
2082 if (folderview->selected) {
2083 folderview_select_node(folderview,
2084 folderview->selected);
2089 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2090 gtk_cmctree_toggle_expansion(
2091 GTK_CMCTREE(folderview->ctree),
2092 folderview->selected);
2097 BREAK_ON_MODIFIER_KEY();
2098 if (folderview->selected) {
2099 if (folderview->opened == folderview->selected &&
2100 (!folderview->summaryview->folder_item ||
2101 folderview->summaryview->folder_item->total_msgs == 0))
2102 folderview_select_next_unread(folderview, TRUE);
2104 folderview_select_node(folderview,
2105 folderview->selected);
2115 typedef struct _PostponedSelectData
2118 GtkCMCTreeNode *row;
2120 FolderView *folderview;
2121 } PostponedSelectData;
2123 static gboolean postpone_select(void *data)
2125 PostponedSelectData *psdata = (PostponedSelectData *)data;
2126 debug_print("trying again\n");
2127 psdata->folderview->open_folder = TRUE;
2128 main_window_cursor_normal(psdata->folderview->mainwin);
2129 STATUSBAR_POP(psdata->folderview->mainwin);
2130 folderview_selected(psdata->ctree, psdata->row,
2131 psdata->column, psdata->folderview);
2136 void folderview_close_opened(FolderView *folderview)
2138 if (folderview->opened) {
2139 FolderItem *olditem;
2141 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2142 folderview->opened);
2144 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2145 olditem->path ? olditem->path:olditem->name);
2146 /* will be null if we just moved the previously opened folder */
2147 STATUSBAR_PUSH(folderview->mainwin, buf);
2148 main_window_cursor_wait(folderview->mainwin);
2150 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2151 summary_show(folderview->summaryview, NULL);
2152 folder_item_close(olditem);
2153 main_window_cursor_normal(folderview->mainwin);
2154 STATUSBAR_POP(folderview->mainwin);
2155 if (olditem->folder->klass->item_closed)
2156 olditem->folder->klass->item_closed(olditem);
2161 if (folderview->opened &&
2162 !GTK_CMCTREE_ROW(folderview->opened)->children)
2163 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2165 folderview->opened = NULL;
2167 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2168 gint column, FolderView *folderview)
2170 static gboolean can_select = TRUE; /* exclusive lock */
2175 GtkCMCTreeNode *old_opened = folderview->opened;
2177 folderview->selected = row;
2179 debug_print("newly selected %p, opened %p\n", folderview->selected,
2180 folderview->opened);
2181 if (folderview->opened == row) {
2182 folderview->open_folder = FALSE;
2187 item = gtk_cmctree_node_get_row_data(ctree, row);
2190 folderview->open_folder = FALSE;
2194 if (!can_select || summary_is_locked(folderview->summaryview)) {
2195 if (folderview->opened) {
2196 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2197 gtk_cmctree_select(ctree, folderview->opened);
2199 folderview->open_folder = FALSE;
2204 if (!folderview->open_folder) {
2211 /* Save cache for old folder */
2212 /* We don't want to lose all caches if sylpheed crashed */
2213 /* resets folderview->opened to NULL */
2214 folderview_close_opened(folderview);
2216 /* CLAWS: set compose button type: news folder items
2217 * always have a news folder as parent */
2219 toolbar_set_compose_button
2220 (folderview->mainwin->toolbar,
2221 FOLDER_TYPE(item->folder) == F_NEWS ?
2222 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2225 debug_print("Folder %s is selected\n", item->path);
2227 if (!GTK_CMCTREE_ROW(row)->children)
2228 gtk_cmctree_expand(ctree, row);
2230 /* ungrab the mouse event */
2231 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2232 gtk_grab_remove(GTK_WIDGET(ctree));
2233 if (gdk_pointer_is_grabbed())
2234 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2238 /* TODO: wwp: avoid displaying (null) in the status bar */
2239 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2240 item->path : "(null)");
2241 debug_print("%s\n", buf);
2242 STATUSBAR_PUSH(folderview->mainwin, buf);
2245 main_window_cursor_wait(folderview->mainwin);
2247 if (folderview->scanning_folder == item->folder) {
2250 res = folder_item_open(item);
2253 if (res == -1 && item->no_select == FALSE) {
2254 main_window_cursor_normal(folderview->mainwin);
2255 STATUSBAR_POP(folderview->mainwin);
2257 alertpanel_error(_("Folder could not be opened."));
2259 folderview->open_folder = FALSE;
2263 } else if (res == -2 && item->no_select == FALSE) {
2264 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2265 data->ctree = ctree;
2267 data->column = column;
2268 data->folderview = folderview;
2269 debug_print("postponing open of %s till end of scan\n",
2270 item->path ? item->path:item->name);
2271 folderview->open_folder = FALSE;
2273 g_timeout_add(500, postpone_select, data);
2278 main_window_cursor_normal(folderview->mainwin);
2281 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2282 opened = summary_show(folderview->summaryview, item);
2284 folder_clean_cache_memory(item);
2287 gtkut_ctree_set_focus_row(ctree, old_opened);
2288 gtk_cmctree_select(ctree, old_opened);
2289 folderview->opened = old_opened;
2291 folderview->opened = row;
2292 if (gtk_cmctree_node_is_visible(ctree, row)
2293 != GTK_VISIBILITY_FULL)
2294 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2297 STATUSBAR_POP(folderview->mainwin);
2299 folderview->open_folder = FALSE;
2304 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2305 FolderView *folderview)
2309 item = gtk_cmctree_node_get_row_data(ctree, node);
2310 g_return_if_fail(item != NULL);
2311 item->collapsed = FALSE;
2312 folderview_update_node(folderview, node);
2315 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2316 FolderView *folderview)
2320 item = gtk_cmctree_node_get_row_data(ctree, node);
2321 g_return_if_fail(item != NULL);
2322 item->collapsed = TRUE;
2323 folderview_update_node(folderview, node);
2326 static void folderview_popup_close(GtkMenuShell *menu_shell,
2327 FolderView *folderview)
2329 if (!folderview->opened) return;
2331 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2334 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2335 FolderView *folderview)
2337 FolderColumnType type = folderview->col_state[column].type;
2339 prefs_common.folder_col_size[type] = width;
2342 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2346 folderview_create_folder_node(folderview, item);
2348 if (!item || !item->folder || !item->folder->node)
2351 srcnode = item->folder->node;
2352 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2353 srcnode = srcnode->children;
2354 while (srcnode != NULL) {
2355 if (srcnode && srcnode->data) {
2356 FolderItem *next_item = (FolderItem*) srcnode->data;
2357 folderview_create_folder_node_recursive(folderview, next_item);
2359 srcnode = srcnode->next;
2363 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2365 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2366 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2367 GtkCMCTreeNode *node, *parent_node;
2368 gint *col_pos = folderview->col_pos;
2369 FolderItemUpdateData hookdata;
2371 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2372 if (parent_node == NULL)
2375 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2377 text[col_pos[F_COL_FOLDER]] = item->name;
2378 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2380 folderxpm, folderxpmmask,
2381 folderopenxpm, folderopenxpmmask,
2383 gtk_cmctree_expand(ctree, parent_node);
2384 gtk_cmctree_node_set_row_data(ctree, node, item);
2386 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2387 folderview_sort_folders(folderview, parent_node, item->folder);
2389 hookdata.item = item;
2390 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2391 hookdata.msg = NULL;
2392 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2394 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2397 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2399 FolderView *folderview = (FolderView *)data;
2400 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2402 GSList *mlist = NULL;
2404 FolderItem *special_trash = NULL;
2407 if (!folderview->selected) return;
2408 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2409 g_return_if_fail(item != NULL);
2410 g_return_if_fail(item->folder != NULL);
2412 if (NULL != (ac = account_find_from_item(item)))
2413 special_trash = account_get_special_folder(ac, F_TRASH);
2415 if (item != item->folder->trash && item != special_trash
2416 && !folder_has_parent_of_type(item, F_TRASH)) return;
2418 if (prefs_common.ask_on_clean) {
2419 if (alertpanel(_("Empty trash"),
2420 _("Delete all messages in trash?"),
2421 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2425 mlist = folder_item_get_msg_list(item);
2427 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2428 MsgInfo * msginfo = (MsgInfo *) cur->data;
2429 if (MSG_IS_LOCKED(msginfo->flags))
2431 /* is it partially received? (partial_recv isn't cached) */
2432 if (msginfo->total_size != 0 &&
2433 msginfo->size != (off_t)msginfo->total_size)
2434 partial_mark_for_delete(msginfo);
2436 procmsg_msg_list_free(mlist);
2438 folder_item_remove_all_msg(item);
2441 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2443 FolderView *folderview = (FolderView *)data;
2444 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2446 FolderItem *special_queue = NULL;
2448 gchar *errstr = NULL;
2450 if (!folderview->selected) return;
2451 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2452 g_return_if_fail(item != NULL);
2453 g_return_if_fail(item->folder != NULL);
2455 if (NULL != (ac = account_find_from_item(item)))
2456 special_queue = account_get_special_folder(ac, F_QUEUE);
2458 if (item != item->folder->queue && item != special_queue
2459 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2461 if (procmsg_queue_is_empty(item))
2464 if (prefs_common.work_offline)
2465 if (alertpanel(_("Offline warning"),
2466 _("You're working offline. Override?"),
2467 GTK_STOCK_NO, GTK_STOCK_YES,
2468 NULL) != G_ALERTALTERNATE)
2471 /* ask for confirmation before sending queued messages only
2472 in online mode and if there is at least one message queued
2473 in any of the folder queue
2475 if (prefs_common.confirm_send_queued_messages) {
2476 if (!prefs_common.work_offline) {
2477 if (alertpanel(_("Send queued messages"),
2478 _("Send all queued messages?"),
2479 GTK_STOCK_CANCEL, _("_Send"),
2480 NULL) != G_ALERTALTERNATE)
2485 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2487 alertpanel_error_log(_("Some errors occurred while "
2488 "sending queued messages."));
2490 alertpanel_error_log(_("Some errors occurred "
2491 "while sending queued messages:\n%s"), errstr);
2497 static void folderview_search_cb(GtkAction *action, gpointer data)
2499 FolderView *folderview = (FolderView *)data;
2500 summary_search(folderview->summaryview);
2503 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2505 FolderView *folderview = (FolderView *)data;
2506 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2509 if (!folderview->selected) return;
2511 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2512 g_return_if_fail(item != NULL);
2513 g_return_if_fail(item->folder != NULL);
2515 item->processing_pending = TRUE;
2516 folder_item_apply_processing(item);
2517 item->processing_pending = FALSE;
2520 static void folderview_property_cb(GtkAction *action, gpointer data)
2522 FolderView *folderview = (FolderView *)data;
2523 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2526 if (!folderview->selected) return;
2528 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2529 g_return_if_fail(item != NULL);
2530 g_return_if_fail(item->folder != NULL);
2532 prefs_folder_item_open(item);
2535 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2537 GSList *list = NULL;
2538 GSList *done = NULL;
2539 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2541 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2542 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2543 && list->data != node) {
2544 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2545 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2548 for (list = done; list != NULL; list = g_slist_next(list)) {
2549 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2555 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2556 FolderItem *to_folder, gboolean copy)
2558 FolderItem *from_parent = NULL;
2559 FolderItem *new_folder = NULL;
2560 GtkCMCTreeNode *src_node = NULL;
2564 g_return_if_fail(folderview != NULL);
2565 g_return_if_fail(from_folder != NULL);
2566 g_return_if_fail(to_folder != NULL);
2568 src_node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), NULL, from_folder);
2569 from_parent = folder_item_parent(from_folder);
2571 if (prefs_common.warn_dnd) {
2572 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2573 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2574 from_folder->name, to_folder->name);
2575 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2576 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2577 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2580 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2582 else if (status & G_ALERTDISABLE)
2583 prefs_common.warn_dnd = FALSE;
2586 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2587 from_folder->name, to_folder->name);
2588 STATUSBAR_PUSH(folderview->mainwin, buf);
2590 summary_clear_all(folderview->summaryview);
2591 folderview->opened = NULL;
2592 folderview->selected = NULL;
2593 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2595 main_window_cursor_wait(folderview->mainwin);
2597 statusbar_verbosity_set(FALSE);
2598 folder_item_update_freeze();
2599 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2600 statusbar_verbosity_set(FALSE);
2601 main_window_cursor_normal(folderview->mainwin);
2602 STATUSBAR_POP(folderview->mainwin);
2603 folder_item_update_thaw();
2604 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2606 folderview_sort_folders(folderview,
2607 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2608 NULL, to_folder), new_folder->folder);
2609 folderview_select(folderview, new_folder);
2611 statusbar_verbosity_set(FALSE);
2612 main_window_cursor_normal(folderview->mainwin);
2613 STATUSBAR_POP(folderview->mainwin);
2614 folder_item_update_thaw();
2616 case F_MOVE_FAILED_DEST_IS_PARENT:
2617 alertpanel_error(_("Source and destination are the same."));
2619 case F_MOVE_FAILED_DEST_IS_CHILD:
2620 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2621 _("Can't move a folder to one of its children."));
2623 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2624 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2627 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2632 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2635 static gint folderview_clist_compare(GtkCMCList *clist,
2636 gconstpointer ptr1, gconstpointer ptr2)
2638 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2639 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2641 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2643 return item1->order - item2->order;
2646 // if only one folder has an order it comes first
2647 if (item1->order > 0)
2651 if (item2->order > 0)
2657 return (item2->name != NULL);
2661 return g_utf8_collate(item1->name, item2->name);
2664 static void folderview_processing_cb(GtkAction *action, gpointer data)
2666 FolderView *folderview = (FolderView *)data;
2667 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2671 if (!folderview->selected) return;
2673 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2674 g_return_if_fail(item != NULL);
2675 g_return_if_fail(item->folder != NULL);
2677 id = folder_item_get_identifier(item);
2678 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2681 prefs_filtering_open(&item->prefs->processing, title,
2682 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2686 void folderview_set_target_folder_color(gint color_op)
2690 FolderView *folderview;
2692 for (list = folderview_list; list != NULL; list = list->next) {
2693 folderview = (FolderView *)list->data;
2694 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2696 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2697 folderview->color_op;
2703 static gchar *last_smallfont = NULL;
2704 static gchar *last_normalfont = NULL;
2705 static gchar *last_boldfont = NULL;
2706 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2709 g_free(last_smallfont);
2710 last_smallfont = NULL;
2711 g_free(last_normalfont);
2712 last_normalfont = NULL;
2713 g_free(last_boldfont);
2714 last_boldfont = NULL;
2717 void folderview_reflect_prefs(void)
2719 gboolean update_font = FALSE;
2720 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2721 FolderItem *item = folderview_get_selected_item(folderview);
2722 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2723 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2724 gint height = pos->value;
2726 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2727 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2728 !last_boldfont || strcmp(last_boldfont, BOLD_FONT))
2731 g_free(last_smallfont);
2732 last_smallfont = g_strdup(SMALL_FONT);
2733 g_free(last_normalfont);
2734 last_normalfont = g_strdup(NORMAL_FONT);
2735 g_free(last_boldfont);
2736 last_boldfont = g_strdup(BOLD_FONT);
2739 normal_style = normal_color_style = bold_style =
2740 bold_color_style = bold_tgtfold_style = NULL;
2742 folderview_init(folderview);
2744 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2745 folderview_column_set_titles(folderview);
2746 folderview_set_all();
2748 g_signal_handlers_block_by_func
2749 (G_OBJECT(folderview->ctree),
2750 G_CALLBACK(folderview_selected), folderview);
2753 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2754 GTK_CMCTREE(folderview->ctree), NULL, item);
2756 folderview_select(folderview, item);
2757 folderview->open_folder = FALSE;
2758 folderview->selected = node;
2761 g_signal_handlers_unblock_by_func
2762 (G_OBJECT(folderview->ctree),
2763 G_CALLBACK(folderview_selected), folderview);
2765 pos = gtk_scrolled_window_get_vadjustment(
2766 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2767 gtk_adjustment_set_value(pos, height);
2768 gtk_adjustment_changed(pos);
2769 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2772 static void drag_state_stop(FolderView *folderview)
2774 if (folderview->drag_timer)
2775 g_source_remove(folderview->drag_timer);
2776 folderview->drag_timer = 0;
2777 folderview->drag_node = NULL;
2780 static gint folderview_defer_expand(FolderView *folderview)
2782 if (folderview->drag_node) {
2783 folderview_recollapse_nodes(folderview, folderview->drag_node);
2784 if (folderview->drag_item->collapsed) {
2785 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2786 folderview->nodes_to_recollapse = g_slist_append
2787 (folderview->nodes_to_recollapse, folderview->drag_node);
2790 folderview->drag_item = NULL;
2791 folderview->drag_timer = 0;
2795 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2797 /* the idea is that we call drag_state_start() whenever we want expansion to
2798 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2799 * we need to call drag_state_stop() */
2800 drag_state_stop(folderview);
2801 /* request expansion */
2802 if (0 != (folderview->drag_timer = g_timeout_add
2803 (prefs_common.hover_timeout,
2804 (GtkFunction)folderview_defer_expand,
2806 folderview->drag_node = node;
2807 folderview->drag_item = item;
2810 #ifndef GENERIC_UMPC
2811 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2812 FolderView *folderview)
2814 GdkDragContext *context;
2816 g_return_if_fail(folderview != NULL);
2817 if (folderview->selected == NULL) return;
2818 if (folderview->nodes_to_recollapse)
2819 g_slist_free(folderview->nodes_to_recollapse);
2820 folderview->nodes_to_recollapse = NULL;
2821 context = gtk_drag_begin(widget, folderview->target_list,
2822 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2823 gtk_drag_set_icon_default(context);
2826 static void folderview_drag_data_get(GtkWidget *widget,
2827 GdkDragContext *drag_context,
2828 GtkSelectionData *selection_data,
2831 FolderView *folderview)
2835 gchar *source = NULL;
2836 if (info == TARGET_DUMMY) {
2837 for (cur = GTK_CMCLIST(folderview->ctree)->selection;
2838 cur != NULL; cur = cur->next) {
2839 item = gtk_cmctree_node_get_row_data
2840 (GTK_CMCTREE(folderview->ctree),
2841 GTK_CMCTREE_NODE(cur->data));
2843 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2844 gtk_selection_data_set(selection_data,
2845 selection_data->target, 8,
2846 source, strlen(source));
2852 g_warning("unknown info %d\n", info);
2856 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2858 FolderUpdateData *hookdata;
2859 FolderView *folderview;
2863 folderview = (FolderView *) userdata;
2864 g_return_val_if_fail(hookdata != NULL, FALSE);
2865 g_return_val_if_fail(folderview != NULL, FALSE);
2867 ctree = folderview->ctree;
2868 g_return_val_if_fail(ctree != NULL, FALSE);
2870 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2871 folderview_create_folder_node(folderview, hookdata->item);
2872 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2873 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2874 NULL, folder_item_parent(hookdata->item));
2875 folderview_sort_folders(folderview, node, hookdata->folder);
2876 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2877 GtkCMCTreeNode *node;
2879 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2881 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2882 if (folderview->selected == node)
2883 folderview->selected = NULL;
2884 if (folderview->opened == node)
2885 folderview->opened = NULL;
2887 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2888 folderview_set(folderview);
2893 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2894 GdkDragContext *context,
2898 FolderView *folderview)
2901 FolderItem *item = NULL, *src_item = NULL;
2902 GtkCMCTreeNode *node = NULL;
2903 gboolean acceptable = FALSE;
2904 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2905 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2906 int height = (int)pos->page_size;
2907 int total_height = (int)pos->upper;
2908 int vpos = (int) pos->value;
2909 int offset = prefs_common.show_col_headers ? 24:0;
2911 if (gtk_cmclist_get_selection_info
2912 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2913 GtkWidget *srcwidget;
2915 if (y > height - 24 && height + vpos < total_height) {
2916 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2917 gtk_adjustment_changed(pos);
2919 if (y < 48 && y > 0) {
2920 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2921 gtk_adjustment_changed(pos);
2924 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2925 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2926 src_item = folderview->summaryview->folder_item;
2928 srcwidget = gtk_drag_get_source_widget(context);
2929 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2930 /* comes from summaryview */
2931 /* we are copying messages, so only accept folder items that are not
2932 the source item, are no root items and can copy messages */
2933 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2934 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2935 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2937 } else if (srcwidget == folderview->ctree) {
2938 /* comes from folderview */
2939 /* we are moving folder items, only accept folders that are not
2940 the source items and can copy messages and create folder items */
2941 if (item && item->folder && src_item && src_item != item &&
2942 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2943 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2944 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2945 || item->folder == src_item->folder))
2948 /* comes from another app */
2949 /* we are adding messages, so only accept folder items that are
2950 no root items and can copy messages */
2951 if (item && item->folder && folder_item_parent(item) != NULL
2952 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2953 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2958 if (acceptable || (src_item && src_item == item))
2959 drag_state_start(folderview, node, item);
2962 g_signal_handlers_block_by_func
2964 G_CALLBACK(folderview_selected), folderview);
2965 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2966 g_signal_handlers_unblock_by_func
2968 G_CALLBACK(folderview_selected), folderview);
2969 gdk_drag_status(context,
2970 (context->actions == GDK_ACTION_COPY ?
2971 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2973 if (folderview->opened)
2974 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2975 gdk_drag_status(context, 0, time);
2981 static void folderview_drag_leave_cb(GtkWidget *widget,
2982 GdkDragContext *context,
2984 FolderView *folderview)
2986 drag_state_stop(folderview);
2987 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2990 static void free_info (gpointer stuff, gpointer data)
2995 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2996 guint time, FolderItem *item)
2999 GSList *msglist = NULL;
3000 list = uri_list_extract_filenames(data);
3001 if (!(item && item->folder && folder_item_parent(item) != NULL
3002 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3004 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3005 debug_print("item doesn't fit\n");
3009 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3010 debug_print("list is empty\n");
3013 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3014 MsgFileInfo *info = NULL;
3016 if (file_is_email((gchar *)tmp->data)) {
3017 info = g_new0(MsgFileInfo, 1);
3018 info->msginfo = NULL;
3019 info->file = (gchar *)tmp->data;
3020 msglist = g_slist_prepend(msglist, info);
3021 debug_print("file is a mail\n");
3023 debug_print("file isn't a mail\n");
3027 msglist = g_slist_reverse(msglist);
3028 folder_item_add_msgs(item, msglist, FALSE);
3029 g_slist_foreach(msglist, free_info, NULL);
3030 g_slist_free(msglist);
3031 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3033 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3035 list_free_strings(list);
3039 static void folderview_drag_received_cb(GtkWidget *widget,
3040 GdkDragContext *drag_context,
3043 GtkSelectionData *data,
3046 FolderView *folderview)
3049 FolderItem *item = NULL, *src_item;
3050 GtkCMCTreeNode *node;
3051 int offset = prefs_common.show_col_headers ? 24:0;
3053 if (info == TARGET_DUMMY) {
3054 drag_state_stop(folderview);
3055 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3056 /* comes from summaryview */
3057 if (gtk_cmclist_get_selection_info
3058 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3061 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3062 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3063 src_item = folderview->summaryview->folder_item;
3065 if (item->no_select) {
3066 alertpanel_error(_("The destination folder can only be used to "
3067 "store subfolders."));
3070 /* re-check (due to acceptable possibly set for folder moves */
3071 if (!(item && item->folder && item->path && !item->no_select &&
3072 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3075 if (item && src_item) {
3076 switch (drag_context->action) {
3077 case GDK_ACTION_COPY:
3078 summary_copy_selected_to(folderview->summaryview, item);
3079 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3081 case GDK_ACTION_MOVE:
3082 case GDK_ACTION_DEFAULT:
3084 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3085 summary_copy_selected_to(folderview->summaryview, item);
3087 summary_move_selected_to(folderview->summaryview, item);
3088 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3091 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3093 /* comes from folderview */
3095 gboolean folder_is_normal = TRUE;
3096 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3098 source = data->data + 17;
3099 if (gtk_cmclist_get_selection_info
3100 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3102 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3105 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3106 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3107 src_item = folder_find_item_from_identifier(source);
3111 src_item->stype == F_NORMAL &&
3112 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3113 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3114 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3115 !folder_has_parent_of_type(src_item, F_TRASH);
3116 if (!item || !src_item || !folder_is_normal) {
3117 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3121 folderview_move_folder(folderview, src_item, item, copy);
3122 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3124 folderview->nodes_to_recollapse = NULL;
3125 } else if (info == TARGET_MAIL_URI_LIST) {
3126 if (gtk_cmclist_get_selection_info
3127 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3130 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3132 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3133 debug_print("no node\n");
3136 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3138 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3139 debug_print("no item\n");
3142 folderview_finish_dnd(data->data, drag_context, time, item);
3146 static void folderview_drag_end_cb(GtkWidget *widget,
3147 GdkDragContext *drag_context,
3148 FolderView *folderview)
3150 drag_state_stop(folderview);
3151 g_slist_free(folderview->nodes_to_recollapse);
3152 folderview->nodes_to_recollapse = NULL;
3155 void folderview_register_popup(FolderViewPopup *fpopup)
3159 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3160 FolderView *folderview = folderviews->data;
3161 GtkActionGroup *factory;
3163 factory = create_action_group(folderview, fpopup);
3164 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3166 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3169 void folderview_unregister_popup(FolderViewPopup *fpopup)
3174 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3175 FolderView *folderview = folderviews->data;
3177 g_hash_table_remove(folderview->popups, fpopup->klass);
3179 g_hash_table_remove(folderview_popups, fpopup->klass);