Statistics
| Revision:

root / src / folderview.c @ 39

History | View | Annotate | Download (65.4 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 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 2 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, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19
20
#include "defs.h"
21
22
#include <glib.h>
23
#include <gdk/gdkkeysyms.h>
24
#include <gtk/gtkwidget.h>
25
#include <gtk/gtkscrolledwindow.h>
26
#include <gtk/gtkctree.h>
27
#include <gtk/gtkcontainer.h>
28
#include <gtk/gtkclist.h>
29
#include <gtk/gtkstyle.h>
30
#include <gtk/gtksignal.h>
31
#include <gtk/gtkmain.h>
32
#include <gtk/gtkstatusbar.h>
33
#include <gtk/gtkmenu.h>
34
#include <gtk/gtkmenuitem.h>
35
#include <gtk/gtkitemfactory.h>
36
#include <stdio.h>
37
#include <string.h>
38
#include <stdlib.h>
39
40
#include "intl.h"
41
#include "main.h"
42
#include "mainwindow.h"
43
#include "folderview.h"
44
#include "summaryview.h"
45
#include "summary_search.h"
46
#include "inputdialog.h"
47
#include "grouplistdialog.h"
48
#include "manage_window.h"
49
#include "alertpanel.h"
50
#include "menu.h"
51
#include "stock_pixmap.h"
52
#include "statusbar.h"
53
#include "procmsg.h"
54
#include "utils.h"
55
#include "gtkutils.h"
56
#include "prefs_common.h"
57
#include "prefs_account.h"
58
#include "prefs_filter.h"
59
#include "prefs_folder_item.h"
60
#include "account.h"
61
#include "folder.h"
62
#include "inc.h"
63
64
typedef enum
65
{
66
        COL_FOLDER        = 0,
67
        COL_NEW                = 1,
68
        COL_UNREAD        = 2,
69
        COL_TOTAL        = 3
70
} FolderColumnPos;
71
72
#define N_FOLDER_COLS                4
73
#define COL_FOLDER_WIDTH        150
74
#define COL_NUM_WIDTH                32
75
76
#define STATUSBAR_PUSH(mainwin, str) \
77
{ \
78
        gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
79
                           mainwin->folderview_cid, str); \
80
        gtkut_widget_wait_for_draw(mainwin->statusbar); \
81
}
82
83
#define STATUSBAR_POP(mainwin) \
84
{ \
85
        gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
86
                          mainwin->folderview_cid); \
87
}
88
89
static GList *folderview_list = NULL;
90
91
static GtkStyle *bold_style;
92
static GtkStyle *bold_color_style;
93
94
static GdkPixmap *inboxxpm;
95
static GdkBitmap *inboxxpmmask;
96
static GdkPixmap *outboxxpm;
97
static GdkBitmap *outboxxpmmask;
98
static GdkPixmap *folderxpm;
99
static GdkBitmap *folderxpmmask;
100
static GdkPixmap *folderopenxpm;
101
static GdkBitmap *folderopenxpmmask;
102
static GdkPixmap *foldernoselectxpm;
103
static GdkBitmap *foldernoselectxpmmask;
104
static GdkPixmap *trashxpm;
105
static GdkBitmap *trashxpmmask;
106
107
static void folderview_select_node         (FolderView        *folderview,
108
                                          GtkCTreeNode        *node);
109
static void folderview_set_folders         (FolderView        *folderview);
110
static void folderview_sort_folders         (FolderView        *folderview,
111
                                          GtkCTreeNode        *root,
112
                                          Folder        *folder);
113
static void folderview_append_folder         (FolderView        *folderview,
114
                                          Folder        *folder);
115
static void folderview_update_node         (FolderView        *folderview,
116
                                          GtkCTreeNode        *node);
117
118
static gint folderview_clist_compare        (GtkCList        *clist,
119
                                         gconstpointer         ptr1,
120
                                         gconstpointer         ptr2);
121
122
/* callback functions */
123
static gboolean folderview_button_pressed        (GtkWidget        *ctree,
124
                                                 GdkEventButton        *event,
125
                                                 FolderView        *folderview);
126
static gboolean folderview_button_released        (GtkWidget        *ctree,
127
                                                 GdkEventButton        *event,
128
                                                 FolderView        *folderview);
129
130
static gboolean folderview_key_pressed        (GtkWidget        *widget,
131
                                         GdkEventKey        *event,
132
                                         FolderView        *folderview);
133
static void folderview_selected                (GtkCTree        *ctree,
134
                                         GtkCTreeNode        *row,
135
                                         gint                 column,
136
                                         FolderView        *folderview);
137
static void folderview_tree_expanded        (GtkCTree        *ctree,
138
                                         GtkCTreeNode        *node,
139
                                         FolderView        *folderview);
140
static void folderview_tree_collapsed        (GtkCTree        *ctree,
141
                                         GtkCTreeNode        *node,
142
                                         FolderView        *folderview);
143
static void folderview_popup_close        (GtkMenuShell        *menu_shell,
144
                                         FolderView        *folderview);
145
static void folderview_col_resized        (GtkCList        *clist,
146
                                         gint                 column,
147
                                         gint                 width,
148
                                         FolderView        *folderview);
149
150
static void folderview_download_cb        (FolderView        *folderview,
151
                                         guint                 action,
152
                                         GtkWidget        *widget);
153
154
static void folderview_update_tree_cb        (FolderView        *folderview,
155
                                         guint                 action,
156
                                         GtkWidget        *widget);
157
158
static void folderview_new_folder_cb        (FolderView        *folderview,
159
                                         guint                 action,
160
                                         GtkWidget        *widget);
161
static void folderview_rename_folder_cb        (FolderView        *folderview,
162
                                         guint                 action,
163
                                         GtkWidget        *widget);
164
static void folderview_delete_folder_cb        (FolderView        *folderview,
165
                                         guint                 action,
166
                                         GtkWidget        *widget);
167
static void folderview_empty_trash_cb        (FolderView        *folderview,
168
                                         guint                 action,
169
                                         GtkWidget        *widget);
170
static void folderview_remove_mailbox_cb(FolderView        *folderview,
171
                                         guint                 action,
172
                                         GtkWidget        *widget);
173
174
static void folderview_rm_imap_server_cb (FolderView        *folderview,
175
                                          guint                 action,
176
                                          GtkWidget        *widget);
177
178
static void folderview_new_news_group_cb(FolderView        *folderview,
179
                                         guint                 action,
180
                                         GtkWidget        *widget);
181
static void folderview_rm_news_group_cb        (FolderView        *folderview,
182
                                         guint                 action,
183
                                         GtkWidget        *widget);
184
static void folderview_rm_news_server_cb(FolderView        *folderview,
185
                                         guint                 action,
186
                                         GtkWidget        *widget);
187
188
static void folderview_search_cb        (FolderView        *folderview,
189
                                         guint                 action,
190
                                         GtkWidget        *widget);
191
192
static void folderview_property_cb        (FolderView        *folderview,
193
                                         guint                 action,
194
                                         GtkWidget        *widget);
195
196
static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
197
                                          GdkDragContext *context,
198
                                          gint            x,
199
                                          gint            y,
200
                                          guint           time,
201
                                          FolderView     *folderview);
202
static void folderview_drag_leave_cb     (GtkWidget        *widget,
203
                                          GdkDragContext   *context,
204
                                          guint             time,
205
                                          FolderView       *folderview);
206
static void folderview_drag_received_cb  (GtkWidget        *widget,
207
                                          GdkDragContext   *context,
208
                                          gint              x,
209
                                          gint              y,
210
                                          GtkSelectionData *data,
211
                                          guint             info,
212
                                          guint             time,
213
                                          FolderView       *folderview);
214
215
static GtkItemFactoryEntry folderview_mail_popup_entries[] =
216
{
217
        {N_("/Create _new folder..."),        NULL, folderview_new_folder_cb,    0, NULL},
218
        {N_("/_Rename folder..."),        NULL, folderview_rename_folder_cb, 0, NULL},
219
        {N_("/_Delete folder"),                NULL, folderview_delete_folder_cb, 0, NULL},
220
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
221
        {N_("/Empty _trash"),                NULL, folderview_empty_trash_cb, 0, NULL},
222
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
223
        {N_("/_Check for new messages"),
224
                                        NULL, folderview_update_tree_cb, 0, NULL},
225
        {N_("/R_ebuild folder tree"),        NULL, folderview_update_tree_cb, 1, NULL},
226
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
227
        {N_("/_Search messages..."),        NULL, folderview_search_cb, 0, NULL},
228
        {N_("/_Properties..."),                NULL, folderview_property_cb, 0, NULL}
229
};
230
231
static GtkItemFactoryEntry folderview_imap_popup_entries[] =
232
{
233
        {N_("/Create _new folder..."),        NULL, folderview_new_folder_cb,    0, NULL},
234
        {N_("/_Rename folder..."),        NULL, folderview_rename_folder_cb, 0, NULL},
235
        {N_("/_Delete folder"),                NULL, folderview_delete_folder_cb, 0, NULL},
236
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
237
        {N_("/Empty _trash"),                NULL, folderview_empty_trash_cb, 0, NULL},
238
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
239
        {N_("/Down_load"),                NULL, folderview_download_cb, 0, NULL},
240
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
241
        {N_("/_Check for new messages"),
242
                                        NULL, folderview_update_tree_cb, 0, NULL},
243
        {N_("/R_ebuild folder tree"),        NULL, folderview_update_tree_cb, 1, NULL},
244
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
245
        {N_("/_Search messages..."),        NULL, folderview_search_cb, 0, NULL},
246
        {N_("/_Properties..."),                NULL, folderview_property_cb, 0, NULL}
247
};
248
249
static GtkItemFactoryEntry folderview_news_popup_entries[] =
250
{
251
        {N_("/Su_bscribe to newsgroup..."),
252
                                        NULL, folderview_new_news_group_cb, 0, NULL},
253
        {N_("/_Remove newsgroup"),        NULL, folderview_rm_news_group_cb, 0, NULL},
254
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
255
        {N_("/Down_load"),                NULL, folderview_download_cb, 0, NULL},
256
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
257
        {N_("/_Check for new messages"),
258
                                        NULL, folderview_update_tree_cb, 0, NULL},
259
        {N_("/---"),                        NULL, NULL, 0, "<Separator>"},
260
        {N_("/_Search messages..."),        NULL, folderview_search_cb, 0, NULL},
261
        {N_("/_Properties..."),                NULL, folderview_property_cb, 0, NULL}
262
};
263
264
265
FolderView *folderview_create(void)
266
{
267
        FolderView *folderview;
268
        GtkWidget *scrolledwin;
269
        GtkWidget *ctree;
270
        gchar *titles[N_FOLDER_COLS];
271
        GtkWidget *mail_popup;
272
        GtkWidget *news_popup;
273
        GtkWidget *imap_popup;
274
        GtkItemFactory *mail_factory;
275
        GtkItemFactory *news_factory;
276
        GtkItemFactory *imap_factory;
277
        gint n_entries;
278
        gint i;
279
280
        debug_print(_("Creating folder view...\n"));
281
        folderview = g_new0(FolderView, 1);
282
283
        titles[COL_FOLDER] = _("Folder");
284
        titles[COL_NEW]    = _("New");
285
        titles[COL_UNREAD] = _("Unread");
286
        titles[COL_TOTAL]  = _("#");
287
288
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
289
        gtk_scrolled_window_set_policy
290
                (GTK_SCROLLED_WINDOW(scrolledwin),
291
                 GTK_POLICY_AUTOMATIC,
292
                 prefs_common.folderview_vscrollbar_policy);
293
        gtk_widget_set_size_request(scrolledwin,
294
                                    prefs_common.folderview_width,
295
                                    prefs_common.folderview_height);
296
297
        ctree = gtk_ctree_new_with_titles(N_FOLDER_COLS, COL_FOLDER, titles);
298
        gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
299
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
300
        gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_NEW,
301
                                           GTK_JUSTIFY_RIGHT);
