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