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