302
        gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_UNREAD,
303
                                           GTK_JUSTIFY_RIGHT);
304
        gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_TOTAL,
305
                                           GTK_JUSTIFY_RIGHT);
306
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_FOLDER,
307
                                   prefs_common.folder_col_folder);
308
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_NEW,
309
                                   prefs_common.folder_col_new);
310
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_UNREAD,        
311
                                   prefs_common.folder_col_unread);
312
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_TOTAL,
313
                                   prefs_common.folder_col_total);
314
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
315
        gtk_ctree_set_expander_style(GTK_CTREE(ctree),
316
                                     GTK_CTREE_EXPANDER_SQUARE);
317
        gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
318
        gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
319
320
        /* don't let title buttons take key focus */
321
        for (i = 0; i < N_FOLDER_COLS; i++)
322
                GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
323
                                       GTK_CAN_FOCUS);
324
325
        /* popup menu */
326
        n_entries = sizeof(folderview_mail_popup_entries) /
327
                sizeof(folderview_mail_popup_entries[0]);
328
        mail_popup = menu_create_items(folderview_mail_popup_entries,
329
                                       n_entries,
330
                                       "<MailFolder>", &mail_factory,
331
                                       folderview);
332
        n_entries = sizeof(folderview_imap_popup_entries) /
333
                sizeof(folderview_imap_popup_entries[0]);
334
        imap_popup = menu_create_items(folderview_imap_popup_entries,
335
                                       n_entries,
336
                                       "<IMAPFolder>", &imap_factory,
337
                                       folderview);
338
        n_entries = sizeof(folderview_news_popup_entries) /
339
                sizeof(folderview_news_popup_entries[0]);
340
        news_popup = menu_create_items(folderview_news_popup_entries,
341
                                       n_entries,
342
                                       "<NewsFolder>", &news_factory,
343
                                       folderview);
344
345
        g_signal_connect(G_OBJECT(ctree), "key_press_event",
346
                         G_CALLBACK(folderview_key_pressed),
347
                         folderview);
348
        g_signal_connect(G_OBJECT(ctree), "button_press_event",
349
                         G_CALLBACK(folderview_button_pressed),
350
                         folderview);
351
        g_signal_connect(G_OBJECT(ctree), "button_release_event",
352
                         G_CALLBACK(folderview_button_released),
353
                         folderview);
354
        g_signal_connect(G_OBJECT(ctree), "tree_select_row",
355
                         G_CALLBACK(folderview_selected), folderview);
356
357
        g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
358
                               G_CALLBACK(folderview_tree_expanded),
359
                               folderview);
360
        g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
361
                               G_CALLBACK(folderview_tree_collapsed),
362
                               folderview);
363
364
        g_signal_connect(G_OBJECT(ctree), "resize_column",
365
                         G_CALLBACK(folderview_col_resized), folderview);
366
367
        g_signal_connect(G_OBJECT(mail_popup), "selection_done",
368
                         G_CALLBACK(folderview_popup_close), folderview);
369
        g_signal_connect(G_OBJECT(imap_popup), "selection_done",
370
                         G_CALLBACK(folderview_popup_close), folderview);
371
        g_signal_connect(G_OBJECT(news_popup), "selection_done",
372
                         G_CALLBACK(folderview_popup_close), folderview);
373
374
        /* drop callback */
375
        gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL &
376
                          ~GTK_DEST_DEFAULT_HIGHLIGHT,
377
                          summary_drag_types, 1,
378
                          GDK_ACTION_MOVE | GDK_ACTION_COPY);
379
        g_signal_connect(G_OBJECT(ctree), "drag_motion",
380
                         G_CALLBACK(folderview_drag_motion_cb), folderview);
381
        g_signal_connect(G_OBJECT(ctree), "drag_leave",
382
                         G_CALLBACK(folderview_drag_leave_cb), folderview);
383
        g_signal_connect(G_OBJECT(ctree), "drag_data_received",
384
                         G_CALLBACK(folderview_drag_received_cb), folderview);
385
386
        folderview->scrolledwin  = scrolledwin;
387
        folderview->ctree        = ctree;
388
        folderview->mail_popup   = mail_popup;
389
        folderview->mail_factory = mail_factory;
390
        folderview->imap_popup   = imap_popup;
391
        folderview->imap_factory = imap_factory;
392
        folderview->news_popup   = news_popup;
393
        folderview->news_factory = news_factory;
394
395
        gtk_widget_show_all(scrolledwin);
396
397
        folderview_list = g_list_append(folderview_list, folderview);
398
399
        return folderview;
400
}
401
402
void folderview_init(FolderView *folderview)
403
{
404
        GtkWidget *ctree = folderview->ctree;
405
406
        gtk_widget_realize(ctree);
407
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX, &inboxxpm, &inboxxpmmask);
408
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX,
409
                         &outboxxpm, &outboxxpmmask);
410
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE,
411
                         &folderxpm, &folderxpmmask);
412
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN,
413
                         &folderopenxpm, &folderopenxpmmask);
414
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT,
415
                         &foldernoselectxpm, &foldernoselectxpmmask);
416
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH, &trashxpm, &trashxpmmask);
417
418
        if (!bold_style) {
419
                bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
420
                pango_font_description_set_weight
421
                        (bold_style->font_desc, PANGO_WEIGHT_BOLD);
422
                bold_color_style = gtk_style_copy(bold_style);
423
                bold_color_style->fg[GTK_STATE_NORMAL] = folderview->color_new;
424
        }
425
}
426
427
void folderview_set(FolderView *folderview)
428
{
429
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
430
        MainWindow *mainwin = folderview->mainwin;
431
432
        debug_print(_("Setting folder info...\n"));
433
        STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
434
435
        main_window_cursor_wait(mainwin);
436
437
        folderview->selected = NULL;
438
        folderview->opened = NULL;
439
440
        gtk_clist_freeze(GTK_CLIST(ctree));
441
        gtk_clist_clear(GTK_CLIST(ctree));
442
        gtk_clist_thaw(GTK_CLIST(ctree));
443
        gtk_clist_freeze(GTK_CLIST(ctree));
444
445
        folderview_set_folders(folderview);
446
447
        gtk_clist_thaw(GTK_CLIST(ctree));
448
        main_window_cursor_normal(mainwin);
449
        STATUSBAR_POP(mainwin);
450
}
451
452
void folderview_set_all(void)
453
{
454
        GList *list;
455
456
        for (list = folderview_list; list != NULL; list = list->next)
457
                folderview_set((FolderView *)list->data);
458
}
459
460
void folderview_select(FolderView *folderview, FolderItem *item)
461
{
462
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
463
        GtkCTreeNode *node;
464
465
        if (!item) return;
466
467
        node = gtk_ctree_find_by_row_data(ctree, NULL, item);
468
        if (node) folderview_select_node(folderview, node);
469
}
470
471
static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
472
{
473
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
474
475
        g_return_if_fail(node != NULL);
476
477
        folderview->open_folder = TRUE;
478
        gtkut_ctree_set_focus_row(ctree, node);
479
        gtk_ctree_select(ctree, node);
480
        if (folderview->summaryview->folder_item &&
481
            folderview->summaryview->folder_item->total > 0)
482
                gtk_widget_grab_focus(folderview->summaryview->ctree);
483
        else
484
                gtk_widget_grab_focus(folderview->ctree);
485
486
        gtkut_ctree_expand_parent_all(ctree, node);
487
}
488
489
void folderview_unselect(FolderView *folderview)
490
{
491
        if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
492
                gtk_ctree_collapse
493
                        (GTK_CTREE(folderview->ctree), folderview->opened);
494
495
        folderview->selected = folderview->opened = NULL;
496
}
497
498
static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
499
                                                 GtkCTreeNode *node)
500
{
501
        FolderItem *item;
502
503
        if (node)
504
                node = gtkut_ctree_node_next(ctree, node);
505
        else
506
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
507
508
        for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
509
                item = gtk_ctree_node_get_row_data(ctree, node);
510
                if (item && item->unread > 0 && item->stype != F_TRASH)
511
                        return node;
512
        }
513
514
        return NULL;
515
}
516
517
void folderview_select_next_unread(FolderView *folderview)
518
{
519
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
520
        GtkCTreeNode *node = NULL;
521
522
        if ((node = folderview_find_next_unread(ctree, folderview->opened))
523
            != NULL) {
524
                folderview_select_node(folderview, node);
525
                return;
526
        }
527
528
        if (!folderview->opened ||
529
            folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list))
530
                return;
531
        /* search again from the first node */
532
        if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
533
                folderview_select_node(folderview, node);
