add an easy way to open any folder on start-up
[claws.git] / src / folderview.c
blob73425b348e18b0b42f000dd733fff40fa1e71652
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 == folder->trash || item == special_trash
2004 || folder_has_parent_of_type(item, F_TRASH)) {
2005 GSList *msglist = folder_item_get_msg_list(item);
2006 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2007 procmsg_msg_list_free(msglist);
2009 if (item == folder->queue || item == special_queue
2010 || folder_has_parent_of_type(item, F_QUEUE)) {
2011 GSList *msglist = folder_item_get_msg_list(item);
2012 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2013 procmsg_msg_list_free(msglist);
2015 #undef SET_SENS
2017 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2018 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2019 g_signal_connect(G_OBJECT(popup), "selection_done",
2020 G_CALLBACK(folderview_popup_close),
2021 folderview);
2022 gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL);
2025 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2026 FolderView *folderview)
2028 GtkCMCList *clist = GTK_CMCLIST(ctree);
2029 gint prev_row = -1, row = -1, column = -1;
2031 if (!event) return FALSE;
2032 if (event->window != clist->clist_window) return FALSE;
2034 if (event->button == 1 || event->button == 2) {
2035 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2036 folderview->open_folder = TRUE;
2038 if (event->type == GDK_2BUTTON_PRESS) {
2039 if (clist->selection) {
2040 GtkCMCTreeNode *node;
2042 node = GTK_CMCTREE_NODE(clist->selection->data);
2043 if (node) {
2044 gtk_cmctree_toggle_expansion(
2045 GTK_CMCTREE(ctree),
2046 node);
2047 folderview->open_folder = FALSE;
2051 return FALSE;
2054 if (event->button == 2 || event->button == 3) {
2055 /* right clicked */
2056 if (clist->selection) {
2057 GtkCMCTreeNode *node;
2059 node = GTK_CMCTREE_NODE(clist->selection->data);
2060 if (node)
2061 prev_row = gtkut_ctree_get_nth_from_node
2062 (GTK_CMCTREE(ctree), node);
2065 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2066 &row, &column))
2067 return FALSE;
2068 if (prev_row != row) {
2069 gtk_cmclist_unselect_all(clist);
2070 if (event->button == 2)
2071 folderview_select_node
2072 (folderview,
2073 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2074 row));
2075 else
2076 gtk_cmclist_select_row(clist, row, column);
2080 if (event->button != 3) return FALSE;
2082 folderview_set_sens_and_popup_menu(folderview, row, event);
2083 return FALSE;
2086 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2087 FolderView *folderview)
2089 int row = -1, column = -1;
2091 if (!event) return FALSE;
2093 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2094 &row, &column))
2095 return FALSE;
2096 if (event->button == 1 && folderview->open_folder == FALSE &&
2097 folderview->opened != NULL) {
2098 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2099 folderview->opened);
2100 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2103 return FALSE;
2106 #define BREAK_ON_MODIFIER_KEY() \
2107 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2109 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2110 FolderView *folderview)
2112 GtkCMCTreeNode *node;
2113 FolderItem *item;
2115 if (!event) return FALSE;
2117 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2118 return FALSE;
2120 switch (event->keyval) {
2121 case GDK_KEY_Right:
2122 if (folderview->selected) {
2123 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2124 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2125 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2126 folderview->selected);
2127 else
2128 folderview_select_node(folderview,
2129 folderview->selected);
2131 break;
2132 #ifdef GENERIC_UMPC
2133 case GDK_KEY_Return:
2134 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2135 gtk_cmctree_toggle_expansion(
2136 GTK_CMCTREE(folderview->ctree),
2137 folderview->selected);
2139 break;
2140 #else
2141 case GDK_KEY_Return:
2142 case GDK_KEY_KP_Enter:
2143 if (folderview->selected)
2144 folderview_select_node(folderview, folderview->selected);
2145 break;
2146 #endif
2147 case GDK_KEY_space:
2148 BREAK_ON_MODIFIER_KEY();
2149 if (folderview->selected) {
2150 if (folderview->opened == folderview->selected &&
2151 (!folderview->summaryview->folder_item ||
2152 folderview->summaryview->folder_item->total_msgs == 0))
2153 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2154 else
2155 folderview_select_node(folderview,
2156 folderview->selected);
2158 break;
2159 case GDK_KEY_Left:
2160 if (folderview->selected) {
2161 /* If the folder is expanded and can be collapsed, do that... */
2162 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2163 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2164 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2165 folderview->selected);
2166 } else {
2167 /* ...otherwise, move cursor to its parent node. */
2168 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2169 folderview->selected))) {
2170 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2171 NULL, folder_item_parent(item)))) {
2172 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2173 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2174 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2175 node, -1, 0, 0);
2180 break;
2181 case GDK_KEY_Home:
2182 case GDK_KEY_End:
2183 if (event->keyval == GDK_KEY_Home)
2184 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2185 else
2186 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2187 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2189 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2191 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2192 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2193 node, -1, 0, 0);
2194 break;
2195 default:
2196 break;
2199 return FALSE;
2202 typedef struct _PostponedSelectData
2204 GtkCMCTree *ctree;
2205 GtkCMCTreeNode *row;
2206 gint column;
2207 FolderView *folderview;
2208 } PostponedSelectData;
2210 static gboolean postpone_select(void *data)
2212 PostponedSelectData *psdata = (PostponedSelectData *)data;
2213 debug_print("trying again\n");
2215 psdata->folderview->postpone_select_id = 0;
2216 psdata->folderview->open_folder = TRUE;
2217 main_window_cursor_normal(psdata->folderview->mainwin);
2218 STATUSBAR_POP(psdata->folderview->mainwin);
2219 folderview_selected(psdata->ctree, psdata->row,
2220 psdata->column, psdata->folderview);
2221 g_free(psdata);
2222 return FALSE;
2225 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2227 if (folderview->opened) {
2228 if (dirty) {
2229 folderview->opened = NULL;
2230 return;
2233 FolderItem *olditem =
2234 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2235 folderview->opened);
2236 if (olditem) {
2237 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2238 olditem->path ? olditem->path:olditem->name);
2239 /* will be null if we just moved the previously opened folder */
2240 STATUSBAR_PUSH(folderview->mainwin, buf);
2241 main_window_cursor_wait(folderview->mainwin);
2242 g_free(buf);
2243 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2244 summary_show(folderview->summaryview, NULL, FALSE);
2245 folder_item_close(olditem);
2246 main_window_cursor_normal(folderview->mainwin);
2247 STATUSBAR_POP(folderview->mainwin);
2248 if (olditem->folder->klass->item_closed)
2249 olditem->folder->klass->item_closed(olditem);
2254 if (folderview->opened &&
2255 !GTK_CMCTREE_ROW(folderview->opened)->children)
2256 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2258 folderview->opened = NULL;
2260 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2261 gint column, FolderView *folderview)
2263 GdkDisplay *display;
2264 GdkSeat *seat;
2265 GdkDevice *device;
2266 static gboolean can_select = TRUE; /* exclusive lock */
2267 gboolean opened;
2268 FolderItem *item;
2269 gchar *buf;
2270 int res = 0;
2271 GtkCMCTreeNode *old_opened = folderview->opened;
2272 START_TIMING("");
2273 folderview->selected = row;
2275 display = gdk_display_get_default();
2276 seat = gdk_display_get_default_seat(display);
2277 device = gdk_seat_get_pointer(seat);
2279 debug_print("newly selected %p, opened %p\n", folderview->selected,
2280 folderview->opened);
2281 if (folderview->opened == row) {
2282 folderview->open_folder = FALSE;
2283 END_TIMING();
2284 return;
2287 item = gtk_cmctree_node_get_row_data(ctree, row);
2288 if (!item) {
2289 END_TIMING();
2290 folderview->open_folder = FALSE;
2291 return;
2294 if (!can_select || summary_is_locked(folderview->summaryview)) {
2295 if (folderview->opened) {
2296 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2297 gtk_cmctree_select(ctree, folderview->opened);
2299 folderview->open_folder = FALSE;
2300 END_TIMING();
2301 return;
2304 if (!folderview->open_folder) {
2305 END_TIMING();
2306 return;
2309 can_select = FALSE;
2311 /* Save cache for old folder */
2312 /* We don't want to lose all caches if app crashes */
2313 /* Resets folderview->opened to NULL */
2314 folderview_close_opened(folderview, FALSE);
2316 /* CLAWS: set compose button type: news folder items
2317 * always have a news folder as parent */
2318 if (item->folder)
2319 toolbar_set_compose_button
2320 (folderview->mainwin->toolbar,
2321 FOLDER_TYPE(item->folder) == F_NEWS ?
2322 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2324 if (item->path)
2325 debug_print("Folder %s is selected\n", item->path);
2327 if (!GTK_CMCTREE_ROW(row)->children)
2328 gtk_cmctree_expand(ctree, row);
2330 /* ungrab the mouse event */
2331 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2332 gtk_grab_remove(GTK_WIDGET(ctree));
2333 if (gdk_display_device_is_grabbed(display, device))
2334 gdk_seat_ungrab(seat);
2337 /* Open Folder */
2338 /* TODO: wwp: avoid displaying (null) in the status bar */
2339 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2340 item->path : "(null)");
2341 debug_print("%s\n", buf);
2342 STATUSBAR_PUSH(folderview->mainwin, buf);
2343 g_free(buf);
2345 main_window_cursor_wait(folderview->mainwin);
2347 if (folderview->scanning_folder == item->folder) {
2348 res = -2;
2349 } else {
2350 res = folder_item_open(item);
2353 if (res == -1 && item->no_select == FALSE) {
2354 main_window_cursor_normal(folderview->mainwin);
2355 STATUSBAR_POP(folderview->mainwin);
2357 alertpanel_error(_("Folder could not be opened."));
2359 folderview->open_folder = FALSE;
2360 can_select = TRUE;
2361 END_TIMING();
2362 return;
2363 } else if (res == -2 && item->no_select == FALSE) {
2364 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2365 data->ctree = ctree;
2366 data->row = row;
2367 data->column = column;
2368 data->folderview = folderview;
2369 debug_print("postponing open of %s till end of scan\n",
2370 item->path ? item->path:item->name);
2371 folderview->open_folder = FALSE;
2372 can_select = TRUE;
2373 if (folderview->postpone_select_id != 0)
2374 g_source_remove(folderview->postpone_select_id);
2375 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2376 END_TIMING();
2377 return;
2380 main_window_cursor_normal(folderview->mainwin);
2382 /* Show messages */
2383 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2384 opened = summary_show(folderview->summaryview, item, FALSE);
2386 folder_clean_cache_memory(item);
2388 if (!opened) {
2389 gtkut_ctree_set_focus_row(ctree, old_opened);
2390 gtk_cmctree_select(ctree, old_opened);
2391 folderview->opened = old_opened;
2392 } else {
2393 folderview->opened = row;
2394 if (gtk_cmctree_node_is_visible(ctree, row)
2395 != GTK_VISIBILITY_FULL)
2396 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2399 STATUSBAR_POP(folderview->mainwin);
2401 folderview->open_folder = FALSE;
2402 can_select = TRUE;
2403 END_TIMING();
2406 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2407 FolderView *folderview)
2409 FolderItem *item;
2411 item = gtk_cmctree_node_get_row_data(ctree, node);
2412 cm_return_if_fail(item != NULL);
2413 item->collapsed = FALSE;
2414 folderview_update_node(folderview, node);
2417 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2418 FolderView *folderview)
2420 FolderItem *item;
2422 item = gtk_cmctree_node_get_row_data(ctree, node);
2423 cm_return_if_fail(item != NULL);
2424 item->collapsed = TRUE;
2425 folderview_update_node(folderview, node);
2428 static void folderview_popup_close(GtkMenuShell *menu_shell,
2429 FolderView *folderview)
2431 if (!folderview->opened) return;
2433 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2436 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2437 FolderView *folderview)
2439 FolderColumnType type = folderview->col_state[column].type;
2441 prefs_common.folder_col_size[type] = width;
2444 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2446 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2447 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2448 GtkCMCTreeNode *node, *parent_node;
2449 gint *col_pos = folderview->col_pos;
2450 FolderItemUpdateData hookdata;
2452 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2453 if (parent_node == NULL)
2454 return;
2456 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2458 text[col_pos[F_COL_FOLDER]] = item->name;
2459 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2460 FOLDER_SPACING,
2461 folderxpm,
2462 folderopenxpm,
2463 FALSE, FALSE);
2464 gtk_cmctree_expand(ctree, parent_node);
2465 gtk_cmctree_node_set_row_data(ctree, node, item);
2466 folderview_sort_folders(folderview, parent_node, item->folder);
2468 hookdata.item = item;
2469 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2470 hookdata.msg = NULL;
2471 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2473 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2476 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2478 FolderView *folderview = (FolderView *)data;
2479 FolderItem *item;
2480 GSList *mlist = NULL;
2481 GSList *cur = NULL;
2482 FolderItem *special_trash = NULL;
2483 PrefsAccount *ac;
2485 if (!folderview->selected) return;
2486 item = folderview_get_selected_item(folderview);
2487 cm_return_if_fail(item != NULL);
2488 cm_return_if_fail(item->folder != NULL);
2490 if (NULL != (ac = account_find_from_item(item)))
2491 special_trash = account_get_special_folder(ac, F_TRASH);
2493 if (item != item->folder->trash && item != special_trash
2494 && !folder_has_parent_of_type(item, F_TRASH)) return;
2496 if (prefs_common.ask_on_clean) {
2497 if (alertpanel(_("Empty trash"),
2498 _("Delete all messages in trash?"),
2499 NULL, _("_Cancel"), NULL, _("_Empty trash"), NULL, NULL,
2500 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2501 return;
2504 mlist = folder_item_get_msg_list(item);
2506 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2507 MsgInfo * msginfo = (MsgInfo *) cur->data;
2508 if (MSG_IS_LOCKED(msginfo->flags))
2509 continue;
2510 /* is it partially received? (partial_recv isn't cached) */
2511 if (msginfo->total_size != 0 &&
2512 msginfo->size != (off_t)msginfo->total_size)
2513 partial_mark_for_delete(msginfo);
2515 procmsg_msg_list_free(mlist);
2517 folder_item_remove_all_msg(item);
2520 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2522 FolderView *folderview = (FolderView *)data;
2523 FolderItem *item;
2524 FolderItem *special_queue = NULL;
2525 PrefsAccount *ac;
2526 gchar *errstr = NULL;
2528 if (!folderview->selected) return;
2529 item = folderview_get_selected_item(folderview);
2530 cm_return_if_fail(item != NULL);
2531 cm_return_if_fail(item->folder != NULL);
2533 if (NULL != (ac = account_find_from_item(item)))
2534 special_queue = account_get_special_folder(ac, F_QUEUE);
2536 if (item != item->folder->queue && item != special_queue
2537 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2539 if (procmsg_queue_is_empty(item))
2540 return;
2542 if (prefs_common.work_offline)
2543 if (alertpanel(_("Offline warning"),
2544 _("You're working offline. Override?"),
2545 NULL, _("_No"), NULL, _("_Yes"),
2546 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2547 return;
2549 /* ask for confirmation before sending queued messages only
2550 in online mode and if there is at least one message queued
2551 in any of the folder queue
2553 if (prefs_common.confirm_send_queued_messages) {
2554 if (!prefs_common.work_offline) {
2555 if (alertpanel(_("Send queued messages"),
2556 _("Send all queued messages?"),
2557 NULL, _("_Cancel"), NULL, _("_Send"),
2558 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2559 return;
2563 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2564 if (!errstr)
2565 alertpanel_error_log(_("Some errors occurred while "
2566 "sending queued messages."));
2567 else {
2568 alertpanel_error_log(_("Some errors occurred "
2569 "while sending queued messages:\n%s"), errstr);
2570 g_free(errstr);
2575 static void folderview_search_cb(GtkAction *action, gpointer data)
2577 FolderView *folderview = (FolderView *)data;
2578 summary_search(folderview->summaryview);
2581 static void folderview_startup_folder_cb(GtkAction *action, gpointer data)
2583 FolderView *folderview = (FolderView *)data;
2584 FolderItem *item;
2586 if (!folderview->selected) return;
2588 item = folderview_get_selected_item(folderview);
2590 prefs_common.goto_last_folder_on_startup = FALSE;
2591 prefs_common.goto_folder_on_startup = TRUE;
2592 prefs_common.startup_folder = folder_item_get_identifier(item);
2595 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2597 FolderView *folderview = (FolderView *)data;
2598 FolderItem *item;
2600 if (!folderview->selected) return;
2602 item = folderview_get_selected_item(folderview);
2604 folderview_run_processing(item);
2607 void folderview_run_processing(FolderItem *item)
2609 cm_return_if_fail(item != NULL);
2610 cm_return_if_fail(item->folder != NULL);
2612 item->processing_pending = TRUE;
2613 folder_item_apply_processing(item);
2614 item->processing_pending = FALSE;
2617 static void folderview_property_cb(GtkAction *action, gpointer data)
2619 FolderView *folderview = (FolderView *)data;
2620 FolderItem *item;
2622 if (!folderview->selected) return;
2624 item = folderview_get_selected_item(folderview);
2625 cm_return_if_fail(item != NULL);
2626 cm_return_if_fail(item->folder != NULL);
2628 prefs_folder_item_open(item);
2631 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2633 GSList *list = NULL;
2634 GSList *done = NULL;
2635 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2637 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2638 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2639 && list->data != node) {
2640 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2641 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2644 for (list = done; list != NULL; list = g_slist_next(list)) {
2645 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2646 list->data);
2648 g_slist_free(done);
2651 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2652 FolderItem *to_folder, gboolean copy)
2654 FolderItem *new_folder = NULL;
2655 gchar *buf;
2656 gint status;
2658 cm_return_if_fail(folderview != NULL);
2659 cm_return_if_fail(from_folder != NULL);
2660 cm_return_if_fail(to_folder != NULL);
2662 if (prefs_common.warn_dnd) {
2663 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2664 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2665 from_folder->name, to_folder->name);
2666 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2667 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
2668 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
2669 g_free(buf);
2671 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2672 return;
2673 else if (status & G_ALERTDISABLE)
2674 prefs_common.warn_dnd = FALSE;
2677 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2678 from_folder->name, to_folder->name);
2679 STATUSBAR_PUSH(folderview->mainwin, buf);
2680 g_free(buf);
2681 summary_clear_all(folderview->summaryview);
2682 folderview->opened = NULL;
2683 folderview->selected = NULL;
2684 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2685 inc_lock();
2686 main_window_cursor_wait(folderview->mainwin);
2688 statusbar_verbosity_set(FALSE);
2689 folder_item_update_freeze();
2690 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2691 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2692 statusbar_verbosity_set(FALSE);
2693 main_window_cursor_normal(folderview->mainwin);
2694 STATUSBAR_POP(folderview->mainwin);
2695 folder_item_update_thaw();
2696 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2698 folderview_sort_folders(folderview,
2699 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2700 NULL, to_folder), new_folder->folder);
2701 folderview_select(folderview, new_folder);
2702 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2703 } else {
2704 statusbar_verbosity_set(FALSE);
2705 main_window_cursor_normal(folderview->mainwin);
2706 STATUSBAR_POP(folderview->mainwin);
2707 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2708 folder_item_update_thaw();
2709 switch (status) {
2710 case F_MOVE_FAILED_DEST_IS_PARENT:
2711 alertpanel_error(_("Source and destination are the same."));
2712 break;
2713 case F_MOVE_FAILED_DEST_IS_CHILD:
2714 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2715 _("Can't move a folder to one of its children."));
2716 break;
2717 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2718 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2719 break;
2720 default:
2721 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2722 break;
2725 inc_unlock();
2726 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2729 static gint folderview_clist_compare(GtkCMCList *clist,
2730 gconstpointer ptr1, gconstpointer ptr2)
2732 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2733 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2735 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2737 return item1->order - item2->order;
2740 // if only one folder has an order it comes first
2741 if (item1->order > 0)
2743 return -1;
2745 if (item2->order > 0)
2747 return 1;
2750 if (!item1->name)
2751 return (item2->name != NULL);
2752 if (!item2->name)
2753 return -1;
2755 return g_utf8_collate(item1->name, item2->name);
2758 static void folderview_processing_cb(GtkAction *action, gpointer data)
2760 FolderView *folderview = (FolderView *)data;
2761 FolderItem *item;
2762 gchar *id, *title;
2764 if (!folderview->selected) return;
2766 item = folderview_get_selected_item(folderview);
2767 cm_return_if_fail(item != NULL);
2768 cm_return_if_fail(item->folder != NULL);
2770 id = folder_item_get_identifier(item);
2771 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2772 g_free (id);
2774 prefs_filtering_open(&item->prefs->processing, title,
2775 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2776 g_free (title);
2779 void folderview_set_target_folder_color(GdkRGBA color_op)
2781 GList *list;
2782 FolderView *folderview;
2784 for (list = folderview_list; list != NULL; list = list->next) {
2785 folderview = (FolderView *)list->data;
2786 folderview->color_op = color_op;
2790 static gchar *last_smallfont = NULL;
2791 static gchar *last_normalfont = NULL;
2792 static gchar *last_boldfont = NULL;
2793 static gboolean last_derive = 0;
2795 void folderview_reinit_fonts(FolderView *folderview)
2797 /* force reinit */
2798 g_free(last_smallfont);
2799 last_smallfont = NULL;
2800 g_free(last_normalfont);
2801 last_normalfont = NULL;
2802 g_free(last_boldfont);
2803 last_boldfont = NULL;
2806 void folderview_reflect_prefs(void)
2808 gboolean update_font = FALSE;
2809 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2810 FolderItem *item = folderview_get_selected_item(folderview);
2811 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2812 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2813 gint height = gtk_adjustment_get_value(pos);
2815 folderview->color_new = prefs_common.color[COL_NEW];
2816 folderview->color_op = prefs_common.color[COL_TGT_FOLDER];
2818 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2819 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2820 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2821 last_derive != prefs_common.derive_from_normal_font)
2822 update_font = TRUE;
2824 if (!update_font)
2825 return;
2827 g_free(last_smallfont);
2828 last_smallfont = g_strdup(SMALL_FONT);
2829 g_free(last_normalfont);
2830 last_normalfont = g_strdup(NORMAL_FONT);
2831 g_free(last_boldfont);
2832 last_boldfont = g_strdup(BOLD_FONT);
2833 last_derive = prefs_common.derive_from_normal_font;
2835 folderview_set_fonts(folderview);
2837 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2838 folderview_column_set_titles(folderview);
2839 folderview_set_all();
2841 g_signal_handlers_block_by_func
2842 (G_OBJECT(folderview->ctree),
2843 G_CALLBACK(folderview_selected), folderview);
2845 if (item) {
2846 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2847 GTK_CMCTREE(folderview->ctree), NULL, item);
2849 folderview_select(folderview, item);
2850 folderview->open_folder = FALSE;
2851 folderview->selected = node;
2854 g_signal_handlers_unblock_by_func
2855 (G_OBJECT(folderview->ctree),
2856 G_CALLBACK(folderview_selected), folderview);
2858 pos = gtk_scrolled_window_get_vadjustment(
2859 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2860 gtk_adjustment_set_value(pos, height);
2861 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2864 static void drag_state_stop(FolderView *folderview)
2866 if (folderview->drag_timer_id)
2867 g_source_remove(folderview->drag_timer_id);
2868 folderview->drag_timer_id = 0;
2869 folderview->drag_node = NULL;
2872 static gboolean folderview_defer_expand(FolderView *folderview)
2874 if (folderview->drag_node) {
2875 folderview_recollapse_nodes(folderview, folderview->drag_node);
2876 if (folderview->drag_item->collapsed) {
2877 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2878 folderview->nodes_to_recollapse = g_slist_append
2879 (folderview->nodes_to_recollapse, folderview->drag_node);
2882 folderview->drag_item = NULL;
2883 folderview->drag_timer_id = 0;
2884 return FALSE;
2887 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2889 /* the idea is that we call drag_state_start() whenever we want expansion to
2890 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2891 * we need to call drag_state_stop() */
2892 drag_state_stop(folderview);
2893 /* request expansion */
2894 if (0 != (folderview->drag_timer_id = g_timeout_add
2895 (prefs_common.hover_timeout,
2896 (GSourceFunc)folderview_defer_expand,
2897 folderview))) {
2898 folderview->drag_node = node;
2899 folderview->drag_item = item;
2902 #ifndef GENERIC_UMPC
2903 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2904 FolderView *folderview)
2906 GdkDragContext *context;
2908 cm_return_if_fail(folderview != NULL);
2909 if (folderview->selected == NULL) return;
2910 if (folderview->nodes_to_recollapse)
2911 g_slist_free(folderview->nodes_to_recollapse);
2912 folderview->nodes_to_recollapse = NULL;
2913 context = gtk_drag_begin_with_coordinates(widget, folderview->target_list,
2914 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
2915 -1, -1);
2916 gtk_drag_set_icon_default(context);
2918 #endif
2919 static void folderview_drag_data_get(GtkWidget *widget,
2920 GdkDragContext *drag_context,
2921 GtkSelectionData *selection_data,
2922 guint info,
2923 guint time,
2924 FolderView *folderview)
2926 FolderItem *item;
2927 GList *sel;
2928 if (info == TARGET_DUMMY) {
2929 sel = GTK_CMCLIST(folderview->ctree)->selection;
2930 if (!sel)
2931 return;
2933 item = gtk_cmctree_node_get_row_data
2934 (GTK_CMCTREE(folderview->ctree),
2935 GTK_CMCTREE_NODE(sel->data));
2936 if (item) {
2937 gchar *source = NULL;
2938 gchar *name = folder_item_get_identifier(item);
2939 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", name);
2940 g_free(name);
2941 gtk_selection_data_set(selection_data,
2942 gtk_selection_data_get_target(selection_data), 8,
2943 source, strlen(source));
2945 } else {
2946 g_warning("unknown info %d", info);
2950 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2952 FolderUpdateData *hookdata;
2953 FolderView *folderview;
2954 GtkWidget *ctree;
2956 hookdata = source;
2957 folderview = (FolderView *) userdata;
2958 cm_return_val_if_fail(hookdata != NULL, FALSE);
2959 cm_return_val_if_fail(folderview != NULL, FALSE);
2961 ctree = folderview->ctree;
2962 cm_return_val_if_fail(ctree != NULL, FALSE);
2964 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2965 folderview_create_folder_node(folderview, hookdata->item);
2966 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2967 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2968 NULL, folder_item_parent(hookdata->item));
2969 folderview_sort_folders(folderview, node, hookdata->folder);
2970 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2971 GtkCMCTreeNode *node;
2973 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2974 if (node != NULL) {
2975 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2976 if (folderview->selected == node)
2977 folderview->selected = NULL;
2978 if (folderview->opened == node)
2979 folderview->opened = NULL;
2981 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2982 /* do nothing, it's done by the ADD and REMOVE) */
2983 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2984 folderview_set(folderview);
2986 return FALSE;
2989 static gboolean folderview_dnd_scroll_cb(gpointer data)
2991 FolderView *folderview = (FolderView *)data;
2992 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2993 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2994 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2995 gint max = (int)gtk_adjustment_get_upper(pos) -
2996 (int)gtk_adjustment_get_page_size(pos);
2998 if (folderview->scroll_value == 0) {
2999 folderview->scroll_timeout_id = 0;
3000 return FALSE;
3003 if (folderview->scroll_value > 0 && new_val > max) {
3004 new_val = max;
3005 } else if (folderview->scroll_value < 0 && new_val < 0) {
3006 new_val = 0;
3008 gtk_adjustment_set_value(pos, new_val);
3010 return TRUE;
3013 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
3014 GdkDragContext *context,
3015 gint x,
3016 gint y,
3017 guint time,
3018 FolderView *folderview)
3020 gint row, column;
3021 FolderItem *item = NULL, *src_item = NULL;
3022 GtkCMCTreeNode *node = NULL;
3023 gboolean acceptable = FALSE;
3024 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3025 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3026 int height = (int)gtk_adjustment_get_page_size(pos);
3027 int total_height = (int)gtk_adjustment_get_upper(pos);
3028 int vpos = (int)gtk_adjustment_get_value(pos);
3029 int offset = prefs_common.show_col_headers ? 24:0;
3030 int dist;
3032 if (gtk_cmclist_get_selection_info
3033 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3034 GtkWidget *srcwidget;
3036 if (y > height - (48 - offset) && height + vpos < total_height) {
3037 dist = -(height - (48 - offset) - y);
3038 folderview->scroll_value = 1.41f * (1+(dist / 6));
3039 } else if (y < 72 - (24 - offset) && y >= 0) {
3040 dist = 72 - (24 - offset) - y;
3041 folderview->scroll_value = -1.41f * (1+(dist / 6));
3042 } else {
3043 folderview->scroll_value = 0;
3045 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3046 folderview->scroll_timeout_id =
3047 g_timeout_add(30, folderview_dnd_scroll_cb,
3048 folderview);
3051 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3052 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3053 src_item = folderview->summaryview->folder_item;
3055 srcwidget = gtk_drag_get_source_widget(context);
3056 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3057 /* comes from summaryview */
3058 /* we are copying messages, so only accept folder items that are not
3059 the source item, are no root items and can copy messages */
3060 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3061 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3062 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3063 acceptable = TRUE;
3064 } else if (srcwidget == folderview->ctree) {
3065 /* comes from folderview */
3066 /* we are moving folder items, only accept folders that are not
3067 the source items and can copy messages and create folder items */
3068 if (item && item->folder && src_item && src_item != item &&
3069 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3070 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3071 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3072 || item->folder == src_item->folder))
3073 acceptable = TRUE;
3074 } else {
3075 /* comes from another app */
3076 /* we are adding messages, so only accept folder items that are
3077 no root items and can copy messages */
3078 if (item && item->folder && folder_item_parent(item) != NULL
3079 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3080 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3081 acceptable = TRUE;
3085 if (acceptable || (src_item && src_item == item))
3086 drag_state_start(folderview, node, item);
3088 if (acceptable) {
3089 g_signal_handlers_block_by_func
3090 (G_OBJECT(widget),
3091 G_CALLBACK(folderview_selected), folderview);
3092 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3093 g_signal_handlers_unblock_by_func
3094 (G_OBJECT(widget),
3095 G_CALLBACK(folderview_selected), folderview);
3096 gdk_drag_status(context,
3097 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3098 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3099 } else {
3100 if (folderview->opened)
3101 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3102 gdk_drag_status(context, 0, time);
3105 return acceptable;
3108 static void folderview_drag_leave_cb(GtkWidget *widget,
3109 GdkDragContext *context,
3110 guint time,
3111 FolderView *folderview)
3113 drag_state_stop(folderview);
3114 folderview->scroll_value = 0;
3115 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3118 static void free_info (gpointer stuff, gpointer data)
3120 g_free(stuff);
3123 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3124 guint time, FolderItem *item)
3126 GList *list, *tmp;
3127 GSList *msglist = NULL;
3128 list = uri_list_extract_filenames(data);
3129 if (!(item && item->folder && folder_item_parent(item) != NULL
3130 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3132 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3133 debug_print("item doesn't fit\n");
3134 return;
3136 if (!list) {
3137 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3138 debug_print("list is empty\n");
3139 return;
3141 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3142 MsgFileInfo *info = NULL;
3144 if (file_is_email((gchar *)tmp->data)) {
3145 info = g_new0(MsgFileInfo, 1);
3146 info->msginfo = NULL;
3147 info->file = (gchar *)tmp->data;
3148 msglist = g_slist_prepend(msglist, info);
3149 debug_print("file is a mail\n");
3150 } else {
3151 debug_print("file isn't a mail\n");
3154 if (msglist) {
3155 msglist = g_slist_reverse(msglist);
3156 folder_item_add_msgs(item, msglist, FALSE);
3157 g_slist_foreach(msglist, free_info, NULL);
3158 g_slist_free(msglist);
3159 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3160 } else {
3161 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3163 list_free_strings_full(list);
3166 static void folderview_drag_received_cb(GtkWidget *widget,
3167 GdkDragContext *drag_context,
3168 gint x,
3169 gint y,
3170 GtkSelectionData *data,
3171 guint info,
3172 guint time,
3173 FolderView *folderview)
3175 gint row, column;
3176 FolderItem *item = NULL, *src_item;
3177 GtkCMCTreeNode *node;
3178 int offset = prefs_common.show_col_headers ? 24:0;
3180 folderview->scroll_value = 0;
3182 if (info == TARGET_DUMMY) {
3183 drag_state_stop(folderview);
3184 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3185 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3186 /* comes from summaryview */
3187 if (gtk_cmclist_get_selection_info
3188 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3189 return;
3191 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3192 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3193 src_item = folderview->summaryview->folder_item;
3195 if (item && item->no_select) {
3196 alertpanel_error(_("The destination folder can only be used to "
3197 "store subfolders."));
3198 return;
3200 /* re-check (due to acceptable possibly set for folder moves */
3201 if (!(item && item->folder && item->path && !item->no_select &&
3202 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3203 return;
3206 switch (gdk_drag_context_get_selected_action(drag_context)) {
3207 case GDK_ACTION_COPY:
3208 summary_copy_selected_to(folderview->summaryview, item);
3209 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3210 break;
3211 case GDK_ACTION_MOVE:
3212 case GDK_ACTION_DEFAULT:
3213 default:
3214 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3215 summary_copy_selected_to(folderview->summaryview, item);
3216 else
3217 summary_move_selected_to(folderview->summaryview, item);
3218 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3220 } else {
3221 /* comes from folderview */
3222 char *source;
3223 gboolean folder_is_normal = TRUE;
3224 gboolean copy = (GDK_ACTION_COPY ==
3225 gdk_drag_context_get_selected_action(drag_context));
3227 source = (char *)gtk_selection_data_get_data(data) + 17;
3228 if (gtk_cmclist_get_selection_info
3229 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3230 || *source == 0) {
3231 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3232 return;
3234 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3235 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3236 src_item = folder_find_item_from_identifier(source);
3238 folder_is_normal =
3239 src_item != NULL &&
3240 src_item->stype == F_NORMAL &&
3241 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3242 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3243 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3244 !folder_has_parent_of_type(src_item, F_TRASH);
3245 if (!item || !src_item || !folder_is_normal) {
3246 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3247 return;
3250 folderview_move_folder(folderview, src_item, item, copy);
3251 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3253 folderview->nodes_to_recollapse = NULL;
3254 } else if (info == TARGET_MAIL_URI_LIST) {
3255 if (gtk_cmclist_get_selection_info
3256 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3257 return;
3259 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3260 if (!node) {
3261 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3262 debug_print("no node\n");
3263 return;
3265 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3266 if (!item) {
3267 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3268 debug_print("no item\n");
3269 return;
3271 folderview_finish_dnd(gtk_selection_data_get_data(data),
3272 drag_context, time, item);
3276 static void folderview_drag_end_cb(GtkWidget *widget,
3277 GdkDragContext *drag_context,
3278 FolderView *folderview)
3280 drag_state_stop(folderview);
3281 folderview->scroll_value = 0;
3282 g_slist_free(folderview->nodes_to_recollapse);
3283 folderview->nodes_to_recollapse = NULL;
3286 void folderview_register_popup(FolderViewPopup *fpopup)
3288 GList *folderviews;
3290 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3291 FolderView *folderview = folderviews->data;
3292 GtkActionGroup *factory;
3294 factory = create_action_group(folderview, fpopup);
3295 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3297 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3300 void folderview_unregister_popup(FolderViewPopup *fpopup)
3302 GList *folderviews;
3305 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3306 FolderView *folderview = folderviews->data;
3308 g_hash_table_remove(folderview->popups, fpopup->klass);
3310 g_hash_table_remove(folderview_popups, fpopup->klass);
3313 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3315 g_return_if_fail(folderview != NULL);
3316 g_return_if_fail(item != NULL);
3318 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3319 g_return_if_fail(ctree != NULL);
3321 GtkCMCTreeNode *node =
3322 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3323 g_return_if_fail(node != NULL);
3325 gtk_cmctree_remove_node(ctree, node);
3328 void folderview_freeze(FolderView *folderview)
3330 if (folderview)
3331 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3334 void folderview_thaw(FolderView *folderview)
3336 if (folderview)
3337 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3340 void folderview_grab_focus(FolderView *folderview)
3342 if (folderview)
3343 gtk_widget_grab_focus(folderview->ctree);
3346 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3347 gpointer data)
3349 prefs_folder_column_open();
3352 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3353 GdkEvent *_event,
3354 gpointer user_data)
3356 GdkEventButton *event = (GdkEventButton *)_event;
3357 FolderView *folderview = (FolderView *)user_data;
3359 cm_return_val_if_fail(folderview != NULL, FALSE);
3361 /* Only handle single button presses. */
3362 if (event->type == GDK_2BUTTON_PRESS ||
3363 event->type == GDK_3BUTTON_PRESS)
3364 return FALSE;
3366 /* Handle right-click for context menu */
3367 if (event->button == 3) {
3368 gtk_menu_popup_at_pointer(GTK_MENU(folderview->headerpopupmenu), NULL);
3369 return TRUE;
3372 return FALSE;