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