534
}
535
536
FolderItem *folderview_get_selected_item(FolderView *folderview)
537
{
538
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
539
540
        if (!folderview->selected) return NULL;
541
        return gtk_ctree_node_get_row_data(ctree, folderview->selected);
542
}
543
544
void folderview_update_msg_num(FolderView *folderview, GtkCTreeNode *row)
545
{
546
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
547
        static GtkCTreeNode *prev_row = NULL;
548
        FolderItem *item;
549
        gint new, unread, total;
550
        gchar *new_str, *unread_str, *total_str;
551
552
        if (!row) return;
553
554
        item = gtk_ctree_node_get_row_data(ctree, row);
555
        if (!item) return;
556
557
        gtk_ctree_node_get_text(ctree, row, COL_NEW, &new_str);
558
        gtk_ctree_node_get_text(ctree, row, COL_UNREAD, &unread_str);
559
        gtk_ctree_node_get_text(ctree, row, COL_TOTAL, &total_str);
560
        new = atoi(new_str);
561
        unread = atoi(unread_str);
562
        total = atoi(total_str);
563
564
        if (prev_row     == row    &&
565
            item->new    == new    &&
566
            item->unread == unread &&
567
            item->total  == total)
568
                return;
569
570
        prev_row = row;
571
572
        folderview_update_node(folderview, row);
573
}
574
575
void folderview_append_item(FolderItem *item)
576
{
577
        FolderItem *parent;
578
        GList *list;
579
580
        g_return_if_fail(item != NULL);
581
        g_return_if_fail(item->folder != NULL);
582
        g_return_if_fail(item->parent != NULL);
583
584
        parent = item->parent;
585
586
        for (list = folderview_list; list != NULL; list = list->next) {
587
                FolderView *folderview = (FolderView *)list->data;
588
                GtkCTree *ctree = GTK_CTREE(folderview->ctree);
589
                GtkCTreeNode *node, *child;
590
591
                node = gtk_ctree_find_by_row_data(ctree, NULL, parent);
592
                if (node) {
593
                        child = gtk_ctree_find_by_row_data(ctree, node, item);
594
                        if (!child) {
595
                                gchar *text[N_FOLDER_COLS] =
596
                                        {NULL, "0", "0", "0"};
597
598
                                gtk_clist_freeze(GTK_CLIST(ctree));
599
600
                                text[COL_FOLDER] = item->name;
601
                                child = gtk_ctree_insert_node
602
                                        (ctree, node, NULL, text,
603
                                         FOLDER_SPACING,
604
                                         folderxpm, folderxpmmask,
605
                                         folderopenxpm, folderopenxpmmask,
606
                                         FALSE, FALSE);
607
                                gtk_ctree_node_set_row_data(ctree, child, item);
608
                                gtk_ctree_expand(ctree, node);
609
                                folderview_update_node(folderview, child);
610
                                folderview_sort_folders(folderview, node,
611
                                                        item->folder);
612
613
                                gtk_clist_thaw(GTK_CLIST(ctree));
614
                        }
615
                }
616
        }
617
}
618
619
static void folderview_set_folders(FolderView *folderview)
620
{
621
        GList *list;
622
623
        list = folder_get_list();
624
625
        for (; list != NULL; list = list->next)
626
                folderview_append_folder(folderview, FOLDER(list->data));
627
}
628
629
static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
630
                                      gpointer data)
631
{
632
        GList *list;
633
        gchar *rootpath;
634
635
        if (FOLDER_IS_LOCAL(folder))
636
                rootpath = LOCAL_FOLDER(folder)->rootpath;
637
        else if (FOLDER_TYPE(folder) == F_IMAP && folder->account &&
638
                 folder->account->recv_server)
639
                rootpath = folder->account->recv_server;
640
        else if (FOLDER_TYPE(folder) == F_NEWS && folder->account &&
641
                 folder->account->nntp_server)
642
                rootpath = folder->account->nntp_server;
643
        else
644
                return;
645
646
        for (list = folderview_list; list != NULL; list = list->next) {
647
                FolderView *folderview = (FolderView *)list->data;
648
                MainWindow *mainwin = folderview->mainwin;
649
                gchar *str;
650
651
                if (item->path)
652
                        str = g_strdup_printf(_("Scanning folder %s%c%s ..."),
653
                                              rootpath, G_DIR_SEPARATOR,
654
                                              item->path);
655
                else
656
                        str = g_strdup_printf(_("Scanning folder %s ..."),
657
                                              rootpath);
658
659
                STATUSBAR_PUSH(mainwin, str);
660
                STATUSBAR_POP(mainwin);
661
                g_free(str);
662
        }
663
}
664
665
static GtkWidget *label_window_create(const gchar *str)
666
{
667
        GtkWidget *window;
668
        GtkWidget *label;
669
670
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
671
        gtk_widget_set_size_request(window, 380, 60);
672
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
673
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
674
        gtk_window_set_title(GTK_WINDOW(window), str);
675
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
676
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
677
        manage_window_set_transient(GTK_WINDOW(window));
678
679
        label = gtk_label_new(str);
680
        gtk_container_add(GTK_CONTAINER(window), label);
681
        gtk_widget_show(label);
682
683
        gtk_widget_show_now(window);
684
685
        return window;
686
}
687
688
static void folderview_rescan_tree(FolderView *folderview, Folder *folder)
689
{
690
        GtkWidget *window;
691
        AlertValue avalue;
692
693
        g_return_if_fail(folder != NULL);
694
695
        if (!folder->klass->scan_tree) return;
696
697
        avalue = alertpanel
698
                (_("Rebuild folder tree"),
699
                 _("The folder tree will be rebuilt. Continue?"),
700
                 _("Yes"), _("No"), NULL);
701
        if (avalue != G_ALERTDEFAULT) return;
702
703
        if (!FOLDER_IS_LOCAL(folder) &&
704
            !main_window_toggle_online_if_offline(folderview->mainwin))
705
                return;
706
707
        inc_lock();
708
        window = label_window_create(_("Rebuilding folder tree..."));
709
710
        summary_show(folderview->summaryview, NULL, FALSE);
711
712
        folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
713
        if (folder->klass->scan_tree(folder) < 0)
714
                alertpanel_error(_("Rebuilding of the folder tree failed."));
715
        folder_set_ui_func(folder, NULL, NULL);
716
717
        folder_write_list();
718
        folderview_set_all();
719
        statusbar_pop_all();
720
721
        gtk_widget_destroy(window);
722
        inc_unlock();
723
}
724
725
#if 0
726
void folderview_rescan_all(void)
727
{
728
        GList *list;
729
        GtkWidget *window;
730
731
        inc_lock();
732
        window = label_window_create(_("Rebuilding all folder trees..."));
733
734
        list = folder_get_list();
735
        for (; list != NULL; list = list->next) {
736
                Folder *folder = list->data;
737
738
                if (!folder->klass->scan_tree) continue;
739
                folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
740
                folder->klass->scan_tree(folder);
741
                folder_set_ui_func(folder, NULL, NULL);
742
        }
743
744
        folder_write_list();
745
        folderview_set_all();
746
        gtk_widget_destroy(window);
747
        inc_unlock();
748
}
749
#endif
750
751
void folderview_check_new(Folder *folder)
752
{
753
        GList *list;
754
        FolderItem *item;
755
        FolderView *folderview;
756
        GtkCTree *ctree;
757
        GtkCTreeNode *node;
758
759
        for (list = folderview_list; list != NULL; list = list->next) {
760
                folderview = (FolderView *)list->data;
761
                ctree = GTK_CTREE(folderview->ctree);
762
763
                if (folder && !FOLDER_IS_LOCAL(folder)) {
764
                        if (!main_window_toggle_online_if_offline
765
                                (folderview->mainwin))
766
                                return;
767
                }
768
769
                inc_lock();
770
                main_window_lock(folderview->mainwin);
771
                gtk_widget_set_sensitive(folderview->ctree, FALSE);
772
773
                for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
774
                     node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
775
                        item = gtk_ctree_node_get_row_data(ctree, node);
776
                        if (!item || !item->path || !item->folder) continue;
777
                        if (item->no_select) continue;
778
                        if (folder && folder != item->folder) continue;
779
                        if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
780
781
                        folderview_scan_tree_func(item->folder, item, NULL);
782
                        if (folder_item_scan(item) < 0) {
783
                                if (folder && !FOLDER_IS_LOCAL(folder))
784
                                        break;
785
                        }
786
                        folderview_update_node(folderview, node);
787
                }
788
789
                gtk_widget_set_sensitive(folderview->ctree, TRUE);
790
                main_window_unlock(folderview->mainwin);
791
                inc_unlock();
792
                statusbar_pop_all();
793
        }
794
795
        folder_write_list();
796
}
797
798
void folderview_check_new_all(void)
799
{
800
        GList *list;
801
        GtkWidget *window;
802
        FolderView *folderview;
803
804
        folderview = (FolderView *)folderview_list->data;
805
806
        inc_lock();
807
        main_window_lock(folderview->mainwin);
808
        window = label_window_create
809
                (_("Checking for new messages in all folders..."));
810
811
        list = folder_get_list();
812
        for (; list != NULL; list = list->next) {
813
                Folder *folder = list->data;
814
815
                folderview_check_new(folder);
816
        }
817
818
        gtk_widget_destroy(window);
819
        main_window_unlock(folderview->mainwin);
820
        inc_unlock();
821
}
822
823
static gboolean folderview_search_new_recursive(GtkCTree *ctree,
824
                                                GtkCTreeNode *node)
825
{
826
        FolderItem *item;
827
828
        if (node) {
829
                item = gtk_ctree_node_get_row_data(ctree, node);
830
                if (item) {
831
                        if (item->new > 0 ||
832
                            (item->stype == F_QUEUE && item->total > 0))
833
                                return TRUE;
834
                }
835
                node = GTK_CTREE_ROW(node)->children;
836
        } else
837
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
838
839
        while (node) {
840
                if (folderview_search_new_recursive(ctree, node) == TRUE)
841
                        return TRUE;
842
                node = GTK_CTREE_ROW(node)->sibling;
843
        }
844
845
        return FALSE;
846
}
847
848
static gboolean folderview_have_new_children(FolderView *folderview,
849
                                             GtkCTreeNode *node)
850
{
851
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
852
853
        if (!node)
854
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
855
        if (!node)
856
                return FALSE;
857
858
        node = GTK_CTREE_ROW(node)->children;
859
860
        while (node) {
861
                if (folderview_search_new_recursive(ctree, node) == TRUE)
862
                        return TRUE;
863
                node = GTK_CTREE_ROW(node)->sibling;
864
        }
865
866
        return FALSE;
867
}
868
869
static gboolean folderview_search_unread_recursive(GtkCTree *ctree,
870
                                                   GtkCTreeNode *node)
871
{
872
        FolderItem *item;
873
874
        if (node) {
875
                item = gtk_ctree_node_get_row_data(ctree, node);
876
                if (item) {
877
                        if (item->unread > 0 ||
878
                            (item->stype == F_QUEUE && item->total > 0))
879
                                return TRUE;
880
                }
881
                node = GTK_CTREE_ROW(node)->children;
882
        } else
883
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
884
885
        while (node) {
886
                if (folderview_search_unread_recursive(ctree, node) == TRUE)
887
                        return TRUE;
888
                node = GTK_CTREE_ROW(node)->sibling;
889
        }
890
891
        return FALSE;
892
}
893
894
static gboolean folderview_have_unread_children(FolderView *folderview,
895
                                                GtkCTreeNode *node)
