2006-09-06 [wwp] 2.4.0cvs154
[claws.git] / src / prefs_folder_column.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto & the Sylpheed-Claws team
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkwindow.h>
30 #include <gtk/gtkvbox.h>
31 #include <gtk/gtkhbox.h>
32 #include <gtk/gtkbutton.h>
33 #include <gdk/gdkkeysyms.h>
34
35 #include "prefs_gtk.h"
36 #include "prefs_common.h"
37 #include "prefs_folder_column.h"
38 #include "manage_window.h"
39 #include "folderview.h"
40 #include "mainwindow.h"
41 #include "inc.h"
42 #include "gtkutils.h"
43 #include "utils.h"
44
45 enum {
46         SUMCOL_NAME,
47         SUMCOL_TYPE,
48         N_SUMCOL_COLUMNS
49 };
50
51 #define TARGET_INFO_SUMCOL  (0xFEEDBABE)
52
53 static const GtkTargetEntry row_targets[] = {
54         { "PREFS_SUM_COL_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_INFO_SUMCOL }
55 };
56
57
58 static struct _FolderColumnDialog
59 {
60         GtkWidget *window;
61
62         GtkWidget *stock_list_view;
63         GtkWidget *shown_list_view;
64
65         GtkWidget *add_btn;
66         GtkWidget *remove_btn;
67         GtkWidget *up_btn;
68         GtkWidget *down_btn;
69
70         GtkWidget *default_btn;
71
72         GtkWidget *ok_btn;
73         GtkWidget *cancel_btn;
74
75         gboolean finished;
76 } folder_col;
77
78 static const gchar *const col_name[N_FOLDER_COLS] = {
79         N_("Folder"),           /* F_COL_FOLDER  */
80         N_("New"),              /* F_COL_NEW     */
81         N_("Unread"),           /* F_COL_UNREAD  */
82         N_("Total"),            /* F_COL_TOTAL   */
83 };
84
85 static FolderColumnState default_state[N_FOLDER_COLS] = {
86         { F_COL_FOLDER   , TRUE  },
87         { F_COL_NEW      , TRUE  },
88         { F_COL_UNREAD   , TRUE  },
89         { F_COL_TOTAL    , TRUE  },
90 };
91
92 static void prefs_folder_column_create  (void);
93
94 static void prefs_folder_column_set_dialog      (FolderColumnState *state);
95 static void prefs_folder_column_set_view        (void);
96
97 /* callback functions */
98 static void prefs_folder_column_add     (void);
99 static void prefs_folder_column_remove  (void);
100
101 static void prefs_folder_column_up      (void);
102 static void prefs_folder_column_down    (void);
103
104 static void prefs_folder_column_set_to_default  (void);
105
106 static void prefs_folder_column_ok      (void);
107 static void prefs_folder_column_cancel  (void);
108
109 static gint prefs_folder_column_delete_event    (GtkWidget      *widget,
110                                                  GdkEventAny    *event,
111                                                  gpointer        data);
112 static gboolean prefs_folder_column_key_pressed(GtkWidget       *widget,
113                                                  GdkEventKey    *event,
114                                                  gpointer        data);
115
116 static GtkListStore *prefs_folder_column_create_store   (void);
117
118 static void prefs_folder_column_insert_column   (GtkListStore *store,
119                                                  gint row,
120                                                  const gchar *name,
121                                                  FolderColumnType type);
122                                                
123 static FolderColumnType prefs_folder_column_get_column  (GtkWidget *list, 
124                                                                  gint row);
125
126 static GtkWidget *prefs_folder_column_list_view_create  (const gchar *name);
127
128 static void prefs_filtering_create_list_view_columns    (GtkWidget *list_view, 
129                                                          const gchar *name);
130
131 static void drag_data_get       (GtkTreeView *tree_view, 
132                                  GdkDragContext *context, 
133                                  GtkSelectionData *data, 
134                                  guint info, 
135                                  guint time, 
136                                  GtkTreeModel *model);
137                           
138 static void drag_data_received  (GtkTreeView *tree_view, 
139                                  GdkDragContext *context,
140                                  gint x, gint y, 
141                                  GtkSelectionData *data,
142                                  guint info, 
143                                  guint time, 
144                                  GtkTreeModel *model);
145
146 void prefs_folder_column_open(void)
147 {
148         inc_lock();
149
150         if (!folder_col.window)
151                 prefs_folder_column_create();
152
153         manage_window_set_transient(GTK_WINDOW(folder_col.window));
154         gtk_widget_grab_focus(folder_col.ok_btn);
155
156         prefs_folder_column_set_dialog(NULL);
157
158         gtk_widget_show(folder_col.window);
159
160         folder_col.finished = FALSE;
161         while (folder_col.finished == FALSE)
162                 gtk_main_iteration();
163
164         gtk_widget_hide(folder_col.window);
165
166         inc_unlock();
167 }
168
169 static void prefs_folder_column_create(void)
170 {
171         GtkWidget *window;
172         GtkWidget *vbox;
173
174         GtkWidget *label_hbox;
175         GtkWidget *label;
176
177         GtkWidget *vbox1;
178
179         GtkWidget *hbox1;
180         GtkWidget *clist_hbox;
181         GtkWidget *scrolledwin;
182         GtkWidget *stock_list_view;
183         GtkWidget *shown_list_view;
184
185         GtkWidget *btn_vbox;
186         GtkWidget *add_btn;
187         GtkWidget *remove_btn;
188         GtkWidget *up_btn;
189         GtkWidget *down_btn;
190
191         GtkWidget *btn_hbox;
192         GtkWidget *default_btn;
193         GtkWidget *confirm_area;
194         GtkWidget *ok_btn;
195         GtkWidget *cancel_btn;
196
197         debug_print("Creating folder column setting window...\n");
198
199         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
200         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
201         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
202         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
203         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
204         gtk_window_set_title(GTK_WINDOW(window),
205                              _("Folder list columns configuration"));
206         g_signal_connect(G_OBJECT(window), "delete_event",
207                          G_CALLBACK(prefs_folder_column_delete_event),
208                          NULL);
209         g_signal_connect(G_OBJECT(window), "key_press_event",
210                          G_CALLBACK(prefs_folder_column_key_pressed),
211                          NULL);
212
213         vbox = gtk_vbox_new(FALSE, 6);
214         gtk_widget_show(vbox);
215         gtk_container_add(GTK_CONTAINER(window), vbox);
216
217         label_hbox = gtk_hbox_new(FALSE, 0);
218         gtk_widget_show(label_hbox);
219         gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4);
220
221         label = gtk_label_new
222                 (_("Select columns to be displayed in the folder list. You can modify\n"
223                    "the order by using the Up / Down buttons or by dragging the items."));
224         gtk_widget_show(label);
225         gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4);
226         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
227
228         vbox1 = gtk_vbox_new(FALSE, VSPACING);
229         gtk_widget_show(vbox1);
230         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
231         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
232
233         hbox1 = gtk_hbox_new(FALSE, 8);
234         gtk_widget_show(hbox1);
235         gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
236
237         clist_hbox = gtk_hbox_new(FALSE, 8);
238         gtk_widget_show(clist_hbox);
239         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
240
241         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
242         gtk_widget_set_size_request(scrolledwin, 180, 210);
243         gtk_widget_show(scrolledwin);
244         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
245         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
246                                        GTK_POLICY_AUTOMATIC,
247                                        GTK_POLICY_AUTOMATIC);
248
249                                        
250         stock_list_view = prefs_folder_column_list_view_create
251                                 (_("Hidden columns"));
252         gtk_widget_show(stock_list_view);
253         gtk_container_add(GTK_CONTAINER(scrolledwin), stock_list_view);
254
255         /* add/remove button */
256         btn_vbox = gtk_vbox_new(FALSE, 8);
257         gtk_widget_show(btn_vbox);
258         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
259
260         add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
261         gtk_widget_show(add_btn);
262         gtk_box_pack_start(GTK_BOX(btn_vbox), add_btn, FALSE, TRUE, 0);
263
264         g_signal_connect(G_OBJECT(add_btn), "clicked",
265                          G_CALLBACK(prefs_folder_column_add), NULL);
266
267         clist_hbox = gtk_hbox_new(FALSE, 8);
268         gtk_widget_show(clist_hbox);
269         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
270
271         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
272         gtk_widget_set_size_request(scrolledwin, 180, 210);
273         gtk_widget_show(scrolledwin);
274         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
275         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
276                                        GTK_POLICY_AUTOMATIC,
277                                        GTK_POLICY_AUTOMATIC);
278
279         shown_list_view = prefs_folder_column_list_view_create
280                                 (_("Displayed columns"));
281         gtk_widget_show(shown_list_view);
282         gtk_container_add(GTK_CONTAINER(scrolledwin), shown_list_view);
283
284         /* up/down button */
285         btn_vbox = gtk_vbox_new(FALSE, 8);
286         gtk_widget_show(btn_vbox);
287         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
288
289         remove_btn = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
290         gtk_widget_show(remove_btn);
291         gtk_box_pack_start(GTK_BOX(btn_vbox), remove_btn, FALSE, TRUE, 0);
292
293         up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
294         gtk_widget_show(up_btn);
295         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, TRUE, 0);
296
297         down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
298         gtk_widget_show(down_btn);
299         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, TRUE, 0);
300
301         g_signal_connect(G_OBJECT(remove_btn), "clicked",
302                          G_CALLBACK(prefs_folder_column_remove), NULL);
303         g_signal_connect(G_OBJECT(up_btn), "clicked",
304                          G_CALLBACK(prefs_folder_column_up), NULL);
305         g_signal_connect(G_OBJECT(down_btn), "clicked",
306                          G_CALLBACK(prefs_folder_column_down), NULL);
307
308         btn_hbox = gtk_hbox_new(FALSE, 8);
309         gtk_widget_show(btn_hbox);
310         gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0);
311
312         btn_vbox = gtk_vbox_new(FALSE, 0);
313         gtk_widget_show(btn_vbox);
314         gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0);
315
316         default_btn = gtk_button_new_with_label(_(" Use default "));
317         gtk_widget_show(default_btn);
318         gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0);
319         g_signal_connect(G_OBJECT(default_btn), "clicked",
320                          G_CALLBACK(prefs_folder_column_set_to_default),
321                          NULL);
322
323         gtkut_stock_button_set_create(&confirm_area,
324                                       &cancel_btn, GTK_STOCK_CANCEL,
325                                       &ok_btn, GTK_STOCK_OK,
326                                       NULL, NULL);
327         gtk_widget_show(confirm_area);
328         gtk_box_pack_end(GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0);
329         gtk_widget_grab_default(ok_btn);
330
331         g_signal_connect(G_OBJECT(ok_btn), "clicked",
332                          G_CALLBACK(prefs_folder_column_ok), NULL);
333         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
334                          G_CALLBACK(prefs_folder_column_cancel), NULL);
335
336
337         folder_col.window      = window;
338         folder_col.add_btn     = add_btn;
339         folder_col.remove_btn  = remove_btn;
340         folder_col.up_btn      = up_btn;
341         folder_col.down_btn    = down_btn;
342         folder_col.ok_btn      = ok_btn;
343         folder_col.cancel_btn  = cancel_btn;
344         folder_col.stock_list_view = stock_list_view;
345         folder_col.shown_list_view = shown_list_view;
346 }
347
348 FolderColumnState *prefs_folder_column_get_config(void)
349 {
350         static FolderColumnState state[N_FOLDER_COLS];
351         FolderColumnType type;
352         gint pos;
353
354         for (pos = 0; pos < N_FOLDER_COLS; pos++)
355                 state[pos].type = -1;
356
357         for (type = 0; type < N_FOLDER_COLS; type++) {
358                 pos = prefs_common.folder_col_pos[type];
359                 if (pos < 0 || pos >= N_FOLDER_COLS ||
360                     state[pos].type != -1) {
361                         g_warning("Wrong column position\n");
362                         prefs_folder_column_set_config(default_state);
363                         return default_state;
364                 }
365
366                 state[pos].type = type;
367                 state[pos].visible = prefs_common.folder_col_visible[type];
368         }
369
370         return state;
371 }
372
373 void prefs_folder_column_set_config(FolderColumnState *state)
374 {
375         FolderColumnType type;
376         gint pos;
377
378         for (pos = 0; pos < N_FOLDER_COLS; pos++) {
379                 type = state[pos].type;
380                 prefs_common.folder_col_visible[type] = state[pos].visible;
381                 prefs_common.folder_col_pos[type] = pos;
382         }
383 }
384
385 static void prefs_folder_column_set_dialog(FolderColumnState *state)
386 {
387         GtkListStore *stock_store, *shown_store;
388         gint pos;
389         FolderColumnType type;
390         gchar *name;
391
392         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
393                         (GTK_TREE_VIEW(folder_col.stock_list_view)));
394         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
395                         (GTK_TREE_VIEW(folder_col.shown_list_view)));
396
397         gtk_list_store_clear(stock_store);
398         gtk_list_store_clear(shown_store);
399
400         if (!state)
401                 state = prefs_folder_column_get_config();
402
403         for (pos = 0; pos < N_FOLDER_COLS; pos++) {
404                 type = state[pos].type;
405                 name = gettext(col_name[type]);
406
407                 if (state[pos].visible)
408                         prefs_folder_column_insert_column(shown_store,
409                                                            -1, name,
410                                                            type);
411                 else
412                         prefs_folder_column_insert_column(stock_store,
413                                                             -1, name,
414                                                             type);
415         }
416 }
417
418 static void prefs_folder_column_set_view(void)
419 {
420         gint stock_n_rows, shown_n_rows;
421         FolderColumnState state[N_FOLDER_COLS];
422         FolderColumnType type;
423         gint row, pos = 0;
424
425         stock_n_rows = gtk_tree_model_iter_n_children
426                 (gtk_tree_view_get_model(GTK_TREE_VIEW
427                         (folder_col.stock_list_view)), NULL);
428         shown_n_rows = gtk_tree_model_iter_n_children
429                 (gtk_tree_view_get_model(GTK_TREE_VIEW
430                         (folder_col.shown_list_view)), NULL);
431
432         g_return_if_fail
433                 (stock_n_rows + shown_n_rows == N_FOLDER_COLS);
434
435         for (row = 0; row < stock_n_rows; row++) {
436                 type = prefs_folder_column_get_column
437                         (folder_col.stock_list_view, row);
438                 state[row].type = type;
439                 state[row].visible = FALSE;
440         }
441
442         pos = row;
443         for (row = 0; row < shown_n_rows; row++) {
444                 type = prefs_folder_column_get_column
445                         (folder_col.shown_list_view, row);
446                 state[pos + row].type = type;
447                 state[pos + row].visible = TRUE;
448         }
449
450         prefs_folder_column_set_config(state);
451         main_window_set_folder_column();
452 }
453
454 static void prefs_folder_column_add(void)
455 {
456         GtkListStore *stock_store, *shown_store;
457         GtkTreeIter stock_sel, shown_sel, shown_add;
458         gboolean shown_sel_valid;
459         gchar *name;
460         FolderColumnType type;
461         
462         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
463                 (GTK_TREE_VIEW(folder_col.stock_list_view)));
464         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
465                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
466         
467         if (!gtk_tree_selection_get_selected
468                 (gtk_tree_view_get_selection
469                         (GTK_TREE_VIEW(folder_col.stock_list_view)),
470                  NULL,
471                  &stock_sel))
472                 return;
473
474         shown_sel_valid = gtk_tree_selection_get_selected
475                 (gtk_tree_view_get_selection
476                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
477                  NULL,
478                  &shown_sel);
479                          
480         gtk_tree_model_get(GTK_TREE_MODEL(stock_store), &stock_sel,
481                            SUMCOL_TYPE, &type,
482                            -1);
483                         
484         gtk_list_store_remove(stock_store, &stock_sel);
485
486         gtk_list_store_insert_after(shown_store, &shown_add, 
487                                     shown_sel_valid ? &shown_sel : NULL);
488
489         name = gettext(col_name[type]);                             
490                                     
491         gtk_list_store_set(shown_store, &shown_add,
492                            SUMCOL_NAME, name,
493                            SUMCOL_TYPE, type,
494                            -1);
495         
496         gtk_tree_selection_select_iter(gtk_tree_view_get_selection
497                 (GTK_TREE_VIEW(folder_col.shown_list_view)),
498                  &shown_add);
499 }
500
501 static void prefs_folder_column_remove(void)
502 {
503         GtkListStore *stock_store, *shown_store;
504         GtkTreeIter shown_sel, stock_sel, stock_add;
505         gboolean stock_sel_valid;
506         gchar *name;
507         FolderColumnType type;
508         
509         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
510                 (GTK_TREE_VIEW(folder_col.stock_list_view)));
511         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
512                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
513                 
514         if (!gtk_tree_selection_get_selected
515                 (gtk_tree_view_get_selection
516                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
517                  NULL,
518                  &shown_sel))
519                 return;
520
521         stock_sel_valid = gtk_tree_selection_get_selected
522                 (gtk_tree_view_get_selection
523                         (GTK_TREE_VIEW(folder_col.stock_list_view)),
524                  NULL,
525                  &stock_sel);
526         
527         gtk_tree_model_get(GTK_TREE_MODEL(shown_store), &shown_sel,
528                            SUMCOL_TYPE, &type,
529                            -1);
530                         
531         gtk_list_store_remove(shown_store, &shown_sel);
532
533         gtk_list_store_insert_after(stock_store, &stock_add, 
534                                     stock_sel_valid ? &stock_sel : NULL);
535
536         name = gettext(col_name[type]);                             
537                                     
538         gtk_list_store_set(stock_store, &stock_add,
539                            SUMCOL_NAME, name,
540                            SUMCOL_TYPE, type,
541                            -1);
542         
543         gtk_tree_selection_select_iter(gtk_tree_view_get_selection
544                 (GTK_TREE_VIEW(folder_col.stock_list_view)),
545                 &stock_add);
546 }
547
548 static void prefs_folder_column_up(void)
549 {
550         GtkTreePath *prev, *sel;
551         GtkTreeIter isel;
552         GtkListStore *shown_store;
553         GtkTreeIter iprev;
554         
555         if (!gtk_tree_selection_get_selected
556                 (gtk_tree_view_get_selection
557                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
558                  NULL,
559                  &isel))
560                 return;
561
562         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
563                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
564
565         sel = gtk_tree_model_get_path(GTK_TREE_MODEL(shown_store), 
566                                       &isel);
567         if (!sel)
568                 return;
569
570         prev = gtk_tree_path_copy(sel);         
571         if (!gtk_tree_path_prev(prev)) {
572                 gtk_tree_path_free(prev);
573                 gtk_tree_path_free(sel);
574                 return;
575         }
576
577         gtk_tree_model_get_iter(GTK_TREE_MODEL(shown_store),
578                                 &iprev, prev);
579         gtk_tree_path_free(sel);
580         gtk_tree_path_free(prev);
581
582         gtk_list_store_swap(shown_store, &iprev, &isel);
583 }
584
585 static void prefs_folder_column_down(void)
586 {
587         GtkListStore *shown_store;
588         GtkTreeIter next, sel;
589         
590         if (!gtk_tree_selection_get_selected
591                 (gtk_tree_view_get_selection
592                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
593                  NULL,
594                  &sel))
595                 return;
596
597         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
598                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
599
600         next = sel;
601         if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(shown_store), &next)) 
602                 return;
603
604         gtk_list_store_swap(shown_store, &next, &sel);
605 }
606
607 static void prefs_folder_column_set_to_default(void)
608 {
609         prefs_folder_column_set_dialog(default_state);
610 }
611
612 static void prefs_folder_column_ok(void)
613 {
614         if (!folder_col.finished) {
615                 folder_col.finished = TRUE;
616                 prefs_folder_column_set_view();
617         }
618 }
619
620 static void prefs_folder_column_cancel(void)
621 {
622         folder_col.finished = TRUE;
623 }
624
625 static gint prefs_folder_column_delete_event(GtkWidget *widget,
626                                               GdkEventAny *event,
627                                               gpointer data)
628 {
629         folder_col.finished = TRUE;
630         return TRUE;
631 }
632
633 static gboolean prefs_folder_column_key_pressed(GtkWidget *widget,
634                                                  GdkEventKey *event,
635                                                  gpointer data)
636 {
637         if (event && event->keyval == GDK_Escape)
638                 folder_col.finished = TRUE;
639         return FALSE;
640 }
641
642 static GtkListStore *prefs_folder_column_create_store(void)
643 {
644         return gtk_list_store_new(N_SUMCOL_COLUMNS,
645                                   G_TYPE_STRING,
646                                   G_TYPE_INT,
647                                   -1);
648 }
649
650 static void prefs_folder_column_insert_column(GtkListStore *store,
651                                                gint row,
652                                                const gchar *name,
653                                                FolderColumnType type)
654 {
655         GtkTreeIter iter;
656
657         if (row >= 0) {
658                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store),
659                                                    &iter, NULL, row))
660                         row = -1;                                                  
661         }
662         if (row < 0) {
663                 /* add new */
664                 gtk_list_store_append(store, &iter);
665                 gtk_list_store_set(store, &iter,
666                                    SUMCOL_NAME, name,
667                                    SUMCOL_TYPE, type,
668                                    -1);
669                 return;
670         } else {
671                 /* change existing */
672                 gtk_list_store_set(store, &iter, 
673                                    SUMCOL_NAME, name,
674                                    SUMCOL_TYPE, type,
675                                    -1);
676         }
677 }
678
679 /*!
680  *\brief        Return the columnn type for a row
681  */
682 static FolderColumnType prefs_folder_column_get_column(GtkWidget *list, gint row)
683 {       
684         GtkTreeView *list_view = GTK_TREE_VIEW(list);
685         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
686         GtkTreeIter iter;
687         FolderColumnType result;
688
689         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
690                 return -1;
691         
692         gtk_tree_model_get(model, &iter, 
693                            SUMCOL_TYPE, &result,
694                            -1);
695         
696         return result;
697 }
698
699 static GtkWidget *prefs_folder_column_list_view_create(const gchar *name)
700 {
701         GtkWidget *list_view;
702         GtkTreeSelection *selector;
703         GtkTreeModel *model;
704
705         model = GTK_TREE_MODEL(prefs_folder_column_create_store());
706         list_view = gtk_tree_view_new_with_model(model);
707         g_object_unref(G_OBJECT(model));
708         
709         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view),
710                                      prefs_common.use_stripes_everywhere);
711         
712         selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
713         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
714
715         prefs_filtering_create_list_view_columns(GTK_WIDGET(list_view), name);
716
717         gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(list_view),
718                                                GDK_BUTTON1_MASK,
719                                                row_targets,
720                                                G_N_ELEMENTS(row_targets), 
721                                                GDK_ACTION_MOVE);
722                                             
723         gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(list_view), 
724                                              row_targets, 
725                                              G_N_ELEMENTS(row_targets), 
726                                              GDK_ACTION_MOVE);
727                 
728         g_signal_connect(G_OBJECT(list_view), "drag_data_get",
729                          G_CALLBACK(drag_data_get),
730                          model);
731
732         g_signal_connect(G_OBJECT(list_view), "drag_data_received",
733                          G_CALLBACK(drag_data_received),
734                          model);
735
736         return list_view;
737 }
738
739 static void prefs_filtering_create_list_view_columns(GtkWidget *list_view, 
740                                                      const gchar *name)
741 {
742         GtkTreeViewColumn *column;
743         GtkCellRenderer *renderer;
744
745         renderer = gtk_cell_renderer_text_new();
746         column = gtk_tree_view_column_new_with_attributes
747                 (name, renderer, "text", SUMCOL_NAME, NULL);
748         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
749 }
750
751 static void drag_data_get(GtkTreeView *tree_view, GdkDragContext *context, 
752                           GtkSelectionData *data, guint info, 
753                           guint time, GtkTreeModel *model)
754 {
755         GtkTreeIter iter;
756         FolderColumnType type;
757         GtkTreeModel *source_model;
758
759         if (info != TARGET_INFO_SUMCOL) 
760                 return;
761
762         if (!gtk_tree_selection_get_selected
763                         (gtk_tree_view_get_selection(tree_view),
764                          &source_model, &iter)) 
765                 return;                  
766         
767         gtk_tree_model_get(source_model, &iter, 
768                            SUMCOL_TYPE, &type,
769                            -1);
770
771         /* send the type */
772         gtk_selection_data_set(data, data->target, 8, (gchar *) &type, sizeof type);
773 }
774
775 static void drag_data_received(GtkTreeView *tree_view, GdkDragContext *context,
776                                gint x, gint y, GtkSelectionData *data,
777                                guint info, guint time, GtkTreeModel *model)
778 {
779         GtkWidget *source;
780         GtkTreePath *dst = NULL, *sel = NULL;
781         GtkTreeIter isel, idst;
782         GtkTreeViewDropPosition pos;
783         FolderColumnType type;
784         GtkTreeModel *sel_model;
785         gchar *name;
786         
787         source = gtk_drag_get_source_widget(context);
788         
789         if (source == GTK_WIDGET(tree_view)) {
790         
791                 /*
792                  * Same widget: re-order
793                  */
794                  
795                 gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
796                                            NULL, &isel);
797                 sel = gtk_tree_model_get_path(model, &isel);
798                 gtk_tree_view_get_dest_row_at_pos(tree_view, x, y,
799                                                   &dst, &pos);
800
801                 /* NOTE: dst is invalid if selection beyond last row, in that
802                  * case move beyond last one (XXX_move_before(..., NULL)) */                                              
803
804                 if (dst)                                                  
805                         gtk_tree_model_get_iter(model, &idst, dst);
806                 else 
807                         gtk_list_store_move_before(GTK_LIST_STORE(model),
808                                                    &isel,
809                                                    NULL);
810
811                 /* we do not drag if no valid dst and sel, and when
812                  * dst and sel are the same (moving after or before
813                  * itself doesn't change order...) */
814                 if ((dst && sel) && gtk_tree_path_compare(sel, dst) != 0) {
815                         if (pos == GTK_TREE_VIEW_DROP_BEFORE
816                         ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
817                                 gtk_list_store_move_before(GTK_LIST_STORE(model),
818                                                            &isel,
819                                                            &idst);
820                         else
821                                 gtk_list_store_move_after(GTK_LIST_STORE(model),
822                                                           &isel,
823                                                           &idst);
824                         
825                 } 
826                 gtk_tree_path_free(dst);                                          
827                 gtk_tree_path_free(sel);
828                 gtk_drag_finish(context, TRUE, FALSE, time);
829                 
830         } else if (source == folder_col.stock_list_view 
831         ||         source == folder_col.shown_list_view) {
832         
833                 /*
834                  * Other widget: change and update
835                  */
836
837                 
838                 /* get source information and remove */
839                 gtk_tree_selection_get_selected(gtk_tree_view_get_selection(
840                                                 GTK_TREE_VIEW(source)),
841                                                 &sel_model, &isel);
842                 type = *((gint *) data->data);
843                 name = gettext(col_name[type]);
844                 gtk_list_store_remove(GTK_LIST_STORE(sel_model), &isel);
845
846                 /* get insertion position */
847                 gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dst, &pos);
848
849                 /* NOTE: dst is invalid if insertion point beyond last row, 
850                  * just append to list in that case (XXX_store_append()) */
851
852                 if (dst) {
853                         gtk_tree_model_get_iter(model, &idst, dst);
854
855                         if (pos == GTK_TREE_VIEW_DROP_BEFORE
856                         ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
857                                 gtk_list_store_insert_before(GTK_LIST_STORE(model),
858                                                              &isel,
859                                                              &idst);
860                         else
861                                 gtk_list_store_insert_after(GTK_LIST_STORE(model),
862                                                             &isel,
863                                                             &idst);
864                 } else
865                         gtk_list_store_append(GTK_LIST_STORE(model),
866                                               &isel);
867                 
868                 gtk_list_store_set(GTK_LIST_STORE(model), &isel,
869                                    SUMCOL_NAME, name,
870                                    SUMCOL_TYPE, type, -1);
871                 gtk_tree_path_free(dst);
872                 gtk_drag_finish(context, TRUE, FALSE, time);
873         }
874
875         /* XXXX: should we call gtk_drag_finish() for other code paths? */
876 }
877