2008-06-13 [wwp] 3.4.0cvs92
[claws.git] / src / prefs_summary_column.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 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
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkwindow.h>
30 #include <gtk/gtkvbox.h>
31 #include <gtk/gtkhbox.h>
32 #include <gtk/gtkbutton.h>
33 #include <gdk/gdkkeysyms.h>
34
35 #include "prefs_gtk.h"
36 #include "prefs_common.h"
37 #include "prefs_summary_column.h"
38 #include "manage_window.h"
39 #include "summaryview.h"
40 #include "mainwindow.h"
41 #include "inc.h"
42 #include "gtkutils.h"
43 #include "utils.h"
44
45 static void prefs_summary_column_set_config(SummaryColumnState *state);
46
47 enum {
48         SUMCOL_NAME,
49         SUMCOL_TYPE,
50         N_SUMCOL_COLUMNS
51 };
52
53 #define TARGET_INFO_SUMCOL  (0xFEEDBABE)
54
55 static const GtkTargetEntry row_targets[] = {
56         { "PREFS_SUM_COL_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_INFO_SUMCOL }
57 };
58
59
60 static struct _SummaryColumnDialog
61 {
62         GtkWidget *window;
63
64         GtkWidget *stock_list_view;
65         GtkWidget *shown_list_view;
66
67         GtkWidget *add_btn;
68         GtkWidget *remove_btn;
69         GtkWidget *up_btn;
70         GtkWidget *down_btn;
71
72         GtkWidget *default_btn;
73
74         GtkWidget *ok_btn;
75         GtkWidget *cancel_btn;
76
77         gboolean finished;
78 } summary_col;
79
80 static const gchar *const col_name[N_SUMMARY_COLS] = {
81         N_("Mark"),             /* S_COL_MARK    */
82         N_("Status"),           /* S_COL_STATUS  */
83         N_("Attachment"),       /* S_COL_MIME    */
84         N_("Subject"),          /* S_COL_SUBJECT */
85         N_("From"),             /* S_COL_FROM    */
86         N_("To"),               /* S_COL_TO      */
87         N_("Date"),             /* S_COL_DATE    */
88         N_("Size"),             /* S_COL_SIZE    */
89         N_("Number"),           /* S_COL_NUMBER  */
90         N_("Score"),            /* S_COL_SCORE   */
91         N_("Locked"),           /* S_COL_LOCKED  */
92         N_("Tags"),             /* S_COL_TAGS  */
93 };
94
95 static SummaryColumnState default_state[N_SUMMARY_COLS] = {
96         { S_COL_MARK   , TRUE  },
97         { S_COL_STATUS , TRUE  },
98         { S_COL_MIME   , TRUE  },
99         { S_COL_SUBJECT, TRUE  },
100         { S_COL_FROM   , TRUE  },
101         { S_COL_TO     , FALSE },
102         { S_COL_DATE   , TRUE  },
103         { S_COL_SIZE   , TRUE  },
104         { S_COL_NUMBER , FALSE },
105         { S_COL_SCORE  , FALSE },
106         { S_COL_LOCKED , FALSE },
107         { S_COL_TAGS   , FALSE },
108 };
109
110 static void prefs_summary_column_create (void);
111
112 static void prefs_summary_column_set_dialog     (SummaryColumnState *state);
113 static void prefs_summary_column_set_view       (void);
114
115 /* callback functions */
116 static void prefs_summary_column_add    (void);
117 static void prefs_summary_column_remove (void);
118
119 static void prefs_summary_column_up     (void);
120 static void prefs_summary_column_down   (void);
121
122 static void prefs_summary_column_set_to_default (void);
123
124 static void prefs_summary_column_ok     (void);
125 static void prefs_summary_column_cancel (void);
126
127 static gint prefs_summary_column_delete_event   (GtkWidget      *widget,
128                                                  GdkEventAny    *event,
129                                                  gpointer        data);
130 static gboolean prefs_summary_column_key_pressed(GtkWidget      *widget,
131                                                  GdkEventKey    *event,
132                                                  gpointer        data);
133
134 static GtkListStore *prefs_summary_column_create_store  (void);
135
136 static void prefs_summary_column_insert_column  (GtkListStore *store,
137                                                  gint row,
138                                                  const gchar *name,
139                                                  SummaryColumnType type);
140                                                
141 static SummaryColumnType prefs_summary_column_get_column        (GtkWidget *list, 
142                                                                  gint row);
143
144 static GtkWidget *prefs_summary_column_list_view_create (const gchar *name);
145
146 static void prefs_filtering_create_list_view_columns    (GtkWidget *list_view, 
147                                                          const gchar *name);
148
149 static void drag_data_get       (GtkTreeView *tree_view, 
150                                  GdkDragContext *context, 
151                                  GtkSelectionData *data, 
152                                  guint info, 
153                                  guint time, 
154                                  GtkTreeModel *model);
155                           
156 static void drag_data_received  (GtkTreeView *tree_view, 
157                                  GdkDragContext *context,
158                                  gint x, gint y, 
159                                  GtkSelectionData *data,
160                                  guint info, 
161                                  guint time, 
162                                  GtkTreeModel *model);
163
164 static void prefs_summary_column_shown_set_btn_sensitivity(void);
165 static void prefs_summary_column_shown_set_active(const gboolean active);
166 static void prefs_summary_column_stock_set_active(const gboolean active);
167 static void prefs_summary_column_shown_sel_changed(void);
168 static void prefs_summary_column_stock_sel_changed(void);
169
170 void prefs_summary_column_open(void)
171 {
172         inc_lock();
173
174         if (!summary_col.window)
175                 prefs_summary_column_create();
176
177         manage_window_set_transient(GTK_WINDOW(summary_col.window));
178         gtk_widget_grab_focus(summary_col.ok_btn);
179
180         prefs_summary_column_set_dialog(NULL);
181
182         gtk_widget_show(summary_col.window);
183
184         summary_col.finished = FALSE;
185         while (summary_col.finished == FALSE)
186                 gtk_main_iteration();
187
188         gtk_widget_hide(summary_col.window);
189
190         inc_unlock();
191 }
192
193 static void prefs_summary_column_create(void)
194 {
195         GtkWidget *window;
196         GtkWidget *vbox;
197
198         GtkWidget *label_hbox;
199         GtkWidget *label;
200
201         GtkWidget *vbox1;
202
203         GtkWidget *hbox1;
204         GtkWidget *clist_hbox;
205         GtkWidget *scrolledwin;
206         GtkWidget *stock_list_view;
207         GtkWidget *shown_list_view;
208
209         GtkWidget *btn_vbox;
210         GtkWidget *add_btn;
211         GtkWidget *remove_btn;
212         GtkWidget *up_btn;
213         GtkWidget *down_btn;
214
215         GtkWidget *btn_hbox;
216         GtkWidget *default_btn;
217         GtkWidget *confirm_area;
218         GtkWidget *ok_btn;
219         GtkWidget *cancel_btn;
220
221         debug_print("Creating summary column setting window...\n");
222
223         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_summary_column");
224         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
225         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
226         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
227         gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
228         gtk_window_set_title(GTK_WINDOW(window),
229                              _("Message list columns configuration"));
230         g_signal_connect(G_OBJECT(window), "delete_event",
231                          G_CALLBACK(prefs_summary_column_delete_event),
232                          NULL);
233         g_signal_connect(G_OBJECT(window), "key_press_event",
234                          G_CALLBACK(prefs_summary_column_key_pressed),
235                          NULL);
236
237         vbox = gtk_vbox_new(FALSE, 6);
238         gtk_widget_show(vbox);
239         gtk_container_add(GTK_CONTAINER(window), vbox);
240
241         label_hbox = gtk_hbox_new(FALSE, 0);
242         gtk_widget_show(label_hbox);
243         gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4);
244
245         label = gtk_label_new
246                 (_("Select columns to be displayed in the message list. You can modify\n"
247                    "the order by using the Up / Down buttons or by dragging the items."));
248         gtk_widget_show(label);
249         gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4);
250         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
251
252         vbox1 = gtk_vbox_new(FALSE, VSPACING);
253         gtk_widget_show(vbox1);
254         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
255         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
256
257         hbox1 = gtk_hbox_new(FALSE, 8);
258         gtk_widget_show(hbox1);
259         gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
260
261         clist_hbox = gtk_hbox_new(FALSE, 8);
262         gtk_widget_show(clist_hbox);
263         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
264
265         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
266         gtk_widget_set_size_request(scrolledwin, 180, 210);
267         gtk_widget_show(scrolledwin);
268         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
269         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
270                                        GTK_POLICY_AUTOMATIC,
271                                        GTK_POLICY_AUTOMATIC);
272
273                                        
274         stock_list_view = prefs_summary_column_list_view_create
275                                 (_("Hidden columns"));
276         g_signal_connect(G_OBJECT(stock_list_view), "cursor-changed",
277                            G_CALLBACK(prefs_summary_column_stock_sel_changed),
278                            NULL);
279         gtk_widget_show(stock_list_view);
280         gtk_container_add(GTK_CONTAINER(scrolledwin), stock_list_view);
281
282         /* add/remove button */
283         btn_vbox = gtk_vbox_new(FALSE, 8);
284         gtk_widget_show(btn_vbox);
285         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
286
287         add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
288         gtk_widget_show(add_btn);
289         gtk_box_pack_start(GTK_BOX(btn_vbox), add_btn, FALSE, TRUE, 0);
290
291         g_signal_connect(G_OBJECT(add_btn), "clicked",
292                          G_CALLBACK(prefs_summary_column_add), NULL);
293
294         clist_hbox = gtk_hbox_new(FALSE, 8);
295         gtk_widget_show(clist_hbox);
296         gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
297
298         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
299         gtk_widget_set_size_request(scrolledwin, 180, 210);
300         gtk_widget_show(scrolledwin);
301         gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
302         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
303                                        GTK_POLICY_AUTOMATIC,
304                                        GTK_POLICY_AUTOMATIC);
305
306         shown_list_view = prefs_summary_column_list_view_create
307                                 (_("Displayed columns"));
308         g_signal_connect(G_OBJECT(shown_list_view), "cursor-changed",
309                            G_CALLBACK(prefs_summary_column_shown_sel_changed),
310                            NULL);
311         gtk_widget_show(shown_list_view);
312         gtk_container_add(GTK_CONTAINER(scrolledwin), shown_list_view);
313
314         /* up/down button */
315         btn_vbox = gtk_vbox_new(FALSE, 8);
316         gtk_widget_show(btn_vbox);
317         gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
318
319         remove_btn = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
320         gtk_widget_show(remove_btn);
321         gtk_box_pack_start(GTK_BOX(btn_vbox), remove_btn, FALSE, TRUE, 0);
322
323         up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
324         gtk_widget_show(up_btn);
325         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, TRUE, 0);
326
327         down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
328         gtk_widget_show(down_btn);
329         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, TRUE, 0);
330
331         g_signal_connect(G_OBJECT(remove_btn), "clicked",
332                          G_CALLBACK(prefs_summary_column_remove), NULL);
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\n");
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         g_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_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, data->target, 8, (gchar *) &type, sizeof type);
814 }
815
816 static void drag_data_received(GtkTreeView *tree_view, GdkDragContext *context,
817                                gint x, gint y, GtkSelectionData *data,
818                                guint info, guint time, GtkTreeModel *model)
819 {
820         GtkWidget *source;
821         GtkTreePath *dst = NULL, *sel = NULL;
822         GtkTreeIter isel, idst;
823         GtkTreeViewDropPosition pos;
824         SummaryColumnType type;
825         GtkTreeModel *sel_model;
826         gchar *name;
827         
828         source = gtk_drag_get_source_widget(context);
829         
830         if (source == GTK_WIDGET(tree_view)) {
831         
832                 /*
833                  * Same widget: re-order
834                  */
835                  
836                 gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
837                                            NULL, &isel);
838                 sel = gtk_tree_model_get_path(model, &isel);
839                 gtk_tree_view_get_dest_row_at_pos(tree_view, x, y,
840                                                   &dst, &pos);
841
842                 /* NOTE: dst is invalid if selection beyond last row, in that
843                  * case move beyond last one (XXX_move_before(..., NULL)) */                                              
844
845                 if (dst)                                                  
846                         gtk_tree_model_get_iter(model, &idst, dst);
847                 else 
848                         gtk_list_store_move_before(GTK_LIST_STORE(model),
849                                                    &isel,
850                                                    NULL);
851
852                 /* we do not drag if no valid dst and sel, and when
853                  * dst and sel are the same (moving after or before
854                  * itself doesn't change order...) */
855                 if ((dst && sel) && gtk_tree_path_compare(sel, dst) != 0) {
856                         if (pos == GTK_TREE_VIEW_DROP_BEFORE
857                         ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
858                                 gtk_list_store_move_before(GTK_LIST_STORE(model),
859                                                            &isel,
860                                                            &idst);
861                         else
862                                 gtk_list_store_move_after(GTK_LIST_STORE(model),
863                                                           &isel,
864                                                           &idst);
865                         
866                 } 
867                 gtk_tree_path_free(dst);                                          
868                 gtk_tree_path_free(sel);
869                 gtk_drag_finish(context, TRUE, FALSE, time);
870                 
871         } else if (source == summary_col.stock_list_view 
872         ||         source == summary_col.shown_list_view) {
873         
874                 /*
875                  * Other widget: change and update
876                  */
877
878                 
879                 /* get source information and remove */
880                 gtk_tree_selection_get_selected(gtk_tree_view_get_selection(
881                                                 GTK_TREE_VIEW(source)),
882                                                 &sel_model, &isel);
883                 type = *((gint *) data->data);
884                 name = gettext(col_name[type]);
885                 gtk_list_store_remove(GTK_LIST_STORE(sel_model), &isel);
886
887                 /* get insertion position */
888                 gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dst, &pos);
889
890                 /* NOTE: dst is invalid if insertion point beyond last row, 
891                  * just append to list in that case (XXX_store_append()) */
892
893                 if (dst) {
894                         gtk_tree_model_get_iter(model, &idst, dst);
895
896                         if (pos == GTK_TREE_VIEW_DROP_BEFORE
897                         ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
898                                 gtk_list_store_insert_before(GTK_LIST_STORE(model),
899                                                              &isel,
900                                                              &idst);
901                         else
902                                 gtk_list_store_insert_after(GTK_LIST_STORE(model),
903                                                             &isel,
904                                                             &idst);
905                 } else
906                         gtk_list_store_append(GTK_LIST_STORE(model),
907                                               &isel);
908                 
909                 gtk_list_store_set(GTK_LIST_STORE(model), &isel,
910                                    SUMCOL_NAME, name,
911                                    SUMCOL_TYPE, type, -1);
912                 gtk_tree_path_free(dst);
913                 gtk_drag_finish(context, TRUE, FALSE, time);
914         }
915
916         prefs_summary_column_shown_set_active(FALSE);
917         prefs_summary_column_stock_set_active(FALSE);
918         
919         /* XXXX: should we call gtk_drag_finish() for other code paths? */
920 }
921
922 static void prefs_summary_column_shown_set_btn_sensitivity(void)
923 {
924         GtkTreeModel *model = GTK_TREE_MODEL(gtk_tree_view_get_model(
925                 GTK_TREE_VIEW(summary_col.shown_list_view)));
926         GtkTreeSelection *selection = gtk_tree_view_get_selection(
927                 GTK_TREE_VIEW(summary_col.shown_list_view));
928         GtkTreeIter iter;
929         GtkTreePath *path;
930         
931         if(!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
932                 gtk_widget_set_sensitive(summary_col.remove_btn, FALSE);
933                 gtk_widget_set_sensitive(summary_col.up_btn, FALSE);
934                 gtk_widget_set_sensitive(summary_col.down_btn, FALSE);
935                 return;
936         }
937         
938         path = gtk_tree_model_get_path(model, &iter);
939
940         gtk_widget_set_sensitive(summary_col.up_btn, gtk_tree_path_prev(path));
941         gtk_widget_set_sensitive(summary_col.down_btn,
942                                  gtk_tree_model_iter_next(model, &iter));
943         gtk_tree_path_free(path);
944 }
945
946 static void prefs_summary_column_shown_set_active(const gboolean active)
947 {
948         GtkTreeSelection *selection = NULL;
949         
950         gtk_widget_set_sensitive(summary_col.remove_btn, active);
951         
952         if(active == FALSE) {
953                 selection = gtk_tree_view_get_selection(
954                         GTK_TREE_VIEW(summary_col.shown_list_view));
955                 gtk_tree_selection_unselect_all(selection);
956                 
957                 gtk_widget_set_sensitive(summary_col.up_btn, FALSE);
958                 gtk_widget_set_sensitive(summary_col.down_btn, FALSE);
959         } else {
960                 prefs_summary_column_shown_set_btn_sensitivity();
961         }
962 }
963
964 static void prefs_summary_column_stock_set_active(const gboolean active)
965 {
966         GtkTreeSelection *selection = NULL;
967         
968         gtk_widget_set_sensitive(summary_col.add_btn, active);
969         
970         if(active == FALSE) {
971                 selection = gtk_tree_view_get_selection(
972                         GTK_TREE_VIEW(summary_col.stock_list_view));
973                 gtk_tree_selection_unselect_all(selection);
974         }
975 }
976
977 static void prefs_summary_column_stock_sel_changed(void)
978 {
979         GtkTreeSelection *selection = gtk_tree_view_get_selection(
980                 GTK_TREE_VIEW(summary_col.stock_list_view));
981         prefs_summary_column_stock_set_active(
982                 (selection != NULL) ? TRUE : FALSE);
983         prefs_summary_column_shown_set_active(
984                 (selection != NULL) ? FALSE : TRUE);
985 }
986
987 static void prefs_summary_column_shown_sel_changed(void)
988 {
989         GtkTreeSelection *selection = gtk_tree_view_get_selection(
990                 GTK_TREE_VIEW(summary_col.shown_list_view));
991         prefs_summary_column_shown_set_active(
992                 (selection != NULL) ? TRUE : FALSE);
993         prefs_summary_column_stock_set_active(
994                 (selection != NULL) ? FALSE : TRUE);
995 }