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