896
{
897
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
898
899
        if (!node)
900
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
901
        if (!node)
902
                return FALSE;
903
904
        node = GTK_CTREE_ROW(node)->children;
905
906
        while (node) {
907
                if (folderview_search_unread_recursive(ctree, node) == TRUE)
908
                        return TRUE;
909
                node = GTK_CTREE_ROW(node)->sibling;
910
        }
911
912
        return FALSE;
913
}
914
915
static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
916
{
917
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
918
        GtkStyle *style = NULL;
919
        FolderItem *item;
920
        GdkPixmap *xpm, *openxpm;
921
        GdkBitmap *mask, *openmask;
922
        gchar *name;
923
        gchar *str;
924
        gboolean add_unread_mark;
925
        gboolean use_bold, use_color;
926
927
        item = gtk_ctree_node_get_row_data(ctree, node);
928
        g_return_if_fail(item != NULL);
929
930
        switch (item->stype) {
931
        case F_INBOX:
932
                xpm = openxpm = inboxxpm;
933
                mask = openmask = inboxxpmmask;
934
                name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
935
                                !strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
936
                                item->name);
937
                break;
938
        case F_OUTBOX:
939
                xpm = openxpm = outboxxpm;
940
                mask = openmask = outboxxpmmask;
941
                name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
942
                                !strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
943
                                item->name);
944
                break;
945
        case F_QUEUE:
946
                xpm = openxpm = outboxxpm;
947
                mask = openmask = outboxxpmmask;
948
                name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
949
                                !strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
950
                                item->name);
951
                break;
952
        case F_TRASH:
953
                xpm = openxpm = trashxpm;
954
                mask = openmask = trashxpmmask;
955
                name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
956
                                !strcmp2(item->name, TRASH_DIR) ? _("Trash") :
957
                                item->name);
958
                break;
959
        case F_DRAFT:
960
                xpm = folderxpm;
961
                mask = folderxpmmask;
962
                openxpm = folderopenxpm;
963
                openmask = folderopenxpmmask;
964
                name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
965
                                !strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
966
                                item->name);
967
                break;
968
        default:
969
                if (item->no_select) {
970
                        xpm = openxpm = foldernoselectxpm;
971
                        mask = openmask = foldernoselectxpmmask;
972
                } else {
973
                        xpm = folderxpm;
974
                        mask = folderxpmmask;
975
                        openxpm = folderopenxpm;
976
                        openmask = folderopenxpmmask;
977
                }
978
979
                if (!item->parent) {
980
                        switch (FOLDER_TYPE(item->folder)) {
981
                        case F_MH:
982
                                name = " (MH)"; break;
983
                        case F_IMAP:
984
                                name = " (IMAP4)"; break;
985
                        case F_NEWS:
986
                                name = " (News)"; break;
987
                        default:
988
                                name = "";
989
                        }
990
                        name = g_strconcat(item->name, name, NULL);
991
                } else {
992
                        if (FOLDER_TYPE(item->folder) == F_NEWS &&
993
                            item->path &&
994
                            !strcmp2(item->name, item->path))
995
                                name = get_abbrev_newsgroup_name
996
                                        (item->path,
997
                                         prefs_common.ng_abbrev_len);
998
                        else
999
                                name = g_strdup(item->name);
1000
                }
1001
        }
1002
1003
        if (!GTK_CTREE_ROW(node)->expanded &&
1004
            folderview_have_unread_children(folderview, node))
1005
                add_unread_mark = TRUE;
1006
        else
1007
                add_unread_mark = FALSE;
1008
1009
        if (item->stype == F_QUEUE && item->total > 0 &&
1010
            prefs_common.display_folder_unread) {
1011
                str = g_strdup_printf("%s (%d%s)", name, item->total,
1012
                                      add_unread_mark ? "+" : "");
1013
                gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1014
                                        xpm, mask, openxpm, openmask,
1015
                                        FALSE, GTK_CTREE_ROW(node)->expanded);
1016
                g_free(str);
1017
        } else if ((item->unread > 0 || add_unread_mark) &&
1018
                 prefs_common.display_folder_unread) {
1019
1020
                if (item->unread > 0)
1021
                        str = g_strdup_printf("%s (%d%s)", name, item->unread,
1022
                                              add_unread_mark ? "+" : "");
1023
                else
1024
                        str = g_strdup_printf("%s (+)", name);
1025
                gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1026
                                        xpm, mask, openxpm, openmask,
1027
                                        FALSE, GTK_CTREE_ROW(node)->expanded);
1028
                g_free(str);
1029
        } else
1030
                gtk_ctree_set_node_info(ctree, node, name, FOLDER_SPACING,
1031
                                        xpm, mask, openxpm, openmask,
1032
                                        FALSE, GTK_CTREE_ROW(node)->expanded);
1033
        g_free(name);
1034
1035
        if (!item->parent) {
1036
                gtk_ctree_node_set_text(ctree, node, COL_NEW,    "-");
1037
                gtk_ctree_node_set_text(ctree, node, COL_UNREAD, "-");
1038
                gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  "-");
1039
        } else {
1040
                gtk_ctree_node_set_text(ctree, node, COL_NEW,    itos(item->new));
1041
                gtk_ctree_node_set_text(ctree, node, COL_UNREAD, itos(item->unread));
1042
                gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  itos(item->total));
1043
        }
1044
1045
        if (item->stype == F_OUTBOX || item->stype == F_DRAFT ||
1046
            item->stype == F_TRASH) {
1047
                use_bold = use_color = FALSE;
1048
        } else if (item->stype == F_QUEUE) {
1049
                /* highlight queue folder if there are any messages */
1050
                use_bold = use_color = (item->total > 0);
1051
        } else {
1052
                /* if unread messages exist, print with bold font */
1053
                use_bold = (item->unread > 0) || add_unread_mark;
1054
                /* if new messages exist, print with colored letter */
1055
                use_color =
1056
                        (item->new > 0) ||
1057
                        (add_unread_mark &&
1058
                         folderview_have_new_children(folderview, node));
1059
        }
1060
1061
        gtk_ctree_node_set_foreground(ctree, node, NULL);
1062
1063
        if (item->no_select)
1064
                gtk_ctree_node_set_foreground(ctree, node,
1065
                                              &folderview->color_noselect);
1066
        else if (use_bold && use_color)
1067
                style = bold_color_style;
1068
        else if (use_bold)
1069
                style = bold_style;
1070
        else if (use_color)
1071
                gtk_ctree_node_set_foreground(ctree, node,
1072
                                              &folderview->color_new);
1073
1074
        gtk_ctree_node_set_row_style(ctree, node, style);
1075
1076
        item->updated = FALSE;
1077
1078
        if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1079
                folderview_update_node(folderview, node);
1080
}
1081
1082
void folderview_update_item(FolderItem *item, gboolean update_summary)
1083
{
1084
        GList *list;
1085
        FolderView *folderview;
1086
        GtkCTree *ctree;
1087
        GtkCTreeNode *node;
1088
1089
        g_return_if_fail(item != NULL);
1090
1091
        for (list = folderview_list; list != NULL; list = list->next) {
1092
                folderview = (FolderView *)list->data;
1093
                ctree = GTK_CTREE(folderview->ctree);
1094
1095
                node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1096
                if (node) {
1097
                        folderview_update_node(folderview, node);
1098
                        if (update_summary && folderview->opened == node)
1099
                                summary_show(folderview->summaryview,
1100
                                             item, FALSE);
1101
                }
1102
        }
1103
}
1104
1105
static void folderview_update_item_foreach_func(gpointer key, gpointer val,
1106
                                                gpointer data)
1107
{
1108
        folderview_update_item((FolderItem *)key, GPOINTER_TO_INT(data));
1109
}
1110
1111
void folderview_update_item_foreach(GHashTable *table, gboolean update_summary)
1112
{
1113
        g_hash_table_foreach(table, folderview_update_item_foreach_func,
1114
                             GINT_TO_POINTER(update_summary));
1115
}
1116
1117
static gboolean folderview_update_all_updated_func(GNode *node, gpointer data)
1118
{
1119
        FolderItem *item;
1120
1121
        item = FOLDER_ITEM(node->data);
1122
        if (item->updated) {
1123
                debug_print("folderview_update_all_updated(): '%s' is updated\n", item->path);
1124
                folderview_update_item(item, GPOINTER_TO_INT(data));
1125
        }
1126
1127
        return FALSE;
1128
}
1129
1130
void folderview_update_all_updated(gboolean update_summary)
1131
{
1132
        GList *list;
1133
        Folder *folder;
1134
1135
        for (list = folder_get_list(); list != NULL; list = list->next) {
1136
                folder = (Folder *)list->data;
1137
                g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1138
                                folderview_update_all_updated_func,
1139
                                GINT_TO_POINTER(update_summary));
1140
        }
1141
}
1142
1143
static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1144
                                      GNode *gnode, GtkCTreeNode *cnode,
1145
                                      gpointer data)
1146
{
1147
        FolderView *folderview = (FolderView *)data;
1148
        FolderItem *item = FOLDER_ITEM(gnode->data);
1149
1150
        g_return_val_if_fail(item != NULL, FALSE);
1151
1152
        gtk_ctree_node_set_row_data(ctree, cnode, item);
1153
        folderview_update_node(folderview, cnode);
1154
1155
        return TRUE;
1156
}
1157
1158
static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1159
                                   gpointer data)
1160
{
1161
        FolderView *folderview = (FolderView *)data;
1162
        FolderItem *item;
1163
1164
        if (GTK_CTREE_ROW(node)->children) {
1165
                item = gtk_ctree_node_get_row_data(ctree, node);
1166
                g_return_if_fail(item != NULL);
1167
1168
                if (!item->collapsed)
1169
                        gtk_ctree_expand(ctree, node);
1170
                else
1171
                        folderview_update_node(folderview, node);
1172
        }
1173
}
1174
1175
#define SET_SPECIAL_FOLDER(ctree, item) \
1176
{ \
1177
        if (item) { \
1178
                GtkCTreeNode *node, *parent, *sibling; \
1179
 \
1180
                node = gtk_ctree_find_by_row_data(ctree, root, item); \
1181
                if (!node) \
1182
                        g_warning("%s not found.\n", item->path); \
1183
                else { \
1184
                        parent = GTK_CTREE_ROW(node)->parent; \
1185
                        if (prev && parent == GTK_CTREE_ROW(prev)->parent) \
1186
                                sibling = GTK_CTREE_ROW(prev)->sibling; \
1187
                        else \
1188
                                sibling = GTK_CTREE_ROW(parent)->children; \
1189
                        while (sibling) { \
1190
                                FolderItem *tmp; \
1191
 \
1192
                                tmp = gtk_ctree_node_get_row_data \
1193
                                        (ctree, sibling); \
1194
                                if (tmp->stype != F_NORMAL) \
1195
                                        sibling = GTK_CTREE_ROW(sibling)->sibling; \
1196
                                else \
1197
                                        break; \
1198
                        } \
1199
                        if (node != sibling) \
1200
                                gtk_ctree_move(ctree, node, parent, sibling); \
1201
                } \
1202
 \
1203
                prev = node; \
1204
        } \
1205
}
1206
1207
static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1208
                                    Folder *folder)
