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