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