1209
{
1210
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1211
        GtkCTreeNode *prev = NULL;
1212
1213
        gtk_ctree_sort_recursive(ctree, root);
1214
1215
        if (GTK_CTREE_ROW(root)->parent) return;
1216
1217
        SET_SPECIAL_FOLDER(ctree, folder->inbox);
1218
        SET_SPECIAL_FOLDER(ctree, folder->outbox);
1219
        SET_SPECIAL_FOLDER(ctree, folder->draft);
1220
        SET_SPECIAL_FOLDER(ctree, folder->queue);
1221
        SET_SPECIAL_FOLDER(ctree, folder->trash);
1222
}
1223
1224
static void folderview_append_folder(FolderView *folderview, Folder *folder)
1225
{
1226
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1227
        GtkCTreeNode *root;
1228
1229
        g_return_if_fail(folder != NULL);
1230
1231
        root = gtk_ctree_insert_gnode(ctree, NULL, NULL, folder->node,
1232
                                      folderview_gnode_func, folderview);
1233
        gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1234
                                folderview);
1235
        folderview_sort_folders(folderview, root, folder);
1236
}
1237
1238
void folderview_new_folder(FolderView *folderview)
1239
{
1240
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1241
        FolderItem *item;
1242
1243
        if (!folderview->selected) return;
1244
1245
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1246
        g_return_if_fail(item != NULL);
1247
        g_return_if_fail(item->folder != NULL);
1248
1249
        switch (FOLDER_TYPE(item->folder)) {
1250
        case F_MH:
1251
        case F_MBOX:
1252
        case F_MAILDIR:
1253
        case F_IMAP:
1254
                folderview_new_folder_cb(folderview, 0, NULL);
1255
                break;
1256
        case F_NEWS:
1257
                folderview_new_news_group_cb(folderview, 0, NULL);
1258
                break;
1259
        default:
1260
                break;
1261
        }
1262
}
1263
1264
void folderview_rename_folder(FolderView *folderview)
1265
{
1266
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1267
        FolderItem *item;
1268
1269
        if (!folderview->selected) return;
1270
1271
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1272
        g_return_if_fail(item != NULL);
1273
        g_return_if_fail(item->folder != NULL);
1274
        if (!item->path) return;
1275
        if (item->stype != F_NORMAL) return;
1276
1277
        switch (FOLDER_TYPE(item->folder)) {
1278
        case F_MH:
1279
        case F_MBOX:
1280
        case F_MAILDIR:
1281
        case F_IMAP:
1282
                folderview_rename_folder_cb(folderview, 0, NULL);
1283
                break;
1284
        case F_NEWS:
1285
        default:
1286
                break;
1287
        }
1288
}
1289
1290
void folderview_delete_folder(FolderView *folderview)
1291
{
1292
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1293
        FolderItem *item;
1294
1295
        if (!folderview->selected) return;
1296
1297
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1298
        g_return_if_fail(item != NULL);
1299
        g_return_if_fail(item->folder != NULL);
1300
        if (!item->path) return;
1301
        if (item->stype != F_NORMAL) return;
1302
1303
        switch (FOLDER_TYPE(item->folder)) {
1304
        case F_MH:
1305
        case F_MBOX:
1306
        case F_MAILDIR:
1307
        case F_IMAP:
1308
                folderview_delete_folder_cb(folderview, 0, NULL);
1309
                break;
1310
        case F_NEWS:
1311
                folderview_rm_news_group_cb(folderview, 0, NULL);
1312
                break;
1313
        default:
1314
                break;
1315
        }
1316
}
1317
1318
void folderview_check_new_selected(FolderView *folderview)
1319
{
1320
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1321
        FolderItem *item;
1322
1323
        if (!folderview->selected) return;
1324
1325
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1326
        g_return_if_fail(item != NULL);
1327
        g_return_if_fail(item->folder != NULL);
1328
        if (item->parent != NULL) return;
1329
1330
        folderview_check_new(item->folder);
1331
}
1332
1333
void folderview_remove_mailbox(FolderView *folderview)
1334
{
1335
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1336
        FolderItem *item;
1337
1338
        if (!folderview->selected) return;
1339
1340
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1341
        g_return_if_fail(item != NULL);
1342
        g_return_if_fail(item->folder != NULL);
1343
        if (item->parent != NULL) return;
1344
1345
        switch (FOLDER_TYPE(item->folder)) {
1346
        case F_MH:
1347
        case F_MBOX:
1348
        case F_MAILDIR:
1349
                folderview_remove_mailbox_cb(folderview, 0, NULL);
1350
                break;
1351
        case F_IMAP:
1352
                folderview_rm_imap_server_cb(folderview, 0, NULL);
1353
                break;
1354
        case F_NEWS:
1355
                folderview_rm_news_server_cb(folderview, 0, NULL);
1356
                break;
1357
        default:
1358
                break;
1359
        }
1360
}
1361
1362
void folderview_rebuild_tree(FolderView *folderview)
1363
{
1364
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1365
        FolderItem *item;
1366
1367
        if (!folderview->selected) return;
1368
1369
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1370
        g_return_if_fail(item != NULL);
1371
        g_return_if_fail(item->folder != NULL);
1372
        if (item->parent != NULL) return;
1373
1374
        folderview_rescan_tree(folderview, item->folder);
1375
}
1376
1377
1378
/* callback functions */
1379
1380
static gboolean folderview_button_pressed(GtkWidget *ctree,
1381
                                          GdkEventButton *event,
1382
                                          FolderView *folderview)
1383
{
1384
        GtkCList *clist = GTK_CLIST(ctree);
1385
        gint prev_row = -1, row = -1, column = -1;
1386
        FolderItem *item;
1387
        Folder *folder;
1388
        GtkWidget *popup;
1389
        gboolean new_folder      = FALSE;
1390
        gboolean rename_folder   = FALSE;
1391
        gboolean delete_folder   = FALSE;
1392
        gboolean empty_trash     = FALSE;
1393
        gboolean download_msg    = FALSE;
1394
        gboolean update_tree     = FALSE;
1395
        gboolean rescan_tree     = FALSE;
1396
        gboolean remove_tree     = FALSE;
1397
        gboolean search_folder   = FALSE;
1398
        gboolean folder_property = FALSE;
1399
1400
        if (!event) return FALSE;
1401
1402
        if (event->button == 1) {
1403
                folderview->open_folder = TRUE;
1404
                return FALSE;
1405
        }
1406
1407
        if (event->button == 2 || event->button == 3) {
1408
                /* right clicked */
1409
                if (clist->selection) {
1410
                        GtkCTreeNode *node;
1411
1412
                        node = GTK_CTREE_NODE(clist->selection->data);
1413
                        if (node)
1414
                                prev_row = gtkut_ctree_get_nth_from_node
1415
                                        (GTK_CTREE(ctree), node);
1416
                }
1417
1418
                if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1419
                                                  &row, &column))
1420
                        return FALSE;
1421
                if (prev_row != row) {
1422
                        gtk_clist_unselect_all(clist);
1423
                        if (event->button == 2)
1424
                                folderview_select_node
1425
                                        (folderview,
1426
                                         gtk_ctree_node_nth(GTK_CTREE(ctree),
1427
                                                             row));
1428
                        else
1429
                                gtk_clist_select_row(clist, row, column);
1430
                }
1431
        }
1432
1433
        if (event->button != 3) return FALSE;
1434
1435
        item = gtk_clist_get_row_data(clist, row);
1436
        g_return_val_if_fail(item != NULL, FALSE);
1437
        g_return_val_if_fail(item->folder != NULL, FALSE);
1438
        folder = item->folder;
1439
1440
        if (folderview->mainwin->lock_count == 0) {
1441
                new_folder = TRUE;
1442
                if (item->parent == NULL) {
1443
                        update_tree = remove_tree = TRUE;
1444
                        if (folder->account)
1445
                                folder_property = TRUE;
1446
                } else {
1447
                        folder_property = TRUE;
1448
                        if (folderview->selected == folderview->opened)
1449
                                search_folder = TRUE;
1450
                }
1451
                if (FOLDER_IS_LOCAL(folder) || FOLDER_TYPE(folder) == F_IMAP) {
1452
                        if (item->parent == NULL)
1453
                                update_tree = rescan_tree = TRUE;
1454
                        else if (item->stype == F_NORMAL)
1455
                                rename_folder = delete_folder = TRUE;
1456
                        else if (item->stype == F_TRASH)
1457
                                empty_trash = TRUE;
1458
                } else if (FOLDER_TYPE(folder) == F_NEWS) {
1459
                        if (item->parent != NULL)
1460
                                delete_folder = TRUE;
1461
                }
1462
                if (FOLDER_TYPE(folder) == F_IMAP ||
1463
                    FOLDER_TYPE(folder) == F_NEWS) {
1464
                        if (item->parent != NULL && item->no_select == FALSE)
1465
                                download_msg = TRUE;
1466
                }
1467
        }
1468
1469
#define SET_SENS(factory, name, sens) \
1470
        menu_set_sensitive(folderview->factory, name, sens)
