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