4ef90f5ea7c11f2d203fe4d112939cb62611c769
[claws.git] / src / prefs_folder_column.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2017 Hiroyuki Yamamoto & the Claws Mail 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 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_title(GTK_WINDOW(window),
211                              _("Folder list columns configuration"));
212         g_signal_connect(G_OBJECT(window), "delete_event",
213                          G_CALLBACK(prefs_folder_column_delete_event),
214                          NULL);
215         g_signal_connect(G_OBJECT(window), "key_press_event",
216                          G_CALLBACK(prefs_folder_column_key_pressed),
217                          NULL);
218
219         vbox = gtk_vbox_new(FALSE, 6);
220         gtk_widget_show(vbox);
221         gtk_container_add(GTK_CONTAINER(window), vbox);
222
223         label_hbox = gtk_hbox_new(FALSE, 0);
224         gtk_widget_show(label_hbox);
225         gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4);
226
227         label = gtk_label_new
228                 (_("Select columns to be displayed in the folder list. You can modify\n"
229                    "the order by using the Up / Down buttons or by dragging the items."));
230         gtk_widget_show(label);
231         gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4);
232         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
233
234         vbox1 = gtk_vbox_new(FALSE, VSPACING);
235         gtk_widget_show(vbox1);
236         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
237         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
238
239         hbox1 = gtk_hbox_new(FALSE, 8);
240         gtk_widget_show(hbox1);
241         gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
242
243         clist_hbox = gtk_hbox_new(FALSE, 8);
244         gtk_widget_show(clist_hbox);
245         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
246
247         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
248         gtk_widget_set_size_request(scrolledwin, 180, 210);
249         gtk_widget_show(scrolledwin);
250         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
251         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
252                                        GTK_POLICY_AUTOMATIC,
253                                        GTK_POLICY_AUTOMATIC);
254         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
255                                            GTK_SHADOW_IN);
256
257         stock_list_view = prefs_folder_column_list_view_create
258                                 (_("Hidden columns"));
259         g_signal_connect(G_OBJECT(stock_list_view), "cursor-changed",
260                            G_CALLBACK(prefs_folder_column_stock_sel_changed),
261                            NULL);
262         gtk_widget_show(stock_list_view);
263         gtk_container_add(GTK_CONTAINER(scrolledwin), stock_list_view);
264
265         /* add/remove button */
266         btn_vbox = gtk_vbox_new(FALSE, 8);
267         gtk_widget_show(btn_vbox);
268         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
269
270         add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
271         gtk_widget_show(add_btn);
272         gtk_box_pack_start(GTK_BOX(btn_vbox), add_btn, FALSE, TRUE, 0);
273
274         g_signal_connect(G_OBJECT(add_btn), "clicked",
275                          G_CALLBACK(prefs_folder_column_add), NULL);
276
277         remove_btn = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
278         gtk_widget_show(remove_btn);
279         gtk_box_pack_start(GTK_BOX(btn_vbox), remove_btn, FALSE, TRUE, 0);
280
281         g_signal_connect(G_OBJECT(remove_btn), "clicked",
282                          G_CALLBACK(prefs_folder_column_remove), NULL);
283
284         clist_hbox = gtk_hbox_new(FALSE, 8);
285         gtk_widget_show(clist_hbox);
286         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
287
288         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
289         gtk_widget_set_size_request(scrolledwin, 180, 210);
290         gtk_widget_show(scrolledwin);
291         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
292         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
293                                        GTK_POLICY_AUTOMATIC,
294                                        GTK_POLICY_AUTOMATIC);
295         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
296                                            GTK_SHADOW_IN);
297
298         shown_list_view = prefs_folder_column_list_view_create
299                                 (_("Displayed columns"));
300         g_signal_connect(G_OBJECT(shown_list_view), "cursor-changed",
301                            G_CALLBACK(prefs_folder_column_shown_sel_changed),
302                            NULL);
303         gtk_widget_show(shown_list_view);
304         gtk_container_add(GTK_CONTAINER(scrolledwin), shown_list_view);
305
306         /* up/down button */
307         btn_vbox = gtk_vbox_new(FALSE, 8);
308         gtk_widget_show(btn_vbox);
309         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
310
311         up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
312         gtk_widget_show(up_btn);
313         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, TRUE, 0);
314
315         down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
316         gtk_widget_show(down_btn);
317         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, TRUE, 0);
318
319         g_signal_connect(G_OBJECT(up_btn), "clicked",
320                          G_CALLBACK(prefs_folder_column_up), NULL);
321         g_signal_connect(G_OBJECT(down_btn), "clicked",
322                          G_CALLBACK(prefs_folder_column_down), NULL);
323
324         btn_hbox = gtk_hbox_new(FALSE, 8);
325         gtk_widget_show(btn_hbox);
326         gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0);
327
328         btn_vbox = gtk_vbox_new(FALSE, 0);
329         gtk_widget_show(btn_vbox);
330         gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0);
331
332         default_btn = gtk_button_new_with_label(_(" Use default "));
333         gtk_widget_show(default_btn);
334         gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0);
335         g_signal_connect(G_OBJECT(default_btn), "clicked",
336                          G_CALLBACK(prefs_folder_column_set_to_default),
337                          NULL);
338
339         gtkut_stock_button_set_create(&confirm_area,
340                                       &cancel_btn, GTK_STOCK_CANCEL,
341                                       &ok_btn, GTK_STOCK_OK,
342                                       NULL, NULL);
343         gtk_widget_show(confirm_area);
344         gtk_box_pack_end(GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0);
345         gtk_widget_grab_default(ok_btn);
346
347         g_signal_connect(G_OBJECT(ok_btn), "clicked",
348                          G_CALLBACK(prefs_folder_column_ok), NULL);
349         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
350                          G_CALLBACK(prefs_folder_column_cancel), NULL);
351
352         folder_col.window      = window;
353         folder_col.add_btn     = add_btn;
354         folder_col.remove_btn  = remove_btn;
355         folder_col.up_btn      = up_btn;
356         folder_col.down_btn    = down_btn;
357         folder_col.ok_btn      = ok_btn;
358         folder_col.cancel_btn  = cancel_btn;
359         folder_col.stock_list_view = stock_list_view;
360         folder_col.shown_list_view = shown_list_view;
361         
362         prefs_folder_column_shown_set_active(FALSE);
363         prefs_folder_column_stock_set_active(FALSE);
364 }
365
366 FolderColumnState *prefs_folder_column_get_config(void)
367 {
368         static FolderColumnState state[N_FOLDER_COLS];
369         FolderColumnType type;
370         gint pos;
371
372         for (pos = 0; pos < N_FOLDER_COLS; pos++)
373                 state[pos].type = -1;
374
375         for (type = 0; type < N_FOLDER_COLS; type++) {
376                 pos = prefs_common.folder_col_pos[type];
377                 if (pos < 0 || pos >= N_FOLDER_COLS ||
378                     state[pos].type != -1) {
379                         g_warning("Wrong column position");
380                         prefs_folder_column_set_config(default_state);
381                         return default_state;
382                 }
383
384                 state[pos].type = type;
385                 state[pos].visible = prefs_common.folder_col_visible[type];
386         }
387
388         return state;
389 }
390
391 static void prefs_folder_column_set_config(FolderColumnState *state)
392 {
393         FolderColumnType type;
394         gint pos;
395
396         for (pos = 0; pos < N_FOLDER_COLS; pos++) {
397                 type = state[pos].type;
398                 prefs_common.folder_col_visible[type] = state[pos].visible;
399                 prefs_common.folder_col_pos[type] = pos;
400         }
401 }
402
403 static void prefs_folder_column_set_dialog(FolderColumnState *state)
404 {
405         GtkListStore *stock_store, *shown_store;
406         gint pos;
407         FolderColumnType type;
408         gchar *name;
409
410         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
411                         (GTK_TREE_VIEW(folder_col.stock_list_view)));
412         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
413                         (GTK_TREE_VIEW(folder_col.shown_list_view)));
414
415         gtk_list_store_clear(stock_store);
416         gtk_list_store_clear(shown_store);
417
418         if (!state)
419                 state = prefs_folder_column_get_config();
420
421         for (pos = 0; pos < N_FOLDER_COLS; pos++) {
422                 type = state[pos].type;
423                 name = gettext(col_name[type]);
424
425                 if (state[pos].visible)
426                         prefs_folder_column_insert_column(shown_store,
427                                                            -1, name,
428                                                            type);
429                 else
430                         prefs_folder_column_insert_column(stock_store,
431                                                             -1, name,
432                                                             type);
433         }
434 }
435
436 static void prefs_folder_column_set_view(void)
437 {
438         gint stock_n_rows, shown_n_rows;
439         FolderColumnState state[N_FOLDER_COLS];
440         FolderColumnType type;
441         gint row, pos = 0;
442
443         stock_n_rows = gtk_tree_model_iter_n_children
444                 (gtk_tree_view_get_model(GTK_TREE_VIEW
445                         (folder_col.stock_list_view)), NULL);
446         shown_n_rows = gtk_tree_model_iter_n_children
447                 (gtk_tree_view_get_model(GTK_TREE_VIEW
448                         (folder_col.shown_list_view)), NULL);
449
450         cm_return_if_fail
451                 (stock_n_rows + shown_n_rows == N_FOLDER_COLS);
452
453         for (row = 0; row < stock_n_rows; row++) {
454                 type = prefs_folder_column_get_column
455                         (folder_col.stock_list_view, row);
456                 state[row].type = type;
457                 state[row].visible = FALSE;
458         }
459
460         pos = row;
461         for (row = 0; row < shown_n_rows; row++) {
462                 type = prefs_folder_column_get_column
463                         (folder_col.shown_list_view, row);
464                 state[pos + row].type = type;
465                 state[pos + row].visible = TRUE;
466         }
467
468         prefs_folder_column_set_config(state);
469         main_window_set_folder_column();
470 }
471
472 static void prefs_folder_column_add(void)
473 {
474         GtkListStore *stock_store, *shown_store;
475         GtkTreeIter stock_sel, shown_sel, shown_add;
476         gboolean shown_sel_valid;
477         gchar *name;
478         FolderColumnType type;
479         
480         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
481                 (GTK_TREE_VIEW(folder_col.stock_list_view)));
482         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
483                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
484         
485         if (!gtk_tree_selection_get_selected
486                 (gtk_tree_view_get_selection
487                         (GTK_TREE_VIEW(folder_col.stock_list_view)),
488                  NULL,
489                  &stock_sel))
490                 return;
491
492         shown_sel_valid = gtk_tree_selection_get_selected
493                 (gtk_tree_view_get_selection
494                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
495                  NULL,
496                  &shown_sel);
497                          
498         gtk_tree_model_get(GTK_TREE_MODEL(stock_store), &stock_sel,
499                            SUMCOL_TYPE, &type,
500                            -1);
501                         
502         gtk_list_store_remove(stock_store, &stock_sel);
503
504         gtk_list_store_insert_after(shown_store, &shown_add, 
505                                     shown_sel_valid ? &shown_sel : NULL);
506
507         name = gettext(col_name[type]);                             
508                                     
509         gtk_list_store_set(shown_store, &shown_add,
510                            SUMCOL_NAME, name,
511                            SUMCOL_TYPE, type,
512                            -1);
513         
514         gtk_tree_selection_select_iter(gtk_tree_view_get_selection
515                 (GTK_TREE_VIEW(folder_col.shown_list_view)),
516                  &shown_add);
517         prefs_folder_column_shown_set_active(TRUE);
518         prefs_folder_column_stock_set_active(FALSE);
519 }
520
521 static void prefs_folder_column_remove(void)
522 {
523         GtkListStore *stock_store, *shown_store;
524         GtkTreeIter shown_sel, stock_sel, stock_add;
525         gboolean stock_sel_valid;
526         gchar *name;
527         FolderColumnType type;
528         
529         stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
530                 (GTK_TREE_VIEW(folder_col.stock_list_view)));
531         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
532                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
533                 
534         if (!gtk_tree_selection_get_selected
535                 (gtk_tree_view_get_selection
536                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
537                  NULL,
538                  &shown_sel))
539                 return;
540
541         stock_sel_valid = gtk_tree_selection_get_selected
542                 (gtk_tree_view_get_selection
543                         (GTK_TREE_VIEW(folder_col.stock_list_view)),
544                  NULL,
545                  &stock_sel);
546         
547         gtk_tree_model_get(GTK_TREE_MODEL(shown_store), &shown_sel,
548                            SUMCOL_TYPE, &type,
549                            -1);
550                         
551         gtk_list_store_remove(shown_store, &shown_sel);
552
553         gtk_list_store_insert_after(stock_store, &stock_add, 
554                                     stock_sel_valid ? &stock_sel : NULL);
555
556         name = gettext(col_name[type]);                             
557                                     
558         gtk_list_store_set(stock_store, &stock_add,
559                            SUMCOL_NAME, name,
560                            SUMCOL_TYPE, type,
561                            -1);
562         
563         gtk_tree_selection_select_iter(gtk_tree_view_get_selection
564                 (GTK_TREE_VIEW(folder_col.stock_list_view)),
565                 &stock_add);
566         prefs_folder_column_shown_set_active(FALSE);
567         prefs_folder_column_stock_set_active(TRUE);
568 }
569
570 static void prefs_folder_column_up(void)
571 {
572         GtkTreePath *prev, *sel;
573         GtkTreeIter isel;
574         GtkListStore *shown_store;
575         GtkTreeIter iprev;
576         
577         if (!gtk_tree_selection_get_selected
578                 (gtk_tree_view_get_selection
579                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
580                  NULL,
581                  &isel))
582                 return;
583
584         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
585                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
586
587         sel = gtk_tree_model_get_path(GTK_TREE_MODEL(shown_store), 
588                                       &isel);
589         if (!sel)
590                 return;
591
592         prev = gtk_tree_path_copy(sel);         
593         if (!gtk_tree_path_prev(prev)) {
594                 gtk_tree_path_free(prev);
595                 gtk_tree_path_free(sel);
596                 return;
597         }
598
599         gtk_tree_model_get_iter(GTK_TREE_MODEL(shown_store),
600                                 &iprev, prev);
601         gtk_tree_path_free(sel);
602         gtk_tree_path_free(prev);
603
604         gtk_list_store_swap(shown_store, &iprev, &isel);
605         prefs_folder_column_shown_set_btn_sensitivity();
606 }
607
608 static void prefs_folder_column_down(void)
609 {
610         GtkListStore *shown_store;
611         GtkTreeIter next, sel;
612         
613         if (!gtk_tree_selection_get_selected
614                 (gtk_tree_view_get_selection
615                         (GTK_TREE_VIEW(folder_col.shown_list_view)),
616                  NULL,
617                  &sel))
618                 return;
619
620         shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
621                 (GTK_TREE_VIEW(folder_col.shown_list_view)));
622
623         next = sel;
624         if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(shown_store), &next)) 
625                 return;
626
627         gtk_list_store_swap(shown_store, &next, &sel);
628         prefs_folder_column_shown_set_btn_sensitivity();        
629 }
630
631 static void prefs_folder_column_set_to_default(void)
632 {
633         prefs_folder_column_set_dialog(default_state);
634         prefs_folder_column_shown_set_active(FALSE);
635         prefs_folder_column_stock_set_active(FALSE);
636 }
637
638 static void prefs_folder_column_ok(void)
639 {
640         if (!folder_col.finished) {
641                 folder_col.finished = TRUE;
642                 prefs_folder_column_set_view();
643         }
644 }
645
646 static void prefs_folder_column_cancel(void)
647 {
648         folder_col.finished = TRUE;
649 }
650
651 static gint prefs_folder_column_delete_event(GtkWidget *widget,
652                                               GdkEventAny *event,
653                                               gpointer data)
654 {
655         folder_col.finished = TRUE;
656         return TRUE;
657 }
658
659 static gboolean prefs_folder_column_key_pressed(GtkWidget *widget,
660                                                  GdkEventKey *event,
661                                                  gpointer data)
662 {
663         if (event && event->keyval == GDK_KEY_Escape)
664                 folder_col.finished = TRUE;
665         return FALSE;
666 }
667
668 static GtkListStore *prefs_folder_column_create_store(void)
669 {
670         return gtk_list_store_new(N_SUMCOL_COLUMNS,
671                                   G_TYPE_STRING,
672                                   G_TYPE_INT,
673                                   -1);
674 }
675
676 static void prefs_folder_column_insert_column(GtkListStore *store,
677                                                gint row,
678                                                const gchar *name,
679                                                FolderColumnType type)
680 {
681         GtkTreeIter iter;
682
683         if (row >= 0) {
684                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store),
685                                                    &iter, NULL, row))
686                         row = -1;                                                  
687         }
688         if (row < 0) {
689                 /* add new */
690                 gtk_list_store_append(store, &iter);
691                 gtk_list_store_set(store, &iter,
692                                    SUMCOL_NAME, name,
693                                    SUMCOL_TYPE, type,
694                                    -1);
695                 return;
696         } else {
697                 /* change existing */
698                 gtk_list_store_set(store, &iter, 
699                                    SUMCOL_NAME, name,
700                                    SUMCOL_TYPE, type,
701                                    -1);
702         }
703 }
704
705 /*!
706  *\brief        Return the columnn type for a row
707  */
708 static FolderColumnType prefs_folder_column_get_column(GtkWidget *list, gint row)
709 {       
710         GtkTreeView *list_view = GTK_TREE_VIEW(list);
711         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
712         GtkTreeIter iter;
713         FolderColumnType result;
714
715         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
716                 return -1;
717
718         gtk_tree_model_get(model, &iter, 
719                            SUMCOL_TYPE, &result,
720                            -1);
721
722         return result;
723 }
724
725 static GtkWidget *prefs_folder_column_list_view_create(const gchar *name)
726 {
727         GtkWidget *list_view;
728         GtkTreeSelection *selector;
729         GtkTreeModel *model;
730
731         model = GTK_TREE_MODEL(prefs_folder_column_create_store());
732         list_view = gtk_tree_view_new_with_model(model);
733         g_object_unref(G_OBJECT(model));
734         
735         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view),
736                                      prefs_common.use_stripes_everywhere);
737
738         selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
739         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
740
741         prefs_filtering_create_list_view_columns(GTK_WIDGET(list_view), name);
742
743         gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(list_view),
744                                                GDK_BUTTON1_MASK,
745                                                row_targets,
746                                                G_N_ELEMENTS(row_targets), 
747                                                GDK_ACTION_MOVE);
748
749         gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(list_view), 
750                                              row_targets, 
751                                              G_N_ELEMENTS(row_targets), 
752                                              GDK_ACTION_MOVE);
753
754         g_signal_connect(G_OBJECT(list_view), "drag_data_get",
755                          G_CALLBACK(drag_data_get),
756                          model);
757
758         g_signal_connect(G_OBJECT(list_view), "drag_data_received",
759                          G_CALLBACK(drag_data_received),
760                          model);
761
762         return list_view;
763 }
764
765 static void prefs_filtering_create_list_view_columns(GtkWidget *list_view, 
766                                                      const gchar *name)
767 {
768         GtkTreeViewColumn *column;
769         GtkCellRenderer *renderer;
770
771         renderer = gtk_cell_renderer_text_new();
772         column = gtk_tree_view_column_new_with_attributes
773                 (name, renderer, "text", SUMCOL_NAME, NULL);
774         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
775 }
776
777 static void drag_data_get(GtkTreeView *tree_view, GdkDragContext *context, 
778                           GtkSelectionData *data, guint info, 
779                           guint time, GtkTreeModel *model)
780 {
781         GtkTreeIter iter;
782         FolderColumnType type;
783         GtkTreeModel *source_model;
784
785         if (info != TARGET_INFO_SUMCOL) 
786                 return;
787
788         if (!gtk_tree_selection_get_selected
789                         (gtk_tree_view_get_selection(tree_view),
790                          &source_model, &iter)) 
791                 return;                  
792         
793         gtk_tree_model_get(source_model, &iter, 
794                            SUMCOL_TYPE, &type,
795                            -1);
796
797         /* send the type */
798         gtk_selection_data_set(data, gtk_selection_data_get_target(data), 8,
799                 (gchar *) &type, sizeof type);
800 }
801
802 static void drag_data_received(GtkTreeView *tree_view, GdkDragContext *context,
803                                gint x, gint y, GtkSelectionData *data,
804                                guint info, guint time, GtkTreeModel *model)
805 {
806         GtkWidget *source;
807         GtkTreePath *dst = NULL, *sel = NULL;
808         GtkTreeIter isel, idst;
809         GtkTreeViewDropPosition pos;
810         FolderColumnType type;
811         GtkTreeModel *sel_model;
812         gchar *name;
813
814         source = gtk_drag_get_source_widget(context);
815
816         if (source == GTK_WIDGET(tree_view)) {
817         
818                 /*
819                  * Same widget: re-order
820                  */
821                  
822                 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
823                                            NULL, &isel)) {
824                         sel = gtk_tree_model_get_path(model, &isel);
825                         gtk_tree_view_get_dest_row_at_pos(tree_view, x, y,
826                                                           &dst, &pos);
827
828                         /* NOTE: dst is invalid if selection beyond last row, in that
829                          * case move beyond last one (XXX_move_before(..., NULL)) */
830
831                         if (dst)
832                                 gtk_tree_model_get_iter(model, &idst, dst);
833                         else 
834                                 gtk_list_store_move_before(GTK_LIST_STORE(model),
835                                                            &isel,
836                                                            NULL);
837
838                         /* we do not drag if no valid dst and sel, and when
839                          * dst and sel are the same (moving after or before
840                          * itself doesn't change order...) */
841                         if ((dst && sel) && gtk_tree_path_compare(sel, dst) != 0) {
842                                 if (pos == GTK_TREE_VIEW_DROP_BEFORE
843                                 ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
844                                         gtk_list_store_move_before(GTK_LIST_STORE(model),
845                                                                    &isel,
846                                                                    &idst);
847                                 else
848                                         gtk_list_store_move_after(GTK_LIST_STORE(model),
849                                                                   &isel,
850                                                                   &idst);
851                         } 
852                         gtk_tree_path_free(dst);
853                         gtk_tree_path_free(sel);
854                 }
855                 gtk_drag_finish(context, TRUE, FALSE, time);
856
857         } else if (source == folder_col.stock_list_view 
858         ||         source == folder_col.shown_list_view) {
859         
860                 /*
861                  * Other widget: change and update
862                  */
863
864                 /* get source information and remove */
865                 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(
866                                                 GTK_TREE_VIEW(source)),
867                                                 &sel_model, &isel)) {
868                         type = *((gint *) gtk_selection_data_get_data(data));
869                         name = gettext(col_name[type]);
870                         gtk_list_store_remove(GTK_LIST_STORE(sel_model), &isel);
871
872                         /* get insertion position */
873                         gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dst, &pos);
874
875                         /* NOTE: dst is invalid if insertion point beyond last row,
876                          * just append to list in that case (XXX_store_append()) */
877
878                         if (dst) {
879                                 gtk_tree_model_get_iter(model, &idst, dst);
880
881                                 if (pos == GTK_TREE_VIEW_DROP_BEFORE
882                                 ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
883                                         gtk_list_store_insert_before(GTK_LIST_STORE(model),
884                                                                      &isel,
885                                                                      &idst);
886                                 else
887                                         gtk_list_store_insert_after(GTK_LIST_STORE(model),
888                                                                     &isel,
889                                                                     &idst);
890                         } else
891                                 gtk_list_store_append(GTK_LIST_STORE(model),
892                                                       &isel);
893
894                         gtk_list_store_set(GTK_LIST_STORE(model), &isel,
895                                            SUMCOL_NAME, name,
896                                            SUMCOL_TYPE, type, -1);
897                         gtk_tree_path_free(dst);
898                 }
899                 gtk_drag_finish(context, TRUE, FALSE, time);
900         }
901
902         prefs_folder_column_shown_set_active(FALSE);
903         prefs_folder_column_stock_set_active(FALSE);
904
905         /* XXXX: should we call gtk_drag_finish() for other code paths? */
906 }
907
908 static void prefs_folder_column_shown_set_btn_sensitivity(void)
909 {
910         GtkTreeModel *model = GTK_TREE_MODEL(gtk_tree_view_get_model(
911                 GTK_TREE_VIEW(folder_col.shown_list_view)));
912         GtkTreeSelection *selection = gtk_tree_view_get_selection(
913                 GTK_TREE_VIEW(folder_col.shown_list_view));
914         GtkTreeIter iter;
915         GtkTreePath *path;
916         
917         if(!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
918                 gtk_widget_set_sensitive(folder_col.remove_btn, FALSE);
919                 gtk_widget_set_sensitive(folder_col.up_btn, FALSE);
920                 gtk_widget_set_sensitive(folder_col.down_btn, FALSE);
921                 return;
922         }
923         
924         path = gtk_tree_model_get_path(model, &iter);
925
926         gtk_widget_set_sensitive(folder_col.up_btn, gtk_tree_path_prev(path));
927         gtk_widget_set_sensitive(folder_col.down_btn,
928                                  gtk_tree_model_iter_next(model, &iter));
929         gtk_tree_path_free(path);
930 }
931
932 static void prefs_folder_column_shown_set_active(const gboolean active)
933 {
934         GtkTreeSelection *selection = NULL;
935         
936         gtk_widget_set_sensitive(folder_col.remove_btn, active);
937         
938         if(active == FALSE) {
939                 selection = gtk_tree_view_get_selection(
940                         GTK_TREE_VIEW(folder_col.shown_list_view));
941                 gtk_tree_selection_unselect_all(selection);
942                 
943                 gtk_widget_set_sensitive(folder_col.up_btn, FALSE);
944                 gtk_widget_set_sensitive(folder_col.down_btn, FALSE);
945         } else {
946                 prefs_folder_column_shown_set_btn_sensitivity();
947         }
948 }
949
950 static void prefs_folder_column_stock_set_active(const gboolean active)
951 {
952         GtkTreeSelection *selection = NULL;
953         
954         gtk_widget_set_sensitive(folder_col.add_btn, active);
955         
956         if(active == FALSE) {
957                 selection = gtk_tree_view_get_selection(
958                         GTK_TREE_VIEW(folder_col.stock_list_view));
959                 gtk_tree_selection_unselect_all(selection);
960         }
961 }
962
963 static void prefs_folder_column_stock_sel_changed(void)
964 {
965         GtkTreeSelection *selection = gtk_tree_view_get_selection(
966                 GTK_TREE_VIEW(folder_col.stock_list_view));
967         prefs_folder_column_stock_set_active(
968                 (selection != NULL) ? TRUE : FALSE);
969         prefs_folder_column_shown_set_active(
970                 (selection != NULL) ? FALSE : TRUE);
971 }
972
973 static void prefs_folder_column_shown_sel_changed(void)
974 {
975         GtkTreeSelection *selection = gtk_tree_view_get_selection(
976                 GTK_TREE_VIEW(folder_col.shown_list_view));
977         prefs_folder_column_shown_set_active(
978                 (selection != NULL) ? TRUE : FALSE);
979         prefs_folder_column_stock_set_active(
980                 (selection != NULL) ? FALSE : TRUE);
981 }