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