2007-09-16 [colin] 3.0.0cvs8-stable
[claws.git] / src / folderview.c
blob2294789a179c514a1493c908ff5b612f57b98946
1 /*
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 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/>.
20 #include "defs.h"
22 #include <glib.h>
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>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
41 #include "main.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"
49 #include "menu.h"
50 #include "stock_pixmap.h"
51 #include "procmsg.h"
52 #include "utils.h"
53 #include "gtkutils.h"
54 #include "prefs_common.h"
55 #include "prefs_account.h"
56 #include "prefs_filtering.h"
57 #include "prefs_folder_item.h"
58 #include "account.h"
59 #include "folder.h"
60 #include "foldersel.h"
61 #include "inc.h"
62 #include "statusbar.h"
63 #include "hooks.h"
64 #include "folderutils.h"
65 #include "partial_download.h"
66 #include "prefs_folder_column.h"
67 #include "filtering.h"
68 #include "quicksearch.h"
69 #include "manual.h"
70 #include "timing.h"
71 #include "log.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,
184 GtkCTreeNode *node);
185 static void folderview_set_folders (FolderView *folderview);
186 static void folderview_sort_folders (FolderView *folderview,
187 GtkCTreeNode *root,
188 Folder *folder);
189 static void folderview_append_folder (FolderView *folderview,
190 Folder *folder);
191 static void folderview_update_node (FolderView *folderview,
192 GtkCTreeNode *node);
194 static gint folderview_clist_compare (GtkCList *clist,
195 gconstpointer ptr1,
196 gconstpointer ptr2);
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,
206 GdkEventKey *event,
207 FolderView *folderview);
208 static void folderview_selected (GtkCTree *ctree,
209 GtkCTreeNode *row,
210 gint column,
211 FolderView *folderview);
212 static void folderview_tree_expanded (GtkCTree *ctree,
213 GtkCTreeNode *node,
214 FolderView *folderview);
215 static void folderview_tree_collapsed (GtkCTree *ctree,
216 GtkCTreeNode *node,
217 FolderView *folderview);
218 static void folderview_popup_close (GtkMenuShell *menu_shell,
219 FolderView *folderview);
220 static void folderview_col_resized (GtkCList *clist,
221 gint column,
222 gint width,
223 FolderView *folderview);
225 static void mark_all_read_cb (FolderView *folderview,
226 guint action,
227 GtkWidget *widget);
229 static void folderview_empty_trash_cb (FolderView *folderview,
230 guint action,
231 GtkWidget *widget);
233 static void folderview_send_queue_cb (FolderView *folderview,
234 guint action,
235 GtkWidget *widget);
237 static void folderview_search_cb (FolderView *folderview,
238 guint action,
239 GtkWidget *widget);
240 static void folderview_run_processing_cb(FolderView *folderview,
241 guint action,
242 GtkWidget *widget);
244 static void folderview_property_cb (FolderView *folderview,
245 guint action,
246 GtkWidget *widget);
248 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
249 GdkDragContext *context,
250 gint x,
251 gint y,
252 guint time,
253 FolderView *folderview);
254 static void folderview_drag_leave_cb (GtkWidget *widget,
255 GdkDragContext *context,
256 guint time,
257 FolderView *folderview);
258 static void folderview_drag_received_cb (GtkWidget *widget,
259 GdkDragContext *drag_context,
260 gint x,
261 gint y,
262 GtkSelectionData *data,
263 guint info,
264 guint time,
265 FolderView *folderview);
266 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
267 FolderView *folderview);
268 static void folderview_drag_data_get (GtkWidget *widget,
269 GdkDragContext *drag_context,
270 GtkSelectionData *selection_data,
271 guint info,
272 guint time,
273 FolderView *folderview);
274 static void folderview_drag_end_cb (GtkWidget *widget,
275 GdkDragContext *drag_context,
276 FolderView *folderview);
278 static void folderview_create_folder_node (FolderView *folderview,
279 FolderItem *item);
280 static gboolean folderview_update_folder (gpointer source,
281 gpointer userdata);
282 static gboolean folderview_update_item_claws (gpointer source,
283 gpointer data);
284 static void folderview_processing_cb(FolderView *folderview, guint action,
285 GtkWidget *widget);
286 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
287 GdkEventButton *event);
289 GHashTable *folderview_popups;
291 static GtkItemFactoryEntry folderview_common_popup_entries[] =
293 {N_("/Mark all re_ad"), NULL, mark_all_read_cb, 0, NULL},
294 {"/---", NULL, NULL, 0, "<Separator>"},
295 {N_("/R_un processing rules"), NULL, folderview_run_processing_cb, 0, NULL},
296 {N_("/_Search folder..."), NULL, folderview_search_cb, 0, NULL},
297 {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL},
298 {N_("/Process_ing..."), NULL, folderview_processing_cb, 0, NULL},
301 static GtkItemFactoryEntry folder_view_trash_popup_entries[] = {
302 {"/------trashsep", NULL, NULL, 0, "<Separator>"},
303 {N_("/Empty _trash..."), NULL, folderview_empty_trash_cb, 0, NULL},
306 static GtkItemFactoryEntry folder_view_queue_popup_entries[] = {
307 {"/------queuesep", NULL, NULL, 0, "<Separator>"},
308 {N_("/Send _queue..."), NULL, folderview_send_queue_cb, 0, NULL},
312 GtkTargetEntry folderview_drag_types[] =
314 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
315 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
318 void folderview_initialize(void)
320 FolderViewPopup *fpopup;
321 guint i, n_entries;
322 GSList *entries = NULL;
324 fpopup = g_new0(FolderViewPopup, 1);
326 n_entries = sizeof(folderview_common_popup_entries) /
327 sizeof(folderview_common_popup_entries[0]);
328 for (i = 0; i < n_entries; i++)
329 entries = g_slist_append(entries, &folderview_common_popup_entries[i]);
331 fpopup->klass = "common";
332 fpopup->path = "<CommonFolder>";
333 fpopup->entries = entries;
334 fpopup->set_sensitivity = NULL;
336 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
337 g_hash_table_insert(folderview_popups, "common", fpopup);
340 static GtkItemFactory *create_ifactory(FolderView *folderview, FolderViewPopup *fpopup)
342 GSList *entries;
343 GtkItemFactory *factory;
344 FolderViewPopup *fpopup_common;
345 GtkWidget *popup;
347 factory = gtk_item_factory_new(GTK_TYPE_MENU, fpopup->path, NULL);
348 gtk_item_factory_set_translate_func(factory, menu_translate,
349 NULL, NULL);
351 for (entries = fpopup->entries; entries != NULL; entries = g_slist_next(entries))
352 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
354 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
355 if (fpopup_common != fpopup)
356 for (entries = fpopup_common->entries; entries != NULL; entries = g_slist_next(entries))
357 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
359 popup = gtk_item_factory_get_widget(factory, fpopup->path);
360 g_signal_connect(G_OBJECT(popup), "selection_done",
361 G_CALLBACK(folderview_popup_close),
362 folderview);
364 return factory;
367 static void create_ifactories(gpointer key, gpointer value, gpointer data)
369 FolderView *folderview = data;
370 FolderViewPopup *fpopup = value;
371 GtkItemFactory *factory;
373 factory = create_ifactory(folderview, fpopup);
374 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
377 static void folderview_column_set_titles(FolderView *folderview)
379 GtkWidget *ctree = folderview->ctree;
380 GtkWidget *label_folder;
381 GtkWidget *label_new;
382 GtkWidget *label_unread;
383 GtkWidget *label_total;
384 GtkWidget *hbox_folder;
385 GtkWidget *hbox_new;
386 GtkWidget *hbox_unread;
387 GtkWidget *hbox_total;
388 gint *col_pos = folderview->col_pos;
390 debug_print("setting titles...\n");
391 gtk_widget_realize(folderview->ctree);
392 gtk_widget_show_all(folderview->scrolledwin);
394 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
395 * instead text (text overflows making them unreadable and ugly) */
396 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
397 &newxpm, &newxpmmask);
398 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
399 &unreadxpm, &unreadxpmmask);
400 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
401 &readxpm, &readxpmmask);
403 label_folder = gtk_label_new(_("Folder"));
404 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
405 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
406 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
408 gtk_clist_column_titles_active(GTK_CLIST(ctree));
410 hbox_folder = gtk_hbox_new(FALSE, 4);
411 hbox_new = gtk_hbox_new(FALSE, 4);
412 hbox_unread = gtk_hbox_new(FALSE, 4);
413 hbox_total = gtk_hbox_new(FALSE, 4);
415 /* left justified */
416 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
417 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
418 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
419 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
420 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
421 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
422 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
423 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
425 gtk_widget_show_all(hbox_folder);
426 gtk_widget_show_all(hbox_new);
427 gtk_widget_show_all(hbox_unread);
428 gtk_widget_show_all(hbox_total);
430 #ifdef MAEMO
431 gtk_widget_set_size_request(hbox_new, -1, 20);
432 gtk_widget_set_size_request(hbox_unread, -1, 20);
433 gtk_widget_set_size_request(hbox_total, -1, 20);
434 #endif
436 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
437 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
438 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
439 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
441 #ifdef MAEMO
442 GTK_EVENTS_FLUSH();
443 #endif
445 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
446 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
447 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
450 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
452 FolderView *folderview = (FolderView *)data;
453 GdkEventButton event;
454 if (folderview_get_selected_item(folderview) == NULL)
455 return FALSE;
457 event.button = 3;
458 event.time = gtk_get_current_event_time();
460 folderview_set_sens_and_popup_menu(folderview, -1,
461 &event);
463 return TRUE;
467 static GtkWidget *folderview_ctree_create(FolderView *folderview)
469 GtkWidget *ctree;
470 gint *col_pos;
471 FolderColumnState *col_state;
472 FolderColumnType type;
473 gchar *titles[N_FOLDER_COLS];
474 gint i;
475 GtkWidget *scrolledwin = folderview->scrolledwin;
477 debug_print("creating tree...\n");
478 memset(titles, 0, sizeof(titles));
480 col_state = prefs_folder_column_get_config();
481 memset(titles, 0, sizeof(titles));
483 col_pos = folderview->col_pos;
485 for (i = 0; i < N_FOLDER_COLS; i++) {
486 folderview->col_state[i] = col_state[i];
487 type = col_state[i].type;
488 col_pos[type] = i;
491 titles[col_pos[F_COL_FOLDER]] = _("Folder");
492 titles[col_pos[F_COL_NEW]] = _("New");
493 titles[col_pos[F_COL_UNREAD]] = _("Unread");
494 /* TRANSLATORS: This in Number sign in American style */
495 titles[col_pos[F_COL_TOTAL]] = _("#");
497 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
498 titles);
500 if (prefs_common.show_col_headers == FALSE)
501 gtk_clist_column_titles_hide(GTK_CLIST(ctree));
504 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
505 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
506 GTK_JUSTIFY_RIGHT);
507 gtk_clist_set_column_justification(GTK_CLIST(ctree),
508 col_pos[F_COL_UNREAD],
509 GTK_JUSTIFY_RIGHT);
510 gtk_clist_set_column_justification(GTK_CLIST(ctree),
511 col_pos[F_COL_TOTAL],
512 GTK_JUSTIFY_RIGHT);
513 if (prefs_common.enable_dotted_lines) {
514 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
515 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
516 GTK_CTREE_EXPANDER_SQUARE);
517 } else {
518 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
519 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
520 GTK_CTREE_EXPANDER_TRIANGLE);
523 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
524 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
526 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
527 gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
529 /* don't let title buttons take key focus */
530 for (i = 0; i < N_FOLDER_COLS; i++) {
531 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
532 GTK_CAN_FOCUS);
533 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
534 prefs_common.folder_col_size[i]);
535 gtk_clist_set_column_visibility
536 (GTK_CLIST(ctree), i, col_state[i].visible);
539 g_signal_connect(G_OBJECT(ctree), "key_press_event",
540 G_CALLBACK(folderview_key_pressed),
541 folderview);
542 g_signal_connect(G_OBJECT(ctree), "button_press_event",
543 G_CALLBACK(folderview_button_pressed),
544 folderview);
545 #ifndef MAEMO
546 g_signal_connect(G_OBJECT(ctree), "popup-menu",
547 G_CALLBACK(folderview_popup_menu), folderview);
548 #else
549 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
550 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
551 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
552 G_CALLBACK(folderview_popup_menu), folderview);
553 #endif
554 g_signal_connect(G_OBJECT(ctree), "button_release_event",
555 G_CALLBACK(folderview_button_released),
556 folderview);
557 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
558 G_CALLBACK(folderview_selected), folderview);
559 #ifndef MAEMO
560 /* drag-n-dropping folders on maemo is impractical as this
561 * opens the folder almost everytime */
562 g_signal_connect(G_OBJECT(ctree), "start_drag",
563 G_CALLBACK(folderview_start_drag), folderview);
564 #endif
565 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
566 G_CALLBACK(folderview_drag_data_get),
567 folderview);
569 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
570 G_CALLBACK(folderview_tree_expanded),
571 folderview);
572 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
573 G_CALLBACK(folderview_tree_collapsed),
574 folderview);
576 g_signal_connect(G_OBJECT(ctree), "resize_column",
577 G_CALLBACK(folderview_col_resized),
578 folderview);
580 /* drop callback */
581 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
582 folderview_drag_types, 2,
583 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
584 g_signal_connect(G_OBJECT(ctree), "drag_motion",
585 G_CALLBACK(folderview_drag_motion_cb),
586 folderview);
587 g_signal_connect(G_OBJECT(ctree), "drag_leave",
588 G_CALLBACK(folderview_drag_leave_cb),
589 folderview);
590 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
591 G_CALLBACK(folderview_drag_received_cb),
592 folderview);
593 g_signal_connect(G_OBJECT(ctree), "drag_end",
594 G_CALLBACK(folderview_drag_end_cb),
595 folderview);
597 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
599 return ctree;
602 void folderview_set_column_order(FolderView *folderview)
604 GtkWidget *ctree;
605 FolderItem *item = folderview_get_selected_item(folderview);
606 GtkWidget *scrolledwin = folderview->scrolledwin;
608 debug_print("recreating tree...\n");
609 gtk_widget_destroy(folderview->ctree);
611 folderview->ctree = ctree = folderview_ctree_create(folderview);
612 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
613 GTK_CLIST(ctree)->hadjustment);
614 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
615 GTK_CLIST(ctree)->vadjustment);
616 gtk_widget_show(ctree);
618 folderview_set(folderview);
619 folderview_column_set_titles(folderview);
621 folderview_select(folderview,item);
624 FolderView *folderview_create(void)
626 FolderView *folderview;
627 GtkWidget *scrolledwin;
628 GtkWidget *ctree;
630 debug_print("Creating folder view...\n");
631 folderview = g_new0(FolderView, 1);
633 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
634 gtk_scrolled_window_set_policy
635 (GTK_SCROLLED_WINDOW(scrolledwin),
636 GTK_POLICY_AUTOMATIC,
637 prefs_common.folderview_vscrollbar_policy);
638 gtk_widget_set_size_request(scrolledwin,
639 prefs_common.folderview_width,
640 prefs_common.folderview_height);
642 folderview->scrolledwin = scrolledwin;
643 ctree = folderview_ctree_create(folderview);
645 /* create popup factories */
646 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
647 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
649 folderview->ctree = ctree;
651 folderview->folder_update_callback_id =
652 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
653 folderview->folder_item_update_callback_id =
654 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
656 gtk_widget_show_all(scrolledwin);
658 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
659 folderview_list = g_list_append(folderview_list, folderview);
660 folderview->deferred_refresh_id = -1;
662 return folderview;
665 void folderview_init(FolderView *folderview)
667 GtkWidget *ctree = folderview->ctree;
668 GdkColor gdk_color;
670 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
671 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
675 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
688 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
695 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
698 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
699 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
700 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
701 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
702 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
703 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
704 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
705 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
706 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
707 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
708 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
709 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
710 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
711 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
712 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
713 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
714 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
715 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
717 if (!normal_style) {
718 PangoFontDescription *font_desc;
719 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
720 font_desc = pango_font_description_from_string(NORMAL_FONT);
721 if (font_desc) {
722 if (normal_style->font_desc)
723 pango_font_description_free
724 (normal_style->font_desc);
725 normal_style->font_desc = font_desc;
727 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
728 normal_color_style = gtk_style_copy(normal_style);
729 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
731 gtk_widget_set_style(ctree, normal_style);
734 if (!bold_style) {
735 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
736 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
737 pango_font_description_set_weight
738 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
739 bold_color_style = gtk_style_copy(bold_style);
740 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
742 bold_tgtfold_style = gtk_style_copy(bold_style);
743 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
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 = -1;
761 return FALSE;
764 void folderview_set(FolderView *folderview)
766 GtkCTree *ctree = GTK_CTREE(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 == -1)
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 = gtk_ctree_node_get_row_data(ctree, folderview->selected);
789 if (folderview->opened)
790 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
792 folderview->selected = NULL;
793 folderview->opened = NULL;
795 gtk_clist_freeze(GTK_CLIST(ctree));
796 gtk_clist_clear(GTK_CLIST(ctree));
798 folderview_set_folders(folderview);
800 if (sel_item)
801 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
802 if (op_item)
803 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
805 gtk_clist_thaw(GTK_CLIST(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 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
822 GtkCTreeNode *node;
823 GtkCTreeNode *old_selected = folderview->selected;
825 if (!item) return;
827 node = gtk_ctree_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(FolderView *folderview, guint action,
835 GtkWidget *widget)
837 FolderItem *item;
838 AlertValue val;
840 item = folderview_get_selected_item(folderview);
841 if (item == NULL)
842 return;
844 if (folderview->summaryview->folder_item != item
845 && prefs_common.ask_mark_all_read) {
846 val = alertpanel_full(_("Mark all as read"),
847 _("Do you really want to mark all mails in this "
848 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
849 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
851 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
852 return;
853 else if (val & G_ALERTDISABLE)
854 prefs_common.ask_mark_all_read = FALSE;
857 summary_lock(folderview->summaryview);
858 folder_item_update_freeze();
859 if (folderview->summaryview->folder_item == item)
860 summary_freeze(folderview->summaryview);
861 folderutils_mark_all_read(item);
862 if (folderview->summaryview->folder_item == item)
863 summary_thaw(folderview->summaryview);
864 folder_item_update_thaw();
865 summary_unlock(folderview->summaryview);
868 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
870 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
872 g_return_if_fail(node != NULL);
874 if (folderview->open_folder) {
875 return;
878 folderview->open_folder = TRUE;
879 gtkut_ctree_set_focus_row(ctree, node);
880 gtk_ctree_select(ctree, node);
881 if (folderview->summaryview->folder_item &&
882 folderview->summaryview->folder_item->total_msgs > 0)
883 summary_grab_focus(folderview->summaryview);
884 else
885 gtk_widget_grab_focus(folderview->ctree);
887 gtkut_ctree_expand_parent_all(ctree, node);
890 void folderview_unselect(FolderView *folderview)
892 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
893 gtk_ctree_collapse
894 (GTK_CTREE(folderview->ctree), folderview->opened);
896 folderview->selected = folderview->opened = NULL;
899 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
900 GtkCTreeNode *node)
902 FolderItem *item;
904 if (node)
905 node = gtkut_ctree_node_next(ctree, node);
906 else
907 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
909 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
910 item = gtk_ctree_node_get_row_data(ctree, node);
911 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
912 return node;
915 return NULL;
918 void folderview_select_next_marked(FolderView *folderview)
920 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
921 GtkCTreeNode *node = NULL;
922 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
923 gboolean last_open = prefs_common.always_show_msg;
925 prefs_common.summary_select_prio[0] = ACTION_MARKED;
926 prefs_common.always_show_msg = TRUE;
928 if ((node = folderview_find_next_marked(ctree, folderview->opened))
929 != NULL) {
930 folderview_select_node(folderview, node);
931 goto out;
934 if (!folderview->opened ||
935 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
936 goto out;
938 /* search again from the first node */
939 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
940 folderview_select_node(folderview, node);
942 out:
943 prefs_common.summary_select_prio[0] = last_summary_select_prio;
944 prefs_common.always_show_msg = last_open;
947 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
948 GtkCTreeNode *node)
950 FolderItem *item;
952 if (node)
953 node = gtkut_ctree_node_next(ctree, node);
954 else
955 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
957 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
958 item = gtk_ctree_node_get_row_data(ctree, node);
959 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
960 return node;
963 return NULL;
966 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
968 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
969 GtkCTreeNode *node = NULL;
970 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
971 gboolean last_open = prefs_common.always_show_msg;
973 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
974 prefs_common.always_show_msg = force_open ? TRUE : last_open;
976 if ((node = folderview_find_next_unread(ctree, folderview->opened))
977 != NULL) {
978 folderview_select_node(folderview, node);
979 goto out;
982 if (!folderview->opened ||
983 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
984 goto out;
986 /* search again from the first node */
987 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
988 folderview_select_node(folderview, node);
990 out:
991 prefs_common.summary_select_prio[0] = last_summary_select_prio;
992 prefs_common.always_show_msg = last_open;
995 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
996 GtkCTreeNode *node)
998 FolderItem *item;
1000 if (node)
1001 node = gtkut_ctree_node_next(ctree, node);
1002 else
1003 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1005 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1006 item = gtk_ctree_node_get_row_data(ctree, node);
1007 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1008 return node;
1011 return NULL;
1014 void folderview_select_next_new(FolderView *folderview)
1016 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1017 GtkCTreeNode *node = NULL;
1018 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1019 gboolean last_open = prefs_common.always_show_msg;
1021 prefs_common.summary_select_prio[0] = ACTION_NEW;
1022 prefs_common.always_show_msg = TRUE;
1024 if ((node = folderview_find_next_new(ctree, folderview->opened))
1025 != NULL) {
1026 folderview_select_node(folderview, node);
1027 goto out;
1030 if (!folderview->opened ||
1031 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1032 goto out;
1034 /* search again from the first node */
1035 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1036 folderview_select_node(folderview, node);
1038 out:
1039 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1040 prefs_common.always_show_msg = last_open;
1043 FolderItem *folderview_get_selected_item(FolderView *folderview)
1045 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1047 if (!folderview->selected) return NULL;
1048 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1051 static void folderview_set_folders(FolderView *folderview)
1053 GList *list;
1054 list = folder_get_list();
1056 for (; list != NULL; list = list->next) {
1057 folderview_append_folder(folderview, FOLDER(list->data));
1061 static gchar *get_scan_str(FolderItem *item)
1063 if (item->path)
1064 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1065 item->folder->name, G_DIR_SEPARATOR,
1066 item->path);
1067 else
1068 return g_strdup_printf(_("Scanning folder %s ..."),
1069 item->folder->name);
1071 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1072 gpointer data)
1074 GList *list;
1075 for (list = folderview_list; list != NULL; list = list->next) {
1076 FolderView *folderview = (FolderView *)list->data;
1077 MainWindow *mainwin = folderview->mainwin;
1078 gchar *str = get_scan_str(item);
1080 STATUSBAR_PUSH(mainwin, str);
1081 STATUSBAR_POP(mainwin);
1082 g_free(str);
1086 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1088 GtkWidget *window;
1089 MainWindow *mainwin = mainwindow_get_mainwindow();
1090 FolderView *folderview = NULL;
1091 GtkAdjustment *pos = NULL;
1092 gint height = 0;
1094 g_return_if_fail(folder != NULL);
1096 if (!folder->klass->scan_tree) return;
1098 if (rebuild &&
1099 alertpanel_full(_("Rebuild folder tree"),
1100 _("Rebuilding the folder tree will remove "
1101 "local caches. Do you want to continue?"),
1102 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1103 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1104 != G_ALERTALTERNATE) {
1105 return;
1108 inc_lock();
1109 if (rebuild)
1110 window = label_window_create(_("Rebuilding folder tree..."));
1111 else
1112 window = label_window_create(_("Scanning folder tree..."));
1114 if (mainwin)
1115 folderview = mainwin->folderview;
1117 if (folderview) {
1118 pos = gtk_scrolled_window_get_vadjustment(
1119 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1120 height = pos->value;
1123 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1124 folder_scan_tree(folder, rebuild);
1125 folder_set_ui_func(folder, NULL, NULL);
1127 folderview_set_all();
1129 if (folderview) {
1130 pos = gtk_scrolled_window_get_vadjustment(
1131 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1132 gtk_adjustment_set_value(pos, height);
1133 gtk_adjustment_changed(pos);
1135 label_window_destroy(window);
1136 inc_unlock();
1139 void folderview_fast_rescan_tree(Folder *folder)
1141 GtkWidget *window;
1142 MainWindow *mainwin = mainwindow_get_mainwindow();
1143 FolderView *folderview = NULL;
1144 GtkAdjustment *pos = NULL;
1145 gint height = 0;
1147 g_return_if_fail(folder != NULL);
1149 if (!folder->klass->scan_tree) return;
1151 inc_lock();
1153 window = label_window_create(_("Scanning folder tree..."));
1155 if (mainwin)
1156 folderview = mainwin->folderview;
1158 if (folderview) {
1159 pos = gtk_scrolled_window_get_vadjustment(
1160 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1161 height = pos->value;
1164 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1165 folder_fast_scan_tree(folder);
1166 folder_set_ui_func(folder, NULL, NULL);
1168 folderview_set_all();
1170 if (folderview) {
1171 pos = gtk_scrolled_window_get_vadjustment(
1172 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1173 gtk_adjustment_set_value(pos, height);
1174 gtk_adjustment_changed(pos);
1176 label_window_destroy(window);
1177 inc_unlock();
1180 /** folderview_check_new()
1181 * Scan and update the folder and return the
1182 * count the number of new messages since last check.
1183 * \param folder the folder to check for new messages
1184 * \return the number of new messages since last check
1186 gint folderview_check_new(Folder *folder)
1188 GList *list;
1189 FolderItem *item;
1190 FolderView *folderview;
1191 GtkCTree *ctree;
1192 GtkCTreeNode *node;
1193 gint new_msgs = 0;
1194 gint former_new_msgs = 0;
1195 gint former_new = 0, former_unread = 0, former_total;
1197 for (list = folderview_list; list != NULL; list = list->next) {
1198 folderview = (FolderView *)list->data;
1199 ctree = GTK_CTREE(folderview->ctree);
1201 inc_lock();
1202 main_window_lock(folderview->mainwin);
1204 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1205 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1206 gchar *str = NULL;
1207 item = gtk_ctree_node_get_row_data(ctree, node);
1208 if (!item || !item->path || !item->folder) continue;
1209 if (item->no_select) continue;
1210 if (folder && folder != item->folder) continue;
1211 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1212 if (!item->prefs->newmailcheck) continue;
1213 if (item->processing_pending == TRUE) {
1214 debug_print("skipping %s, processing pending\n",
1215 item->path ? item->path : item->name);
1216 continue;
1219 str = get_scan_str(item);
1221 STATUSBAR_PUSH(folderview->mainwin, str);
1222 GTK_EVENTS_FLUSH();
1223 g_free(str);
1225 folderview_scan_tree_func(item->folder, item, NULL);
1226 former_new = item->new_msgs;
1227 former_unread = item->unread_msgs;
1228 former_total = item->total_msgs;
1230 if (item->folder->klass->scan_required &&
1231 (item->folder->klass->scan_required(item->folder, item) ||
1232 item->folder->inbox == item ||
1233 item->opened == TRUE ||
1234 item->processing_pending == TRUE)) {
1235 if (folder_item_scan(item) < 0) {
1236 if (folder) {
1237 summaryview_unlock(folderview->summaryview, item);
1238 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1239 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1240 item->path ? item->path:item->name);
1241 STATUSBAR_POP(folderview->mainwin);
1242 continue;
1243 } else if (!FOLDER_IS_LOCAL(folder)) {
1244 STATUSBAR_POP(folderview->mainwin);
1245 break;
1249 } else if (!item->folder->klass->scan_required) {
1250 if (folder_item_scan(item) < 0) {
1251 summaryview_unlock(folderview->summaryview, item);
1252 if (folder && !FOLDER_IS_LOCAL(folder)) {
1253 STATUSBAR_POP(folderview->mainwin);
1254 break;
1258 if (former_new != item->new_msgs ||
1259 former_unread != item->unread_msgs ||
1260 former_total != item->total_msgs)
1261 folderview_update_node(folderview, node);
1263 new_msgs += item->new_msgs;
1264 former_new_msgs += former_new;
1265 STATUSBAR_POP(folderview->mainwin);
1268 main_window_unlock(folderview->mainwin);
1269 inc_unlock();
1272 folder_write_list();
1273 /* Number of new messages since last check is the just the difference
1274 * between former_new_msgs and new_msgs. If new_msgs is less than
1275 * former_new_msgs, that would mean another session accessed the folder
1276 * and the result is not well defined.
1278 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1279 return new_msgs;
1282 void folderview_check_new_all(void)
1284 GList *list;
1285 GtkWidget *window;
1286 FolderView *folderview;
1288 folderview = (FolderView *)folderview_list->data;
1290 inc_lock();
1291 main_window_lock(folderview->mainwin);
1292 window = label_window_create
1293 (_("Checking for new messages in all folders..."));
1295 list = folder_get_list();
1296 for (; list != NULL; list = list->next) {
1297 Folder *folder = list->data;
1299 folderview_check_new(folder);
1302 folder_write_list();
1303 folderview_set_all();
1305 label_window_destroy(window);
1306 main_window_unlock(folderview->mainwin);
1307 inc_unlock();
1310 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1311 FolderItem *item,
1312 gboolean in_sub)
1314 GNode *node = NULL;
1316 if (!item || !item->folder || !item->folder->node)
1317 return FALSE;
1319 node = item->folder->node;
1321 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1322 node = node->children;
1324 if (in_sub &&
1325 (item->new_msgs > 0 ||
1326 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1327 return TRUE;
1330 while (node != NULL) {
1331 if (node && node->data) {
1332 FolderItem *next_item = (FolderItem*) node->data;
1333 node = node->next;
1334 if (folderview_have_new_children_sub(folderview,
1335 next_item, TRUE))
1336 return TRUE;
1340 return FALSE;
1343 static gboolean folderview_have_new_children(FolderView *folderview,
1344 FolderItem *item)
1346 return folderview_have_new_children_sub(folderview, item, FALSE);
1349 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1350 FolderItem *item,
1351 gboolean in_sub)
1353 GNode *node = NULL;
1355 if (!item || !item->folder || !item->folder->node)
1356 return FALSE;
1358 node = item->folder->node;
1360 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1361 node = node->children;
1363 if (in_sub &&
1364 (item->unread_msgs > 0 ||
1365 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1366 return TRUE;
1369 while (node != NULL) {
1370 if (node && node->data) {
1371 FolderItem *next_item = (FolderItem*) node->data;
1372 node = node->next;
1373 if (folderview_have_unread_children_sub(folderview,
1374 next_item,
1375 TRUE))
1376 return TRUE;
1380 return FALSE;
1383 static gboolean folderview_have_unread_children(FolderView *folderview,
1384 FolderItem *item)
1386 return folderview_have_unread_children_sub(folderview, item, FALSE);
1389 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1390 FolderItem *item,
1391 gboolean in_sub)
1393 GNode *node = NULL;
1395 if (!item || !item->folder || !item->folder->node)
1396 return FALSE;
1398 node = item->folder->node;
1400 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1401 node = node->children;
1403 if (in_sub && item->search_match){
1404 return TRUE;
1407 while (node != NULL) {
1408 if (node && node->data) {
1409 FolderItem *next_item = (FolderItem*) node->data;
1410 node = node->next;
1411 if (folderview_have_matching_children_sub(folderview,
1412 next_item,
1413 TRUE))
1414 return TRUE;
1418 return FALSE;
1421 static gboolean folderview_have_matching_children(FolderView *folderview,
1422 FolderItem *item)
1424 return folderview_have_matching_children_sub(folderview, item, FALSE);
1427 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1428 FolderItem *item,
1429 gboolean in_sub)
1431 GNode *node = NULL;
1433 if (!item || !item->folder || !item->folder->node)
1434 return FALSE;
1436 node = item->folder->node;
1438 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1439 node = node->children;
1441 if (item->marked_msgs != 0) {
1442 return TRUE;
1445 while (node != NULL) {
1446 if (node && node->data) {
1447 FolderItem *next_item = (FolderItem*) node->data;
1448 node = node->next;
1449 if (folderview_have_marked_children_sub(folderview,
1450 next_item, TRUE))
1451 return TRUE;
1455 return FALSE;
1458 static gboolean folderview_have_marked_children(FolderView *folderview,
1459 FolderItem *item)
1461 return folderview_have_marked_children_sub(folderview, item, FALSE);
1464 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1466 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1467 GtkStyle *style = NULL;
1468 GtkStyle *color_style = NULL;
1469 FolderItem *item;
1470 GdkPixmap *xpm, *openxpm;
1471 GdkBitmap *mask, *openmask;
1472 static GdkPixmap *searchicon;
1473 static GdkBitmap *searchmask;
1474 gboolean mark = FALSE;
1475 gchar *name;
1476 gchar *str;
1477 gboolean add_unread_mark;
1478 gboolean add_sub_match_mark;
1479 gboolean use_bold, use_color;
1480 gint *col_pos = folderview->col_pos;
1481 SpecialFolderItemType stype;
1483 item = gtk_ctree_node_get_row_data(ctree, node);
1484 g_return_if_fail(item != NULL);
1486 if (!GTK_CTREE_ROW(node)->expanded)
1487 mark = folderview_have_marked_children(folderview, item);
1488 else
1489 mark = (item->marked_msgs != 0);
1491 stype = item->stype;
1492 if (stype == F_NORMAL) {
1493 if (folder_has_parent_of_type(item, F_TRASH))
1494 stype = F_TRASH;
1495 else if (folder_has_parent_of_type(item, F_DRAFT))
1496 stype = F_DRAFT;
1497 else if (folder_has_parent_of_type(item, F_OUTBOX))
1498 stype = F_OUTBOX;
1499 else if (folder_has_parent_of_type(item, F_QUEUE))
1500 stype = F_QUEUE;
1502 switch (stype) {
1503 case F_INBOX:
1504 if (item->hide_read_msgs) {
1505 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1506 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1507 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1508 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1509 } else {
1510 xpm = mark?m_inboxxpm:inboxxpm;
1511 mask = mark?m_inboxxpmmask:inboxxpmmask;
1512 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1513 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1515 break;
1516 case F_OUTBOX:
1517 if (item->hide_read_msgs) {
1518 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1519 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1520 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1521 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1522 } else {
1523 xpm = mark?m_outboxxpm:outboxxpm;
1524 mask = mark?m_outboxxpmmask:outboxxpmmask;
1525 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1526 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1528 break;
1529 case F_QUEUE:
1530 if (item->hide_read_msgs) {
1531 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1532 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1533 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1534 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1535 } else {
1536 xpm = mark?m_queuexpm:queuexpm;
1537 mask = mark?m_queuexpmmask:queuexpmmask;
1538 openxpm = mark?m_queueopenxpm:queueopenxpm;
1539 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1541 break;
1542 case F_TRASH:
1543 if (item->hide_read_msgs) {
1544 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1545 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1546 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1547 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1548 } else {
1549 xpm = mark?m_trashxpm:trashxpm;
1550 mask = mark?m_trashxpmmask:trashxpmmask;
1551 openxpm = mark?m_trashopenxpm:trashopenxpm;
1552 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1554 break;
1555 case F_DRAFT:
1556 xpm = mark?m_draftsxpm:draftsxpm;
1557 mask = mark?m_draftsxpmmask:draftsxpmmask;
1558 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1559 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1560 break;
1561 default:
1562 if (item->hide_read_msgs) {
1563 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1564 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1565 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1566 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1567 } else {
1568 xpm = mark?m_folderxpm:folderxpm;
1569 mask = mark?m_folderxpmmask:folderxpmmask;
1570 openxpm = mark?m_folderopenxpm:folderopenxpm;
1571 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1575 if (item->no_select) {
1576 xpm = openxpm = noselectxpm;
1577 mask = openmask = noselectxpmmask;
1580 name = folder_item_get_name(item);
1582 if (!GTK_CTREE_ROW(node)->expanded) {
1583 add_unread_mark = folderview_have_unread_children(
1584 folderview, item);
1585 add_sub_match_mark = folderview_have_matching_children(
1586 folderview, item);
1587 } else {
1588 add_unread_mark = FALSE;
1589 add_sub_match_mark = FALSE;
1592 if (item->search_match) {
1593 if (!searchicon) {
1594 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1595 &searchicon, &searchmask);
1597 xpm = openxpm = searchicon;
1598 mask = openmask = searchmask;
1601 str = NULL;
1602 if (prefs_common.display_folder_unread) {
1603 if (folder_has_parent_of_type(item, F_QUEUE)) {
1604 /* only total_msgs matters here */
1605 if (item->total_msgs > 0) {
1606 /* show total number (should be equal to the unread number)
1607 and signs if any */
1608 str = g_strdup_printf("%s (%d%s%s)",
1609 name, item->total_msgs,
1610 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1611 (item->unreadmarked_msgs > 0) ? "!" : "");
1613 } else {
1614 if (prefs_common.display_folder_unread == 1) {
1615 if (item->unread_msgs > 0) {
1616 /* show unread number and signs */
1617 str = g_strdup_printf("%s (%d%s%s)",
1618 name, item->unread_msgs,
1619 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1620 (item->unreadmarked_msgs > 0) ? "!" : "");
1622 } else {
1623 if (item->total_msgs > 0) {
1624 /* show unread number, total number and signs if any */
1625 str = g_strdup_printf("%s (%d/%d%s%s)",
1626 name, item->unread_msgs, item->total_msgs,
1627 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1628 (item->unreadmarked_msgs > 0) ? "!" : "");
1632 if ((str == NULL) &&
1633 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1634 /* no unread/total numbers, but at least one sign */
1635 str = g_strdup_printf("%s (%s%s)",
1636 name,
1637 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1638 (item->unreadmarked_msgs > 0) ? "!" : "");
1641 if (str == NULL) {
1642 /* last fallback, folder name only or with ! sign */
1643 str = g_strdup_printf("%s%s",
1644 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1646 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1647 xpm, mask, openxpm, openmask,
1648 FALSE, GTK_CTREE_ROW(node)->expanded);
1649 g_free(str);
1650 g_free(name);
1652 if (!folder_item_parent(item)) {
1653 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1654 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1655 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1656 } else {
1657 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1658 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1659 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1662 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1663 folder_has_parent_of_type(item, F_DRAFT) ||
1664 folder_has_parent_of_type(item, F_TRASH)) {
1665 use_bold = use_color = FALSE;
1666 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1667 /* highlight queue folder if there are any messages */
1668 use_bold = use_color = (item->total_msgs > 0);
1669 } else {
1670 /* if unread messages exist, print with bold font */
1671 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1672 || add_unread_mark;
1673 /* if new messages exist, print with colored letter */
1674 use_color =
1675 (item->new_msgs > 0) ||
1676 (add_unread_mark &&
1677 folderview_have_new_children(folderview, item));
1680 gtk_ctree_node_set_foreground(ctree, node, NULL);
1682 if (use_bold) {
1683 GdkColor gdk_color;
1685 if (item->prefs->color > 0 && !use_color) {
1686 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1687 color_style = gtk_style_copy(bold_style);
1688 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1689 style = color_style;
1690 } else if (use_color) {
1691 style = bold_color_style;
1692 } else
1693 style = bold_style;
1694 if (item->op_count > 0) {
1695 style = bold_tgtfold_style;
1697 } else if (use_color) {
1698 style = normal_color_style;
1699 gtk_ctree_node_set_foreground(ctree, node,
1700 &folderview->color_new);
1701 } else if (item->op_count > 0) {
1702 style = bold_tgtfold_style;
1703 } else if (item->prefs->color > 0) {
1704 GdkColor gdk_color;
1705 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1706 color_style = gtk_style_copy(normal_style);
1707 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1708 style = color_style;
1709 } else {
1710 style = normal_style;
1713 gtk_ctree_node_set_row_style(ctree, node, style);
1715 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1716 folderview_update_node(folderview, node);
1719 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1721 GList *list;
1722 FolderView *folderview;
1723 GtkCTree *ctree;
1724 GtkCTreeNode *node;
1726 g_return_if_fail(item != NULL);
1728 for (list = folderview_list; list != NULL; list = list->next) {
1729 folderview = (FolderView *)list->data;
1730 ctree = GTK_CTREE(folderview->ctree);
1732 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1733 if (node) {
1734 item->search_match = matches;
1735 folderview_update_node(folderview, node);
1740 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1742 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1743 FolderView *folderview = (FolderView *)data;
1744 GtkCTree *ctree;
1745 GtkCTreeNode *node;
1746 g_return_val_if_fail(update_info != NULL, TRUE);
1747 g_return_val_if_fail(update_info->item != NULL, TRUE);
1748 g_return_val_if_fail(folderview != NULL, FALSE);
1750 ctree = GTK_CTREE(folderview->ctree);
1752 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1754 if (node) {
1755 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1756 folderview_update_node(folderview, node);
1758 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1759 update_info->item == folderview->summaryview->folder_item &&
1760 update_info->item != NULL)
1761 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1762 summary_show(folderview->summaryview, update_info->item);
1765 return FALSE;
1768 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1769 GNode *gnode, GtkCTreeNode *cnode,
1770 gpointer data)
1772 FolderView *folderview = (FolderView *)data;
1773 FolderItem *item = FOLDER_ITEM(gnode->data);
1775 g_return_val_if_fail(item != NULL, FALSE);
1777 gtk_ctree_node_set_row_data(ctree, cnode, item);
1778 folderview_update_node(folderview, cnode);
1780 return TRUE;
1783 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1784 gpointer data)
1786 FolderView *folderview = (FolderView *)data;
1787 FolderItem *item;
1789 if (GTK_CTREE_ROW(node)->children) {
1790 item = gtk_ctree_node_get_row_data(ctree, node);
1791 g_return_if_fail(item != NULL);
1793 if (!item->collapsed)
1794 gtk_ctree_expand(ctree, node);
1795 else
1796 folderview_update_node(folderview, node);
1800 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1801 GtkCTreeNode *root, GtkCTreeNode **prev)
1803 if (item) {
1804 GtkCTreeNode *node, *parent, *sibling;
1806 node = gtk_ctree_find_by_row_data(ctree, root, item);
1807 if (!node)
1808 g_warning("%s not found.\n", item->path);
1809 else {
1810 parent = GTK_CTREE_ROW(node)->parent;
1811 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1812 sibling = GTK_CTREE_ROW(*prev)->sibling;
1813 else
1814 sibling = GTK_CTREE_ROW(parent)->children;
1815 while (sibling) {
1816 FolderItem *tmp;
1818 tmp = gtk_ctree_node_get_row_data
1819 (ctree, sibling);
1820 if (tmp->stype != F_NORMAL)
1821 sibling = GTK_CTREE_ROW(sibling)->sibling;
1822 else
1823 break;
1825 if (node != sibling)
1826 gtk_ctree_move(ctree, node, parent, sibling);
1829 *prev = node;
1833 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1834 Folder *folder)
1836 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1837 GtkCTreeNode *prev = NULL;
1839 gtk_clist_freeze(GTK_CLIST(ctree));
1840 gtk_sctree_sort_recursive(ctree, root);
1841 if (root && GTK_CTREE_ROW(root)->parent) {
1842 gtk_clist_thaw(GTK_CLIST(ctree));
1843 return;
1845 set_special_folder(ctree, folder->inbox, root, &prev);
1846 set_special_folder(ctree, folder->outbox, root, &prev);
1847 set_special_folder(ctree, folder->draft, root, &prev);
1848 set_special_folder(ctree, folder->queue, root, &prev);
1849 set_special_folder(ctree, folder->trash, root, &prev);
1850 gtk_clist_thaw(GTK_CLIST(ctree));
1853 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1855 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1856 GtkCTreeNode *root;
1858 g_return_if_fail(folder != NULL);
1860 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1861 folderview_gnode_func, folderview);
1862 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1863 folderview);
1864 folderview_sort_folders(folderview, root, folder);
1867 /* callback functions */
1868 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1869 GdkEventButton *event)
1871 GtkCList *clist = GTK_CLIST(folderview->ctree);
1872 FolderItem *item;
1873 Folder *folder;
1874 FolderViewPopup *fpopup;
1875 GtkItemFactory *fpopup_factory;
1876 GtkWidget *popup;
1877 FolderItem *special_trash = NULL, *special_queue = NULL;
1878 PrefsAccount *ac;
1880 if (row > 0)
1881 item = gtk_clist_get_row_data(clist, row);
1882 else
1883 item = folderview_get_selected_item(folderview);
1885 g_return_if_fail(item != NULL);
1886 g_return_if_fail(item->folder != NULL);
1887 folder = item->folder;
1889 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1890 if (fpopup != NULL)
1891 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1892 else {
1893 fpopup = g_hash_table_lookup(folderview_popups, "common");
1894 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1897 if (fpopup->set_sensitivity != NULL)
1898 fpopup->set_sensitivity(fpopup_factory, item);
1900 if (NULL != (ac = account_find_from_item(item))) {
1901 special_trash = account_get_special_folder(ac, F_TRASH);
1902 special_queue = account_get_special_folder(ac, F_QUEUE);
1905 if ((item == folder->trash || item == special_trash
1906 || folder_has_parent_of_type(item, F_TRASH)) &&
1907 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1908 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1909 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1910 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1911 && !folder_has_parent_of_type(item, F_TRASH)) {
1912 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1913 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1916 if ((item == folder->queue || item == special_queue
1917 || folder_has_parent_of_type(item, F_QUEUE)) &&
1918 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1919 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1920 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1921 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1922 && !folder_has_parent_of_type(item, F_QUEUE)) {
1923 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1924 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1927 #define SET_SENS(name, sens) \
1928 menu_set_sensitive(fpopup_factory, name, sens)
1930 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1931 SET_SENS("/Download messages", !item->no_select);
1932 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1933 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1934 folderview->selected == folderview->opened);
1935 SET_SENS("/Run processing rules", item->prefs->processing &&
1936 item->total_msgs >= 1);
1937 SET_SENS("/Properties...", !item->no_select);
1938 SET_SENS("/Processing...", item->node->parent != NULL && !item->no_select);
1939 if (item == folder->trash || item == special_trash
1940 || folder_has_parent_of_type(item, F_TRASH)) {
1941 GSList *msglist = folder_item_get_msg_list(item);
1942 SET_SENS("/Empty trash...", msglist != NULL);
1943 procmsg_msg_list_free(msglist);
1945 if (item == folder->queue || item == special_queue
1946 || folder_has_parent_of_type(item, F_QUEUE)) {
1947 GSList *msglist = folder_item_get_msg_list(item);
1948 SET_SENS("/Send queue...", msglist != NULL);
1949 procmsg_msg_list_free(msglist);
1951 #undef SET_SENS
1953 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1954 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1955 event->button, event->time);
1958 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1959 FolderView *folderview)
1961 GtkCList *clist = GTK_CLIST(ctree);
1962 gint prev_row = -1, row = -1, column = -1;
1964 if (!event) return FALSE;
1966 if (event->button == 1 || event->button == 2) {
1967 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1968 folderview->open_folder = TRUE;
1970 if (event->type == GDK_2BUTTON_PRESS) {
1971 if (clist->selection) {
1972 GtkCTreeNode *node;
1974 node = GTK_CTREE_NODE(clist->selection->data);
1975 if (node) {
1976 gtk_ctree_toggle_expansion(
1977 GTK_CTREE(ctree),
1978 node);
1979 folderview->open_folder = FALSE;
1983 return FALSE;
1986 if (event->button == 2 || event->button == 3) {
1987 /* right clicked */
1988 if (clist->selection) {
1989 GtkCTreeNode *node;
1991 node = GTK_CTREE_NODE(clist->selection->data);
1992 if (node)
1993 prev_row = gtkut_ctree_get_nth_from_node
1994 (GTK_CTREE(ctree), node);
1997 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1998 &row, &column))
1999 return FALSE;
2000 if (prev_row != row) {
2001 gtk_clist_unselect_all(clist);
2002 if (event->button == 2)
2003 folderview_select_node
2004 (folderview,
2005 gtk_ctree_node_nth(GTK_CTREE(ctree),
2006 row));
2007 else
2008 gtk_clist_select_row(clist, row, column);
2012 if (event->button != 3) return FALSE;
2014 folderview_set_sens_and_popup_menu(folderview, row, event);
2015 return FALSE;
2018 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2019 FolderView *folderview)
2021 int row = -1, column = -1;
2023 if (!event) return FALSE;
2025 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2026 &row, &column))
2027 return FALSE;
2028 if (event->button == 1 && folderview->open_folder == FALSE &&
2029 folderview->opened != NULL) {
2030 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2031 folderview->opened);
2032 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2035 return FALSE;
2038 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2039 FolderView *folderview)
2041 if (!event) return FALSE;
2043 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2044 return FALSE;
2046 switch (event->keyval) {
2047 case GDK_Right:
2048 #ifndef MAEMO
2049 case GDK_Return:
2050 case GDK_KP_Enter:
2051 #endif
2052 if (folderview->selected) {
2053 folderview_select_node(folderview,
2054 folderview->selected);
2056 break;
2057 #ifdef MAEMO
2058 case GDK_Return:
2059 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2060 gtk_ctree_toggle_expansion(
2061 GTK_CTREE(folderview->ctree),
2062 folderview->selected);
2064 break;
2065 #endif
2066 case GDK_space:
2067 if (folderview->selected) {
2068 if (folderview->opened == folderview->selected &&
2069 (!folderview->summaryview->folder_item ||
2070 folderview->summaryview->folder_item->total_msgs == 0))
2071 folderview_select_next_unread(folderview, TRUE);
2072 else
2073 folderview_select_node(folderview,
2074 folderview->selected);
2076 break;
2077 default:
2078 break;
2081 return FALSE;
2084 typedef struct _PostponedSelectData
2086 GtkCTree *ctree;
2087 GtkCTreeNode *row;
2088 gint column;
2089 FolderView *folderview;
2090 } PostponedSelectData;
2092 static gboolean postpone_select(void *data)
2094 PostponedSelectData *psdata = (PostponedSelectData *)data;
2095 debug_print("trying again\n");
2096 psdata->folderview->open_folder = TRUE;
2097 main_window_cursor_normal(psdata->folderview->mainwin);
2098 STATUSBAR_POP(psdata->folderview->mainwin);
2099 folderview_selected(psdata->ctree, psdata->row,
2100 psdata->column, psdata->folderview);
2101 g_free(psdata);
2102 return FALSE;
2105 void folderview_close_opened(FolderView *folderview)
2107 if (folderview->opened) {
2108 FolderItem *olditem;
2110 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2111 folderview->opened);
2112 if (olditem) {
2113 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2114 olditem->path ? olditem->path:olditem->name);
2115 /* will be null if we just moved the previously opened folder */
2116 STATUSBAR_PUSH(folderview->mainwin, buf);
2117 main_window_cursor_wait(folderview->mainwin);
2118 g_free(buf);
2119 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2120 summary_show(folderview->summaryview, NULL);
2121 folder_item_close(olditem);
2122 main_window_cursor_normal(folderview->mainwin);
2123 STATUSBAR_POP(folderview->mainwin);
2127 if (folderview->opened &&
2128 !GTK_CTREE_ROW(folderview->opened)->children)
2129 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2131 folderview->opened = NULL;
2133 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2134 gint column, FolderView *folderview)
2136 static gboolean can_select = TRUE; /* exclusive lock */
2137 gboolean opened;
2138 FolderItem *item;
2139 gchar *buf;
2140 int res = 0;
2141 GtkCTreeNode *old_opened = folderview->opened;
2142 START_TIMING("");
2143 folderview->selected = row;
2145 debug_print("newly selected %p, opened %p\n", folderview->selected,
2146 folderview->opened);
2147 if (folderview->opened == row) {
2148 folderview->open_folder = FALSE;
2149 END_TIMING();
2150 return;
2153 if (!can_select || summary_is_locked(folderview->summaryview)) {
2154 if (folderview->opened) {
2155 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2156 gtk_ctree_select(ctree, folderview->opened);
2158 folderview->open_folder = FALSE;
2159 END_TIMING();
2160 return;
2163 if (!folderview->open_folder) {
2164 END_TIMING();
2165 return;
2167 item = gtk_ctree_node_get_row_data(ctree, row);
2168 if (!item) {
2169 END_TIMING();
2170 folderview->open_folder = FALSE;
2171 return;
2174 can_select = FALSE;
2176 /* Save cache for old folder */
2177 /* We don't want to lose all caches if sylpheed crashed */
2178 /* resets folderview->opened to NULL */
2179 folderview_close_opened(folderview);
2181 /* CLAWS: set compose button type: news folder items
2182 * always have a news folder as parent */
2183 if (item->folder)
2184 toolbar_set_compose_button
2185 (folderview->mainwin->toolbar,
2186 FOLDER_TYPE(item->folder) == F_NEWS ?
2187 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2189 if (item->path)
2190 debug_print("Folder %s is selected\n", item->path);
2192 if (!GTK_CTREE_ROW(row)->children)
2193 gtk_ctree_expand(ctree, row);
2195 /* ungrab the mouse event */
2196 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2197 gtk_grab_remove(GTK_WIDGET(ctree));
2198 if (gdk_pointer_is_grabbed())
2199 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2202 /* Open Folder */
2203 /* TODO: wwp: avoid displaying (null) in the status bar */
2204 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2205 item->path : "(null)");
2206 debug_print("%s\n", buf);
2207 STATUSBAR_PUSH(folderview->mainwin, buf);
2208 g_free(buf);
2210 main_window_cursor_wait(folderview->mainwin);
2212 res = folder_item_open(item);
2213 if (res == -1 && item->no_select == FALSE) {
2214 main_window_cursor_normal(folderview->mainwin);
2215 STATUSBAR_POP(folderview->mainwin);
2217 alertpanel_error(_("Folder could not be opened."));
2219 folderview->open_folder = FALSE;
2220 can_select = TRUE;
2221 END_TIMING();
2222 return;
2223 } else if (res == -2 && item->no_select == FALSE) {
2224 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2225 data->ctree = ctree;
2226 data->row = row;
2227 data->column = column;
2228 data->folderview = folderview;
2229 debug_print("postponing open of %s till end of scan\n",
2230 item->path ? item->path:item->name);
2231 folderview->open_folder = FALSE;
2232 can_select = TRUE;
2233 g_timeout_add(500, postpone_select, data);
2234 END_TIMING();
2235 return;
2238 main_window_cursor_normal(folderview->mainwin);
2240 /* Show messages */
2241 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2242 opened = summary_show(folderview->summaryview, item);
2244 folder_clean_cache_memory(item);
2246 if (!opened) {
2247 gtkut_ctree_set_focus_row(ctree, old_opened);
2248 gtk_ctree_select(ctree, old_opened);
2249 folderview->opened = old_opened;
2250 } else {
2251 folderview->opened = row;
2252 if (gtk_ctree_node_is_visible(ctree, row)
2253 != GTK_VISIBILITY_FULL)
2254 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2257 STATUSBAR_POP(folderview->mainwin);
2259 folderview->open_folder = FALSE;
2260 can_select = TRUE;
2261 END_TIMING();
2264 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2265 FolderView *folderview)
2267 FolderItem *item;
2269 item = gtk_ctree_node_get_row_data(ctree, node);
2270 g_return_if_fail(item != NULL);
2271 item->collapsed = FALSE;
2272 folderview_update_node(folderview, node);
2275 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2276 FolderView *folderview)
2278 FolderItem *item;
2280 item = gtk_ctree_node_get_row_data(ctree, node);
2281 g_return_if_fail(item != NULL);
2282 item->collapsed = TRUE;
2283 folderview_update_node(folderview, node);
2286 static void folderview_popup_close(GtkMenuShell *menu_shell,
2287 FolderView *folderview)
2289 if (!folderview->opened) return;
2291 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2294 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2295 FolderView *folderview)
2297 FolderColumnType type = folderview->col_state[column].type;
2299 prefs_common.folder_col_size[type] = width;
2302 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2304 GNode *srcnode;
2306 folderview_create_folder_node(folderview, item);
2308 if (!item || !item->folder || !item->folder->node)
2309 return;
2311 srcnode = item->folder->node;
2312 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2313 srcnode = srcnode->children;
2314 while (srcnode != NULL) {
2315 if (srcnode && srcnode->data) {
2316 FolderItem *next_item = (FolderItem*) srcnode->data;
2317 folderview_create_folder_node_recursive(folderview, next_item);
2319 srcnode = srcnode->next;
2323 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2325 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2326 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2327 GtkCTreeNode *node, *parent_node;
2328 gint *col_pos = folderview->col_pos;
2329 FolderItemUpdateData hookdata;
2331 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2332 if (parent_node == NULL)
2333 return;
2335 gtk_clist_freeze(GTK_CLIST(ctree));
2337 text[col_pos[F_COL_FOLDER]] = item->name;
2338 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2339 FOLDER_SPACING,
2340 folderxpm, folderxpmmask,
2341 folderopenxpm, folderopenxpmmask,
2342 FALSE, FALSE);
2343 gtk_ctree_expand(ctree, parent_node);
2344 gtk_ctree_node_set_row_data(ctree, node, item);
2345 if (normal_style)
2346 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2347 folderview_sort_folders(folderview, parent_node, item->folder);
2349 hookdata.item = item;
2350 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2351 hookdata.msg = NULL;
2352 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2354 gtk_clist_thaw(GTK_CLIST(ctree));
2357 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2358 GtkWidget *widget)
2360 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2361 FolderItem *item;
2362 GSList *mlist = NULL;
2363 GSList *cur = NULL;
2364 FolderItem *special_trash = NULL;
2365 PrefsAccount *ac;
2367 if (!folderview->selected) return;
2368 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2369 g_return_if_fail(item != NULL);
2370 g_return_if_fail(item->folder != NULL);
2372 if (NULL != (ac = account_find_from_item(item)))
2373 special_trash = account_get_special_folder(ac, F_TRASH);
2375 if (item != item->folder->trash && item != special_trash
2376 && !folder_has_parent_of_type(item, F_TRASH)) return;
2378 if (prefs_common.ask_on_clean) {
2379 if (alertpanel(_("Empty trash"),
2380 _("Delete all messages in trash?"),
2381 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2382 return;
2385 mlist = folder_item_get_msg_list(item);
2387 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2388 MsgInfo * msginfo = (MsgInfo *) cur->data;
2389 if (MSG_IS_LOCKED(msginfo->flags))
2390 continue;
2391 /* is it partially received? (partial_recv isn't cached) */
2392 if (msginfo->total_size != 0 &&
2393 msginfo->size != (off_t)msginfo->total_size)
2394 partial_mark_for_delete(msginfo);
2396 procmsg_msg_list_free(mlist);
2398 folder_item_remove_all_msg(item);
2401 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2402 GtkWidget *widget)
2404 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2405 FolderItem *item;
2406 FolderItem *special_queue = NULL;
2407 PrefsAccount *ac;
2408 gchar *errstr = NULL;
2410 if (!folderview->selected) return;
2411 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2412 g_return_if_fail(item != NULL);
2413 g_return_if_fail(item->folder != NULL);
2415 if (NULL != (ac = account_find_from_item(item)))
2416 special_queue = account_get_special_folder(ac, F_QUEUE);
2418 if (item != item->folder->queue && item != special_queue
2419 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2421 if (procmsg_queue_is_empty(item))
2422 return;
2424 if (prefs_common.work_offline)
2425 if (alertpanel(_("Offline warning"),
2426 _("You're working offline. Override?"),
2427 GTK_STOCK_NO, GTK_STOCK_YES,
2428 NULL) != G_ALERTALTERNATE)
2429 return;
2431 /* ask for confirmation before sending queued messages only
2432 in online mode and if there is at least one message queued
2433 in any of the folder queue
2435 if (prefs_common.confirm_send_queued_messages) {
2436 if (!prefs_common.work_offline) {
2437 if (alertpanel(_("Send queued messages"),
2438 _("Send all queued messages?"),
2439 GTK_STOCK_CANCEL, _("_Send"),
2440 NULL) != G_ALERTALTERNATE)
2441 return;
2445 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2446 if (!errstr)
2447 alertpanel_error_log(_("Some errors occurred while "
2448 "sending queued messages."));
2449 else {
2450 alertpanel_error_log(_("Some errors occurred "
2451 "while sending queued messages:\n%s"), errstr);
2452 g_free(errstr);
2457 static void folderview_search_cb(FolderView *folderview, guint action,
2458 GtkWidget *widget)
2460 summary_search(folderview->summaryview);
2463 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2464 GtkWidget *widget)
2466 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2467 FolderItem *item;
2469 if (!folderview->selected) return;
2471 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2472 g_return_if_fail(item != NULL);
2473 g_return_if_fail(item->folder != NULL);
2475 folder_item_apply_processing(item);
2478 static void folderview_property_cb(FolderView *folderview, guint action,
2479 GtkWidget *widget)
2481 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2482 FolderItem *item;
2484 if (!folderview->selected) return;
2486 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2487 g_return_if_fail(item != NULL);
2488 g_return_if_fail(item->folder != NULL);
2490 prefs_folder_item_open(item);
2493 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2495 GSList *list = NULL;
2496 GSList *done = NULL;
2497 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2499 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2500 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2501 && list->data != node) {
2502 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2503 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2506 for (list = done; list != NULL; list = g_slist_next(list)) {
2507 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2508 list->data);
2510 g_slist_free(done);
2513 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2514 FolderItem *to_folder, gboolean copy)
2516 FolderItem *from_parent = NULL;
2517 FolderItem *new_folder = NULL;
2518 GtkCTreeNode *src_node = NULL;
2519 gchar *buf;
2520 gint status;
2522 g_return_if_fail(folderview != NULL);
2523 g_return_if_fail(from_folder != NULL);
2524 g_return_if_fail(to_folder != NULL);
2526 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2527 from_parent = folder_item_parent(from_folder);
2529 if (prefs_common.warn_dnd) {
2530 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2531 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2532 from_folder->name, to_folder->name);
2533 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2534 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2535 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2536 g_free(buf);
2538 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2539 return;
2540 else if (status & G_ALERTDISABLE)
2541 prefs_common.warn_dnd = FALSE;
2544 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2545 from_folder->name, to_folder->name);
2546 STATUSBAR_PUSH(folderview->mainwin, buf);
2547 g_free(buf);
2548 summary_clear_all(folderview->summaryview);
2549 folderview->opened = NULL;
2550 folderview->selected = NULL;
2551 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2552 inc_lock();
2553 main_window_cursor_wait(folderview->mainwin);
2555 statusbar_verbosity_set(FALSE);
2556 folder_item_update_freeze();
2557 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2558 statusbar_verbosity_set(FALSE);
2559 main_window_cursor_normal(folderview->mainwin);
2560 STATUSBAR_POP(folderview->mainwin);
2561 folder_item_update_thaw();
2562 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2564 folderview_sort_folders(folderview,
2565 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2566 NULL, to_folder), new_folder->folder);
2567 folderview_select(folderview, new_folder);
2568 } else {
2569 statusbar_verbosity_set(FALSE);
2570 main_window_cursor_normal(folderview->mainwin);
2571 STATUSBAR_POP(folderview->mainwin);
2572 folder_item_update_thaw();
2573 switch (status) {
2574 case F_MOVE_FAILED_DEST_IS_PARENT:
2575 alertpanel_error(_("Source and destination are the same."));
2576 break;
2577 case F_MOVE_FAILED_DEST_IS_CHILD:
2578 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2579 _("Can't move a folder to one of its children."));
2580 break;
2581 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2582 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2583 break;
2584 default:
2585 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2586 break;
2589 inc_unlock();
2590 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2593 static gint folderview_clist_compare(GtkCList *clist,
2594 gconstpointer ptr1, gconstpointer ptr2)
2596 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2597 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2599 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2601 return item1->order - item2->order;
2604 // if only one folder has an order it comes first
2605 if (item1->order > 0)
2607 return -1;
2609 if (item2->order > 0)
2611 return 1;
2614 if (!item1->name)
2615 return (item2->name != NULL);
2616 if (!item2->name)
2617 return -1;
2619 return g_utf8_collate(item1->name, item2->name);
2622 static void folderview_processing_cb(FolderView *folderview, guint action,
2623 GtkWidget *widget)
2625 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2626 FolderItem *item;
2627 gchar *id, *title;
2629 if (!folderview->selected) return;
2631 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2632 g_return_if_fail(item != NULL);
2633 g_return_if_fail(item->folder != NULL);
2635 id = folder_item_get_identifier(item);
2636 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2637 g_free (id);
2639 prefs_filtering_open(&item->prefs->processing, title,
2640 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2641 g_free (title);
2644 void folderview_set_target_folder_color(gint color_op)
2646 gint firstone = 1;
2647 GList *list;
2648 FolderView *folderview;
2650 for (list = folderview_list; list != NULL; list = list->next) {
2651 folderview = (FolderView *)list->data;
2652 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2653 if (firstone) {
2654 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2655 folderview->color_op;
2656 firstone = 0;
2661 static gchar *last_font = NULL;
2662 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2664 /* force reinit */
2665 g_free(last_font);
2666 last_font = NULL;
2670 void folderview_reflect_prefs(void)
2672 gboolean update_font = TRUE;
2673 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2674 FolderItem *item = folderview_get_selected_item(folderview);
2675 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2676 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2677 gint height = pos->value;
2679 if (last_font && !strcmp(last_font, NORMAL_FONT))
2680 update_font = FALSE;
2682 g_free(last_font);
2683 last_font = g_strdup(NORMAL_FONT);
2685 if (update_font) {
2686 normal_style = normal_color_style = bold_style =
2687 bold_color_style = bold_tgtfold_style = NULL;
2689 folderview_init(folderview);
2691 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2692 folderview_column_set_titles(folderview);
2693 folderview_set_all();
2695 g_signal_handlers_block_by_func
2696 (G_OBJECT(folderview->ctree),
2697 G_CALLBACK(folderview_selected), folderview);
2699 if (item) {
2700 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2701 GTK_CTREE(folderview->ctree), NULL, item);
2703 folderview_select(folderview, item);
2704 folderview->open_folder = FALSE;
2705 folderview->selected = node;
2708 g_signal_handlers_unblock_by_func
2709 (G_OBJECT(folderview->ctree),
2710 G_CALLBACK(folderview_selected), folderview);
2712 pos = gtk_scrolled_window_get_vadjustment(
2713 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2714 gtk_adjustment_set_value(pos, height);
2715 gtk_adjustment_changed(pos);
2716 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2719 static void drag_state_stop(FolderView *folderview)
2721 if (folderview->drag_timer)
2722 g_source_remove(folderview->drag_timer);
2723 folderview->drag_timer = 0;
2724 folderview->drag_node = NULL;
2727 static gint folderview_defer_expand(FolderView *folderview)
2729 if (folderview->drag_node) {
2730 folderview_recollapse_nodes(folderview, folderview->drag_node);
2731 if (folderview->drag_item->collapsed) {
2732 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2733 folderview->nodes_to_recollapse = g_slist_append
2734 (folderview->nodes_to_recollapse, folderview->drag_node);
2737 folderview->drag_item = NULL;
2738 folderview->drag_timer = 0;
2739 return FALSE;
2742 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2744 /* the idea is that we call drag_state_start() whenever we want expansion to
2745 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2746 * we need to call drag_state_stop() */
2747 drag_state_stop(folderview);
2748 /* request expansion */
2749 if (0 != (folderview->drag_timer = g_timeout_add
2750 (prefs_common.hover_timeout,
2751 (GtkFunction)folderview_defer_expand,
2752 folderview))) {
2753 folderview->drag_node = node;
2754 folderview->drag_item = item;
2758 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2759 FolderView *folderview)
2761 GdkDragContext *context;
2763 g_return_if_fail(folderview != NULL);
2764 if (folderview->selected == NULL) return;
2765 if (folderview->nodes_to_recollapse)
2766 g_slist_free(folderview->nodes_to_recollapse);
2767 folderview->nodes_to_recollapse = NULL;
2768 context = gtk_drag_begin(widget, folderview->target_list,
2769 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2770 gtk_drag_set_icon_default(context);
2773 static void folderview_drag_data_get(GtkWidget *widget,
2774 GdkDragContext *drag_context,
2775 GtkSelectionData *selection_data,
2776 guint info,
2777 guint time,
2778 FolderView *folderview)
2780 FolderItem *item;
2781 GList *cur;
2782 gchar *source = NULL;
2783 if (info == TARGET_DUMMY) {
2784 for (cur = GTK_CLIST(folderview->ctree)->selection;
2785 cur != NULL; cur = cur->next) {
2786 item = gtk_ctree_node_get_row_data
2787 (GTK_CTREE(folderview->ctree),
2788 GTK_CTREE_NODE(cur->data));
2789 if (item) {
2790 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2791 gtk_selection_data_set(selection_data,
2792 selection_data->target, 8,
2793 source, strlen(source));
2794 break;
2795 } else
2796 return;
2798 } else {
2799 g_warning("unknown info %d\n", info);
2803 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2805 FolderUpdateData *hookdata;
2806 FolderView *folderview;
2807 GtkWidget *ctree;
2809 hookdata = source;
2810 folderview = (FolderView *) userdata;
2811 g_return_val_if_fail(hookdata != NULL, FALSE);
2812 g_return_val_if_fail(folderview != NULL, FALSE);
2814 ctree = folderview->ctree;
2815 g_return_val_if_fail(ctree != NULL, FALSE);
2817 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2818 folderview_create_folder_node(folderview, hookdata->item);
2819 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2820 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2821 NULL, folder_item_parent(hookdata->item));
2822 folderview_sort_folders(folderview, node, hookdata->folder);
2823 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2824 GtkCTreeNode *node;
2826 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2827 if (node != NULL) {
2828 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2829 if (folderview->selected == node)
2830 folderview->selected = NULL;
2831 if (folderview->opened == node)
2832 folderview->opened = NULL;
2834 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2835 folderview_set(folderview);
2837 return FALSE;
2840 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2841 GdkDragContext *context,
2842 gint x,
2843 gint y,
2844 guint time,
2845 FolderView *folderview)
2847 gint row, column;
2848 FolderItem *item = NULL, *src_item = NULL;
2849 GtkCTreeNode *node = NULL;
2850 gboolean acceptable = FALSE;
2851 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2852 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2853 int height = (int)pos->page_size;
2854 int total_height = (int)pos->upper;
2855 int vpos = (int) pos->value;
2857 if (gtk_clist_get_selection_info
2858 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
2859 GtkWidget *srcwidget;
2861 if (y > height - 24 && height + vpos < total_height) {
2862 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2863 gtk_adjustment_changed(pos);
2865 if (y < 48 && y > 0) {
2866 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2867 gtk_adjustment_changed(pos);
2870 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2871 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2872 src_item = folderview->summaryview->folder_item;
2874 srcwidget = gtk_drag_get_source_widget(context);
2875 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2876 /* comes from summaryview */
2877 /* we are copying messages, so only accept folder items that are not
2878 the source item, are no root items and can copy messages */
2879 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2880 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2881 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2882 acceptable = TRUE;
2883 } else if (srcwidget == folderview->ctree) {
2884 /* comes from folderview */
2885 /* we are moving folder items, only accept folders that are not
2886 the source items and can copy messages and create folder items */
2887 if (item && item->folder && src_item && src_item != item &&
2888 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2889 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2890 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2891 || item->folder == src_item->folder))
2892 acceptable = TRUE;
2893 } else {
2894 /* comes from another app */
2895 /* we are adding messages, so only accept folder items that are
2896 no root items and can copy messages */
2897 if (item && item->folder && folder_item_parent(item) != NULL
2898 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2899 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2900 acceptable = TRUE;
2904 if (acceptable || (src_item && src_item == item))
2905 drag_state_start(folderview, node, item);
2907 if (acceptable) {
2908 g_signal_handlers_block_by_func
2909 (G_OBJECT(widget),
2910 G_CALLBACK(folderview_selected), folderview);
2911 gtk_ctree_select(GTK_CTREE(widget), node);
2912 g_signal_handlers_unblock_by_func
2913 (G_OBJECT(widget),
2914 G_CALLBACK(folderview_selected), folderview);
2915 gdk_drag_status(context,
2916 (context->actions == GDK_ACTION_COPY ?
2917 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2918 } else {
2919 if (folderview->opened)
2920 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2921 gdk_drag_status(context, 0, time);
2924 return acceptable;
2927 static void folderview_drag_leave_cb(GtkWidget *widget,
2928 GdkDragContext *context,
2929 guint time,
2930 FolderView *folderview)
2932 drag_state_stop(folderview);
2933 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2936 static void free_info (gpointer stuff, gpointer data)
2938 g_free(stuff);
2941 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2942 guint time, FolderItem *item)
2944 GList *list, *tmp;
2945 GSList *msglist = NULL;
2946 list = uri_list_extract_filenames(data);
2947 if (!(item && item->folder && folder_item_parent(item) != NULL
2948 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2950 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2951 debug_print("item doesn't fit\n");
2952 return;
2954 if (!list) {
2955 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2956 debug_print("list is empty\n");
2957 return;
2959 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2960 MsgFileInfo *info = NULL;
2962 if (file_is_email((gchar *)tmp->data)) {
2963 info = g_new0(MsgFileInfo, 1);
2964 info->msginfo = NULL;
2965 info->file = (gchar *)tmp->data;
2966 msglist = g_slist_prepend(msglist, info);
2967 debug_print("file is a mail\n");
2968 } else {
2969 debug_print("file isn't a mail\n");
2972 if (msglist) {
2973 msglist = g_slist_reverse(msglist);
2974 folder_item_add_msgs(item, msglist, FALSE);
2975 g_slist_foreach(msglist, free_info, NULL);
2976 g_slist_free(msglist);
2977 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2978 } else {
2979 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2981 list_free_strings(list);
2982 g_list_free(list);
2985 static void folderview_drag_received_cb(GtkWidget *widget,
2986 GdkDragContext *drag_context,
2987 gint x,
2988 gint y,
2989 GtkSelectionData *data,
2990 guint info,
2991 guint time,
2992 FolderView *folderview)
2994 gint row, column;
2995 FolderItem *item = NULL, *src_item;
2996 GtkCTreeNode *node;
2998 if (info == TARGET_DUMMY) {
2999 drag_state_stop(folderview);
3000 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3001 /* comes from summaryview */
3002 if (gtk_clist_get_selection_info
3003 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3004 return;
3006 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3007 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3008 src_item = folderview->summaryview->folder_item;
3010 if (item->no_select) {
3011 alertpanel_error(_("The destination folder can only be used to "
3012 "store subfolders."));
3013 return;
3015 /* re-check (due to acceptable possibly set for folder moves */
3016 if (!(item && item->folder && item->path && !item->no_select &&
3017 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3018 return;
3020 if (item && src_item) {
3021 switch (drag_context->action) {
3022 case GDK_ACTION_COPY:
3023 summary_copy_selected_to(folderview->summaryview, item);
3024 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3025 break;
3026 case GDK_ACTION_MOVE:
3027 case GDK_ACTION_DEFAULT:
3028 default:
3029 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3030 summary_copy_selected_to(folderview->summaryview, item);
3031 else
3032 summary_move_selected_to(folderview->summaryview, item);
3033 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3035 } else
3036 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3037 } else {
3038 /* comes from folderview */
3039 char *source;
3040 gboolean folder_is_normal = TRUE;
3041 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3043 source = data->data + 17;
3044 if (gtk_clist_get_selection_info
3045 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
3046 || *source == 0) {
3047 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3048 return;
3050 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3051 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3052 src_item = folder_find_item_from_identifier(source);
3054 folder_is_normal =
3055 src_item != NULL &&
3056 src_item->stype == F_NORMAL &&
3057 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3058 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3059 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3060 !folder_has_parent_of_type(src_item, F_TRASH);
3061 if (!item || !src_item || !folder_is_normal) {
3062 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3063 return;
3066 folderview_move_folder(folderview, src_item, item, copy);
3067 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3069 folderview->nodes_to_recollapse = NULL;
3070 } else if (info == TARGET_MAIL_URI_LIST) {
3071 if (gtk_clist_get_selection_info
3072 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3073 return;
3075 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3076 if (!node) {
3077 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3078 debug_print("no node\n");
3079 return;
3081 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3082 if (!item) {
3083 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3084 debug_print("no item\n");
3085 return;
3087 folderview_finish_dnd(data->data, drag_context, time, item);
3091 static void folderview_drag_end_cb(GtkWidget *widget,
3092 GdkDragContext *drag_context,
3093 FolderView *folderview)
3095 drag_state_stop(folderview);
3096 g_slist_free(folderview->nodes_to_recollapse);
3097 folderview->nodes_to_recollapse = NULL;
3100 void folderview_register_popup(FolderViewPopup *fpopup)
3102 GList *folderviews;
3104 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3105 FolderView *folderview = folderviews->data;
3106 GtkItemFactory *factory;
3108 factory = create_ifactory(folderview, fpopup);
3109 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3111 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3114 void folderview_unregister_popup(FolderViewPopup *fpopup)
3116 GList *folderviews;
3118 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3119 FolderView *folderview = folderviews->data;
3121 g_hash_table_remove(folderview->popups, fpopup->klass);
3123 g_hash_table_remove(folderview_popups, fpopup->klass);