Fix minor issues on new start-up folder feature
[claws.git] / src / folderview.c
blob79188513459fc35d2c2e5375ac51acd14e89b559
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2023 the Claws Mail team and Hiroyuki Yamamoto
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/>.
19 #include "config.h"
20 #include "defs.h"
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtk.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
30 #include "main.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"
38 #include "menu.h"
39 #include "stock_pixmap.h"
40 #include "procmsg.h"
41 #include "utils.h"
42 #include "gtkutils.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
47 #include "account.h"
48 #include "folder.h"
49 #include "foldersel.h"
50 #include "inc.h"
51 #include "statusbar.h"
52 #include "hooks.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
58 #include "manual.h"
59 #include "timing.h"
60 #include "log.h"
61 #include "gtkcmctree.h"
63 #define COL_FOLDER_WIDTH 150
64 #define COL_NUM_WIDTH 32
66 static GList *folderview_list = NULL;
68 static GtkStyle *bold_style;
70 static GdkPixbuf *inboxxpm;
71 static GdkPixbuf *inboxhrmxpm;
72 static GdkPixbuf *inboxopenxpm;
73 static GdkPixbuf *inboxopenhrmxpm;
74 static GdkPixbuf *outboxxpm;
75 static GdkPixbuf *outboxhrmxpm;
76 static GdkPixbuf *outboxopenxpm;
77 static GdkPixbuf *outboxopenhrmxpm;
78 static GdkPixbuf *folderxpm;
79 static GdkPixbuf *folderhrmxpm;
80 static GdkPixbuf *folderopenxpm;
81 static GdkPixbuf *folderopenhrmxpm;
82 static GdkPixbuf *trashopenxpm;
83 static GdkPixbuf *trashopenhrmxpm;
84 static GdkPixbuf *trashxpm;
85 static GdkPixbuf *trashhrmxpm;
86 static GdkPixbuf *queuexpm;
87 static GdkPixbuf *queuehrmxpm;
88 static GdkPixbuf *queueopenxpm;
89 static GdkPixbuf *queueopenhrmxpm;
90 static GdkPixbuf *draftsxpm;
91 static GdkPixbuf *draftsopenxpm;
92 static GdkPixbuf *foldersubsxpm;
93 static GdkPixbuf *foldersubsopenxpm;
94 static GdkPixbuf *foldernoselectxpm;
95 static GdkPixbuf *foldernoselectopenxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
119 static GdkPixbuf *m_foldersubsxpm;
120 static GdkPixbuf *m_foldernoselectxpm;
122 static GdkPixbuf *newxpm;
123 static GdkPixbuf *unreadxpm;
124 static GdkPixbuf *readxpm;
126 static void folderview_select_node (FolderView *folderview,
127 GtkCMCTreeNode *node);
128 static void folderview_set_folders (FolderView *folderview);
129 static void folderview_sort_folders (FolderView *folderview,
130 GtkCMCTreeNode *root,
131 Folder *folder);
132 static void folderview_append_folder (FolderView *folderview,
133 Folder *folder);
134 static void folderview_update_node (FolderView *folderview,
135 GtkCMCTreeNode *node);
137 static gint folderview_clist_compare (GtkCMCList *clist,
138 gconstpointer ptr1,
139 gconstpointer ptr2);
141 /* callback functions */
142 static gboolean folderview_button_pressed (GtkWidget *ctree,
143 GdkEventButton *event,
144 FolderView *folderview);
145 static gboolean folderview_button_released (GtkWidget *ctree,
146 GdkEventButton *event,
147 FolderView *folderview);
148 static gboolean folderview_key_pressed (GtkWidget *widget,
149 GdkEventKey *event,
150 FolderView *folderview);
151 static void folderview_selected (GtkCMCTree *ctree,
152 GtkCMCTreeNode *row,
153 gint column,
154 FolderView *folderview);
155 static void folderview_tree_expanded (GtkCMCTree *ctree,
156 GtkCMCTreeNode *node,
157 FolderView *folderview);
158 static void folderview_tree_collapsed (GtkCMCTree *ctree,
159 GtkCMCTreeNode *node,
160 FolderView *folderview);
161 static void folderview_popup_close (GtkMenuShell *menu_shell,
162 FolderView *folderview);
163 static void folderview_col_resized (GtkCMCList *clist,
164 gint column,
165 gint width,
166 FolderView *folderview);
168 static void mark_all_read_unread_handler (GtkAction *action,
169 gpointer data,
170 gboolean recursive,
171 gboolean read);
173 static void mark_all_read_cb (GtkAction *action,
174 gpointer data);
175 static void mark_all_unread_cb (GtkAction *action,
176 gpointer data);
177 static void mark_all_read_recursive_cb (GtkAction *action,
178 gpointer data);
179 static void mark_all_unread_recursive_cb (GtkAction *action,
180 gpointer data);
182 static void folderview_empty_trash_cb (GtkAction *action,
183 gpointer data);
185 static void folderview_send_queue_cb (GtkAction *action,
186 gpointer data);
188 static void folderview_search_cb (GtkAction *action,
189 gpointer data);
190 static void folderview_run_processing_cb(GtkAction *action,
191 gpointer data);
192 static void folderview_startup_folder_cb(GtkAction *action,
193 gpointer data);
195 static void folderview_property_cb (GtkAction *action,
196 gpointer data);
198 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
199 GdkDragContext *context,
200 gint x,
201 gint y,
202 guint time,
203 FolderView *folderview);
204 static void folderview_drag_leave_cb (GtkWidget *widget,
205 GdkDragContext *context,
206 guint time,
207 FolderView *folderview);
208 static void folderview_drag_received_cb (GtkWidget *widget,
209 GdkDragContext *drag_context,
210 gint x,
211 gint y,
212 GtkSelectionData *data,
213 guint info,
214 guint time,
215 FolderView *folderview);
216 #ifndef GENERIC_UMPC
217 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
218 FolderView *folderview);
219 #endif
220 static void folderview_drag_data_get (GtkWidget *widget,
221 GdkDragContext *drag_context,
222 GtkSelectionData *selection_data,
223 guint info,
224 guint time,
225 FolderView *folderview);
226 static void folderview_drag_end_cb (GtkWidget *widget,
227 GdkDragContext *drag_context,
228 FolderView *folderview);
230 static void folderview_create_folder_node (FolderView *folderview,
231 FolderItem *item);
232 static gboolean folderview_update_folder (gpointer source,
233 gpointer userdata);
234 static gboolean folderview_update_item_claws (gpointer source,
235 gpointer data);
236 static void folderview_processing_cb(GtkAction *action, gpointer data);
237 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
238 GdkEventButton *event);
239 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
240 gpointer data);
241 static gboolean folderview_header_button_pressed(GtkWidget *widget,
242 GdkEvent *_event,
243 gpointer user_data);
245 GHashTable *folderview_popups;
247 static GtkActionEntry folderview_common_popup_entries[] =
249 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
250 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
251 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
252 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
253 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
254 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
255 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
256 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
257 {"FolderViewPopup/OpenFolder", NULL, N_("Open on start-up"), NULL, NULL, G_CALLBACK(folderview_startup_folder_cb) },
258 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
259 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
260 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
261 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
265 static GtkActionEntry folderview_header_popup_entries[] =
267 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL },
268 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) }
271 GtkTargetEntry folderview_drag_types[] =
273 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
274 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
277 void folderview_initialize(void)
279 FolderViewPopup *fpopup;
281 fpopup = g_new0(FolderViewPopup, 1);
283 fpopup->klass = "common";
284 fpopup->path = "<CommonFolder>";
285 fpopup->entries = folderview_common_popup_entries;
286 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
287 fpopup->set_sensitivity = NULL;
289 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
290 g_hash_table_insert(folderview_popups, "common", fpopup);
293 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
295 FolderViewPopup *fpopup_common;
296 GtkActionGroup *action_group;
298 action_group = cm_menu_create_action_group(
299 fpopup->path,
300 fpopup->entries, fpopup->n_entries,
301 (gpointer)folderview);
303 if (fpopup->toggle_entries)
304 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
305 fpopup->n_toggle_entries,
306 (gpointer)folderview);
307 if (fpopup->radio_entries)
308 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
309 fpopup->n_radio_entries, fpopup->radio_default,
310 G_CALLBACK(fpopup->radio_callback),
311 (gpointer)folderview);
313 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
314 if (fpopup_common != fpopup) {
315 gtk_action_group_add_actions(action_group, fpopup_common->entries,
316 fpopup_common->n_entries,
317 (gpointer)folderview);
318 if (fpopup_common->toggle_entries)
319 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
320 fpopup_common->n_toggle_entries,
321 (gpointer)folderview);
322 if (fpopup_common->radio_entries)
323 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
324 fpopup_common->n_radio_entries, fpopup_common->radio_default,
325 G_CALLBACK(fpopup_common->radio_callback),
326 (gpointer)folderview);
329 return action_group;
332 static void create_action_groups(gpointer key, gpointer value, gpointer data)
334 FolderView *folderview = data;
335 FolderViewPopup *fpopup = value;
336 GtkActionGroup *group;
338 group = create_action_group(folderview, fpopup);
339 g_hash_table_insert(folderview->popups, fpopup->klass, group);
342 static void folderview_column_set_titles(FolderView *folderview)
344 GtkWidget *ctree = folderview->ctree;
345 GtkWidget *label_folder;
346 GtkWidget *label_new;
347 GtkWidget *label_unread;
348 GtkWidget *label_total;
349 GtkWidget *hbox_folder;
350 GtkWidget *hbox_new;
351 GtkWidget *hbox_unread;
352 GtkWidget *hbox_total;
353 gint *col_pos = folderview->col_pos;
355 debug_print("setting titles...\n");
356 gtk_widget_realize(folderview->ctree);
357 gtk_widget_show_all(folderview->scrolledwin);
359 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
360 * instead text (text overflows making them unreadable and ugly) */
361 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
362 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
363 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
365 label_folder = gtk_label_new(_("Folder"));
366 label_new = gtk_image_new_from_pixbuf(newxpm);
367 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
368 label_total = gtk_image_new_from_pixbuf(readxpm);
370 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
372 hbox_folder = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
373 hbox_new = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
374 hbox_unread = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
375 hbox_total = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
377 /* left justified */
378 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
379 gtk_widget_set_halign(label_folder, GTK_ALIGN_START);
380 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
381 gtk_widget_set_halign(label_new, GTK_ALIGN_END);
382 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
383 gtk_widget_set_halign(label_unread, GTK_ALIGN_END);
384 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
385 gtk_widget_set_halign(label_total, GTK_ALIGN_END);
387 gtk_widget_show_all(hbox_folder);
388 gtk_widget_show_all(hbox_new);
389 gtk_widget_show_all(hbox_unread);
390 gtk_widget_show_all(hbox_total);
392 #ifdef GENERIC_UMPC
393 gtk_widget_set_size_request(hbox_new, -1, 20);
394 gtk_widget_set_size_request(hbox_unread, -1, 20);
395 gtk_widget_set_size_request(hbox_total, -1, 20);
396 #endif
398 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
399 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
400 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
401 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
403 #ifdef GENERIC_UMPC
404 GTK_EVENTS_FLUSH();
405 #endif
407 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
408 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
409 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
412 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
414 FolderView *folderview = (FolderView *)data;
415 GdkEventButton event;
416 if (folderview_get_selected_item(folderview) == NULL)
417 return FALSE;
419 event.button = 3;
420 event.time = gtk_get_current_event_time();
422 folderview_set_sens_and_popup_menu(folderview, -1,
423 &event);
425 return TRUE;
429 static GtkWidget *folderview_ctree_create(FolderView *folderview)
431 GtkWidget *ctree;
432 gint *col_pos;
433 FolderColumnState *col_state;
434 FolderColumnType type;
435 gchar *titles[N_FOLDER_COLS];
436 gint i;
437 GtkWidget *scrolledwin = folderview->scrolledwin;
439 debug_print("creating tree...\n");
440 memset(titles, 0, sizeof(titles));
442 col_state = prefs_folder_column_get_config();
443 memset(titles, 0, sizeof(titles));
445 col_pos = folderview->col_pos;
447 for (i = 0; i < N_FOLDER_COLS; i++) {
448 folderview->col_state[i] = col_state[i];
449 type = col_state[i].type;
450 col_pos[type] = i;
453 titles[col_pos[F_COL_FOLDER]] = _("Folder");
454 titles[col_pos[F_COL_NEW]] = _("New");
455 titles[col_pos[F_COL_UNREAD]] = _("Unread");
456 /* TRANSLATORS: This in Number sign in American style */
457 titles[col_pos[F_COL_TOTAL]] = _("#");
459 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
460 titles);
462 gtk_widget_set_name(GTK_WIDGET(ctree), "folderview_sctree");
464 if (prefs_common.show_col_headers == FALSE)
465 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
468 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
469 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
470 GTK_JUSTIFY_RIGHT);
471 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
472 col_pos[F_COL_UNREAD],
473 GTK_JUSTIFY_RIGHT);
474 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
475 col_pos[F_COL_TOTAL],
476 GTK_JUSTIFY_RIGHT);
477 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
478 GTK_CMCTREE_EXPANDER_TRIANGLE);
480 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
481 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
483 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
484 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
486 /* don't let title buttons take key focus */
487 for (i = 0; i < N_FOLDER_COLS; i++) {
488 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
489 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
490 prefs_common.folder_col_size[i]);
491 gtk_cmclist_set_column_visibility
492 (GTK_CMCLIST(ctree), i, col_state[i].visible);
494 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button),
495 "button-press-event",
496 G_CALLBACK(folderview_header_button_pressed),
497 folderview);
500 g_signal_connect(G_OBJECT(ctree), "key_press_event",
501 G_CALLBACK(folderview_key_pressed),
502 folderview);
503 g_signal_connect(G_OBJECT(ctree), "button_press_event",
504 G_CALLBACK(folderview_button_pressed),
505 folderview);
506 g_signal_connect(G_OBJECT(ctree), "popup-menu",
507 G_CALLBACK(folderview_popup_menu), folderview);
508 g_signal_connect(G_OBJECT(ctree), "button_release_event",
509 G_CALLBACK(folderview_button_released),
510 folderview);
511 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
512 G_CALLBACK(folderview_selected), folderview);
513 #ifndef GENERIC_UMPC
514 /* drag-n-dropping folders on maemo is impractical as this
515 * opens the folder almost everytime */
516 g_signal_connect(G_OBJECT(ctree), "start_drag",
517 G_CALLBACK(folderview_start_drag), folderview);
518 #endif
519 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
520 G_CALLBACK(folderview_drag_data_get),
521 folderview);
523 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
524 G_CALLBACK(folderview_tree_expanded),
525 folderview);
526 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
527 G_CALLBACK(folderview_tree_collapsed),
528 folderview);
530 g_signal_connect(G_OBJECT(ctree), "resize_column",
531 G_CALLBACK(folderview_col_resized),
532 folderview);
534 /* drop callback */
535 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
536 folderview_drag_types, 2,
537 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
538 g_signal_connect(G_OBJECT(ctree), "drag_motion",
539 G_CALLBACK(folderview_drag_motion_cb),
540 folderview);
541 g_signal_connect(G_OBJECT(ctree), "drag_leave",
542 G_CALLBACK(folderview_drag_leave_cb),
543 folderview);
544 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
545 G_CALLBACK(folderview_drag_received_cb),
546 folderview);
547 g_signal_connect(G_OBJECT(ctree), "drag_end",
548 G_CALLBACK(folderview_drag_end_cb),
549 folderview);
551 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
553 return ctree;
556 void folderview_set_column_order(FolderView *folderview)
558 GtkWidget *ctree = folderview->ctree;
559 FolderItem *item = folderview_get_selected_item(folderview);
560 FolderItem *sel_item = NULL, *op_item = NULL;
561 GtkWidget *scrolledwin = folderview->scrolledwin;
563 if (folderview->drag_timer_id != 0) {
564 g_source_remove(folderview->drag_timer_id);
565 folderview->drag_timer_id = 0;
567 if (folderview->deferred_refresh_id != 0) {
568 g_source_remove(folderview->deferred_refresh_id);
569 folderview->deferred_refresh_id = 0;
571 if (folderview->scroll_timeout_id != 0) {
572 g_source_remove(folderview->scroll_timeout_id);
573 folderview->scroll_timeout_id = 0;
575 if (folderview->postpone_select_id != 0) {
576 g_source_remove(folderview->postpone_select_id);
577 folderview->postpone_select_id = 0;
580 if (folderview->selected)
581 sel_item = folderview_get_selected_item(folderview);
582 if (folderview->opened)
583 op_item = folderview_get_opened_item(folderview);
585 debug_print("recreating tree...\n");
586 gtk_widget_destroy(folderview->ctree);
589 folderview->ctree = ctree = folderview_ctree_create(folderview);
590 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
591 GTK_CMCLIST(ctree)->hadjustment);
592 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
593 GTK_CMCLIST(ctree)->vadjustment);
594 gtk_widget_show(ctree);
596 if (sel_item)
597 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
598 if (op_item)
599 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
601 folderview_set(folderview);
602 folderview_column_set_titles(folderview);
604 folderview_select(folderview,item);
607 FolderView *folderview_create(MainWindow *mainwin)
609 FolderView *folderview;
610 GtkWidget *scrolledwin;
611 GtkWidget *ctree;
613 debug_print("Creating folder view...\n");
614 folderview = g_new0(FolderView, 1);
616 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
617 gtk_widget_set_name(GTK_WIDGET(scrolledwin), "folderview");
618 gtk_scrolled_window_set_policy
619 (GTK_SCROLLED_WINDOW(scrolledwin),
620 GTK_POLICY_AUTOMATIC,
621 prefs_common.folderview_vscrollbar_policy);
623 folderview->scrolledwin = scrolledwin;
624 ctree = folderview_ctree_create(folderview);
625 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
627 /* create popup factories */
628 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
629 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
631 gtk_action_group_add_actions(mainwin->action_group,
632 folderview_header_popup_entries,
633 G_N_ELEMENTS(folderview_header_popup_entries),
634 (gpointer)folderview);
636 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU)
637 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
639 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
640 gtk_ui_manager_get_widget(mainwin->ui_manager,
641 "/Menus/FolderViewHeaderPopup") ));
643 folderview->ctree = ctree;
645 folderview->folder_update_callback_id =
646 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
647 folderview->folder_item_update_callback_id =
648 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
650 gtk_widget_show_all(scrolledwin);
652 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
653 folderview_list = g_list_append(folderview_list, folderview);
655 folderview->drag_timer_id = 0;
656 folderview->deferred_refresh_id = 0;
657 folderview->scroll_timeout_id = 0;
658 folderview->postpone_select_id = 0;
660 return folderview;
663 static void folderview_set_fonts(FolderView *folderview)
665 PangoFontDescription *font_desc;
666 GtkWidget *ctree = folderview->ctree;
668 font_desc = pango_font_description_from_string(NORMAL_FONT);
669 if (font_desc) {
670 gtk_widget_override_font(ctree, font_desc);
671 pango_font_description_free(font_desc);
674 if (!bold_style) {
675 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
677 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
678 font_desc = pango_font_description_from_string(NORMAL_FONT);
679 if (font_desc) {
680 pango_font_description_free(bold_style->font_desc);
681 bold_style->font_desc = font_desc;
683 pango_font_description_set_weight
684 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
685 } else {
686 font_desc = pango_font_description_from_string(BOLD_FONT);
687 if (font_desc) {
688 pango_font_description_free(bold_style->font_desc);
689 bold_style->font_desc = font_desc;
695 void folderview_init(FolderView *folderview)
697 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
698 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
699 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
700 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
701 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
702 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
703 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
704 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
705 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
706 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
707 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
708 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
709 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
710 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
711 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
712 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
713 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
714 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
715 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
716 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
717 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
718 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
719 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
720 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
721 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
722 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
724 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
725 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
726 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
727 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
728 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
729 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
730 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
731 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
732 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
733 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
734 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
735 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
736 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
737 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
738 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
739 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
740 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
741 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
742 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
743 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
744 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
745 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
746 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
747 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
749 folderview_set_fonts(folderview);
752 static gboolean folderview_defer_set(gpointer data)
754 FolderView *folderview = (FolderView *)data;
755 MainWindow *mainwin = folderview->mainwin;
757 if (!mainwin)
758 return FALSE;
759 if (mainwin->lock_count)
760 return TRUE;
762 debug_print("doing deferred folderview_set now\n");
763 folderview_set(folderview);
765 folderview->deferred_refresh_id = 0;
766 return FALSE;
769 void folderview_set(FolderView *folderview)
771 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
772 MainWindow *mainwin = folderview->mainwin;
773 FolderItem *sel_item = NULL, *op_item = NULL;
775 if (!mainwin)
776 return;
778 if (mainwin->lock_count) {
779 if (folderview->deferred_refresh_id == 0)
780 folderview->deferred_refresh_id =
781 g_timeout_add(500, folderview_defer_set, folderview);
782 debug_print("deferred folderview_set\n");
783 return;
786 inc_lock();
787 debug_print("Setting folder info...\n");
788 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
790 main_window_cursor_wait(mainwin);
792 if (folderview->selected)
793 sel_item = folderview_get_selected_item(folderview);
794 if (folderview->opened)
795 op_item = folderview_get_opened_item(folderview);
797 folderview->selected = NULL;
798 folderview->opened = NULL;
800 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
801 gtk_cmclist_clear(GTK_CMCLIST(ctree));
803 folderview_set_folders(folderview);
805 if (sel_item)
806 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
807 if (op_item)
808 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
810 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
811 main_window_cursor_normal(mainwin);
812 STATUSBAR_POP(mainwin);
813 inc_unlock();
816 void folderview_set_all(void)
818 GList *list;
820 for (list = folderview_list; list != NULL; list = list->next)
821 folderview_set((FolderView *)list->data);
824 void folderview_select(FolderView *folderview, FolderItem *item)
826 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
827 GtkCMCTreeNode *node;
828 GtkCMCTreeNode *old_selected = folderview->selected;
830 if (!item) return;
832 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
833 if (node) folderview_select_node(folderview, node);
835 if (old_selected != node)
836 folder_update_op_count();
839 static void mark_all_read_cb(GtkAction *action, gpointer data)
841 mark_all_read_unread_handler(action, data, FALSE, TRUE);
844 static void mark_all_unread_cb(GtkAction *action, gpointer data)
846 mark_all_read_unread_handler(action, data, FALSE, FALSE);
849 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
851 mark_all_read_unread_handler(action, data, TRUE, TRUE);
854 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
856 mark_all_read_unread_handler(action, data, TRUE, FALSE);
859 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
860 gboolean recursive, gboolean read)
862 FolderView *folderview = (FolderView *)data;
863 FolderItem *item;
864 AlertValue val;
865 gchar *message;
866 gchar *title;
868 item = folderview_get_selected_item(folderview);
869 if (item == NULL)
870 return;
872 if (read) {
873 title = _("Mark all as read");
874 message = recursive? _("Do you really want to mark all mails in this "
875 "folder and its subfolders as read?") :
876 _("Do you really want to mark all mails in this "
877 "folder as read?");
878 } else {
879 title = _("Mark all as unread");
880 message = recursive? _("Do you really want to mark all mails in this "
881 "folder and its subfolders as unread?") :
882 _("Do you really want to mark all mails in this "
883 "folder as unread?");
885 if (prefs_common.ask_mark_all_read) {
886 val = alertpanel_full(title, message,
887 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
888 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
890 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
891 return;
892 else if (val & G_ALERTDISABLE)
893 prefs_common.ask_mark_all_read = FALSE;
896 folder_item_update_freeze();
897 if (folderview->summaryview->folder_item != item && !recursive)
898 summary_lock(folderview->summaryview);
899 else
900 summary_freeze(folderview->summaryview);
902 if (read) {
903 if (recursive)
904 folderutils_mark_all_read_recursive(item, TRUE);
905 else {
906 if (prefs_common.run_processingrules_before_mark_all)
907 folderview_run_processing(item);
908 folderutils_mark_all_read(item, TRUE);
910 } else {
911 if (recursive)
912 folderutils_mark_all_read_recursive(item, FALSE);
913 else {
914 folderutils_mark_all_read(item, FALSE);
915 if (prefs_common.run_processingrules_before_mark_all)
916 folderview_run_processing(item);
919 if (folderview->summaryview->folder_item != item && !recursive)
920 summary_unlock(folderview->summaryview);
921 else
922 summary_thaw(folderview->summaryview);
923 folder_item_update_thaw();
926 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
928 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
930 cm_return_if_fail(node != NULL);
932 if (folderview->open_folder) {
933 return;
936 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
937 gtkut_ctree_expand_parent_all(ctree, node);
939 folderview->open_folder = TRUE;
940 gtkut_ctree_set_focus_row(ctree, node);
941 gtk_cmctree_select(ctree, node);
942 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
943 if ((folderview->summaryview->folder_item &&
944 folderview->summaryview->folder_item->total_msgs > 0) ||
945 prefs_common.layout_mode == SMALL_LAYOUT)
946 summary_select_node(folderview->summaryview,
947 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
948 else
949 gtk_widget_grab_focus(folderview->ctree);
952 void folderview_unselect(FolderView *folderview)
954 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
955 gtk_cmctree_collapse
956 (GTK_CMCTREE(folderview->ctree), folderview->opened);
958 folderview->selected = folderview->opened = NULL;
961 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
962 GtkCMCTreeNode *node,
963 MsgPermFlags flag)
965 FolderItem *item;
967 if (node)
968 node = gtkut_ctree_node_next(ctree, node);
969 else
970 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
972 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
973 item = gtk_cmctree_node_get_row_data(ctree, node);
974 if (!item)
975 continue;
976 if (item->stype == F_TRASH || item->stype == F_DRAFT)
977 continue;
978 switch (flag) {
979 case MSG_UNREAD:
980 if((item->unread_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new))
981 return node;
982 break;
983 case MSG_NEW:
984 if((item->new_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new))
985 return node;
986 break;
987 case MSG_MARKED:
988 if(item->marked_msgs > 0)
989 return node;
990 break;
991 default:
992 if(item->total_msgs > 0)
993 return node;
994 break;
998 return NULL;
1001 void folderview_select_next_with_flag(FolderView *folderview,
1002 MsgPermFlags flag)
1004 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1005 GtkCMCTreeNode *node = NULL;
1006 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1008 switch (flag) {
1009 case MSG_UNREAD:
1010 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1011 break;
1012 case MSG_NEW:
1013 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1014 break;
1015 case MSG_MARKED:
1016 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1017 break;
1018 default:
1019 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1020 break;
1023 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1024 if (node != NULL) {
1025 folderview_select_node(folderview, node);
1026 goto out;
1029 if (!folderview->opened ||
1030 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1031 goto out;
1034 /* search again from the first node */
1035 node = folderview_find_next_with_flag(ctree, NULL, flag);
1036 if (node != NULL)
1037 folderview_select_node(folderview, node);
1039 out:
1040 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1043 FolderItem *folderview_get_selected_item(FolderView *folderview)
1045 g_return_val_if_fail(folderview != NULL, NULL);
1046 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1048 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1050 if (!folderview->selected) return NULL;
1051 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1054 FolderItem *folderview_get_opened_item(FolderView *folderview)
1056 g_return_val_if_fail(folderview != NULL, NULL);
1057 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1059 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1061 if (!folderview->opened) return NULL;
1062 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1065 static void folderview_set_folders(FolderView *folderview)
1067 GList *list;
1068 list = folder_get_list();
1070 for (; list != NULL; list = list->next) {
1071 folderview_append_folder(folderview, FOLDER(list->data));
1075 static gchar *get_scan_str(FolderItem *item)
1077 if (item->path)
1078 return g_strdup_printf(_("Scanning folder %s/%s..."),
1079 item->folder->name, item->path);
1080 else
1081 return g_strdup_printf(_("Scanning folder %s..."),
1082 item->folder->name);
1084 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1085 gpointer data)
1087 GList *list;
1088 for (list = folderview_list; list != NULL; list = list->next) {
1089 FolderView *folderview = (FolderView *)list->data;
1090 MainWindow *mainwin = folderview->mainwin;
1091 gchar *str = get_scan_str(item);
1093 STATUSBAR_PUSH(mainwin, str);
1094 STATUSBAR_POP(mainwin);
1095 g_free(str);
1099 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1101 GtkWidget *window;
1102 MainWindow *mainwin = mainwindow_get_mainwindow();
1103 FolderView *folderview = NULL;
1104 GtkAdjustment *pos = NULL;
1105 gint height = 0;
1107 cm_return_if_fail(folder != NULL);
1109 if (!folder->klass->scan_tree) return;
1111 if (rebuild &&
1112 alertpanel_full(_("Rebuild folder tree"),
1113 _("Rebuilding the folder tree will remove "
1114 "local caches. Do you want to continue?"),
1115 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
1116 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING)
1117 != G_ALERTALTERNATE) {
1118 return;
1121 inc_lock();
1122 if (rebuild)
1123 window = label_window_create(_("Rebuilding folder tree..."));
1124 else
1125 window = label_window_create(_("Scanning folder tree..."));
1127 if (mainwin)
1128 folderview = mainwin->folderview;
1130 if (folderview) {
1131 pos = gtk_scrolled_window_get_vadjustment(
1132 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1133 height = gtk_adjustment_get_value(pos);
1136 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1137 folder_scan_tree(folder, rebuild);
1138 folder_set_ui_func(folder, NULL, NULL);
1140 folderview_set_all();
1142 if (folderview) {
1143 pos = gtk_scrolled_window_get_vadjustment(
1144 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1145 gtk_adjustment_set_value(pos, height);
1147 label_window_destroy(window);
1148 inc_unlock();
1151 /** folderview_check_new()
1152 * Scan and update the folder and return the
1153 * count the number of new messages since last check.
1154 * \param folder the folder to check for new messages
1155 * \return the number of new messages since last check
1157 gint folderview_check_new(Folder *folder)
1159 GList *list;
1160 FolderItem *item;
1161 FolderView *folderview;
1162 GtkCMCTree *ctree;
1163 GtkCMCTreeNode *node;
1164 gint new_msgs = 0;
1165 gint former_new_msgs = 0;
1166 gint former_new = 0, former_unread = 0, former_total;
1168 for (list = folderview_list; list != NULL; list = list->next) {
1169 folderview = (FolderView *)list->data;
1170 ctree = GTK_CMCTREE(folderview->ctree);
1171 folderview->scanning_folder = folder;
1172 inc_lock();
1173 main_window_lock(folderview->mainwin);
1175 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1176 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1177 gchar *str = NULL;
1178 item = gtk_cmctree_node_get_row_data(ctree, node);
1179 if (!item || !item->path || !item->folder) continue;
1180 if (item->no_select) continue;
1181 if (folder && folder != item->folder) continue;
1182 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1183 if (!item->prefs->newmailcheck) continue;
1184 if (item->processing_pending == TRUE) {
1185 debug_print("skipping %s, processing pending\n",
1186 item->path ? item->path : item->name);
1187 continue;
1189 if (item->scanning != ITEM_NOT_SCANNING) {
1190 debug_print("skipping %s, scanning\n",
1191 item->path ? item->path : item->name);
1192 continue;
1195 str = get_scan_str(item);
1197 STATUSBAR_PUSH(folderview->mainwin, str);
1198 GTK_EVENTS_FLUSH();
1199 g_free(str);
1201 folderview_scan_tree_func(item->folder, item, NULL);
1202 former_new = item->new_msgs;
1203 former_unread = item->unread_msgs;
1204 former_total = item->total_msgs;
1206 if (item->folder->klass->scan_required &&
1207 (item->folder->klass->scan_required(item->folder, item) ||
1208 item->folder->inbox == item ||
1209 item->opened == TRUE ||
1210 item->processing_pending == TRUE)) {
1211 if (folder_item_scan(item) < 0) {
1212 if (folder) {
1213 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1214 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1215 item->path ? item->path:item->name);
1216 STATUSBAR_POP(folderview->mainwin);
1217 continue;
1218 } else if (!FOLDER_IS_LOCAL(folder)) {
1219 STATUSBAR_POP(folderview->mainwin);
1220 break;
1224 } else if (!item->folder->klass->scan_required) {
1225 if (folder_item_scan(item) < 0) {
1226 if (folder && !FOLDER_IS_LOCAL(folder)) {
1227 STATUSBAR_POP(folderview->mainwin);
1228 break;
1232 if (former_new != item->new_msgs ||
1233 former_unread != item->unread_msgs ||
1234 former_total != item->total_msgs)
1235 folderview_update_node(folderview, node);
1237 new_msgs += item->new_msgs;
1238 former_new_msgs += former_new;
1239 STATUSBAR_POP(folderview->mainwin);
1241 folderview->scanning_folder = NULL;
1242 main_window_unlock(folderview->mainwin);
1243 inc_unlock();
1246 folder_write_list();
1247 /* Number of new messages since last check is the just the difference
1248 * between former_new_msgs and new_msgs. If new_msgs is less than
1249 * former_new_msgs, that would mean another session accessed the folder
1250 * and the result is not well defined.
1252 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1253 return new_msgs;
1256 void folderview_check_new_all(void)
1258 GList *list;
1259 GtkWidget *window;
1260 FolderView *folderview;
1262 folderview = (FolderView *)folderview_list->data;
1264 inc_lock();
1265 main_window_lock(folderview->mainwin);
1266 window = label_window_create
1267 (_("Checking for new messages in all folders..."));
1269 list = folder_get_list();
1270 for (; list != NULL; list = list->next) {
1271 Folder *folder = list->data;
1273 folderview_check_new(folder);
1276 folder_write_list();
1277 folderview_set_all();
1279 label_window_destroy(window);
1280 main_window_unlock(folderview->mainwin);
1281 inc_unlock();
1284 static gboolean folderview_have_children_sub(FolderView *folderview,
1285 FolderItem *item,
1286 gboolean in_sub)
1288 GNode *node = NULL;
1290 if (!item || !item->folder || !item->folder->node)
1291 return FALSE;
1293 node = item->folder->node;
1295 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1296 node = node->children;
1298 if (in_sub && item->total_msgs > 0) {
1299 return TRUE;
1302 while (node != NULL) {
1303 if (node && node->data) {
1304 FolderItem *next_item = (FolderItem*) node->data;
1305 node = node->next;
1306 if (folderview_have_children_sub(folderview,
1307 next_item, TRUE))
1308 return TRUE;
1312 return FALSE;
1315 static gboolean folderview_have_children(FolderView *folderview,
1316 FolderItem *item)
1318 return folderview_have_children_sub(folderview, item, FALSE);
1321 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1322 FolderItem *item,
1323 gboolean in_sub)
1325 GNode *node = NULL;
1327 if (!item || !item->folder || !item->folder->node)
1328 return FALSE;
1330 node = item->folder->node;
1332 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1333 node = node->children;
1335 if (in_sub &&
1336 (item->new_msgs > 0 ||
1337 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1338 return TRUE;
1341 while (node != NULL) {
1342 if (node && node->data) {
1343 FolderItem *next_item = (FolderItem*) node->data;
1344 node = node->next;
1345 if (folderview_have_new_children_sub(folderview,
1346 next_item, TRUE))
1347 return TRUE;
1351 return FALSE;
1354 static gboolean folderview_have_new_children(FolderView *folderview,
1355 FolderItem *item)
1357 return folderview_have_new_children_sub(folderview, item, FALSE);
1360 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1361 FolderItem *item,
1362 gboolean in_sub)
1364 GNode *node = NULL;
1366 if (!item || !item->folder || !item->folder->node)
1367 return FALSE;
1369 node = item->folder->node;
1371 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1372 node = node->children;
1374 if (in_sub &&
1375 (item->unread_msgs > 0 ||
1376 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1377 return TRUE;
1380 while (node != NULL) {
1381 if (node && node->data) {
1382 FolderItem *next_item = (FolderItem*) node->data;
1383 node = node->next;
1384 if (folderview_have_unread_children_sub(folderview,
1385 next_item,
1386 TRUE))
1387 return TRUE;
1391 return FALSE;
1394 static gboolean folderview_have_unread_children(FolderView *folderview,
1395 FolderItem *item)
1397 return folderview_have_unread_children_sub(folderview, item, FALSE);
1400 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1401 FolderItem *item,
1402 gboolean in_sub)
1404 GNode *node = NULL;
1406 if (!item || !item->folder || !item->folder->node) {
1407 return FALSE;
1410 node = item->folder->node;
1412 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1413 node = node->children;
1415 if (in_sub &&
1416 (((item->total_msgs > 0) &&
1417 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1418 return TRUE;
1421 while (node != NULL) {
1422 if (node && node->data) {
1423 FolderItem *next_item = (FolderItem*) node->data;
1424 node = node->next;
1425 if (folderview_have_read_children_sub(folderview,
1426 next_item,
1427 TRUE)) {
1428 return TRUE;
1433 return FALSE;
1436 static gboolean folderview_have_read_children(FolderView *folderview,
1437 FolderItem *item)
1439 return folderview_have_read_children_sub(folderview, item, FALSE);
1442 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1443 FolderItem *item,
1444 gboolean in_sub)
1446 GNode *node = NULL;
1448 if (!item || !item->folder || !item->folder->node)
1449 return FALSE;
1451 node = item->folder->node;
1453 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1454 node = node->children;
1456 if (in_sub && item->search_match){
1457 return TRUE;
1460 while (node != NULL) {
1461 if (node && node->data) {
1462 FolderItem *next_item = (FolderItem*) node->data;
1463 node = node->next;
1464 if (folderview_have_matching_children_sub(folderview,
1465 next_item,
1466 TRUE))
1467 return TRUE;
1471 return FALSE;
1474 static gboolean folderview_have_matching_children(FolderView *folderview,
1475 FolderItem *item)
1477 return folderview_have_matching_children_sub(folderview, item, FALSE);
1480 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1481 FolderItem *item,
1482 gboolean in_sub)
1484 GNode *node = NULL;
1486 if (!item || !item->folder || !item->folder->node)
1487 return FALSE;
1489 node = item->folder->node;
1491 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1492 node = node->children;
1494 if (item->marked_msgs != 0) {
1495 return TRUE;
1498 while (node != NULL) {
1499 if (node && node->data) {
1500 FolderItem *next_item = (FolderItem*) node->data;
1501 node = node->next;
1502 if (folderview_have_marked_children_sub(folderview,
1503 next_item, TRUE))
1504 return TRUE;
1508 return FALSE;
1511 static gboolean folderview_have_marked_children(FolderView *folderview,
1512 FolderItem *item)
1514 return folderview_have_marked_children_sub(folderview, item, FALSE);
1517 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1519 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1520 GtkStyle *style = NULL, *prev_style;
1521 FolderItem *item;
1522 GdkRGBA black = { 0, 0, 0, 1 };
1523 GdkPixbuf *xpm, *openxpm;
1524 static GdkPixbuf *searchicon;
1525 gboolean mark = FALSE;
1526 gchar *name;
1527 gchar *str;
1528 gboolean add_unread_mark;
1529 gboolean add_sub_match_mark;
1530 gboolean use_bold, use_color;
1531 gint *col_pos = folderview->col_pos;
1532 SpecialFolderItemType stype;
1534 item = gtk_cmctree_node_get_row_data(ctree, node);
1535 cm_return_if_fail(item != NULL);
1537 if (!GTK_CMCTREE_ROW(node)->expanded)
1538 mark = folderview_have_marked_children(folderview, item);
1539 else
1540 mark = (item->marked_msgs != 0);
1542 stype = item->stype;
1543 if (stype == F_NORMAL) {
1544 if (folder_has_parent_of_type(item, F_TRASH))
1545 stype = F_TRASH;
1546 else if (folder_has_parent_of_type(item, F_DRAFT))
1547 stype = F_DRAFT;
1548 else if (folder_has_parent_of_type(item, F_OUTBOX))
1549 stype = F_OUTBOX;
1550 else if (folder_has_parent_of_type(item, F_QUEUE))
1551 stype = F_QUEUE;
1553 switch (stype) {
1554 case F_INBOX:
1555 if (item->hide_read_msgs || item->hide_read_threads) {
1556 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1557 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1558 } else {
1559 xpm = mark?m_inboxxpm:inboxxpm;
1560 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1562 break;
1563 case F_OUTBOX:
1564 if (item->hide_read_msgs || item->hide_read_threads) {
1565 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1566 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1567 } else {
1568 xpm = mark?m_outboxxpm:outboxxpm;
1569 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1571 break;
1572 case F_QUEUE:
1573 if (item->hide_read_msgs || item->hide_read_threads) {
1574 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1575 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1576 } else {
1577 xpm = mark?m_queuexpm:queuexpm;
1578 openxpm = mark?m_queueopenxpm:queueopenxpm;
1580 break;
1581 case F_TRASH:
1582 if (item->hide_read_msgs || item->hide_read_threads) {
1583 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1584 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1585 } else {
1586 xpm = mark?m_trashxpm:trashxpm;
1587 openxpm = mark?m_trashopenxpm:trashopenxpm;
1589 break;
1590 case F_DRAFT:
1591 xpm = mark?m_draftsxpm:draftsxpm;
1592 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1593 break;
1594 default:
1595 if (!item->path &&
1596 FOLDER_TYPE(item->folder) == F_IMAP &&
1597 item->folder->account->imap_subsonly) {
1598 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1599 openxpm = foldersubsopenxpm;
1600 } else if (item->no_select) {
1601 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1602 openxpm = foldernoselectopenxpm;
1603 } else if (item->hide_read_msgs || item->hide_read_threads) {
1604 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1605 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1606 } else {
1607 xpm = mark?m_folderxpm:folderxpm;
1608 openxpm = mark?m_folderopenxpm:folderopenxpm;
1612 name = folder_item_get_name(item);
1614 if (!GTK_CMCTREE_ROW(node)->expanded) {
1615 add_unread_mark = folderview_have_unread_children(
1616 folderview, item);
1617 add_sub_match_mark = folderview_have_matching_children(
1618 folderview, item);
1619 } else {
1620 add_unread_mark = FALSE;
1621 add_sub_match_mark = FALSE;
1624 if (item->search_match) {
1625 if (!searchicon) {
1626 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1627 &searchicon);
1629 xpm = openxpm = searchicon;
1632 str = NULL;
1633 if (prefs_common.display_folder_unread) {
1634 if (folder_has_parent_of_type(item, F_QUEUE)) {
1635 /* only total_msgs matters here */
1636 if (item->total_msgs > 0) {
1637 /* show total number (should be equal to the unread number)
1638 and signs if any */
1639 str = g_strdup_printf("%s (%d%s%s)",
1640 name, item->total_msgs,
1641 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1642 (item->unreadmarked_msgs > 0) ? "!" : "");
1644 } else {
1645 if (prefs_common.display_folder_unread == 1) {
1646 if (item->unread_msgs > 0) {
1647 /* show unread number and signs */
1648 str = g_strdup_printf("%s (%d%s%s)",
1649 name, item->unread_msgs,
1650 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1651 (item->unreadmarked_msgs > 0) ? "!" : "");
1653 } else {
1654 if (item->total_msgs > 0) {
1655 /* show unread number, total number and signs if any */
1656 str = g_strdup_printf("%s (%d/%d%s%s)",
1657 name, item->unread_msgs, item->total_msgs,
1658 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1659 (item->unreadmarked_msgs > 0) ? "!" : "");
1663 if ((str == NULL) &&
1664 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1665 /* no unread/total numbers, but at least one sign */
1666 str = g_strdup_printf("%s (%s%s)",
1667 name,
1668 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1669 (item->unreadmarked_msgs > 0) ? "!" : "");
1672 if (str == NULL) {
1673 /* last fallback, folder name only or with +! sign */
1674 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1675 str = g_strdup_printf("%s%s",
1676 name, " (+!)");
1677 } else if (item->unreadmarked_msgs > 0) {
1678 str = g_strdup_printf("%s%s",
1679 name, " (!)");
1680 } else if (add_sub_match_mark) {
1681 str = g_strdup_printf("%s%s",
1682 name, " (+)");
1683 } else {
1684 str = g_strdup_printf("%s", name);
1687 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1688 xpm, openxpm,
1689 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1690 g_free(str);
1691 g_free(name);
1693 if (!folder_item_parent(item)) {
1694 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1695 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1696 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1697 } else {
1698 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1699 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1700 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1703 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1704 folder_has_parent_of_type(item, F_TRASH)) {
1705 use_bold = use_color = FALSE;
1706 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1707 GSList *list = folder_item_get_msg_list(item);
1708 GSList *cur;
1709 use_bold = use_color = FALSE;
1710 for (cur = list; cur; cur = cur->next) {
1711 MsgInfo *msginfo = (MsgInfo *)cur->data;
1712 if (!MSG_IS_DELETED(msginfo->flags)) {
1713 /* highlight queue folder if there are any messages */
1714 use_bold = use_color = TRUE;
1715 break;
1718 if (!GTK_CMCTREE_ROW(node)->expanded &&
1719 use_bold == FALSE &&
1720 folderview_have_children(folderview, item))
1721 use_bold = use_color = TRUE;
1722 procmsg_msg_list_free(list);
1723 } else {
1724 /* if unread messages exist or target folder is set, print with bold font */
1725 use_bold = (item->unread_msgs > 0 || item->new_msgs > 0 || item->op_count > 0)
1726 || add_unread_mark;
1727 /* if new messages exist, print with colored letter */
1728 use_color =
1729 (item->new_msgs > 0) ||
1730 (add_unread_mark &&
1731 folderview_have_new_children(folderview, item));
1734 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1736 if (use_bold) {
1737 style = bold_style;
1738 if (item->op_count > 0)
1739 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1740 else if (use_color)
1741 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1742 else if (!gdk_rgba_equal(&item->prefs->color, &black))
1743 gtk_cmctree_node_set_foreground(ctree, node, &item->prefs->color);
1744 } else if (use_color)
1745 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1746 else if (item->op_count > 0)
1747 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1748 else if (!gdk_rgba_equal(&item->prefs->color, &black))
1749 gtk_cmctree_node_set_foreground(ctree, node, &item->prefs->color);
1751 gtk_cmctree_node_set_row_style(ctree, node, style);
1753 prev_style = gtk_cmctree_node_get_row_style(ctree, node);
1754 if (prev_style) {
1755 GtkStyle *ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
1757 style = gtk_style_copy(prev_style);
1758 style->text[GTK_STATE_NORMAL] = ctree_style->text[GTK_STATE_NORMAL];
1759 style->text[GTK_STATE_SELECTED] = ctree_style->text[GTK_STATE_SELECTED];
1760 gtk_cmctree_node_set_row_style(ctree, node, style);
1761 g_object_unref(style);
1764 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1765 folderview_update_node(folderview, node);
1768 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1770 GList *list;
1771 FolderView *folderview;
1772 GtkCMCTree *ctree;
1773 GtkCMCTreeNode *node;
1775 cm_return_if_fail(item != NULL);
1777 for (list = folderview_list; list != NULL; list = list->next) {
1778 folderview = (FolderView *)list->data;
1779 ctree = GTK_CMCTREE(folderview->ctree);
1781 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1782 if (node && item->search_match != matches) {
1783 item->search_match = matches;
1784 folderview_update_node(folderview, node);
1789 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1791 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1792 FolderView *folderview = (FolderView *)data;
1793 GtkCMCTree *ctree;
1794 GtkCMCTreeNode *node;
1795 cm_return_val_if_fail(update_info != NULL, TRUE);
1796 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1797 cm_return_val_if_fail(folderview != NULL, FALSE);
1799 ctree = GTK_CMCTREE(folderview->ctree);
1801 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1803 if (node) {
1804 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1805 folderview_update_node(folderview, node);
1807 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1808 update_info->item == folderview->summaryview->folder_item &&
1809 update_info->item != NULL)
1810 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1811 summary_show(folderview->summaryview, update_info->item, FALSE);
1814 return FALSE;
1817 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1818 GNode *gnode, GtkCMCTreeNode *cnode,
1819 gpointer data)
1821 FolderView *folderview = (FolderView *)data;
1822 FolderItem *item = FOLDER_ITEM(gnode->data);
1824 cm_return_val_if_fail(item != NULL, FALSE);
1826 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1827 folderview_update_node(folderview, cnode);
1829 return TRUE;
1832 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1833 gpointer data)
1835 FolderView *folderview = (FolderView *)data;
1836 FolderItem *item;
1838 if (GTK_CMCTREE_ROW(node)->children) {
1839 item = gtk_cmctree_node_get_row_data(ctree, node);
1840 cm_return_if_fail(item != NULL);
1842 if (!item->collapsed)
1843 gtk_cmctree_expand(ctree, node);
1844 else
1845 folderview_update_node(folderview, node);
1849 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1850 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1852 if (item) {
1853 GtkCMCTreeNode *node, *parent, *sibling;
1855 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1856 if (!node)
1857 g_warning("%s not found", item->path);
1858 else {
1859 parent = GTK_CMCTREE_ROW(node)->parent;
1860 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1861 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1862 else
1863 sibling = GTK_CMCTREE_ROW(parent)->children;
1864 while (sibling) {
1865 FolderItem *tmp;
1867 tmp = gtk_cmctree_node_get_row_data
1868 (ctree, sibling);
1869 if (tmp && tmp->stype != F_NORMAL)
1870 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1871 else
1872 break;
1874 if (node != sibling)
1875 gtk_cmctree_move(ctree, node, parent, sibling);
1878 *prev = node;
1882 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1883 Folder *folder)
1885 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1886 GtkCMCTreeNode *prev = NULL;
1888 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1889 gtk_sctree_sort_recursive(ctree, root);
1890 if (root && GTK_CMCTREE_ROW(root)->parent) {
1891 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1892 return;
1894 set_special_folder(ctree, folder->inbox, root, &prev);
1895 set_special_folder(ctree, folder->outbox, root, &prev);
1896 set_special_folder(ctree, folder->draft, root, &prev);
1897 set_special_folder(ctree, folder->queue, root, &prev);
1898 set_special_folder(ctree, folder->trash, root, &prev);
1899 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1902 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1904 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1905 GtkCMCTreeNode *root;
1907 cm_return_if_fail(folder != NULL);
1909 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1910 folderview_gnode_func, folderview);
1911 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1912 folderview);
1913 folderview_sort_folders(folderview, root, folder);
1916 /* callback functions */
1917 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1918 GdkEventButton *event)
1920 FolderItem *item;
1921 Folder *folder;
1922 FolderViewPopup *fpopup;
1923 GtkActionGroup *action_group;
1924 GtkWidget *popup;
1925 FolderItem *special_trash = NULL, *special_queue = NULL;
1926 PrefsAccount *ac;
1927 GtkUIManager *ui_manager = gtk_ui_manager_new();
1929 if (folderview->ui_manager)
1930 g_object_unref(folderview->ui_manager);
1932 folderview->ui_manager = ui_manager;
1933 item = folderview_get_selected_item(folderview);
1935 cm_return_if_fail(item != NULL);
1936 cm_return_if_fail(item->folder != NULL);
1937 folder = item->folder;
1939 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1941 if (fpopup != NULL)
1942 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1943 else {
1944 fpopup = g_hash_table_lookup(folderview_popups, "common");
1945 action_group = g_hash_table_lookup(folderview->popups, "common");
1948 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1949 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1950 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1952 if (fpopup->add_menuitems)
1953 fpopup->add_menuitems(ui_manager, item);
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1960 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1961 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1962 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "OpenFolder", "FolderViewPopup/OpenFolder", GTK_UI_MANAGER_MENUITEM)
1963 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1964 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1966 if (fpopup->set_sensitivity != NULL)
1967 fpopup->set_sensitivity(ui_manager, item);
1969 if (NULL != (ac = account_find_from_item(item))) {
1970 special_trash = account_get_special_folder(ac, F_TRASH);
1971 special_queue = account_get_special_folder(ac, F_QUEUE);
1974 if ((item == folder->trash || item == special_trash
1975 || folder_has_parent_of_type(item, F_TRASH))) {
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1980 if ((item == folder->queue || item == special_queue
1981 || folder_has_parent_of_type(item, F_QUEUE))) {
1982 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1983 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1986 #define SET_SENS(name, sens) \
1987 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1989 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1990 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1991 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1992 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1993 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1994 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1995 folderview->selected == folderview->opened);
1996 SET_SENS("FolderViewPopup/Properties", TRUE);
1998 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1999 item->total_msgs >= 1 && !item->processing_pending);
2000 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
2001 !item->no_select && !item->processing_pending);
2003 SET_SENS("FolderViewPopup/OpenFolder", item->node->parent != NULL
2004 && (!prefs_common.goto_folder_on_startup
2005 || strcmp(folder_item_get_identifier(item), prefs_common.startup_folder)));
2007 if (item == folder->trash || item == special_trash
2008 || folder_has_parent_of_type(item, F_TRASH)) {
2009 GSList *msglist = folder_item_get_msg_list(item);
2010 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2011 procmsg_msg_list_free(msglist);
2013 if (item == folder->queue || item == special_queue
2014 || folder_has_parent_of_type(item, F_QUEUE)) {
2015 GSList *msglist = folder_item_get_msg_list(item);
2016 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2017 procmsg_msg_list_free(msglist);
2019 #undef SET_SENS
2021 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2022 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2023 g_signal_connect(G_OBJECT(popup), "selection_done",
2024 G_CALLBACK(folderview_popup_close),
2025 folderview);
2026 gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL);
2029 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2030 FolderView *folderview)
2032 GtkCMCList *clist = GTK_CMCLIST(ctree);
2033 gint prev_row = -1, row = -1, column = -1;
2035 if (!event) return FALSE;
2036 if (event->window != clist->clist_window) return FALSE;
2038 if (event->button == 1 || event->button == 2) {
2039 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2040 folderview->open_folder = TRUE;
2042 if (event->type == GDK_2BUTTON_PRESS) {
2043 if (clist->selection) {
2044 GtkCMCTreeNode *node;
2046 node = GTK_CMCTREE_NODE(clist->selection->data);
2047 if (node) {
2048 gtk_cmctree_toggle_expansion(
2049 GTK_CMCTREE(ctree),
2050 node);
2051 folderview->open_folder = FALSE;
2055 return FALSE;
2058 if (event->button == 2 || event->button == 3) {
2059 /* right clicked */
2060 if (clist->selection) {
2061 GtkCMCTreeNode *node;
2063 node = GTK_CMCTREE_NODE(clist->selection->data);
2064 if (node)
2065 prev_row = gtkut_ctree_get_nth_from_node
2066 (GTK_CMCTREE(ctree), node);
2069 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2070 &row, &column))
2071 return FALSE;
2072 if (prev_row != row) {
2073 gtk_cmclist_unselect_all(clist);
2074 if (event->button == 2)
2075 folderview_select_node
2076 (folderview,
2077 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2078 row));
2079 else
2080 gtk_cmclist_select_row(clist, row, column);
2084 if (event->button != 3) return FALSE;
2086 folderview_set_sens_and_popup_menu(folderview, row, event);
2087 return FALSE;
2090 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2091 FolderView *folderview)
2093 int row = -1, column = -1;
2095 if (!event) return FALSE;
2097 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2098 &row, &column))
2099 return FALSE;
2100 if (event->button == 1 && folderview->open_folder == FALSE &&
2101 folderview->opened != NULL) {
2102 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2103 folderview->opened);
2104 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2107 return FALSE;
2110 #define BREAK_ON_MODIFIER_KEY() \
2111 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2113 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2114 FolderView *folderview)
2116 GtkCMCTreeNode *node;
2117 FolderItem *item;
2119 if (!event) return FALSE;
2121 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2122 return FALSE;
2124 switch (event->keyval) {
2125 case GDK_KEY_Right:
2126 if (folderview->selected) {
2127 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2128 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2129 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2130 folderview->selected);
2131 else
2132 folderview_select_node(folderview,
2133 folderview->selected);
2135 break;
2136 #ifdef GENERIC_UMPC
2137 case GDK_KEY_Return:
2138 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2139 gtk_cmctree_toggle_expansion(
2140 GTK_CMCTREE(folderview->ctree),
2141 folderview->selected);
2143 break;
2144 #else
2145 case GDK_KEY_Return:
2146 case GDK_KEY_KP_Enter:
2147 if (folderview->selected)
2148 folderview_select_node(folderview, folderview->selected);
2149 break;
2150 #endif
2151 case GDK_KEY_space:
2152 BREAK_ON_MODIFIER_KEY();
2153 if (folderview->selected) {
2154 if (folderview->opened == folderview->selected &&
2155 (!folderview->summaryview->folder_item ||
2156 folderview->summaryview->folder_item->total_msgs == 0))
2157 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2158 else
2159 folderview_select_node(folderview,
2160 folderview->selected);
2162 break;
2163 case GDK_KEY_Left:
2164 if (folderview->selected) {
2165 /* If the folder is expanded and can be collapsed, do that... */
2166 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2167 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2168 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2169 folderview->selected);
2170 } else {
2171 /* ...otherwise, move cursor to its parent node. */
2172 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2173 folderview->selected))) {
2174 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2175 NULL, folder_item_parent(item)))) {
2176 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2177 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2178 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2179 node, -1, 0, 0);
2184 break;
2185 case GDK_KEY_Home:
2186 case GDK_KEY_End:
2187 if (event->keyval == GDK_KEY_Home)
2188 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2189 else
2190 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2191 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2193 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2195 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2196 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2197 node, -1, 0, 0);
2198 break;
2199 default:
2200 break;
2203 return FALSE;
2206 typedef struct _PostponedSelectData
2208 GtkCMCTree *ctree;
2209 GtkCMCTreeNode *row;
2210 gint column;
2211 FolderView *folderview;
2212 } PostponedSelectData;
2214 static gboolean postpone_select(void *data)
2216 PostponedSelectData *psdata = (PostponedSelectData *)data;
2217 debug_print("trying again\n");
2219 psdata->folderview->postpone_select_id = 0;
2220 psdata->folderview->open_folder = TRUE;
2221 main_window_cursor_normal(psdata->folderview->mainwin);
2222 STATUSBAR_POP(psdata->folderview->mainwin);
2223 folderview_selected(psdata->ctree, psdata->row,
2224 psdata->column, psdata->folderview);
2225 g_free(psdata);
2226 return FALSE;
2229 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2231 if (folderview->opened) {
2232 if (dirty) {
2233 folderview->opened = NULL;
2234 return;
2237 FolderItem *olditem =
2238 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2239 folderview->opened);
2240 if (olditem) {
2241 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2242 olditem->path ? olditem->path:olditem->name);
2243 /* will be null if we just moved the previously opened folder */
2244 STATUSBAR_PUSH(folderview->mainwin, buf);
2245 main_window_cursor_wait(folderview->mainwin);
2246 g_free(buf);
2247 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2248 summary_show(folderview->summaryview, NULL, FALSE);
2249 folder_item_close(olditem);
2250 main_window_cursor_normal(folderview->mainwin);
2251 STATUSBAR_POP(folderview->mainwin);
2252 if (olditem->folder->klass->item_closed)
2253 olditem->folder->klass->item_closed(olditem);
2258 if (folderview->opened &&
2259 !GTK_CMCTREE_ROW(folderview->opened)->children)
2260 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2262 folderview->opened = NULL;
2264 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2265 gint column, FolderView *folderview)
2267 GdkDisplay *display;
2268 GdkSeat *seat;
2269 GdkDevice *device;
2270 static gboolean can_select = TRUE; /* exclusive lock */
2271 gboolean opened;
2272 FolderItem *item;
2273 gchar *buf;
2274 int res = 0;
2275 GtkCMCTreeNode *old_opened = folderview->opened;
2276 START_TIMING("");
2277 folderview->selected = row;
2279 display = gdk_display_get_default();
2280 seat = gdk_display_get_default_seat(display);
2281 device = gdk_seat_get_pointer(seat);
2283 debug_print("newly selected %p, opened %p\n", folderview->selected,
2284 folderview->opened);
2285 if (folderview->opened == row) {
2286 folderview->open_folder = FALSE;
2287 END_TIMING();
2288 return;
2291 item = gtk_cmctree_node_get_row_data(ctree, row);
2292 if (!item) {
2293 END_TIMING();
2294 folderview->open_folder = FALSE;
2295 return;
2298 if (!can_select || summary_is_locked(folderview->summaryview)) {
2299 if (folderview->opened) {
2300 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2301 gtk_cmctree_select(ctree, folderview->opened);
2303 folderview->open_folder = FALSE;
2304 END_TIMING();
2305 return;
2308 if (!folderview->open_folder) {
2309 END_TIMING();
2310 return;
2313 can_select = FALSE;
2315 /* Save cache for old folder */
2316 /* We don't want to lose all caches if app crashes */
2317 /* Resets folderview->opened to NULL */
2318 folderview_close_opened(folderview, FALSE);
2320 /* CLAWS: set compose button type: news folder items
2321 * always have a news folder as parent */
2322 if (item->folder)
2323 toolbar_set_compose_button
2324 (folderview->mainwin->toolbar,
2325 FOLDER_TYPE(item->folder) == F_NEWS ?
2326 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2328 if (item->path)
2329 debug_print("Folder %s is selected\n", item->path);
2331 if (!GTK_CMCTREE_ROW(row)->children)
2332 gtk_cmctree_expand(ctree, row);
2334 /* ungrab the mouse event */
2335 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2336 gtk_grab_remove(GTK_WIDGET(ctree));
2337 if (gdk_display_device_is_grabbed(display, device))
2338 gdk_seat_ungrab(seat);
2341 /* Open Folder */
2342 /* TODO: wwp: avoid displaying (null) in the status bar */
2343 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2344 item->path : "(null)");
2345 debug_print("%s\n", buf);
2346 STATUSBAR_PUSH(folderview->mainwin, buf);
2347 g_free(buf);
2349 main_window_cursor_wait(folderview->mainwin);
2351 if (folderview->scanning_folder == item->folder) {
2352 res = -2;
2353 } else {
2354 res = folder_item_open(item);
2357 if (res == -1 && item->no_select == FALSE) {
2358 main_window_cursor_normal(folderview->mainwin);
2359 STATUSBAR_POP(folderview->mainwin);
2361 alertpanel_error(_("Folder could not be opened."));
2363 folderview->open_folder = FALSE;
2364 can_select = TRUE;
2365 END_TIMING();
2366 return;
2367 } else if (res == -2 && item->no_select == FALSE) {
2368 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2369 data->ctree = ctree;
2370 data->row = row;
2371 data->column = column;
2372 data->folderview = folderview;
2373 debug_print("postponing open of %s till end of scan\n",
2374 item->path ? item->path:item->name);
2375 folderview->open_folder = FALSE;
2376 can_select = TRUE;
2377 if (folderview->postpone_select_id != 0)
2378 g_source_remove(folderview->postpone_select_id);
2379 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2380 END_TIMING();
2381 return;
2384 main_window_cursor_normal(folderview->mainwin);
2386 /* Show messages */
2387 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2388 opened = summary_show(folderview->summaryview, item, FALSE);
2390 folder_clean_cache_memory(item);
2392 if (!opened) {
2393 gtkut_ctree_set_focus_row(ctree, old_opened);
2394 gtk_cmctree_select(ctree, old_opened);
2395 folderview->opened = old_opened;
2396 } else {
2397 folderview->opened = row;
2398 if (gtk_cmctree_node_is_visible(ctree, row)
2399 != GTK_VISIBILITY_FULL)
2400 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2403 STATUSBAR_POP(folderview->mainwin);
2405 folderview->open_folder = FALSE;
2406 can_select = TRUE;
2407 END_TIMING();
2410 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2411 FolderView *folderview)
2413 FolderItem *item;
2415 item = gtk_cmctree_node_get_row_data(ctree, node);
2416 cm_return_if_fail(item != NULL);
2417 item->collapsed = FALSE;
2418 folderview_update_node(folderview, node);
2421 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2422 FolderView *folderview)
2424 FolderItem *item;
2426 item = gtk_cmctree_node_get_row_data(ctree, node);
2427 cm_return_if_fail(item != NULL);
2428 item->collapsed = TRUE;
2429 folderview_update_node(folderview, node);
2432 static void folderview_popup_close(GtkMenuShell *menu_shell,
2433 FolderView *folderview)
2435 if (!folderview->opened) return;
2437 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2440 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2441 FolderView *folderview)
2443 FolderColumnType type = folderview->col_state[column].type;
2445 prefs_common.folder_col_size[type] = width;
2448 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2450 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2451 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2452 GtkCMCTreeNode *node, *parent_node;
2453 gint *col_pos = folderview->col_pos;
2454 FolderItemUpdateData hookdata;
2456 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2457 if (parent_node == NULL)
2458 return;
2460 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2462 text[col_pos[F_COL_FOLDER]] = item->name;
2463 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2464 FOLDER_SPACING,
2465 folderxpm,
2466 folderopenxpm,
2467 FALSE, FALSE);
2468 gtk_cmctree_expand(ctree, parent_node);
2469 gtk_cmctree_node_set_row_data(ctree, node, item);
2470 folderview_sort_folders(folderview, parent_node, item->folder);
2472 hookdata.item = item;
2473 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2474 hookdata.msg = NULL;
2475 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2477 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2480 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2482 FolderView *folderview = (FolderView *)data;
2483 FolderItem *item;
2484 GSList *mlist = NULL;
2485 GSList *cur = NULL;
2486 FolderItem *special_trash = NULL;
2487 PrefsAccount *ac;
2489 if (!folderview->selected) return;
2490 item = folderview_get_selected_item(folderview);
2491 cm_return_if_fail(item != NULL);
2492 cm_return_if_fail(item->folder != NULL);
2494 if (NULL != (ac = account_find_from_item(item)))
2495 special_trash = account_get_special_folder(ac, F_TRASH);
2497 if (item != item->folder->trash && item != special_trash
2498 && !folder_has_parent_of_type(item, F_TRASH)) return;
2500 if (prefs_common.ask_on_clean) {
2501 if (alertpanel(_("Empty trash"),
2502 _("Delete all messages in trash?"),
2503 NULL, _("_Cancel"), NULL, _("_Empty trash"), NULL, NULL,
2504 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2505 return;
2508 mlist = folder_item_get_msg_list(item);
2510 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2511 MsgInfo * msginfo = (MsgInfo *) cur->data;
2512 if (MSG_IS_LOCKED(msginfo->flags))
2513 continue;
2514 /* is it partially received? (partial_recv isn't cached) */
2515 if (msginfo->total_size != 0 &&
2516 msginfo->size != (off_t)msginfo->total_size)
2517 partial_mark_for_delete(msginfo);
2519 procmsg_msg_list_free(mlist);
2521 folder_item_remove_all_msg(item);
2524 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2526 FolderView *folderview = (FolderView *)data;
2527 FolderItem *item;
2528 FolderItem *special_queue = NULL;
2529 PrefsAccount *ac;
2530 gchar *errstr = NULL;
2532 if (!folderview->selected) return;
2533 item = folderview_get_selected_item(folderview);
2534 cm_return_if_fail(item != NULL);
2535 cm_return_if_fail(item->folder != NULL);
2537 if (NULL != (ac = account_find_from_item(item)))
2538 special_queue = account_get_special_folder(ac, F_QUEUE);
2540 if (item != item->folder->queue && item != special_queue
2541 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2543 if (procmsg_queue_is_empty(item))
2544 return;
2546 if (prefs_common.work_offline)
2547 if (alertpanel(_("Offline warning"),
2548 _("You're working offline. Override?"),
2549 NULL, _("_No"), NULL, _("_Yes"),
2550 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2551 return;
2553 /* ask for confirmation before sending queued messages only
2554 in online mode and if there is at least one message queued
2555 in any of the folder queue
2557 if (prefs_common.confirm_send_queued_messages) {
2558 if (!prefs_common.work_offline) {
2559 if (alertpanel(_("Send queued messages"),
2560 _("Send all queued messages?"),
2561 NULL, _("_Cancel"), NULL, _("_Send"),
2562 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2563 return;
2567 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2568 if (!errstr)
2569 alertpanel_error_log(_("Some errors occurred while "
2570 "sending queued messages."));
2571 else {
2572 alertpanel_error_log(_("Some errors occurred "
2573 "while sending queued messages:\n%s"), errstr);
2574 g_free(errstr);
2579 static void folderview_search_cb(GtkAction *action, gpointer data)
2581 FolderView *folderview = (FolderView *)data;
2582 summary_search(folderview->summaryview);
2585 static void folderview_startup_folder_cb(GtkAction *action, gpointer data)
2587 FolderView *folderview = (FolderView *)data;
2588 FolderItem *item;
2590 if (!folderview->selected) return;
2592 item = folderview_get_selected_item(folderview);
2594 prefs_common.goto_last_folder_on_startup = FALSE;
2595 prefs_common.goto_folder_on_startup = TRUE;
2596 prefs_common.startup_folder = folder_item_get_identifier(item);
2599 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2601 FolderView *folderview = (FolderView *)data;
2602 FolderItem *item;
2604 if (!folderview->selected) return;
2606 item = folderview_get_selected_item(folderview);
2608 folderview_run_processing(item);
2611 void folderview_run_processing(FolderItem *item)
2613 cm_return_if_fail(item != NULL);
2614 cm_return_if_fail(item->folder != NULL);
2616 item->processing_pending = TRUE;
2617 folder_item_apply_processing(item);
2618 item->processing_pending = FALSE;
2621 static void folderview_property_cb(GtkAction *action, gpointer data)
2623 FolderView *folderview = (FolderView *)data;
2624 FolderItem *item;
2626 if (!folderview->selected) return;
2628 item = folderview_get_selected_item(folderview);
2629 cm_return_if_fail(item != NULL);
2630 cm_return_if_fail(item->folder != NULL);
2632 prefs_folder_item_open(item);
2635 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2637 GSList *list = NULL;
2638 GSList *done = NULL;
2639 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2641 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2642 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2643 && list->data != node) {
2644 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2645 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2648 for (list = done; list != NULL; list = g_slist_next(list)) {
2649 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2650 list->data);
2652 g_slist_free(done);
2655 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2656 FolderItem *to_folder, gboolean copy)
2658 FolderItem *new_folder = NULL;
2659 gchar *buf;
2660 gint status;
2662 cm_return_if_fail(folderview != NULL);
2663 cm_return_if_fail(from_folder != NULL);
2664 cm_return_if_fail(to_folder != NULL);
2666 if (prefs_common.warn_dnd) {
2667 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2668 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2669 from_folder->name, to_folder->name);
2670 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2671 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
2672 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
2673 g_free(buf);
2675 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2676 return;
2677 else if (status & G_ALERTDISABLE)
2678 prefs_common.warn_dnd = FALSE;
2681 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2682 from_folder->name, to_folder->name);
2683 STATUSBAR_PUSH(folderview->mainwin, buf);
2684 g_free(buf);
2685 summary_clear_all(folderview->summaryview);
2686 folderview->opened = NULL;
2687 folderview->selected = NULL;
2688 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2689 inc_lock();
2690 main_window_cursor_wait(folderview->mainwin);
2692 statusbar_verbosity_set(FALSE);
2693 folder_item_update_freeze();
2694 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2695 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2696 statusbar_verbosity_set(FALSE);
2697 main_window_cursor_normal(folderview->mainwin);
2698 STATUSBAR_POP(folderview->mainwin);
2699 folder_item_update_thaw();
2700 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2702 folderview_sort_folders(folderview,
2703 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2704 NULL, to_folder), new_folder->folder);
2705 folderview_select(folderview, new_folder);
2706 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2707 } else {
2708 statusbar_verbosity_set(FALSE);
2709 main_window_cursor_normal(folderview->mainwin);
2710 STATUSBAR_POP(folderview->mainwin);
2711 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2712 folder_item_update_thaw();
2713 switch (status) {
2714 case F_MOVE_FAILED_DEST_IS_PARENT:
2715 alertpanel_error(_("Source and destination are the same."));
2716 break;
2717 case F_MOVE_FAILED_DEST_IS_CHILD:
2718 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2719 _("Can't move a folder to one of its children."));
2720 break;
2721 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2722 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2723 break;
2724 default:
2725 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2726 break;
2729 inc_unlock();
2730 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2733 static gint folderview_clist_compare(GtkCMCList *clist,
2734 gconstpointer ptr1, gconstpointer ptr2)
2736 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2737 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2739 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2741 return item1->order - item2->order;
2744 // if only one folder has an order it comes first
2745 if (item1->order > 0)
2747 return -1;
2749 if (item2->order > 0)
2751 return 1;
2754 if (!item1->name)
2755 return (item2->name != NULL);
2756 if (!item2->name)
2757 return -1;
2759 return g_utf8_collate(item1->name, item2->name);
2762 static void folderview_processing_cb(GtkAction *action, gpointer data)
2764 FolderView *folderview = (FolderView *)data;
2765 FolderItem *item;
2766 gchar *id, *title;
2768 if (!folderview->selected) return;
2770 item = folderview_get_selected_item(folderview);
2771 cm_return_if_fail(item != NULL);
2772 cm_return_if_fail(item->folder != NULL);
2774 id = folder_item_get_identifier(item);
2775 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2776 g_free (id);
2778 prefs_filtering_open(&item->prefs->processing, title,
2779 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2780 g_free (title);
2783 void folderview_set_target_folder_color(GdkRGBA color_op)
2785 GList *list;
2786 FolderView *folderview;
2788 for (list = folderview_list; list != NULL; list = list->next) {
2789 folderview = (FolderView *)list->data;
2790 folderview->color_op = color_op;
2794 static gchar *last_smallfont = NULL;
2795 static gchar *last_normalfont = NULL;
2796 static gchar *last_boldfont = NULL;
2797 static gboolean last_derive = 0;
2799 void folderview_reinit_fonts(FolderView *folderview)
2801 /* force reinit */
2802 g_free(last_smallfont);
2803 last_smallfont = NULL;
2804 g_free(last_normalfont);
2805 last_normalfont = NULL;
2806 g_free(last_boldfont);
2807 last_boldfont = NULL;
2810 void folderview_reflect_prefs(void)
2812 gboolean update_font = FALSE;
2813 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2814 FolderItem *item = folderview_get_selected_item(folderview);
2815 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2816 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2817 gint height = gtk_adjustment_get_value(pos);
2819 folderview->color_new = prefs_common.color[COL_NEW];
2820 folderview->color_op = prefs_common.color[COL_TGT_FOLDER];
2822 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2823 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2824 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2825 last_derive != prefs_common.derive_from_normal_font)
2826 update_font = TRUE;
2828 if (!update_font)
2829 return;
2831 g_free(last_smallfont);
2832 last_smallfont = g_strdup(SMALL_FONT);
2833 g_free(last_normalfont);
2834 last_normalfont = g_strdup(NORMAL_FONT);
2835 g_free(last_boldfont);
2836 last_boldfont = g_strdup(BOLD_FONT);
2837 last_derive = prefs_common.derive_from_normal_font;
2839 folderview_set_fonts(folderview);
2841 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2842 folderview_column_set_titles(folderview);
2843 folderview_set_all();
2845 g_signal_handlers_block_by_func
2846 (G_OBJECT(folderview->ctree),
2847 G_CALLBACK(folderview_selected), folderview);
2849 if (item) {
2850 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2851 GTK_CMCTREE(folderview->ctree), NULL, item);
2853 folderview_select(folderview, item);
2854 folderview->open_folder = FALSE;
2855 folderview->selected = node;
2858 g_signal_handlers_unblock_by_func
2859 (G_OBJECT(folderview->ctree),
2860 G_CALLBACK(folderview_selected), folderview);
2862 pos = gtk_scrolled_window_get_vadjustment(
2863 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2864 gtk_adjustment_set_value(pos, height);
2865 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2868 static void drag_state_stop(FolderView *folderview)
2870 if (folderview->drag_timer_id)
2871 g_source_remove(folderview->drag_timer_id);
2872 folderview->drag_timer_id = 0;
2873 folderview->drag_node = NULL;
2876 static gboolean folderview_defer_expand(FolderView *folderview)
2878 if (folderview->drag_node) {
2879 folderview_recollapse_nodes(folderview, folderview->drag_node);
2880 if (folderview->drag_item->collapsed) {
2881 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2882 folderview->nodes_to_recollapse = g_slist_append
2883 (folderview->nodes_to_recollapse, folderview->drag_node);
2886 folderview->drag_item = NULL;
2887 folderview->drag_timer_id = 0;
2888 return FALSE;
2891 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2893 /* the idea is that we call drag_state_start() whenever we want expansion to
2894 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2895 * we need to call drag_state_stop() */
2896 drag_state_stop(folderview);
2897 /* request expansion */
2898 if (0 != (folderview->drag_timer_id = g_timeout_add
2899 (prefs_common.hover_timeout,
2900 (GSourceFunc)folderview_defer_expand,
2901 folderview))) {
2902 folderview->drag_node = node;
2903 folderview->drag_item = item;
2906 #ifndef GENERIC_UMPC
2907 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2908 FolderView *folderview)
2910 GdkDragContext *context;
2912 cm_return_if_fail(folderview != NULL);
2913 if (folderview->selected == NULL) return;
2914 if (folderview->nodes_to_recollapse)
2915 g_slist_free(folderview->nodes_to_recollapse);
2916 folderview->nodes_to_recollapse = NULL;
2917 context = gtk_drag_begin_with_coordinates(widget, folderview->target_list,
2918 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
2919 -1, -1);
2920 gtk_drag_set_icon_default(context);
2922 #endif
2923 static void folderview_drag_data_get(GtkWidget *widget,
2924 GdkDragContext *drag_context,
2925 GtkSelectionData *selection_data,
2926 guint info,
2927 guint time,
2928 FolderView *folderview)
2930 FolderItem *item;
2931 GList *sel;
2932 if (info == TARGET_DUMMY) {
2933 sel = GTK_CMCLIST(folderview->ctree)->selection;
2934 if (!sel)
2935 return;
2937 item = gtk_cmctree_node_get_row_data
2938 (GTK_CMCTREE(folderview->ctree),
2939 GTK_CMCTREE_NODE(sel->data));
2940 if (item) {
2941 gchar *source = NULL;
2942 gchar *name = folder_item_get_identifier(item);
2943 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", name);
2944 g_free(name);
2945 gtk_selection_data_set(selection_data,
2946 gtk_selection_data_get_target(selection_data), 8,
2947 source, strlen(source));
2949 } else {
2950 g_warning("unknown info %d", info);
2954 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2956 FolderUpdateData *hookdata;
2957 FolderView *folderview;
2958 GtkWidget *ctree;
2960 hookdata = source;
2961 folderview = (FolderView *) userdata;
2962 cm_return_val_if_fail(hookdata != NULL, FALSE);
2963 cm_return_val_if_fail(folderview != NULL, FALSE);
2965 ctree = folderview->ctree;
2966 cm_return_val_if_fail(ctree != NULL, FALSE);
2968 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2969 folderview_create_folder_node(folderview, hookdata->item);
2970 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2971 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2972 NULL, folder_item_parent(hookdata->item));
2973 folderview_sort_folders(folderview, node, hookdata->folder);
2974 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2975 GtkCMCTreeNode *node;
2977 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2978 if (node != NULL) {
2979 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2980 if (folderview->selected == node)
2981 folderview->selected = NULL;
2982 if (folderview->opened == node)
2983 folderview->opened = NULL;
2985 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2986 /* do nothing, it's done by the ADD and REMOVE) */
2987 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2988 folderview_set(folderview);
2990 return FALSE;
2993 static gboolean folderview_dnd_scroll_cb(gpointer data)
2995 FolderView *folderview = (FolderView *)data;
2996 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2997 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2998 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2999 gint max = (int)gtk_adjustment_get_upper(pos) -
3000 (int)gtk_adjustment_get_page_size(pos);
3002 if (folderview->scroll_value == 0) {
3003 folderview->scroll_timeout_id = 0;
3004 return FALSE;
3007 if (folderview->scroll_value > 0 && new_val > max) {
3008 new_val = max;
3009 } else if (folderview->scroll_value < 0 && new_val < 0) {
3010 new_val = 0;
3012 gtk_adjustment_set_value(pos, new_val);
3014 return TRUE;
3017 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
3018 GdkDragContext *context,
3019 gint x,
3020 gint y,
3021 guint time,
3022 FolderView *folderview)
3024 gint row, column;
3025 FolderItem *item = NULL, *src_item = NULL;
3026 GtkCMCTreeNode *node = NULL;
3027 gboolean acceptable = FALSE;
3028 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3029 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3030 int height = (int)gtk_adjustment_get_page_size(pos);
3031 int total_height = (int)gtk_adjustment_get_upper(pos);
3032 int vpos = (int)gtk_adjustment_get_value(pos);
3033 int offset = prefs_common.show_col_headers ? 24:0;
3034 int dist;
3036 if (gtk_cmclist_get_selection_info
3037 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3038 GtkWidget *srcwidget;
3040 if (y > height - (48 - offset) && height + vpos < total_height) {
3041 dist = -(height - (48 - offset) - y);
3042 folderview->scroll_value = 1.41f * (1+(dist / 6));
3043 } else if (y < 72 - (24 - offset) && y >= 0) {
3044 dist = 72 - (24 - offset) - y;
3045 folderview->scroll_value = -1.41f * (1+(dist / 6));
3046 } else {
3047 folderview->scroll_value = 0;
3049 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3050 folderview->scroll_timeout_id =
3051 g_timeout_add(30, folderview_dnd_scroll_cb,
3052 folderview);
3055 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3056 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3057 src_item = folderview->summaryview->folder_item;
3059 srcwidget = gtk_drag_get_source_widget(context);
3060 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3061 /* comes from summaryview */
3062 /* we are copying messages, so only accept folder items that are not
3063 the source item, are no root items and can copy messages */
3064 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3065 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3066 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3067 acceptable = TRUE;
3068 } else if (srcwidget == folderview->ctree) {
3069 /* comes from folderview */
3070 /* we are moving folder items, only accept folders that are not
3071 the source items and can copy messages and create folder items */
3072 if (item && item->folder && src_item && src_item != item &&
3073 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3074 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3075 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3076 || item->folder == src_item->folder))
3077 acceptable = TRUE;
3078 } else {
3079 /* comes from another app */
3080 /* we are adding messages, so only accept folder items that are
3081 no root items and can copy messages */
3082 if (item && item->folder && folder_item_parent(item) != NULL
3083 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3084 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3085 acceptable = TRUE;
3089 if (acceptable || (src_item && src_item == item))
3090 drag_state_start(folderview, node, item);
3092 if (acceptable) {
3093 g_signal_handlers_block_by_func
3094 (G_OBJECT(widget),
3095 G_CALLBACK(folderview_selected), folderview);
3096 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3097 g_signal_handlers_unblock_by_func
3098 (G_OBJECT(widget),
3099 G_CALLBACK(folderview_selected), folderview);
3100 gdk_drag_status(context,
3101 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3102 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3103 } else {
3104 if (folderview->opened)
3105 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3106 gdk_drag_status(context, 0, time);
3109 return acceptable;
3112 static void folderview_drag_leave_cb(GtkWidget *widget,
3113 GdkDragContext *context,
3114 guint time,
3115 FolderView *folderview)
3117 drag_state_stop(folderview);
3118 folderview->scroll_value = 0;
3119 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3122 static void free_info (gpointer stuff, gpointer data)
3124 g_free(stuff);
3127 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3128 guint time, FolderItem *item)
3130 GList *list, *tmp;
3131 GSList *msglist = NULL;
3132 list = uri_list_extract_filenames(data);
3133 if (!(item && item->folder && folder_item_parent(item) != NULL
3134 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3136 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3137 debug_print("item doesn't fit\n");
3138 return;
3140 if (!list) {
3141 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3142 debug_print("list is empty\n");
3143 return;
3145 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3146 MsgFileInfo *info = NULL;
3148 if (file_is_email((gchar *)tmp->data)) {
3149 info = g_new0(MsgFileInfo, 1);
3150 info->msginfo = NULL;
3151 info->file = (gchar *)tmp->data;
3152 msglist = g_slist_prepend(msglist, info);
3153 debug_print("file is a mail\n");
3154 } else {
3155 debug_print("file isn't a mail\n");
3158 if (msglist) {
3159 msglist = g_slist_reverse(msglist);
3160 folder_item_add_msgs(item, msglist, FALSE);
3161 g_slist_foreach(msglist, free_info, NULL);
3162 g_slist_free(msglist);
3163 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3164 } else {
3165 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3167 list_free_strings_full(list);
3170 static void folderview_drag_received_cb(GtkWidget *widget,
3171 GdkDragContext *drag_context,
3172 gint x,
3173 gint y,
3174 GtkSelectionData *data,
3175 guint info,
3176 guint time,
3177 FolderView *folderview)
3179 gint row, column;
3180 FolderItem *item = NULL, *src_item;
3181 GtkCMCTreeNode *node;
3182 int offset = prefs_common.show_col_headers ? 24:0;
3184 folderview->scroll_value = 0;
3186 if (info == TARGET_DUMMY) {
3187 drag_state_stop(folderview);
3188 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3189 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3190 /* comes from summaryview */
3191 if (gtk_cmclist_get_selection_info
3192 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3193 return;
3195 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3196 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3197 src_item = folderview->summaryview->folder_item;
3199 if (item && item->no_select) {
3200 alertpanel_error(_("The destination folder can only be used to "
3201 "store subfolders."));
3202 return;
3204 /* re-check (due to acceptable possibly set for folder moves */
3205 if (!(item && item->folder && item->path && !item->no_select &&
3206 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3207 return;
3210 switch (gdk_drag_context_get_selected_action(drag_context)) {
3211 case GDK_ACTION_COPY:
3212 summary_copy_selected_to(folderview->summaryview, item);
3213 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3214 break;
3215 case GDK_ACTION_MOVE:
3216 case GDK_ACTION_DEFAULT:
3217 default:
3218 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3219 summary_copy_selected_to(folderview->summaryview, item);
3220 else
3221 summary_move_selected_to(folderview->summaryview, item);
3222 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3224 } else {
3225 /* comes from folderview */
3226 char *source;
3227 gboolean folder_is_normal = TRUE;
3228 gboolean copy = (GDK_ACTION_COPY ==
3229 gdk_drag_context_get_selected_action(drag_context));
3231 source = (char *)gtk_selection_data_get_data(data) + 17;
3232 if (gtk_cmclist_get_selection_info
3233 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3234 || *source == 0) {
3235 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3236 return;
3238 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3239 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3240 src_item = folder_find_item_from_identifier(source);
3242 folder_is_normal =
3243 src_item != NULL &&
3244 src_item->stype == F_NORMAL &&
3245 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3246 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3247 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3248 !folder_has_parent_of_type(src_item, F_TRASH);
3249 if (!item || !src_item || !folder_is_normal) {
3250 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3251 return;
3254 folderview_move_folder(folderview, src_item, item, copy);
3255 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3257 folderview->nodes_to_recollapse = NULL;
3258 } else if (info == TARGET_MAIL_URI_LIST) {
3259 if (gtk_cmclist_get_selection_info
3260 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3261 return;
3263 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3264 if (!node) {
3265 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3266 debug_print("no node\n");
3267 return;
3269 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3270 if (!item) {
3271 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3272 debug_print("no item\n");
3273 return;
3275 folderview_finish_dnd(gtk_selection_data_get_data(data),
3276 drag_context, time, item);
3280 static void folderview_drag_end_cb(GtkWidget *widget,
3281 GdkDragContext *drag_context,
3282 FolderView *folderview)
3284 drag_state_stop(folderview);
3285 folderview->scroll_value = 0;
3286 g_slist_free(folderview->nodes_to_recollapse);
3287 folderview->nodes_to_recollapse = NULL;
3290 void folderview_register_popup(FolderViewPopup *fpopup)
3292 GList *folderviews;
3294 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3295 FolderView *folderview = folderviews->data;
3296 GtkActionGroup *factory;
3298 factory = create_action_group(folderview, fpopup);
3299 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3301 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3304 void folderview_unregister_popup(FolderViewPopup *fpopup)
3306 GList *folderviews;
3309 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3310 FolderView *folderview = folderviews->data;
3312 g_hash_table_remove(folderview->popups, fpopup->klass);
3314 g_hash_table_remove(folderview_popups, fpopup->klass);
3317 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3319 g_return_if_fail(folderview != NULL);
3320 g_return_if_fail(item != NULL);
3322 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3323 g_return_if_fail(ctree != NULL);
3325 GtkCMCTreeNode *node =
3326 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3327 g_return_if_fail(node != NULL);
3329 gtk_cmctree_remove_node(ctree, node);
3332 void folderview_freeze(FolderView *folderview)
3334 if (folderview)
3335 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3338 void folderview_thaw(FolderView *folderview)
3340 if (folderview)
3341 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3344 void folderview_grab_focus(FolderView *folderview)
3346 if (folderview)
3347 gtk_widget_grab_focus(folderview->ctree);
3350 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3351 gpointer data)
3353 prefs_folder_column_open();
3356 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3357 GdkEvent *_event,
3358 gpointer user_data)
3360 GdkEventButton *event = (GdkEventButton *)_event;
3361 FolderView *folderview = (FolderView *)user_data;
3363 cm_return_val_if_fail(folderview != NULL, FALSE);
3365 /* Only handle single button presses. */
3366 if (event->type == GDK_2BUTTON_PRESS ||
3367 event->type == GDK_3BUTTON_PRESS)
3368 return FALSE;
3370 /* Handle right-click for context menu */
3371 if (event->button == 3) {
3372 gtk_menu_popup_at_pointer(GTK_MENU(folderview->headerpopupmenu), NULL);
3373 return TRUE;
3376 return FALSE;