talons

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

prefswindow.c (20196B)


      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 <glib.h>
     20 #include <glib/gi18n.h>
     21 #include <string.h>
     22 #include <gtk/gtk.h>
     23 #include <gdk/gdkkeysyms.h>
     24 
     25 #include "utils.h"
     26 #include "prefswindow.h"
     27 #include "gtkutils.h"
     28 #include "prefs_common.h"
     29 #include "gtk/manage_window.h"
     30 
     31 static void prefs_size_allocate_cb(GtkWidget *widget,
     32 							 GtkAllocation *allocation, gpointer *user_data);
     33 static GtkTreeStore *prefswindow_create_data_store	(void);
     34 static GtkWidget *prefswindow_tree_view_create		(PrefsWindow* prefswindow);
     35 static void prefs_filtering_create_tree_view_columns	(GtkWidget *tree_view);
     36 static gboolean prefswindow_row_selected		(GtkTreeSelection *selector,
     37 							 GtkTreeModel *model,
     38 							 GtkTreePath *path,
     39 							 gboolean currently_selected,
     40 							 gpointer data);
     41 
     42 static void save_all_pages(GSList *prefs_pages)
     43 {
     44 	GSList *cur;
     45 
     46 	for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
     47 		PrefsPage *page = (PrefsPage *) cur->data;
     48 
     49 		if (page->page_open) {
     50 			page->save_page(page);
     51 		}
     52 	}
     53 }
     54 
     55 static gboolean query_can_close_all_pages(GSList *prefs_pages)
     56 {
     57 	GSList *cur;
     58 
     59 	for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
     60 		PrefsPage *page = (PrefsPage *) cur->data;
     61 
     62 		if (page->can_close)
     63 			if (!page->can_close(page))
     64 				return FALSE;
     65 	}
     66 	return TRUE;
     67 }
     68 
     69 static void close_all_pages(GSList *prefs_pages)
     70 {
     71 	GSList *cur;
     72 
     73 	for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
     74 		PrefsPage *page = (PrefsPage *) cur->data;
     75 
     76 		if (page->page_open) {
     77 			page->destroy_widget(page);
     78 			page->page_open = FALSE;
     79 		}
     80 	}
     81 }
     82 
     83 static void apply_button_clicked(GtkButton *button, gpointer user_data)
     84 {
     85 	PrefsWindow *prefswindow = (PrefsWindow *) user_data;
     86 
     87 	prefswindow->dialog_response = PREFSWINDOW_RESPONSE_APPLY;
     88 
     89 	save_all_pages(prefswindow->prefs_pages);
     90 	if (prefswindow->apply_cb)
     91 		prefswindow->apply_cb(prefswindow);
     92 }
     93 
     94 static void close_prefs_window(PrefsWindow *prefswindow)
     95 {
     96 	debug_print("prefs window closed\n");
     97 
     98 	close_all_pages(prefswindow->prefs_pages);
     99 
    100 	if (prefswindow->close_cb)
    101 		prefswindow->close_cb(prefswindow);
    102 
    103 	gtk_widget_destroy(prefswindow->window);
    104 	g_slist_free(prefswindow->prefs_pages);
    105 	if(prefswindow->func != NULL)
    106 		prefswindow->func(prefswindow->data);
    107 	g_free(prefswindow);
    108 }
    109 
    110 static void ok_button_clicked(GtkButton *button, gpointer user_data)
    111 {
    112 	PrefsWindow *prefswindow = (PrefsWindow *) user_data;
    113 
    114 	prefswindow->dialog_response = PREFSWINDOW_RESPONSE_OK;
    115 
    116 	if (query_can_close_all_pages(prefswindow->prefs_pages)) {
    117 		save_all_pages(prefswindow->prefs_pages);
    118 		close_prefs_window(prefswindow);
    119 	}
    120 }
    121 
    122 static void cancel_button_clicked(GtkButton *button, gpointer user_data)
    123 {
    124 	PrefsWindow *prefswindow = (PrefsWindow *) user_data;
    125 
    126 	close_prefs_window(prefswindow);
    127 }
    128 
    129 static gboolean window_closed(GtkWidget *widget, GdkEvent *event, gpointer user_data)
    130 {
    131 	PrefsWindow *prefswindow = (PrefsWindow *) user_data;
    132 
    133 	close_prefs_window(prefswindow);
    134 	return FALSE;
    135 }
    136 
    137 static gboolean prefswindow_key_pressed(GtkWidget *widget, GdkEventKey *event,
    138 				    PrefsWindow *data)
    139 {
    140 	GtkWidget *focused_child;
    141 
    142 	if (event) {
    143 		switch (event->keyval) {
    144 			case GDK_KEY_Return :
    145 			case GDK_KEY_KP_Enter :
    146 				focused_child = gtkut_get_focused_child
    147 					(GTK_CONTAINER(data->notebook));
    148 				/* Press ok, if the focused child is not a text view
    149 				 * and text (anything that accepts return) (can pass
    150 				 * NULL to any of the GTK_xxx() casts) */
    151 				if (!GTK_IS_TEXT_VIEW(focused_child))
    152 					ok_button_clicked(NULL, data);
    153 				break;
    154 			default:
    155 				break;
    156 		}
    157 	}
    158 	return FALSE;
    159 }
    160 
    161 typedef struct FindNodeByName {
    162 	const gchar *name;
    163 	gboolean     found;
    164 	GtkTreeIter  node;
    165 } FindNodeByName;
    166 
    167 static gboolean find_node_by_name(GtkTreeModel *model, GtkTreePath *path,
    168 				  GtkTreeIter *iter, FindNodeByName *data)
    169 {
    170 	gchar *name;
    171 	gboolean result = FALSE;
    172 
    173 	gtk_tree_model_get(model, iter, PREFS_PAGE_TITLE, &name, -1);
    174 	if (name) {
    175 		result = strcmp(name, data->name) == 0;
    176 		if (result) {
    177 			data->found = TRUE;
    178 			data->node  = *iter;
    179 		}
    180 		g_free(name);
    181 	}
    182 
    183 	return result;
    184 }
    185 
    186 static gint prefswindow_tree_sort_by_weight(GtkTreeModel *model,
    187 					    GtkTreeIter  *a,
    188 					    GtkTreeIter  *b,
    189 					    gpointer      context)
    190 {
    191 	gfloat f1, f2;
    192 	gint i1, i2;
    193 
    194 	/* From observation sorting should keep in account the original
    195 	 * order in the prefs_pages list. I.e. if equal weight, prefer
    196 	 * the index in the pages list */
    197 	gtk_tree_model_get(model, a,
    198 			   PREFS_PAGE_INDEX,  &i1,
    199 			   PREFS_PAGE_WEIGHT, &f1, -1);
    200 	gtk_tree_model_get(model, b,
    201 			   PREFS_PAGE_INDEX,  &i2,
    202 			   PREFS_PAGE_WEIGHT, &f2, -1);
    203 
    204 	return f1 < f2 ? -1 : (f1 > f2 ?  1 :
    205 	      (i1 < i2 ?  1 : (i1 > i2 ? -1 : 0)));
    206 }
    207 
    208 static void prefswindow_build_page(PrefsWindow *prefswindow, PrefsPage *page)
    209 {
    210 	GtkWidget *scrolledwin;
    211 
    212 	debug_print("prefswindow_build_page %s\n", page->path);
    213 	if (!page->page_open) {
    214 		scrolledwin = gtk_scrolled_window_new(NULL, NULL);
    215 		gtk_widget_show(scrolledwin);
    216 		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
    217 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    218 
    219 		page->create_widget(page, GTK_WINDOW(prefswindow->window), prefswindow->data);
    220 		gtk_container_add(GTK_CONTAINER(scrolledwin), page->widget);
    221 		gtk_container_add(GTK_CONTAINER(prefswindow->notebook), scrolledwin);
    222 
    223 		page->widget	= scrolledwin;
    224 		page->page_open	= TRUE;
    225 	}
    226 }
    227 
    228 static GSList *prefswindow_build_all_pages(PrefsWindow *prefswindow, GSList *prefs_pages)
    229 {
    230 	GSList *cur;
    231 	prefs_pages = g_slist_reverse(prefs_pages);
    232 	for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
    233 		PrefsPage *page = (PrefsPage *) cur->data;
    234 		prefswindow_build_page(prefswindow, page);
    235 	}
    236 	return g_slist_reverse(prefs_pages);
    237 }
    238 
    239 static void prefswindow_build_tree(GtkWidget *tree_view, GSList *prefs_pages,
    240 									PrefsWindow *prefswindow,
    241 									gboolean preload_pages)
    242 {
    243 	GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model
    244 			(GTK_TREE_VIEW(tree_view)));
    245 	GSList *cur;
    246 	gint index; /* index in pages list */
    247 	GtkTreeSelection *selection;
    248 	GtkTreeIter iter;
    249 
    250 	for (cur = prefs_pages, index = 0; cur != NULL; cur = g_slist_next(cur), index++) {
    251 		PrefsPage *page = (PrefsPage *)cur->data;
    252 		FindNodeByName find_name;
    253 		GtkTreeIter node, child;
    254 		PrefsTreeNode *prefs_node = NULL;
    255 		int i;
    256 
    257 		/* each page tree component string */
    258 		for (i = 0; page->path[i] != NULL; i++) {
    259 			find_name.found = FALSE;
    260 			find_name.name  = page->path[i];
    261 
    262 			/* find node to attach to */
    263 			gtk_tree_model_foreach(GTK_TREE_MODEL(store),
    264 					       (GtkTreeModelForeachFunc) find_node_by_name,
    265 					       &find_name);
    266 			if (find_name.found && (i == 0 || page->path[i] != page->path[i-1])) {
    267 				node = find_name.node;
    268 				gtk_tree_model_get(GTK_TREE_MODEL(store), &node,
    269 						   PREFS_PAGE_DATA, &prefs_node,
    270 						   -1);
    271 			} else {
    272 				GAuto *autoptr;
    273 
    274 				/* create a new top level */
    275 				gtk_tree_store_append(store, &child, i == 0 ? NULL : &node);
    276 				prefs_node = g_new0(PrefsTreeNode, 1);
    277 				autoptr = g_auto_pointer_new(prefs_node);
    278 				gtk_tree_store_set(store, &child,
    279 						   PREFS_PAGE_TITLE, page->path[i],
    280 						   PREFS_PAGE_DATA,  prefs_node,
    281 						   PREFS_PAGE_DATA_AUTO_FREE, autoptr,
    282 						   PREFS_PAGE_INDEX, index,
    283 						   PREFS_PAGE_WEIGHT, 0.0f,
    284 						   -1);
    285 				g_auto_pointer_free(autoptr);
    286 				node = child;
    287 			}
    288 		}
    289 
    290 		/* right now we have a node and its prefs_node */
    291 		if (!prefs_node)
    292 			g_warning("no prefs_node :/");
    293 		else
    294 			prefs_node->page = page;
    295 
    296 		/* parents "inherit" the max weight of the children */
    297 		do {
    298 			gfloat f;
    299 
    300 			gtk_tree_model_get(GTK_TREE_MODEL(store), &node,
    301 					   PREFS_PAGE_WEIGHT, &f,
    302 					   -1);
    303 			if (page->weight > f) {
    304 				f = page->weight;
    305 				gtk_tree_store_set(store, &node,
    306 						   PREFS_PAGE_WEIGHT, f,
    307 						   -1);
    308 			}
    309 			child = node;
    310 		} while (gtk_tree_model_iter_parent(GTK_TREE_MODEL(store),
    311 						    &node, &child));
    312 	}
    313 
    314 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
    315 
    316 	/* set sort func & sort */
    317 	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store),
    318 					PREFS_PAGE_WEIGHT,
    319 					prefswindow_tree_sort_by_weight,
    320 					NULL, NULL);
    321 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
    322 					     PREFS_PAGE_WEIGHT,
    323 					     GTK_SORT_DESCENDING);
    324 
    325 	if (preload_pages)
    326 		prefs_pages = prefswindow_build_all_pages(prefswindow, prefs_pages);
    327 
    328 	/* select first one or its first child if necessary */
    329 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
    330 	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
    331 		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter)) {
    332 			GtkTreeIter parent = iter;
    333 			if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, &parent))
    334 				iter = parent;
    335 		}
    336 		gtk_tree_selection_select_iter(selection, &iter);
    337 	}
    338 }
    339 
    340 void prefswindow_open_full(const gchar *title, GSList *prefs_pages,
    341 							 gpointer data, GDestroyNotify func,
    342 							 gint *save_width, gint *save_height,
    343 							 gboolean preload_pages,
    344 							 PrefsOpenCallbackFunc open_cb,
    345 							 PrefsApplyCallbackFunc apply_cb,
    346 							 PrefsCloseCallbackFunc close_cb)
    347 {
    348 	PrefsWindow *prefswindow;
    349 	gint x;
    350 	gint y;
    351 	static GdkGeometry geometry;
    352 	GtkAdjustment *adj;
    353 	GdkRectangle workarea = {0};
    354 
    355 	prefswindow = g_new0(PrefsWindow, 1);
    356 
    357 	prefswindow->data = data;
    358 	prefswindow->func = func;
    359 	prefswindow->prefs_pages = g_slist_copy(prefs_pages);
    360 	prefswindow->save_width = save_width;
    361 	prefswindow->save_height = save_height;
    362 	prefswindow->open_cb = open_cb;
    363 	prefswindow->apply_cb = apply_cb;
    364 	prefswindow->close_cb = close_cb;
    365 	prefswindow->dialog_response = PREFSWINDOW_RESPONSE_CANCEL;
    366 
    367 	prefswindow->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefswindow");
    368 	gtk_window_set_title(GTK_WINDOW(prefswindow->window), title);
    369 
    370 	gtk_window_set_position (GTK_WINDOW(prefswindow->window), GTK_WIN_POS_CENTER);
    371 	gtk_window_set_modal (GTK_WINDOW (prefswindow->window), TRUE);
    372 	gtk_window_set_resizable (GTK_WINDOW(prefswindow->window), TRUE);
    373 	gtk_window_set_type_hint(GTK_WINDOW(prefswindow->window), GDK_WINDOW_TYPE_HINT_DIALOG);
    374 	gtk_window_set_transient_for (GTK_WINDOW(prefswindow->window),
    375 			GTK_WINDOW(mainwindow_get_mainwindow()->window));
    376 	gtk_container_set_border_width(GTK_CONTAINER(prefswindow->window), 4);
    377 
    378 	prefswindow->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    379 	gtk_widget_show(prefswindow->vbox);
    380 
    381 	prefswindow->paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
    382 	gtk_widget_show(prefswindow->paned);
    383 
    384 	gtk_container_add(GTK_CONTAINER(prefswindow->window), prefswindow->vbox);
    385 
    386 	gtk_box_pack_start(GTK_BOX(prefswindow->vbox), prefswindow->paned, TRUE, TRUE, 0);
    387 
    388 	prefswindow->scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL);
    389 	gtk_widget_show(prefswindow->scrolledwindow1);
    390 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(prefswindow->scrolledwindow1),
    391 			GTK_SHADOW_IN);
    392 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(prefswindow->scrolledwindow1),
    393 			GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    394 
    395 	gtk_paned_add1(GTK_PANED(prefswindow->paned), prefswindow->scrolledwindow1);
    396 
    397 	prefswindow->tree_view = prefswindow_tree_view_create(prefswindow);
    398 	gtk_widget_show(prefswindow->tree_view);
    399 	gtk_container_add(GTK_CONTAINER(prefswindow->scrolledwindow1),
    400 			  prefswindow->tree_view);
    401 
    402 	prefswindow->vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
    403 	gtk_widget_show(prefswindow->vbox2);
    404 
    405 	gtk_paned_add2(GTK_PANED(prefswindow->paned), prefswindow->vbox2);
    406 
    407 	prefswindow->table2 = gtk_grid_new();
    408 	gtk_widget_show(prefswindow->table2);
    409 	gtk_box_pack_start(GTK_BOX(prefswindow->vbox2), prefswindow->table2, TRUE, TRUE, 0);
    410 
    411 	prefswindow->labelframe = gtk_frame_new(NULL);
    412 	gtk_widget_show(prefswindow->labelframe);
    413 	gtk_frame_set_shadow_type(GTK_FRAME(prefswindow->labelframe), GTK_SHADOW_NONE);
    414 	gtk_grid_attach(GTK_GRID(prefswindow->table2), prefswindow->labelframe, 0, 0, 1, 1);
    415 	gtk_widget_set_hexpand(prefswindow->labelframe, TRUE);
    416 	gtk_widget_set_halign(prefswindow->labelframe, GTK_ALIGN_FILL);
    417 
    418 	prefswindow->pagelabel = gtk_label_new("");
    419 	gtk_widget_set_margin_top(prefswindow->pagelabel, 4);
    420 	gtk_widget_set_margin_bottom(prefswindow->pagelabel, 4);
    421 	gtk_widget_set_margin_start(prefswindow->pagelabel, 4);
    422 	gtk_widget_show(prefswindow->pagelabel);
    423 	gtk_label_set_justify(GTK_LABEL(prefswindow->pagelabel), GTK_JUSTIFY_LEFT);
    424 	gtk_label_set_xalign(GTK_LABEL(prefswindow->pagelabel), 0.0);
    425 	gtk_label_set_yalign(GTK_LABEL(prefswindow->pagelabel), 0.0);
    426 	gtk_container_add(GTK_CONTAINER(prefswindow->labelframe), prefswindow->pagelabel);
    427 
    428 	prefswindow->notebook = gtk_notebook_new();
    429 	gtk_widget_show(prefswindow->notebook);
    430 	gtk_notebook_set_scrollable(GTK_NOTEBOOK(prefswindow->notebook), TRUE);
    431 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefswindow->notebook), FALSE);
    432 	gtk_notebook_set_show_border(GTK_NOTEBOOK(prefswindow->notebook), FALSE);
    433 
    434 	gtk_grid_attach(GTK_GRID(prefswindow->table2), prefswindow->notebook, 0, 1, 1, 1);
    435 	gtk_widget_set_vexpand(prefswindow->notebook, TRUE);
    436 	gtk_widget_set_hexpand(prefswindow->notebook, TRUE);
    437 	gtk_widget_set_halign(prefswindow->notebook, GTK_ALIGN_FILL);
    438 
    439 	prefswindow->empty_page = gtk_label_new("");
    440 	gtk_widget_show(prefswindow->empty_page);
    441 	gtk_container_add(GTK_CONTAINER(prefswindow->notebook), prefswindow->empty_page);
    442 
    443 	prefswindow_build_tree(prefswindow->tree_view, prefs_pages, prefswindow,
    444 							preload_pages);
    445 
    446 	if (open_cb)
    447 		open_cb(prefswindow);
    448 
    449 	gtk_widget_grab_focus(prefswindow->tree_view);
    450 
    451 	gtkut_stock_button_set_create(&prefswindow->confirm_area,
    452 				      &prefswindow->apply_btn, NULL, _("_Apply"),
    453 				      &prefswindow->cancel_btn, NULL, _("_Cancel"),
    454 				      &prefswindow->ok_btn, NULL, _("_OK"));
    455 	gtk_widget_show_all(prefswindow->confirm_area);
    456 	gtk_widget_show(prefswindow->vbox);
    457 	gtk_widget_show(prefswindow->scrolledwindow1);
    458 
    459 	gtk_box_pack_start(GTK_BOX(prefswindow->vbox), prefswindow->confirm_area, FALSE, FALSE, 0);
    460 
    461 	g_signal_connect(G_OBJECT(prefswindow->ok_btn), "clicked",
    462 			 G_CALLBACK(ok_button_clicked), prefswindow);
    463 	g_signal_connect(G_OBJECT(prefswindow->cancel_btn), "clicked",
    464 			 G_CALLBACK(cancel_button_clicked), prefswindow);
    465 	g_signal_connect(G_OBJECT(prefswindow->apply_btn), "clicked",
    466 			 G_CALLBACK(apply_button_clicked), prefswindow);
    467 	g_signal_connect(G_OBJECT(prefswindow->window), "delete_event",
    468 			 G_CALLBACK(window_closed), prefswindow);
    469 	g_signal_connect(G_OBJECT(prefswindow->window), "key_press_event",
    470 			   G_CALLBACK(prefswindow_key_pressed), &(prefswindow->window));
    471 
    472 	/* connect to callback only if we have non-NULL pointers to store size to */
    473 	if (prefswindow->save_width && prefswindow->save_height) {
    474 		g_signal_connect(G_OBJECT(prefswindow->window), "size_allocate",
    475 				 G_CALLBACK(prefs_size_allocate_cb), prefswindow);
    476 	}
    477 
    478 	MANAGE_WINDOW_SIGNALS_CONNECT(prefswindow->window);
    479 
    480 	gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
    481 				 &workarea);
    482 	x =  workarea.width;
    483 	y =  workarea.height;
    484 
    485 	if (!geometry.min_height) {
    486 
    487 		if (x < 800 && y < 600) {
    488 			geometry.min_width = 600;
    489 			geometry.min_height = 440;
    490 		} else {
    491 			geometry.min_width = 700;
    492 			geometry.min_height = 550;
    493 		}
    494 	}
    495 	gtk_window_set_geometry_hints(GTK_WINDOW(prefswindow->window),
    496 			NULL, &geometry, GDK_HINT_MIN_SIZE);
    497 	if (prefswindow->save_width && prefswindow->save_height) {
    498 		gtk_window_set_default_size(GTK_WINDOW(prefswindow->window),
    499 				*(prefswindow->save_width),
    500 					    *(prefswindow->save_height));
    501 	}
    502 
    503 	gtk_widget_show(prefswindow->window);
    504 	adj = gtk_scrolled_window_get_vadjustment(
    505 			GTK_SCROLLED_WINDOW(prefswindow->scrolledwindow1));
    506 	gtk_adjustment_set_value(adj, gtk_adjustment_get_lower(adj));
    507 }
    508 
    509 void prefswindow_open(const gchar *title, GSList *prefs_pages, gpointer data,
    510 					 gint *save_width, gint *save_height,
    511 					 PrefsOpenCallbackFunc open_cb,
    512 					 PrefsApplyCallbackFunc apply_cb,
    513 					 PrefsCloseCallbackFunc close_cb)
    514 {
    515 	prefswindow_open_full(title, prefs_pages, data, NULL, save_width, save_height,
    516 						  FALSE, open_cb, apply_cb, close_cb);
    517 }
    518 
    519 /*!
    520  *\brief	Save Gtk object size to prefs dataset
    521  */
    522 static void prefs_size_allocate_cb(GtkWidget *widget,
    523 					 GtkAllocation *allocation, gpointer *user_data)
    524 {
    525 	PrefsWindow *prefswindow = (PrefsWindow *) user_data;
    526 
    527 	cm_return_if_fail(allocation != NULL);
    528 
    529 	/* don't try to save size to NULL pointers */
    530 	if (prefswindow && prefswindow->save_width && prefswindow->save_height) {
    531 		gtk_window_get_size(GTK_WINDOW(widget),
    532 			prefswindow->save_width, prefswindow->save_height);
    533 	}
    534 }
    535 
    536 static GtkTreeStore *prefswindow_create_data_store(void)
    537 {
    538 	return gtk_tree_store_new(N_PREFS_PAGE_COLUMNS,
    539 				  G_TYPE_STRING,
    540 				  G_TYPE_POINTER,
    541 				  G_TYPE_AUTO_POINTER,
    542 				  G_TYPE_FLOAT,
    543 				  G_TYPE_INT,
    544 				  -1);
    545 }
    546 
    547 static GtkWidget *prefswindow_tree_view_create(PrefsWindow *prefswindow)
    548 {
    549 	GtkTreeView *tree_view;
    550 	GtkTreeSelection *selector;
    551 	GtkTreeModel *model;
    552 
    553 	model = GTK_TREE_MODEL(prefswindow_create_data_store());
    554 	tree_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
    555 	g_object_unref(model);
    556 
    557 	selector = gtk_tree_view_get_selection(tree_view);
    558 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
    559 	gtk_tree_selection_set_select_function(selector, prefswindow_row_selected,
    560 					       prefswindow, NULL);
    561 
    562 	/* create the columns */
    563 	prefs_filtering_create_tree_view_columns(GTK_WIDGET(tree_view));
    564 
    565 	return GTK_WIDGET(tree_view);
    566 }
    567 
    568 static void prefs_filtering_create_tree_view_columns(GtkWidget *tree_view)
    569 {
    570 	GtkTreeViewColumn *column;
    571 	GtkCellRenderer *renderer;
    572 
    573 	renderer = gtk_cell_renderer_text_new();
    574 	column = gtk_tree_view_column_new_with_attributes
    575 		(_("Page Index"),
    576 		 renderer,
    577 		 "text", PREFS_PAGE_TITLE,
    578 		 NULL);
    579 	gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
    580 }
    581 
    582 static gboolean prefswindow_row_selected(GtkTreeSelection *selector,
    583 					 GtkTreeModel *model,
    584 					 GtkTreePath *path,
    585 					 gboolean currently_selected,
    586 					 gpointer data)
    587 {
    588 	PrefsTreeNode *prefsnode;
    589 	PrefsPage *page;
    590 	PrefsWindow *prefswindow = (PrefsWindow *) data;
    591 	gfloat lower;
    592 	gchar *labeltext;
    593 	gint pagenum, i;
    594 	GtkTreeIter iter;
    595 	GtkAdjustment *adj;
    596 	gchar *markup;
    597 
    598 	if (currently_selected)
    599 		return TRUE;
    600 	if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
    601 		return TRUE;
    602 
    603 	gtk_tree_model_get(model, &iter, PREFS_PAGE_DATA, &prefsnode, -1);
    604 	page = prefsnode->page;
    605 
    606 	debug_print("%f\n", prefsnode->treeweight);
    607 
    608 	if (page == NULL) {
    609 		gtk_label_set_text(GTK_LABEL(prefswindow->pagelabel), "");
    610 		pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(prefswindow->notebook),
    611 						prefswindow->empty_page);
    612 		gtk_notebook_set_current_page(GTK_NOTEBOOK(prefswindow->notebook),
    613 					      pagenum);
    614 		return TRUE;
    615 	}
    616 
    617 	prefswindow_build_page(prefswindow, page);
    618 
    619 	i = 0;
    620 	while (page->path[i + 1] != 0)
    621 		i++;
    622 	labeltext = page->path[i];
    623 
    624 	markup = g_markup_printf_escaped("<span weight=\"bold\">\%s</span>", labeltext);
    625 	gtk_label_set_markup(GTK_LABEL(prefswindow->pagelabel), markup);
    626 	g_free(markup);
    627 
    628 	pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(prefswindow->notebook),
    629 					page->widget);
    630 	gtk_notebook_set_current_page(GTK_NOTEBOOK(prefswindow->notebook),
    631 				      pagenum);
    632 
    633 	adj = gtk_scrolled_window_get_vadjustment(
    634 			GTK_SCROLLED_WINDOW(page->widget));
    635 	lower = gtk_adjustment_get_lower(adj);
    636 	gtk_adjustment_set_value(adj, lower);
    637 	adj = gtk_scrolled_window_get_hadjustment(
    638 			GTK_SCROLLED_WINDOW(page->widget));
    639 	gtk_adjustment_set_value(adj, lower);
    640 	return TRUE;
    641 }