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