Statistics
| Revision:

root / src / foldersel.c @ 1

History | View | Annotate | Download (13.5 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/gtkmain.h>
25
#include <gtk/gtkwidget.h>
26
#include <gtk/gtkctree.h>
27
#include <gtk/gtkwindow.h>
28
#include <gtk/gtkvbox.h>
29
#include <gtk/gtkscrolledwindow.h>
30
#include <gtk/gtkentry.h>
31
#include <gtk/gtkhbbox.h>
32
#include <gtk/gtksignal.h>
33
#include <stdio.h>
34
#include <unistd.h>
35
#include <string.h>
36
#include <sys/stat.h>
37
#include <sys/types.h>
38
#include <fcntl.h>
39
#include <errno.h>
40

    
41
#include "intl.h"
42
#include "main.h"
43
#include "utils.h"
44
#include "gtkutils.h"
45
#include "stock_pixmap.h"
46
#include "foldersel.h"
47
#include "alertpanel.h"
48
#include "manage_window.h"
49
#include "folderview.h"
50
#include "inputdialog.h"
51
#include "folder.h"
52

    
53
static GdkPixmap *folderxpm;
54
static GdkBitmap *folderxpmmask;
55
static GdkPixmap *folderopenxpm;
56
static GdkBitmap *folderopenxpmmask;
57
static GdkPixmap *foldernoselectxpm;
58
static GdkBitmap *foldernoselectxpmmask;
59

    
60
static GtkWidget *window;
61
static GtkWidget *ctree;
62
static GtkWidget *entry;
63
static GtkWidget *ok_button;
64
static GtkWidget *cancel_button;
65
static GtkWidget *new_button;
66

    
67
static FolderItem *folder_item;
68
static FolderItem *selected_item;
69

    
70
static gboolean cancelled;
71
static gboolean finished;
72

    
73
static void foldersel_create        (void);
74
static void foldersel_init        (void);
75
static void foldersel_set_tree        (Folder                        *cur_folder,
76
                                 FolderSelectionType         type);
77

    
78
static void foldersel_selected        (GtkCList        *clist,
79
                                 gint                 row,
80
                                 gint                 column,
81
                                 GdkEvent        *event,
82
                                 gpointer         data);
83

    
84
static void foldersel_ok        (GtkButton        *button,
85
                                 gpointer         data);
86
static void foldersel_cancel        (GtkButton        *button,
87
                                 gpointer         data);
88
static void foldersel_new_folder(GtkButton        *button,
89
                                 gpointer         data);
90
static void foldersel_activated        (void);
91
static gint delete_event        (GtkWidget        *widget,
92
                                 GdkEventAny        *event,
93
                                 gpointer         data);
94
static gboolean key_pressed        (GtkWidget        *widget,
95
                                 GdkEventKey        *event,
96
                                 gpointer         data);
97

    
98
static gint foldersel_clist_compare        (GtkCList        *clist,
99
                                         gconstpointer         ptr1,
100
                                         gconstpointer         ptr2);
101

    
102
FolderItem *foldersel_folder_sel(Folder *cur_folder,
103
                                 FolderSelectionType type,
104
                                 const gchar *default_folder)
105
{
106
        GtkCTreeNode *node;
107

    
108
        selected_item = NULL;
109

    
110
        if (!window) {
111
                foldersel_create();
112
                foldersel_init();
113
        } else
114
                gtk_widget_show(window);
115
        manage_window_set_transient(GTK_WINDOW(window));
116

    
117
        foldersel_set_tree(cur_folder, type);
118

    
119
        if (folder_item) {
120
                node = gtk_ctree_find_by_row_data
121
                        (GTK_CTREE(ctree), NULL, folder_item);
122
                if (node) {
123
                        gint row;
124

    
125
                        row = gtkut_ctree_get_nth_from_node
126
                                (GTK_CTREE(ctree), node);
127
                        gtk_clist_select_row(GTK_CLIST(ctree), row, -1);
128
                        gtkut_clist_set_focus_row(GTK_CLIST(ctree), row);
129
                        gtk_ctree_node_moveto(GTK_CTREE(ctree), node, -1,
130
                                              0.5, 0);
131
                }
132
        }
133
        gtk_widget_grab_focus(ok_button);
134
        gtk_widget_grab_focus(ctree);
135

    
136
        cancelled = finished = FALSE;
137

    
138
        while (finished == FALSE)
139
                gtk_main_iteration();
140

    
141
        gtk_widget_hide(window);
142
        gtk_entry_set_text(GTK_ENTRY(entry), "");
143
        gtk_clist_clear(GTK_CLIST(ctree));
144

    
145
        if (!cancelled &&
146
            selected_item && selected_item->path && !selected_item->no_select) {
147
                folder_item = selected_item;
148
                return folder_item;
149
        } else
150
                return NULL;
151
}
152

    
153
static void foldersel_create(void)
154
{
155
        GtkWidget *vbox;
156
        GtkWidget *scrolledwin;
157
        GtkWidget *confirm_area;
158

    
159
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
160
        gtk_window_set_title(GTK_WINDOW(window), _("Select folder"));
161
        gtk_container_set_border_width(GTK_CONTAINER(window), 4);
162
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
163
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
164
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE);
165
        gtk_window_set_wmclass
166
                (GTK_WINDOW(window), "folder_selection", "Sylpheed");
167
        g_signal_connect(G_OBJECT(window), "delete_event",
168
                         G_CALLBACK(delete_event), NULL);
169
        g_signal_connect(G_OBJECT(window), "key_press_event",
170
                         G_CALLBACK(key_pressed), NULL);
171
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
172

    
173
        vbox = gtk_vbox_new(FALSE, 4);
174
        gtk_container_add(GTK_CONTAINER(window), vbox);
175

    
176
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
177
        gtk_widget_set_size_request(scrolledwin, 300, 360);
178
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
179
                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
180
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
181

    
182
        ctree = gtk_ctree_new(1, 0);
183
        gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
184
                                            GTK_CLIST(ctree)->vadjustment);
