remove unnecessary cm_return_val_if_fail()
[claws.git] / src / folderview.c
blob263d3d8d7e0da309b7394bcfdb359c39787d1419
1 /*
2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2019 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 "defs.h"
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "main.h"
30 #include "mainwindow.h"
31 #include "folderview.h"
32 #include "summaryview.h"
33 #include "summary_search.h"
34 #include "inputdialog.h"
35 #include "manage_window.h"
36 #include "alertpanel.h"
37 #include "menu.h"
38 #include "stock_pixmap.h"
39 #include "procmsg.h"
40 #include "utils.h"
41 #include "gtkutils.h"
42 #include "prefs_common.h"
43 #include "prefs_account.h"
44 #include "prefs_filtering.h"
45 #include "prefs_folder_item.h"
46 #include "account.h"
47 #include "folder.h"
48 #include "foldersel.h"
49 #include "inc.h"
50 #include "statusbar.h"
51 #include "hooks.h"
52 #include "folderutils.h"
53 #include "partial_download.h"
54 #include "prefs_folder_column.h"
55 #include "filtering.h"
56 #include "quicksearch.h"
57 #include "manual.h"
58 #include "timing.h"
59 #include "log.h"
60 #include "gtkcmctree.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *bold_style;
69 static GdkPixbuf *inboxxpm;
70 static GdkPixbuf *inboxhrmxpm;
71 static GdkPixbuf *inboxopenxpm;
72 static GdkPixbuf *inboxopenhrmxpm;
73 static GdkPixbuf *outboxxpm;
74 static GdkPixbuf *outboxhrmxpm;
75 static GdkPixbuf *outboxopenxpm;
76 static GdkPixbuf *outboxopenhrmxpm;
77 static GdkPixbuf *folderxpm;
78 static GdkPixbuf *folderhrmxpm;
79 static GdkPixbuf *folderopenxpm;
80 static GdkPixbuf *folderopenhrmxpm;
81 static GdkPixbuf *trashopenxpm;
82 static GdkPixbuf *trashopenhrmxpm;
83 static GdkPixbuf *trashxpm;
84 static GdkPixbuf *trashhrmxpm;
85 static GdkPixbuf *queuexpm;
86 static GdkPixbuf *queuehrmxpm;
87 static GdkPixbuf *queueopenxpm;
88 static GdkPixbuf *queueopenhrmxpm;
89 static GdkPixbuf *draftsxpm;
90 static GdkPixbuf *draftsopenxpm;
91 static GdkPixbuf *foldersubsxpm;
92 static GdkPixbuf *foldersubsopenxpm;
93 static GdkPixbuf *foldernoselectxpm;
94 static GdkPixbuf *foldernoselectopenxpm;
96 static GdkPixbuf *m_inboxxpm;
97 static GdkPixbuf *m_inboxhrmxpm;
98 static GdkPixbuf *m_inboxopenxpm;
99 static GdkPixbuf *m_inboxopenhrmxpm;
100 static GdkPixbuf *m_outboxxpm;
101 static GdkPixbuf *m_outboxhrmxpm;
102 static GdkPixbuf *m_outboxopenxpm;
103 static GdkPixbuf *m_outboxopenhrmxpm;
104 static GdkPixbuf *m_folderxpm;
105 static GdkPixbuf *m_folderhrmxpm;
106 static GdkPixbuf *m_folderopenxpm;
107 static GdkPixbuf *m_folderopenhrmxpm;
108 static GdkPixbuf *m_trashopenxpm;
109 static GdkPixbuf *m_trashopenhrmxpm;
110 static GdkPixbuf *m_trashxpm;
111 static GdkPixbuf *m_trashhrmxpm;
112 static GdkPixbuf *m_queuexpm;
113 static GdkPixbuf *m_queuehrmxpm;
114 static GdkPixbuf *m_queueopenxpm;
115 static GdkPixbuf *m_queueopenhrmxpm;
116 static GdkPixbuf *m_draftsxpm;
117 static GdkPixbuf *m_draftsopenxpm;
118 static GdkPixbuf *m_foldersubsxpm;
119 static GdkPixbuf *m_foldernoselectxpm;
121 static GdkPixbuf *newxpm;
122 static GdkPixbuf *unreadxpm;
123 static GdkPixbuf *readxpm;
125 static void folderview_select_node (FolderView *folderview,
126 GtkCMCTreeNode *node);
127 static void folderview_set_folders (FolderView *folderview);
128 static void folderview_sort_folders (FolderView *folderview,
129 GtkCMCTreeNode *root,
130 Folder *folder);
131 static void folderview_append_folder (FolderView *folderview,
132 Folder *folder);
133 static void folderview_update_node (FolderView *folderview,
134 GtkCMCTreeNode *node);
136 static gint folderview_clist_compare (GtkCMCList *clist,
137 gconstpointer ptr1,
138 gconstpointer ptr2);
140 /* callback functions */
141 static gboolean folderview_button_pressed (GtkWidget *ctree,
142 GdkEventButton *event,
143 FolderView *folderview);
144 static gboolean folderview_button_released (GtkWidget *ctree,
145 GdkEventButton *event,
146 FolderView *folderview);
147 static gboolean folderview_key_pressed (GtkWidget *widget,
148 GdkEventKey *event,
149 FolderView *folderview);
150 static void folderview_selected (GtkCMCTree *ctree,
151 GtkCMCTreeNode *row,
152 gint column,
153 FolderView *folderview);
154 static void folderview_tree_expanded (GtkCMCTree *ctree,
155 GtkCMCTreeNode *node,
156 FolderView *folderview);
157 static void folderview_tree_collapsed (GtkCMCTree *ctree,
158 GtkCMCTreeNode *node,
159 FolderView *folderview);
160 static void folderview_popup_close (GtkMenuShell *menu_shell,
161 FolderView *folderview);
162 static void folderview_col_resized (GtkCMCList *clist,
163 gint column,
164 gint width,
165 FolderView *folderview);
167 static void mark_all_read_unread_handler (GtkAction *action,
168 gpointer data,
169 gboolean recursive,
170 gboolean read);
172 static void mark_all_read_cb (GtkAction *action,
173 gpointer data);
174 static void mark_all_unread_cb (GtkAction *action,
175 gpointer data);
176 static void mark_all_read_recursive_cb (GtkAction *action,
177 gpointer data);
178 static void mark_all_unread_recursive_cb (GtkAction *action,
179 gpointer data);
181 static void folderview_empty_trash_cb (GtkAction *action,
182 gpointer data);
184 static void folderview_send_queue_cb (GtkAction *action,
185 gpointer data);
187 static void folderview_search_cb (GtkAction *action,
188 gpointer data);
189 static void folderview_run_processing_cb(GtkAction *action,
190 gpointer data);
192 static void folderview_property_cb (GtkAction *action,
193 gpointer data);
195 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
196 GdkDragContext *context,
197 gint x,
198 gint y,
199 guint time,
200 FolderView *folderview);
201 static void folderview_drag_leave_cb (GtkWidget *widget,
202 GdkDragContext *context,
203 guint time,
204 FolderView *folderview);
205 static void folderview_drag_received_cb (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 gint x,
208 gint y,
209 GtkSelectionData *data,
210 guint info,
211 guint time,
212 FolderView *folderview);
213 #ifndef GENERIC_UMPC
214 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
215 FolderView *folderview);
216 #endif
217 static void folderview_drag_data_get (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 GtkSelectionData *selection_data,
220 guint info,
221 guint time,
222 FolderView *folderview);
223 static void folderview_drag_end_cb (GtkWidget *widget,
224 GdkDragContext *drag_context,
225 FolderView *folderview);
227 static void folderview_create_folder_node (FolderView *folderview,
228 FolderItem *item);
229 static gboolean folderview_update_folder (gpointer source,
230 gpointer userdata);
231 static gboolean folderview_update_item_claws (gpointer source,
232 gpointer data);
233 static void folderview_processing_cb(GtkAction *action, gpointer data);
234 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
235 GdkEventButton *event);
236 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
237 gpointer data);
238 static gboolean folderview_header_button_pressed(GtkWidget *widget,
239 GdkEvent *_event,
240 gpointer user_data);
242 GHashTable *folderview_popups;
244 static GtkActionEntry folderview_common_popup_entries[] =
246 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
247 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
248 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
249 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
250 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
251 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
252 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
253 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
254 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
255 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
256 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
257 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
261 static GtkActionEntry folderview_header_popup_entries[] =
263 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL },
264 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) }
267 GtkTargetEntry folderview_drag_types[] =
269 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
270 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
273 void folderview_initialize(void)
275 FolderViewPopup *fpopup;
277 fpopup = g_new0(FolderViewPopup, 1);
279 fpopup->klass = "common";
280 fpopup->path = "<CommonFolder>";
281 fpopup->entries = folderview_common_popup_entries;
282 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
283 fpopup->set_sensitivity = NULL;
285 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
286 g_hash_table_insert(folderview_popups, "common", fpopup);
289 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
291 FolderViewPopup *fpopup_common;
292 GtkActionGroup *action_group;
294 action_group = cm_menu_create_action_group(
295 fpopup->path,
296 fpopup->entries, fpopup->n_entries,
297 (gpointer)folderview);
299 if (fpopup->toggle_entries)
300 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
301 fpopup->n_toggle_entries,
302 (gpointer)folderview);
303 if (fpopup->radio_entries)
304 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
305 fpopup->n_radio_entries, fpopup->radio_default,
306 G_CALLBACK(fpopup->radio_callback),
307 (gpointer)folderview);
309 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
310 if (fpopup_common != fpopup) {
311 gtk_action_group_add_actions(action_group, fpopup_common->entries,
312 fpopup_common->n_entries,
313 (gpointer)folderview);
314 if (fpopup_common->toggle_entries)
315 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
316 fpopup_common->n_toggle_entries,
317 (gpointer)folderview);
318 if (fpopup_common->radio_entries)
319 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
320 fpopup_common->n_radio_entries, fpopup_common->radio_default,
321 G_CALLBACK(fpopup_common->radio_callback),
322 (gpointer)folderview);
325 return action_group;
328 static void create_action_groups(gpointer key, gpointer value, gpointer data)
330 FolderView *folderview = data;
331 FolderViewPopup *fpopup = value;
332 GtkActionGroup *group;
334 group = create_action_group(folderview, fpopup);
335 g_hash_table_insert(folderview->popups, fpopup->klass, group);
338 static void folderview_column_set_titles(FolderView *folderview)
340 GtkWidget *ctree = folderview->ctree;
341 GtkWidget *label_folder;
342 GtkWidget *label_new;
343 GtkWidget *label_unread;
344 GtkWidget *label_total;
345 GtkWidget *hbox_folder;
346 GtkWidget *hbox_new;
347 GtkWidget *hbox_unread;
348 GtkWidget *hbox_total;
349 gint *col_pos = folderview->col_pos;
351 debug_print("setting titles...\n");
352 gtk_widget_realize(folderview->ctree);
353 gtk_widget_show_all(folderview->scrolledwin);
355 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
356 * instead text (text overflows making them unreadable and ugly) */
357 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
358 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
359 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
361 label_folder = gtk_label_new(_("Folder"));
362 label_new = gtk_image_new_from_pixbuf(newxpm);
363 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
364 label_total = gtk_image_new_from_pixbuf(readxpm);
366 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
368 hbox_folder = gtk_hbox_new(FALSE, 4);
369 hbox_new = gtk_hbox_new(FALSE, 4);
370 hbox_unread = gtk_hbox_new(FALSE, 4);
371 hbox_total = gtk_hbox_new(FALSE, 4);
373 /* left justified */
374 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
375 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
376 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
377 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
378 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
379 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
380 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
381 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
383 gtk_widget_show_all(hbox_folder);
384 gtk_widget_show_all(hbox_new);
385 gtk_widget_show_all(hbox_unread);
386 gtk_widget_show_all(hbox_total);
388 #ifdef GENERIC_UMPC
389 gtk_widget_set_size_request(hbox_new, -1, 20);
390 gtk_widget_set_size_request(hbox_unread, -1, 20);
391 gtk_widget_set_size_request(hbox_total, -1, 20);
392 #endif
394 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
395 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
396 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
397 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
399 #ifdef GENERIC_UMPC
400 GTK_EVENTS_FLUSH();
401 #endif
403 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
404 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
405 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
408 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
410 FolderView *folderview = (FolderView *)data;
411 GdkEventButton event;
412 if (folderview_get_selected_item(folderview) == NULL)
413 return FALSE;
415 event.button = 3;
416 event.time = gtk_get_current_event_time();
418 folderview_set_sens_and_popup_menu(folderview, -1,
419 &event);
421 return TRUE;
425 static GtkWidget *folderview_ctree_create(FolderView *folderview)
427 GtkWidget *ctree;
428 gint *col_pos;
429 FolderColumnState *col_state;
430 FolderColumnType type;
431 gchar *titles[N_FOLDER_COLS];
432 gint i;
433 GtkWidget *scrolledwin = folderview->scrolledwin;
435 debug_print("creating tree...\n");
436 memset(titles, 0, sizeof(titles));
438 col_state = prefs_folder_column_get_config();
439 memset(titles, 0, sizeof(titles));
441 col_pos = folderview->col_pos;
443 for (i = 0; i < N_FOLDER_COLS; i++) {
444 folderview->col_state[i] = col_state[i];
445 type = col_state[i].type;
446 col_pos[type] = i;
449 titles[col_pos[F_COL_FOLDER]] = _("Folder");
450 titles[col_pos[F_COL_NEW]] = _("New");
451 titles[col_pos[F_COL_UNREAD]] = _("Unread");
452 /* TRANSLATORS: This in Number sign in American style */
453 titles[col_pos[F_COL_TOTAL]] = _("#");
455 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
456 titles);
458 if (prefs_common.show_col_headers == FALSE)
459 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
462 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
463 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
464 GTK_JUSTIFY_RIGHT);
465 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
466 col_pos[F_COL_UNREAD],
467 GTK_JUSTIFY_RIGHT);
468 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
469 col_pos[F_COL_TOTAL],
470 GTK_JUSTIFY_RIGHT);
471 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
472 GTK_CMCTREE_EXPANDER_TRIANGLE);
474 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
475 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
477 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
478 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
480 /* don't let title buttons take key focus */
481 for (i = 0; i < N_FOLDER_COLS; i++) {
482 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
483 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
484 prefs_common.folder_col_size[i]);
485 gtk_cmclist_set_column_visibility
486 (GTK_CMCLIST(ctree), i, col_state[i].visible);
488 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button),
489 "button-press-event",
490 G_CALLBACK(folderview_header_button_pressed),
491 folderview);
494 g_signal_connect(G_OBJECT(ctree), "key_press_event",
495 G_CALLBACK(folderview_key_pressed),
496 folderview);
497 g_signal_connect(G_OBJECT(ctree), "button_press_event",
498 G_CALLBACK(folderview_button_pressed),
499 folderview);
500 g_signal_connect(G_OBJECT(ctree), "popup-menu",
501 G_CALLBACK(folderview_popup_menu), folderview);
502 g_signal_connect(G_OBJECT(ctree), "button_release_event",
503 G_CALLBACK(folderview_button_released),
504 folderview);
505 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
506 G_CALLBACK(folderview_selected), folderview);
507 #ifndef GENERIC_UMPC
508 /* drag-n-dropping folders on maemo is impractical as this
509 * opens the folder almost everytime */
510 g_signal_connect(G_OBJECT(ctree), "start_drag",
511 G_CALLBACK(folderview_start_drag), folderview);
512 #endif
513 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
514 G_CALLBACK(folderview_drag_data_get),
515 folderview);
517 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
518 G_CALLBACK(folderview_tree_expanded),
519 folderview);
520 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
521 G_CALLBACK(folderview_tree_collapsed),
522 folderview);
524 g_signal_connect(G_OBJECT(ctree), "resize_column",
525 G_CALLBACK(folderview_col_resized),
526 folderview);
528 /* drop callback */
529 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
530 folderview_drag_types, 2,
531 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
532 g_signal_connect(G_OBJECT(ctree), "drag_motion",
533 G_CALLBACK(folderview_drag_motion_cb),
534 folderview);
535 g_signal_connect(G_OBJECT(ctree), "drag_leave",
536 G_CALLBACK(folderview_drag_leave_cb),
537 folderview);
538 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
539 G_CALLBACK(folderview_drag_received_cb),
540 folderview);
541 g_signal_connect(G_OBJECT(ctree), "drag_end",
542 G_CALLBACK(folderview_drag_end_cb),
543 folderview);
545 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
547 return ctree;
550 void folderview_set_column_order(FolderView *folderview)
552 GtkWidget *ctree = folderview->ctree;
553 FolderItem *item = folderview_get_selected_item(folderview);
554 FolderItem *sel_item = NULL, *op_item = NULL;
555 GtkWidget *scrolledwin = folderview->scrolledwin;
557 if (folderview->drag_timer_id != 0) {
558 g_source_remove(folderview->drag_timer_id);
559 folderview->drag_timer_id = 0;
561 if (folderview->deferred_refresh_id != 0) {
562 g_source_remove(folderview->deferred_refresh_id);
563 folderview->deferred_refresh_id = 0;
565 if (folderview->scroll_timeout_id != 0) {
566 g_source_remove(folderview->scroll_timeout_id);
567 folderview->scroll_timeout_id = 0;
569 if (folderview->postpone_select_id != 0) {
570 g_source_remove(folderview->postpone_select_id);
571 folderview->postpone_select_id = 0;
574 if (folderview->selected)
575 sel_item = folderview_get_selected_item(folderview);
576 if (folderview->opened)
577 op_item = folderview_get_opened_item(folderview);
579 debug_print("recreating tree...\n");
580 gtk_widget_destroy(folderview->ctree);
583 folderview->ctree = ctree = folderview_ctree_create(folderview);
584 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
585 GTK_CMCLIST(ctree)->hadjustment);
586 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
587 GTK_CMCLIST(ctree)->vadjustment);
588 gtk_widget_show(ctree);
590 if (sel_item)
591 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
592 if (op_item)
593 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
595 folderview_set(folderview);
596 folderview_column_set_titles(folderview);
598 folderview_select(folderview,item);
601 FolderView *folderview_create(MainWindow *mainwin)
603 FolderView *folderview;
604 GtkWidget *scrolledwin;
605 GtkWidget *ctree;
607 debug_print("Creating folder view...\n");
608 folderview = g_new0(FolderView, 1);
610 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
611 gtk_scrolled_window_set_policy
612 (GTK_SCROLLED_WINDOW(scrolledwin),
613 GTK_POLICY_AUTOMATIC,
614 prefs_common.folderview_vscrollbar_policy);
615 gtk_widget_set_size_request(scrolledwin,
616 prefs_common.folderview_width,
617 prefs_common.folderview_height);
619 folderview->scrolledwin = scrolledwin;
620 ctree = folderview_ctree_create(folderview);
622 /* create popup factories */
623 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
624 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
626 gtk_action_group_add_actions(mainwin->action_group,
627 folderview_header_popup_entries,
628 G_N_ELEMENTS(folderview_header_popup_entries),
629 (gpointer)folderview);
631 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU)
632 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
634 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
635 gtk_ui_manager_get_widget(mainwin->ui_manager,
636 "/Menus/FolderViewHeaderPopup") ));
638 folderview->ctree = ctree;
640 folderview->folder_update_callback_id =
641 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
642 folderview->folder_item_update_callback_id =
643 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
645 gtk_widget_show_all(scrolledwin);
647 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
648 folderview_list = g_list_append(folderview_list, folderview);
650 folderview->drag_timer_id = 0;
651 folderview->deferred_refresh_id = 0;
652 folderview->scroll_timeout_id = 0;
653 folderview->postpone_select_id = 0;
655 return folderview;
658 static void folderview_set_fonts(FolderView *folderview)
660 PangoFontDescription *font_desc;
661 GtkWidget *ctree = folderview->ctree;
663 font_desc = pango_font_description_from_string(NORMAL_FONT);
664 if (font_desc) {
665 gtk_widget_modify_font(ctree, font_desc);
666 pango_font_description_free(font_desc);
669 if (!bold_style) {
670 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
672 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
673 font_desc = pango_font_description_from_string(NORMAL_FONT);
674 if (font_desc) {
675 pango_font_description_free(bold_style->font_desc);
676 bold_style->font_desc = font_desc;
678 pango_font_description_set_weight
679 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
680 } else {
681 font_desc = pango_font_description_from_string(BOLD_FONT);
682 if (font_desc) {
683 pango_font_description_free(bold_style->font_desc);
684 bold_style->font_desc = font_desc;
690 void folderview_init(FolderView *folderview)
692 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
693 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
694 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
695 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
696 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
697 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
698 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
699 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
700 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
701 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
702 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
703 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
704 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
705 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
706 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
707 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
708 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
709 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
710 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
711 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
712 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
713 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
714 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
715 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
716 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
717 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
719 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
720 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
721 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
722 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
723 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
724 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
725 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
726 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
727 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
728 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
729 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
730 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
731 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
732 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
733 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
734 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
735 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
736 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
737 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
738 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
739 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
740 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
741 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
742 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
744 folderview_set_fonts(folderview);
747 static gboolean folderview_defer_set(gpointer data)
749 FolderView *folderview = (FolderView *)data;
750 MainWindow *mainwin = folderview->mainwin;
752 if (!mainwin)
753 return FALSE;
754 if (mainwin->lock_count)
755 return TRUE;
757 debug_print("doing deferred folderview_set now\n");
758 folderview_set(folderview);
760 folderview->deferred_refresh_id = 0;
761 return FALSE;
764 void folderview_set(FolderView *folderview)
766 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
767 MainWindow *mainwin = folderview->mainwin;
768 FolderItem *sel_item = NULL, *op_item = NULL;
770 if (!mainwin)
771 return;
773 if (mainwin->lock_count) {
774 if (folderview->deferred_refresh_id == 0)
775 folderview->deferred_refresh_id =
776 g_timeout_add(500, folderview_defer_set, folderview);
777 debug_print("deferred folderview_set\n");
778 return;
781 inc_lock();
782 debug_print("Setting folder info...\n");
783 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
785 main_window_cursor_wait(mainwin);
787 if (folderview->selected)
788 sel_item = folderview_get_selected_item(folderview);
789 if (folderview->opened)
790 op_item = folderview_get_opened_item(folderview);
792 folderview->selected = NULL;
793 folderview->opened = NULL;
795 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
796 gtk_cmclist_clear(GTK_CMCLIST(ctree));
798 folderview_set_folders(folderview);
800 if (sel_item)
801 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
802 if (op_item)
803 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
805 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
806 main_window_cursor_normal(mainwin);
807 STATUSBAR_POP(mainwin);
808 inc_unlock();
811 void folderview_set_all(void)
813 GList *list;
815 for (list = folderview_list; list != NULL; list = list->next)
816 folderview_set((FolderView *)list->data);
819 void folderview_select(FolderView *folderview, FolderItem *item)
821 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
822 GtkCMCTreeNode *node;
823 GtkCMCTreeNode *old_selected = folderview->selected;
825 if (!item) return;
827 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
828 if (node) folderview_select_node(folderview, node);
830 if (old_selected != node)
831 folder_update_op_count();
834 static void mark_all_read_cb(GtkAction *action, gpointer data)
836 mark_all_read_unread_handler(action, data, FALSE, TRUE);
839 static void mark_all_unread_cb(GtkAction *action, gpointer data)
841 mark_all_read_unread_handler(action, data, FALSE, FALSE);
844 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
846 mark_all_read_unread_handler(action, data, TRUE, TRUE);
849 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
851 mark_all_read_unread_handler(action, data, TRUE, FALSE);
854 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
855 gboolean recursive, gboolean read)
857 FolderView *folderview = (FolderView *)data;
858 FolderItem *item;
859 AlertValue val;
860 gchar *message;
861 gchar *title;
863 item = folderview_get_selected_item(folderview);
864 if (item == NULL)
865 return;
867 if (read) {
868 title = _("Mark all as read");
869 message = recursive? _("Do you really want to mark all mails in this "
870 "folder and its sub-folders as read?") :
871 _("Do you really want to mark all mails in this "
872 "folder as read?");
873 } else {
874 title = _("Mark all as unread");
875 message = recursive? _("Do you really want to mark all mails in this "
876 "folder and its sub-folders as unread?") :
877 _("Do you really want to mark all mails in this "
878 "folder as unread?");
880 if (prefs_common.ask_mark_all_read) {
881 val = alertpanel_full(title, message,
882 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
883 TRUE, NULL, ALERT_QUESTION);
885 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
886 return;
887 else if (val & G_ALERTDISABLE)
888 prefs_common.ask_mark_all_read = FALSE;
891 folder_item_update_freeze();
892 if (folderview->summaryview->folder_item != item && !recursive)
893 summary_lock(folderview->summaryview);
894 else
895 summary_freeze(folderview->summaryview);
897 if (read) {
898 if (recursive)
899 folderutils_mark_all_read_recursive(item, TRUE);
900 else
901 folderutils_mark_all_read(item, TRUE);
902 } else {
903 if (recursive)
904 folderutils_mark_all_read_recursive(item, FALSE);
905 else
906 folderutils_mark_all_read(item, FALSE);
908 if (folderview->summaryview->folder_item != item && !recursive)
909 summary_unlock(folderview->summaryview);
910 else
911 summary_thaw(folderview->summaryview);
912 folder_item_update_thaw();
915 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
917 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
919 cm_return_if_fail(node != NULL);
921 if (folderview->open_folder) {
922 return;
925 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
926 gtkut_ctree_expand_parent_all(ctree, node);
928 folderview->open_folder = TRUE;
929 gtkut_ctree_set_focus_row(ctree, node);
930 gtk_cmctree_select(ctree, node);
931 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
932 if ((folderview->summaryview->folder_item &&
933 folderview->summaryview->folder_item->total_msgs > 0) ||
934 prefs_common.layout_mode == SMALL_LAYOUT)
935 summary_select_node(folderview->summaryview,
936 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
937 else
938 gtk_widget_grab_focus(folderview->ctree);
941 void folderview_unselect(FolderView *folderview)
943 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
944 gtk_cmctree_collapse
945 (GTK_CMCTREE(folderview->ctree), folderview->opened);
947 folderview->selected = folderview->opened = NULL;
950 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
951 GtkCMCTreeNode *node,
952 MsgPermFlags flag)
954 FolderItem *item;
956 if (node)
957 node = gtkut_ctree_node_next(ctree, node);
958 else
959 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
961 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
962 item = gtk_cmctree_node_get_row_data(ctree, node);
963 if (!item)
964 continue;
965 if (item->stype == F_TRASH || item->stype == F_DRAFT)
966 continue;
967 switch (flag) {
968 case MSG_UNREAD:
969 if(item->unread_msgs > 0)
970 return node;
971 break;
972 case MSG_NEW:
973 if(item->new_msgs > 0)
974 return node;
975 break;
976 case MSG_MARKED:
977 if(item->marked_msgs > 0)
978 return node;
979 break;
980 default:
981 if(item->total_msgs > 0)
982 return node;
983 break;
987 return NULL;
990 void folderview_select_next_with_flag(FolderView *folderview,
991 MsgPermFlags flag)
993 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
994 GtkCMCTreeNode *node = NULL;
995 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
997 switch (flag) {
998 case MSG_UNREAD:
999 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1000 break;
1001 case MSG_NEW:
1002 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1003 break;
1004 case MSG_MARKED:
1005 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1006 break;
1007 default:
1008 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1009 break;
1012 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1013 if (node != NULL) {
1014 folderview_select_node(folderview, node);
1015 goto out;
1018 if (!folderview->opened ||
1019 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1020 goto out;
1023 /* search again from the first node */
1024 node = folderview_find_next_with_flag(ctree, NULL, flag);
1025 if (node != NULL)
1026 folderview_select_node(folderview, node);
1028 out:
1029 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1032 FolderItem *folderview_get_selected_item(FolderView *folderview)
1034 g_return_val_if_fail(folderview != NULL, NULL);
1035 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1037 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1039 if (!folderview->selected) return NULL;
1040 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1043 FolderItem *folderview_get_opened_item(FolderView *folderview)
1045 g_return_val_if_fail(folderview != NULL, NULL);
1046 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1048 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1050 if (!folderview->opened) return NULL;
1051 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1054 static void folderview_set_folders(FolderView *folderview)
1056 GList *list;
1057 list = folder_get_list();
1059 for (; list != NULL; list = list->next) {
1060 folderview_append_folder(folderview, FOLDER(list->data));
1064 static gchar *get_scan_str(FolderItem *item)
1066 if (item->path)
1067 return g_strdup_printf(_("Scanning folder %s/%s..."),
1068 item->folder->name, item->path);
1069 else
1070 return g_strdup_printf(_("Scanning folder %s..."),
1071 item->folder->name);
1073 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1074 gpointer data)
1076 GList *list;
1077 for (list = folderview_list; list != NULL; list = list->next) {
1078 FolderView *folderview = (FolderView *)list->data;
1079 MainWindow *mainwin = folderview->mainwin;
1080 gchar *str = get_scan_str(item);
1082 STATUSBAR_PUSH(mainwin, str);
1083 STATUSBAR_POP(mainwin);
1084 g_free(str);
1088 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1090 GtkWidget *window;
1091 MainWindow *mainwin = mainwindow_get_mainwindow();
1092 FolderView *folderview = NULL;
1093 GtkAdjustment *pos = NULL;
1094 gint height = 0;
1096 cm_return_if_fail(folder != NULL);
1098 if (!folder->klass->scan_tree) return;
1100 if (rebuild &&
1101 alertpanel_full(_("Rebuild folder tree"),
1102 _("Rebuilding the folder tree will remove "
1103 "local caches. Do you want to continue?"),
1104 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
1105 FALSE, NULL, ALERT_WARNING)
1106 != G_ALERTALTERNATE) {
1107 return;
1110 inc_lock();
1111 if (rebuild)
1112 window = label_window_create(_("Rebuilding folder tree..."));
1113 else
1114 window = label_window_create(_("Scanning folder tree..."));
1116 if (mainwin)
1117 folderview = mainwin->folderview;
1119 if (folderview) {
1120 pos = gtk_scrolled_window_get_vadjustment(
1121 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1122 height = gtk_adjustment_get_value(pos);
1125 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1126 folder_scan_tree(folder, rebuild);
1127 folder_set_ui_func(folder, NULL, NULL);
1129 folderview_set_all();
1131 if (folderview) {
1132 pos = gtk_scrolled_window_get_vadjustment(
1133 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1134 gtk_adjustment_set_value(pos, height);
1135 gtk_adjustment_changed(pos);
1137 label_window_destroy(window);
1138 inc_unlock();
1141 /** folderview_check_new()
1142 * Scan and update the folder and return the
1143 * count the number of new messages since last check.
1144 * \param folder the folder to check for new messages
1145 * \return the number of new messages since last check
1147 gint folderview_check_new(Folder *folder)
1149 GList *list;
1150 FolderItem *item;
1151 FolderView *folderview;
1152 GtkCMCTree *ctree;
1153 GtkCMCTreeNode *node;
1154 gint new_msgs = 0;
1155 gint former_new_msgs = 0;
1156 gint former_new = 0, former_unread = 0, former_total;
1158 for (list = folderview_list; list != NULL; list = list->next) {
1159 folderview = (FolderView *)list->data;
1160 ctree = GTK_CMCTREE(folderview->ctree);
1161 folderview->scanning_folder = folder;
1162 inc_lock();
1163 main_window_lock(folderview->mainwin);
1165 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1166 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1167 gchar *str = NULL;
1168 item = gtk_cmctree_node_get_row_data(ctree, node);
1169 if (!item || !item->path || !item->folder) continue;
1170 if (item->no_select) continue;
1171 if (folder && folder != item->folder) continue;
1172 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1173 if (!item->prefs->newmailcheck) continue;
1174 if (item->processing_pending == TRUE) {
1175 debug_print("skipping %s, processing pending\n",
1176 item->path ? item->path : item->name);
1177 continue;
1179 if (item->scanning != ITEM_NOT_SCANNING) {
1180 debug_print("skipping %s, scanning\n",
1181 item->path ? item->path : item->name);
1182 continue;
1185 str = get_scan_str(item);
1187 STATUSBAR_PUSH(folderview->mainwin, str);
1188 GTK_EVENTS_FLUSH();
1189 g_free(str);
1191 folderview_scan_tree_func(item->folder, item, NULL);
1192 former_new = item->new_msgs;
1193 former_unread = item->unread_msgs;
1194 former_total = item->total_msgs;
1196 if (item->folder->klass->scan_required &&
1197 (item->folder->klass->scan_required(item->folder, item) ||
1198 item->folder->inbox == item ||
1199 item->opened == TRUE ||
1200 item->processing_pending == TRUE)) {
1201 if (folder_item_scan(item) < 0) {
1202 if (folder) {
1203 summaryview_unlock(folderview->summaryview, item);
1204 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1205 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1206 item->path ? item->path:item->name);
1207 STATUSBAR_POP(folderview->mainwin);
1208 continue;
1209 } else if (!FOLDER_IS_LOCAL(folder)) {
1210 STATUSBAR_POP(folderview->mainwin);
1211 break;
1215 } else if (!item->folder->klass->scan_required) {
1216 if (folder_item_scan(item) < 0) {
1217 summaryview_unlock(folderview->summaryview, item);
1218 if (folder && !FOLDER_IS_LOCAL(folder)) {
1219 STATUSBAR_POP(folderview->mainwin);
1220 break;
1224 if (former_new != item->new_msgs ||
1225 former_unread != item->unread_msgs ||
1226 former_total != item->total_msgs)
1227 folderview_update_node(folderview, node);
1229 new_msgs += item->new_msgs;
1230 former_new_msgs += former_new;
1231 STATUSBAR_POP(folderview->mainwin);
1233 folderview->scanning_folder = NULL;
1234 main_window_unlock(folderview->mainwin);
1235 inc_unlock();
1238 folder_write_list();
1239 /* Number of new messages since last check is the just the difference
1240 * between former_new_msgs and new_msgs. If new_msgs is less than
1241 * former_new_msgs, that would mean another session accessed the folder
1242 * and the result is not well defined.
1244 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1245 return new_msgs;
1248 void folderview_check_new_all(void)
1250 GList *list;
1251 GtkWidget *window;
1252 FolderView *folderview;
1254 folderview = (FolderView *)folderview_list->data;
1256 inc_lock();
1257 main_window_lock(folderview->mainwin);
1258 window = label_window_create
1259 (_("Checking for new messages in all folders..."));
1261 list = folder_get_list();
1262 for (; list != NULL; list = list->next) {
1263 Folder *folder = list->data;
1265 folderview_check_new(folder);
1268 folder_write_list();
1269 folderview_set_all();
1271 label_window_destroy(window);
1272 main_window_unlock(folderview->mainwin);
1273 inc_unlock();
1276 static gboolean folderview_have_children_sub(FolderView *folderview,
1277 FolderItem *item,
1278 gboolean in_sub)
1280 GNode *node = NULL;
1282 if (!item || !item->folder || !item->folder->node)
1283 return FALSE;
1285 node = item->folder->node;
1287 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1288 node = node->children;
1290 if (in_sub && item->total_msgs > 0) {
1291 return TRUE;
1294 while (node != NULL) {
1295 if (node && node->data) {
1296 FolderItem *next_item = (FolderItem*) node->data;
1297 node = node->next;
1298 if (folderview_have_children_sub(folderview,
1299 next_item, TRUE))
1300 return TRUE;
1304 return FALSE;
1307 static gboolean folderview_have_children(FolderView *folderview,
1308 FolderItem *item)
1310 return folderview_have_children_sub(folderview, item, FALSE);
1313 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1314 FolderItem *item,
1315 gboolean in_sub)
1317 GNode *node = NULL;
1319 if (!item || !item->folder || !item->folder->node)
1320 return FALSE;
1322 node = item->folder->node;
1324 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1325 node = node->children;
1327 if (in_sub &&
1328 (item->new_msgs > 0 ||
1329 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1330 return TRUE;
1333 while (node != NULL) {
1334 if (node && node->data) {
1335 FolderItem *next_item = (FolderItem*) node->data;
1336 node = node->next;
1337 if (folderview_have_new_children_sub(folderview,
1338 next_item, TRUE))
1339 return TRUE;
1343 return FALSE;
1346 static gboolean folderview_have_new_children(FolderView *folderview,
1347 FolderItem *item)
1349 return folderview_have_new_children_sub(folderview, item, FALSE);
1352 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1353 FolderItem *item,
1354 gboolean in_sub)
1356 GNode *node = NULL;
1358 if (!item || !item->folder || !item->folder->node)
1359 return FALSE;
1361 node = item->folder->node;
1363 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1364 node = node->children;
1366 if (in_sub &&
1367 (item->unread_msgs > 0 ||
1368 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1369 return TRUE;
1372 while (node != NULL) {
1373 if (node && node->data) {
1374 FolderItem *next_item = (FolderItem*) node->data;
1375 node = node->next;
1376 if (folderview_have_unread_children_sub(folderview,
1377 next_item,
1378 TRUE))
1379 return TRUE;
1383 return FALSE;
1386 static gboolean folderview_have_unread_children(FolderView *folderview,
1387 FolderItem *item)
1389 return folderview_have_unread_children_sub(folderview, item, FALSE);
1392 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1393 FolderItem *item,
1394 gboolean in_sub)
1396 GNode *node = NULL;
1398 if (!item || !item->folder || !item->folder->node) {
1399 return FALSE;
1402 node = item->folder->node;
1404 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1405 node = node->children;
1407 if (in_sub &&
1408 (((item->total_msgs > 0) &&
1409 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1410 return TRUE;
1413 while (node != NULL) {
1414 if (node && node->data) {
1415 FolderItem *next_item = (FolderItem*) node->data;
1416 node = node->next;
1417 if (folderview_have_read_children_sub(folderview,
1418 next_item,
1419 TRUE)) {
1420 return TRUE;
1425 return FALSE;
1428 static gboolean folderview_have_read_children(FolderView *folderview,
1429 FolderItem *item)
1431 return folderview_have_read_children_sub(folderview, item, FALSE);
1434 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1435 FolderItem *item,
1436 gboolean in_sub)
1438 GNode *node = NULL;
1440 if (!item || !item->folder || !item->folder->node)
1441 return FALSE;
1443 node = item->folder->node;
1445 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1446 node = node->children;
1448 if (in_sub && item->search_match){
1449 return TRUE;
1452 while (node != NULL) {
1453 if (node && node->data) {
1454 FolderItem *next_item = (FolderItem*) node->data;
1455 node = node->next;
1456 if (folderview_have_matching_children_sub(folderview,
1457 next_item,
1458 TRUE))
1459 return TRUE;
1463 return FALSE;
1466 static gboolean folderview_have_matching_children(FolderView *folderview,
1467 FolderItem *item)
1469 return folderview_have_matching_children_sub(folderview, item, FALSE);
1472 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1473 FolderItem *item,
1474 gboolean in_sub)
1476 GNode *node = NULL;
1478 if (!item || !item->folder || !item->folder->node)
1479 return FALSE;
1481 node = item->folder->node;
1483 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1484 node = node->children;
1486 if (item->marked_msgs != 0) {
1487 return TRUE;
1490 while (node != NULL) {
1491 if (node && node->data) {
1492 FolderItem *next_item = (FolderItem*) node->data;
1493 node = node->next;
1494 if (folderview_have_marked_children_sub(folderview,
1495 next_item, TRUE))
1496 return TRUE;
1500 return FALSE;
1503 static gboolean folderview_have_marked_children(FolderView *folderview,
1504 FolderItem *item)
1506 return folderview_have_marked_children_sub(folderview, item, FALSE);
1509 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1511 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1512 GtkStyle *style = NULL, *prev_style;
1513 FolderItem *item;
1514 GdkPixbuf *xpm, *openxpm;
1515 static GdkPixbuf *searchicon;
1516 gboolean mark = FALSE;
1517 gchar *name;
1518 gchar *str;
1519 gboolean add_unread_mark;
1520 gboolean add_sub_match_mark;
1521 gboolean use_bold, use_color;
1522 gint *col_pos = folderview->col_pos;
1523 SpecialFolderItemType stype;
1524 GdkColor gdk_color;
1526 item = gtk_cmctree_node_get_row_data(ctree, node);
1527 cm_return_if_fail(item != NULL);
1529 if (!GTK_CMCTREE_ROW(node)->expanded)
1530 mark = folderview_have_marked_children(folderview, item);
1531 else
1532 mark = (item->marked_msgs != 0);
1534 stype = item->stype;
1535 if (stype == F_NORMAL) {
1536 if (folder_has_parent_of_type(item, F_TRASH))
1537 stype = F_TRASH;
1538 else if (folder_has_parent_of_type(item, F_DRAFT))
1539 stype = F_DRAFT;
1540 else if (folder_has_parent_of_type(item, F_OUTBOX))
1541 stype = F_OUTBOX;
1542 else if (folder_has_parent_of_type(item, F_QUEUE))
1543 stype = F_QUEUE;
1545 switch (stype) {
1546 case F_INBOX:
1547 if (item->hide_read_msgs || item->hide_read_threads) {
1548 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1549 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1550 } else {
1551 xpm = mark?m_inboxxpm:inboxxpm;
1552 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1554 break;
1555 case F_OUTBOX:
1556 if (item->hide_read_msgs || item->hide_read_threads) {
1557 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1558 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1559 } else {
1560 xpm = mark?m_outboxxpm:outboxxpm;
1561 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1563 break;
1564 case F_QUEUE:
1565 if (item->hide_read_msgs || item->hide_read_threads) {
1566 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1567 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1568 } else {
1569 xpm = mark?m_queuexpm:queuexpm;
1570 openxpm = mark?m_queueopenxpm:queueopenxpm;
1572 break;
1573 case F_TRASH:
1574 if (item->hide_read_msgs || item->hide_read_threads) {
1575 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1576 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1577 } else {
1578 xpm = mark?m_trashxpm:trashxpm;
1579 openxpm = mark?m_trashopenxpm:trashopenxpm;
1581 break;
1582 case F_DRAFT:
1583 xpm = mark?m_draftsxpm:draftsxpm;
1584 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1585 break;
1586 default:
1587 if (!item->path &&
1588 FOLDER_TYPE(item->folder) == F_IMAP &&
1589 item->folder->account->imap_subsonly) {
1590 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1591 openxpm = foldersubsopenxpm;
1592 } else if (item->no_select) {
1593 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1594 openxpm = foldernoselectopenxpm;
1595 } else if (item->hide_read_msgs || item->hide_read_threads) {
1596 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1597 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1598 } else {
1599 xpm = mark?m_folderxpm:folderxpm;
1600 openxpm = mark?m_folderopenxpm:folderopenxpm;
1604 name = folder_item_get_name(item);
1606 if (!GTK_CMCTREE_ROW(node)->expanded) {
1607 add_unread_mark = folderview_have_unread_children(
1608 folderview, item);
1609 add_sub_match_mark = folderview_have_matching_children(
1610 folderview, item);
1611 } else {
1612 add_unread_mark = FALSE;
1613 add_sub_match_mark = FALSE;
1616 if (item->search_match) {
1617 if (!searchicon) {
1618 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1619 &searchicon);
1621 xpm = openxpm = searchicon;
1624 str = NULL;
1625 if (prefs_common.display_folder_unread) {
1626 if (folder_has_parent_of_type(item, F_QUEUE)) {
1627 /* only total_msgs matters here */
1628 if (item->total_msgs > 0) {
1629 /* show total number (should be equal to the unread number)
1630 and signs if any */
1631 str = g_strdup_printf("%s (%d%s%s)",
1632 name, item->total_msgs,
1633 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1634 (item->unreadmarked_msgs > 0) ? "!" : "");
1636 } else {
1637 if (prefs_common.display_folder_unread == 1) {
1638 if (item->unread_msgs > 0) {
1639 /* show unread number and signs */
1640 str = g_strdup_printf("%s (%d%s%s)",
1641 name, item->unread_msgs,
1642 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1643 (item->unreadmarked_msgs > 0) ? "!" : "");
1645 } else {
1646 if (item->total_msgs > 0) {
1647 /* show unread number, total number and signs if any */
1648 str = g_strdup_printf("%s (%d/%d%s%s)",
1649 name, item->unread_msgs, item->total_msgs,
1650 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1651 (item->unreadmarked_msgs > 0) ? "!" : "");
1655 if ((str == NULL) &&
1656 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1657 /* no unread/total numbers, but at least one sign */
1658 str = g_strdup_printf("%s (%s%s)",
1659 name,
1660 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1661 (item->unreadmarked_msgs > 0) ? "!" : "");
1664 if (str == NULL) {
1665 /* last fallback, folder name only or with +! sign */
1666 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1667 str = g_strdup_printf("%s%s",
1668 name, " (+!)");
1669 } else if (item->unreadmarked_msgs > 0) {
1670 str = g_strdup_printf("%s%s",
1671 name, " (!)");
1672 } else if (add_sub_match_mark) {
1673 str = g_strdup_printf("%s%s",
1674 name, " (+)");
1675 } else {
1676 str = g_strdup_printf("%s", name);
1679 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1680 xpm, openxpm,
1681 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1682 g_free(str);
1683 g_free(name);
1685 if (!folder_item_parent(item)) {
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1687 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1689 } else {
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1691 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1692 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1695 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1696 folder_has_parent_of_type(item, F_TRASH)) {
1697 use_bold = use_color = FALSE;
1698 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1699 GSList *list = folder_item_get_msg_list(item);
1700 GSList *cur;
1701 use_bold = use_color = FALSE;
1702 for (cur = list; cur; cur = cur->next) {
1703 MsgInfo *msginfo = (MsgInfo *)cur->data;
1704 if (!MSG_IS_DELETED(msginfo->flags)) {
1705 /* highlight queue folder if there are any messages */
1706 use_bold = use_color = TRUE;
1707 break;
1710 if (!GTK_CMCTREE_ROW(node)->expanded &&
1711 use_bold == FALSE &&
1712 folderview_have_children(folderview, item))
1713 use_bold = use_color = TRUE;
1714 procmsg_msg_list_free(list);
1715 } else {
1716 /* if unread messages exist or target folder is set, print with bold font */
1717 use_bold = (item->unread_msgs > 0 || item->new_msgs > 0 || item->op_count > 0)
1718 || add_unread_mark;
1719 /* if new messages exist, print with colored letter */
1720 use_color =
1721 (item->new_msgs > 0) ||
1722 (add_unread_mark &&
1723 folderview_have_new_children(folderview, item));
1726 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1728 if (use_bold) {
1729 style = bold_style;
1730 if (item->op_count > 0) {
1731 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1732 } else if (use_color) {
1733 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1734 } else if (item->prefs->color != 0) {
1735 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1736 gtk_cmctree_node_set_foreground(ctree, node, &gdk_color);
1738 } else if (use_color)
1739 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1740 else if (item->op_count > 0)
1741 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1742 else if (item->prefs->color != 0) {
1743 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1744 gtk_cmctree_node_set_foreground(ctree, node, &gdk_color);
1747 gtk_cmctree_node_set_row_style(ctree, node, style);
1749 prev_style = gtk_cmctree_node_get_row_style(ctree, node);
1750 if (prev_style) {
1751 GtkStyle *ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
1753 style = gtk_style_copy(prev_style);
1754 style->text[GTK_STATE_NORMAL] = ctree_style->text[GTK_STATE_NORMAL];
1755 style->text[GTK_STATE_SELECTED] = ctree_style->text[GTK_STATE_SELECTED];
1756 gtk_cmctree_node_set_row_style(ctree, node, style);
1757 g_object_unref(style);
1760 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1761 folderview_update_node(folderview, node);
1764 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1766 GList *list;
1767 FolderView *folderview;
1768 GtkCMCTree *ctree;
1769 GtkCMCTreeNode *node;
1771 cm_return_if_fail(item != NULL);
1773 for (list = folderview_list; list != NULL; list = list->next) {
1774 folderview = (FolderView *)list->data;
1775 ctree = GTK_CMCTREE(folderview->ctree);
1777 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1778 if (node && item->search_match != matches) {
1779 item->search_match = matches;
1780 folderview_update_node(folderview, node);
1785 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1787 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1788 FolderView *folderview = (FolderView *)data;
1789 GtkCMCTree *ctree;
1790 GtkCMCTreeNode *node;
1791 cm_return_val_if_fail(update_info != NULL, TRUE);
1792 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1793 cm_return_val_if_fail(folderview != NULL, FALSE);
1795 ctree = GTK_CMCTREE(folderview->ctree);
1797 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1799 if (node) {
1800 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1801 folderview_update_node(folderview, node);
1803 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1804 update_info->item == folderview->summaryview->folder_item &&
1805 update_info->item != NULL)
1806 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1807 summary_show(folderview->summaryview, update_info->item, FALSE);
1810 return FALSE;
1813 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1814 GNode *gnode, GtkCMCTreeNode *cnode,
1815 gpointer data)
1817 FolderView *folderview = (FolderView *)data;
1818 FolderItem *item = FOLDER_ITEM(gnode->data);
1820 cm_return_val_if_fail(item != NULL, FALSE);
1822 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1823 folderview_update_node(folderview, cnode);
1825 return TRUE;
1828 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1829 gpointer data)
1831 FolderView *folderview = (FolderView *)data;
1832 FolderItem *item;
1834 if (GTK_CMCTREE_ROW(node)->children) {
1835 item = gtk_cmctree_node_get_row_data(ctree, node);
1836 cm_return_if_fail(item != NULL);
1838 if (!item->collapsed)
1839 gtk_cmctree_expand(ctree, node);
1840 else
1841 folderview_update_node(folderview, node);
1845 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1846 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1848 if (item) {
1849 GtkCMCTreeNode *node, *parent, *sibling;
1851 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1852 if (!node)
1853 g_warning("%s not found.", item->path);
1854 else {
1855 parent = GTK_CMCTREE_ROW(node)->parent;
1856 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1857 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1858 else
1859 sibling = GTK_CMCTREE_ROW(parent)->children;
1860 while (sibling) {
1861 FolderItem *tmp;
1863 tmp = gtk_cmctree_node_get_row_data
1864 (ctree, sibling);
1865 if (tmp && tmp->stype != F_NORMAL)
1866 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1867 else
1868 break;
1870 if (node != sibling)
1871 gtk_cmctree_move(ctree, node, parent, sibling);
1874 *prev = node;
1878 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1879 Folder *folder)
1881 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1882 GtkCMCTreeNode *prev = NULL;
1884 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1885 gtk_sctree_sort_recursive(ctree, root);
1886 if (root && GTK_CMCTREE_ROW(root)->parent) {
1887 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1888 return;
1890 set_special_folder(ctree, folder->inbox, root, &prev);
1891 set_special_folder(ctree, folder->outbox, root, &prev);
1892 set_special_folder(ctree, folder->draft, root, &prev);
1893 set_special_folder(ctree, folder->queue, root, &prev);
1894 set_special_folder(ctree, folder->trash, root, &prev);
1895 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1898 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1900 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1901 GtkCMCTreeNode *root;
1903 cm_return_if_fail(folder != NULL);
1905 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1906 folderview_gnode_func, folderview);
1907 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1908 folderview);
1909 folderview_sort_folders(folderview, root, folder);
1912 /* callback functions */
1913 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1914 GdkEventButton *event)
1916 FolderItem *item;
1917 Folder *folder;
1918 FolderViewPopup *fpopup;
1919 GtkActionGroup *action_group;
1920 GtkWidget *popup;
1921 FolderItem *special_trash = NULL, *special_queue = NULL;
1922 PrefsAccount *ac;
1923 GtkUIManager *ui_manager = gtk_ui_manager_new();
1925 if (folderview->ui_manager)
1926 g_object_unref(folderview->ui_manager);
1928 folderview->ui_manager = ui_manager;
1929 item = folderview_get_selected_item(folderview);
1931 cm_return_if_fail(item != NULL);
1932 cm_return_if_fail(item->folder != NULL);
1933 folder = item->folder;
1935 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1937 if (fpopup != NULL)
1938 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1939 else {
1940 fpopup = g_hash_table_lookup(folderview_popups, "common");
1941 action_group = g_hash_table_lookup(folderview->popups, "common");
1944 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1945 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1946 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1948 if (fpopup->add_menuitems)
1949 fpopup->add_menuitems(ui_manager, item);
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1961 if (fpopup->set_sensitivity != NULL)
1962 fpopup->set_sensitivity(ui_manager, item);
1964 if (NULL != (ac = account_find_from_item(item))) {
1965 special_trash = account_get_special_folder(ac, F_TRASH);
1966 special_queue = account_get_special_folder(ac, F_QUEUE);
1969 if ((item == folder->trash || item == special_trash
1970 || folder_has_parent_of_type(item, F_TRASH))) {
1971 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1972 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1975 if ((item == folder->queue || item == special_queue
1976 || folder_has_parent_of_type(item, F_QUEUE))) {
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1978 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1981 #define SET_SENS(name, sens) \
1982 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1984 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1985 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1986 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1987 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1988 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1989 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1990 folderview->selected == folderview->opened);
1991 SET_SENS("FolderViewPopup/Properties", TRUE);
1993 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1994 item->total_msgs >= 1 && !item->processing_pending);
1995 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1996 !item->no_select && !item->processing_pending);
1998 if (item == folder->trash || item == special_trash
1999 || folder_has_parent_of_type(item, F_TRASH)) {
2000 GSList *msglist = folder_item_get_msg_list(item);
2001 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2002 procmsg_msg_list_free(msglist);
2004 if (item == folder->queue || item == special_queue
2005 || folder_has_parent_of_type(item, F_QUEUE)) {
2006 GSList *msglist = folder_item_get_msg_list(item);
2007 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2008 procmsg_msg_list_free(msglist);
2010 #undef SET_SENS
2012 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2013 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2014 g_signal_connect(G_OBJECT(popup), "selection_done",
2015 G_CALLBACK(folderview_popup_close),
2016 folderview);
2017 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2018 event->button, event->time);
2021 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2022 FolderView *folderview)
2024 GtkCMCList *clist = GTK_CMCLIST(ctree);
2025 gint prev_row = -1, row = -1, column = -1;
2027 if (!event) return FALSE;
2028 if (event->window != clist->clist_window) return FALSE;
2030 if (event->button == 1 || event->button == 2) {
2031 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2032 folderview->open_folder = TRUE;
2034 if (event->type == GDK_2BUTTON_PRESS) {
2035 if (clist->selection) {
2036 GtkCMCTreeNode *node;
2038 node = GTK_CMCTREE_NODE(clist->selection->data);
2039 if (node) {
2040 gtk_cmctree_toggle_expansion(
2041 GTK_CMCTREE(ctree),
2042 node);
2043 folderview->open_folder = FALSE;
2047 return FALSE;
2050 if (event->button == 2 || event->button == 3) {
2051 /* right clicked */
2052 if (clist->selection) {
2053 GtkCMCTreeNode *node;
2055 node = GTK_CMCTREE_NODE(clist->selection->data);
2056 if (node)
2057 prev_row = gtkut_ctree_get_nth_from_node
2058 (GTK_CMCTREE(ctree), node);
2061 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2062 &row, &column))
2063 return FALSE;
2064 if (prev_row != row) {
2065 gtk_cmclist_unselect_all(clist);
2066 if (event->button == 2)
2067 folderview_select_node
2068 (folderview,
2069 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2070 row));
2071 else
2072 gtk_cmclist_select_row(clist, row, column);
2076 if (event->button != 3) return FALSE;
2078 folderview_set_sens_and_popup_menu(folderview, row, event);
2079 return FALSE;
2082 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2083 FolderView *folderview)
2085 int row = -1, column = -1;
2087 if (!event) return FALSE;
2089 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2090 &row, &column))
2091 return FALSE;
2092 if (event->button == 1 && folderview->open_folder == FALSE &&
2093 folderview->opened != NULL) {
2094 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2095 folderview->opened);
2096 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2099 return FALSE;
2102 #define BREAK_ON_MODIFIER_KEY() \
2103 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2105 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2106 FolderView *folderview)
2108 GtkCMCTreeNode *node;
2109 FolderItem *item;
2111 if (!event) return FALSE;
2113 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2114 return FALSE;
2116 switch (event->keyval) {
2117 case GDK_KEY_Right:
2118 if (folderview->selected) {
2119 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2120 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2121 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2122 folderview->selected);
2123 else
2124 folderview_select_node(folderview,
2125 folderview->selected);
2127 break;
2128 #ifdef GENERIC_UMPC
2129 case GDK_KEY_Return:
2130 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2131 gtk_cmctree_toggle_expansion(
2132 GTK_CMCTREE(folderview->ctree),
2133 folderview->selected);
2135 break;
2136 #else
2137 case GDK_KEY_Return:
2138 case GDK_KEY_KP_Enter:
2139 if (folderview->selected)
2140 folderview_select_node(folderview, folderview->selected);
2141 break;
2142 #endif
2143 case GDK_KEY_space:
2144 BREAK_ON_MODIFIER_KEY();
2145 if (folderview->selected) {
2146 if (folderview->opened == folderview->selected &&
2147 (!folderview->summaryview->folder_item ||
2148 folderview->summaryview->folder_item->total_msgs == 0))
2149 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2150 else
2151 folderview_select_node(folderview,
2152 folderview->selected);
2154 break;
2155 case GDK_KEY_Left:
2156 if (folderview->selected) {
2157 /* If the folder is expanded and can be collapsed, do that... */
2158 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2159 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2160 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2161 folderview->selected);
2162 } else {
2163 /* ...otherwise, move cursor to its parent node. */
2164 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2165 folderview->selected))) {
2166 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2167 NULL, folder_item_parent(item)))) {
2168 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2169 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2170 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2171 node, -1, 0, 0);
2176 break;
2177 case GDK_KEY_Home:
2178 case GDK_KEY_End:
2179 if (event->keyval == GDK_KEY_Home)
2180 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2181 else
2182 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2183 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2185 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2187 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2188 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2189 node, -1, 0, 0);
2190 break;
2191 default:
2192 break;
2195 return FALSE;
2198 typedef struct _PostponedSelectData
2200 GtkCMCTree *ctree;
2201 GtkCMCTreeNode *row;
2202 gint column;
2203 FolderView *folderview;
2204 } PostponedSelectData;
2206 static gboolean postpone_select(void *data)
2208 PostponedSelectData *psdata = (PostponedSelectData *)data;
2209 debug_print("trying again\n");
2211 psdata->folderview->postpone_select_id = 0;
2212 psdata->folderview->open_folder = TRUE;
2213 main_window_cursor_normal(psdata->folderview->mainwin);
2214 STATUSBAR_POP(psdata->folderview->mainwin);
2215 folderview_selected(psdata->ctree, psdata->row,
2216 psdata->column, psdata->folderview);
2217 g_free(psdata);
2218 return FALSE;
2221 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2223 if (folderview->opened) {
2224 if (dirty) {
2225 folderview->opened = NULL;
2226 return;
2229 FolderItem *olditem =
2230 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2231 folderview->opened);
2232 if (olditem) {
2233 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2234 olditem->path ? olditem->path:olditem->name);
2235 /* will be null if we just moved the previously opened folder */
2236 STATUSBAR_PUSH(folderview->mainwin, buf);
2237 main_window_cursor_wait(folderview->mainwin);
2238 g_free(buf);
2239 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2240 summary_show(folderview->summaryview, NULL, FALSE);
2241 folder_item_close(olditem);
2242 main_window_cursor_normal(folderview->mainwin);
2243 STATUSBAR_POP(folderview->mainwin);
2244 if (olditem->folder->klass->item_closed)
2245 olditem->folder->klass->item_closed(olditem);
2250 if (folderview->opened &&
2251 !GTK_CMCTREE_ROW(folderview->opened)->children)
2252 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2254 folderview->opened = NULL;
2256 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2257 gint column, FolderView *folderview)
2259 static gboolean can_select = TRUE; /* exclusive lock */
2260 gboolean opened;
2261 FolderItem *item;
2262 gchar *buf;
2263 int res = 0;
2264 GtkCMCTreeNode *old_opened = folderview->opened;
2265 START_TIMING("");
2266 folderview->selected = row;
2268 debug_print("newly selected %p, opened %p\n", folderview->selected,
2269 folderview->opened);
2270 if (folderview->opened == row) {
2271 folderview->open_folder = FALSE;
2272 END_TIMING();
2273 return;
2276 item = gtk_cmctree_node_get_row_data(ctree, row);
2277 if (!item) {
2278 END_TIMING();
2279 folderview->open_folder = FALSE;
2280 return;
2283 if (!can_select || summary_is_locked(folderview->summaryview)) {
2284 if (folderview->opened) {
2285 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2286 gtk_cmctree_select(ctree, folderview->opened);
2288 folderview->open_folder = FALSE;
2289 END_TIMING();
2290 return;
2293 if (!folderview->open_folder) {
2294 END_TIMING();
2295 return;
2298 can_select = FALSE;
2300 /* Save cache for old folder */
2301 /* We don't want to lose all caches if app crashes */
2302 /* Resets folderview->opened to NULL */
2303 folderview_close_opened(folderview, FALSE);
2305 /* CLAWS: set compose button type: news folder items
2306 * always have a news folder as parent */
2307 if (item->folder)
2308 toolbar_set_compose_button
2309 (folderview->mainwin->toolbar,
2310 FOLDER_TYPE(item->folder) == F_NEWS ?
2311 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2313 if (item->path)
2314 debug_print("Folder %s is selected\n", item->path);
2316 if (!GTK_CMCTREE_ROW(row)->children)
2317 gtk_cmctree_expand(ctree, row);
2319 /* ungrab the mouse event */
2320 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2321 gtk_grab_remove(GTK_WIDGET(ctree));
2322 if (gdk_pointer_is_grabbed())
2323 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2326 /* Open Folder */
2327 /* TODO: wwp: avoid displaying (null) in the status bar */
2328 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2329 item->path : "(null)");
2330 debug_print("%s\n", buf);
2331 STATUSBAR_PUSH(folderview->mainwin, buf);
2332 g_free(buf);
2334 main_window_cursor_wait(folderview->mainwin);
2336 if (folderview->scanning_folder == item->folder) {
2337 res = -2;
2338 } else {
2339 res = folder_item_open(item);
2342 if (res == -1 && item->no_select == FALSE) {
2343 main_window_cursor_normal(folderview->mainwin);
2344 STATUSBAR_POP(folderview->mainwin);
2346 alertpanel_error(_("Folder could not be opened."));
2348 folderview->open_folder = FALSE;
2349 can_select = TRUE;
2350 END_TIMING();
2351 return;
2352 } else if (res == -2 && item->no_select == FALSE) {
2353 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2354 data->ctree = ctree;
2355 data->row = row;
2356 data->column = column;
2357 data->folderview = folderview;
2358 debug_print("postponing open of %s till end of scan\n",
2359 item->path ? item->path:item->name);
2360 folderview->open_folder = FALSE;
2361 can_select = TRUE;
2362 if (folderview->postpone_select_id != 0)
2363 g_source_remove(folderview->postpone_select_id);
2364 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2365 END_TIMING();
2366 return;
2369 main_window_cursor_normal(folderview->mainwin);
2371 /* Show messages */
2372 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2373 opened = summary_show(folderview->summaryview, item, FALSE);
2375 folder_clean_cache_memory(item);
2377 if (!opened) {
2378 gtkut_ctree_set_focus_row(ctree, old_opened);
2379 gtk_cmctree_select(ctree, old_opened);
2380 folderview->opened = old_opened;
2381 } else {
2382 folderview->opened = row;
2383 if (gtk_cmctree_node_is_visible(ctree, row)
2384 != GTK_VISIBILITY_FULL)
2385 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2388 STATUSBAR_POP(folderview->mainwin);
2390 folderview->open_folder = FALSE;
2391 can_select = TRUE;
2392 END_TIMING();
2395 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2396 FolderView *folderview)
2398 FolderItem *item;
2400 item = gtk_cmctree_node_get_row_data(ctree, node);
2401 cm_return_if_fail(item != NULL);
2402 item->collapsed = FALSE;
2403 folderview_update_node(folderview, node);
2406 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2407 FolderView *folderview)
2409 FolderItem *item;
2411 item = gtk_cmctree_node_get_row_data(ctree, node);
2412 cm_return_if_fail(item != NULL);
2413 item->collapsed = TRUE;
2414 folderview_update_node(folderview, node);
2417 static void folderview_popup_close(GtkMenuShell *menu_shell,
2418 FolderView *folderview)
2420 if (!folderview->opened) return;
2422 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2425 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2426 FolderView *folderview)
2428 FolderColumnType type = folderview->col_state[column].type;
2430 prefs_common.folder_col_size[type] = width;
2433 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2435 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2436 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2437 GtkCMCTreeNode *node, *parent_node;
2438 gint *col_pos = folderview->col_pos;
2439 FolderItemUpdateData hookdata;
2441 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2442 if (parent_node == NULL)
2443 return;
2445 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2447 text[col_pos[F_COL_FOLDER]] = item->name;
2448 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2449 FOLDER_SPACING,
2450 folderxpm,
2451 folderopenxpm,
2452 FALSE, FALSE);
2453 gtk_cmctree_expand(ctree, parent_node);
2454 gtk_cmctree_node_set_row_data(ctree, node, item);
2455 folderview_sort_folders(folderview, parent_node, item->folder);
2457 hookdata.item = item;
2458 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2459 hookdata.msg = NULL;
2460 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2462 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2465 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2467 FolderView *folderview = (FolderView *)data;
2468 FolderItem *item;
2469 GSList *mlist = NULL;
2470 GSList *cur = NULL;
2471 FolderItem *special_trash = NULL;
2472 PrefsAccount *ac;
2474 if (!folderview->selected) return;
2475 item = folderview_get_selected_item(folderview);
2476 cm_return_if_fail(item != NULL);
2477 cm_return_if_fail(item->folder != NULL);
2479 if (NULL != (ac = account_find_from_item(item)))
2480 special_trash = account_get_special_folder(ac, F_TRASH);
2482 if (item != item->folder->trash && item != special_trash
2483 && !folder_has_parent_of_type(item, F_TRASH)) return;
2485 if (prefs_common.ask_on_clean) {
2486 if (alertpanel(_("Empty trash"),
2487 _("Delete all messages in trash?"),
2488 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2489 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2490 return;
2493 mlist = folder_item_get_msg_list(item);
2495 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2496 MsgInfo * msginfo = (MsgInfo *) cur->data;
2497 if (MSG_IS_LOCKED(msginfo->flags))
2498 continue;
2499 /* is it partially received? (partial_recv isn't cached) */
2500 if (msginfo->total_size != 0 &&
2501 msginfo->size != (off_t)msginfo->total_size)
2502 partial_mark_for_delete(msginfo);
2504 procmsg_msg_list_free(mlist);
2506 folder_item_remove_all_msg(item);
2509 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2511 FolderView *folderview = (FolderView *)data;
2512 FolderItem *item;
2513 FolderItem *special_queue = NULL;
2514 PrefsAccount *ac;
2515 gchar *errstr = NULL;
2517 if (!folderview->selected) return;
2518 item = folderview_get_selected_item(folderview);
2519 cm_return_if_fail(item != NULL);
2520 cm_return_if_fail(item->folder != NULL);
2522 if (NULL != (ac = account_find_from_item(item)))
2523 special_queue = account_get_special_folder(ac, F_QUEUE);
2525 if (item != item->folder->queue && item != special_queue
2526 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2528 if (procmsg_queue_is_empty(item))
2529 return;
2531 if (prefs_common.work_offline)
2532 if (alertpanel(_("Offline warning"),
2533 _("You're working offline. Override?"),
2534 GTK_STOCK_NO, GTK_STOCK_YES,
2535 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2536 return;
2538 /* ask for confirmation before sending queued messages only
2539 in online mode and if there is at least one message queued
2540 in any of the folder queue
2542 if (prefs_common.confirm_send_queued_messages) {
2543 if (!prefs_common.work_offline) {
2544 if (alertpanel(_("Send queued messages"),
2545 _("Send all queued messages?"),
2546 GTK_STOCK_CANCEL, _("_Send"),
2547 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2548 return;
2552 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2553 if (!errstr)
2554 alertpanel_error_log(_("Some errors occurred while "
2555 "sending queued messages."));
2556 else {
2557 alertpanel_error_log(_("Some errors occurred "
2558 "while sending queued messages:\n%s"), errstr);
2559 g_free(errstr);
2564 static void folderview_search_cb(GtkAction *action, gpointer data)
2566 FolderView *folderview = (FolderView *)data;
2567 summary_search(folderview->summaryview);
2570 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2572 FolderView *folderview = (FolderView *)data;
2573 FolderItem *item;
2575 if (!folderview->selected) return;
2577 item = folderview_get_selected_item(folderview);
2578 cm_return_if_fail(item != NULL);
2579 cm_return_if_fail(item->folder != NULL);
2581 item->processing_pending = TRUE;
2582 folder_item_apply_processing(item);
2583 item->processing_pending = FALSE;
2586 static void folderview_property_cb(GtkAction *action, gpointer data)
2588 FolderView *folderview = (FolderView *)data;
2589 FolderItem *item;
2591 if (!folderview->selected) return;
2593 item = folderview_get_selected_item(folderview);
2594 cm_return_if_fail(item != NULL);
2595 cm_return_if_fail(item->folder != NULL);
2597 prefs_folder_item_open(item);
2600 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2602 GSList *list = NULL;
2603 GSList *done = NULL;
2604 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2606 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2607 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2608 && list->data != node) {
2609 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2610 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2613 for (list = done; list != NULL; list = g_slist_next(list)) {
2614 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2615 list->data);
2617 g_slist_free(done);
2620 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2621 FolderItem *to_folder, gboolean copy)
2623 FolderItem *new_folder = NULL;
2624 gchar *buf;
2625 gint status;
2627 cm_return_if_fail(folderview != NULL);
2628 cm_return_if_fail(from_folder != NULL);
2629 cm_return_if_fail(to_folder != NULL);
2631 if (prefs_common.warn_dnd) {
2632 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2633 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2634 from_folder->name, to_folder->name);
2635 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2636 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2637 TRUE, NULL, ALERT_QUESTION);
2638 g_free(buf);
2640 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2641 return;
2642 else if (status & G_ALERTDISABLE)
2643 prefs_common.warn_dnd = FALSE;
2646 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2647 from_folder->name, to_folder->name);
2648 STATUSBAR_PUSH(folderview->mainwin, buf);
2649 g_free(buf);
2650 summary_clear_all(folderview->summaryview);
2651 folderview->opened = NULL;
2652 folderview->selected = NULL;
2653 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2654 inc_lock();
2655 main_window_cursor_wait(folderview->mainwin);
2657 statusbar_verbosity_set(FALSE);
2658 folder_item_update_freeze();
2659 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2660 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2661 statusbar_verbosity_set(FALSE);
2662 main_window_cursor_normal(folderview->mainwin);
2663 STATUSBAR_POP(folderview->mainwin);
2664 folder_item_update_thaw();
2665 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2667 folderview_sort_folders(folderview,
2668 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2669 NULL, to_folder), new_folder->folder);
2670 folderview_select(folderview, new_folder);
2671 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2672 } else {
2673 statusbar_verbosity_set(FALSE);
2674 main_window_cursor_normal(folderview->mainwin);
2675 STATUSBAR_POP(folderview->mainwin);
2676 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2677 folder_item_update_thaw();
2678 switch (status) {
2679 case F_MOVE_FAILED_DEST_IS_PARENT:
2680 alertpanel_error(_("Source and destination are the same."));
2681 break;
2682 case F_MOVE_FAILED_DEST_IS_CHILD:
2683 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2684 _("Can't move a folder to one of its children."));
2685 break;
2686 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2687 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2688 break;
2689 default:
2690 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2691 break;
2694 inc_unlock();
2695 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2698 static gint folderview_clist_compare(GtkCMCList *clist,
2699 gconstpointer ptr1, gconstpointer ptr2)
2701 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2702 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2704 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2706 return item1->order - item2->order;
2709 // if only one folder has an order it comes first
2710 if (item1->order > 0)
2712 return -1;
2714 if (item2->order > 0)
2716 return 1;
2719 if (!item1->name)
2720 return (item2->name != NULL);
2721 if (!item2->name)
2722 return -1;
2724 return g_utf8_collate(item1->name, item2->name);
2727 static void folderview_processing_cb(GtkAction *action, gpointer data)
2729 FolderView *folderview = (FolderView *)data;
2730 FolderItem *item;
2731 gchar *id, *title;
2733 if (!folderview->selected) return;
2735 item = folderview_get_selected_item(folderview);
2736 cm_return_if_fail(item != NULL);
2737 cm_return_if_fail(item->folder != NULL);
2739 id = folder_item_get_identifier(item);
2740 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2741 g_free (id);
2743 prefs_filtering_open(&item->prefs->processing, title,
2744 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2745 g_free (title);
2748 void folderview_set_target_folder_color(gint color_op)
2750 GList *list;
2751 FolderView *folderview;
2753 for (list = folderview_list; list != NULL; list = list->next) {
2754 folderview = (FolderView *)list->data;
2755 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2759 static gchar *last_smallfont = NULL;
2760 static gchar *last_normalfont = NULL;
2761 static gchar *last_boldfont = NULL;
2762 static gboolean last_derive = 0;
2764 void folderview_reinit_fonts(FolderView *folderview)
2766 /* force reinit */
2767 g_free(last_smallfont);
2768 last_smallfont = NULL;
2769 g_free(last_normalfont);
2770 last_normalfont = NULL;
2771 g_free(last_boldfont);
2772 last_boldfont = NULL;
2775 void folderview_reflect_prefs(void)
2777 gboolean update_font = FALSE;
2778 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2779 FolderItem *item = folderview_get_selected_item(folderview);
2780 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2781 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2782 gint height = gtk_adjustment_get_value(pos);
2784 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW],
2785 &folderview->color_new);
2786 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_TGT_FOLDER],
2787 &folderview->color_op);
2789 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2790 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2791 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2792 last_derive != prefs_common.derive_from_normal_font)
2793 update_font = TRUE;
2795 if (!update_font)
2796 return;
2798 g_free(last_smallfont);
2799 last_smallfont = g_strdup(SMALL_FONT);
2800 g_free(last_normalfont);
2801 last_normalfont = g_strdup(NORMAL_FONT);
2802 g_free(last_boldfont);
2803 last_boldfont = g_strdup(BOLD_FONT);
2804 last_derive = prefs_common.derive_from_normal_font;
2806 #define STYLE_FREE(s) \
2807 if (s != NULL) { \
2808 g_object_unref(s); \
2809 s = NULL; \
2812 STYLE_FREE(bold_style);
2814 #undef STYLE_FREE
2816 folderview_set_fonts(folderview);
2818 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2819 folderview_column_set_titles(folderview);
2820 folderview_set_all();
2822 g_signal_handlers_block_by_func
2823 (G_OBJECT(folderview->ctree),
2824 G_CALLBACK(folderview_selected), folderview);
2826 if (item) {
2827 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2828 GTK_CMCTREE(folderview->ctree), NULL, item);
2830 folderview_select(folderview, item);
2831 folderview->open_folder = FALSE;
2832 folderview->selected = node;
2835 g_signal_handlers_unblock_by_func
2836 (G_OBJECT(folderview->ctree),
2837 G_CALLBACK(folderview_selected), folderview);
2839 pos = gtk_scrolled_window_get_vadjustment(
2840 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2841 gtk_adjustment_set_value(pos, height);
2842 gtk_adjustment_changed(pos);
2843 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2846 static void drag_state_stop(FolderView *folderview)
2848 if (folderview->drag_timer_id)
2849 g_source_remove(folderview->drag_timer_id);
2850 folderview->drag_timer_id = 0;
2851 folderview->drag_node = NULL;
2854 static gboolean folderview_defer_expand(FolderView *folderview)
2856 if (folderview->drag_node) {
2857 folderview_recollapse_nodes(folderview, folderview->drag_node);
2858 if (folderview->drag_item->collapsed) {
2859 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2860 folderview->nodes_to_recollapse = g_slist_append
2861 (folderview->nodes_to_recollapse, folderview->drag_node);
2864 folderview->drag_item = NULL;
2865 folderview->drag_timer_id = 0;
2866 return FALSE;
2869 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2871 /* the idea is that we call drag_state_start() whenever we want expansion to
2872 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2873 * we need to call drag_state_stop() */
2874 drag_state_stop(folderview);
2875 /* request expansion */
2876 if (0 != (folderview->drag_timer_id = g_timeout_add
2877 (prefs_common.hover_timeout,
2878 (GSourceFunc)folderview_defer_expand,
2879 folderview))) {
2880 folderview->drag_node = node;
2881 folderview->drag_item = item;
2884 #ifndef GENERIC_UMPC
2885 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2886 FolderView *folderview)
2888 GdkDragContext *context;
2890 cm_return_if_fail(folderview != NULL);
2891 if (folderview->selected == NULL) return;
2892 if (folderview->nodes_to_recollapse)
2893 g_slist_free(folderview->nodes_to_recollapse);
2894 folderview->nodes_to_recollapse = NULL;
2895 context = gtk_drag_begin(widget, folderview->target_list,
2896 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2897 gtk_drag_set_icon_default(context);
2899 #endif
2900 static void folderview_drag_data_get(GtkWidget *widget,
2901 GdkDragContext *drag_context,
2902 GtkSelectionData *selection_data,
2903 guint info,
2904 guint time,
2905 FolderView *folderview)
2907 FolderItem *item;
2908 GList *sel;
2909 gchar *source = NULL;
2910 if (info == TARGET_DUMMY) {
2911 sel = GTK_CMCLIST(folderview->ctree)->selection;
2912 if (!sel)
2913 return;
2915 item = gtk_cmctree_node_get_row_data
2916 (GTK_CMCTREE(folderview->ctree),
2917 GTK_CMCTREE_NODE(sel->data));
2918 if (item) {
2919 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2920 gtk_selection_data_set(selection_data,
2921 gtk_selection_data_get_target(selection_data), 8,
2922 source, strlen(source));
2924 } else {
2925 g_warning("unknown info %d", info);
2929 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2931 FolderUpdateData *hookdata;
2932 FolderView *folderview;
2933 GtkWidget *ctree;
2935 hookdata = source;
2936 folderview = (FolderView *) userdata;
2937 cm_return_val_if_fail(hookdata != NULL, FALSE);
2938 cm_return_val_if_fail(folderview != NULL, FALSE);
2940 ctree = folderview->ctree;
2941 cm_return_val_if_fail(ctree != NULL, FALSE);
2943 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2944 folderview_create_folder_node(folderview, hookdata->item);
2945 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2946 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2947 NULL, folder_item_parent(hookdata->item));
2948 folderview_sort_folders(folderview, node, hookdata->folder);
2949 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2950 GtkCMCTreeNode *node;
2952 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2953 if (node != NULL) {
2954 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2955 if (folderview->selected == node)
2956 folderview->selected = NULL;
2957 if (folderview->opened == node)
2958 folderview->opened = NULL;
2960 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2961 /* do nothing, it's done by the ADD and REMOVE) */
2962 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2963 folderview_set(folderview);
2965 return FALSE;
2968 static gboolean folderview_dnd_scroll_cb(gpointer data)
2970 FolderView *folderview = (FolderView *)data;
2971 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2972 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2973 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2974 gint max = (int)gtk_adjustment_get_upper(pos) -
2975 (int)gtk_adjustment_get_page_size(pos);
2977 if (folderview->scroll_value == 0) {
2978 folderview->scroll_timeout_id = 0;
2979 return FALSE;
2982 if (folderview->scroll_value > 0 && new_val > max) {
2983 new_val = max;
2984 } else if (folderview->scroll_value < 0 && new_val < 0) {
2985 new_val = 0;
2987 gtk_adjustment_set_value(pos, new_val);
2989 return TRUE;
2992 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2993 GdkDragContext *context,
2994 gint x,
2995 gint y,
2996 guint time,
2997 FolderView *folderview)
2999 gint row, column;
3000 FolderItem *item = NULL, *src_item = NULL;
3001 GtkCMCTreeNode *node = NULL;
3002 gboolean acceptable = FALSE;
3003 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3004 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3005 int height = (int)gtk_adjustment_get_page_size(pos);
3006 int total_height = (int)gtk_adjustment_get_upper(pos);
3007 int vpos = (int)gtk_adjustment_get_value(pos);
3008 int offset = prefs_common.show_col_headers ? 24:0;
3009 int dist;
3011 if (gtk_cmclist_get_selection_info
3012 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3013 GtkWidget *srcwidget;
3015 if (y > height - (48 - offset) && height + vpos < total_height) {
3016 dist = -(height - (48 - offset) - y);
3017 folderview->scroll_value = 1.41f * (1+(dist / 6));
3018 } else if (y < 72 - (24 - offset) && y >= 0) {
3019 dist = 72 - (24 - offset) - y;
3020 folderview->scroll_value = -1.41f * (1+(dist / 6));
3021 } else {
3022 folderview->scroll_value = 0;
3024 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3025 folderview->scroll_timeout_id =
3026 g_timeout_add(30, folderview_dnd_scroll_cb,
3027 folderview);
3030 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3031 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3032 src_item = folderview->summaryview->folder_item;
3034 srcwidget = gtk_drag_get_source_widget(context);
3035 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3036 /* comes from summaryview */
3037 /* we are copying messages, so only accept folder items that are not
3038 the source item, are no root items and can copy messages */
3039 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3040 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3041 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3042 acceptable = TRUE;
3043 } else if (srcwidget == folderview->ctree) {
3044 /* comes from folderview */
3045 /* we are moving folder items, only accept folders that are not
3046 the source items and can copy messages and create folder items */
3047 if (item && item->folder && src_item && src_item != item &&
3048 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3049 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3050 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3051 || item->folder == src_item->folder))
3052 acceptable = TRUE;
3053 } else {
3054 /* comes from another app */
3055 /* we are adding messages, so only accept folder items that are
3056 no root items and can copy messages */
3057 if (item && item->folder && folder_item_parent(item) != NULL
3058 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3059 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3060 acceptable = TRUE;
3064 if (acceptable || (src_item && src_item == item))
3065 drag_state_start(folderview, node, item);
3067 if (acceptable) {
3068 g_signal_handlers_block_by_func
3069 (G_OBJECT(widget),
3070 G_CALLBACK(folderview_selected), folderview);
3071 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3072 g_signal_handlers_unblock_by_func
3073 (G_OBJECT(widget),
3074 G_CALLBACK(folderview_selected), folderview);
3075 gdk_drag_status(context,
3076 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3077 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3078 } else {
3079 if (folderview->opened)
3080 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3081 gdk_drag_status(context, 0, time);
3084 return acceptable;
3087 static void folderview_drag_leave_cb(GtkWidget *widget,
3088 GdkDragContext *context,
3089 guint time,
3090 FolderView *folderview)
3092 drag_state_stop(folderview);
3093 folderview->scroll_value = 0;
3094 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3097 static void free_info (gpointer stuff, gpointer data)
3099 g_free(stuff);
3102 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3103 guint time, FolderItem *item)
3105 GList *list, *tmp;
3106 GSList *msglist = NULL;
3107 list = uri_list_extract_filenames(data);
3108 if (!(item && item->folder && folder_item_parent(item) != NULL
3109 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3111 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3112 debug_print("item doesn't fit\n");
3113 return;
3115 if (!list) {
3116 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3117 debug_print("list is empty\n");
3118 return;
3120 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3121 MsgFileInfo *info = NULL;
3123 if (file_is_email((gchar *)tmp->data)) {
3124 info = g_new0(MsgFileInfo, 1);
3125 info->msginfo = NULL;
3126 info->file = (gchar *)tmp->data;
3127 msglist = g_slist_prepend(msglist, info);
3128 debug_print("file is a mail\n");
3129 } else {
3130 debug_print("file isn't a mail\n");
3133 if (msglist) {
3134 msglist = g_slist_reverse(msglist);
3135 folder_item_add_msgs(item, msglist, FALSE);
3136 g_slist_foreach(msglist, free_info, NULL);
3137 g_slist_free(msglist);
3138 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3139 } else {
3140 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3142 list_free_strings_full(list);
3145 static void folderview_drag_received_cb(GtkWidget *widget,
3146 GdkDragContext *drag_context,
3147 gint x,
3148 gint y,
3149 GtkSelectionData *data,
3150 guint info,
3151 guint time,
3152 FolderView *folderview)
3154 gint row, column;
3155 FolderItem *item = NULL, *src_item;
3156 GtkCMCTreeNode *node;
3157 int offset = prefs_common.show_col_headers ? 24:0;
3159 folderview->scroll_value = 0;
3161 if (info == TARGET_DUMMY) {
3162 drag_state_stop(folderview);
3163 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3164 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3165 /* comes from summaryview */
3166 if (gtk_cmclist_get_selection_info
3167 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3168 return;
3170 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3171 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3172 src_item = folderview->summaryview->folder_item;
3174 if (item->no_select) {
3175 alertpanel_error(_("The destination folder can only be used to "
3176 "store subfolders."));
3177 return;
3179 /* re-check (due to acceptable possibly set for folder moves */
3180 if (!(item && item->folder && item->path && !item->no_select &&
3181 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3182 return;
3185 switch (gdk_drag_context_get_selected_action(drag_context)) {
3186 case GDK_ACTION_COPY:
3187 summary_copy_selected_to(folderview->summaryview, item);
3188 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3189 break;
3190 case GDK_ACTION_MOVE:
3191 case GDK_ACTION_DEFAULT:
3192 default:
3193 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3194 summary_copy_selected_to(folderview->summaryview, item);
3195 else
3196 summary_move_selected_to(folderview->summaryview, item);
3197 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3199 } else {
3200 /* comes from folderview */
3201 char *source;
3202 gboolean folder_is_normal = TRUE;
3203 gboolean copy = (GDK_ACTION_COPY ==
3204 gdk_drag_context_get_selected_action(drag_context));
3206 source = (char *)gtk_selection_data_get_data(data) + 17;
3207 if (gtk_cmclist_get_selection_info
3208 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3209 || *source == 0) {
3210 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3211 return;
3213 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3214 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3215 src_item = folder_find_item_from_identifier(source);
3217 folder_is_normal =
3218 src_item != NULL &&
3219 src_item->stype == F_NORMAL &&
3220 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3221 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3222 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3223 !folder_has_parent_of_type(src_item, F_TRASH);
3224 if (!item || !src_item || !folder_is_normal) {
3225 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3226 return;
3229 folderview_move_folder(folderview, src_item, item, copy);
3230 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3232 folderview->nodes_to_recollapse = NULL;
3233 } else if (info == TARGET_MAIL_URI_LIST) {
3234 if (gtk_cmclist_get_selection_info
3235 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3236 return;
3238 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3239 if (!node) {
3240 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3241 debug_print("no node\n");
3242 return;
3244 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3245 if (!item) {
3246 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3247 debug_print("no item\n");
3248 return;
3250 folderview_finish_dnd(gtk_selection_data_get_data(data),
3251 drag_context, time, item);
3255 static void folderview_drag_end_cb(GtkWidget *widget,
3256 GdkDragContext *drag_context,
3257 FolderView *folderview)
3259 drag_state_stop(folderview);
3260 folderview->scroll_value = 0;
3261 g_slist_free(folderview->nodes_to_recollapse);
3262 folderview->nodes_to_recollapse = NULL;
3265 void folderview_register_popup(FolderViewPopup *fpopup)
3267 GList *folderviews;
3269 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3270 FolderView *folderview = folderviews->data;
3271 GtkActionGroup *factory;
3273 factory = create_action_group(folderview, fpopup);
3274 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3276 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3279 void folderview_unregister_popup(FolderViewPopup *fpopup)
3281 GList *folderviews;
3284 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3285 FolderView *folderview = folderviews->data;
3287 g_hash_table_remove(folderview->popups, fpopup->klass);
3289 g_hash_table_remove(folderview_popups, fpopup->klass);
3292 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3294 g_return_if_fail(folderview != NULL);
3295 g_return_if_fail(item != NULL);
3297 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3298 g_return_if_fail(ctree != NULL);
3300 GtkCMCTreeNode *node =
3301 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3302 g_return_if_fail(node != NULL);
3304 gtk_cmctree_remove_node(ctree, node);
3307 void folderview_freeze(FolderView *folderview)
3309 if (folderview)
3310 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3313 void folderview_thaw(FolderView *folderview)
3315 if (folderview)
3316 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3319 void folderview_grab_focus(FolderView *folderview)
3321 if (folderview)
3322 gtk_widget_grab_focus(folderview->ctree);
3325 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3326 gpointer data)
3328 prefs_folder_column_open();
3331 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3332 GdkEvent *_event,
3333 gpointer user_data)
3335 GdkEventButton *event = (GdkEventButton *)_event;
3336 FolderView *folderview = (FolderView *)user_data;
3338 cm_return_val_if_fail(folderview != NULL, FALSE);
3340 /* Only handle single button presses. */
3341 if (event->type == GDK_2BUTTON_PRESS ||
3342 event->type == GDK_3BUTTON_PRESS)
3343 return FALSE;
3345 /* Handle right-click for context menu */
3346 if (event->button == 3) {
3347 gtk_menu_popup(GTK_MENU(folderview->headerpopupmenu),
3348 NULL, NULL, NULL, NULL, 3, event->time);
3349 return TRUE;
3352 return FALSE;