talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

prefs_summary_column.c (27666B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail team and 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 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 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 #include <gtk/gtk.h>
     24 #include <gdk/gdkkeysyms.h>
     25 
     26 #include "prefs_gtk.h"
     27 #include "prefs_common.h"
     28 #include "prefs_summary_column.h"
     29 #include "manage_window.h"
     30 #include "summaryview.h"
     31 #include "mainwindow.h"
     32 #include "inc.h"
     33 #include "gtkutils.h"
     34 #include "utils.h"
     35 
     36 static void prefs_summary_column_set_config(SummaryColumnState *state);
     37 
     38 enum {
     39 	SUMCOL_NAME,
     40 	SUMCOL_TYPE,
     41 	N_SUMCOL_COLUMNS
     42 };
     43 
     44 #define TARGET_INFO_SUMCOL  (0xFEEDBABE)
     45 
     46 static const GtkTargetEntry row_targets[] = {
     47 	{ "PREFS_SUM_COL_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_INFO_SUMCOL }
     48 };
     49 
     50 
     51 static struct _SummaryColumnDialog
     52 {
     53 	GtkWidget *window;
     54 
     55 	GtkWidget *stock_list_view;
     56 	GtkWidget *shown_list_view;
     57 
     58 	GtkWidget *add_btn;
     59 	GtkWidget *remove_btn;
     60 	GtkWidget *up_btn;
     61 	GtkWidget *down_btn;
     62 
     63 	GtkWidget *default_btn;
     64 
     65 	GtkWidget *ok_btn;
     66 	GtkWidget *cancel_btn;
     67 
     68 	gboolean finished;
     69 } summary_col;
     70 
     71 static const gchar *const col_name[N_SUMMARY_COLS] = {
     72 	N_("Mark"),		/* S_COL_MARK    */
     73 	N_("Status"),		/* S_COL_STATUS  */
     74 	N_("Attachment"),	/* S_COL_MIME    */
     75 	N_("Subject"),		/* S_COL_SUBJECT */
     76 	N_("From"),		/* S_COL_FROM    */
     77 	N_("To"),		/* S_COL_TO      */
     78 	N_("Date"),		/* S_COL_DATE    */
     79 	N_("Size"),		/* S_COL_SIZE    */
     80 	N_("Number"),		/* S_COL_NUMBER  */
     81 	N_("Score"),		/* S_COL_SCORE   */
     82 	N_("Locked"),		/* S_COL_LOCKED  */
     83 	N_("Tags"),		/* S_COL_TAGS  */
     84 };
     85 
     86 static SummaryColumnState default_state[N_SUMMARY_COLS] = {
     87 	{ S_COL_MARK   , TRUE  },
     88 	{ S_COL_STATUS , TRUE  },
     89 	{ S_COL_MIME   , TRUE  },
     90 	{ S_COL_SUBJECT, TRUE  },
     91 	{ S_COL_FROM   , TRUE  },
     92 	{ S_COL_TO     , FALSE },
     93 	{ S_COL_DATE   , TRUE  },
     94 	{ S_COL_SIZE   , TRUE  },
     95 	{ S_COL_NUMBER , FALSE },
     96 	{ S_COL_SCORE  , FALSE },
     97 	{ S_COL_LOCKED , FALSE },
     98 	{ S_COL_TAGS   , FALSE },
     99 };
    100 
    101 static void prefs_summary_column_create	(void);
    102 
    103 static void prefs_summary_column_set_dialog	(SummaryColumnState *state);
    104 static void prefs_summary_column_set_view	(void);
    105 
    106 /* callback functions */
    107 static void prefs_summary_column_add	(void);
    108 static void prefs_summary_column_remove	(void);
    109 
    110 static void prefs_summary_column_up	(void);
    111 static void prefs_summary_column_down	(void);
    112 
    113 static void prefs_summary_column_set_to_default	(void);
    114 
    115 static void prefs_summary_column_ok	(void);
    116 static void prefs_summary_column_cancel	(void);
    117 
    118 static gint prefs_summary_column_delete_event	(GtkWidget	*widget,
    119 						 GdkEventAny	*event,
    120 						 gpointer	 data);
    121 
    122 static GtkListStore *prefs_summary_column_create_store	(void);
    123 
    124 static void prefs_summary_column_insert_column	(GtkListStore *store,
    125 						 gint row,
    126 						 const gchar *name,
    127 						 SummaryColumnType type);
    128 
    129 static SummaryColumnType prefs_summary_column_get_column	(GtkWidget *list,
    130 								 gint row);
    131 
    132 static GtkWidget *prefs_summary_column_list_view_create	(const gchar *name);
    133 
    134 static void prefs_filtering_create_list_view_columns	(GtkWidget *list_view,
    135 							 const gchar *name);
    136 
    137 static void drag_data_get	(GtkTreeView *tree_view,
    138 				 GdkDragContext *context,
    139 				 GtkSelectionData *data,
    140 				 guint info,
    141 				 guint time,
    142 				 GtkTreeModel *model);
    143 
    144 static void drag_data_received	(GtkTreeView *tree_view,
    145 				 GdkDragContext *context,
    146 				 gint x, gint y,
    147 				 GtkSelectionData *data,
    148 				 guint info,
    149 				 guint time,
    150 				 GtkTreeModel *model);
    151 
    152 static void prefs_summary_column_shown_set_btn_sensitivity(void);
    153 static void prefs_summary_column_shown_set_active(const gboolean active);
    154 static void prefs_summary_column_stock_set_active(const gboolean active);
    155 static void prefs_summary_column_shown_sel_changed(void);
    156 static void prefs_summary_column_stock_sel_changed(void);
    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 	gtk_window_set_modal(GTK_WINDOW(summary_col.window), TRUE);
    172 
    173 	summary_col.finished = FALSE;
    174 	while (summary_col.finished == FALSE)
    175 		gtk_main_iteration();
    176 
    177 	gtk_widget_hide(summary_col.window);
    178 	gtk_window_set_modal(GTK_WINDOW(summary_col.window), FALSE);
    179 	inc_unlock();
    180 }
    181 
    182 static void prefs_summary_column_create(void)
    183 {
    184 	GtkWidget *window;
    185 	GtkWidget *vbox;
    186 
    187 	GtkWidget *label_hbox;
    188 	GtkWidget *label;
    189 
    190 	GtkWidget *vbox1;
    191 
    192 	GtkWidget *hbox1;
    193 	GtkWidget *clist_hbox;
    194 	GtkWidget *scrolledwin;
    195 	GtkWidget *stock_list_view;
    196 	GtkWidget *shown_list_view;
    197 
    198 	GtkWidget *btn_vbox;
    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 = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_summary_column");
    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_resizable(GTK_WINDOW(window), FALSE);
    216 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
    217 	gtk_window_set_title(GTK_WINDOW(window),
    218 			     _("Message list columns configuration"));
    219 	g_signal_connect(G_OBJECT(window), "delete_event",
    220 			 G_CALLBACK(prefs_summary_column_delete_event),
    221 			 NULL);
    222 
    223 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    224 	gtk_widget_show(vbox);
    225 	gtk_container_add(GTK_CONTAINER(window), vbox);
    226 
    227 	label_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    228 	gtk_widget_show(label_hbox);
    229 	gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4);
    230 
    231 	label = gtk_label_new
    232 		(_("Select columns to be displayed in the message list. You can modify\n"
    233 		   "the order by using the Up / Down buttons or by dragging the items."));
    234 	gtk_widget_show(label);
    235 	gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4);
    236 	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
    237 
    238 	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, VSPACING);
    239 	gtk_widget_show(vbox1);
    240 	gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
    241 	gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
    242 
    243 	hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    244 	gtk_widget_show(hbox1);
    245 	gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
    246 
    247 	clist_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    248 	gtk_widget_show(clist_hbox);
    249 	gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
    250 
    251 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
    252 	gtk_widget_set_size_request(scrolledwin, 180, 210);
    253 	gtk_widget_show(scrolledwin);
    254 	gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
    255 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
    256 				       GTK_POLICY_AUTOMATIC,
    257 				       GTK_POLICY_AUTOMATIC);
    258 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
    259 					   GTK_SHADOW_IN);
    260 
    261 	stock_list_view = prefs_summary_column_list_view_create
    262 				(_("Hidden columns"));
    263 	g_signal_connect(G_OBJECT(stock_list_view), "cursor-changed",
    264 			   G_CALLBACK(prefs_summary_column_stock_sel_changed),
    265 			   NULL);
    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_box_new(GTK_ORIENTATION_VERTICAL, 8);
    271 	gtk_widget_show(btn_vbox);
    272 	gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
    273 
    274 	add_btn = gtkut_stock_button("list-add", _("_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 	remove_btn = gtkut_stock_button("list-remove", _("_Remove"));
    282 	gtk_widget_show(remove_btn);
    283 	gtk_box_pack_start(GTK_BOX(btn_vbox), remove_btn, FALSE, TRUE, 0);
    284 
    285 	g_signal_connect(G_OBJECT(remove_btn), "clicked",
    286 			 G_CALLBACK(prefs_summary_column_remove), NULL);
    287 
    288 	clist_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    289 	gtk_widget_show(clist_hbox);
    290 	gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
    291 
    292 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
    293 	gtk_widget_set_size_request(scrolledwin, 180, 210);
    294 	gtk_widget_show(scrolledwin);
    295 	gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
    296 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
    297 				       GTK_POLICY_AUTOMATIC,
    298 				       GTK_POLICY_AUTOMATIC);
    299 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
    300 					   GTK_SHADOW_IN);
    301 
    302 	shown_list_view = prefs_summary_column_list_view_create
    303 				(_("Displayed columns"));
    304 	g_signal_connect(G_OBJECT(shown_list_view), "cursor-changed",
    305 			   G_CALLBACK(prefs_summary_column_shown_sel_changed),
    306 			   NULL);
    307 	gtk_widget_show(shown_list_view);
    308 	gtk_container_add(GTK_CONTAINER(scrolledwin), shown_list_view);
    309 
    310 	/* up/down button */
    311 	btn_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
    312 	gtk_widget_show(btn_vbox);
    313 	gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
    314 
    315 	up_btn = gtkut_stock_button("go-up", _("_Up"));
    316 	gtk_widget_show(up_btn);
    317 	gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, TRUE, 0);
    318 
    319 	down_btn = gtkut_stock_button("go-down", _("_Down"));
    320 	gtk_widget_show(down_btn);
    321 	gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, TRUE, 0);
    322 
    323 	g_signal_connect(G_OBJECT(up_btn), "clicked",
    324 			 G_CALLBACK(prefs_summary_column_up), NULL);
    325 	g_signal_connect(G_OBJECT(down_btn), "clicked",
    326 			 G_CALLBACK(prefs_summary_column_down), NULL);
    327 
    328 	btn_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    329 	gtk_widget_show(btn_hbox);
    330 	gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0);
    331 
    332 	btn_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    333 	gtk_widget_show(btn_vbox);
    334 	gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0);
    335 
    336 	default_btn = gtk_button_new_with_label(_(" Use default "));
    337 	gtk_widget_show(default_btn);
    338 	gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0);
    339 	g_signal_connect(G_OBJECT(default_btn), "clicked",
    340 			 G_CALLBACK(prefs_summary_column_set_to_default),
    341 			 NULL);
    342 
    343 	gtkut_stock_button_set_create(&confirm_area,
    344 				      &cancel_btn, NULL, _("_Cancel"),
    345 				      &ok_btn, NULL, _("_OK"),
    346 				      NULL, 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 	prefs_summary_column_shown_set_active(FALSE);
    368 	prefs_summary_column_stock_set_active(FALSE);
    369 }
    370 
    371 SummaryColumnState *prefs_summary_column_get_config(void)
    372 {
    373 	static SummaryColumnState state[N_SUMMARY_COLS];
    374 	SummaryColumnType type;
    375 	gint pos;
    376 
    377 	for (pos = 0; pos < N_SUMMARY_COLS; pos++)
    378 		state[pos].type = -1;
    379 
    380 	for (type = 0; type < N_SUMMARY_COLS; type++) {
    381 		pos = prefs_common.summary_col_pos[type];
    382 		if (pos < 0 || pos >= N_SUMMARY_COLS ||
    383 		    state[pos].type != -1) {
    384 			g_warning("wrong column position");
    385 			prefs_summary_column_set_config(default_state);
    386 			return default_state;
    387 		}
    388 
    389 		state[pos].type = type;
    390 		state[pos].visible = prefs_common.summary_col_visible[type];
    391 	}
    392 
    393 	return state;
    394 }
    395 
    396 static void prefs_summary_column_set_config(SummaryColumnState *state)
    397 {
    398 	SummaryColumnType type;
    399 	gint pos;
    400 
    401 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
    402 		type = state[pos].type;
    403 		prefs_common.summary_col_visible[type] = state[pos].visible;
    404 		prefs_common.summary_col_pos[type] = pos;
    405 	}
    406 }
    407 
    408 static void prefs_summary_column_set_dialog(SummaryColumnState *state)
    409 {
    410 	GtkListStore *stock_store, *shown_store;
    411 	gint pos;
    412 	SummaryColumnType type;
    413 	gchar *name;
    414 
    415 	stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
    416 			(GTK_TREE_VIEW(summary_col.stock_list_view)));
    417 	shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
    418 			(GTK_TREE_VIEW(summary_col.shown_list_view)));
    419 
    420 	gtk_list_store_clear(stock_store);
    421 	gtk_list_store_clear(shown_store);
    422 
    423 	if (!state)
    424 		state = prefs_summary_column_get_config();
    425 
    426 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
    427 		type = state[pos].type;
    428 		name = col_name[type];
    429 
    430 		if (state[pos].visible)
    431 			prefs_summary_column_insert_column(shown_store, -1, name, type);
    432 		else
    433 			prefs_summary_column_insert_column(stock_store, -1, name, type);
    434 	}
    435 }
    436 
    437 static void prefs_summary_column_set_view(void)
    438 {
    439 	gint stock_n_rows, shown_n_rows;
    440 	SummaryColumnState state[N_SUMMARY_COLS];
    441 	SummaryColumnType type;
    442 	gint row, pos = 0;
    443 
    444 	stock_n_rows = gtk_tree_model_iter_n_children
    445 		(gtk_tree_view_get_model(GTK_TREE_VIEW
    446 			(summary_col.stock_list_view)), NULL);
    447 	shown_n_rows = gtk_tree_model_iter_n_children
    448 		(gtk_tree_view_get_model(GTK_TREE_VIEW
    449 			(summary_col.shown_list_view)), NULL);
    450 
    451 	cm_return_if_fail
    452 		(stock_n_rows + shown_n_rows == N_SUMMARY_COLS);
    453 
    454 	for (row = 0; row < stock_n_rows; row++) {
    455 		type = prefs_summary_column_get_column
    456 			(summary_col.stock_list_view, row);
    457 		state[row].type = type;
    458 		state[row].visible = FALSE;
    459 	}
    460 
    461 	pos = row;
    462 	for (row = 0; row < shown_n_rows; row++) {
    463 		type = prefs_summary_column_get_column
    464 			(summary_col.shown_list_view, row);
    465 		state[pos + row].type = type;
    466 		state[pos + row].visible = TRUE;
    467 	}
    468 
    469 	prefs_summary_column_set_config(state);
    470 	main_window_set_summary_column();
    471 }
    472 
    473 static void prefs_summary_column_add(void)
    474 {
    475 	GtkListStore *stock_store, *shown_store;
    476 	GtkTreeIter stock_sel, shown_sel, shown_add;
    477 	gboolean shown_sel_valid;
    478 	SummaryColumnType type;
    479 
    480 	stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
    481 		(GTK_TREE_VIEW(summary_col.stock_list_view)));
    482 	shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
    483 		(GTK_TREE_VIEW(summary_col.shown_list_view)));
    484 
    485 	if (!gtk_tree_selection_get_selected
    486 		(gtk_tree_view_get_selection
    487 			(GTK_TREE_VIEW(summary_col.stock_list_view)),
    488 		 NULL,
    489 		 &stock_sel))
    490 		return;
    491 
    492 	shown_sel_valid = gtk_tree_selection_get_selected
    493 		(gtk_tree_view_get_selection
    494 			(GTK_TREE_VIEW(summary_col.shown_list_view)),
    495 		 NULL,
    496 		 &shown_sel);
    497 
    498 	gtk_tree_model_get(GTK_TREE_MODEL(stock_store), &stock_sel,
    499 			   SUMCOL_TYPE, &type,
    500 			   -1);
    501 
    502 	gtk_list_store_remove(stock_store, &stock_sel);
    503 
    504 	gtk_list_store_insert_after(shown_store, &shown_add,
    505 				    shown_sel_valid ? &shown_sel : NULL);
    506 
    507 	gtk_list_store_set(shown_store, &shown_add,
    508 			   SUMCOL_NAME, col_name[type],
    509 			   SUMCOL_TYPE, type,
    510 			   -1);
    511 
    512 	gtk_tree_selection_select_iter(gtk_tree_view_get_selection
    513 		(GTK_TREE_VIEW(summary_col.shown_list_view)),
    514 		 &shown_add);
    515 	prefs_summary_column_shown_set_active(TRUE);
    516 	prefs_summary_column_stock_set_active(FALSE);
    517 }
    518 
    519 static void prefs_summary_column_remove(void)
    520 {
    521 	GtkListStore *stock_store, *shown_store;
    522 	GtkTreeIter shown_sel, stock_sel, stock_add;
    523 	gboolean stock_sel_valid;
    524 	SummaryColumnType type;
    525 
    526 	stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
    527 		(GTK_TREE_VIEW(summary_col.stock_list_view)));
    528 	shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
    529 		(GTK_TREE_VIEW(summary_col.shown_list_view)));
    530 
    531 	if (!gtk_tree_selection_get_selected
    532 		(gtk_tree_view_get_selection
    533 			(GTK_TREE_VIEW(summary_col.shown_list_view)),
    534 		 NULL,
    535 		 &shown_sel))
    536 		return;
    537 
    538 	stock_sel_valid = gtk_tree_selection_get_selected
    539 		(gtk_tree_view_get_selection
    540 			(GTK_TREE_VIEW(summary_col.stock_list_view)),
    541 		 NULL,
    542 		 &stock_sel);
    543 
    544 	gtk_tree_model_get(GTK_TREE_MODEL(shown_store), &shown_sel,
    545 			   SUMCOL_TYPE, &type,
    546 			   -1);
    547 
    548 	gtk_list_store_remove(shown_store, &shown_sel);
    549 
    550 	gtk_list_store_insert_after(stock_store, &stock_add,
    551 				    stock_sel_valid ? &stock_sel : NULL);
    552 
    553 	gtk_list_store_set(stock_store, &stock_add,
    554 			   SUMCOL_NAME, col_name[type],
    555 			   SUMCOL_TYPE, type,
    556 			   -1);
    557 
    558 	gtk_tree_selection_select_iter(gtk_tree_view_get_selection
    559 		(GTK_TREE_VIEW(summary_col.stock_list_view)),
    560 		&stock_add);
    561 	prefs_summary_column_shown_set_active(FALSE);
    562 	prefs_summary_column_stock_set_active(TRUE);
    563 }
    564 
    565 static void prefs_summary_column_up(void)
    566 {
    567 	GtkTreePath *prev, *sel;
    568 	GtkTreeIter isel;
    569 	GtkListStore *shown_store;
    570 	GtkTreeIter iprev;
    571 
    572 	if (!gtk_tree_selection_get_selected
    573 		(gtk_tree_view_get_selection
    574 			(GTK_TREE_VIEW(summary_col.shown_list_view)),
    575 		 NULL,
    576 		 &isel))
    577 		return;
    578 
    579 	shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
    580 		(GTK_TREE_VIEW(summary_col.shown_list_view)));
    581 
    582 	sel = gtk_tree_model_get_path(GTK_TREE_MODEL(shown_store),
    583 				      &isel);
    584 	if (!sel)
    585 		return;
    586 
    587 	prev = gtk_tree_path_copy(sel);
    588 	if (!gtk_tree_path_prev(prev)) {
    589 		gtk_tree_path_free(prev);
    590 		gtk_tree_path_free(sel);
    591 		return;
    592 	}
    593 
    594 	gtk_tree_model_get_iter(GTK_TREE_MODEL(shown_store),
    595 				&iprev, prev);
    596 	gtk_tree_path_free(sel);
    597 	gtk_tree_path_free(prev);
    598 
    599 	gtk_list_store_swap(shown_store, &iprev, &isel);
    600 	prefs_summary_column_shown_set_btn_sensitivity();
    601 }
    602 
    603 static void prefs_summary_column_down(void)
    604 {
    605 	GtkListStore *shown_store;
    606 	GtkTreeIter next, sel;
    607 
    608 	if (!gtk_tree_selection_get_selected
    609 		(gtk_tree_view_get_selection
    610 			(GTK_TREE_VIEW(summary_col.shown_list_view)),
    611 		 NULL,
    612 		 &sel))
    613 		return;
    614 
    615 	shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
    616 		(GTK_TREE_VIEW(summary_col.shown_list_view)));
    617 
    618 	next = sel;
    619 	if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(shown_store), &next))
    620 		return;
    621 
    622 	gtk_list_store_swap(shown_store, &next, &sel);
    623 	prefs_summary_column_shown_set_btn_sensitivity();
    624 }
    625 
    626 static void prefs_summary_column_set_to_default(void)
    627 {
    628 	prefs_summary_column_set_dialog(default_state);
    629 	prefs_summary_column_shown_set_active(FALSE);
    630 	prefs_summary_column_stock_set_active(FALSE);
    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 GtkListStore *prefs_summary_column_create_store(void)
    655 {
    656 	return gtk_list_store_new(N_SUMCOL_COLUMNS,
    657 				  G_TYPE_STRING,
    658 				  G_TYPE_INT,
    659 				  -1);
    660 }
    661 
    662 static void prefs_summary_column_insert_column(GtkListStore *store,
    663 					       gint row,
    664 					       const gchar *name,
    665 					       SummaryColumnType type)
    666 {
    667 	GtkTreeIter iter;
    668 
    669 	if (row >= 0) {
    670 		if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store),
    671 						   &iter, NULL, row))
    672 			row = -1;
    673 	}
    674 	if (row < 0) {
    675 		/* add new */
    676 		gtk_list_store_append(store, &iter);
    677 		gtk_list_store_set(store, &iter,
    678 				   SUMCOL_NAME, name,
    679 				   SUMCOL_TYPE, type,
    680 				   -1);
    681 		return;
    682 	} else {
    683 		/* change existing */
    684 		gtk_list_store_set(store, &iter,
    685 				   SUMCOL_NAME, name,
    686 				   SUMCOL_TYPE, type,
    687 				   -1);
    688 	}
    689 }
    690 
    691 /*!
    692  *\brief	Return the columnn type for a row
    693  */
    694 static SummaryColumnType prefs_summary_column_get_column(GtkWidget *list, gint row)
    695 {
    696 	GtkTreeView *list_view = GTK_TREE_VIEW(list);
    697 	GtkTreeModel *model = gtk_tree_view_get_model(list_view);
    698 	GtkTreeIter iter;
    699 	SummaryColumnType result;
    700 
    701 	if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
    702 		return -1;
    703 
    704 	gtk_tree_model_get(model, &iter,
    705 			   SUMCOL_TYPE, &result,
    706 			   -1);
    707 
    708 	return result;
    709 }
    710 
    711 static GtkWidget *prefs_summary_column_list_view_create(const gchar *name)
    712 {
    713 	GtkWidget *list_view;
    714 	GtkTreeSelection *selector;
    715 	GtkTreeModel *model;
    716 
    717 	model = GTK_TREE_MODEL(prefs_summary_column_create_store());
    718 	list_view = gtk_tree_view_new_with_model(model);
    719 	g_object_unref(G_OBJECT(model));
    720 
    721 	selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
    722 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
    723 
    724 	prefs_filtering_create_list_view_columns(GTK_WIDGET(list_view), name);
    725 
    726 	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(list_view),
    727 					       GDK_BUTTON1_MASK,
    728 					       row_targets,
    729 					       G_N_ELEMENTS(row_targets),
    730 					       GDK_ACTION_MOVE);
    731 
    732 	gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(list_view),
    733 					     row_targets,
    734 					     G_N_ELEMENTS(row_targets),
    735 					     GDK_ACTION_MOVE);
    736 
    737 	g_signal_connect(G_OBJECT(list_view), "drag_data_get",
    738 			 G_CALLBACK(drag_data_get),
    739 			 model);
    740 
    741 	g_signal_connect(G_OBJECT(list_view), "drag_data_received",
    742 			 G_CALLBACK(drag_data_received),
    743 			 model);
    744 
    745 	return list_view;
    746 }
    747 
    748 static void prefs_filtering_create_list_view_columns(GtkWidget *list_view,
    749 						     const gchar *name)
    750 {
    751 	GtkTreeViewColumn *column;
    752 	GtkCellRenderer *renderer;
    753 
    754 	renderer = gtk_cell_renderer_text_new();
    755 	column = gtk_tree_view_column_new_with_attributes
    756 		(name, renderer, "text", SUMCOL_NAME, NULL);
    757 	gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
    758 }
    759 
    760 static void drag_data_get(GtkTreeView *tree_view, GdkDragContext *context,
    761 			  GtkSelectionData *data, guint info,
    762 			  guint time, GtkTreeModel *model)
    763 {
    764 	GtkTreeIter iter;
    765 	SummaryColumnType type;
    766 	GtkTreeModel *source_model;
    767 
    768 	if (info != TARGET_INFO_SUMCOL)
    769 		return;
    770 
    771 	if (!gtk_tree_selection_get_selected
    772 			(gtk_tree_view_get_selection(tree_view),
    773 			 &source_model, &iter))
    774 		return;
    775 
    776 	gtk_tree_model_get(source_model, &iter,
    777 			   SUMCOL_TYPE, &type,
    778 			   -1);
    779 
    780 	/* send the type */
    781 	gtk_selection_data_set(data, gtk_selection_data_get_target(data), 8,
    782 		(gchar *) &type, sizeof type);
    783 }
    784 
    785 static void drag_data_received(GtkTreeView *tree_view, GdkDragContext *context,
    786 			       gint x, gint y, GtkSelectionData *data,
    787 			       guint info, guint time, GtkTreeModel *model)
    788 {
    789 	GtkWidget *source;
    790 	GtkTreePath *dst = NULL, *sel = NULL;
    791 	GtkTreeIter isel, idst;
    792 	GtkTreeViewDropPosition pos;
    793 	SummaryColumnType type;
    794 	GtkTreeModel *sel_model;
    795 	gchar *name;
    796 
    797 	source = gtk_drag_get_source_widget(context);
    798 
    799 	if (source == GTK_WIDGET(tree_view)) {
    800 
    801 		/*
    802 		 * Same widget: re-order
    803 		 */
    804 
    805 		if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
    806 					   NULL, &isel)) {
    807 			sel = gtk_tree_model_get_path(model, &isel);
    808 			gtk_tree_view_get_dest_row_at_pos(tree_view, x, y,
    809 							  &dst, &pos);
    810 
    811 			/* NOTE: dst is invalid if selection beyond last row, in that
    812 			 * case move beyond last one (XXX_move_before(..., NULL)) */
    813 
    814 			if (dst)
    815 				gtk_tree_model_get_iter(model, &idst, dst);
    816 			else
    817 				gtk_list_store_move_before(GTK_LIST_STORE(model),
    818 							   &isel,
    819 							   NULL);
    820 
    821 			/* we do not drag if no valid dst and sel, and when
    822 			 * dst and sel are the same (moving after or before
    823 			 * itself doesn't change order...) */
    824 			if ((dst && sel) && gtk_tree_path_compare(sel, dst) != 0) {
    825 				if (pos == GTK_TREE_VIEW_DROP_BEFORE
    826 				||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
    827 					gtk_list_store_move_before(GTK_LIST_STORE(model),
    828 								   &isel,
    829 								   &idst);
    830 				else
    831 					gtk_list_store_move_after(GTK_LIST_STORE(model),
    832 								  &isel,
    833 								  &idst);
    834 			}
    835 			gtk_tree_path_free(dst);
    836 			gtk_tree_path_free(sel);
    837 		}
    838 		gtk_drag_finish(context, TRUE, FALSE, time);
    839 
    840 	} else if (source == summary_col.stock_list_view
    841 	||	   source == summary_col.shown_list_view) {
    842 
    843 		/*
    844 		 * Other widget: change and update
    845 		 */
    846 
    847 		/* get source information and remove */
    848 		if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(
    849 						GTK_TREE_VIEW(source)),
    850 						&sel_model, &isel)) {
    851 			type = *((gint *) gtk_selection_data_get_data(data));
    852 			gtk_list_store_remove(GTK_LIST_STORE(sel_model), &isel);
    853 
    854 			/* get insertion position */
    855 			gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dst, &pos);
    856 
    857 			/* NOTE: dst is invalid if insertion point beyond last row,
    858 			 * just append to list in that case (XXX_store_append()) */
    859 
    860 			if (dst) {
    861 				gtk_tree_model_get_iter(model, &idst, dst);
    862 
    863 				if (pos == GTK_TREE_VIEW_DROP_BEFORE
    864 				||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
    865 					gtk_list_store_insert_before(GTK_LIST_STORE(model),
    866 								     &isel,
    867 								     &idst);
    868 				else
    869 					gtk_list_store_insert_after(GTK_LIST_STORE(model),
    870 								    &isel,
    871 								    &idst);
    872 			} else
    873 				gtk_list_store_append(GTK_LIST_STORE(model),
    874 						      &isel);
    875 
    876 			gtk_list_store_set(GTK_LIST_STORE(model), &isel,
    877 					   SUMCOL_NAME, col_name[type],
    878 					   SUMCOL_TYPE, type, -1);
    879 			gtk_tree_path_free(dst);
    880 		}
    881 		gtk_drag_finish(context, TRUE, FALSE, time);
    882 	}
    883 
    884 	prefs_summary_column_shown_set_active(FALSE);
    885 	prefs_summary_column_stock_set_active(FALSE);
    886 
    887 	/* XXXX: should we call gtk_drag_finish() for other code paths? */
    888 }
    889 
    890 static void prefs_summary_column_shown_set_btn_sensitivity(void)
    891 {
    892 	GtkTreeModel *model = GTK_TREE_MODEL(gtk_tree_view_get_model(
    893 		GTK_TREE_VIEW(summary_col.shown_list_view)));
    894 	GtkTreeSelection *selection = gtk_tree_view_get_selection(
    895 		GTK_TREE_VIEW(summary_col.shown_list_view));
    896 	GtkTreeIter iter;
    897 	GtkTreePath *path;
    898 
    899 	if(!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
    900 		gtk_widget_set_sensitive(summary_col.remove_btn, FALSE);
    901 		gtk_widget_set_sensitive(summary_col.up_btn, FALSE);
    902 		gtk_widget_set_sensitive(summary_col.down_btn, FALSE);
    903 		return;
    904 	}
    905 
    906 	path = gtk_tree_model_get_path(model, &iter);
    907 
    908 	gtk_widget_set_sensitive(summary_col.up_btn, gtk_tree_path_prev(path));
    909 	gtk_widget_set_sensitive(summary_col.down_btn,
    910 				 gtk_tree_model_iter_next(model, &iter));
    911 	gtk_tree_path_free(path);
    912 }
    913 
    914 static void prefs_summary_column_shown_set_active(const gboolean active)
    915 {
    916 	GtkTreeSelection *selection = NULL;
    917 
    918 	gtk_widget_set_sensitive(summary_col.remove_btn, active);
    919 
    920 	if(active == FALSE) {
    921 		selection = gtk_tree_view_get_selection(
    922 			GTK_TREE_VIEW(summary_col.shown_list_view));
    923 		gtk_tree_selection_unselect_all(selection);
    924 
    925 		gtk_widget_set_sensitive(summary_col.up_btn, FALSE);
    926 		gtk_widget_set_sensitive(summary_col.down_btn, FALSE);
    927 	} else {
    928 		prefs_summary_column_shown_set_btn_sensitivity();
    929 	}
    930 }
    931 
    932 static void prefs_summary_column_stock_set_active(const gboolean active)
    933 {
    934 	GtkTreeSelection *selection = NULL;
    935 
    936 	gtk_widget_set_sensitive(summary_col.add_btn, active);
    937 
    938 	if(active == FALSE) {
    939 		selection = gtk_tree_view_get_selection(
    940 			GTK_TREE_VIEW(summary_col.stock_list_view));
    941 		gtk_tree_selection_unselect_all(selection);
    942 	}
    943 }
    944 
    945 static void prefs_summary_column_stock_sel_changed(void)
    946 {
    947 	GtkTreeSelection *selection = gtk_tree_view_get_selection(
    948 		GTK_TREE_VIEW(summary_col.stock_list_view));
    949 	prefs_summary_column_stock_set_active(
    950 		(selection != NULL) ? TRUE : FALSE);
    951 	prefs_summary_column_shown_set_active(
    952 		(selection != NULL) ? FALSE : TRUE);
    953 }
    954 
    955 static void prefs_summary_column_shown_sel_changed(void)
    956 {
    957 	GtkTreeSelection *selection = gtk_tree_view_get_selection(
    958 		GTK_TREE_VIEW(summary_col.shown_list_view));
    959 	prefs_summary_column_shown_set_active(
    960 		(selection != NULL) ? TRUE : FALSE);
    961 	prefs_summary_column_stock_set_active(
    962 		(selection != NULL) ? FALSE : TRUE);
    963 }