185
        gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
186
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
187
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
188
        gtk_ctree_set_expander_style(GTK_CTREE(ctree),
189
                                     GTK_CTREE_EXPANDER_SQUARE);
190
        gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
191
        gtk_clist_set_compare_func(GTK_CLIST(ctree), foldersel_clist_compare);
192
        GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[0].button,
193
                               GTK_CAN_FOCUS);
194
        /* g_signal_connect(G_OBJECT(ctree), "tree_select_row",
195
                            G_CALLBACK(foldersel_selected), NULL); */
196
        g_signal_connect(G_OBJECT(ctree), "select_row",
197
                         G_CALLBACK(foldersel_selected), NULL);
198

    
199
        entry = gtk_entry_new();
200
        gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
201
        gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
202
        g_signal_connect(G_OBJECT(entry), "activate",
203
                         G_CALLBACK(foldersel_activated), NULL);
204

    
205
        gtkut_button_set_create(&confirm_area,
206
                                &ok_button,        _("OK"),
207
                                &cancel_button,        _("Cancel"),
208
                                &new_button,    _("New folder"));
209

    
210
        gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
211
        gtk_widget_grab_default(ok_button);
212

    
213
        g_signal_connect(G_OBJECT(ok_button), "clicked",
214
                         G_CALLBACK(foldersel_ok), NULL);
215
        g_signal_connect(G_OBJECT(cancel_button), "clicked",
216
                         G_CALLBACK(foldersel_cancel), NULL);
217
        g_signal_connect(G_OBJECT(new_button), "clicked",
218
                         G_CALLBACK(foldersel_new_folder), NULL);
219

    
220
        gtk_widget_show_all(window);
221
}
222

    
223
static void foldersel_init(void)
224
{
225
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE,
226
                         &folderxpm, &folderxpmmask);
227
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN,
228
                         &folderopenxpm, &folderopenxpmmask);
229
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT,
230
                         &foldernoselectxpm, &foldernoselectxpmmask);
231
}
232

    
233
static gboolean foldersel_gnode_func(GtkCTree *ctree, guint depth,
234
                                     GNode *gnode, GtkCTreeNode *cnode,
235
                                     gpointer data)