1471
1472
        if (FOLDER_IS_LOCAL(folder)) {
1473
                popup = folderview->mail_popup;
1474
                menu_set_insensitive_all(GTK_MENU_SHELL(popup));
1475
                SET_SENS(mail_factory, "/Create new folder...", new_folder);
1476
                SET_SENS(mail_factory, "/Rename folder...", rename_folder);
1477
                SET_SENS(mail_factory, "/Delete folder", delete_folder);
1478
                SET_SENS(mail_factory, "/Empty trash", empty_trash);
1479
                SET_SENS(mail_factory, "/Check for new messages", update_tree);
1480
                SET_SENS(mail_factory, "/Rebuild folder tree", rescan_tree);
1481
                SET_SENS(mail_factory, "/Search messages...", search_folder);
1482
                SET_SENS(mail_factory, "/Properties...", folder_property);
1483
        } else if (FOLDER_TYPE(folder) == F_IMAP) {
1484
                popup = folderview->imap_popup;
1485
                menu_set_insensitive_all(GTK_MENU_SHELL(popup));
1486
                SET_SENS(imap_factory, "/Create new folder...", new_folder);
1487
                SET_SENS(imap_factory, "/Rename folder...", rename_folder);
1488
                SET_SENS(imap_factory, "/Delete folder", delete_folder);
1489
                SET_SENS(imap_factory, "/Empty trash", empty_trash);
1490
                SET_SENS(imap_factory, "/Download", download_msg);
1491
                SET_SENS(imap_factory, "/Check for new messages", update_tree);
1492
                SET_SENS(imap_factory, "/Rebuild folder tree", rescan_tree);
1493
                SET_SENS(imap_factory, "/Search messages...", search_folder);
1494
                SET_SENS(imap_factory, "/Properties...", folder_property);
1495
        } else if (FOLDER_TYPE(folder) == F_NEWS) {
1496
                popup = folderview->news_popup;
1497
                menu_set_insensitive_all(GTK_MENU_SHELL(popup));
1498
                SET_SENS(news_factory, "/Subscribe to newsgroup...", new_folder);
1499
                SET_SENS(news_factory, "/Remove newsgroup", delete_folder);
1500
                SET_SENS(news_factory, "/Download", download_msg);
1501
                SET_SENS(news_factory, "/Check for new messages", update_tree);
1502
                SET_SENS(news_factory, "/Search messages...", search_folder);
1503
                SET_SENS(news_factory, "/Properties...", folder_property);
1504
        } else
1505
                return FALSE;
1506
1507
#undef SET_SENS
1508
1509
        gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1510
                       event->button, event->time);
1511
1512
        return FALSE;
1513
}
1514
1515
static gboolean folderview_button_released(GtkWidget *ctree,
1516
                                           GdkEventButton *event,
1517
                                           FolderView *folderview)
1518
{
1519
        if (!event) return FALSE;
1520
1521
        if (event->button == 1 && folderview->open_folder == FALSE &&
1522
            folderview->opened != NULL) {
1523
                gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
1524
                                          folderview->opened);
1525
                gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
1526
        }
1527
1528
        return FALSE;
1529
}
1530
1531
static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1532
                                       FolderView *folderview)
1533
{
1534
        if (!event) return FALSE;
1535
1536
        switch (event->keyval) {
1537
        case GDK_Return:
1538
                if (folderview->selected) {
1539
                        folderview_select_node(folderview,
1540
                                               folderview->selected);
1541
                }
1542
                break;
1543
        case GDK_space:
1544
                if (folderview->selected) {
1545
                        if (folderview->opened == folderview->selected &&
1546
                            (!folderview->summaryview->folder_item ||
1547
                             folderview->summaryview->folder_item->total == 0))
1548
                                folderview_select_next_unread(folderview);
1549
                        else
1550
                                folderview_select_node(folderview,
1551
                                                       folderview->selected);
1552
                }
1553
                break;
1554
        default:
1555
                break;
1556
        }
1557
1558
        return FALSE;
1559
}
1560
1561
static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
1562
                                gint column, FolderView *folderview)
1563
{
1564
        static gboolean can_select = TRUE;        /* exclusive lock */
1565
        gboolean opened;
1566
        FolderItem *item;
1567
1568
        folderview->selected = row;
1569
1570
        main_window_set_menu_sensitive(folderview->mainwin);
1571
1572
        if (folderview->opened == row) {
1573
                folderview->open_folder = FALSE;
1574
                return;
1575
        }
1576
1577
        if (!can_select || summary_is_locked(folderview->summaryview)) {
1578
                gtkut_ctree_set_focus_row(ctree, folderview->opened);
1579
                gtk_ctree_select(ctree, folderview->opened);
1580
                return;
1581
        }
1582
1583
        if (!folderview->open_folder) return;
1584
1585
        item = gtk_ctree_node_get_row_data(ctree, row);
1586
        if (!item) return;
1587
1588
        can_select = FALSE;
1589
1590
        if (item->path)
1591
                debug_print(_("Folder %s is selected\n"), item->path);
1592
1593
        if (!GTK_CTREE_ROW(row)->children)
1594
                gtk_ctree_expand(ctree, row);
1595
        if (folderview->opened &&
1596
            !GTK_CTREE_ROW(folderview->opened)->children)
1597
                gtk_ctree_collapse(ctree, folderview->opened);
1598
1599
        /* ungrab the mouse event */
1600
        if (GTK_WIDGET_HAS_GRAB(ctree)) {
1601
                gtk_grab_remove(GTK_WIDGET(ctree));
1602
                if (gdk_pointer_is_grabbed())
1603
                        gdk_pointer_ungrab(GDK_CURRENT_TIME);
1604
        }
1605
1606
        opened = summary_show(folderview->summaryview, item, FALSE);
1607
1608
        if (!opened) {
1609
                gtkut_ctree_set_focus_row(ctree, folderview->opened);
1610
                gtk_ctree_select(ctree, folderview->opened);
1611
        } else {
1612
                folderview->opened = row;
1613
                if (gtk_ctree_node_is_visible(ctree, row)
1614
                    != GTK_VISIBILITY_FULL)
1615
                        gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
1616
        }
1617
1618
        folderview->open_folder = FALSE;
1619
        can_select = TRUE;
1620
}
1621
1622
static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
1623
                                     FolderView *folderview)
1624
{
1625
        FolderItem *item;
1626
1627
        item = gtk_ctree_node_get_row_data(ctree, node);
1628
        g_return_if_fail(item != NULL);
1629
        item->collapsed = FALSE;
1630
        folderview_update_node(folderview, node);
1631
}
1632
1633
static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
1634
                                      FolderView *folderview)
1635
{
1636
        FolderItem *item;
1637
1638
        item = gtk_ctree_node_get_row_data(ctree, node);
1639
        g_return_if_fail(item != NULL);
1640
        item->collapsed= TRUE;
1641
        folderview_update_node(folderview, node);
1642
}
1643
1644
static void folderview_popup_close(GtkMenuShell *menu_shell,
1645
                                   FolderView *folderview)
1646
{
1647
        if (!folderview->opened) return;
1648
1649
        gtkut_ctree_set_focus_row(GTK_CTREE(folderview->ctree),
1650
                                  folderview->opened);
1651
        gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
1652
}
1653
1654
static void folderview_col_resized(GtkCList *clist, gint column, gint width,
1655
                                   FolderView *folderview)
1656
{
1657
        switch (column) {
1658
        case COL_FOLDER:
1659
                prefs_common.folder_col_folder = width;
1660
                break;
1661
        case COL_NEW:
1662
                prefs_common.folder_col_new = width;
1663
                break;
1664
        case COL_UNREAD:
1665
                prefs_common.folder_col_unread = width;
1666
                break;
1667
        case COL_TOTAL:
1668
                prefs_common.folder_col_total = width;
1669
                break;
1670
        default:
1671
                break;
1672
        }
1673
}
1674
1675
static void folderview_download_func(Folder *folder, FolderItem *item,
1676
                                     gpointer data)
1677
{
1678
        GList *list;
1679
1680
        for (list = folderview_list; list != NULL; list = list->next) {
1681
                FolderView *folderview = (FolderView *)list->data;
1682
                MainWindow *mainwin = folderview->mainwin;
1683
                gchar *str;
1684
1685
                str = g_strdup_printf
1686
                        (_("Downloading messages in %s ..."), item->path);
1687
                main_window_progress_set(mainwin,
1688
                                         GPOINTER_TO_INT(data), item->total);
1689
                STATUSBAR_PUSH(mainwin, str);
1690
                STATUSBAR_POP(mainwin);
1691
                g_free(str);
1692
        }
1693
}
1694
1695
static void folderview_download_cb(FolderView *folderview, guint action,
1696
                                   GtkWidget *widget)
1697
{
1698
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1699
        MainWindow *mainwin = folderview->mainwin;
1700
        FolderItem *item;
1701
1702
        if (!folderview->selected) return;
1703
1704
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1705
        g_return_if_fail(item != NULL);
1706
        g_return_if_fail(item->folder != NULL);
1707
1708
        if (!main_window_toggle_online_if_offline(folderview->mainwin))
1709
                return;
1710
1711
        main_window_cursor_wait(mainwin);
1712
        inc_lock();
1713
        main_window_lock(mainwin);
1714
        gtk_widget_set_sensitive(folderview->ctree, FALSE);
1715
        main_window_progress_on(mainwin);
1716
        GTK_EVENTS_FLUSH();
1717
        folder_set_ui_func(item->folder, folderview_download_func, NULL);
1718
        if (folder_item_fetch_all_msg(item) < 0) {
1719
                gchar *name;
1720
1721
                name = trim_string(item->name, 32);
1722
                alertpanel_error(_("Error occurred while downloading messages in `%s'."), name);
1723
                g_free(name);
1724
        }
1725
        folder_set_ui_func(item->folder, NULL, NULL);
1726
        main_window_progress_off(mainwin);
1727
        gtk_widget_set_sensitive(folderview->ctree, TRUE);
1728
        main_window_unlock(mainwin);
1729
        inc_unlock();
1730
        main_window_cursor_normal(mainwin);
1731
        statusbar_pop_all();
1732
}
1733
1734
static void folderview_update_tree_cb(FolderView *folderview, guint action,
1735
                                      GtkWidget *widget)
1736
{
1737
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1738
        FolderItem *item;
1739
1740
        if (!folderview->selected) return;
1741
1742
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1743
        g_return_if_fail(item != NULL);
1744
        g_return_if_fail(item->folder != NULL);
1745
1746
        if (action == 0)
1747
                folderview_check_new(item->folder);
1748
        else
1749
                folderview_rescan_tree(folderview, item->folder);
1750
}
1751
1752
static void folderview_new_folder_cb(FolderView *folderview, guint action,
1753
                                     GtkWidget *widget)
