Improve some sieve-related translations
[claws.git] / src / folderview.c
blobc48aefc13af7ac8683e9829de19bf658e966c9ee
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 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);
193 static void folderview_property_cb (GtkAction *action,
194 gpointer data);
196 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
197 GdkDragContext *context,
198 gint x,
199 gint y,
200 guint time,
201 FolderView *folderview);
202 static void folderview_drag_leave_cb (GtkWidget *widget,
203 GdkDragContext *context,
204 guint time,
205 FolderView *folderview);
206 static void folderview_drag_received_cb (GtkWidget *widget,
207 GdkDragContext *drag_context,
208 gint x,
209 gint y,
210 GtkSelectionData *data,
211 guint info,
212 guint time,
213 FolderView *folderview);
214 #ifndef GENERIC_UMPC
215 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
216 FolderView *folderview);
217 #endif
218 static void folderview_drag_data_get (GtkWidget *widget,
219 GdkDragContext *drag_context,
220 GtkSelectionData *selection_data,
221 guint info,
222 guint time,
223 FolderView *folderview);
224 static void folderview_drag_end_cb (GtkWidget *widget,
225 GdkDragContext *drag_context,
226 FolderView *folderview);
228 static void folderview_create_folder_node (FolderView *folderview,
229 FolderItem *item);
230 static gboolean folderview_update_folder (gpointer source,
231 gpointer userdata);
232 static gboolean folderview_update_item_claws (gpointer source,
233 gpointer data);
234 static void folderview_processing_cb(GtkAction *action, gpointer data);
235 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
236 GdkEventButton *event);
237 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
238 gpointer data);
239 static gboolean folderview_header_button_pressed(GtkWidget *widget,
240 GdkEvent *_event,
241 gpointer user_data);
243 GHashTable *folderview_popups;
245 static GtkActionEntry folderview_common_popup_entries[] =
247 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
248 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
249 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
250 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
251 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
252 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
253 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
254 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
255 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
256 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
257 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
258 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
262 static GtkActionEntry folderview_header_popup_entries[] =
264 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL },
265 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) }
268 GtkTargetEntry folderview_drag_types[] =
270 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
271 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
274 void folderview_initialize(void)
276 FolderViewPopup *fpopup;
278 fpopup = g_new0(FolderViewPopup, 1);
280 fpopup->klass = "common";
281 fpopup->path = "<CommonFolder>";
282 fpopup->entries = folderview_common_popup_entries;
283 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
284 fpopup->set_sensitivity = NULL;
286 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
287 g_hash_table_insert(folderview_popups, "common", fpopup);
290 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
292 FolderViewPopup *fpopup_common;
293 GtkActionGroup *action_group;
295 action_group = cm_menu_create_action_group(
296 fpopup->path,
297 fpopup->entries, fpopup->n_entries,
298 (gpointer)folderview);
300 if (fpopup->toggle_entries)
301 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
302 fpopup->n_toggle_entries,
303 (gpointer)folderview);
304 if (fpopup->radio_entries)
305 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
306 fpopup->n_radio_entries, fpopup->radio_default,
307 G_CALLBACK(fpopup->radio_callback),
308 (gpointer)folderview);
310 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
311 if (fpopup_common != fpopup) {
312 gtk_action_group_add_actions(action_group, fpopup_common->entries,
313 fpopup_common->n_entries,
314 (gpointer)folderview);
315 if (fpopup_common->toggle_entries)
316 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
317 fpopup_common->n_toggle_entries,
318 (gpointer)folderview);
319 if (fpopup_common->radio_entries)
320 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
321 fpopup_common->n_radio_entries, fpopup_common->radio_default,
322 G_CALLBACK(fpopup_common->radio_callback),
323 (gpointer)folderview);
326 return action_group;
329 static void create_action_groups(gpointer key, gpointer value, gpointer data)
331 FolderView *folderview = data;
332 FolderViewPopup *fpopup = value;
333 GtkActionGroup *group;
335 group = create_action_group(folderview, fpopup);
336 g_hash_table_insert(folderview->popups, fpopup->klass, group);
339 static void folderview_column_set_titles(FolderView *folderview)
341 GtkWidget *ctree = folderview->ctree;
342 GtkWidget *label_folder;
343 GtkWidget *label_new;
344 GtkWidget *label_unread;
345 GtkWidget *label_total;
346 GtkWidget *hbox_folder;
347 GtkWidget *hbox_new;
348 GtkWidget *hbox_unread;
349 GtkWidget *hbox_total;
350 gint *col_pos = folderview->col_pos;
352 debug_print("setting titles...\n");
353 gtk_widget_realize(folderview->ctree);
354 gtk_widget_show_all(folderview->scrolledwin);
356 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
357 * instead text (text overflows making them unreadable and ugly) */
358 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
359 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
360 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
362 label_folder = gtk_label_new(_("Folder"));
363 label_new = gtk_image_new_from_pixbuf(newxpm);
364 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
365 label_total = gtk_image_new_from_pixbuf(readxpm);
367 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
369 hbox_folder = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
370 hbox_new = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
371 hbox_unread = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
372 hbox_total = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
374 /* left justified */
375 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
376 gtk_widget_set_halign(label_folder, GTK_ALIGN_START);
377 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
378 gtk_widget_set_halign(label_new, GTK_ALIGN_END);
379 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
380 gtk_widget_set_halign(label_unread, GTK_ALIGN_END);
381 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
382 gtk_widget_set_halign(label_total, GTK_ALIGN_END);
384 gtk_widget_show_all(hbox_folder);
385 gtk_widget_show_all(hbox_new);
386 gtk_widget_show_all(hbox_unread);
387 gtk_widget_show_all(hbox_total);
389 #ifdef GENERIC_UMPC
390 gtk_widget_set_size_request(hbox_new, -1, 20);
391 gtk_widget_set_size_request(hbox_unread, -1, 20);
392 gtk_widget_set_size_request(hbox_total, -1, 20);
393 #endif
395 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
396 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
397 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
398 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
400 #ifdef GENERIC_UMPC
401 GTK_EVENTS_FLUSH();
402 #endif
404 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
405 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
406 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
409 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
411 FolderView *folderview = (FolderView *)data;
412 GdkEventButton event;
413 if (folderview_get_selected_item(folderview) == NULL)
414 return FALSE;
416 event.button = 3;
417 event.time = gtk_get_current_event_time();
419 folderview_set_sens_and_popup_menu(folderview, -1,
420 &event);
422 return TRUE;
426 static GtkWidget *folderview_ctree_create(FolderView *folderview)
428 GtkWidget *ctree;
429 gint *col_pos;
430 FolderColumnState *col_state;
431 FolderColumnType type;
432 gchar *titles[N_FOLDER_COLS];
433 gint i;
434 GtkWidget *scrolledwin = folderview->scrolledwin;
436 debug_print("creating tree...\n");
437 memset(titles, 0, sizeof(titles));
439 col_state = prefs_folder_column_get_config();
440 memset(titles, 0, sizeof(titles));
442 col_pos = folderview->col_pos;
444 for (i = 0; i < N_FOLDER_COLS; i++) {
445 folderview->col_state[i] = col_state[i];
446 type = col_state[i].type;
447 col_pos[type] = i;
450 titles[col_pos[F_COL_FOLDER]] = _("Folder");
451 titles[col_pos[F_COL_NEW]] = _("New");
452 titles[col_pos[F_COL_UNREAD]] = _("Unread");
453 /* TRANSLATORS: This in Number sign in American style */
454 titles[col_pos[F_COL_TOTAL]] = _("#");
456 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
457 titles);
459 gtk_widget_set_name(GTK_WIDGET(ctree), "folderview_sctree");
461 if (prefs_common.show_col_headers == FALSE)
462 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
465 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
466 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
467 GTK_JUSTIFY_RIGHT);
468 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
469 col_pos[F_COL_UNREAD],
470 GTK_JUSTIFY_RIGHT);
471 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
472 col_pos[F_COL_TOTAL],
473 GTK_JUSTIFY_RIGHT);
474 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
475 GTK_CMCTREE_EXPANDER_TRIANGLE);
477 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
478 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
480 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
481 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
483 /* don't let title buttons take key focus */
484 for (i = 0; i < N_FOLDER_COLS; i++) {
485 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
486 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
487 prefs_common.folder_col_size[i]);
488 gtk_cmclist_set_column_visibility
489 (GTK_CMCLIST(ctree), i, col_state[i].visible);
491 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button),
492 "button-press-event",
493 G_CALLBACK(folderview_header_button_pressed),
494 folderview);
497 g_signal_connect(G_OBJECT(ctree), "key_press_event",
498 G_CALLBACK(folderview_key_pressed),
499 folderview);
500 g_signal_connect(G_OBJECT(ctree), "button_press_event",
501 G_CALLBACK(folderview_button_pressed),
502 folderview);
503 g_signal_connect(G_OBJECT(ctree), "popup-menu",
504 G_CALLBACK(folderview_popup_menu), folderview);
505 g_signal_connect(G_OBJECT(ctree), "button_release_event",
506 G_CALLBACK(folderview_button_released),
507 folderview);
508 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
509 G_CALLBACK(folderview_selected), folderview);
510 #ifndef GENERIC_UMPC
511 /* drag-n-dropping folders on maemo is impractical as this
512 * opens the folder almost everytime */
513 g_signal_connect(G_OBJECT(ctree), "start_drag",
514 G_CALLBACK(folderview_start_drag), folderview);
515 #endif
516 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
517 G_CALLBACK(folderview_drag_data_get),
518 folderview);
520 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
521 G_CALLBACK(folderview_tree_expanded),
522 folderview);
523 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
524 G_CALLBACK(folderview_tree_collapsed),
525 folderview);
527 g_signal_connect(G_OBJECT(ctree), "resize_column",
528 G_CALLBACK(folderview_col_resized),
529 folderview);
531 /* drop callback */
532 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
533 folderview_drag_types, 2,
534 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
535 g_signal_connect(G_OBJECT(ctree), "drag_motion",
536 G_CALLBACK(folderview_drag_motion_cb),
537 folderview);
538 g_signal_connect(G_OBJECT(ctree), "drag_leave",
539 G_CALLBACK(folderview_drag_leave_cb),
540 folderview);
541 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
542 G_CALLBACK(folderview_drag_received_cb),
543 folderview);
544 g_signal_connect(G_OBJECT(ctree), "drag_end",
545 G_CALLBACK(folderview_drag_end_cb),
546 folderview);
548 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
550 return ctree;
553 void folderview_set_column_order(FolderView *folderview)
555 GtkWidget *ctree = folderview->ctree;
556 FolderItem *item = folderview_get_selected_item(folderview);
557 FolderItem *sel_item = NULL, *op_item = NULL;
558 GtkWidget *scrolledwin = folderview->scrolledwin;
560 if (folderview->drag_timer_id != 0) {
561 g_source_remove(folderview->drag_timer_id);
562 folderview->drag_timer_id = 0;
564 if (folderview->deferred_refresh_id != 0) {
565 g_source_remove(folderview->deferred_refresh_id);
566 folderview->deferred_refresh_id = 0;
568 if (folderview->scroll_timeout_id != 0) {
569 g_source_remove(folderview->scroll_timeout_id);
570 folderview->scroll_timeout_id = 0;
572 if (folderview->postpone_select_id != 0) {
573 g_source_remove(folderview->postpone_select_id);
574 folderview->postpone_select_id = 0;
577 if (folderview->selected)
578 sel_item = folderview_get_selected_item(folderview);
579 if (folderview->opened)
580 op_item = folderview_get_opened_item(folderview);
582 debug_print("recreating tree...\n");
583 gtk_widget_destroy(folderview->ctree);
586 folderview->ctree = ctree = folderview_ctree_create(folderview);
587 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
588 GTK_CMCLIST(ctree)->hadjustment);
589 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
590 GTK_CMCLIST(ctree)->vadjustment);
591 gtk_widget_show(ctree);
593 if (sel_item)
594 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
595 if (op_item)
596 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
598 folderview_set(folderview);
599 folderview_column_set_titles(folderview);
601 folderview_select(folderview,item);
604 FolderView *folderview_create(MainWindow *mainwin)
606 FolderView *folderview;
607 GtkWidget *scrolledwin;
608 GtkWidget *ctree;
610 debug_print("Creating folder view...\n");
611 folderview = g_new0(FolderView, 1);
613 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
614 gtk_widget_set_name(GTK_WIDGET(scrolledwin), "folderview");
615 gtk_scrolled_window_set_policy
616 (GTK_SCROLLED_WINDOW(scrolledwin),
617 GTK_POLICY_AUTOMATIC,
618 prefs_common.folderview_vscrollbar_policy);
620 folderview->scrolledwin = scrolledwin;
621 ctree = folderview_ctree_create(folderview);
622 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
624 /* create popup factories */
625 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
626 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
628 gtk_action_group_add_actions(mainwin->action_group,
629 folderview_header_popup_entries,
630 G_N_ELEMENTS(folderview_header_popup_entries),
631 (gpointer)folderview);
633 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU)
634 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
636 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
637 gtk_ui_manager_get_widget(mainwin->ui_manager,
638 "/Menus/FolderViewHeaderPopup") ));
640 folderview->ctree = ctree;
642 folderview->folder_update_callback_id =
643 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
644 folderview->folder_item_update_callback_id =
645 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
647 gtk_widget_show_all(scrolledwin);
649 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
650 folderview_list = g_list_append(folderview_list, folderview);
652 folderview->drag_timer_id = 0;
653 folderview->deferred_refresh_id = 0;
654 folderview->scroll_timeout_id = 0;
655 folderview->postpone_select_id = 0;
657 return folderview;
660 static void folderview_set_fonts(FolderView *folderview)
662 PangoFontDescription *font_desc;
663 GtkWidget *ctree = folderview->ctree;
665 font_desc = pango_font_description_from_string(NORMAL_FONT);
666 if (font_desc) {
667 gtk_widget_override_font(ctree, font_desc);
668 pango_font_description_free(font_desc);
671 if (!bold_style) {
672 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
674 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
675 font_desc = pango_font_description_from_string(NORMAL_FONT);
676 if (font_desc) {
677 pango_font_description_free(bold_style->font_desc);
678 bold_style->font_desc = font_desc;
680 pango_font_description_set_weight
681 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
682 } else {
683 font_desc = pango_font_description_from_string(BOLD_FONT);
684 if (font_desc) {
685 pango_font_description_free(bold_style->font_desc);
686 bold_style->font_desc = font_desc;
692 void folderview_init(FolderView *folderview)
694 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
695 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
696 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
697 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
698 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
699 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
700 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
701 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
702 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
703 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
704 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
705 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
706 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
707 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
708 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
709 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
710 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
711 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
712 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
713 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
714 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
715 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
716 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
717 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
718 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
719 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
721 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
722 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
723 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
724 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
725 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
726 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
727 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
728 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
729 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
730 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
731 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
732 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
733 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
734 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
735 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
736 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
737 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
738 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
739 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
740 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
741 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
742 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
743 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
744 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
746 folderview_set_fonts(folderview);
749 static gboolean folderview_defer_set(gpointer data)
751 FolderView *folderview = (FolderView *)data;
752 MainWindow *mainwin = folderview->mainwin;
754 if (!mainwin)
755 return FALSE;
756 if (mainwin->lock_count)
757 return TRUE;
759 debug_print("doing deferred folderview_set now\n");
760 folderview_set(folderview);
762 folderview->deferred_refresh_id = 0;
763 return FALSE;
766 void folderview_set(FolderView *folderview)
768 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
769 MainWindow *mainwin = folderview->mainwin;
770 FolderItem *sel_item = NULL, *op_item = NULL;
772 if (!mainwin)
773 return;
775 if (mainwin->lock_count) {
776 if (folderview->deferred_refresh_id == 0)
777 folderview->deferred_refresh_id =
778 g_timeout_add(500, folderview_defer_set, folderview);
779 debug_print("deferred folderview_set\n");
780 return;
783 inc_lock();
784 debug_print("Setting folder info...\n");
785 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
787 main_window_cursor_wait(mainwin);
789 if (folderview->selected)
790 sel_item = folderview_get_selected_item(folderview);
791 if (folderview->opened)
792 op_item = folderview_get_opened_item(folderview);
794 folderview->selected = NULL;
795 folderview->opened = NULL;
797 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
798 gtk_cmclist_clear(GTK_CMCLIST(ctree));
800 folderview_set_folders(folderview);
802 if (sel_item)
803 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
804 if (op_item)
805 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
807 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
808 main_window_cursor_normal(mainwin);
809 STATUSBAR_POP(mainwin);
810 inc_unlock();
813 void folderview_set_all(void)
815 GList *list;
817 for (list = folderview_list; list != NULL; list = list->next)
818 folderview_set((FolderView *)list->data);
821 void folderview_select(FolderView *folderview, FolderItem *item)
823 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
824 GtkCMCTreeNode *node;
825 GtkCMCTreeNode *old_selected = folderview->selected;
827 if (!item) return;
829 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
830 if (node) folderview_select_node(folderview, node);
832 if (old_selected != node)
833 folder_update_op_count();
836 static void mark_all_read_cb(GtkAction *action, gpointer data)
838 mark_all_read_unread_handler(action, data, FALSE, TRUE);
841 static void mark_all_unread_cb(GtkAction *action, gpointer data)
843 mark_all_read_unread_handler(action, data, FALSE, FALSE);
846 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
848 mark_all_read_unread_handler(action, data, TRUE, TRUE);
851 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
853 mark_all_read_unread_handler(action, data, TRUE, FALSE);
856 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
857 gboolean recursive, gboolean read)
859 FolderView *folderview = (FolderView *)data;
860 FolderItem *item;
861 AlertValue val;
862 gchar *message;
863 gchar *title;
865 item = folderview_get_selected_item(folderview);
866 if (item == NULL)
867 return;
869 if (read) {
870 title = _("Mark all as read");
871 message = recursive? _("Do you really want to mark all mails in this "
872 "folder and its subfolders as read?") :
873 _("Do you really want to mark all mails in this "
874 "folder as read?");
875 } else {
876 title = _("Mark all as unread");
877 message = recursive? _("Do you really want to mark all mails in this "
878 "folder and its subfolders as unread?") :
879 _("Do you really want to mark all mails in this "
880 "folder as unread?");
882 if (prefs_common.ask_mark_all_read) {
883 val = alertpanel_full(title, message,
884 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
885 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
887 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
888 return;
889 else if (val & G_ALERTDISABLE)
890 prefs_common.ask_mark_all_read = FALSE;
893 folder_item_update_freeze();
894 if (folderview->summaryview->folder_item != item && !recursive)
895 summary_lock(folderview->summaryview);
896 else
897 summary_freeze(folderview->summaryview);
899 if (read) {
900 if (recursive)
901 folderutils_mark_all_read_recursive(item, TRUE);
902 else {
903 if (prefs_common.run_processingrules_before_mark_all)
904 folderview_run_processing(item);
905 folderutils_mark_all_read(item, TRUE);
907 } else {
908 if (recursive)
909 folderutils_mark_all_read_recursive(item, FALSE);
910 else {
911 folderutils_mark_all_read(item, FALSE);
912 if (prefs_common.run_processingrules_before_mark_all)
913 folderview_run_processing(item);
916 if (folderview->summaryview->folder_item != item && !recursive)
917 summary_unlock(folderview->summaryview);
918 else
919 summary_thaw(folderview->summaryview);
920 folder_item_update_thaw();
923 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
925 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
927 cm_return_if_fail(node != NULL);
929 if (folderview->open_folder) {
930 return;
933 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
934 gtkut_ctree_expand_parent_all(ctree, node);
936 folderview->open_folder = TRUE;
937 gtkut_ctree_set_focus_row(ctree, node);
938 gtk_cmctree_select(ctree, node);
939 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
940 if ((folderview->summaryview->folder_item &&
941 folderview->summaryview->folder_item->total_msgs > 0) ||
942 prefs_common.layout_mode == SMALL_LAYOUT)
943 summary_select_node(folderview->summaryview,
944 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
945 else
946 gtk_widget_grab_focus(folderview->ctree);
949 void folderview_unselect(FolderView *folderview)
951 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
952 gtk_cmctree_collapse
953 (GTK_CMCTREE(folderview->ctree), folderview->opened);
955 folderview->selected = folderview->opened = NULL;
958 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
959 GtkCMCTreeNode *node,
960 MsgPermFlags flag)
962 FolderItem *item;
964 if (node)
965 node = gtkut_ctree_node_next(ctree, node);
966 else
967 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
969 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
970 item = gtk_cmctree_node_get_row_data(ctree, node);
971 if (!item)
972 continue;
973 if (item->stype == F_TRASH || item->stype == F_DRAFT)
974 continue;
975 switch (flag) {
976 case MSG_UNREAD:
977 if((item->unread_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new))
978 return node;
979 break;
980 case MSG_NEW:
981 if((item->new_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new))
982 return node;
983 break;
984 case MSG_MARKED:
985 if(item->marked_msgs > 0)
986 return node;
987 break;
988 default:
989 if(item->total_msgs > 0)
990 return node;
991 break;
995 return NULL;
998 void folderview_select_next_with_flag(FolderView *folderview,
999 MsgPermFlags flag)
1001 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1002 GtkCMCTreeNode *node = NULL;
1003 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1005 switch (flag) {
1006 case MSG_UNREAD:
1007 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1008 break;
1009 case MSG_NEW:
1010 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1011 break;
1012 case MSG_MARKED:
1013 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1014 break;
1015 default:
1016 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1017 break;
1020 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1021 if (node != NULL) {
1022 folderview_select_node(folderview, node);
1023 goto out;
1026 if (!folderview->opened ||
1027 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1028 goto out;
1031 /* search again from the first node */
1032 node = folderview_find_next_with_flag(ctree, NULL, flag);
1033 if (node != NULL)
1034 folderview_select_node(folderview, node);
1036 out:
1037 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1040 FolderItem *folderview_get_selected_item(FolderView *folderview)
1042 g_return_val_if_fail(folderview != NULL, NULL);
1043 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1045 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1047 if (!folderview->selected) return NULL;
1048 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1051 FolderItem *folderview_get_opened_item(FolderView *folderview)
1053 g_return_val_if_fail(folderview != NULL, NULL);
1054 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1056 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1058 if (!folderview->opened) return NULL;
1059 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1062 static void folderview_set_folders(FolderView *folderview)
1064 GList *list;
1065 list = folder_get_list();
1067 for (; list != NULL; list = list->next) {
1068 folderview_append_folder(folderview, FOLDER(list->data));
1072 static gchar *get_scan_str(FolderItem *item)
1074 if (item->path)
1075 return g_strdup_printf(_("Scanning folder %s/%s..."),
1076 item->folder->name, item->path);
1077 else
1078 return g_strdup_printf(_("Scanning folder %s..."),
1079 item->folder->name);
1081 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1082 gpointer data)
1084 GList *list;
1085 for (list = folderview_list; list != NULL; list = list->next) {
1086 FolderView *folderview = (FolderView *)list->data;
1087 MainWindow *mainwin = folderview->mainwin;
1088 gchar *str = get_scan_str(item);
1090 STATUSBAR_PUSH(mainwin, str);
1091 STATUSBAR_POP(mainwin);
1092 g_free(str);
1096 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1098 GtkWidget *window;
1099 MainWindow *mainwin = mainwindow_get_mainwindow();
1100 FolderView *folderview = NULL;
1101 GtkAdjustment *pos = NULL;
1102 gint height = 0;
1104 cm_return_if_fail(folder != NULL);
1106 if (!folder->klass->scan_tree) return;
1108 if (rebuild &&
1109 alertpanel_full(_("Rebuild folder tree"),
1110 _("Rebuilding the folder tree will remove "
1111 "local caches. Do you want to continue?"),
1112 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
1113 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING)
1114 != G_ALERTALTERNATE) {
1115 return;
1118 inc_lock();
1119 if (rebuild)
1120 window = label_window_create(_("Rebuilding folder tree..."));
1121 else
1122 window = label_window_create(_("Scanning folder tree..."));
1124 if (mainwin)
1125 folderview = mainwin->folderview;
1127 if (folderview) {
1128 pos = gtk_scrolled_window_get_vadjustment(
1129 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1130 height = gtk_adjustment_get_value(pos);
1133 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1134 folder_scan_tree(folder, rebuild);
1135 folder_set_ui_func(folder, NULL, NULL);
1137 folderview_set_all();
1139 if (folderview) {
1140 pos = gtk_scrolled_window_get_vadjustment(
1141 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1142 gtk_adjustment_set_value(pos, height);
1144 label_window_destroy(window);
1145 inc_unlock();
1148 /** folderview_check_new()
1149 * Scan and update the folder and return the
1150 * count the number of new messages since last check.
1151 * \param folder the folder to check for new messages
1152 * \return the number of new messages since last check
1154 gint folderview_check_new(Folder *folder)
1156 GList *list;
1157 FolderItem *item;
1158 FolderView *folderview;
1159 GtkCMCTree *ctree;
1160 GtkCMCTreeNode *node;
1161 gint new_msgs = 0;
1162 gint former_new_msgs = 0;
1163 gint former_new = 0, former_unread = 0, former_total;
1165 for (list = folderview_list; list != NULL; list = list->next) {
1166 folderview = (FolderView *)list->data;
1167 ctree = GTK_CMCTREE(folderview->ctree);
1168 folderview->scanning_folder = folder;
1169 inc_lock();
1170 main_window_lock(folderview->mainwin);
1172 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1173 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1174 gchar *str = NULL;
1175 item = gtk_cmctree_node_get_row_data(ctree, node);
1176 if (!item || !item->path || !item->folder) continue;
1177 if (item->no_select) continue;
1178 if (folder && folder != item->folder) continue;
1179 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1180 if (!item->prefs->newmailcheck) continue;
1181 if (item->processing_pending == TRUE) {
1182 debug_print("skipping %s, processing pending\n",
1183 item->path ? item->path : item->name);
1184 continue;
1186 if (item->scanning != ITEM_NOT_SCANNING) {
1187 debug_print("skipping %s, scanning\n",
1188 item->path ? item->path : item->name);
1189 continue;
1192 str = get_scan_str(item);
1194 STATUSBAR_PUSH(folderview->mainwin, str);
1195 GTK_EVENTS_FLUSH();
1196 g_free(str);
1198 folderview_scan_tree_func(item->folder, item, NULL);
1199 former_new = item->new_msgs;
1200 former_unread = item->unread_msgs;
1201 former_total = item->total_msgs;
1203 if (item->folder->klass->scan_required &&
1204 (item->folder->klass->scan_required(item->folder, item) ||
1205 item->folder->inbox == item ||
1206 item->opened == TRUE ||
1207 item->processing_pending == TRUE)) {
1208 if (folder_item_scan(item) < 0) {
1209 if (folder) {
1210 summaryview_unlock(folderview->summaryview, item);
1211 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1212 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1213 item->path ? item->path:item->name);
1214 STATUSBAR_POP(folderview->mainwin);
1215 continue;
1216 } else if (!FOLDER_IS_LOCAL(folder)) {
1217 STATUSBAR_POP(folderview->mainwin);
1218 break;
1222 } else if (!item->folder->klass->scan_required) {
1223 if (folder_item_scan(item) < 0) {
1224 summaryview_unlock(folderview->summaryview, item);
1225 if (folder && !FOLDER_IS_LOCAL(folder)) {
1226 STATUSBAR_POP(folderview->mainwin);
1227 break;
1231 if (former_new != item->new_msgs ||
1232 former_unread != item->unread_msgs ||
1233 former_total != item->total_msgs)
1234 folderview_update_node(folderview, node);
1236 new_msgs += item->new_msgs;
1237 former_new_msgs += former_new;
1238 STATUSBAR_POP(folderview->mainwin);
1240 folderview->scanning_folder = NULL;
1241 main_window_unlock(folderview->mainwin);
1242 inc_unlock();
1245 folder_write_list();
1246 /* Number of new messages since last check is the just the difference
1247 * between former_new_msgs and new_msgs. If new_msgs is less than
1248 * former_new_msgs, that would mean another session accessed the folder
1249 * and the result is not well defined.
1251 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1252 return new_msgs;
1255 void folderview_check_new_all(void)
1257 GList *list;
1258 GtkWidget *window;
1259 FolderView *folderview;
1261 folderview = (FolderView *)folderview_list->data;
1263 inc_lock();
1264 main_window_lock(folderview->mainwin);
1265 window = label_window_create
1266 (_("Checking for new messages in all folders..."));
1268 list = folder_get_list();
1269 for (; list != NULL; list = list->next) {
1270 Folder *folder = list->data;
1272 folderview_check_new(folder);
1275 folder_write_list();
1276 folderview_set_all();
1278 label_window_destroy(window);
1279 main_window_unlock(folderview->mainwin);
1280 inc_unlock();
1283 static gboolean folderview_have_children_sub(FolderView *folderview,
1284 FolderItem *item,
1285 gboolean in_sub)
1287 GNode *node = NULL;
1289 if (!item || !item->folder || !item->folder->node)
1290 return FALSE;
1292 node = item->folder->node;
1294 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1295 node = node->children;
1297 if (in_sub && item->total_msgs > 0) {
1298 return TRUE;
1301 while (node != NULL) {
1302 if (node && node->data) {
1303 FolderItem *next_item = (FolderItem*) node->data;
1304 node = node->next;
1305 if (folderview_have_children_sub(folderview,
1306 next_item, TRUE))
1307 return TRUE;
1311 return FALSE;
1314 static gboolean folderview_have_children(FolderView *folderview,
1315 FolderItem *item)
1317 return folderview_have_children_sub(folderview, item, FALSE);
1320 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1321 FolderItem *item,
1322 gboolean in_sub)
1324 GNode *node = NULL;
1326 if (!item || !item->folder || !item->folder->node)
1327 return FALSE;
1329 node = item->folder->node;
1331 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1332 node = node->children;
1334 if (in_sub &&
1335 (item->new_msgs > 0 ||
1336 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1337 return TRUE;
1340 while (node != NULL) {
1341 if (node && node->data) {
1342 FolderItem *next_item = (FolderItem*) node->data;
1343 node = node->next;
1344 if (folderview_have_new_children_sub(folderview,
1345 next_item, TRUE))
1346 return TRUE;
1350 return FALSE;
1353 static gboolean folderview_have_new_children(FolderView *folderview,
1354 FolderItem *item)
1356 return folderview_have_new_children_sub(folderview, item, FALSE);
1359 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1360 FolderItem *item,
1361 gboolean in_sub)
1363 GNode *node = NULL;
1365 if (!item || !item->folder || !item->folder->node)
1366 return FALSE;
1368 node = item->folder->node;
1370 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1371 node = node->children;
1373 if (in_sub &&
1374 (item->unread_msgs > 0 ||
1375 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1376 return TRUE;
1379 while (node != NULL) {
1380 if (node && node->data) {
1381 FolderItem *next_item = (FolderItem*) node->data;
1382 node = node->next;
1383 if (folderview_have_unread_children_sub(folderview,
1384 next_item,
1385 TRUE))
1386 return TRUE;
1390 return FALSE;
1393 static gboolean folderview_have_unread_children(FolderView *folderview,
1394 FolderItem *item)
1396 return folderview_have_unread_children_sub(folderview, item, FALSE);
1399 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1400 FolderItem *item,
1401 gboolean in_sub)
1403 GNode *node = NULL;
1405 if (!item || !item->folder || !item->folder->node) {
1406 return FALSE;
1409 node = item->folder->node;
1411 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1412 node = node->children;
1414 if (in_sub &&
1415 (((item->total_msgs > 0) &&
1416 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1417 return TRUE;
1420 while (node != NULL) {
1421 if (node && node->data) {
1422 FolderItem *next_item = (FolderItem*) node->data;
1423 node = node->next;
1424 if (folderview_have_read_children_sub(folderview,
1425 next_item,
1426 TRUE)) {
1427 return TRUE;
1432 return FALSE;
1435 static gboolean folderview_have_read_children(FolderView *folderview,
1436 FolderItem *item)
1438 return folderview_have_read_children_sub(folderview, item, FALSE);
1441 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1442 FolderItem *item,
1443 gboolean in_sub)
1445 GNode *node = NULL;
1447 if (!item || !item->folder || !item->folder->node)
1448 return FALSE;
1450 node = item->folder->node;
1452 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1453 node = node->children;
1455 if (in_sub && item->search_match){
1456 return TRUE;
1459 while (node != NULL) {
1460 if (node && node->data) {
1461 FolderItem *next_item = (FolderItem*) node->data;
1462 node = node->next;
1463 if (folderview_have_matching_children_sub(folderview,
1464 next_item,
1465 TRUE))
1466 return TRUE;
1470 return FALSE;
1473 static gboolean folderview_have_matching_children(FolderView *folderview,
1474 FolderItem *item)
1476 return folderview_have_matching_children_sub(folderview, item, FALSE);
1479 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1480 FolderItem *item,
1481 gboolean in_sub)
1483 GNode *node = NULL;
1485 if (!item || !item->folder || !item->folder->node)
1486 return FALSE;
1488 node = item->folder->node;
1490 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1491 node = node->children;
1493 if (item->marked_msgs != 0) {
1494 return TRUE;
1497 while (node != NULL) {
1498 if (node && node->data) {
1499 FolderItem *next_item = (FolderItem*) node->data;
1500 node = node->next;
1501 if (folderview_have_marked_children_sub(folderview,
1502 next_item, TRUE))
1503 return TRUE;
1507 return FALSE;
1510 static gboolean folderview_have_marked_children(FolderView *folderview,
1511 FolderItem *item)
1513 return folderview_have_marked_children_sub(folderview, item, FALSE);
1516 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1518 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1519 GtkStyle *style = NULL, *prev_style;
1520 FolderItem *item;
1521 GdkRGBA black = { 0, 0, 0, 1 };
1522 GdkPixbuf *xpm, *openxpm;
1523 static GdkPixbuf *searchicon;
1524 gboolean mark = FALSE;
1525 gchar *name;
1526 gchar *str;
1527 gboolean add_unread_mark;
1528 gboolean add_sub_match_mark;
1529 gboolean use_bold, use_color;
1530 gint *col_pos = folderview->col_pos;
1531 SpecialFolderItemType stype;
1533 item = gtk_cmctree_node_get_row_data(ctree, node);
1534 cm_return_if_fail(item != NULL);
1536 if (!GTK_CMCTREE_ROW(node)->expanded)
1537 mark = folderview_have_marked_children(folderview, item);
1538 else
1539 mark = (item->marked_msgs != 0);
1541 stype = item->stype;
1542 if (stype == F_NORMAL) {
1543 if (folder_has_parent_of_type(item, F_TRASH))
1544 stype = F_TRASH;
1545 else if (folder_has_parent_of_type(item, F_DRAFT))
1546 stype = F_DRAFT;
1547 else if (folder_has_parent_of_type(item, F_OUTBOX))
1548 stype = F_OUTBOX;
1549 else if (folder_has_parent_of_type(item, F_QUEUE))
1550 stype = F_QUEUE;
1552 switch (stype) {
1553 case F_INBOX:
1554 if (item->hide_read_msgs || item->hide_read_threads) {
1555 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1556 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1557 } else {
1558 xpm = mark?m_inboxxpm:inboxxpm;
1559 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1561 break;
1562 case F_OUTBOX:
1563 if (item->hide_read_msgs || item->hide_read_threads) {
1564 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1565 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1566 } else {
1567 xpm = mark?m_outboxxpm:outboxxpm;
1568 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1570 break;
1571 case F_QUEUE:
1572 if (item->hide_read_msgs || item->hide_read_threads) {
1573 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1574 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1575 } else {
1576 xpm = mark?m_queuexpm:queuexpm;
1577 openxpm = mark?m_queueopenxpm:queueopenxpm;
1579 break;
1580 case F_TRASH:
1581 if (item->hide_read_msgs || item->hide_read_threads) {
1582 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1583 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1584 } else {
1585 xpm = mark?m_trashxpm:trashxpm;
1586 openxpm = mark?m_trashopenxpm:trashopenxpm;
1588 break;
1589 case F_DRAFT:
1590 xpm = mark?m_draftsxpm:draftsxpm;
1591 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1592 break;
1593 default:
1594 if (!item->path &&
1595 FOLDER_TYPE(item->folder) == F_IMAP &&
1596 item->folder->account->imap_subsonly) {
1597 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1598 openxpm = foldersubsopenxpm;
1599 } else if (item->no_select) {
1600 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1601 openxpm = foldernoselectopenxpm;
1602 } else if (item->hide_read_msgs || item->hide_read_threads) {
1603 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1604 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1605 } else {
1606 xpm = mark?m_folderxpm:folderxpm;
1607 openxpm = mark?m_folderopenxpm:folderopenxpm;
1611 name = folder_item_get_name(item);
1613 if (!GTK_CMCTREE_ROW(node)->expanded) {
1614 add_unread_mark = folderview_have_unread_children(
1615 folderview, item);
1616 add_sub_match_mark = folderview_have_matching_children(
1617 folderview, item);
1618 } else {
1619 add_unread_mark = FALSE;
1620 add_sub_match_mark = FALSE;
1623 if (item->search_match) {
1624 if (!searchicon) {
1625 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1626 &searchicon);
1628 xpm = openxpm = searchicon;
1631 str = NULL;
1632 if (prefs_common.display_folder_unread) {
1633 if (folder_has_parent_of_type(item, F_QUEUE)) {
1634 /* only total_msgs matters here */
1635 if (item->total_msgs > 0) {
1636 /* show total number (should be equal to the unread number)
1637 and signs if any */
1638 str = g_strdup_printf("%s (%d%s%s)",
1639 name, item->total_msgs,
1640 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1641 (item->unreadmarked_msgs > 0) ? "!" : "");
1643 } else {
1644 if (prefs_common.display_folder_unread == 1) {
1645 if (item->unread_msgs > 0) {
1646 /* show unread number and signs */
1647 str = g_strdup_printf("%s (%d%s%s)",
1648 name, item->unread_msgs,
1649 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1650 (item->unreadmarked_msgs > 0) ? "!" : "");
1652 } else {
1653 if (item->total_msgs > 0) {
1654 /* show unread number, total number and signs if any */
1655 str = g_strdup_printf("%s (%d/%d%s%s)",
1656 name, item->unread_msgs, item->total_msgs,
1657 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1658 (item->unreadmarked_msgs > 0) ? "!" : "");
1662 if ((str == NULL) &&
1663 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1664 /* no unread/total numbers, but at least one sign */
1665 str = g_strdup_printf("%s (%s%s)",
1666 name,
1667 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1668 (item->unreadmarked_msgs > 0) ? "!" : "");
1671 if (str == NULL) {
1672 /* last fallback, folder name only or with +! sign */
1673 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1674 str = g_strdup_printf("%s%s",
1675 name, " (+!)");
1676 } else if (item->unreadmarked_msgs > 0) {
1677 str = g_strdup_printf("%s%s",
1678 name, " (!)");
1679 } else if (add_sub_match_mark) {
1680 str = g_strdup_printf("%s%s",
1681 name, " (+)");
1682 } else {
1683 str = g_strdup_printf("%s", name);
1686 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1687 xpm, openxpm,
1688 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1689 g_free(str);
1690 g_free(name);
1692 if (!folder_item_parent(item)) {
1693 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1694 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1695 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1696 } else {
1697 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1698 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1699 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1702 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1703 folder_has_parent_of_type(item, F_TRASH)) {
1704 use_bold = use_color = FALSE;
1705 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1706 GSList *list = folder_item_get_msg_list(item);
1707 GSList *cur;
1708 use_bold = use_color = FALSE;
1709 for (cur = list; cur; cur = cur->next) {
1710 MsgInfo *msginfo = (MsgInfo *)cur->data;
1711 if (!MSG_IS_DELETED(msginfo->flags)) {
1712 /* highlight queue folder if there are any messages */
1713 use_bold = use_color = TRUE;
1714 break;
1717 if (!GTK_CMCTREE_ROW(node)->expanded &&
1718 use_bold == FALSE &&
1719 folderview_have_children(folderview, item))
1720 use_bold = use_color = TRUE;
1721 procmsg_msg_list_free(list);
1722 } else {
1723 /* if unread messages exist or target folder is set, print with bold font */
1724 use_bold = (item->unread_msgs > 0 || item->new_msgs > 0 || item->op_count > 0)
1725 || add_unread_mark;
1726 /* if new messages exist, print with colored letter */
1727 use_color =
1728 (item->new_msgs > 0) ||
1729 (add_unread_mark &&
1730 folderview_have_new_children(folderview, item));
1733 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1735 if (use_bold) {
1736 style = bold_style;
1737 if (item->op_count > 0)
1738 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1739 else if (use_color)
1740 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1741 else if (!gdk_rgba_equal(&item->prefs->color, &black))
1742 gtk_cmctree_node_set_foreground(ctree, node, &item->prefs->color);
1743 } else if (use_color)
1744 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1745 else if (item->op_count > 0)
1746 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1747 else if (!gdk_rgba_equal(&item->prefs->color, &black))
1748 gtk_cmctree_node_set_foreground(ctree, node, &item->prefs->color);
1750 gtk_cmctree_node_set_row_style(ctree, node, style);
1752 prev_style = gtk_cmctree_node_get_row_style(ctree, node);
1753 if (prev_style) {
1754 GtkStyle *ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
1756 style = gtk_style_copy(prev_style);
1757 style->text[GTK_STATE_NORMAL] = ctree_style->text[GTK_STATE_NORMAL];
1758 style->text[GTK_STATE_SELECTED] = ctree_style->text[GTK_STATE_SELECTED];
1759 gtk_cmctree_node_set_row_style(ctree, node, style);
1760 g_object_unref(style);
1763 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1764 folderview_update_node(folderview, node);
1767 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1769 GList *list;
1770 FolderView *folderview;
1771 GtkCMCTree *ctree;
1772 GtkCMCTreeNode *node;
1774 cm_return_if_fail(item != NULL);
1776 for (list = folderview_list; list != NULL; list = list->next) {
1777 folderview = (FolderView *)list->data;
1778 ctree = GTK_CMCTREE(folderview->ctree);
1780 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1781 if (node && item->search_match != matches) {
1782 item->search_match = matches;
1783 folderview_update_node(folderview, node);
1788 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1790 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1791 FolderView *folderview = (FolderView *)data;
1792 GtkCMCTree *ctree;
1793 GtkCMCTreeNode *node;
1794 cm_return_val_if_fail(update_info != NULL, TRUE);
1795 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1796 cm_return_val_if_fail(folderview != NULL, FALSE);
1798 ctree = GTK_CMCTREE(folderview->ctree);
1800 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1802 if (node) {
1803 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1804 folderview_update_node(folderview, node);
1806 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1807 update_info->item == folderview->summaryview->folder_item &&
1808 update_info->item != NULL)
1809 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1810 summary_show(folderview->summaryview, update_info->item, FALSE);
1813 return FALSE;
1816 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1817 GNode *gnode, GtkCMCTreeNode *cnode,
1818 gpointer data)
1820 FolderView *folderview = (FolderView *)data;
1821 FolderItem *item = FOLDER_ITEM(gnode->data);
1823 cm_return_val_if_fail(item != NULL, FALSE);
1825 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1826 folderview_update_node(folderview, cnode);
1828 return TRUE;
1831 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1832 gpointer data)
1834 FolderView *folderview = (FolderView *)data;
1835 FolderItem *item;
1837 if (GTK_CMCTREE_ROW(node)->children) {
1838 item = gtk_cmctree_node_get_row_data(ctree, node);
1839 cm_return_if_fail(item != NULL);
1841 if (!item->collapsed)
1842 gtk_cmctree_expand(ctree, node);
1843 else
1844 folderview_update_node(folderview, node);
1848 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1849 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1851 if (item) {
1852 GtkCMCTreeNode *node, *parent, *sibling;
1854 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1855 if (!node)
1856 g_warning("%s not found", item->path);
1857 else {
1858 parent = GTK_CMCTREE_ROW(node)->parent;
1859 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1860 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1861 else
1862 sibling = GTK_CMCTREE_ROW(parent)->children;
1863 while (sibling) {
1864 FolderItem *tmp;
1866 tmp = gtk_cmctree_node_get_row_data
1867 (ctree, sibling);
1868 if (tmp && tmp->stype != F_NORMAL)
1869 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1870 else
1871 break;
1873 if (node != sibling)
1874 gtk_cmctree_move(ctree, node, parent, sibling);
1877 *prev = node;
1881 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1882 Folder *folder)
1884 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1885 GtkCMCTreeNode *prev = NULL;
1887 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1888 gtk_sctree_sort_recursive(ctree, root);
1889 if (root && GTK_CMCTREE_ROW(root)->parent) {
1890 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1891 return;
1893 set_special_folder(ctree, folder->inbox, root, &prev);
1894 set_special_folder(ctree, folder->outbox, root, &prev);
1895 set_special_folder(ctree, folder->draft, root, &prev);
1896 set_special_folder(ctree, folder->queue, root, &prev);
1897 set_special_folder(ctree, folder->trash, root, &prev);
1898 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1901 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1903 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1904 GtkCMCTreeNode *root;
1906 cm_return_if_fail(folder != NULL);
1908 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1909 folderview_gnode_func, folderview);
1910 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1911 folderview);
1912 folderview_sort_folders(folderview, root, folder);
1915 /* callback functions */
1916 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1917 GdkEventButton *event)
1919 FolderItem *item;
1920 Folder *folder;
1921 FolderViewPopup *fpopup;
1922 GtkActionGroup *action_group;
1923 GtkWidget *popup;
1924 FolderItem *special_trash = NULL, *special_queue = NULL;
1925 PrefsAccount *ac;
1926 GtkUIManager *ui_manager = gtk_ui_manager_new();
1928 if (folderview->ui_manager)
1929 g_object_unref(folderview->ui_manager);
1931 folderview->ui_manager = ui_manager;
1932 item = folderview_get_selected_item(folderview);
1934 cm_return_if_fail(item != NULL);
1935 cm_return_if_fail(item->folder != NULL);
1936 folder = item->folder;
1938 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1940 if (fpopup != NULL)
1941 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1942 else {
1943 fpopup = g_hash_table_lookup(folderview_popups, "common");
1944 action_group = g_hash_table_lookup(folderview->popups, "common");
1947 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1948 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1949 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1951 if (fpopup->add_menuitems)
1952 fpopup->add_menuitems(ui_manager, item);
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1960 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1961 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1962 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1964 if (fpopup->set_sensitivity != NULL)
1965 fpopup->set_sensitivity(ui_manager, item);
1967 if (NULL != (ac = account_find_from_item(item))) {
1968 special_trash = account_get_special_folder(ac, F_TRASH);
1969 special_queue = account_get_special_folder(ac, F_QUEUE);
1972 if ((item == folder->trash || item == special_trash
1973 || folder_has_parent_of_type(item, F_TRASH))) {
1974 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1975 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1978 if ((item == folder->queue || item == special_queue
1979 || folder_has_parent_of_type(item, F_QUEUE))) {
1980 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1981 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1984 #define SET_SENS(name, sens) \
1985 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1987 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1988 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1989 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1990 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1991 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1992 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1993 folderview->selected == folderview->opened);
1994 SET_SENS("FolderViewPopup/Properties", TRUE);
1996 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1997 item->total_msgs >= 1 && !item->processing_pending);
1998 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1999 !item->no_select && !item->processing_pending);
2001 if (item == folder->trash || item == special_trash
2002 || folder_has_parent_of_type(item, F_TRASH)) {
2003 GSList *msglist = folder_item_get_msg_list(item);
2004 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2005 procmsg_msg_list_free(msglist);
2007 if (item == folder->queue || item == special_queue
2008 || folder_has_parent_of_type(item, F_QUEUE)) {
2009 GSList *msglist = folder_item_get_msg_list(item);
2010 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2011 procmsg_msg_list_free(msglist);
2013 #undef SET_SENS
2015 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2016 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2017 g_signal_connect(G_OBJECT(popup), "selection_done",
2018 G_CALLBACK(folderview_popup_close),
2019 folderview);
2020 gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL);
2023 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2024 FolderView *folderview)
2026 GtkCMCList *clist = GTK_CMCLIST(ctree);
2027 gint prev_row = -1, row = -1, column = -1;
2029 if (!event) return FALSE;
2030 if (event->window != clist->clist_window) return FALSE;
2032 if (event->button == 1 || event->button == 2) {
2033 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2034 folderview->open_folder = TRUE;
2036 if (event->type == GDK_2BUTTON_PRESS) {
2037 if (clist->selection) {
2038 GtkCMCTreeNode *node;
2040 node = GTK_CMCTREE_NODE(clist->selection->data);
2041 if (node) {
2042 gtk_cmctree_toggle_expansion(
2043 GTK_CMCTREE(ctree),
2044 node);
2045 folderview->open_folder = FALSE;
2049 return FALSE;
2052 if (event->button == 2 || event->button == 3) {
2053 /* right clicked */
2054 if (clist->selection) {
2055 GtkCMCTreeNode *node;
2057 node = GTK_CMCTREE_NODE(clist->selection->data);
2058 if (node)
2059 prev_row = gtkut_ctree_get_nth_from_node
2060 (GTK_CMCTREE(ctree), node);
2063 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2064 &row, &column))
2065 return FALSE;
2066 if (prev_row != row) {
2067 gtk_cmclist_unselect_all(clist);
2068 if (event->button == 2)
2069 folderview_select_node
2070 (folderview,
2071 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2072 row));
2073 else
2074 gtk_cmclist_select_row(clist, row, column);
2078 if (event->button != 3) return FALSE;
2080 folderview_set_sens_and_popup_menu(folderview, row, event);
2081 return FALSE;
2084 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2085 FolderView *folderview)
2087 int row = -1, column = -1;
2089 if (!event) return FALSE;
2091 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2092 &row, &column))
2093 return FALSE;
2094 if (event->button == 1 && folderview->open_folder == FALSE &&
2095 folderview->opened != NULL) {
2096 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2097 folderview->opened);
2098 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2101 return FALSE;
2104 #define BREAK_ON_MODIFIER_KEY() \
2105 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2107 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2108 FolderView *folderview)
2110 GtkCMCTreeNode *node;
2111 FolderItem *item;
2113 if (!event) return FALSE;
2115 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2116 return FALSE;
2118 switch (event->keyval) {
2119 case GDK_KEY_Right:
2120 if (folderview->selected) {
2121 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2122 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2123 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2124 folderview->selected);
2125 else
2126 folderview_select_node(folderview,
2127 folderview->selected);
2129 break;
2130 #ifdef GENERIC_UMPC
2131 case GDK_KEY_Return:
2132 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2133 gtk_cmctree_toggle_expansion(
2134 GTK_CMCTREE(folderview->ctree),
2135 folderview->selected);
2137 break;
2138 #else
2139 case GDK_KEY_Return:
2140 case GDK_KEY_KP_Enter:
2141 if (folderview->selected)
2142 folderview_select_node(folderview, folderview->selected);
2143 break;
2144 #endif
2145 case GDK_KEY_space:
2146 BREAK_ON_MODIFIER_KEY();
2147 if (folderview->selected) {
2148 if (folderview->opened == folderview->selected &&
2149 (!folderview->summaryview->folder_item ||
2150 folderview->summaryview->folder_item->total_msgs == 0))
2151 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2152 else
2153 folderview_select_node(folderview,
2154 folderview->selected);
2156 break;
2157 case GDK_KEY_Left:
2158 if (folderview->selected) {
2159 /* If the folder is expanded and can be collapsed, do that... */
2160 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2161 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2162 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2163 folderview->selected);
2164 } else {
2165 /* ...otherwise, move cursor to its parent node. */
2166 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2167 folderview->selected))) {
2168 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2169 NULL, folder_item_parent(item)))) {
2170 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2171 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2172 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2173 node, -1, 0, 0);
2178 break;
2179 case GDK_KEY_Home:
2180 case GDK_KEY_End:
2181 if (event->keyval == GDK_KEY_Home)
2182 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2183 else
2184 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2185 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2187 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2189 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2190 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2191 node, -1, 0, 0);
2192 break;
2193 default:
2194 break;
2197 return FALSE;
2200 typedef struct _PostponedSelectData
2202 GtkCMCTree *ctree;
2203 GtkCMCTreeNode *row;
2204 gint column;
2205 FolderView *folderview;
2206 } PostponedSelectData;
2208 static gboolean postpone_select(void *data)
2210 PostponedSelectData *psdata = (PostponedSelectData *)data;
2211 debug_print("trying again\n");
2213 psdata->folderview->postpone_select_id = 0;
2214 psdata->folderview->open_folder = TRUE;
2215 main_window_cursor_normal(psdata->folderview->mainwin);
2216 STATUSBAR_POP(psdata->folderview->mainwin);
2217 folderview_selected(psdata->ctree, psdata->row,
2218 psdata->column, psdata->folderview);
2219 g_free(psdata);
2220 return FALSE;
2223 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2225 if (folderview->opened) {
2226 if (dirty) {
2227 folderview->opened = NULL;
2228 return;
2231 FolderItem *olditem =
2232 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2233 folderview->opened);
2234 if (olditem) {
2235 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2236 olditem->path ? olditem->path:olditem->name);
2237 /* will be null if we just moved the previously opened folder */
2238 STATUSBAR_PUSH(folderview->mainwin, buf);
2239 main_window_cursor_wait(folderview->mainwin);
2240 g_free(buf);
2241 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2242 summary_show(folderview->summaryview, NULL, FALSE);
2243 folder_item_close(olditem);
2244 main_window_cursor_normal(folderview->mainwin);
2245 STATUSBAR_POP(folderview->mainwin);
2246 if (olditem->folder->klass->item_closed)
2247 olditem->folder->klass->item_closed(olditem);
2252 if (folderview->opened &&
2253 !GTK_CMCTREE_ROW(folderview->opened)->children)
2254 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2256 folderview->opened = NULL;
2258 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2259 gint column, FolderView *folderview)
2261 GdkDisplay *display;
2262 GdkSeat *seat;
2263 GdkDevice *device;
2264 static gboolean can_select = TRUE; /* exclusive lock */
2265 gboolean opened;
2266 FolderItem *item;
2267 gchar *buf;
2268 int res = 0;
2269 GtkCMCTreeNode *old_opened = folderview->opened;
2270 START_TIMING("");
2271 folderview->selected = row;
2273 display = gdk_display_get_default();
2274 seat = gdk_display_get_default_seat(display);
2275 device = gdk_seat_get_pointer(seat);
2277 debug_print("newly selected %p, opened %p\n", folderview->selected,
2278 folderview->opened);
2279 if (folderview->opened == row) {
2280 folderview->open_folder = FALSE;
2281 END_TIMING();
2282 return;
2285 item = gtk_cmctree_node_get_row_data(ctree, row);
2286 if (!item) {
2287 END_TIMING();
2288 folderview->open_folder = FALSE;
2289 return;
2292 if (!can_select || summary_is_locked(folderview->summaryview)) {
2293 if (folderview->opened) {
2294 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2295 gtk_cmctree_select(ctree, folderview->opened);
2297 folderview->open_folder = FALSE;
2298 END_TIMING();
2299 return;
2302 if (!folderview->open_folder) {
2303 END_TIMING();
2304 return;
2307 can_select = FALSE;
2309 /* Save cache for old folder */
2310 /* We don't want to lose all caches if app crashes */
2311 /* Resets folderview->opened to NULL */
2312 folderview_close_opened(folderview, FALSE);
2314 /* CLAWS: set compose button type: news folder items
2315 * always have a news folder as parent */
2316 if (item->folder)
2317 toolbar_set_compose_button
2318 (folderview->mainwin->toolbar,
2319 FOLDER_TYPE(item->folder) == F_NEWS ?
2320 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2322 if (item->path)
2323 debug_print("Folder %s is selected\n", item->path);
2325 if (!GTK_CMCTREE_ROW(row)->children)
2326 gtk_cmctree_expand(ctree, row);
2328 /* ungrab the mouse event */
2329 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2330 gtk_grab_remove(GTK_WIDGET(ctree));
2331 if (gdk_display_device_is_grabbed(display, device))
2332 gdk_seat_ungrab(seat);
2335 /* Open Folder */
2336 /* TODO: wwp: avoid displaying (null) in the status bar */
2337 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2338 item->path : "(null)");
2339 debug_print("%s\n", buf);
2340 STATUSBAR_PUSH(folderview->mainwin, buf);
2341 g_free(buf);
2343 main_window_cursor_wait(folderview->mainwin);
2345 if (folderview->scanning_folder == item->folder) {
2346 res = -2;
2347 } else {
2348 res = folder_item_open(item);
2351 if (res == -1 && item->no_select == FALSE) {
2352 main_window_cursor_normal(folderview->mainwin);
2353 STATUSBAR_POP(folderview->mainwin);
2355 alertpanel_error(_("Folder could not be opened."));
2357 folderview->open_folder = FALSE;
2358 can_select = TRUE;
2359 END_TIMING();
2360 return;
2361 } else if (res == -2 && item->no_select == FALSE) {
2362 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2363 data->ctree = ctree;
2364 data->row = row;
2365 data->column = column;
2366 data->folderview = folderview;
2367 debug_print("postponing open of %s till end of scan\n",
2368 item->path ? item->path:item->name);
2369 folderview->open_folder = FALSE;
2370 can_select = TRUE;
2371 if (folderview->postpone_select_id != 0)
2372 g_source_remove(folderview->postpone_select_id);
2373 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2374 END_TIMING();
2375 return;
2378 main_window_cursor_normal(folderview->mainwin);
2380 /* Show messages */
2381 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2382 opened = summary_show(folderview->summaryview, item, FALSE);
2384 folder_clean_cache_memory(item);
2386 if (!opened) {
2387 gtkut_ctree_set_focus_row(ctree, old_opened);
2388 gtk_cmctree_select(ctree, old_opened);
2389 folderview->opened = old_opened;
2390 } else {
2391 folderview->opened = row;
2392 if (gtk_cmctree_node_is_visible(ctree, row)
2393 != GTK_VISIBILITY_FULL)
2394 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2397 STATUSBAR_POP(folderview->mainwin);
2399 folderview->open_folder = FALSE;
2400 can_select = TRUE;
2401 END_TIMING();
2404 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2405 FolderView *folderview)
2407 FolderItem *item;
2409 item = gtk_cmctree_node_get_row_data(ctree, node);
2410 cm_return_if_fail(item != NULL);
2411 item->collapsed = FALSE;
2412 folderview_update_node(folderview, node);
2415 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2416 FolderView *folderview)
2418 FolderItem *item;
2420 item = gtk_cmctree_node_get_row_data(ctree, node);
2421 cm_return_if_fail(item != NULL);
2422 item->collapsed = TRUE;
2423 folderview_update_node(folderview, node);
2426 static void folderview_popup_close(GtkMenuShell *menu_shell,
2427 FolderView *folderview)
2429 if (!folderview->opened) return;
2431 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2434 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2435 FolderView *folderview)
2437 FolderColumnType type = folderview->col_state[column].type;
2439 prefs_common.folder_col_size[type] = width;
2442 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2444 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2445 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2446 GtkCMCTreeNode *node, *parent_node;
2447 gint *col_pos = folderview->col_pos;
2448 FolderItemUpdateData hookdata;
2450 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2451 if (parent_node == NULL)
2452 return;
2454 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2456 text[col_pos[F_COL_FOLDER]] = item->name;
2457 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2458 FOLDER_SPACING,
2459 folderxpm,
2460 folderopenxpm,
2461 FALSE, FALSE);
2462 gtk_cmctree_expand(ctree, parent_node);
2463 gtk_cmctree_node_set_row_data(ctree, node, item);
2464 folderview_sort_folders(folderview, parent_node, item->folder);
2466 hookdata.item = item;
2467 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2468 hookdata.msg = NULL;
2469 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2471 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2474 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2476 FolderView *folderview = (FolderView *)data;
2477 FolderItem *item;
2478 GSList *mlist = NULL;
2479 GSList *cur = NULL;
2480 FolderItem *special_trash = NULL;
2481 PrefsAccount *ac;
2483 if (!folderview->selected) return;
2484 item = folderview_get_selected_item(folderview);
2485 cm_return_if_fail(item != NULL);
2486 cm_return_if_fail(item->folder != NULL);
2488 if (NULL != (ac = account_find_from_item(item)))
2489 special_trash = account_get_special_folder(ac, F_TRASH);
2491 if (item != item->folder->trash && item != special_trash
2492 && !folder_has_parent_of_type(item, F_TRASH)) return;
2494 if (prefs_common.ask_on_clean) {
2495 if (alertpanel(_("Empty trash"),
2496 _("Delete all messages in trash?"),
2497 NULL, _("_Cancel"), NULL, _("_Empty trash"), NULL, NULL,
2498 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2499 return;
2502 mlist = folder_item_get_msg_list(item);
2504 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2505 MsgInfo * msginfo = (MsgInfo *) cur->data;
2506 if (MSG_IS_LOCKED(msginfo->flags))
2507 continue;
2508 /* is it partially received? (partial_recv isn't cached) */
2509 if (msginfo->total_size != 0 &&
2510 msginfo->size != (off_t)msginfo->total_size)
2511 partial_mark_for_delete(msginfo);
2513 procmsg_msg_list_free(mlist);
2515 folder_item_remove_all_msg(item);
2518 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2520 FolderView *folderview = (FolderView *)data;
2521 FolderItem *item;
2522 FolderItem *special_queue = NULL;
2523 PrefsAccount *ac;
2524 gchar *errstr = NULL;
2526 if (!folderview->selected) return;
2527 item = folderview_get_selected_item(folderview);
2528 cm_return_if_fail(item != NULL);
2529 cm_return_if_fail(item->folder != NULL);
2531 if (NULL != (ac = account_find_from_item(item)))
2532 special_queue = account_get_special_folder(ac, F_QUEUE);
2534 if (item != item->folder->queue && item != special_queue
2535 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2537 if (procmsg_queue_is_empty(item))
2538 return;
2540 if (prefs_common.work_offline)
2541 if (alertpanel(_("Offline warning"),
2542 _("You're working offline. Override?"),
2543 NULL, _("_No"), NULL, _("_Yes"),
2544 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2545 return;
2547 /* ask for confirmation before sending queued messages only
2548 in online mode and if there is at least one message queued
2549 in any of the folder queue
2551 if (prefs_common.confirm_send_queued_messages) {
2552 if (!prefs_common.work_offline) {
2553 if (alertpanel(_("Send queued messages"),
2554 _("Send all queued messages?"),
2555 NULL, _("_Cancel"), NULL, _("_Send"),
2556 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2557 return;
2561 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2562 if (!errstr)
2563 alertpanel_error_log(_("Some errors occurred while "
2564 "sending queued messages."));
2565 else {
2566 alertpanel_error_log(_("Some errors occurred "
2567 "while sending queued messages:\n%s"), errstr);
2568 g_free(errstr);
2573 static void folderview_search_cb(GtkAction *action, gpointer data)
2575 FolderView *folderview = (FolderView *)data;
2576 summary_search(folderview->summaryview);
2579 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2581 FolderView *folderview = (FolderView *)data;
2582 FolderItem *item;
2584 if (!folderview->selected) return;
2586 item = folderview_get_selected_item(folderview);
2588 folderview_run_processing(item);
2591 void folderview_run_processing(FolderItem *item)
2593 cm_return_if_fail(item != NULL);
2594 cm_return_if_fail(item->folder != NULL);
2596 item->processing_pending = TRUE;
2597 folder_item_apply_processing(item);
2598 item->processing_pending = FALSE;
2601 static void folderview_property_cb(GtkAction *action, gpointer data)
2603 FolderView *folderview = (FolderView *)data;
2604 FolderItem *item;
2606 if (!folderview->selected) return;
2608 item = folderview_get_selected_item(folderview);
2609 cm_return_if_fail(item != NULL);
2610 cm_return_if_fail(item->folder != NULL);
2612 prefs_folder_item_open(item);
2615 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2617 GSList *list = NULL;
2618 GSList *done = NULL;
2619 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2621 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2622 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2623 && list->data != node) {
2624 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2625 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2628 for (list = done; list != NULL; list = g_slist_next(list)) {
2629 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2630 list->data);
2632 g_slist_free(done);
2635 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2636 FolderItem *to_folder, gboolean copy)
2638 FolderItem *new_folder = NULL;
2639 gchar *buf;
2640 gint status;
2642 cm_return_if_fail(folderview != NULL);
2643 cm_return_if_fail(from_folder != NULL);
2644 cm_return_if_fail(to_folder != NULL);
2646 if (prefs_common.warn_dnd) {
2647 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2648 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2649 from_folder->name, to_folder->name);
2650 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2651 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL,
2652 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION);
2653 g_free(buf);
2655 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2656 return;
2657 else if (status & G_ALERTDISABLE)
2658 prefs_common.warn_dnd = FALSE;
2661 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2662 from_folder->name, to_folder->name);
2663 STATUSBAR_PUSH(folderview->mainwin, buf);
2664 g_free(buf);
2665 summary_clear_all(folderview->summaryview);
2666 folderview->opened = NULL;
2667 folderview->selected = NULL;
2668 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2669 inc_lock();
2670 main_window_cursor_wait(folderview->mainwin);
2672 statusbar_verbosity_set(FALSE);
2673 folder_item_update_freeze();
2674 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2675 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2676 statusbar_verbosity_set(FALSE);
2677 main_window_cursor_normal(folderview->mainwin);
2678 STATUSBAR_POP(folderview->mainwin);
2679 folder_item_update_thaw();
2680 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2682 folderview_sort_folders(folderview,
2683 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2684 NULL, to_folder), new_folder->folder);
2685 folderview_select(folderview, new_folder);
2686 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2687 } else {
2688 statusbar_verbosity_set(FALSE);
2689 main_window_cursor_normal(folderview->mainwin);
2690 STATUSBAR_POP(folderview->mainwin);
2691 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2692 folder_item_update_thaw();
2693 switch (status) {
2694 case F_MOVE_FAILED_DEST_IS_PARENT:
2695 alertpanel_error(_("Source and destination are the same."));
2696 break;
2697 case F_MOVE_FAILED_DEST_IS_CHILD:
2698 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2699 _("Can't move a folder to one of its children."));
2700 break;
2701 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2702 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2703 break;
2704 default:
2705 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2706 break;
2709 inc_unlock();
2710 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2713 static gint folderview_clist_compare(GtkCMCList *clist,
2714 gconstpointer ptr1, gconstpointer ptr2)
2716 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2717 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2719 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2721 return item1->order - item2->order;
2724 // if only one folder has an order it comes first
2725 if (item1->order > 0)
2727 return -1;
2729 if (item2->order > 0)
2731 return 1;
2734 if (!item1->name)
2735 return (item2->name != NULL);
2736 if (!item2->name)
2737 return -1;
2739 return g_utf8_collate(item1->name, item2->name);
2742 static void folderview_processing_cb(GtkAction *action, gpointer data)
2744 FolderView *folderview = (FolderView *)data;
2745 FolderItem *item;
2746 gchar *id, *title;
2748 if (!folderview->selected) return;
2750 item = folderview_get_selected_item(folderview);
2751 cm_return_if_fail(item != NULL);
2752 cm_return_if_fail(item->folder != NULL);
2754 id = folder_item_get_identifier(item);
2755 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2756 g_free (id);
2758 prefs_filtering_open(&item->prefs->processing, title,
2759 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2760 g_free (title);
2763 void folderview_set_target_folder_color(GdkRGBA color_op)
2765 GList *list;
2766 FolderView *folderview;
2768 for (list = folderview_list; list != NULL; list = list->next) {
2769 folderview = (FolderView *)list->data;
2770 folderview->color_op = color_op;
2774 static gchar *last_smallfont = NULL;
2775 static gchar *last_normalfont = NULL;
2776 static gchar *last_boldfont = NULL;
2777 static gboolean last_derive = 0;
2779 void folderview_reinit_fonts(FolderView *folderview)
2781 /* force reinit */
2782 g_free(last_smallfont);
2783 last_smallfont = NULL;
2784 g_free(last_normalfont);
2785 last_normalfont = NULL;
2786 g_free(last_boldfont);
2787 last_boldfont = NULL;
2790 void folderview_reflect_prefs(void)
2792 gboolean update_font = FALSE;
2793 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2794 FolderItem *item = folderview_get_selected_item(folderview);
2795 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2796 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2797 gint height = gtk_adjustment_get_value(pos);
2799 folderview->color_new = prefs_common.color[COL_NEW];
2800 folderview->color_op = prefs_common.color[COL_TGT_FOLDER];
2802 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2803 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2804 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2805 last_derive != prefs_common.derive_from_normal_font)
2806 update_font = TRUE;
2808 if (!update_font)
2809 return;
2811 g_free(last_smallfont);
2812 last_smallfont = g_strdup(SMALL_FONT);
2813 g_free(last_normalfont);
2814 last_normalfont = g_strdup(NORMAL_FONT);
2815 g_free(last_boldfont);
2816 last_boldfont = g_strdup(BOLD_FONT);
2817 last_derive = prefs_common.derive_from_normal_font;
2819 folderview_set_fonts(folderview);
2821 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2822 folderview_column_set_titles(folderview);
2823 folderview_set_all();
2825 g_signal_handlers_block_by_func
2826 (G_OBJECT(folderview->ctree),
2827 G_CALLBACK(folderview_selected), folderview);
2829 if (item) {
2830 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2831 GTK_CMCTREE(folderview->ctree), NULL, item);
2833 folderview_select(folderview, item);
2834 folderview->open_folder = FALSE;
2835 folderview->selected = node;
2838 g_signal_handlers_unblock_by_func
2839 (G_OBJECT(folderview->ctree),
2840 G_CALLBACK(folderview_selected), folderview);
2842 pos = gtk_scrolled_window_get_vadjustment(
2843 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2844 gtk_adjustment_set_value(pos, height);
2845 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2848 static void drag_state_stop(FolderView *folderview)
2850 if (folderview->drag_timer_id)
2851 g_source_remove(folderview->drag_timer_id);
2852 folderview->drag_timer_id = 0;
2853 folderview->drag_node = NULL;
2856 static gboolean folderview_defer_expand(FolderView *folderview)
2858 if (folderview->drag_node) {
2859 folderview_recollapse_nodes(folderview, folderview->drag_node);
2860 if (folderview->drag_item->collapsed) {
2861 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2862 folderview->nodes_to_recollapse = g_slist_append
2863 (folderview->nodes_to_recollapse, folderview->drag_node);
2866 folderview->drag_item = NULL;
2867 folderview->drag_timer_id = 0;
2868 return FALSE;
2871 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2873 /* the idea is that we call drag_state_start() whenever we want expansion to
2874 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2875 * we need to call drag_state_stop() */
2876 drag_state_stop(folderview);
2877 /* request expansion */
2878 if (0 != (folderview->drag_timer_id = g_timeout_add
2879 (prefs_common.hover_timeout,
2880 (GSourceFunc)folderview_defer_expand,
2881 folderview))) {
2882 folderview->drag_node = node;
2883 folderview->drag_item = item;
2886 #ifndef GENERIC_UMPC
2887 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2888 FolderView *folderview)
2890 GdkDragContext *context;
2892 cm_return_if_fail(folderview != NULL);
2893 if (folderview->selected == NULL) return;
2894 if (folderview->nodes_to_recollapse)
2895 g_slist_free(folderview->nodes_to_recollapse);
2896 folderview->nodes_to_recollapse = NULL;
2897 context = gtk_drag_begin_with_coordinates(widget, folderview->target_list,
2898 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
2899 -1, -1);
2900 gtk_drag_set_icon_default(context);
2902 #endif
2903 static void folderview_drag_data_get(GtkWidget *widget,
2904 GdkDragContext *drag_context,
2905 GtkSelectionData *selection_data,
2906 guint info,
2907 guint time,
2908 FolderView *folderview)
2910 FolderItem *item;
2911 GList *sel;
2912 if (info == TARGET_DUMMY) {
2913 sel = GTK_CMCLIST(folderview->ctree)->selection;
2914 if (!sel)
2915 return;
2917 item = gtk_cmctree_node_get_row_data
2918 (GTK_CMCTREE(folderview->ctree),
2919 GTK_CMCTREE_NODE(sel->data));
2920 if (item) {
2921 gchar *source = NULL;
2922 gchar *name = folder_item_get_identifier(item);
2923 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", name);
2924 g_free(name);
2925 gtk_selection_data_set(selection_data,
2926 gtk_selection_data_get_target(selection_data), 8,
2927 source, strlen(source));
2929 } else {
2930 g_warning("unknown info %d", info);
2934 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2936 FolderUpdateData *hookdata;
2937 FolderView *folderview;
2938 GtkWidget *ctree;
2940 hookdata = source;
2941 folderview = (FolderView *) userdata;
2942 cm_return_val_if_fail(hookdata != NULL, FALSE);
2943 cm_return_val_if_fail(folderview != NULL, FALSE);
2945 ctree = folderview->ctree;
2946 cm_return_val_if_fail(ctree != NULL, FALSE);
2948 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2949 folderview_create_folder_node(folderview, hookdata->item);
2950 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2951 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2952 NULL, folder_item_parent(hookdata->item));
2953 folderview_sort_folders(folderview, node, hookdata->folder);
2954 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2955 GtkCMCTreeNode *node;
2957 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2958 if (node != NULL) {
2959 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2960 if (folderview->selected == node)
2961 folderview->selected = NULL;
2962 if (folderview->opened == node)
2963 folderview->opened = NULL;
2965 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2966 /* do nothing, it's done by the ADD and REMOVE) */
2967 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2968 folderview_set(folderview);
2970 return FALSE;
2973 static gboolean folderview_dnd_scroll_cb(gpointer data)
2975 FolderView *folderview = (FolderView *)data;
2976 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2977 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2978 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2979 gint max = (int)gtk_adjustment_get_upper(pos) -
2980 (int)gtk_adjustment_get_page_size(pos);
2982 if (folderview->scroll_value == 0) {
2983 folderview->scroll_timeout_id = 0;
2984 return FALSE;
2987 if (folderview->scroll_value > 0 && new_val > max) {
2988 new_val = max;
2989 } else if (folderview->scroll_value < 0 && new_val < 0) {
2990 new_val = 0;
2992 gtk_adjustment_set_value(pos, new_val);
2994 return TRUE;
2997 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2998 GdkDragContext *context,
2999 gint x,
3000 gint y,
3001 guint time,
3002 FolderView *folderview)
3004 gint row, column;
3005 FolderItem *item = NULL, *src_item = NULL;
3006 GtkCMCTreeNode *node = NULL;
3007 gboolean acceptable = FALSE;
3008 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3009 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3010 int height = (int)gtk_adjustment_get_page_size(pos);
3011 int total_height = (int)gtk_adjustment_get_upper(pos);
3012 int vpos = (int)gtk_adjustment_get_value(pos);
3013 int offset = prefs_common.show_col_headers ? 24:0;
3014 int dist;
3016 if (gtk_cmclist_get_selection_info
3017 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3018 GtkWidget *srcwidget;
3020 if (y > height - (48 - offset) && height + vpos < total_height) {
3021 dist = -(height - (48 - offset) - y);
3022 folderview->scroll_value = 1.41f * (1+(dist / 6));
3023 } else if (y < 72 - (24 - offset) && y >= 0) {
3024 dist = 72 - (24 - offset) - y;
3025 folderview->scroll_value = -1.41f * (1+(dist / 6));
3026 } else {
3027 folderview->scroll_value = 0;
3029 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3030 folderview->scroll_timeout_id =
3031 g_timeout_add(30, folderview_dnd_scroll_cb,
3032 folderview);
3035 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3036 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3037 src_item = folderview->summaryview->folder_item;
3039 srcwidget = gtk_drag_get_source_widget(context);
3040 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3041 /* comes from summaryview */
3042 /* we are copying messages, so only accept folder items that are not
3043 the source item, are no root items and can copy messages */
3044 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3045 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3046 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3047 acceptable = TRUE;
3048 } else if (srcwidget == folderview->ctree) {
3049 /* comes from folderview */
3050 /* we are moving folder items, only accept folders that are not
3051 the source items and can copy messages and create folder items */
3052 if (item && item->folder && src_item && src_item != item &&
3053 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3054 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3055 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3056 || item->folder == src_item->folder))
3057 acceptable = TRUE;
3058 } else {
3059 /* comes from another app */
3060 /* we are adding messages, so only accept folder items that are
3061 no root items and can copy messages */
3062 if (item && item->folder && folder_item_parent(item) != NULL
3063 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3064 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3065 acceptable = TRUE;
3069 if (acceptable || (src_item && src_item == item))
3070 drag_state_start(folderview, node, item);
3072 if (acceptable) {
3073 g_signal_handlers_block_by_func
3074 (G_OBJECT(widget),
3075 G_CALLBACK(folderview_selected), folderview);
3076 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3077 g_signal_handlers_unblock_by_func
3078 (G_OBJECT(widget),
3079 G_CALLBACK(folderview_selected), folderview);
3080 gdk_drag_status(context,
3081 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3082 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3083 } else {
3084 if (folderview->opened)
3085 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3086 gdk_drag_status(context, 0, time);
3089 return acceptable;
3092 static void folderview_drag_leave_cb(GtkWidget *widget,
3093 GdkDragContext *context,
3094 guint time,
3095 FolderView *folderview)
3097 drag_state_stop(folderview);
3098 folderview->scroll_value = 0;
3099 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3102 static void free_info (gpointer stuff, gpointer data)
3104 g_free(stuff);
3107 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3108 guint time, FolderItem *item)
3110 GList *list, *tmp;
3111 GSList *msglist = NULL;
3112 list = uri_list_extract_filenames(data);
3113 if (!(item && item->folder && folder_item_parent(item) != NULL
3114 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3116 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3117 debug_print("item doesn't fit\n");
3118 return;
3120 if (!list) {
3121 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3122 debug_print("list is empty\n");
3123 return;
3125 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3126 MsgFileInfo *info = NULL;
3128 if (file_is_email((gchar *)tmp->data)) {
3129 info = g_new0(MsgFileInfo, 1);
3130 info->msginfo = NULL;
3131 info->file = (gchar *)tmp->data;
3132 msglist = g_slist_prepend(msglist, info);
3133 debug_print("file is a mail\n");
3134 } else {
3135 debug_print("file isn't a mail\n");
3138 if (msglist) {
3139 msglist = g_slist_reverse(msglist);
3140 folder_item_add_msgs(item, msglist, FALSE);
3141 g_slist_foreach(msglist, free_info, NULL);
3142 g_slist_free(msglist);
3143 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3144 } else {
3145 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3147 list_free_strings_full(list);
3150 static void folderview_drag_received_cb(GtkWidget *widget,
3151 GdkDragContext *drag_context,
3152 gint x,
3153 gint y,
3154 GtkSelectionData *data,
3155 guint info,
3156 guint time,
3157 FolderView *folderview)
3159 gint row, column;
3160 FolderItem *item = NULL, *src_item;
3161 GtkCMCTreeNode *node;
3162 int offset = prefs_common.show_col_headers ? 24:0;
3164 folderview->scroll_value = 0;
3166 if (info == TARGET_DUMMY) {
3167 drag_state_stop(folderview);
3168 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3169 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3170 /* comes from summaryview */
3171 if (gtk_cmclist_get_selection_info
3172 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3173 return;
3175 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3176 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3177 src_item = folderview->summaryview->folder_item;
3179 if (item && item->no_select) {
3180 alertpanel_error(_("The destination folder can only be used to "
3181 "store subfolders."));
3182 return;
3184 /* re-check (due to acceptable possibly set for folder moves */
3185 if (!(item && item->folder && item->path && !item->no_select &&
3186 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3187 return;
3190 switch (gdk_drag_context_get_selected_action(drag_context)) {
3191 case GDK_ACTION_COPY:
3192 summary_copy_selected_to(folderview->summaryview, item);
3193 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3194 break;
3195 case GDK_ACTION_MOVE:
3196 case GDK_ACTION_DEFAULT:
3197 default:
3198 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3199 summary_copy_selected_to(folderview->summaryview, item);
3200 else
3201 summary_move_selected_to(folderview->summaryview, item);
3202 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3204 } else {
3205 /* comes from folderview */
3206 char *source;
3207 gboolean folder_is_normal = TRUE;
3208 gboolean copy = (GDK_ACTION_COPY ==
3209 gdk_drag_context_get_selected_action(drag_context));
3211 source = (char *)gtk_selection_data_get_data(data) + 17;
3212 if (gtk_cmclist_get_selection_info
3213 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3214 || *source == 0) {
3215 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3216 return;
3218 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3219 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3220 src_item = folder_find_item_from_identifier(source);
3222 folder_is_normal =
3223 src_item != NULL &&
3224 src_item->stype == F_NORMAL &&
3225 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3226 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3227 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3228 !folder_has_parent_of_type(src_item, F_TRASH);
3229 if (!item || !src_item || !folder_is_normal) {
3230 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3231 return;
3234 folderview_move_folder(folderview, src_item, item, copy);
3235 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3237 folderview->nodes_to_recollapse = NULL;
3238 } else if (info == TARGET_MAIL_URI_LIST) {
3239 if (gtk_cmclist_get_selection_info
3240 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3241 return;
3243 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3244 if (!node) {
3245 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3246 debug_print("no node\n");
3247 return;
3249 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3250 if (!item) {
3251 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3252 debug_print("no item\n");
3253 return;
3255 folderview_finish_dnd(gtk_selection_data_get_data(data),
3256 drag_context, time, item);
3260 static void folderview_drag_end_cb(GtkWidget *widget,
3261 GdkDragContext *drag_context,
3262 FolderView *folderview)
3264 drag_state_stop(folderview);
3265 folderview->scroll_value = 0;
3266 g_slist_free(folderview->nodes_to_recollapse);
3267 folderview->nodes_to_recollapse = NULL;
3270 void folderview_register_popup(FolderViewPopup *fpopup)
3272 GList *folderviews;
3274 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3275 FolderView *folderview = folderviews->data;
3276 GtkActionGroup *factory;
3278 factory = create_action_group(folderview, fpopup);
3279 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3281 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3284 void folderview_unregister_popup(FolderViewPopup *fpopup)
3286 GList *folderviews;
3289 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3290 FolderView *folderview = folderviews->data;
3292 g_hash_table_remove(folderview->popups, fpopup->klass);
3294 g_hash_table_remove(folderview_popups, fpopup->klass);
3297 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3299 g_return_if_fail(folderview != NULL);
3300 g_return_if_fail(item != NULL);
3302 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3303 g_return_if_fail(ctree != NULL);
3305 GtkCMCTreeNode *node =
3306 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3307 g_return_if_fail(node != NULL);
3309 gtk_cmctree_remove_node(ctree, node);
3312 void folderview_freeze(FolderView *folderview)
3314 if (folderview)
3315 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3318 void folderview_thaw(FolderView *folderview)
3320 if (folderview)
3321 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3324 void folderview_grab_focus(FolderView *folderview)
3326 if (folderview)
3327 gtk_widget_grab_focus(folderview->ctree);
3330 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3331 gpointer data)
3333 prefs_folder_column_open();
3336 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3337 GdkEvent *_event,
3338 gpointer user_data)
3340 GdkEventButton *event = (GdkEventButton *)_event;
3341 FolderView *folderview = (FolderView *)user_data;
3343 cm_return_val_if_fail(folderview != NULL, FALSE);
3345 /* Only handle single button presses. */
3346 if (event->type == GDK_2BUTTON_PRESS ||
3347 event->type == GDK_3BUTTON_PRESS)
3348 return FALSE;
3350 /* Handle right-click for context menu */
3351 if (event->button == 3) {
3352 gtk_menu_popup_at_pointer(GTK_MENU(folderview->headerpopupmenu), NULL);
3353 return TRUE;
3356 return FALSE;