236
{
237
        FolderItem *item = FOLDER_ITEM(gnode->data);
238
        gchar *name;
239
        GdkPixmap *xpm, *openxpm;
240
        GdkBitmap *mask, *openmask;
241

    
242
        switch (item->stype) {
243
        case F_INBOX:
244
                name = _("Inbox");
245
                break;
246
        case F_OUTBOX:
247
                name = _("Sent");
248
                break;
249
        case F_QUEUE:
250
                name = _("Queue");
251
                break;
252
        case F_TRASH:
253
                name = _("Trash");
254
                break;
255
        case F_DRAFT:
256
                name = _("Drafts");
257
                break;
258
        default:
259
                name = item->name;
260

    
261
                if (!item->parent) {
262
                        switch (FOLDER_TYPE(item->folder)) {
263
                        case F_MH:
264
                                Xstrcat_a(name, name, " (MH)", ); break;
265
                        case F_IMAP:
266
                                Xstrcat_a(name, name, " (IMAP4)", ); break;
267
                        case F_NEWS:
268
                                Xstrcat_a(name, name, " (News)", ); break;
269
                        default:
270
                                break;
271
                        }
272
                }
273
        }
274

    
275
        if (item->no_select) {
276
                GdkColor color_noselect = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM};
277
                xpm = openxpm = foldernoselectxpm;
278
                mask = openmask = foldernoselectxpmmask;
279
                gtk_ctree_node_set_foreground(ctree, cnode, &color_noselect);
280
        } else {
281
                xpm = folderxpm;
282
                mask = folderxpmmask;
283
                openxpm = folderopenxpm;
284
                openmask = folderopenxpmmask;
285
        }
286

    
287
        gtk_ctree_node_set_row_data(ctree, cnode, item);
288
        gtk_ctree_set_node_info(ctree, cnode, name,
289
                                FOLDER_SPACING,
290
                                xpm, mask, openxpm, openmask,
291
                                FALSE, FALSE);
292

    
293
        return TRUE;
294
}
295

    
296
static void foldersel_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
297
                                  gpointer data)
298
{
299
        if (GTK_CTREE_ROW(node)->children)
300
                gtk_ctree_expand(ctree, node);
301
}
302

    
303
#define SET_SPECIAL_FOLDER(item)                                           \
304
{                                                                           \
305
        if (item) {                                                           \
306
                GtkCTreeNode *node_, *parent, *sibling;                           \
307
                                                                           \
308
                node_ = gtk_ctree_find_by_row_data                           \
309
                        (GTK_CTREE(ctree), node, item);                           \
310
                if (!node_)                                                   \
311
                        g_warning("%s not found.\n", item->path);           \
312
                else {                                                           \
313
                        parent = GTK_CTREE_ROW(node_)->parent;                   \
314
                        if (prev && parent == GTK_CTREE_ROW(prev)->parent) \
315
                                sibling = GTK_CTREE_ROW(prev)->sibling;           \
316
                        else                                                   \
317
                                sibling = GTK_CTREE_ROW(parent)->children; \
318
                        if (node_ != sibling)                                   \
319
                                gtk_ctree_move(GTK_CTREE(ctree),           \
320
                                               node_, parent, sibling);           \
321
                }                                                           \
322
                                                                           \
323
                prev = node_;                                                   \
324
        }                                                                   \
325
}
326

    
327
static void foldersel_set_tree(Folder *cur_folder, FolderSelectionType type)
328
{
329
        Folder *folder;
330
        GtkCTreeNode *node;
331
        GList *list;
332

    
333
        list = folder_get_list();
334

    
335
        gtk_clist_freeze(GTK_CLIST(ctree));
336

    
337
        for (; list != NULL; list = list->next) {
338
                GtkCTreeNode *prev = NULL;
339

    
340
                folder = FOLDER(list->data);
341
                g_return_if_fail(folder != NULL);
342

    
343
                if (type != FOLDER_SEL_ALL) {
344
                        if (FOLDER_TYPE(folder) == F_NEWS)
345
                                continue;
346
                }
347

    
348
                node = gtk_ctree_insert_gnode(GTK_CTREE(ctree), NULL, NULL,
349
                                              folder->node,
350
                                              foldersel_gnode_func,
351
                                              NULL);
352
                gtk_ctree_sort_recursive(GTK_CTREE(ctree), node);
353
                SET_SPECIAL_FOLDER(folder->inbox);
354
                SET_SPECIAL_FOLDER(folder->outbox);
355
                SET_SPECIAL_FOLDER(folder->draft);
356
                SET_SPECIAL_FOLDER(folder->queue);
357
                SET_SPECIAL_FOLDER(folder->trash);
358
                gtk_ctree_pre_recursive(GTK_CTREE(ctree), node,
359
                                        foldersel_expand_func,
360
                                        NULL);
361
        }
362

    
363
        gtk_clist_thaw(GTK_CLIST(ctree));
364
}
365

    
366
static void foldersel_selected(GtkCList *clist, gint row, gint column,
367
                               GdkEvent *event, gpointer data)