1754
{
1755
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1756
        FolderItem *item;
1757
        FolderItem *new_item;
1758
        gchar *new_folder;
1759
        gchar *name;
1760
        gchar *p;
1761
1762
        if (!folderview->selected) return;
1763
1764
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1765
        g_return_if_fail(item != NULL);
1766
        g_return_if_fail(item->folder != NULL);
1767
        if (FOLDER_TYPE(item->folder) == F_IMAP)
1768
                g_return_if_fail(item->folder->account != NULL);
1769
1770
        if (FOLDER_TYPE(item->folder) == F_IMAP) {
1771
                new_folder = input_dialog
1772
                        (_("New folder"),
1773
                         _("Input the name of new folder:\n"
1774
                           "(if you want to create a folder to store subfolders,\n"
1775
                           " append `/' at the end of the name)"),
1776
                         _("NewFolder"));
1777
        } else {
1778
                new_folder = input_dialog(_("New folder"),
1779
                                          _("Input the name of new folder:"),
1780
                                          _("NewFolder"));
1781
        }
1782
        if (!new_folder) return;
1783
        AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
1784
1785
        p = strchr(new_folder, G_DIR_SEPARATOR);
1786
        if ((p && FOLDER_TYPE(item->folder) != F_IMAP) ||
1787
            (p && FOLDER_TYPE(item->folder) == F_IMAP && *(p + 1) != '\0')) {
1788
                alertpanel_error(_("`%c' can't be included in folder name."),
1789
                                 G_DIR_SEPARATOR);
1790
                return;
1791
        }
1792
1793
        name = trim_string(new_folder, 32);
1794
        AUTORELEASE_STR(name, {g_free(name); return;});
1795
1796
        /* find whether the directory already exists */
1797
        if (folder_find_child_item_by_name(item, new_folder)) {
1798
                alertpanel_error(_("The folder `%s' already exists."), name);
1799
                return;
1800
        }
1801
1802
        new_item = item->folder->klass->create_folder(item->folder, item,
1803
                                                      new_folder);
1804
        if (!new_item) {
1805
                alertpanel_error(_("Can't create the folder `%s'."), name);
1806
                return;
1807
        }
1808
1809
        folderview_append_item(new_item);
1810
        folder_write_list();
1811
}
1812
1813
static void folderview_rename_folder_cb(FolderView *folderview, guint action,
1814
                                        GtkWidget *widget)
1815
{
1816
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1817
        FolderItem *item;
1818
        gchar *new_folder;
1819
        gchar *name;
1820
        gchar *message;
1821
        gchar *old_path;
1822
        gchar *old_id;
1823
        gchar *new_id;
1824
1825
        if (!folderview->selected) return;
1826
1827
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1828
        g_return_if_fail(item != NULL);
1829
        g_return_if_fail(item->path != NULL);
1830
        g_return_if_fail(item->folder != NULL);
1831
1832
        name = trim_string(item->name, 32);
1833
        message = g_strdup_printf(_("Input new name for `%s':"), name);
1834
        new_folder = input_dialog(_("Rename folder"), message,
1835
                                  g_basename(item->path));
1836
        g_free(message);
1837
        g_free(name);
1838
        if (!new_folder) return;
1839
        AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
1840
1841
        if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
1842
                alertpanel_error(_("`%c' can't be included in folder name."),
1843
                                 G_DIR_SEPARATOR);
1844
                return;
1845
        }
1846
1847
        if (folder_find_child_item_by_name(item->parent, new_folder)) {
1848
                name = trim_string(new_folder, 32);
1849
                alertpanel_error(_("The folder `%s' already exists."), name);
1850
                g_free(name);
1851
                return;
1852
        }
1853
1854
        Xstrdup_a(old_path, item->path, {g_free(new_folder); return;});
1855
        old_id = folder_item_get_identifier(item);
1856
1857
        if (item->folder->klass->rename_folder(item->folder, item,
1858
                                               new_folder) < 0) {
1859
                g_free(old_id);
1860
                return;
1861
        }
1862
1863
        if (folder_get_default_folder() == item->folder)
1864
                prefs_filter_rename_path(old_path, item->path);
1865
        new_id = folder_item_get_identifier(item);
1866
        prefs_filter_rename_path(old_id, new_id);
1867
        g_free(old_id);
1868
        g_free(new_id);
1869
1870
        gtk_clist_freeze(GTK_CLIST(ctree));
1871
1872
        folderview_update_node(folderview, folderview->selected);
1873
        folderview_sort_folders(folderview,
1874
                                GTK_CTREE_ROW(folderview->selected)->parent,
1875
                                item->folder);
1876
        if (folderview->opened == folderview->selected ||
1877
            gtk_ctree_is_ancestor(ctree,
1878
                                  folderview->selected,
1879
                                  folderview->opened)) {
1880
                GtkCTreeNode *node = folderview->opened;
1881
                folderview_unselect(folderview);
1882
                folderview_select_node(folderview, node);
1883
        }
1884
1885
        gtk_clist_thaw(GTK_CLIST(ctree));
1886
1887
        folder_write_list();
1888
}
1889
1890
static void folderview_delete_folder_cb(FolderView *folderview, guint action,
1891
                                        GtkWidget *widget)
1892
{
1893
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1894
        Folder *folder;
1895
        FolderItem *item;
1896
        gchar *message, *name;
1897
        AlertValue avalue;
1898
        gchar *old_path;
1899
        gchar *old_id;
1900
1901
        if (!folderview->selected) return;
1902
1903
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1904
        g_return_if_fail(item != NULL);
1905
        g_return_if_fail(item->path != NULL);
1906
        g_return_if_fail(item->folder != NULL);
1907
1908
        folder = item->folder;
1909
1910
        name = trim_string(item->name, 32);
1911
        AUTORELEASE_STR(name, {g_free(name); return;});
1912
        message = g_strdup_printf
1913
                (_("All folder(s) and message(s) under `%s' will be deleted.\n"
1914
                   "Do you really want to delete?"), name);
1915
        avalue = alertpanel(_("Delete folder"), message,
1916
                            _("Yes"), _("+No"), NULL);
1917
        g_free(message);
1918
        if (avalue != G_ALERTDEFAULT) return;
1919
1920
        Xstrdup_a(old_path, item->path, return);
1921
        old_id = folder_item_get_identifier(item);
1922
1923
        if (folderview->opened == folderview->selected ||
1924
            gtk_ctree_is_ancestor(ctree,
1925
                                  folderview->selected, folderview->opened)) {
1926
                summary_clear_all(folderview->summaryview);
1927
                folderview->opened = NULL;
1928
        }
1929
1930
        if (folder->klass->remove_folder(folder, item) < 0) {
1931
                alertpanel_error(_("Can't remove the folder `%s'."), name);
1932
                g_free(old_id);
1933
                return;
1934
        }
1935
1936
        if (folder_get_default_folder() == folder)
1937
                prefs_filter_delete_path(old_path);
1938
        prefs_filter_delete_path(old_id);
1939
        g_free(old_id);
1940
1941
        gtk_ctree_remove_node(ctree, folderview->selected);
1942
        folder_write_list();
1943
}
1944
1945
static void folderview_empty_trash_cb(FolderView *folderview, guint action,
1946
                                      GtkWidget *widget)
