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