368
{
369
        GdkEventButton *ev = (GdkEventButton *)event;
370

    
371
        selected_item = gtk_clist_get_row_data(clist, row);
372
        if (selected_item && selected_item->path && !selected_item->no_select) {
373
                gchar *id;
374
                id = folder_item_get_identifier(selected_item);
375
                gtk_entry_set_text(GTK_ENTRY(entry), id);
376
                g_free(id);
377
        } else
378
                gtk_entry_set_text(GTK_ENTRY(entry), "");
379

    
380
        if (ev && GDK_2BUTTON_PRESS == ev->type)
381
                gtk_button_clicked(GTK_BUTTON(ok_button));
382
}
383

    
384
static void foldersel_ok(GtkButton *button, gpointer data)
385
{
386
        finished = TRUE;
387
}
388

    
389
static void foldersel_cancel(GtkButton *button, gpointer data)
390
{
391
        cancelled = TRUE;
392
        finished = TRUE;
393
}
394

    
395
static void foldersel_new_folder(GtkButton *button, gpointer data)
396
{
397
        FolderItem *new_item;
398
        gchar *new_folder;
399
        gchar *disp_name;
400
        gchar *p;
401
        gchar *text[1] = {NULL};
402
        GtkCTreeNode *selected_node;
403
        GtkCTreeNode *node;
404
        gint row;
405

    
406
        if (!selected_item || FOLDER_TYPE(selected_item->folder) == F_NEWS)
407
                return;
408
        selected_node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL,
409
                                                   selected_item);
410
        if (!selected_node) return;
411

    
412
        new_folder = input_dialog(_("New folder"),
413
                                  _("Input the name of new folder:"),
414
                                  _("NewFolder"));
415
        if (!new_folder) return;
416
        AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
417

    
418
        p = strchr(new_folder, G_DIR_SEPARATOR);
419
        if ((p && FOLDER_TYPE(selected_item->folder) != F_IMAP) ||
420
            (p && FOLDER_TYPE(selected_item->folder) == F_IMAP &&
421
             *(p + 1) != '\0')) {
422
                alertpanel_error(_("`%c' can't be included in folder name."),
423
                                G_DIR_SEPARATOR);
424
                return;
425
        }
426

    
427
        disp_name = trim_string(new_folder, 32);
428
        AUTORELEASE_STR(disp_name, {g_free(disp_name); return;});
429

    
430
        /* find whether the directory already exists */
431
        if (folder_find_child_item_by_name(selected_item, new_folder)) {
432
                alertpanel_error(_("The folder `%s' already exists."),
433
                                 disp_name);
434
                return;
435
        }
436

    
437
        new_item = selected_item->folder->klass->create_folder
438
                (selected_item->folder, selected_item, new_folder);
439
        if (!new_item) {
440
                alertpanel_error(_("Can't create the folder `%s'."), disp_name);
441
                return;
442
        }
443

    
444
        text[0] = new_item->name;
445
        node = gtk_ctree_insert_node(GTK_CTREE(ctree), selected_node,
446
                                     NULL, text, FOLDER_SPACING,
447
                                     folderxpm, folderxpmmask,
448
                                     folderopenxpm, folderopenxpmmask,
449
                                     FALSE, FALSE);
450
        gtk_ctree_expand(GTK_CTREE(ctree), selected_node);
451
        gtk_ctree_node_set_row_data(GTK_CTREE(ctree), node, new_item);
452
        gtk_ctree_sort_recursive(GTK_CTREE(ctree), selected_node);
453

    
454
        row = gtkut_ctree_get_nth_from_node(GTK_CTREE(ctree), node);
455
        gtk_clist_select_row(GTK_CLIST(ctree), row, -1);
456
        gtkut_clist_set_focus_row(GTK_CLIST(ctree), row);
457
        gtk_ctree_node_moveto(GTK_CTREE(ctree), node, -1, 0.5, 0);
458

    
459
        folderview_append_item(new_item);
460
        folder_write_list();
461
}
462

    
463
static void foldersel_activated(void)
464
{
465
        gtk_button_clicked(GTK_BUTTON(ok_button));
466
}
467

    
468
static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
469
{
470
        foldersel_cancel(NULL, NULL);
471
        return TRUE;
472
}
473

    
474
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
475
{
476
        if (event && event->keyval == GDK_Escape)
477
                foldersel_cancel(NULL, NULL);
478
        return FALSE;
479
}
480

    
481
static gint foldersel_clist_compare(GtkCList *clist,
482
                                    gconstpointer ptr1, gconstpointer ptr2)
483
{
484
        FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
485
        FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
486

    
487
        if (!item1->name)
488
                return (item2->name != NULL);
489
        if (!item2->name)
490
                return -1;
491

    
492
        return g_strcasecmp(item1->name, item2->name);
493
}