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