2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtkwidget.h>
26 #include <gtk/gtkscrolledwindow.h>
27 #include <gtk/gtkctree.h>
28 #include <gtk/gtkcontainer.h>
29 #include <gtk/gtkclist.h>
30 #include <gtk/gtkstyle.h>
31 #include <gtk/gtksignal.h>
32 #include <gtk/gtkmain.h>
33 #include <gtk/gtkstatusbar.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
42 #include "mainwindow.h"
43 #include "folderview.h"
44 #include "summaryview.h"
45 #include "summary_search.h"
46 #include "inputdialog.h"
47 #include "manage_window.h"
48 #include "alertpanel.h"
50 #include "stock_pixmap.h"
54 #include "prefs_common.h"
55 #include "prefs_account.h"
56 #include "prefs_filtering.h"
57 #include "prefs_folder_item.h"
60 #include "foldersel.h"
62 #include "statusbar.h"
64 #include "folderutils.h"
65 #include "partial_download.h"
66 #include "prefs_folder_column.h"
67 #include "filtering.h"
68 #include "quicksearch.h"
73 #define COL_FOLDER_WIDTH 150
74 #define COL_NUM_WIDTH 32
76 static GList *folderview_list = NULL;
78 static GtkStyle *normal_style;
79 static GtkStyle *normal_color_style;
80 static GtkStyle *bold_style;
81 static GtkStyle *bold_color_style;
82 static GtkStyle *bold_tgtfold_style;
84 static GdkPixmap *inboxxpm;
85 static GdkBitmap *inboxxpmmask;
86 static GdkPixmap *inboxhrmxpm;
87 static GdkBitmap *inboxhrmxpmmask;
88 static GdkPixmap *inboxopenxpm;
89 static GdkBitmap *inboxopenxpmmask;
90 static GdkPixmap *inboxopenhrmxpm;
91 static GdkBitmap *inboxopenhrmxpmmask;
92 static GdkPixmap *outboxxpm;
93 static GdkBitmap *outboxxpmmask;
94 static GdkPixmap *outboxhrmxpm;
95 static GdkBitmap *outboxhrmxpmmask;
96 static GdkPixmap *outboxopenxpm;
97 static GdkBitmap *outboxopenxpmmask;
98 static GdkPixmap *outboxopenhrmxpm;
99 static GdkBitmap *outboxopenhrmxpmmask;
100 static GdkPixmap *folderxpm;
101 static GdkBitmap *folderxpmmask;
102 static GdkPixmap *folderhrmxpm;
103 static GdkBitmap *folderhrmxpmmask;
104 static GdkPixmap *folderopenxpm;
105 static GdkBitmap *folderopenxpmmask;
106 static GdkPixmap *folderopenhrmxpm;
107 static GdkBitmap *folderopenhrmxpmmask;
108 static GdkPixmap *trashopenxpm;
109 static GdkBitmap *trashopenxpmmask;
110 static GdkPixmap *trashopenhrmxpm;
111 static GdkBitmap *trashopenhrmxpmmask;
112 static GdkPixmap *trashxpm;
113 static GdkBitmap *trashxpmmask;
114 static GdkPixmap *trashhrmxpm;
115 static GdkBitmap *trashhrmxpmmask;
116 static GdkPixmap *queuexpm;
117 static GdkBitmap *queuexpmmask;
118 static GdkPixmap *queuehrmxpm;
119 static GdkBitmap *queuehrmxpmmask;
120 static GdkPixmap *queueopenxpm;
121 static GdkBitmap *queueopenxpmmask;
122 static GdkPixmap *queueopenhrmxpm;
123 static GdkBitmap *queueopenhrmxpmmask;
124 static GdkPixmap *draftsxpm;
125 static GdkBitmap *draftsxpmmask;
126 static GdkPixmap *draftsopenxpm;
127 static GdkBitmap *draftsopenxpmmask;
128 static GdkPixmap *noselectxpm;
129 static GdkBitmap *noselectxpmmask;
131 static GdkPixmap *m_inboxxpm;
132 static GdkBitmap *m_inboxxpmmask;
133 static GdkPixmap *m_inboxhrmxpm;
134 static GdkBitmap *m_inboxhrmxpmmask;
135 static GdkPixmap *m_inboxopenxpm;
136 static GdkBitmap *m_inboxopenxpmmask;
137 static GdkPixmap *m_inboxopenhrmxpm;
138 static GdkBitmap *m_inboxopenhrmxpmmask;
139 static GdkPixmap *m_outboxxpm;
140 static GdkBitmap *m_outboxxpmmask;
141 static GdkPixmap *m_outboxhrmxpm;
142 static GdkBitmap *m_outboxhrmxpmmask;
143 static GdkPixmap *m_outboxopenxpm;
144 static GdkBitmap *m_outboxopenxpmmask;
145 static GdkPixmap *m_outboxopenhrmxpm;
146 static GdkBitmap *m_outboxopenhrmxpmmask;
147 static GdkPixmap *m_folderxpm;
148 static GdkBitmap *m_folderxpmmask;
149 static GdkPixmap *m_folderhrmxpm;
150 static GdkBitmap *m_folderhrmxpmmask;
151 static GdkPixmap *m_folderopenxpm;
152 static GdkBitmap *m_folderopenxpmmask;
153 static GdkPixmap *m_folderopenhrmxpm;
154 static GdkBitmap *m_folderopenhrmxpmmask;
155 static GdkPixmap *m_trashopenxpm;
156 static GdkBitmap *m_trashopenxpmmask;
157 static GdkPixmap *m_trashopenhrmxpm;
158 static GdkBitmap *m_trashopenhrmxpmmask;
159 static GdkPixmap *m_trashxpm;
160 static GdkBitmap *m_trashxpmmask;
161 static GdkPixmap *m_trashhrmxpm;
162 static GdkBitmap *m_trashhrmxpmmask;
163 static GdkPixmap *m_queuexpm;
164 static GdkBitmap *m_queuexpmmask;
165 static GdkPixmap *m_queuehrmxpm;
166 static GdkBitmap *m_queuehrmxpmmask;
167 static GdkPixmap *m_queueopenxpm;
168 static GdkBitmap *m_queueopenxpmmask;
169 static GdkPixmap *m_queueopenhrmxpm;
170 static GdkBitmap *m_queueopenhrmxpmmask;
171 static GdkPixmap *m_draftsxpm;
172 static GdkBitmap *m_draftsxpmmask;
173 static GdkPixmap *m_draftsopenxpm;
174 static GdkBitmap *m_draftsopenxpmmask;
176 static GdkPixmap *newxpm;
177 static GdkBitmap *newxpmmask;
178 static GdkPixmap *unreadxpm;
179 static GdkBitmap *unreadxpmmask;
180 static GdkPixmap *readxpm;
181 static GdkBitmap *readxpmmask;
183 static void folderview_select_node (FolderView *folderview,
185 static void folderview_set_folders (FolderView *folderview);
186 static void folderview_sort_folders (FolderView *folderview,
189 static void folderview_append_folder (FolderView *folderview,
191 static void folderview_update_node (FolderView *folderview,
194 static gint folderview_clist_compare (GtkCList *clist,
198 /* callback functions */
199 static gboolean folderview_button_pressed (GtkWidget *ctree,
200 GdkEventButton *event,
201 FolderView *folderview);
202 static gboolean folderview_button_released (GtkWidget *ctree,
203 GdkEventButton *event,
204 FolderView *folderview);
205 static gboolean folderview_key_pressed (GtkWidget *widget,
207 FolderView *folderview);
208 static void folderview_selected (GtkCTree *ctree,
211 FolderView *folderview);
212 static void folderview_tree_expanded (GtkCTree *ctree,
214 FolderView *folderview);
215 static void folderview_tree_collapsed (GtkCTree *ctree,
217 FolderView *folderview);
218 static void folderview_popup_close (GtkMenuShell *menu_shell,
219 FolderView *folderview);
220 static void folderview_col_resized (GtkCList *clist,
223 FolderView *folderview);
225 static void mark_all_read_cb (FolderView *folderview,
229 static void folderview_empty_trash_cb (FolderView *folderview,
233 static void folderview_send_queue_cb (FolderView *folderview,
237 static void folderview_search_cb (FolderView *folderview,
241 static void folderview_property_cb (FolderView *folderview,
245 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
246 GdkDragContext *context,
250 FolderView *folderview);
251 static void folderview_drag_leave_cb (GtkWidget *widget,
252 GdkDragContext *context,
254 FolderView *folderview);
255 static void folderview_drag_received_cb (GtkWidget *widget,
256 GdkDragContext *drag_context,
259 GtkSelectionData *data,
262 FolderView *folderview);
263 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
264 FolderView *folderview);
265 static void folderview_drag_data_get (GtkWidget *widget,
266 GdkDragContext *drag_context,
267 GtkSelectionData *selection_data,
270 FolderView *folderview);
271 static void folderview_drag_end_cb (GtkWidget *widget,
272 GdkDragContext *drag_context,
273 FolderView *folderview);
275 static void folderview_create_folder_node (FolderView *folderview,
277 static gboolean folderview_update_folder (gpointer source,
279 static gboolean folderview_update_item_claws (gpointer source,
281 static void folderview_processing_cb(FolderView *folderview, guint action,
283 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
284 GdkEventButton *event);
286 GHashTable *folderview_popups;
288 static GtkItemFactoryEntry folderview_common_popup_entries[] =
290 {N_("/Mark all re_ad"), NULL, mark_all_read_cb, 0, NULL},
291 {"/---", NULL, NULL, 0, "<Separator>"},
292 {N_("/_Search folder..."), NULL, folderview_search_cb, 0, NULL},
293 {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL},
294 {N_("/Process_ing..."), NULL, folderview_processing_cb, 0, NULL},
297 static GtkItemFactoryEntry folder_view_trash_popup_entries[] = {
298 {"/------trashsep", NULL, NULL, 0, "<Separator>"},
299 {N_("/Empty _trash..."), NULL, folderview_empty_trash_cb, 0, NULL},
302 static GtkItemFactoryEntry folder_view_queue_popup_entries[] = {
303 {"/------queuesep", NULL, NULL, 0, "<Separator>"},
304 {N_("/Send _queue..."), NULL, folderview_send_queue_cb, 0, NULL},
308 GtkTargetEntry folderview_drag_types[] =
310 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
311 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
314 void folderview_initialize(void)
316 FolderViewPopup *fpopup;
318 GSList *entries = NULL;
320 fpopup = g_new0(FolderViewPopup, 1);
322 n_entries = sizeof(folderview_common_popup_entries) /
323 sizeof(folderview_common_popup_entries[0]);
324 for (i = 0; i < n_entries; i++)
325 entries = g_slist_append(entries, &folderview_common_popup_entries[i]);
327 fpopup->klass = "common";
328 fpopup->path = "<CommonFolder>";
329 fpopup->entries = entries;
330 fpopup->set_sensitivity = NULL;
332 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
333 g_hash_table_insert(folderview_popups, "common", fpopup);
336 static GtkItemFactory *create_ifactory(FolderView *folderview, FolderViewPopup *fpopup)
339 GtkItemFactory *factory;
340 FolderViewPopup *fpopup_common;
343 factory = gtk_item_factory_new(GTK_TYPE_MENU, fpopup->path, NULL);
344 gtk_item_factory_set_translate_func(factory, menu_translate,
347 for (entries = fpopup->entries; entries != NULL; entries = g_slist_next(entries))
348 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
350 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
351 if (fpopup_common != fpopup)
352 for (entries = fpopup_common->entries; entries != NULL; entries = g_slist_next(entries))
353 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
355 popup = gtk_item_factory_get_widget(factory, fpopup->path);
356 g_signal_connect(G_OBJECT(popup), "selection_done",
357 G_CALLBACK(folderview_popup_close),
363 static void create_ifactories(gpointer key, gpointer value, gpointer data)
365 FolderView *folderview = data;
366 FolderViewPopup *fpopup = value;
367 GtkItemFactory *factory;
369 factory = create_ifactory(folderview, fpopup);
370 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
373 static void folderview_column_set_titles(FolderView *folderview)
375 GtkWidget *ctree = folderview->ctree;
376 GtkWidget *label_new;
377 GtkWidget *label_unread;
378 GtkWidget *label_total;
380 GtkWidget *hbox_unread;
381 GtkWidget *hbox_total;
382 gint *col_pos = folderview->col_pos;
384 debug_print("setting titles...\n");
385 gtk_widget_realize(folderview->ctree);
386 gtk_widget_show_all(folderview->scrolledwin);
388 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
389 * instead text (text overflows making them unreadable and ugly) */
390 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
391 &newxpm, &newxpmmask);
392 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
393 &unreadxpm, &unreadxpmmask);
394 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
395 &readxpm, &readxpmmask);
397 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
398 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
399 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
401 gtk_clist_column_titles_active(GTK_CLIST(ctree));
403 hbox_new = gtk_hbox_new(FALSE, 4);
404 hbox_unread = gtk_hbox_new(FALSE, 4);
405 hbox_total = gtk_hbox_new(FALSE, 4);
408 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
409 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
410 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
411 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
412 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
413 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
415 gtk_widget_show_all(hbox_new);
416 gtk_widget_show_all(hbox_unread);
417 gtk_widget_show_all(hbox_total);
419 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
420 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
421 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
423 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
424 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
425 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
428 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
430 FolderView *folderview = (FolderView *)data;
431 GdkEventButton event;
432 if (folderview_get_selected_item(folderview) == NULL)
436 event.time = gtk_get_current_event_time();
438 folderview_set_sens_and_popup_menu(folderview, -1,
445 static GtkWidget *folderview_ctree_create(FolderView *folderview)
449 FolderColumnState *col_state;
450 FolderColumnType type;
451 gchar *titles[N_FOLDER_COLS];
453 GtkWidget *scrolledwin = folderview->scrolledwin;
455 debug_print("creating tree...\n");
456 memset(titles, 0, sizeof(titles));
458 col_state = prefs_folder_column_get_config();
459 memset(titles, 0, sizeof(titles));
461 col_pos = folderview->col_pos;
463 for (i = 0; i < N_FOLDER_COLS; i++) {
464 folderview->col_state[i] = col_state[i];
465 type = col_state[i].type;
469 titles[col_pos[F_COL_FOLDER]] = _("Folder");
470 titles[col_pos[F_COL_NEW]] = _("New");
471 titles[col_pos[F_COL_UNREAD]] = _("Unread");
472 /* TRANSLATORS: This in Number sign in American style */
473 titles[col_pos[F_COL_TOTAL]] = _("#");
475 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
478 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
479 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
481 gtk_clist_set_column_justification(GTK_CLIST(ctree),
482 col_pos[F_COL_UNREAD],
484 gtk_clist_set_column_justification(GTK_CLIST(ctree),
485 col_pos[F_COL_TOTAL],
487 if (prefs_common.enable_dotted_lines) {
488 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
489 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
490 GTK_CTREE_EXPANDER_SQUARE);
492 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
493 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
494 GTK_CTREE_EXPANDER_TRIANGLE);
497 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
498 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
500 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
501 gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
503 /* don't let title buttons take key focus */
504 for (i = 0; i < N_FOLDER_COLS; i++) {
505 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
507 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
508 prefs_common.folder_col_size[i]);
509 gtk_clist_set_column_visibility
510 (GTK_CLIST(ctree), i, col_state[i].visible);
513 g_signal_connect(G_OBJECT(ctree), "key_press_event",
514 G_CALLBACK(folderview_key_pressed),
516 g_signal_connect(G_OBJECT(ctree), "button_press_event",
517 G_CALLBACK(folderview_button_pressed),
520 g_signal_connect(G_OBJECT(ctree), "popup-menu",
521 G_CALLBACK(folderview_popup_menu), folderview);
523 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
524 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
525 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
526 G_CALLBACK(folderview_popup_menu), folderview);
528 g_signal_connect(G_OBJECT(ctree), "button_release_event",
529 G_CALLBACK(folderview_button_released),
531 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
532 G_CALLBACK(folderview_selected), folderview);
533 g_signal_connect(G_OBJECT(ctree), "start_drag",
534 G_CALLBACK(folderview_start_drag), folderview);
535 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
536 G_CALLBACK(folderview_drag_data_get),
539 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
540 G_CALLBACK(folderview_tree_expanded),
542 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
543 G_CALLBACK(folderview_tree_collapsed),
546 g_signal_connect(G_OBJECT(ctree), "resize_column",
547 G_CALLBACK(folderview_col_resized),
551 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
552 folderview_drag_types, 2,
553 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
554 g_signal_connect(G_OBJECT(ctree), "drag_motion",
555 G_CALLBACK(folderview_drag_motion_cb),
557 g_signal_connect(G_OBJECT(ctree), "drag_leave",
558 G_CALLBACK(folderview_drag_leave_cb),
560 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
561 G_CALLBACK(folderview_drag_received_cb),
563 g_signal_connect(G_OBJECT(ctree), "drag_end",
564 G_CALLBACK(folderview_drag_end_cb),
567 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
572 void folderview_set_column_order(FolderView *folderview)
575 FolderItem *item = folderview_get_selected_item(folderview);
576 GtkWidget *scrolledwin = folderview->scrolledwin;
578 debug_print("recreating tree...\n");
579 gtk_widget_destroy(folderview->ctree);
581 folderview->ctree = ctree = folderview_ctree_create(folderview);
582 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
583 GTK_CLIST(ctree)->hadjustment);
584 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
585 GTK_CLIST(ctree)->vadjustment);
586 gtk_widget_show(ctree);
588 folderview_set(folderview);
589 folderview_column_set_titles(folderview);
591 folderview_select(folderview,item);
594 FolderView *folderview_create(void)
596 FolderView *folderview;
597 GtkWidget *scrolledwin;
600 debug_print("Creating folder view...\n");
601 folderview = g_new0(FolderView, 1);
603 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
604 gtk_scrolled_window_set_policy
605 (GTK_SCROLLED_WINDOW(scrolledwin),
606 GTK_POLICY_AUTOMATIC,
607 prefs_common.folderview_vscrollbar_policy);
608 gtk_widget_set_size_request(scrolledwin,
609 prefs_common.folderview_width,
610 prefs_common.folderview_height);
612 folderview->scrolledwin = scrolledwin;
613 ctree = folderview_ctree_create(folderview);
615 /* create popup factories */
616 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
617 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
619 folderview->ctree = ctree;
621 folderview->folder_update_callback_id =
622 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
623 folderview->folder_item_update_callback_id =
624 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
626 gtk_widget_show_all(scrolledwin);
628 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
629 folderview_list = g_list_append(folderview_list, folderview);
630 folderview->deferred_refresh_id = -1;
635 void folderview_init(FolderView *folderview)
637 GtkWidget *ctree = folderview->ctree;
640 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
641 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
642 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
643 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
644 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
645 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
646 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
647 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
648 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
649 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
650 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
651 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
652 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
653 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
654 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
655 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
656 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
657 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
658 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
659 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
660 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
661 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
662 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
664 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
665 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
666 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
667 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
668 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
669 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
670 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
671 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
675 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
688 PangoFontDescription *font_desc;
689 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
690 font_desc = pango_font_description_from_string(NORMAL_FONT);
692 if (normal_style->font_desc)
693 pango_font_description_free
694 (normal_style->font_desc);
695 normal_style->font_desc = font_desc;
697 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
698 normal_color_style = gtk_style_copy(normal_style);
699 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
701 gtk_widget_set_style(ctree, normal_style);
705 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
706 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
707 pango_font_description_set_weight
708 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
709 bold_color_style = gtk_style_copy(bold_style);
710 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
712 bold_tgtfold_style = gtk_style_copy(bold_style);
713 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
717 static gboolean folderview_defer_set(gpointer data)
719 FolderView *folderview = (FolderView *)data;
720 MainWindow *mainwin = folderview->mainwin;
724 if (mainwin->lock_count)
727 printf("doing deferred folderview_set now\n");
728 folderview_set(folderview);
730 folderview->deferred_refresh_id = -1;
734 void folderview_set(FolderView *folderview)
736 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
737 MainWindow *mainwin = folderview->mainwin;
738 FolderItem *sel_item = NULL, *op_item = NULL;
743 if (mainwin->lock_count) {
744 if (folderview->deferred_refresh_id == -1)
745 folderview->deferred_refresh_id =
746 g_timeout_add(500, folderview_defer_set, folderview);
747 printf("deferred folderview_set\n");
752 debug_print("Setting folder info...\n");
753 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
755 main_window_cursor_wait(mainwin);
757 if (folderview->selected)
758 sel_item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
759 if (folderview->opened)
760 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
762 folderview->selected = NULL;
763 folderview->opened = NULL;
765 gtk_clist_freeze(GTK_CLIST(ctree));
766 gtk_clist_clear(GTK_CLIST(ctree));
768 folderview_set_folders(folderview);
771 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
773 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
775 gtk_clist_thaw(GTK_CLIST(ctree));
776 main_window_cursor_normal(mainwin);
777 STATUSBAR_POP(mainwin);
781 void folderview_set_all(void)
785 for (list = folderview_list; list != NULL; list = list->next)
786 folderview_set((FolderView *)list->data);
789 void folderview_select(FolderView *folderview, FolderItem *item)
791 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
793 GtkCTreeNode *old_selected = folderview->selected;
797 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
798 if (node) folderview_select_node(folderview, node);
800 if (old_selected != node)
801 folder_update_op_count();
804 static void mark_all_read_cb(FolderView *folderview, guint action,
810 item = folderview_get_selected_item(folderview);
814 if (folderview->summaryview->folder_item != item
815 && prefs_common.ask_mark_all_read) {
816 val = alertpanel_full(_("Mark all as read"),
817 _("Do you really want to mark all mails in this "
818 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
819 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
821 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
823 else if (val & G_ALERTDISABLE)
824 prefs_common.ask_mark_all_read = FALSE;
827 summary_lock(folderview->summaryview);
828 folder_item_update_freeze();
829 if (folderview->summaryview->folder_item == item)
830 summary_freeze(folderview->summaryview);
831 folderutils_mark_all_read(item);
832 if (folderview->summaryview->folder_item == item)
833 summary_thaw(folderview->summaryview);
834 folder_item_update_thaw();
835 summary_unlock(folderview->summaryview);
838 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
840 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
842 g_return_if_fail(node != NULL);
844 if (folderview->open_folder) {
848 folderview->open_folder = TRUE;
849 gtkut_ctree_set_focus_row(ctree, node);
850 gtk_ctree_select(ctree, node);
851 if (folderview->summaryview->folder_item &&
852 folderview->summaryview->folder_item->total_msgs > 0)
853 summary_grab_focus(folderview->summaryview);
855 gtk_widget_grab_focus(folderview->ctree);
857 gtkut_ctree_expand_parent_all(ctree, node);
860 void folderview_unselect(FolderView *folderview)
862 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
864 (GTK_CTREE(folderview->ctree), folderview->opened);
866 folderview->selected = folderview->opened = NULL;
869 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
875 node = gtkut_ctree_node_next(ctree, node);
877 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
879 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
880 item = gtk_ctree_node_get_row_data(ctree, node);
881 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
888 void folderview_select_next_marked(FolderView *folderview)
890 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
891 GtkCTreeNode *node = NULL;
892 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
893 gboolean last_open = prefs_common.always_show_msg;
895 prefs_common.summary_select_prio[0] = ACTION_MARKED;
896 prefs_common.always_show_msg = TRUE;
898 if ((node = folderview_find_next_marked(ctree, folderview->opened))
900 folderview_select_node(folderview, node);
904 if (!folderview->opened ||
905 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
908 /* search again from the first node */
909 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
910 folderview_select_node(folderview, node);
913 prefs_common.summary_select_prio[0] = last_summary_select_prio;
914 prefs_common.always_show_msg = last_open;
917 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
923 node = gtkut_ctree_node_next(ctree, node);
925 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
927 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
928 item = gtk_ctree_node_get_row_data(ctree, node);
929 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
936 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
938 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
939 GtkCTreeNode *node = NULL;
940 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
941 gboolean last_open = prefs_common.always_show_msg;
943 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
944 prefs_common.always_show_msg = force_open ? TRUE : last_open;
946 if ((node = folderview_find_next_unread(ctree, folderview->opened))
948 folderview_select_node(folderview, node);
952 if (!folderview->opened ||
953 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
956 /* search again from the first node */
957 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
958 folderview_select_node(folderview, node);
961 prefs_common.summary_select_prio[0] = last_summary_select_prio;
962 prefs_common.always_show_msg = last_open;
965 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
971 node = gtkut_ctree_node_next(ctree, node);
973 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
975 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
976 item = gtk_ctree_node_get_row_data(ctree, node);
977 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
984 void folderview_select_next_new(FolderView *folderview)
986 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
987 GtkCTreeNode *node = NULL;
988 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
989 gboolean last_open = prefs_common.always_show_msg;
991 prefs_common.summary_select_prio[0] = ACTION_NEW;
992 prefs_common.always_show_msg = TRUE;
994 if ((node = folderview_find_next_new(ctree, folderview->opened))
996 folderview_select_node(folderview, node);
1000 if (!folderview->opened ||
1001 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1004 /* search again from the first node */
1005 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1006 folderview_select_node(folderview, node);
1009 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1010 prefs_common.always_show_msg = last_open;
1013 FolderItem *folderview_get_selected_item(FolderView *folderview)
1015 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1017 if (!folderview->selected) return NULL;
1018 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1021 static void folderview_set_folders(FolderView *folderview)
1024 list = folder_get_list();
1026 for (; list != NULL; list = list->next) {
1027 folderview_append_folder(folderview, FOLDER(list->data));
1031 static gchar *get_scan_str(FolderItem *item)
1034 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1035 item->folder->name, G_DIR_SEPARATOR,
1038 return g_strdup_printf(_("Scanning folder %s ..."),
1039 item->folder->name);
1041 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1045 for (list = folderview_list; list != NULL; list = list->next) {
1046 FolderView *folderview = (FolderView *)list->data;
1047 MainWindow *mainwin = folderview->mainwin;
1048 gchar *str = get_scan_str(item);
1050 STATUSBAR_PUSH(mainwin, str);
1051 STATUSBAR_POP(mainwin);
1056 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1059 MainWindow *mainwin = mainwindow_get_mainwindow();
1060 FolderView *folderview = NULL;
1061 GtkAdjustment *pos = NULL;
1064 g_return_if_fail(folder != NULL);
1066 if (!folder->klass->scan_tree) return;
1069 alertpanel_full(_("Rebuild folder tree"),
1070 _("Rebuilding the folder tree will remove "
1071 "local caches. Do you want to continue?"),
1072 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1073 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1074 != G_ALERTALTERNATE) {
1080 window = label_window_create(_("Rebuilding folder tree..."));
1082 window = label_window_create(_("Scanning folder tree..."));
1085 folderview = mainwin->folderview;
1088 pos = gtk_scrolled_window_get_vadjustment(
1089 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1090 height = pos->value;
1093 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1094 folder_scan_tree(folder, rebuild);
1095 folder_set_ui_func(folder, NULL, NULL);
1097 folderview_set_all();
1100 pos = gtk_scrolled_window_get_vadjustment(
1101 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1102 gtk_adjustment_set_value(pos, height);
1104 label_window_destroy(window);
1108 void folderview_fast_rescan_tree(Folder *folder)
1111 MainWindow *mainwin = mainwindow_get_mainwindow();
1112 FolderView *folderview = NULL;
1113 GtkAdjustment *pos = NULL;
1116 g_return_if_fail(folder != NULL);
1118 if (!folder->klass->scan_tree) return;
1122 window = label_window_create(_("Scanning folder tree..."));
1125 folderview = mainwin->folderview;
1128 pos = gtk_scrolled_window_get_vadjustment(
1129 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1130 height = pos->value;
1133 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1134 folder_fast_scan_tree(folder);
1135 folder_set_ui_func(folder, NULL, NULL);
1137 folderview_set_all();
1140 pos = gtk_scrolled_window_get_vadjustment(
1141 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1142 gtk_adjustment_set_value(pos, height);
1144 label_window_destroy(window);
1148 /** folderview_check_new()
1149 * Scan and update the folder and return the
1150 * count the number of new messages since last check.
1151 * \param folder the folder to check for new messages
1152 * \return the number of new messages since last check
1154 gint folderview_check_new(Folder *folder)
1158 FolderView *folderview;
1162 gint former_new_msgs = 0;
1163 gint former_new = 0, former_unread = 0, former_total;
1165 for (list = folderview_list; list != NULL; list = list->next) {
1166 folderview = (FolderView *)list->data;
1167 ctree = GTK_CTREE(folderview->ctree);
1170 main_window_lock(folderview->mainwin);
1172 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1173 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1175 item = gtk_ctree_node_get_row_data(ctree, node);
1176 if (!item || !item->path || !item->folder) continue;
1177 if (item->no_select) continue;
1178 if (folder && folder != item->folder) continue;
1179 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1180 if (!item->prefs->newmailcheck) continue;
1181 if (item->processing_pending == TRUE) {
1182 debug_print("skipping %s, processing pending\n",
1183 item->path ? item->path : item->name);
1187 str = get_scan_str(item);
1189 STATUSBAR_PUSH(folderview->mainwin, str);
1193 folderview_scan_tree_func(item->folder, item, NULL);
1194 former_new = item->new_msgs;
1195 former_unread = item->unread_msgs;
1196 former_total = item->total_msgs;
1198 if (item->folder->klass->scan_required &&
1199 (item->folder->klass->scan_required(item->folder, item) ||
1200 item->folder->inbox == item ||
1201 item->opened == TRUE ||
1202 item->processing_pending == TRUE)) {
1203 if (folder_item_scan(item) < 0) {
1205 summaryview_unlock(folderview->summaryview, item);
1206 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1207 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1208 item->path ? item->path:item->name);
1209 STATUSBAR_POP(folderview->mainwin);
1211 } else if (!FOLDER_IS_LOCAL(folder)) {
1212 STATUSBAR_POP(folderview->mainwin);
1217 } else if (!item->folder->klass->scan_required) {
1218 if (folder_item_scan(item) < 0) {
1219 summaryview_unlock(folderview->summaryview, item);
1220 if (folder && !FOLDER_IS_LOCAL(folder)) {
1221 STATUSBAR_POP(folderview->mainwin);
1226 if (former_new != item->new_msgs ||
1227 former_unread != item->unread_msgs ||
1228 former_total != item->total_msgs)
1229 folderview_update_node(folderview, node);
1231 new_msgs += item->new_msgs;
1232 former_new_msgs += former_new;
1233 STATUSBAR_POP(folderview->mainwin);
1236 main_window_unlock(folderview->mainwin);
1240 folder_write_list();
1241 /* Number of new messages since last check is the just the difference
1242 * between former_new_msgs and new_msgs. If new_msgs is less than
1243 * former_new_msgs, that would mean another session accessed the folder
1244 * and the result is not well defined.
1246 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1250 void folderview_check_new_all(void)
1254 FolderView *folderview;
1256 folderview = (FolderView *)folderview_list->data;
1259 main_window_lock(folderview->mainwin);
1260 window = label_window_create
1261 (_("Checking for new messages in all folders..."));
1263 list = folder_get_list();
1264 for (; list != NULL; list = list->next) {
1265 Folder *folder = list->data;
1267 folderview_check_new(folder);
1270 folder_write_list();
1271 folderview_set_all();
1273 label_window_destroy(window);
1274 main_window_unlock(folderview->mainwin);
1278 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1284 if (!item || !item->folder || !item->folder->node)
1287 node = item->folder->node;
1289 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1290 node = node->children;
1293 (item->new_msgs > 0 ||
1294 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1298 while (node != NULL) {
1299 if (node && node->data) {
1300 FolderItem *next_item = (FolderItem*) node->data;
1302 if (folderview_have_new_children_sub(folderview,
1311 static gboolean folderview_have_new_children(FolderView *folderview,
1314 return folderview_have_new_children_sub(folderview, item, FALSE);
1317 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1323 if (!item || !item->folder || !item->folder->node)
1326 node = item->folder->node;
1328 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1329 node = node->children;
1332 (item->unread_msgs > 0 ||
1333 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1337 while (node != NULL) {
1338 if (node && node->data) {
1339 FolderItem *next_item = (FolderItem*) node->data;
1341 if (folderview_have_unread_children_sub(folderview,
1351 static gboolean folderview_have_unread_children(FolderView *folderview,
1354 return folderview_have_unread_children_sub(folderview, item, FALSE);
1357 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1363 if (!item || !item->folder || !item->folder->node)
1366 node = item->folder->node;
1368 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1369 node = node->children;
1371 if (in_sub && item->search_match){
1375 while (node != NULL) {
1376 if (node && node->data) {
1377 FolderItem *next_item = (FolderItem*) node->data;
1379 if (folderview_have_matching_children_sub(folderview,
1389 static gboolean folderview_have_matching_children(FolderView *folderview,
1392 return folderview_have_matching_children_sub(folderview, item, FALSE);
1395 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1401 if (!item || !item->folder || !item->folder->node)
1404 node = item->folder->node;
1406 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1407 node = node->children;
1409 if (item->marked_msgs != 0) {
1413 while (node != NULL) {
1414 if (node && node->data) {
1415 FolderItem *next_item = (FolderItem*) node->data;
1417 if (folderview_have_marked_children_sub(folderview,
1426 static gboolean folderview_have_marked_children(FolderView *folderview,
1429 return folderview_have_marked_children_sub(folderview, item, FALSE);
1432 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1434 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1435 GtkStyle *style = NULL;
1436 GtkStyle *color_style = NULL;
1438 GdkPixmap *xpm, *openxpm;
1439 GdkBitmap *mask, *openmask;
1440 static GdkPixmap *searchicon;
1441 static GdkBitmap *searchmask;
1442 gboolean mark = FALSE;
1445 gboolean add_unread_mark;
1446 gboolean add_sub_match_mark;
1447 gboolean use_bold, use_color;
1448 gint *col_pos = folderview->col_pos;
1449 SpecialFolderItemType stype;
1451 item = gtk_ctree_node_get_row_data(ctree, node);
1452 g_return_if_fail(item != NULL);
1454 if (!GTK_CTREE_ROW(node)->expanded)
1455 mark = folderview_have_marked_children(folderview, item);
1457 mark = (item->marked_msgs != 0);
1459 stype = item->stype;
1460 if (stype == F_NORMAL) {
1461 if (folder_has_parent_of_type(item, F_TRASH))
1463 else if (folder_has_parent_of_type(item, F_DRAFT))
1465 else if (folder_has_parent_of_type(item, F_OUTBOX))
1467 else if (folder_has_parent_of_type(item, F_QUEUE))
1472 if (item->hide_read_msgs) {
1473 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1474 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1475 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1476 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1478 xpm = mark?m_inboxxpm:inboxxpm;
1479 mask = mark?m_inboxxpmmask:inboxxpmmask;
1480 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1481 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1485 if (item->hide_read_msgs) {
1486 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1487 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1488 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1489 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1491 xpm = mark?m_outboxxpm:outboxxpm;
1492 mask = mark?m_outboxxpmmask:outboxxpmmask;
1493 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1494 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1498 if (item->hide_read_msgs) {
1499 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1500 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1501 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1502 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1504 xpm = mark?m_queuexpm:queuexpm;
1505 mask = mark?m_queuexpmmask:queuexpmmask;
1506 openxpm = mark?m_queueopenxpm:queueopenxpm;
1507 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1511 if (item->hide_read_msgs) {
1512 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1513 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1514 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1515 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1517 xpm = mark?m_trashxpm:trashxpm;
1518 mask = mark?m_trashxpmmask:trashxpmmask;
1519 openxpm = mark?m_trashopenxpm:trashopenxpm;
1520 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1524 xpm = mark?m_draftsxpm:draftsxpm;
1525 mask = mark?m_draftsxpmmask:draftsxpmmask;
1526 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1527 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1530 if (item->hide_read_msgs) {
1531 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1532 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1533 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1534 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1536 xpm = mark?m_folderxpm:folderxpm;
1537 mask = mark?m_folderxpmmask:folderxpmmask;
1538 openxpm = mark?m_folderopenxpm:folderopenxpm;
1539 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1543 if (item->no_select) {
1544 xpm = openxpm = noselectxpm;
1545 mask = openmask = noselectxpmmask;
1548 name = folder_item_get_name(item);
1550 if (!GTK_CTREE_ROW(node)->expanded) {
1551 add_unread_mark = folderview_have_unread_children(
1553 add_sub_match_mark = folderview_have_matching_children(
1556 add_unread_mark = FALSE;
1557 add_sub_match_mark = FALSE;
1560 if (item->search_match) {
1562 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1563 &searchicon, &searchmask);
1565 xpm = openxpm = searchicon;
1566 mask = openmask = searchmask;
1570 if (prefs_common.display_folder_unread) {
1571 if (folder_has_parent_of_type(item, F_QUEUE)) {
1572 /* only total_msgs matters here */
1573 if (item->total_msgs > 0) {
1574 /* show total number (should be equal to the unread number)
1576 str = g_strdup_printf("%s (%d%s%s)",
1577 name, item->total_msgs,
1578 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1579 (item->unreadmarked_msgs > 0) ? "!" : "");
1582 if (prefs_common.display_folder_unread == 1) {
1583 if (item->unread_msgs > 0) {
1584 /* show unread number and signs */
1585 str = g_strdup_printf("%s (%d%s%s)",
1586 name, item->unread_msgs,
1587 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1588 (item->unreadmarked_msgs > 0) ? "!" : "");
1591 if (item->total_msgs > 0) {
1592 /* show unread number, total number and signs if any */
1593 str = g_strdup_printf("%s (%d/%d%s%s)",
1594 name, item->unread_msgs, item->total_msgs,
1595 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1596 (item->unreadmarked_msgs > 0) ? "!" : "");
1600 if ((str == NULL) &&
1601 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1602 /* no unread/total numbers, but at least one sign */
1603 str = g_strdup_printf("%s (%s%s)",
1605 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1606 (item->unreadmarked_msgs > 0) ? "!" : "");
1610 /* last fallback, folder name only or with ! sign */
1611 str = g_strdup_printf("%s%s",
1612 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1614 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1615 xpm, mask, openxpm, openmask,
1616 FALSE, GTK_CTREE_ROW(node)->expanded);
1620 if (!folder_item_parent(item)) {
1621 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1622 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1623 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1625 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1626 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1627 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1630 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1631 folder_has_parent_of_type(item, F_DRAFT) ||
1632 folder_has_parent_of_type(item, F_TRASH)) {
1633 use_bold = use_color = FALSE;
1634 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1635 /* highlight queue folder if there are any messages */
1636 use_bold = use_color = (item->total_msgs > 0);
1638 /* if unread messages exist, print with bold font */
1639 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1641 /* if new messages exist, print with colored letter */
1643 (item->new_msgs > 0) ||
1645 folderview_have_new_children(folderview, item));
1648 gtk_ctree_node_set_foreground(ctree, node, NULL);
1653 if (item->prefs->color > 0 && !use_color) {
1654 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1655 color_style = gtk_style_copy(bold_style);
1656 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1657 style = color_style;
1658 } else if (use_color) {
1659 style = bold_color_style;
1662 if (item->op_count > 0) {
1663 style = bold_tgtfold_style;
1665 } else if (use_color) {
1666 style = normal_color_style;
1667 gtk_ctree_node_set_foreground(ctree, node,
1668 &folderview->color_new);
1669 } else if (item->op_count > 0) {
1670 style = bold_tgtfold_style;
1671 } else if (item->prefs->color > 0) {
1673 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1674 color_style = gtk_style_copy(normal_style);
1675 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1676 style = color_style;
1678 style = normal_style;
1681 gtk_ctree_node_set_row_style(ctree, node, style);
1683 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1684 folderview_update_node(folderview, node);
1687 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1690 FolderView *folderview;
1694 g_return_if_fail(item != NULL);
1696 for (list = folderview_list; list != NULL; list = list->next) {
1697 folderview = (FolderView *)list->data;
1698 ctree = GTK_CTREE(folderview->ctree);
1700 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1702 item->search_match = matches;
1703 folderview_update_node(folderview, node);
1708 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1710 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1711 FolderView *folderview = (FolderView *)data;
1714 g_return_val_if_fail(update_info != NULL, TRUE);
1715 g_return_val_if_fail(update_info->item != NULL, TRUE);
1716 g_return_val_if_fail(folderview != NULL, FALSE);
1718 ctree = GTK_CTREE(folderview->ctree);
1720 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1723 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1724 folderview_update_node(folderview, node);
1726 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1727 update_info->item == folderview->summaryview->folder_item &&
1728 update_info->item != NULL)
1729 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1730 summary_show(folderview->summaryview, update_info->item);
1736 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1737 GNode *gnode, GtkCTreeNode *cnode,
1740 FolderView *folderview = (FolderView *)data;
1741 FolderItem *item = FOLDER_ITEM(gnode->data);
1743 g_return_val_if_fail(item != NULL, FALSE);
1745 gtk_ctree_node_set_row_data(ctree, cnode, item);
1746 folderview_update_node(folderview, cnode);
1751 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1754 FolderView *folderview = (FolderView *)data;
1757 if (GTK_CTREE_ROW(node)->children) {
1758 item = gtk_ctree_node_get_row_data(ctree, node);
1759 g_return_if_fail(item != NULL);
1761 if (!item->collapsed)
1762 gtk_ctree_expand(ctree, node);
1764 folderview_update_node(folderview, node);
1768 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1769 GtkCTreeNode *root, GtkCTreeNode **prev)
1772 GtkCTreeNode *node, *parent, *sibling;
1774 node = gtk_ctree_find_by_row_data(ctree, root, item);
1776 g_warning("%s not found.\n", item->path);
1778 parent = GTK_CTREE_ROW(node)->parent;
1779 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1780 sibling = GTK_CTREE_ROW(*prev)->sibling;
1782 sibling = GTK_CTREE_ROW(parent)->children;
1786 tmp = gtk_ctree_node_get_row_data
1788 if (tmp->stype != F_NORMAL)
1789 sibling = GTK_CTREE_ROW(sibling)->sibling;
1793 if (node != sibling)
1794 gtk_ctree_move(ctree, node, parent, sibling);
1801 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1804 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1805 GtkCTreeNode *prev = NULL;
1807 gtk_clist_freeze(GTK_CLIST(ctree));
1808 gtk_sctree_sort_recursive(ctree, root);
1809 if (root && GTK_CTREE_ROW(root)->parent) {
1810 gtk_clist_thaw(GTK_CLIST(ctree));
1813 set_special_folder(ctree, folder->inbox, root, &prev);
1814 set_special_folder(ctree, folder->outbox, root, &prev);
1815 set_special_folder(ctree, folder->draft, root, &prev);
1816 set_special_folder(ctree, folder->queue, root, &prev);
1817 set_special_folder(ctree, folder->trash, root, &prev);
1818 gtk_clist_thaw(GTK_CLIST(ctree));
1821 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1823 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1826 g_return_if_fail(folder != NULL);
1828 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1829 folderview_gnode_func, folderview);
1830 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1832 folderview_sort_folders(folderview, root, folder);
1835 /* callback functions */
1836 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1837 GdkEventButton *event)
1839 GtkCList *clist = GTK_CLIST(folderview->ctree);
1842 FolderViewPopup *fpopup;
1843 GtkItemFactory *fpopup_factory;
1845 FolderItem *special_trash = NULL, *special_queue = NULL;
1849 item = gtk_clist_get_row_data(clist, row);
1851 item = folderview_get_selected_item(folderview);
1853 g_return_if_fail(item != NULL);
1854 g_return_if_fail(item->folder != NULL);
1855 folder = item->folder;
1857 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1859 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1861 fpopup = g_hash_table_lookup(folderview_popups, "common");
1862 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1865 if (fpopup->set_sensitivity != NULL)
1866 fpopup->set_sensitivity(fpopup_factory, item);
1868 if (NULL != (ac = account_find_from_item(item))) {
1869 special_trash = account_get_special_folder(ac, F_TRASH);
1870 special_queue = account_get_special_folder(ac, F_QUEUE);
1873 if ((item == folder->trash || item == special_trash
1874 || folder_has_parent_of_type(item, F_TRASH)) &&
1875 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1876 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1877 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1878 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1879 && !folder_has_parent_of_type(item, F_TRASH)) {
1880 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1881 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1884 if ((item == folder->queue || item == special_queue
1885 || folder_has_parent_of_type(item, F_QUEUE)) &&
1886 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1887 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1888 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1889 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1890 && !folder_has_parent_of_type(item, F_QUEUE)) {
1891 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1892 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1895 #define SET_SENS(name, sens) \
1896 menu_set_sensitive(fpopup_factory, name, sens)
1898 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1899 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1900 folderview->selected == folderview->opened);
1901 SET_SENS("/Properties...", TRUE);
1902 SET_SENS("/Processing...", item->node->parent != NULL);
1903 if (item == folder->trash || item == special_trash
1904 || folder_has_parent_of_type(item, F_TRASH)) {
1905 GSList *msglist = folder_item_get_msg_list(item);
1906 SET_SENS("/Empty trash...", msglist != NULL);
1907 procmsg_msg_list_free(msglist);
1909 if (item == folder->queue || item == special_queue
1910 || folder_has_parent_of_type(item, F_QUEUE)) {
1911 GSList *msglist = folder_item_get_msg_list(item);
1912 SET_SENS("/Send queue...", msglist != NULL);
1913 procmsg_msg_list_free(msglist);
1917 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1918 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1919 event->button, event->time);
1924 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1925 FolderView *folderview)
1927 GtkCList *clist = GTK_CLIST(ctree);
1928 gint prev_row = -1, row = -1, column = -1;
1930 if (!event) return FALSE;
1932 if (event->button == 1 || event->button == 2) {
1933 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1934 folderview->open_folder = TRUE;
1936 if (event->type == GDK_2BUTTON_PRESS) {
1937 if (clist->selection) {
1940 node = GTK_CTREE_NODE(clist->selection->data);
1942 gtk_ctree_toggle_expansion(
1945 folderview->open_folder = FALSE;
1952 if (event->button == 2 || event->button == 3) {
1954 if (clist->selection) {
1957 node = GTK_CTREE_NODE(clist->selection->data);
1959 prev_row = gtkut_ctree_get_nth_from_node
1960 (GTK_CTREE(ctree), node);
1963 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1966 if (prev_row != row) {
1967 gtk_clist_unselect_all(clist);
1968 if (event->button == 2)
1969 folderview_select_node
1971 gtk_ctree_node_nth(GTK_CTREE(ctree),
1974 gtk_clist_select_row(clist, row, column);
1978 if (event->button != 3) return FALSE;
1980 folderview_set_sens_and_popup_menu(folderview, row, event);
1984 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1985 FolderView *folderview)
1987 if (!event) return FALSE;
1989 if (event->button == 1 && folderview->open_folder == FALSE &&
1990 folderview->opened != NULL) {
1991 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
1992 folderview->opened);
1993 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
1999 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2000 FolderView *folderview)
2002 if (!event) return FALSE;
2004 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2007 switch (event->keyval) {
2012 if (folderview->selected) {
2013 folderview_select_node(folderview,
2014 folderview->selected);
2019 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2020 gtk_ctree_toggle_expansion(
2021 GTK_CTREE(folderview->ctree),
2022 folderview->selected);
2027 if (folderview->selected) {
2028 if (folderview->opened == folderview->selected &&
2029 (!folderview->summaryview->folder_item ||
2030 folderview->summaryview->folder_item->total_msgs == 0))
2031 folderview_select_next_unread(folderview, TRUE);
2033 folderview_select_node(folderview,
2034 folderview->selected);
2044 typedef struct _PostponedSelectData
2049 FolderView *folderview;
2050 } PostponedSelectData;
2052 static gboolean postpone_select(void *data)
2054 PostponedSelectData *psdata = (PostponedSelectData *)data;
2055 debug_print("trying again\n");
2056 psdata->folderview->open_folder = TRUE;
2057 main_window_cursor_normal(psdata->folderview->mainwin);
2058 STATUSBAR_POP(psdata->folderview->mainwin);
2059 folderview_selected(psdata->ctree, psdata->row,
2060 psdata->column, psdata->folderview);
2065 void folderview_close_opened(FolderView *folderview)
2067 if (folderview->opened) {
2068 FolderItem *olditem;
2070 olditem = gtk_ctree_node_get_row_data(folderview->ctree, folderview->opened);
2072 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2073 olditem->path ? olditem->path:olditem->name);
2074 /* will be null if we just moved the previously opened folder */
2075 STATUSBAR_PUSH(folderview->mainwin, buf);
2076 main_window_cursor_wait(folderview->mainwin);
2078 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2079 summary_show(folderview->summaryview, NULL);
2080 folder_item_close(olditem);
2081 main_window_cursor_normal(folderview->mainwin);
2082 STATUSBAR_POP(folderview->mainwin);
2086 if (folderview->opened &&
2087 !GTK_CTREE_ROW(folderview->opened)->children)
2088 gtk_ctree_collapse(folderview->ctree, folderview->opened);
2090 folderview->opened = NULL;
2092 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2093 gint column, FolderView *folderview)
2095 static gboolean can_select = TRUE; /* exclusive lock */
2100 GtkCTreeNode *old_opened = folderview->opened;
2102 folderview->selected = row;
2104 debug_print("newly selected %p, opened %p\n", folderview->selected,
2105 folderview->opened);
2106 if (folderview->opened == row) {
2107 folderview->open_folder = FALSE;
2112 if (!can_select || summary_is_locked(folderview->summaryview)) {
2113 if (folderview->opened) {
2114 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2115 gtk_ctree_select(ctree, folderview->opened);
2117 folderview->open_folder = FALSE;
2122 if (!folderview->open_folder) {
2126 item = gtk_ctree_node_get_row_data(ctree, row);
2127 if (!item || item->no_select) {
2129 folderview->open_folder = FALSE;
2135 /* Save cache for old folder */
2136 /* We don't want to lose all caches if sylpheed crashed */
2137 /* resets folderview->opened to NULL */
2138 folderview_close_opened(folderview);
2140 /* CLAWS: set compose button type: news folder items
2141 * always have a news folder as parent */
2143 toolbar_set_compose_button
2144 (folderview->mainwin->toolbar,
2145 FOLDER_TYPE(item->folder) == F_NEWS ?
2146 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2149 debug_print("Folder %s is selected\n", item->path);
2151 if (!GTK_CTREE_ROW(row)->children)
2152 gtk_ctree_expand(ctree, row);
2154 /* ungrab the mouse event */
2155 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2156 gtk_grab_remove(GTK_WIDGET(ctree));
2157 if (gdk_pointer_is_grabbed())
2158 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2162 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2163 item->path : "(null)");
2164 debug_print("%s\n", buf);
2165 STATUSBAR_PUSH(folderview->mainwin, buf);
2168 main_window_cursor_wait(folderview->mainwin);
2170 res = folder_item_open(item);
2172 main_window_cursor_normal(folderview->mainwin);
2173 STATUSBAR_POP(folderview->mainwin);
2175 alertpanel_error(_("Folder could not be opened."));
2177 folderview->open_folder = FALSE;
2181 } else if (res == -2) {
2182 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2183 data->ctree = ctree;
2185 data->column = column;
2186 data->folderview = folderview;
2187 debug_print("postponing open of %s till end of scan\n",
2188 item->path ? item->path:item->name);
2189 folderview->open_folder = FALSE;
2191 g_timeout_add(500, postpone_select, data);
2196 main_window_cursor_normal(folderview->mainwin);
2199 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2200 opened = summary_show(folderview->summaryview, item);
2202 folder_clean_cache_memory(item);
2205 gtkut_ctree_set_focus_row(ctree, old_opened);
2206 gtk_ctree_select(ctree, old_opened);
2207 folderview->opened = old_opened;
2209 folderview->opened = row;
2210 if (gtk_ctree_node_is_visible(ctree, row)
2211 != GTK_VISIBILITY_FULL)
2212 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2215 STATUSBAR_POP(folderview->mainwin);
2217 folderview->open_folder = FALSE;
2222 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2223 FolderView *folderview)
2227 item = gtk_ctree_node_get_row_data(ctree, node);
2228 g_return_if_fail(item != NULL);
2229 item->collapsed = FALSE;
2230 folderview_update_node(folderview, node);
2233 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2234 FolderView *folderview)
2238 item = gtk_ctree_node_get_row_data(ctree, node);
2239 g_return_if_fail(item != NULL);
2240 item->collapsed = TRUE;
2241 folderview_update_node(folderview, node);
2244 static void folderview_popup_close(GtkMenuShell *menu_shell,
2245 FolderView *folderview)
2247 if (!folderview->opened) return;
2249 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2252 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2253 FolderView *folderview)
2255 FolderColumnType type = folderview->col_state[column].type;
2257 prefs_common.folder_col_size[type] = width;
2260 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2264 folderview_create_folder_node(folderview, item);
2266 if (!item || !item->folder || !item->folder->node)
2269 srcnode = item->folder->node;
2270 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2271 srcnode = srcnode->children;
2272 while (srcnode != NULL) {
2273 if (srcnode && srcnode->data) {
2274 FolderItem *next_item = (FolderItem*) srcnode->data;
2275 folderview_create_folder_node_recursive(folderview, next_item);
2277 srcnode = srcnode->next;
2281 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2283 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2284 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2285 GtkCTreeNode *node, *parent_node;
2286 gint *col_pos = folderview->col_pos;
2287 FolderItemUpdateData hookdata;
2289 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2290 if (parent_node == NULL)
2293 gtk_clist_freeze(GTK_CLIST(ctree));
2295 text[col_pos[F_COL_FOLDER]] = item->name;
2296 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2298 folderxpm, folderxpmmask,
2299 folderopenxpm, folderopenxpmmask,
2301 gtk_ctree_expand(ctree, parent_node);
2302 gtk_ctree_node_set_row_data(ctree, node, item);
2304 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2305 folderview_sort_folders(folderview, parent_node, item->folder);
2307 hookdata.item = item;
2308 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2309 hookdata.msg = NULL;
2310 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2312 gtk_clist_thaw(GTK_CLIST(ctree));
2315 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2318 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2320 GSList *mlist = NULL;
2322 FolderItem *special_trash = NULL;
2325 if (!folderview->selected) return;
2326 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2327 g_return_if_fail(item != NULL);
2328 g_return_if_fail(item->folder != NULL);
2330 if (NULL != (ac = account_find_from_item(item)))
2331 special_trash = account_get_special_folder(ac, F_TRASH);
2333 if (item != item->folder->trash && item != special_trash
2334 && !folder_has_parent_of_type(item, F_TRASH)) return;
2336 if (prefs_common.ask_on_clean) {
2337 if (alertpanel(_("Empty trash"),
2338 _("Delete all messages in trash?"),
2339 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2343 mlist = folder_item_get_msg_list(item);
2345 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2346 MsgInfo * msginfo = (MsgInfo *) cur->data;
2347 if (MSG_IS_LOCKED(msginfo->flags))
2349 /* is it partially received? (partial_recv isn't cached) */
2350 if (msginfo->total_size != 0 &&
2351 msginfo->size != (off_t)msginfo->total_size)
2352 partial_mark_for_delete(msginfo);
2354 procmsg_msg_list_free(mlist);
2356 folder_item_remove_all_msg(item);
2359 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2362 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2364 FolderItem *special_queue = NULL;
2366 gchar *errstr = NULL;
2368 if (!folderview->selected) return;
2369 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2370 g_return_if_fail(item != NULL);
2371 g_return_if_fail(item->folder != NULL);
2373 if (NULL != (ac = account_find_from_item(item)))
2374 special_queue = account_get_special_folder(ac, F_QUEUE);
2376 if (item != item->folder->queue && item != special_queue
2377 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2379 if (procmsg_queue_is_empty(item))
2382 if (prefs_common.work_offline)
2383 if (alertpanel(_("Offline warning"),
2384 _("You're working offline. Override?"),
2385 GTK_STOCK_NO, GTK_STOCK_YES,
2386 NULL) != G_ALERTALTERNATE)
2389 /* ask for confirmation before sending queued messages only
2390 in online mode and if there is at least one message queued
2391 in any of the folder queue
2393 if (prefs_common.confirm_send_queued_messages) {
2394 if (!prefs_common.work_offline) {
2395 if (alertpanel(_("Send queued messages"),
2396 _("Send all queued messages?"),
2397 GTK_STOCK_CANCEL, _("_Send"),
2398 NULL) != G_ALERTALTERNATE)
2403 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2405 alertpanel_error_log(_("Some errors occurred while "
2406 "sending queued messages."));
2408 gchar *tmp = g_strdup_printf(_("Some errors occurred "
2409 "while sending queued messages:\n%s"), errstr);
2411 alertpanel_error_log(tmp);
2417 static void folderview_search_cb(FolderView *folderview, guint action,
2420 summary_search(folderview->summaryview);
2423 static void folderview_property_cb(FolderView *folderview, guint action,
2426 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2429 if (!folderview->selected) return;
2431 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2432 g_return_if_fail(item != NULL);
2433 g_return_if_fail(item->folder != NULL);
2435 prefs_folder_item_open(item);
2438 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2440 GSList *list = NULL;
2441 GSList *done = NULL;
2442 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2444 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2445 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2446 && list->data != node) {
2447 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2448 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2451 for (list = done; list != NULL; list = g_slist_next(list)) {
2452 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2458 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2459 FolderItem *to_folder, gboolean copy)
2461 FolderItem *from_parent = NULL;
2462 FolderItem *new_folder = NULL;
2463 GtkCTreeNode *src_node = NULL;
2467 g_return_if_fail(folderview != NULL);
2468 g_return_if_fail(from_folder != NULL);
2469 g_return_if_fail(to_folder != NULL);
2471 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2472 from_parent = folder_item_parent(from_folder);
2474 if (prefs_common.warn_dnd) {
2475 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2476 _("Do you really want to make folder '%s' a sub-folder of '%s' ?"),
2477 from_folder->name, to_folder->name);
2478 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2479 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2480 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2483 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2485 else if (status & G_ALERTDISABLE)
2486 prefs_common.warn_dnd = FALSE;
2489 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2490 from_folder->name, to_folder->name);
2491 STATUSBAR_PUSH(folderview->mainwin, buf);
2493 summary_clear_all(folderview->summaryview);
2494 folderview->opened = NULL;
2495 folderview->selected = NULL;
2496 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2498 main_window_cursor_wait(folderview->mainwin);
2500 statusbar_verbosity_set(FALSE);
2501 folder_item_update_freeze();
2502 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2503 statusbar_verbosity_set(FALSE);
2504 main_window_cursor_normal(folderview->mainwin);
2505 STATUSBAR_POP(folderview->mainwin);
2506 folder_item_update_thaw();
2507 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2509 folderview_sort_folders(folderview,
2510 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2511 NULL, to_folder), new_folder->folder);
2512 folderview_select(folderview, new_folder);
2514 statusbar_verbosity_set(FALSE);
2515 main_window_cursor_normal(folderview->mainwin);
2516 STATUSBAR_POP(folderview->mainwin);
2517 folder_item_update_thaw();
2519 case F_MOVE_FAILED_DEST_IS_PARENT:
2520 alertpanel_error(_("Source and destination are the same."));
2522 case F_MOVE_FAILED_DEST_IS_CHILD:
2523 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2524 _("Can't move a folder to one of its children."));
2526 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2527 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2530 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2535 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2538 static gint folderview_clist_compare(GtkCList *clist,
2539 gconstpointer ptr1, gconstpointer ptr2)
2541 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2542 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2544 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2546 return item1->order - item2->order;
2549 // if only one folder has an order it comes first
2550 if (item1->order > 0)
2554 if (item2->order > 0)
2560 return (item2->name != NULL);
2564 return g_utf8_collate(item1->name, item2->name);
2567 static void folderview_processing_cb(FolderView *folderview, guint action,
2570 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2574 if (!folderview->selected) return;
2576 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2577 g_return_if_fail(item != NULL);
2578 g_return_if_fail(item->folder != NULL);
2580 id = folder_item_get_identifier(item);
2581 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2584 prefs_filtering_open(&item->prefs->processing, title,
2585 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2589 void folderview_set_target_folder_color(gint color_op)
2593 FolderView *folderview;
2595 for (list = folderview_list; list != NULL; list = list->next) {
2596 folderview = (FolderView *)list->data;
2597 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2599 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2600 folderview->color_op;
2606 static gchar *last_font = NULL;
2607 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2615 void folderview_reflect_prefs(void)
2617 gboolean update_font = TRUE;
2618 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2619 FolderItem *item = folderview_get_selected_item(folderview);
2620 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2621 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2622 gint height = pos->value;
2624 if (last_font && !strcmp(last_font, NORMAL_FONT))
2625 update_font = FALSE;
2628 last_font = g_strdup(NORMAL_FONT);
2631 normal_style = normal_color_style = bold_style =
2632 bold_color_style = bold_tgtfold_style = NULL;
2634 folderview_init(folderview);
2636 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2637 folderview_column_set_titles(folderview);
2638 folderview_set_all();
2640 g_signal_handlers_block_by_func
2641 (G_OBJECT(folderview->ctree),
2642 G_CALLBACK(folderview_selected), folderview);
2645 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2646 GTK_CTREE(folderview->ctree), NULL, item);
2648 folderview_select(folderview, item);
2649 folderview->open_folder = FALSE;
2650 folderview->selected = node;
2653 g_signal_handlers_unblock_by_func
2654 (G_OBJECT(folderview->ctree),
2655 G_CALLBACK(folderview_selected), folderview);
2657 pos = gtk_scrolled_window_get_vadjustment(
2658 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2659 gtk_adjustment_set_value(pos, height);
2660 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2663 static void drag_state_stop(FolderView *folderview)
2665 if (folderview->drag_timer)
2666 g_source_remove(folderview->drag_timer);
2667 folderview->drag_timer = 0;
2668 folderview->drag_node = NULL;
2671 static gint folderview_defer_expand(FolderView *folderview)
2673 if (folderview->drag_node) {
2674 folderview_recollapse_nodes(folderview, folderview->drag_node);
2675 if (folderview->drag_item->collapsed) {
2676 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2677 folderview->nodes_to_recollapse = g_slist_append
2678 (folderview->nodes_to_recollapse, folderview->drag_node);
2681 folderview->drag_item = NULL;
2682 folderview->drag_timer = 0;
2686 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2688 /* the idea is that we call drag_state_start() whenever we want expansion to
2689 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2690 * we need to call drag_state_stop() */
2691 drag_state_stop(folderview);
2692 /* request expansion */
2693 if (0 != (folderview->drag_timer = g_timeout_add
2694 (prefs_common.hover_timeout,
2695 (GtkFunction)folderview_defer_expand,
2697 folderview->drag_node = node;
2698 folderview->drag_item = item;
2702 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2703 FolderView *folderview)
2705 GdkDragContext *context;
2707 g_return_if_fail(folderview != NULL);
2708 if (folderview->selected == NULL) return;
2709 if (folderview->nodes_to_recollapse)
2710 g_slist_free(folderview->nodes_to_recollapse);
2711 folderview->nodes_to_recollapse = NULL;
2712 context = gtk_drag_begin(widget, folderview->target_list,
2713 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2714 gtk_drag_set_icon_default(context);
2717 static void folderview_drag_data_get(GtkWidget *widget,
2718 GdkDragContext *drag_context,
2719 GtkSelectionData *selection_data,
2722 FolderView *folderview)
2726 gchar *source = NULL;
2727 if (info == TARGET_DUMMY) {
2728 for (cur = GTK_CLIST(folderview->ctree)->selection;
2729 cur != NULL; cur = cur->next) {
2730 item = gtk_ctree_node_get_row_data
2731 (GTK_CTREE(folderview->ctree),
2732 GTK_CTREE_NODE(cur->data));
2734 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2735 gtk_selection_data_set(selection_data,
2736 selection_data->target, 8,
2737 source, strlen(source));
2743 g_warning("unknown info %d\n", info);
2747 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2749 FolderUpdateData *hookdata;
2750 FolderView *folderview;
2754 folderview = (FolderView *) userdata;
2755 g_return_val_if_fail(hookdata != NULL, FALSE);
2756 g_return_val_if_fail(folderview != NULL, FALSE);
2758 ctree = folderview->ctree;
2759 g_return_val_if_fail(ctree != NULL, FALSE);
2761 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2762 folderview_create_folder_node(folderview, hookdata->item);
2763 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2764 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2765 NULL, folder_item_parent(hookdata->item));
2766 folderview_sort_folders(folderview, node, hookdata->folder);
2767 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2770 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2772 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2773 if (folderview->selected == node)
2774 folderview->selected = NULL;
2775 if (folderview->opened == node)
2776 folderview->opened = NULL;
2778 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2779 folderview_set(folderview);
2784 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2785 GdkDragContext *context,
2789 FolderView *folderview)
2792 FolderItem *item = NULL, *src_item = NULL;
2793 GtkCTreeNode *node = NULL;
2794 gboolean acceptable = FALSE;
2795 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2796 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2797 int height = (int)pos->page_size;
2798 int total_height = (int)pos->upper;
2799 int vpos = (int) pos->value;
2801 if (gtk_clist_get_selection_info
2802 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
2803 GtkWidget *srcwidget;
2805 if (y > height - 24 && height + vpos < total_height)
2806 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2808 if (y < 48 && y > 0)
2809 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2811 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2812 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2813 src_item = folderview->summaryview->folder_item;
2815 srcwidget = gtk_drag_get_source_widget(context);
2816 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2817 /* comes from summaryview */
2818 /* we are copying messages, so only accept folder items that are not
2819 the source item, are no root items and can copy messages */
2820 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2821 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2822 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2824 } else if (srcwidget == folderview->ctree) {
2825 /* comes from folderview */
2826 /* we are moving folder items, only accept folders that are not
2827 the source items and can copy messages and create folder items */
2828 if (item && item->folder && src_item && src_item != item &&
2829 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2830 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2831 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2832 || item->folder == src_item->folder))
2835 /* comes from another app */
2836 /* we are adding messages, so only accept folder items that are
2837 no root items and can copy messages */
2838 if (item && item->folder && folder_item_parent(item) != NULL
2839 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2840 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2845 if (acceptable || (src_item && src_item == item))
2846 drag_state_start(folderview, node, item);
2849 g_signal_handlers_block_by_func
2851 G_CALLBACK(folderview_selected), folderview);
2852 gtk_ctree_select(GTK_CTREE(widget), node);
2853 g_signal_handlers_unblock_by_func
2855 G_CALLBACK(folderview_selected), folderview);
2856 gdk_drag_status(context,
2857 (context->actions == GDK_ACTION_COPY ?
2858 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2860 if (folderview->opened)
2861 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2862 gdk_drag_status(context, 0, time);
2868 static void folderview_drag_leave_cb(GtkWidget *widget,
2869 GdkDragContext *context,
2871 FolderView *folderview)
2873 drag_state_stop(folderview);
2874 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2877 static void free_info (gpointer stuff, gpointer data)
2882 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2883 guint time, FolderItem *item)
2886 GSList *msglist = NULL;
2887 list = uri_list_extract_filenames(data);
2888 if (!(item && item->folder && folder_item_parent(item) != NULL
2889 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2891 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2892 debug_print("item doesn't fit\n");
2896 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2897 debug_print("list is empty\n");
2900 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2901 MsgFileInfo *info = NULL;
2903 if (file_is_email((gchar *)tmp->data)) {
2904 info = g_new0(MsgFileInfo, 1);
2905 info->msginfo = NULL;
2906 info->file = (gchar *)tmp->data;
2907 msglist = g_slist_prepend(msglist, info);
2908 debug_print("file is a mail\n");
2910 debug_print("file isn't a mail\n");
2914 msglist = g_slist_reverse(msglist);
2915 folder_item_add_msgs(item, msglist, FALSE);
2916 g_slist_foreach(msglist, free_info, NULL);
2917 g_slist_free(msglist);
2918 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2920 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2922 list_free_strings(list);
2926 static void folderview_drag_received_cb(GtkWidget *widget,
2927 GdkDragContext *drag_context,
2930 GtkSelectionData *data,
2933 FolderView *folderview)
2936 FolderItem *item = NULL, *src_item;
2939 if (info == TARGET_DUMMY) {
2940 drag_state_stop(folderview);
2941 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2942 /* comes from summaryview */
2943 if (gtk_clist_get_selection_info
2944 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
2947 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2948 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2949 src_item = folderview->summaryview->folder_item;
2951 /* re-check (due to acceptable possibly set for folder moves */
2952 if (!(item && item->folder && item->path && !item->no_select &&
2953 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2956 if (item && src_item) {
2957 switch (drag_context->action) {
2958 case GDK_ACTION_COPY:
2959 summary_copy_selected_to(folderview->summaryview, item);
2960 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2962 case GDK_ACTION_MOVE:
2963 case GDK_ACTION_DEFAULT:
2965 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2966 summary_copy_selected_to(folderview->summaryview, item);
2968 summary_move_selected_to(folderview->summaryview, item);
2969 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2972 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2974 /* comes from folderview */
2976 gboolean folder_is_normal = TRUE;
2977 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
2979 source = data->data + 17;
2980 if (gtk_clist_get_selection_info
2981 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
2983 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2986 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2987 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2988 src_item = folder_find_item_from_identifier(source);
2992 src_item->stype == F_NORMAL &&
2993 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
2994 !folder_has_parent_of_type(src_item, F_DRAFT) &&
2995 !folder_has_parent_of_type(src_item, F_QUEUE) &&
2996 !folder_has_parent_of_type(src_item, F_TRASH);
2997 if (!item || item->no_select || !src_item
2998 || !folder_is_normal) {
2999 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3003 folderview_move_folder(folderview, src_item, item, copy);
3004 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3006 folderview->nodes_to_recollapse = NULL;
3007 } else if (info == TARGET_MAIL_URI_LIST) {
3008 if (gtk_clist_get_selection_info
3009 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3012 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3014 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3015 debug_print("no node\n");
3018 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3020 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3021 debug_print("no item\n");
3024 folderview_finish_dnd(data->data, drag_context, time, item);
3028 static void folderview_drag_end_cb(GtkWidget *widget,
3029 GdkDragContext *drag_context,
3030 FolderView *folderview)
3032 drag_state_stop(folderview);
3033 g_slist_free(folderview->nodes_to_recollapse);
3034 folderview->nodes_to_recollapse = NULL;
3037 void folderview_register_popup(FolderViewPopup *fpopup)
3041 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3042 FolderView *folderview = folderviews->data;
3043 GtkItemFactory *factory;
3045 factory = create_ifactory(folderview, fpopup);
3046 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3048 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3051 void folderview_unregister_popup(FolderViewPopup *fpopup)
3055 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3056 FolderView *folderview = folderviews->data;
3058 g_hash_table_remove(folderview->popups, fpopup->klass);
3060 g_hash_table_remove(folderview_popups, fpopup->klass);