Use custom get_default_font_size() and _name()
[claws.git] / src / folderview.c
blobe265635cf6587bacd91e28dc6577a2839c2237bd
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 if (item->node->parent != NULL) {
2004 gchar *id = folder_item_get_identifier(item);
2005 SET_SENS("FolderViewPopup/OpenFolder", !prefs_common.goto_folder_on_startup
2006 || strcmp(id, prefs_common.startup_folder));
2007 g_free(id);
2010 if (item == folder->trash || item == special_trash
2011 || folder_has_parent_of_type(item, F_TRASH)) {
2012 GSList *msglist = folder_item_get_msg_list(item);
2013 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2014 procmsg_msg_list_free(msglist);
2016 if (item == folder->queue || item == special_queue
2017 || folder_has_parent_of_type(item, F_QUEUE)) {
2018 GSList *msglist = folder_item_get_msg_list(item);
2019 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2020 procmsg_msg_list_free(msglist);
2022 #undef SET_SENS
2024 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2025 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2026 g_signal_connect(G_OBJECT(popup), "selection_done",
2027 G_CALLBACK(folderview_popup_close),
2028 folderview);
2029 gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL);
2032 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2033 FolderView *folderview)
2035 GtkCMCList *clist = GTK_CMCLIST(ctree);
2036 gint prev_row = -1, row = -1, column = -1;
2038 if (!event) return FALSE;
2039 if (event->window != clist->clist_window) return FALSE;
2041 if (event->button == 1 || event->button == 2) {
2042 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2043 folderview->open_folder = TRUE;
2045 if (event->type == GDK_2BUTTON_PRESS) {
2046 if (clist->selection) {
2047 GtkCMCTreeNode *node;
2049 node = GTK_CMCTREE_NODE(clist->selection->data);
2050 if (node) {
2051 gtk_cmctree_toggle_expansion(
2052 GTK_CMCTREE(ctree),
2053 node);
2054 folderview->open_folder = FALSE;
2058 return FALSE;
2061 if (event->button == 2 || event->button == 3) {
2062 /* right clicked */
2063 if (clist->selection) {
2064 GtkCMCTreeNode *node;
2066 node = GTK_CMCTREE_NODE(clist->selection->data);
2067 if (node)
2068 prev_row = gtkut_ctree_get_nth_from_node
2069 (GTK_CMCTREE(ctree), node);
2072 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2073 &row, &column))
2074 return FALSE;
2075 if (prev_row != row) {
2076 gtk_cmclist_unselect_all(clist);
2077 if (event->button == 2)
2078 folderview_select_node
2079 (folderview,
2080 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2081 row));
2082 else
2083 gtk_cmclist_select_row(clist, row, column);
2087 if (event->button != 3) return FALSE;
2089 folderview_set_sens_and_popup_menu(folderview, row, event);
2090 return FALSE;
2093 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2094 FolderView *folderview)
2096 int row = -1, column = -1;
2098 if (!event) return FALSE;
2100 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2101 &row, &column))
2102 return FALSE;
2103 if (event->button == 1 && folderview->open_folder == FALSE &&
2104 folderview->opened != NULL) {
2105 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2106 folderview->opened);
2107 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2110 return FALSE;
2113 #define BREAK_ON_MODIFIER_KEY() \
2114 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2116 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2117 FolderView *folderview)
2119 GtkCMCTreeNode *node;
2120 FolderItem *item;
2122 if (!event) return FALSE;
2124 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2125 return FALSE;
2127 switch (event->keyval) {
2128 case GDK_KEY_Right:
2129 if (folderview->selected) {
2130 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2131 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2132 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2133 folderview->selected);
2134 else
2135 folderview_select_node(folderview,
2136 folderview->selected);
2138 break;
2139 #ifdef GENERIC_UMPC
2140 case GDK_KEY_Return:
2141 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2142 gtk_cmctree_toggle_expansion(
2143 GTK_CMCTREE(folderview->ctree),
2144 folderview->selected);
2146 break;
2147 #else
2148 case GDK_KEY_Return:
2149 case GDK_KEY_KP_Enter:
2150 if (folderview->selected)
2151 folderview_select_node(folderview, folderview->selected);
2152 break;
2153 #endif
2154 case GDK_KEY_space:
2155 BREAK_ON_MODIFIER_KEY();
2156 if (folderview->selected) {
2157 if (folderview->opened == folderview->selected &&
2158 (!folderview->summaryview->folder_item ||
2159 folderview->summaryview->folder_item->total_msgs == 0))
2160 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2161 else
2162 folderview_select_node(folderview,
2163 folderview->selected);
2165 break;
2166 case GDK_KEY_Left:
2167 if (folderview->selected) {
2168 /* If the folder is expanded and can be collapsed, do that... */
2169 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2170 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2171 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2172 folderview->selected);
2173 } else {
2174 /* ...otherwise, move cursor to its parent node. */
2175 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2176 folderview->selected))) {
2177 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2178 NULL, folder_item_parent(item)))) {
2179 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2180 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2181 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2182 node, -1, 0, 0);
2187 break;
2188 case GDK_KEY_Home:
2189 case GDK_KEY_End:
2190 if (event->keyval == GDK_KEY_Home)
2191 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2192 else
2193 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2194 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2196 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2198 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2199 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2200 node, -1, 0, 0);
2201 break;
2202 default:
2203 break;
2206 return FALSE;
2209 typedef struct _PostponedSelectData
2211 GtkCMCTree *ctree;
2212 GtkCMCTreeNode *row;
2213 gint column;
2214 FolderView *folderview;
2215 } PostponedSelectData;
2217 static gboolean postpone_select(void *data)
2219 PostponedSelectData *psdata = (PostponedSelectData *)data;
2220 debug_print("trying again\n");
2222 psdata->folderview->postpone_select_id = 0;
2223 psdata->folderview->open_folder = TRUE;
2224 main_window_cursor_normal(psdata->folderview->mainwin);
2225 STATUSBAR_POP(psdata->folderview->mainwin);
2226 folderview_selected(psdata->ctree, psdata->row,
2227 psdata->column, psdata->folderview);
2228 g_free(psdata);
2229 return FALSE;
2232 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2234 if (folderview->opened) {
2235 if (dirty) {
2236 folderview->opened = NULL;
2237 return;
2240 FolderItem *olditem =
2241 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2242 folderview->opened);
2243 if (olditem) {
2244 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2245 olditem->path ? olditem->path:olditem->name);
2246 /* will be null if we just moved the previously opened folder */
2247 STATUSBAR_PUSH(folderview->mainwin, buf);
2248 main_window_cursor_wait(folderview->mainwin);
2249 g_free(buf);
2250 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2251 summary_show(folderview->summaryview, NULL, FALSE);
2252 folder_item_close(olditem);
2253 main_window_cursor_normal(folderview->mainwin);
2254 STATUSBAR_POP(folderview->mainwin);
2255 if (olditem->folder->klass->item_closed)
2256 olditem->folder->klass->item_closed(olditem);
2261 if (folderview->opened &&
2262 !GTK_CMCTREE_ROW(folderview->opened)->children)
2263 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2265 folderview->opened = NULL;
2267 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2268 gint column, FolderView *folderview)
2270 GdkDisplay *display;
2271 GdkSeat *seat;
2272 GdkDevice *device;
2273 static gboolean can_select = TRUE; /* exclusive lock */
2274 gboolean opened;
2275 FolderItem *item;
2276 gchar *buf;
2277 int res = 0;
2278 GtkCMCTreeNode *old_opened = folderview->opened;
2279 START_TIMING("");
2280 folderview->selected = row;
2282 display = gdk_display_get_default();
2283 seat = gdk_display_get_default_seat(display);
2284 device = gdk_seat_get_pointer(seat);
2286 debug_print("newly selected %p, opened %p\n", folderview->selected,
2287 folderview->opened);
2288 if (folderview->opened == row) {
2289 folderview->open_folder = FALSE;
2290 END_TIMING();
2291 return;
2294 item = gtk_cmctree_node_get_row_data(ctree, row);
2295 if (!item) {
2296 END_TIMING();
2297 folderview->open_folder = FALSE;
2298 return;
2301 if (!can_select || summary_is_locked(folderview->summaryview)) {
2302 if (folderview->opened) {
2303 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2304 gtk_cmctree_select(ctree, folderview->opened);
2306 folderview->open_folder = FALSE;
2307 END_TIMING();
2308 return;
2311 if (!folderview->open_folder) {
2312 END_TIMING();
2313 return;
2316 can_select = FALSE;
2318 /* Save cache for old folder */
2319 /* We don't want to lose all caches if app crashes */
2320 /* Resets folderview->opened to NULL */
2321 folderview_close_opened(folderview, FALSE);
2323 /* CLAWS: set compose button type: news folder items
2324 * always have a news folder as parent */
2325 if (item->folder)
2326 toolbar_set_compose_button
2327 (folderview->mainwin->toolbar,
2328 FOLDER_TYPE(item->folder) == F_NEWS ?
2329 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2331 if (item->path)
2332 debug_print("Folder %s is selected\n", item->path);
2334 if (!GTK_CMCTREE_ROW(row)->children)
2335 gtk_cmctree_expand(ctree, row);
2337 /* ungrab the mouse event */
2338 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2339 gtk_grab_remove(GTK_WIDGET(ctree));
2340 if (gdk_display_device_is_grabbed(display, device))
2341 gdk_seat_ungrab(seat);
2344 /* Open Folder */
2345 /* TODO: wwp: avoid displaying (null) in the status bar */
2346 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2347 item->path : "(null)");
2348 debug_print("%s\n", buf);
2349 STATUSBAR_PUSH(folderview->mainwin, buf);
2350 g_free(buf);
2352 main_window_cursor_wait(folderview->mainwin);
2354 if (folderview->scanning_folder == item->folder) {
2355 res = -2;
2356 } else {
2357 res = folder_item_open(item);
2360 if (res == -1 && item->no_select == FALSE) {
2361 main_window_cursor_normal(folderview->mainwin);
2362 STATUSBAR_POP(folderview->mainwin);
2364 alertpanel_error(_("Folder could not be opened."));
2366 folderview->open_folder = FALSE;
2367 can_select = TRUE;
2368 END_TIMING();
2369 return;
2370 } else if (res == -2 && item->no_select == FALSE) {
2371 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2372 data->ctree = ctree;
2373 data->row = row;
2374 data->column = column;
2375 data->folderview = folderview;
2376 debug_print("postponing open of %s till end of scan\n",
2377 item->path ? item->path:item->name);
2378 folderview->open_folder = FALSE;
2379 can_select = TRUE;
2380 if (folderview->postpone_select_id != 0)
2381 g_source_remove(folderview->postpone_select_id);
2382 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2383 END_TIMING();
2384 return;
2387 main_window_cursor_normal(folderview->mainwin);
2389 /* Show messages */
2390 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2391 opened = summary_show(folderview->summaryview, item, FALSE);
2393 folder_clean_cache_memory(item);
2395 if (!opened) {
2396 gtkut_ctree_set_focus_row(ctree, old_opened);
2397 gtk_cmctree_select(ctree, old_opened);
2398 folderview->opened = old_opened;
2399 } else {
2400 folderview->opened = row;
2401 if (gtk_cmctree_node_is_visible(ctree, row)
2402 != GTK_VISIBILITY_FULL)
2403 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2406 STATUSBAR_POP(folderview->mainwin);
2408 folderview->open_folder = FALSE;
2409 can_select = TRUE;
2410 END_TIMING();
2413 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2414 FolderView *folderview)
2416 FolderItem *item;
2418 item = gtk_cmctree_node_get_row_data(ctree, node);
2419 cm_return_if_fail(item != NULL);
2420 item->collapsed = FALSE;
2421 folderview_update_node(folderview, node);
2424 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2425 FolderView *folderview)
2427 FolderItem *item;
2429 item = gtk_cmctree_node_get_row_data(ctree, node);
2430 cm_return_if_fail(item != NULL);
2431 item->collapsed = TRUE;
2432 folderview_update_node(folderview, node);
2435 static void folderview_popup_close(GtkMenuShell *menu_shell,
2436 FolderView *folderview)
2438 if (!folderview->opened) return;
2440 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2443 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2444 FolderView *folderview)
2446 FolderColumnType type = folderview->col_state[column].type;
2448 prefs_common.folder_col_size[type] = width;
2451 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2453 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2454 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2455 GtkCMCTreeNode *node, *parent_node;
2456 gint *col_pos = folderview->col_pos;
2457 FolderItemUpdateData hookdata;
2459 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2460 if (parent_node == NULL)
2461 return;
2463 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2465 text[col_pos[F_COL_FOLDER]] = item->name;
2466 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2467 FOLDER_SPACING,
2468 folderxpm,
2469 folderopenxpm,
2470 FALSE, FALSE);
2471 gtk_cmctree_expand(ctree, parent_node);
2472 gtk_cmctree_node_set_row_data(ctree, node, item);
2473 folderview_sort_folders(folderview, parent_node, item->folder);
2475 hookdata.item = item;
2476 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2477 hookdata.msg = NULL;
2478 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2480 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2483 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2485 FolderView *folderview = (FolderView *)data;
2486 FolderItem *item;
2487 GSList *mlist = NULL;
2488 GSList *cur = NULL;
2489 FolderItem *special_trash = NULL;
2490 PrefsAccount *ac;
2492 if (!folderview->selected) return;
2493 item = folderview_get_selected_item(folderview);
2494 cm_return_if_fail(item != NULL);
2495 cm_return_if_fail(item->folder != NULL);
2497 if (NULL != (ac = account_find_from_item(item)))
2498 special_trash = account_get_special_folder(ac, F_TRASH);
2500 if (item != item->folder->trash && item != special_trash
2501 && !folder_has_parent_of_type(item, F_TRASH)) return;
2503 if (prefs_common.ask_on_clean) {
2504 if (alertpanel(_("Empty trash"),
2505 _("Delete all messages in trash?"),
2506 NULL, _("_Cancel"), NULL, _("_Empty trash"), NULL, NULL,
2507 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2508 return;
2511 mlist = folder_item_get_msg_list(item);
2513 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2514 MsgInfo * msginfo = (MsgInfo *) cur->data;
2515 if (MSG_IS_LOCKED(msginfo->flags))
2516 continue;
2517 /* is it partially received? (partial_recv isn't cached) */
2518 if (msginfo->total_size != 0 &&
2519 msginfo->size != (off_t)msginfo->total_size)
2520 partial_mark_for_delete(msginfo);
2522 procmsg_msg_list_free(mlist);
2524 folder_item_remove_all_msg(item);
2527 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2529 FolderView *folderview = (FolderView *)data;
2530 FolderItem *item;
2531 FolderItem *special_queue = NULL;
2532 PrefsAccount *ac;
2533 gchar *errstr = NULL;
2535 if (!folderview->selected) return;
2536 item = folderview_get_selected_item(folderview);
2537 cm_return_if_fail(item != NULL);
2538 cm_return_if_fail(item->folder != NULL);
2540 if (NULL != (ac = account_find_from_item(item)))
2541 special_queue = account_get_special_folder(ac, F_QUEUE);
2543 if (item != item->folder->queue && item != special_queue
2544 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2546 if (procmsg_queue_is_empty(item))
2547 return;
2549 if (prefs_common.work_offline)
2550 if (alertpanel(_("Offline warning"),
2551 _("You're working offline. Override?"),
2552 NULL, _("_No"), NULL, _("_Yes"),
2553 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2554 return;
2556 /* ask for confirmation before sending queued messages only
2557 in online mode and if there is at least one message queued
2558 in any of the folder queue
2560 if (prefs_common.confirm_send_queued_messages) {
2561 if (!prefs_common.work_offline) {
2562 if (alertpanel(_("Send queued messages"),
2563 _("Send all queued messages?"),
2564 NULL, _("_Cancel"), NULL, _("_Send"),
2565 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2566 return;
2570 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2571 if (!errstr)
2572 alertpanel_error_log(_("Some errors occurred while "
2573 "sending queued messages."));
2574 else {
2575 alertpanel_error_log(_("Some errors occurred "
2576 "while sending queued messages:\n%s"), errstr);
2577 g_free(errstr);
2582 static void folderview_search_cb(GtkAction *action, gpointer data)
2584 FolderView *folderview = (FolderView *)data;
2585 summary_search(folderview->summaryview);
2588 static void folderview_startup_folder_cb(GtkAction *action, gpointer data)
2590 FolderView *folderview = (FolderView *)data;
2591 FolderItem *item;
2593 if (!folderview->selected) return;
2595 item = folderview_get_selected_item(folderview);
2597 prefs_common.goto_last_folder_on_startup = FALSE;
2598 prefs_common.startup_folder = folder_item_get_identifier(item);
2599 prefs_common.goto_folder_on_startup = prefs_common.startup_folder? TRUE : FALSE;
2602 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2604 FolderView *folderview = (FolderView *)data;
2605 FolderItem *item;
2607 if (!folderview->selected) return;
2609 item = folderview_get_selected_item(folderview);
2611 folderview_run_processing(item);
2614 void folderview_run_processing(FolderItem *item)
2616 cm_return_if_fail(item != NULL);
2617 cm_return_if_fail(item->folder != NULL);
2619 item->processing_pending = TRUE;
2620 folder_item_apply_processing(item);
2621 item->processing_pending = FALSE;
2624 static void folderview_property_cb(GtkAction *action, gpointer data)
2626 FolderView *folderview = (FolderView *)data;
2627 FolderItem *item;
2629 if (!folderview->selected) return;
2631 item = folderview_get_selected_item(folderview);
2632 cm_return_if_fail(item != NULL);
2633 cm_return_if_fail(item->folder != NULL);
2635 prefs_folder_item_open(item);
2638 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2640 GSList *list = NULL;
2641 GSList *done = NULL;
2642 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2644 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2645 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2646 && list->data != node) {
2647 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2648 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2651 for (list = done; list != NULL; list = g_slist_next(list)) {
2652 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2653 list->data);
2655 g_slist_free(done);
2658 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2659 FolderItem *to_folder, gboolean copy)
2661 FolderItem *new_folder = NULL;
2662 gchar *buf;
2663 gint status;
2665 cm_return_if_fail(folderview != NULL);
2666 cm_return_if_fail(from_folder != NULL);
2667 cm_return_if_fail(to_folder != NULL);
2669 if (prefs_common.warn_dnd) {
2670 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2671 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2672 from_folder->name, to_folder->name);
2673 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2674 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
2675 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
2676 g_free(buf);
2678 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2679 return;
2680 else if (status & G_ALERTDISABLE)
2681 prefs_common.warn_dnd = FALSE;
2684 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2685 from_folder->name, to_folder->name);
2686 STATUSBAR_PUSH(folderview->mainwin, buf);
2687 g_free(buf);
2688 summary_clear_all(folderview->summaryview);
2689 folderview->opened = NULL;
2690 folderview->selected = NULL;
2691 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2692 inc_lock();
2693 main_window_cursor_wait(folderview->mainwin);
2695 statusbar_verbosity_set(FALSE);
2696 folder_item_update_freeze();
2697 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2698 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2699 statusbar_verbosity_set(FALSE);
2700 main_window_cursor_normal(folderview->mainwin);
2701 STATUSBAR_POP(folderview->mainwin);
2702 folder_item_update_thaw();
2703 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2705 folderview_sort_folders(folderview,
2706 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2707 NULL, to_folder), new_folder->folder);
2708 folderview_select(folderview, new_folder);
2709 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2710 } else {
2711 statusbar_verbosity_set(FALSE);
2712 main_window_cursor_normal(folderview->mainwin);
2713 STATUSBAR_POP(folderview->mainwin);
2714 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2715 folder_item_update_thaw();
2716 switch (status) {
2717 case F_MOVE_FAILED_DEST_IS_PARENT:
2718 alertpanel_error(_("Source and destination are the same."));
2719 break;
2720 case F_MOVE_FAILED_DEST_IS_CHILD:
2721 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2722 _("Can't move a folder to one of its children."));
2723 break;
2724 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2725 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2726 break;
2727 default:
2728 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2729 break;
2732 inc_unlock();
2733 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2736 static gint folderview_clist_compare(GtkCMCList *clist,
2737 gconstpointer ptr1, gconstpointer ptr2)
2739 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2740 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2742 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2744 return item1->order - item2->order;
2747 // if only one folder has an order it comes first
2748 if (item1->order > 0)
2750 return -1;
2752 if (item2->order > 0)
2754 return 1;
2757 if (!item1->name)
2758 return (item2->name != NULL);
2759 if (!item2->name)
2760 return -1;
2762 return g_utf8_collate(item1->name, item2->name);
2765 static void folderview_processing_cb(GtkAction *action, gpointer data)
2767 FolderView *folderview = (FolderView *)data;
2768 FolderItem *item;
2769 gchar *id, *title;
2771 if (!folderview->selected) return;
2773 item = folderview_get_selected_item(folderview);
2774 cm_return_if_fail(item != NULL);
2775 cm_return_if_fail(item->folder != NULL);
2777 id = folder_item_get_identifier(item);
2778 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2779 g_free (id);
2781 prefs_filtering_open(&item->prefs->processing, title,
2782 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2783 g_free (title);
2786 void folderview_set_target_folder_color(GdkRGBA color_op)
2788 GList *list;
2789 FolderView *folderview;
2791 for (list = folderview_list; list != NULL; list = list->next) {
2792 folderview = (FolderView *)list->data;
2793 folderview->color_op = color_op;
2797 static gchar *last_smallfont = NULL;
2798 static gchar *last_normalfont = NULL;
2799 static gchar *last_boldfont = NULL;
2800 static gboolean last_derive = 0;
2802 void folderview_reinit_fonts(FolderView *folderview)
2804 /* force reinit */
2805 g_free(last_smallfont);
2806 last_smallfont = NULL;
2807 g_free(last_normalfont);
2808 last_normalfont = NULL;
2809 g_free(last_boldfont);
2810 last_boldfont = NULL;
2813 void folderview_reflect_prefs(void)
2815 gboolean update_font = FALSE;
2816 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2817 FolderItem *item = folderview_get_selected_item(folderview);
2818 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2819 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2820 gint height = gtk_adjustment_get_value(pos);
2822 folderview->color_new = prefs_common.color[COL_NEW];
2823 folderview->color_op = prefs_common.color[COL_TGT_FOLDER];
2825 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2826 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2827 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2828 last_derive != prefs_common.derive_from_normal_font)
2829 update_font = TRUE;
2831 if (!update_font)
2832 return;
2834 g_free(last_smallfont);
2835 last_smallfont = g_strdup(SMALL_FONT);
2836 g_free(last_normalfont);
2837 last_normalfont = g_strdup(NORMAL_FONT);
2838 g_free(last_boldfont);
2839 last_boldfont = g_strdup(BOLD_FONT);
2840 last_derive = prefs_common.derive_from_normal_font;
2842 folderview_set_fonts(folderview);
2844 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2845 folderview_column_set_titles(folderview);
2846 folderview_set_all();
2848 g_signal_handlers_block_by_func
2849 (G_OBJECT(folderview->ctree),
2850 G_CALLBACK(folderview_selected), folderview);
2852 if (item) {
2853 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2854 GTK_CMCTREE(folderview->ctree), NULL, item);
2856 folderview_select(folderview, item);
2857 folderview->open_folder = FALSE;
2858 folderview->selected = node;
2861 g_signal_handlers_unblock_by_func
2862 (G_OBJECT(folderview->ctree),
2863 G_CALLBACK(folderview_selected), folderview);
2865 pos = gtk_scrolled_window_get_vadjustment(
2866 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2867 gtk_adjustment_set_value(pos, height);
2868 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2871 static void drag_state_stop(FolderView *folderview)
2873 if (folderview->drag_timer_id)
2874 g_source_remove(folderview->drag_timer_id);
2875 folderview->drag_timer_id = 0;
2876 folderview->drag_node = NULL;
2879 static gboolean folderview_defer_expand(FolderView *folderview)
2881 if (folderview->drag_node) {
2882 folderview_recollapse_nodes(folderview, folderview->drag_node);
2883 if (folderview->drag_item->collapsed) {
2884 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2885 folderview->nodes_to_recollapse = g_slist_append
2886 (folderview->nodes_to_recollapse, folderview->drag_node);
2889 folderview->drag_item = NULL;
2890 folderview->drag_timer_id = 0;
2891 return FALSE;
2894 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2896 /* the idea is that we call drag_state_start() whenever we want expansion to
2897 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2898 * we need to call drag_state_stop() */
2899 drag_state_stop(folderview);
2900 /* request expansion */
2901 if (0 != (folderview->drag_timer_id = g_timeout_add
2902 (prefs_common.hover_timeout,
2903 (GSourceFunc)folderview_defer_expand,
2904 folderview))) {
2905 folderview->drag_node = node;
2906 folderview->drag_item = item;
2909 #ifndef GENERIC_UMPC
2910 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2911 FolderView *folderview)
2913 GdkDragContext *context;
2915 cm_return_if_fail(folderview != NULL);
2916 if (folderview->selected == NULL) return;
2917 if (folderview->nodes_to_recollapse)
2918 g_slist_free(folderview->nodes_to_recollapse);
2919 folderview->nodes_to_recollapse = NULL;
2920 context = gtk_drag_begin_with_coordinates(widget, folderview->target_list,
2921 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
2922 -1, -1);
2923 gtk_drag_set_icon_default(context);
2925 #endif
2926 static void folderview_drag_data_get(GtkWidget *widget,
2927 GdkDragContext *drag_context,
2928 GtkSelectionData *selection_data,
2929 guint info,
2930 guint time,
2931 FolderView *folderview)
2933 FolderItem *item;
2934 GList *sel;
2935 if (info == TARGET_DUMMY) {
2936 sel = GTK_CMCLIST(folderview->ctree)->selection;
2937 if (!sel)
2938 return;
2940 item = gtk_cmctree_node_get_row_data
2941 (GTK_CMCTREE(folderview->ctree),
2942 GTK_CMCTREE_NODE(sel->data));
2943 if (item) {
2944 gchar *source = NULL;
2945 gchar *name = folder_item_get_identifier(item);
2946 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", name);
2947 g_free(name);
2948 gtk_selection_data_set(selection_data,
2949 gtk_selection_data_get_target(selection_data), 8,
2950 source, strlen(source));
2952 } else {
2953 g_warning("unknown info %d", info);
2957 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2959 FolderUpdateData *hookdata;
2960 FolderView *folderview;
2961 GtkWidget *ctree;
2963 hookdata = source;
2964 folderview = (FolderView *) userdata;
2965 cm_return_val_if_fail(hookdata != NULL, FALSE);
2966 cm_return_val_if_fail(folderview != NULL, FALSE);
2968 ctree = folderview->ctree;
2969 cm_return_val_if_fail(ctree != NULL, FALSE);
2971 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2972 folderview_create_folder_node(folderview, hookdata->item);
2973 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2974 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2975 NULL, folder_item_parent(hookdata->item));
2976 folderview_sort_folders(folderview, node, hookdata->folder);
2977 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2978 GtkCMCTreeNode *node;
2980 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2981 if (node != NULL) {
2982 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2983 if (folderview->selected == node)
2984 folderview->selected = NULL;
2985 if (folderview->opened == node)
2986 folderview->opened = NULL;
2988 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2989 /* do nothing, it's done by the ADD and REMOVE) */
2990 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2991 folderview_set(folderview);
2993 return FALSE;
2996 static gboolean folderview_dnd_scroll_cb(gpointer data)
2998 FolderView *folderview = (FolderView *)data;
2999 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3000 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3001 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
3002 gint max = (int)gtk_adjustment_get_upper(pos) -
3003 (int)gtk_adjustment_get_page_size(pos);
3005 if (folderview->scroll_value == 0) {
3006 folderview->scroll_timeout_id = 0;
3007 return FALSE;
3010 if (folderview->scroll_value > 0 && new_val > max) {
3011 new_val = max;
3012 } else if (folderview->scroll_value < 0 && new_val < 0) {
3013 new_val = 0;
3015 gtk_adjustment_set_value(pos, new_val);
3017 return TRUE;
3020 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
3021 GdkDragContext *context,
3022 gint x,
3023 gint y,
3024 guint time,
3025 FolderView *folderview)
3027 gint row, column;
3028 FolderItem *item = NULL, *src_item = NULL;
3029 GtkCMCTreeNode *node = NULL;
3030 gboolean acceptable = FALSE;
3031 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3032 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3033 int height = (int)gtk_adjustment_get_page_size(pos);
3034 int total_height = (int)gtk_adjustment_get_upper(pos);
3035 int vpos = (int)gtk_adjustment_get_value(pos);
3036 int offset = prefs_common.show_col_headers ? 24:0;
3037 int dist;
3039 if (gtk_cmclist_get_selection_info
3040 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3041 GtkWidget *srcwidget;
3043 if (y > height - (48 - offset) && height + vpos < total_height) {
3044 dist = -(height - (48 - offset) - y);
3045 folderview->scroll_value = 1.41f * (1+(dist / 6));
3046 } else if (y < 72 - (24 - offset) && y >= 0) {
3047 dist = 72 - (24 - offset) - y;
3048 folderview->scroll_value = -1.41f * (1+(dist / 6));
3049 } else {
3050 folderview->scroll_value = 0;
3052 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3053 folderview->scroll_timeout_id =
3054 g_timeout_add(30, folderview_dnd_scroll_cb,
3055 folderview);
3058 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3059 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3060 src_item = folderview->summaryview->folder_item;
3062 srcwidget = gtk_drag_get_source_widget(context);
3063 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3064 /* comes from summaryview */
3065 /* we are copying messages, so only accept folder items that are not
3066 the source item, are no root items and can copy messages */
3067 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3068 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3069 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3070 acceptable = TRUE;
3071 } else if (srcwidget == folderview->ctree) {
3072 /* comes from folderview */
3073 /* we are moving folder items, only accept folders that are not
3074 the source items and can copy messages and create folder items */
3075 if (item && item->folder && src_item && src_item != item &&
3076 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3077 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3078 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3079 || item->folder == src_item->folder))
3080 acceptable = TRUE;
3081 } else {
3082 /* comes from another app */
3083 /* we are adding messages, so only accept folder items that are
3084 no root items and can copy messages */
3085 if (item && item->folder && folder_item_parent(item) != NULL
3086 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3087 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3088 acceptable = TRUE;
3092 if (acceptable || (src_item && src_item == item))
3093 drag_state_start(folderview, node, item);
3095 if (acceptable) {
3096 g_signal_handlers_block_by_func
3097 (G_OBJECT(widget),
3098 G_CALLBACK(folderview_selected), folderview);
3099 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3100 g_signal_handlers_unblock_by_func
3101 (G_OBJECT(widget),
3102 G_CALLBACK(folderview_selected), folderview);
3103 gdk_drag_status(context,
3104 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3105 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3106 } else {
3107 if (folderview->opened)
3108 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3109 gdk_drag_status(context, 0, time);
3112 return acceptable;
3115 static void folderview_drag_leave_cb(GtkWidget *widget,
3116 GdkDragContext *context,
3117 guint time,
3118 FolderView *folderview)
3120 drag_state_stop(folderview);
3121 folderview->scroll_value = 0;
3122 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3125 static void free_info (gpointer stuff, gpointer data)
3127 g_free(stuff);
3130 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3131 guint time, FolderItem *item)
3133 GList *list, *tmp;
3134 GSList *msglist = NULL;
3135 list = uri_list_extract_filenames(data);
3136 if (!(item && item->folder && folder_item_parent(item) != NULL
3137 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3139 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3140 debug_print("item doesn't fit\n");
3141 return;
3143 if (!list) {
3144 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3145 debug_print("list is empty\n");
3146 return;
3148 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3149 MsgFileInfo *info = NULL;
3151 if (file_is_email((gchar *)tmp->data)) {
3152 info = g_new0(MsgFileInfo, 1);
3153 info->msginfo = NULL;
3154 info->file = (gchar *)tmp->data;
3155 msglist = g_slist_prepend(msglist, info);
3156 debug_print("file is a mail\n");
3157 } else {
3158 debug_print("file isn't a mail\n");
3161 if (msglist) {
3162 msglist = g_slist_reverse(msglist);
3163 folder_item_add_msgs(item, msglist, FALSE);
3164 g_slist_foreach(msglist, free_info, NULL);
3165 g_slist_free(msglist);
3166 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3167 } else {
3168 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3170 list_free_strings_full(list);
3173 static void folderview_drag_received_cb(GtkWidget *widget,
3174 GdkDragContext *drag_context,
3175 gint x,
3176 gint y,
3177 GtkSelectionData *data,
3178 guint info,
3179 guint time,
3180 FolderView *folderview)
3182 gint row, column;
3183 FolderItem *item = NULL, *src_item;
3184 GtkCMCTreeNode *node;
3185 int offset = prefs_common.show_col_headers ? 24:0;
3187 folderview->scroll_value = 0;
3189 if (info == TARGET_DUMMY) {
3190 drag_state_stop(folderview);
3191 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3192 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3193 /* comes from summaryview */
3194 if (gtk_cmclist_get_selection_info
3195 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3196 return;
3198 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3199 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3200 src_item = folderview->summaryview->folder_item;
3202 if (item && item->no_select) {
3203 alertpanel_error(_("The destination folder can only be used to "
3204 "store subfolders."));
3205 return;
3207 /* re-check (due to acceptable possibly set for folder moves */
3208 if (!(item && item->folder && item->path && !item->no_select &&
3209 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3210 return;
3213 switch (gdk_drag_context_get_selected_action(drag_context)) {
3214 case GDK_ACTION_COPY:
3215 summary_copy_selected_to(folderview->summaryview, item);
3216 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3217 break;
3218 case GDK_ACTION_MOVE:
3219 case GDK_ACTION_DEFAULT:
3220 default:
3221 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3222 summary_copy_selected_to(folderview->summaryview, item);
3223 else
3224 summary_move_selected_to(folderview->summaryview, item);
3225 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3227 } else {
3228 /* comes from folderview */
3229 char *source;
3230 gboolean folder_is_normal = TRUE;
3231 gboolean copy = (GDK_ACTION_COPY ==
3232 gdk_drag_context_get_selected_action(drag_context));
3234 source = (char *)gtk_selection_data_get_data(data) + 17;
3235 if (gtk_cmclist_get_selection_info
3236 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3237 || *source == 0) {
3238 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3239 return;
3241 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3242 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3243 src_item = folder_find_item_from_identifier(source);
3245 folder_is_normal =
3246 src_item != NULL &&
3247 src_item->stype == F_NORMAL &&
3248 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3249 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3250 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3251 !folder_has_parent_of_type(src_item, F_TRASH);
3252 if (!item || !src_item || !folder_is_normal) {
3253 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3254 return;
3257 folderview_move_folder(folderview, src_item, item, copy);
3258 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3260 folderview->nodes_to_recollapse = NULL;
3261 } else if (info == TARGET_MAIL_URI_LIST) {
3262 if (gtk_cmclist_get_selection_info
3263 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3264 return;
3266 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3267 if (!node) {
3268 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3269 debug_print("no node\n");
3270 return;
3272 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3273 if (!item) {
3274 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3275 debug_print("no item\n");
3276 return;
3278 folderview_finish_dnd(gtk_selection_data_get_data(data),
3279 drag_context, time, item);
3283 static void folderview_drag_end_cb(GtkWidget *widget,
3284 GdkDragContext *drag_context,
3285 FolderView *folderview)
3287 drag_state_stop(folderview);
3288 folderview->scroll_value = 0;
3289 g_slist_free(folderview->nodes_to_recollapse);
3290 folderview->nodes_to_recollapse = NULL;
3293 void folderview_register_popup(FolderViewPopup *fpopup)
3295 GList *folderviews;
3297 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3298 FolderView *folderview = folderviews->data;
3299 GtkActionGroup *factory;
3301 factory = create_action_group(folderview, fpopup);
3302 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3304 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3307 void folderview_unregister_popup(FolderViewPopup *fpopup)
3309 GList *folderviews;
3312 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3313 FolderView *folderview = folderviews->data;
3315 g_hash_table_remove(folderview->popups, fpopup->klass);
3317 g_hash_table_remove(folderview_popups, fpopup->klass);
3320 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3322 g_return_if_fail(folderview != NULL);
3323 g_return_if_fail(item != NULL);
3325 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3326 g_return_if_fail(ctree != NULL);
3328 GtkCMCTreeNode *node =
3329 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3330 g_return_if_fail(node != NULL);
3332 gtk_cmctree_remove_node(ctree, node);
3335 void folderview_freeze(FolderView *folderview)
3337 if (folderview)
3338 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3341 void folderview_thaw(FolderView *folderview)
3343 if (folderview)
3344 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3347 void folderview_grab_focus(FolderView *folderview)
3349 if (folderview)
3350 gtk_widget_grab_focus(folderview->ctree);
3353 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3354 gpointer data)
3356 prefs_folder_column_open();
3359 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3360 GdkEvent *_event,
3361 gpointer user_data)
3363 GdkEventButton *event = (GdkEventButton *)_event;
3364 FolderView *folderview = (FolderView *)user_data;
3366 cm_return_val_if_fail(folderview != NULL, FALSE);
3368 /* Only handle single button presses. */
3369 if (event->type == GDK_2BUTTON_PRESS ||
3370 event->type == GDK_3BUTTON_PRESS)
3371 return FALSE;
3373 /* Handle right-click for context menu */
3374 if (event->button == 3) {
3375 gtk_menu_popup_at_pointer(GTK_MENU(folderview->headerpopupmenu), NULL);
3376 return TRUE;
3379 return FALSE;