1947
{
1948
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1949
        FolderItem *item;
1950
        Folder *folder;
1951
1952
        if (!folderview->selected) return;
1953
1954
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1955
        g_return_if_fail(item != NULL);
1956
        g_return_if_fail(item->path != NULL);
1957
        g_return_if_fail(item->folder != NULL);
1958
1959
        folder = item->folder;
1960
1961
        if (folder->trash != item) return;
1962
        if (item->stype != F_TRASH) return;
1963
1964
        if (alertpanel(_("Empty trash"), _("Empty all messages in trash?"),
1965
                       _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
1966
                return;
1967
1968
        procmsg_empty_trash(folder->trash);
1969
        statusbar_pop_all();
1970
        folderview_update_item(folder->trash, TRUE);
1971
1972
        if (folderview->opened == folderview->selected)
1973
                gtk_widget_grab_focus(folderview->ctree);
1974
}
1975
1976
static void folderview_remove_mailbox_cb(FolderView *folderview, guint action,
1977
                                         GtkWidget *widget)
1978
{
1979
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1980
        GtkCTreeNode *node;
1981
        FolderItem *item;
1982
        gchar *name;
1983
        gchar *message;
1984
        AlertValue avalue;
1985
1986
        if (!folderview->selected) return;
1987
        node = folderview->selected;
1988
        item = gtk_ctree_node_get_row_data(ctree, node);
1989
        g_return_if_fail(item != NULL);
1990
        g_return_if_fail(item->folder != NULL);
1991
        if (item->parent) return;
1992
1993
        name = trim_string(item->folder->name, 32);
1994
        message = g_strdup_printf
1995
                (_("Really remove the mailbox `%s' ?\n"
1996
                   "(The messages are NOT deleted from the disk)"), name);
1997
        avalue = alertpanel(_("Remove mailbox"), message,
1998
                            _("Yes"), _("+No"), NULL);
1999
        g_free(message);
2000
        g_free(name);
2001
        if (avalue != G_ALERTDEFAULT) return;
2002
2003
        if (folderview->summaryview->folder_item &&
2004
            folderview->summaryview->folder_item->folder == item->folder) {
2005
                summary_clear_all(folderview->summaryview);
2006
                folderview->opened = NULL;
2007
        }
2008
        folder_destroy(item->folder);
2009
        gtk_ctree_remove_node(ctree, node);
2010
        folder_write_list();
2011
}
2012
2013
static void folderview_rm_imap_server_cb(FolderView *folderview, guint action,
2014
                                         GtkWidget *widget)
2015
{
2016
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2017
        FolderItem *item;
2018
        PrefsAccount *account;
2019
        gchar *name;
2020
        gchar *message;
2021
        AlertValue avalue;
2022
2023
        if (!folderview->selected) return;
2024
2025
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2026
        g_return_if_fail(item != NULL);
2027
        g_return_if_fail(item->folder != NULL);
2028
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2029
        g_return_if_fail(item->folder->account != NULL);
2030
2031
        name = trim_string(item->folder->name, 32);
2032
        message = g_strdup_printf(_("Really delete IMAP4 account `%s'?"), name);
2033
        avalue = alertpanel(_("Delete IMAP4 account"), message,
2034
                            _("Yes"), _("+No"), NULL);
2035
        g_free(message);
2036
        g_free(name);
2037
2038
        if (avalue != G_ALERTDEFAULT) return;
2039
2040
        if (folderview->summaryview->folder_item &&
2041
            folderview->summaryview->folder_item->folder == item->folder) {
2042
                summary_clear_all(folderview->summaryview);
2043
                folderview->opened = NULL;
2044
        }
2045
2046
        account = item->folder->account;
2047
        folder_destroy(item->folder);
2048
        account_destroy(account);
2049
        gtk_ctree_remove_node(ctree, folderview->selected);
2050
        account_set_menu();
2051
        main_window_reflect_prefs_all();
2052
        folder_write_list();
2053
}
2054
2055
static void folderview_new_news_group_cb(FolderView *folderview, guint action,
2056
                                         GtkWidget *widget)
2057
{
2058
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2059
        GtkCTreeNode *servernode, *node;
2060
        Folder *folder;
2061
        FolderItem *item;
2062
        FolderItem *rootitem;
2063
        FolderItem *newitem;
2064
        GSList *new_subscr;
2065
        GSList *cur;
2066
        GNode *gnode;
2067
2068
        if (!folderview->selected) return;
2069
2070
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2071
        g_return_if_fail(item != NULL);
2072
        folder = item->folder;
2073
        g_return_if_fail(folder != NULL);
2074
        g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS);
2075
        g_return_if_fail(folder->account != NULL);
2076
2077
        if (GTK_CTREE_ROW(folderview->selected)->parent != NULL)
2078
                servernode = GTK_CTREE_ROW(folderview->selected)->parent;
2079
        else
2080
                servernode = folderview->selected;
2081
2082
        rootitem = gtk_ctree_node_get_row_data(ctree, servernode);
2083
2084
        new_subscr = grouplist_dialog(folder);
2085
2086
        /* remove unsubscribed newsgroups */
2087
        for (gnode = folder->node->children; gnode != NULL; ) {
2088
                GNode *next = gnode->next;
2089
2090
                item = FOLDER_ITEM(gnode->data);
2091
                if (g_slist_find_custom(new_subscr, item->path,
2092
                                        (GCompareFunc)g_strcasecmp) != NULL) {
2093
                        gnode = next;
2094
                        continue;
2095
                }
2096
2097
                node = gtk_ctree_find_by_row_data(ctree, servernode, item);
2098
                if (!node) {
2099
                        gnode = next;
2100
                        continue;
2101
                }
2102
2103
                if (folderview->opened == node) {
2104
                        summary_clear_all(folderview->summaryview);
2105
                        folderview->opened = NULL;
2106
                }
2107
2108
                folder_item_remove(item);
2109
                gtk_ctree_remove_node(ctree, node);
2110
2111
                gnode = next;
2112
        }
2113
2114
        gtk_clist_freeze(GTK_CLIST(ctree));
2115
2116
        /* add subscribed newsgroups */
2117
        for (cur = new_subscr; cur != NULL; cur = cur->next) {
2118
                gchar *name = (gchar *)cur->data;
2119
2120
                if (folder_find_child_item_by_name(rootitem, name) != NULL)
2121
                        continue;
2122
2123
                newitem = folder_item_new(name, name);
2124
                folder_item_append(rootitem, newitem);
2125
                folderview_append_item(newitem);
2126
        }
2127
2128
        gtk_clist_thaw(GTK_CLIST(ctree));
2129
2130
        slist_free_strings(new_subscr);
2131
        g_slist_free(new_subscr);
2132
2133
        folder_write_list();
2134
}
2135
2136
static void folderview_rm_news_group_cb(FolderView *folderview, guint action,
2137
                                        GtkWidget *widget)
2138
{
2139
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2140
        FolderItem *item;
2141
        gchar *name;
2142
        gchar *message;
2143
        AlertValue avalue;
2144
2145
        if (!folderview->selected) return;
2146
2147
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2148
        g_return_if_fail(item != NULL);
2149
        g_return_if_fail(item->folder != NULL);
2150
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
2151
        g_return_if_fail(item->folder->account != NULL);
2152
2153
        name = trim_string_before(item->path, 32);
2154
        message = g_strdup_printf(_("Really delete newsgroup `%s'?"), name);
2155
        avalue = alertpanel(_("Delete newsgroup"), message,
2156
                            _("Yes"), _("+No"), NULL);
2157
        g_free(message);
2158
        g_free(name);
2159
        if (avalue != G_ALERTDEFAULT) return;
2160
2161
        if (folderview->opened == folderview->selected) {
2162
                summary_clear_all(folderview->summaryview);
2163
                folderview->opened = NULL;
2164
        }
2165
2166
        folder_item_remove(item);
2167
        gtk_ctree_remove_node(ctree, folderview->selected);
2168
        folder_write_list();
2169
}
2170
2171
static void folderview_rm_news_server_cb(FolderView *folderview, guint action,
2172
                                         GtkWidget *widget)
2173
{
2174
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2175
        FolderItem *item;
2176
        PrefsAccount *account;
2177
        gchar *name;
2178
        gchar *message;
2179
        AlertValue avalue;
2180
2181
        if (!folderview->selected) return;
2182
2183
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2184
        g_return_if_fail(item != NULL);
2185
        g_return_if_fail(item->folder != NULL);
2186
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
2187
        g_return_if_fail(item->folder->account != NULL);
2188
2189
        name = trim_string(item->folder->name, 32);
2190
        message = g_strdup_printf(_("Really delete news account `%s'?"), name);
2191
        avalue = alertpanel(_("Delete news account"), message,
2192
                            _("Yes"), _("+No"), NULL);
2193
        g_free(message);
2194
        g_free(name);
2195
2196
        if (avalue != G_ALERTDEFAULT) return;
2197
2198
        if (folderview->summaryview->folder_item &&
2199
            folderview->summaryview->folder_item->folder == item->folder) {
2200
                summary_clear_all(folderview->summaryview);
2201
                folderview->opened = NULL;
2202
        }
2203
2204
        account = item->folder->account;
2205
        folder_destroy(item->folder);
2206
        account_destroy(account);
2207
        gtk_ctree_remove_node(ctree, folderview->selected);
2208
        account_set_menu();
2209
        main_window_reflect_prefs_all();
2210
        folder_write_list();
2211
}
2212
2213
static void folderview_search_cb(FolderView *folderview, guint action,
2214
                                 GtkWidget *widget)
2215
{
2216
        summary_search(folderview->summaryview);
2217
}
2218
2219
static void folderview_property_cb(FolderView *folderview, guint action,
2220
                                   GtkWidget *widget)
2221
{
2222
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2223
        FolderItem *item;
2224
2225
        if (!folderview->selected) return;
2226
2227
        item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2228
        g_return_if_fail(item != NULL);
2229
        g_return_if_fail(item->folder != NULL);
2230
2231
        if (item->parent == NULL && item->folder->account)
2232
                account_open(item->folder->account);
2233
        else
2234
                prefs_folder_item_open(item);
2235
}
2236
2237
static void folderview_defer_expand_stop(FolderView *folderview)
2238
{
2239
        if (folderview->spring_timer > 0) {
2240
                gtk_timeout_remove(folderview->spring_timer);
2241
                folderview->spring_timer = 0;
2242
        }
2243
        folderview->spring_node = NULL;
2244
}
2245
2246
static gint folderview_defer_expand(gpointer data)
2247
{
2248
        FolderView *folderview = (FolderView *)data;
2249
2250
        if (folderview->spring_node) {
2251
                gtk_ctree_expand(GTK_CTREE(folderview->ctree),
2252
                                 folderview->spring_node);
2253
        }
2254
        folderview_defer_expand_stop(folderview);
2255
2256
        return FALSE;
2257
}
2258
2259
static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
2260
                                          GdkDragContext *context,
2261
                                          gint            x,
2262
                                          gint            y,
2263
                                          guint           time,
2264
                                          FolderView     *folderview)
2265
{
2266
        gint row, column;
2267
        FolderItem *item, *src_item;
2268
        GtkCTreeNode *node = NULL;
2269
        gboolean acceptable = FALSE;
2270
2271
        if (gtk_clist_get_selection_info
2272
                (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
2273
                node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2274
                item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2275
                src_item = folderview->summaryview->folder_item;
2276
                if (src_item && src_item != item)
2277
                        acceptable = FOLDER_ITEM_CAN_ADD(item);
2278
        }
2279
2280
        if (node != folderview->spring_node) {
2281
                folderview_defer_expand_stop(folderview);
2282
                if (node && !GTK_CTREE_ROW(node)->expanded &&
2283
                    GTK_CTREE_ROW(node)->children) {
2284
                        folderview->spring_timer =
2285
                                gtk_timeout_add(1000, folderview_defer_expand,
2286
                                                folderview);
2287
                        folderview->spring_node = node;
2288
                }
2289
        }
2290
2291
        if (acceptable) {
2292
                g_signal_handlers_block_by_func
2293
                        (G_OBJECT(widget),
2294
                         G_CALLBACK(folderview_selected), folderview);
2295
                gtk_ctree_select(GTK_CTREE(widget), node);
2296
                g_signal_handlers_unblock_by_func
2297
                        (G_OBJECT(widget),
2298
                         G_CALLBACK(folderview_selected), folderview);
2299
                if ((context->actions & GDK_ACTION_MOVE) != 0)
2300
                        gdk_drag_status(context, GDK_ACTION_MOVE, time);
2301
                else if ((context->actions & GDK_ACTION_COPY) != 0)
2302
                        gdk_drag_status(context, GDK_ACTION_COPY, time);
2303
                else if ((context->actions & GDK_ACTION_LINK) != 0)
2304
                        gdk_drag_status(context, GDK_ACTION_LINK, time);
2305
                else
2306
                        gdk_drag_status(context, 0, time);
2307
        } else {
2308
                gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2309
                gdk_drag_status(context, 0, time);
2310
        }
2311
2312
        return acceptable;
2313
}
2314
2315
static void folderview_drag_leave_cb(GtkWidget      *widget,
2316
                                     GdkDragContext *context,
2317
                                     guint           time,
2318
                                     FolderView     *folderview)
2319
{
2320
        folderview_defer_expand_stop(folderview);
2321
        gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2322
}
2323
2324
static void folderview_drag_received_cb(GtkWidget        *widget,
2325
                                        GdkDragContext   *context,
2326
                                        gint              x,
2327
                                        gint              y,
2328
                                        GtkSelectionData *data,
2329
                                        guint             info,
2330
                                        guint             time,
2331
                                        FolderView       *folderview)
2332
{
2333
        gint row, column;
2334
        FolderItem *item, *src_item;
2335
        GtkCTreeNode *node;
2336
2337
        folderview_defer_expand_stop(folderview);
2338
2339
        if (gtk_clist_get_selection_info
2340
                (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
2341
                return;
2342
2343
        node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2344
        item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2345
        src_item = folderview->summaryview->folder_item;
2346
        if (FOLDER_ITEM_CAN_ADD(item) && src_item && src_item != item) {
2347
                if ((context->actions & GDK_ACTION_MOVE) != 0) {
2348
                        summary_move_selected_to(folderview->summaryview, item);
2349
                        gtk_drag_finish(context, TRUE, TRUE, time);
2350
                } else if ((context->actions & GDK_ACTION_COPY) != 0) {
2351
                        summary_copy_selected_to(folderview->summaryview, item);
2352
                        gtk_drag_finish(context, TRUE, TRUE, time);
2353
                } else
2354
                        gtk_drag_finish(context, FALSE, FALSE, time);
2355
        } else
2356
                gtk_drag_finish(context, FALSE, FALSE, time);
2357
}
2358
2359
static gint folderview_clist_compare(GtkCList *clist,
2360
                                     gconstpointer ptr1, gconstpointer ptr2)
2361
{
2362
        FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2363
        FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2364
2365
        if (!item1->name)
2366
                return (item2->name != NULL);
2367
        if (!item2->name)
2368
                return -1;
2369
2370
        return g_strcasecmp(item1->name, item2->name);
2371
}