xref: /linux/scripts/kconfig/gconf.c (revision b957df3b858d16ba3d4291233569bba09cfd08c7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
4  */
5 
6 #include <stdlib.h>
7 #include "lkc.h"
8 #include "images.h"
9 
10 #include <glade/glade.h>
11 #include <gtk/gtk.h>
12 #include <glib.h>
13 #include <gdk/gdkkeysyms.h>
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <strings.h>
18 #include <unistd.h>
19 #include <time.h>
20 
21 //#define DEBUG
22 
23 enum {
24 	SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
25 };
26 
27 enum {
28 	OPT_NORMAL, OPT_ALL, OPT_PROMPT
29 };
30 
31 static gint view_mode = FULL_VIEW;
32 static gboolean show_name = TRUE;
33 static gboolean show_range = TRUE;
34 static gboolean show_value = TRUE;
35 static gboolean resizeable = FALSE;
36 static int opt_mode = OPT_NORMAL;
37 
38 GtkWidget *main_wnd = NULL;
39 GtkWidget *tree1_w = NULL;	// left  frame
40 GtkWidget *tree2_w = NULL;	// right frame
41 GtkWidget *text_w = NULL;
42 GtkWidget *hpaned = NULL;
43 GtkWidget *vpaned = NULL;
44 GtkWidget *back_btn = NULL;
45 GtkWidget *save_btn = NULL;
46 GtkWidget *save_menu_item = NULL;
47 
48 GtkTextTag *tag1, *tag2;
49 GdkColor color;
50 
51 GtkTreeStore *tree1, *tree2, *tree;
52 GtkTreeModel *model1, *model2;
53 static GtkTreeIter *parents[256];
54 static gint indent;
55 
56 static struct menu *current; // current node for SINGLE view
57 static struct menu *browsed; // browsed node for SPLIT view
58 
59 enum {
60 	COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
61 	COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
62 	COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
63 	COL_NUMBER
64 };
65 
66 static void display_list(void);
67 static void display_tree(struct menu *menu);
68 static void display_tree_part(void);
69 static void update_tree(struct menu *src, GtkTreeIter * dst);
70 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
71 static gchar **fill_row(struct menu *menu);
72 static void conf_changed(void);
73 
74 /* Helping/Debugging Functions */
75 #ifdef DEBUG
76 static const char *dbg_sym_flags(int val)
77 {
78 	static char buf[256];
79 
80 	bzero(buf, 256);
81 
82 	if (val & SYMBOL_CONST)
83 		strcat(buf, "const/");
84 	if (val & SYMBOL_CHECK)
85 		strcat(buf, "check/");
86 	if (val & SYMBOL_CHOICEVAL)
87 		strcat(buf, "choiceval/");
88 	if (val & SYMBOL_VALID)
89 		strcat(buf, "valid/");
90 	if (val & SYMBOL_WRITE)
91 		strcat(buf, "write/");
92 	if (val & SYMBOL_CHANGED)
93 		strcat(buf, "changed/");
94 
95 	buf[strlen(buf) - 1] = '\0';
96 
97 	return buf;
98 }
99 #endif
100 
101 static void replace_button_icon(GladeXML *xml, GdkDrawable *window,
102 				GtkStyle *style, gchar *btn_name, gchar **xpm)
103 {
104 	GdkPixmap *pixmap;
105 	GdkBitmap *mask;
106 	GtkToolButton *button;
107 	GtkWidget *image;
108 
109 	pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
110 					      &style->bg[GTK_STATE_NORMAL],
111 					      xpm);
112 
113 	button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
114 	image = gtk_image_new_from_pixmap(pixmap, mask);
115 	gtk_widget_show(image);
116 	gtk_tool_button_set_icon_widget(button, image);
117 }
118 
119 /* Main Window Initialization */
120 static void init_main_window(const gchar *glade_file)
121 {
122 	GladeXML *xml;
123 	GtkWidget *widget;
124 	GtkTextBuffer *txtbuf;
125 	GtkStyle *style;
126 
127 	xml = glade_xml_new(glade_file, "window1", NULL);
128 	if (!xml)
129 		g_error("GUI loading failed !\n");
130 	glade_xml_signal_autoconnect(xml);
131 
132 	main_wnd = glade_xml_get_widget(xml, "window1");
133 	hpaned = glade_xml_get_widget(xml, "hpaned1");
134 	vpaned = glade_xml_get_widget(xml, "vpaned1");
135 	tree1_w = glade_xml_get_widget(xml, "treeview1");
136 	tree2_w = glade_xml_get_widget(xml, "treeview2");
137 	text_w = glade_xml_get_widget(xml, "textview3");
138 
139 	back_btn = glade_xml_get_widget(xml, "button1");
140 	gtk_widget_set_sensitive(back_btn, FALSE);
141 
142 	widget = glade_xml_get_widget(xml, "show_name1");
143 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
144 				       show_name);
145 
146 	widget = glade_xml_get_widget(xml, "show_range1");
147 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
148 				       show_range);
149 
150 	widget = glade_xml_get_widget(xml, "show_data1");
151 	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
152 				       show_value);
153 
154 	save_btn = glade_xml_get_widget(xml, "button3");
155 	save_menu_item = glade_xml_get_widget(xml, "save1");
156 	conf_set_changed_callback(conf_changed);
157 
158 	style = gtk_widget_get_style(main_wnd);
159 	widget = glade_xml_get_widget(xml, "toolbar1");
160 
161 	replace_button_icon(xml, main_wnd->window, style,
162 			    "button4", (gchar **) xpm_single_view);
163 	replace_button_icon(xml, main_wnd->window, style,
164 			    "button5", (gchar **) xpm_split_view);
165 	replace_button_icon(xml, main_wnd->window, style,
166 			    "button6", (gchar **) xpm_tree_view);
167 
168 	txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
169 	tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
170 					  "foreground", "red",
171 					  "weight", PANGO_WEIGHT_BOLD,
172 					  NULL);
173 	tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
174 					  /*"style", PANGO_STYLE_OBLIQUE, */
175 					  NULL);
176 
177 	gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
178 
179 	gtk_widget_show(main_wnd);
180 }
181 
182 static void init_tree_model(void)
183 {
184 	gint i;
185 
186 	tree = tree2 = gtk_tree_store_new(COL_NUMBER,
187 					  G_TYPE_STRING, G_TYPE_STRING,
188 					  G_TYPE_STRING, G_TYPE_STRING,
189 					  G_TYPE_STRING, G_TYPE_STRING,
190 					  G_TYPE_POINTER, GDK_TYPE_COLOR,
191 					  G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
192 					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
193 					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
194 					  G_TYPE_BOOLEAN);
195 	model2 = GTK_TREE_MODEL(tree2);
196 
197 	for (parents[0] = NULL, i = 1; i < 256; i++)
198 		parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
199 
200 	tree1 = gtk_tree_store_new(COL_NUMBER,
201 				   G_TYPE_STRING, G_TYPE_STRING,
202 				   G_TYPE_STRING, G_TYPE_STRING,
203 				   G_TYPE_STRING, G_TYPE_STRING,
204 				   G_TYPE_POINTER, GDK_TYPE_COLOR,
205 				   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
206 				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
207 				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
208 				   G_TYPE_BOOLEAN);
209 	model1 = GTK_TREE_MODEL(tree1);
210 }
211 
212 static void init_left_tree(void)
213 {
214 	GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
215 	GtkCellRenderer *renderer;
216 	GtkTreeSelection *sel;
217 	GtkTreeViewColumn *column;
218 
219 	gtk_tree_view_set_model(view, model1);
220 	gtk_tree_view_set_headers_visible(view, TRUE);
221 	gtk_tree_view_set_rules_hint(view, TRUE);
222 
223 	column = gtk_tree_view_column_new();
224 	gtk_tree_view_append_column(view, column);
225 	gtk_tree_view_column_set_title(column, "Options");
226 
227 	renderer = gtk_cell_renderer_toggle_new();
228 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
229 					renderer, FALSE);
230 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
231 					    renderer,
232 					    "active", COL_BTNACT,
233 					    "inconsistent", COL_BTNINC,
234 					    "visible", COL_BTNVIS,
235 					    "radio", COL_BTNRAD, NULL);
236 	renderer = gtk_cell_renderer_text_new();
237 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
238 					renderer, FALSE);
239 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
240 					    renderer,
241 					    "text", COL_OPTION,
242 					    "foreground-gdk",
243 					    COL_COLOR, NULL);
244 
245 	sel = gtk_tree_view_get_selection(view);
246 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
247 	gtk_widget_realize(tree1_w);
248 }
249 
250 static void renderer_edited(GtkCellRendererText * cell,
251 			    const gchar * path_string,
252 			    const gchar * new_text, gpointer user_data);
253 
254 static void init_right_tree(void)
255 {
256 	GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
257 	GtkCellRenderer *renderer;
258 	GtkTreeSelection *sel;
259 	GtkTreeViewColumn *column;
260 	gint i;
261 
262 	gtk_tree_view_set_model(view, model2);
263 	gtk_tree_view_set_headers_visible(view, TRUE);
264 	gtk_tree_view_set_rules_hint(view, TRUE);
265 
266 	column = gtk_tree_view_column_new();
267 	gtk_tree_view_append_column(view, column);
268 	gtk_tree_view_column_set_title(column, "Options");
269 
270 	renderer = gtk_cell_renderer_pixbuf_new();
271 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
272 					renderer, FALSE);
273 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
274 					    renderer,
275 					    "pixbuf", COL_PIXBUF,
276 					    "visible", COL_PIXVIS, NULL);
277 	renderer = gtk_cell_renderer_toggle_new();
278 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
279 					renderer, FALSE);
280 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
281 					    renderer,
282 					    "active", COL_BTNACT,
283 					    "inconsistent", COL_BTNINC,
284 					    "visible", COL_BTNVIS,
285 					    "radio", COL_BTNRAD, NULL);
286 	renderer = gtk_cell_renderer_text_new();
287 	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
288 					renderer, FALSE);
289 	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
290 					    renderer,
291 					    "text", COL_OPTION,
292 					    "foreground-gdk",
293 					    COL_COLOR, NULL);
294 
295 	renderer = gtk_cell_renderer_text_new();
296 	gtk_tree_view_insert_column_with_attributes(view, -1,
297 						    "Name", renderer,
298 						    "text", COL_NAME,
299 						    "foreground-gdk",
300 						    COL_COLOR, NULL);
301 	renderer = gtk_cell_renderer_text_new();
302 	gtk_tree_view_insert_column_with_attributes(view, -1,
303 						    "N", renderer,
304 						    "text", COL_NO,
305 						    "foreground-gdk",
306 						    COL_COLOR, NULL);
307 	renderer = gtk_cell_renderer_text_new();
308 	gtk_tree_view_insert_column_with_attributes(view, -1,
309 						    "M", renderer,
310 						    "text", COL_MOD,
311 						    "foreground-gdk",
312 						    COL_COLOR, NULL);
313 	renderer = gtk_cell_renderer_text_new();
314 	gtk_tree_view_insert_column_with_attributes(view, -1,
315 						    "Y", renderer,
316 						    "text", COL_YES,
317 						    "foreground-gdk",
318 						    COL_COLOR, NULL);
319 	renderer = gtk_cell_renderer_text_new();
320 	gtk_tree_view_insert_column_with_attributes(view, -1,
321 						    "Value", renderer,
322 						    "text", COL_VALUE,
323 						    "editable",
324 						    COL_EDIT,
325 						    "foreground-gdk",
326 						    COL_COLOR, NULL);
327 	g_signal_connect(G_OBJECT(renderer), "edited",
328 			 G_CALLBACK(renderer_edited), NULL);
329 
330 	column = gtk_tree_view_get_column(view, COL_NAME);
331 	gtk_tree_view_column_set_visible(column, show_name);
332 	column = gtk_tree_view_get_column(view, COL_NO);
333 	gtk_tree_view_column_set_visible(column, show_range);
334 	column = gtk_tree_view_get_column(view, COL_MOD);
335 	gtk_tree_view_column_set_visible(column, show_range);
336 	column = gtk_tree_view_get_column(view, COL_YES);
337 	gtk_tree_view_column_set_visible(column, show_range);
338 	column = gtk_tree_view_get_column(view, COL_VALUE);
339 	gtk_tree_view_column_set_visible(column, show_value);
340 
341 	if (resizeable) {
342 		for (i = 0; i < COL_VALUE; i++) {
343 			column = gtk_tree_view_get_column(view, i);
344 			gtk_tree_view_column_set_resizable(column, TRUE);
345 		}
346 	}
347 
348 	sel = gtk_tree_view_get_selection(view);
349 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
350 }
351 
352 
353 /* Utility Functions */
354 
355 
356 static void text_insert_help(struct menu *menu)
357 {
358 	GtkTextBuffer *buffer;
359 	GtkTextIter start, end;
360 	const char *prompt = menu_get_prompt(menu);
361 	struct gstr help = str_new();
362 
363 	menu_get_ext_help(menu, &help);
364 
365 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
366 	gtk_text_buffer_get_bounds(buffer, &start, &end);
367 	gtk_text_buffer_delete(buffer, &start, &end);
368 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
369 
370 	gtk_text_buffer_get_end_iter(buffer, &end);
371 	gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
372 					 NULL);
373 	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
374 	gtk_text_buffer_get_end_iter(buffer, &end);
375 	gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
376 					 NULL);
377 	str_free(&help);
378 }
379 
380 
381 static void text_insert_msg(const char *title, const char *message)
382 {
383 	GtkTextBuffer *buffer;
384 	GtkTextIter start, end;
385 	const char *msg = message;
386 
387 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
388 	gtk_text_buffer_get_bounds(buffer, &start, &end);
389 	gtk_text_buffer_delete(buffer, &start, &end);
390 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
391 
392 	gtk_text_buffer_get_end_iter(buffer, &end);
393 	gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
394 					 NULL);
395 	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
396 	gtk_text_buffer_get_end_iter(buffer, &end);
397 	gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
398 					 NULL);
399 }
400 
401 
402 /* Main Windows Callbacks */
403 
404 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
405 gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
406 				 gpointer user_data)
407 {
408 	GtkWidget *dialog, *label;
409 	gint result;
410 
411 	if (!conf_get_changed())
412 		return FALSE;
413 
414 	dialog = gtk_dialog_new_with_buttons("Warning !",
415 					     GTK_WINDOW(main_wnd),
416 					     (GtkDialogFlags)
417 					     (GTK_DIALOG_MODAL |
418 					      GTK_DIALOG_DESTROY_WITH_PARENT),
419 					     GTK_STOCK_OK,
420 					     GTK_RESPONSE_YES,
421 					     GTK_STOCK_NO,
422 					     GTK_RESPONSE_NO,
423 					     GTK_STOCK_CANCEL,
424 					     GTK_RESPONSE_CANCEL, NULL);
425 	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
426 					GTK_RESPONSE_CANCEL);
427 
428 	label = gtk_label_new("\nSave configuration ?\n");
429 	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
430 	gtk_widget_show(label);
431 
432 	result = gtk_dialog_run(GTK_DIALOG(dialog));
433 	switch (result) {
434 	case GTK_RESPONSE_YES:
435 		on_save_activate(NULL, NULL);
436 		return FALSE;
437 	case GTK_RESPONSE_NO:
438 		return FALSE;
439 	case GTK_RESPONSE_CANCEL:
440 	case GTK_RESPONSE_DELETE_EVENT:
441 	default:
442 		gtk_widget_destroy(dialog);
443 		return TRUE;
444 	}
445 
446 	return FALSE;
447 }
448 
449 
450 void on_window1_destroy(GtkObject * object, gpointer user_data)
451 {
452 	gtk_main_quit();
453 }
454 
455 
456 void
457 on_window1_size_request(GtkWidget * widget,
458 			GtkRequisition * requisition, gpointer user_data)
459 {
460 	static gint old_h;
461 	gint w, h;
462 
463 	if (widget->window == NULL)
464 		gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
465 	else
466 		gdk_window_get_size(widget->window, &w, &h);
467 
468 	if (h == old_h)
469 		return;
470 	old_h = h;
471 
472 	gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
473 }
474 
475 
476 /* Menu & Toolbar Callbacks */
477 
478 
479 static void
480 load_filename(GtkFileSelection * file_selector, gpointer user_data)
481 {
482 	const gchar *fn;
483 
484 	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
485 					     (user_data));
486 
487 	if (conf_read(fn))
488 		text_insert_msg("Error", "Unable to load configuration !");
489 	else
490 		display_tree(&rootmenu);
491 }
492 
493 void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
494 {
495 	GtkWidget *fs;
496 
497 	fs = gtk_file_selection_new("Load file...");
498 	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
499 			 "clicked",
500 			 G_CALLBACK(load_filename), (gpointer) fs);
501 	g_signal_connect_swapped(GTK_OBJECT
502 				 (GTK_FILE_SELECTION(fs)->ok_button),
503 				 "clicked", G_CALLBACK(gtk_widget_destroy),
504 				 (gpointer) fs);
505 	g_signal_connect_swapped(GTK_OBJECT
506 				 (GTK_FILE_SELECTION(fs)->cancel_button),
507 				 "clicked", G_CALLBACK(gtk_widget_destroy),
508 				 (gpointer) fs);
509 	gtk_widget_show(fs);
510 }
511 
512 
513 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
514 {
515 	if (conf_write(NULL))
516 		text_insert_msg("Error", "Unable to save configuration !");
517 	conf_write_autoconf(0);
518 }
519 
520 
521 static void
522 store_filename(GtkFileSelection * file_selector, gpointer user_data)
523 {
524 	const gchar *fn;
525 
526 	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
527 					     (user_data));
528 
529 	if (conf_write(fn))
530 		text_insert_msg("Error", "Unable to save configuration !");
531 
532 	gtk_widget_destroy(GTK_WIDGET(user_data));
533 }
534 
535 void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
536 {
537 	GtkWidget *fs;
538 
539 	fs = gtk_file_selection_new("Save file as...");
540 	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
541 			 "clicked",
542 			 G_CALLBACK(store_filename), (gpointer) fs);
543 	g_signal_connect_swapped(GTK_OBJECT
544 				 (GTK_FILE_SELECTION(fs)->ok_button),
545 				 "clicked", G_CALLBACK(gtk_widget_destroy),
546 				 (gpointer) fs);
547 	g_signal_connect_swapped(GTK_OBJECT
548 				 (GTK_FILE_SELECTION(fs)->cancel_button),
549 				 "clicked", G_CALLBACK(gtk_widget_destroy),
550 				 (gpointer) fs);
551 	gtk_widget_show(fs);
552 }
553 
554 
555 void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
556 {
557 	if (!on_window1_delete_event(NULL, NULL, NULL))
558 		gtk_widget_destroy(GTK_WIDGET(main_wnd));
559 }
560 
561 
562 void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
563 {
564 	GtkTreeViewColumn *col;
565 
566 	show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
567 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
568 	if (col)
569 		gtk_tree_view_column_set_visible(col, show_name);
570 }
571 
572 
573 void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
574 {
575 	GtkTreeViewColumn *col;
576 
577 	show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
578 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
579 	if (col)
580 		gtk_tree_view_column_set_visible(col, show_range);
581 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
582 	if (col)
583 		gtk_tree_view_column_set_visible(col, show_range);
584 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
585 	if (col)
586 		gtk_tree_view_column_set_visible(col, show_range);
587 
588 }
589 
590 
591 void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
592 {
593 	GtkTreeViewColumn *col;
594 
595 	show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
596 	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
597 	if (col)
598 		gtk_tree_view_column_set_visible(col, show_value);
599 }
600 
601 
602 void
603 on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
604 {
605 	opt_mode = OPT_NORMAL;
606 	gtk_tree_store_clear(tree2);
607 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
608 }
609 
610 
611 void
612 on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
613 {
614 	opt_mode = OPT_ALL;
615 	gtk_tree_store_clear(tree2);
616 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
617 }
618 
619 
620 void
621 on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
622 {
623 	opt_mode = OPT_PROMPT;
624 	gtk_tree_store_clear(tree2);
625 	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
626 }
627 
628 
629 void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
630 {
631 	GtkWidget *dialog;
632 	const gchar *intro_text =
633 	    "Welcome to gconfig, the GTK+ graphical configuration tool.\n"
634 	    "For each option, a blank box indicates the feature is disabled, a\n"
635 	    "check indicates it is enabled, and a dot indicates that it is to\n"
636 	    "be compiled as a module.  Clicking on the box will cycle through the three states.\n"
637 	    "\n"
638 	    "If you do not see an option (e.g., a device driver) that you\n"
639 	    "believe should be present, try turning on Show All Options\n"
640 	    "under the Options menu.\n"
641 	    "Although there is no cross reference yet to help you figure out\n"
642 	    "what other options must be enabled to support the option you\n"
643 	    "are interested in, you can still view the help of a grayed-out\n"
644 	    "option.";
645 
646 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
647 					GTK_DIALOG_DESTROY_WITH_PARENT,
648 					GTK_MESSAGE_INFO,
649 					GTK_BUTTONS_CLOSE, "%s", intro_text);
650 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
651 				 G_CALLBACK(gtk_widget_destroy),
652 				 GTK_OBJECT(dialog));
653 	gtk_widget_show_all(dialog);
654 }
655 
656 
657 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
658 {
659 	GtkWidget *dialog;
660 	const gchar *about_text =
661 	    "gconfig is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
662 	      "Based on the source code from Roman Zippel.\n";
663 
664 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
665 					GTK_DIALOG_DESTROY_WITH_PARENT,
666 					GTK_MESSAGE_INFO,
667 					GTK_BUTTONS_CLOSE, "%s", about_text);
668 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
669 				 G_CALLBACK(gtk_widget_destroy),
670 				 GTK_OBJECT(dialog));
671 	gtk_widget_show_all(dialog);
672 }
673 
674 
675 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
676 {
677 	GtkWidget *dialog;
678 	const gchar *license_text =
679 	    "gconfig is released under the terms of the GNU GPL v2.\n"
680 	      "For more information, please see the source code or\n"
681 	      "visit http://www.fsf.org/licenses/licenses.html\n";
682 
683 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
684 					GTK_DIALOG_DESTROY_WITH_PARENT,
685 					GTK_MESSAGE_INFO,
686 					GTK_BUTTONS_CLOSE, "%s", license_text);
687 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
688 				 G_CALLBACK(gtk_widget_destroy),
689 				 GTK_OBJECT(dialog));
690 	gtk_widget_show_all(dialog);
691 }
692 
693 
694 void on_back_clicked(GtkButton * button, gpointer user_data)
695 {
696 	enum prop_type ptype;
697 
698 	current = current->parent;
699 	ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
700 	if (ptype != P_MENU)
701 		current = current->parent;
702 	display_tree_part();
703 
704 	if (current == &rootmenu)
705 		gtk_widget_set_sensitive(back_btn, FALSE);
706 }
707 
708 
709 void on_load_clicked(GtkButton * button, gpointer user_data)
710 {
711 	on_load1_activate(NULL, user_data);
712 }
713 
714 
715 void on_single_clicked(GtkButton * button, gpointer user_data)
716 {
717 	view_mode = SINGLE_VIEW;
718 	gtk_widget_hide(tree1_w);
719 	current = &rootmenu;
720 	display_tree_part();
721 }
722 
723 
724 void on_split_clicked(GtkButton * button, gpointer user_data)
725 {
726 	gint w, h;
727 	view_mode = SPLIT_VIEW;
728 	gtk_widget_show(tree1_w);
729 	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
730 	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
731 	if (tree2)
732 		gtk_tree_store_clear(tree2);
733 	display_list();
734 
735 	/* Disable back btn, like in full mode. */
736 	gtk_widget_set_sensitive(back_btn, FALSE);
737 }
738 
739 
740 void on_full_clicked(GtkButton * button, gpointer user_data)
741 {
742 	view_mode = FULL_VIEW;
743 	gtk_widget_hide(tree1_w);
744 	if (tree2)
745 		gtk_tree_store_clear(tree2);
746 	display_tree(&rootmenu);
747 	gtk_widget_set_sensitive(back_btn, FALSE);
748 }
749 
750 
751 void on_collapse_clicked(GtkButton * button, gpointer user_data)
752 {
753 	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
754 }
755 
756 
757 void on_expand_clicked(GtkButton * button, gpointer user_data)
758 {
759 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
760 }
761 
762 
763 /* CTree Callbacks */
764 
765 /* Change hex/int/string value in the cell */
766 static void renderer_edited(GtkCellRendererText * cell,
767 			    const gchar * path_string,
768 			    const gchar * new_text, gpointer user_data)
769 {
770 	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
771 	GtkTreeIter iter;
772 	const char *old_def, *new_def;
773 	struct menu *menu;
774 	struct symbol *sym;
775 
776 	if (!gtk_tree_model_get_iter(model2, &iter, path))
777 		return;
778 
779 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
780 	sym = menu->sym;
781 
782 	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
783 	new_def = new_text;
784 
785 	sym_set_string_value(sym, new_def);
786 
787 	update_tree(&rootmenu, NULL);
788 
789 	gtk_tree_path_free(path);
790 }
791 
792 /* Change the value of a symbol and update the tree */
793 static void change_sym_value(struct menu *menu, gint col)
794 {
795 	struct symbol *sym = menu->sym;
796 	tristate newval;
797 
798 	if (!sym)
799 		return;
800 
801 	if (col == COL_NO)
802 		newval = no;
803 	else if (col == COL_MOD)
804 		newval = mod;
805 	else if (col == COL_YES)
806 		newval = yes;
807 	else
808 		return;
809 
810 	switch (sym_get_type(sym)) {
811 	case S_BOOLEAN:
812 	case S_TRISTATE:
813 		if (!sym_tristate_within_range(sym, newval))
814 			newval = yes;
815 		sym_set_tristate_value(sym, newval);
816 		if (view_mode == FULL_VIEW)
817 			update_tree(&rootmenu, NULL);
818 		else if (view_mode == SPLIT_VIEW) {
819 			update_tree(browsed, NULL);
820 			display_list();
821 		}
822 		else if (view_mode == SINGLE_VIEW)
823 			display_tree_part();	//fixme: keep exp/coll
824 		break;
825 	case S_INT:
826 	case S_HEX:
827 	case S_STRING:
828 	default:
829 		break;
830 	}
831 }
832 
833 static void toggle_sym_value(struct menu *menu)
834 {
835 	if (!menu->sym)
836 		return;
837 
838 	sym_toggle_tristate_value(menu->sym);
839 	if (view_mode == FULL_VIEW)
840 		update_tree(&rootmenu, NULL);
841 	else if (view_mode == SPLIT_VIEW) {
842 		update_tree(browsed, NULL);
843 		display_list();
844 	}
845 	else if (view_mode == SINGLE_VIEW)
846 		display_tree_part();	//fixme: keep exp/coll
847 }
848 
849 static gint column2index(GtkTreeViewColumn * column)
850 {
851 	gint i;
852 
853 	for (i = 0; i < COL_NUMBER; i++) {
854 		GtkTreeViewColumn *col;
855 
856 		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
857 		if (col == column)
858 			return i;
859 	}
860 
861 	return -1;
862 }
863 
864 
865 /* User click: update choice (full) or goes down (single) */
866 gboolean
867 on_treeview2_button_press_event(GtkWidget * widget,
868 				GdkEventButton * event, gpointer user_data)
869 {
870 	GtkTreeView *view = GTK_TREE_VIEW(widget);
871 	GtkTreePath *path;
872 	GtkTreeViewColumn *column;
873 	GtkTreeIter iter;
874 	struct menu *menu;
875 	gint col;
876 
877 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
878 	gint tx = (gint) event->x;
879 	gint ty = (gint) event->y;
880 	gint cx, cy;
881 
882 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
883 				      &cy);
884 #else
885 	gtk_tree_view_get_cursor(view, &path, &column);
886 #endif
887 	if (path == NULL)
888 		return FALSE;
889 
890 	if (!gtk_tree_model_get_iter(model2, &iter, path))
891 		return FALSE;
892 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
893 
894 	col = column2index(column);
895 	if (event->type == GDK_2BUTTON_PRESS) {
896 		enum prop_type ptype;
897 		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
898 
899 		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
900 			// goes down into menu
901 			current = menu;
902 			display_tree_part();
903 			gtk_widget_set_sensitive(back_btn, TRUE);
904 		} else if (col == COL_OPTION) {
905 			toggle_sym_value(menu);
906 			gtk_tree_view_expand_row(view, path, TRUE);
907 		}
908 	} else {
909 		if (col == COL_VALUE) {
910 			toggle_sym_value(menu);
911 			gtk_tree_view_expand_row(view, path, TRUE);
912 		} else if (col == COL_NO || col == COL_MOD
913 			   || col == COL_YES) {
914 			change_sym_value(menu, col);
915 			gtk_tree_view_expand_row(view, path, TRUE);
916 		}
917 	}
918 
919 	return FALSE;
920 }
921 
922 /* Key pressed: update choice */
923 gboolean
924 on_treeview2_key_press_event(GtkWidget * widget,
925 			     GdkEventKey * event, gpointer user_data)
926 {
927 	GtkTreeView *view = GTK_TREE_VIEW(widget);
928 	GtkTreePath *path;
929 	GtkTreeViewColumn *column;
930 	GtkTreeIter iter;
931 	struct menu *menu;
932 	gint col;
933 
934 	gtk_tree_view_get_cursor(view, &path, &column);
935 	if (path == NULL)
936 		return FALSE;
937 
938 	if (event->keyval == GDK_space) {
939 		if (gtk_tree_view_row_expanded(view, path))
940 			gtk_tree_view_collapse_row(view, path);
941 		else
942 			gtk_tree_view_expand_row(view, path, FALSE);
943 		return TRUE;
944 	}
945 	if (event->keyval == GDK_KP_Enter) {
946 	}
947 	if (widget == tree1_w)
948 		return FALSE;
949 
950 	gtk_tree_model_get_iter(model2, &iter, path);
951 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
952 
953 	if (!strcasecmp(event->string, "n"))
954 		col = COL_NO;
955 	else if (!strcasecmp(event->string, "m"))
956 		col = COL_MOD;
957 	else if (!strcasecmp(event->string, "y"))
958 		col = COL_YES;
959 	else
960 		col = -1;
961 	change_sym_value(menu, col);
962 
963 	return FALSE;
964 }
965 
966 
967 /* Row selection changed: update help */
968 void
969 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
970 {
971 	GtkTreeSelection *selection;
972 	GtkTreeIter iter;
973 	struct menu *menu;
974 
975 	selection = gtk_tree_view_get_selection(treeview);
976 	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
977 		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
978 		text_insert_help(menu);
979 	}
980 }
981 
982 
983 /* User click: display sub-tree in the right frame. */
984 gboolean
985 on_treeview1_button_press_event(GtkWidget * widget,
986 				GdkEventButton * event, gpointer user_data)
987 {
988 	GtkTreeView *view = GTK_TREE_VIEW(widget);
989 	GtkTreePath *path;
990 	GtkTreeViewColumn *column;
991 	GtkTreeIter iter;
992 	struct menu *menu;
993 
994 	gint tx = (gint) event->x;
995 	gint ty = (gint) event->y;
996 	gint cx, cy;
997 
998 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
999 				      &cy);
1000 	if (path == NULL)
1001 		return FALSE;
1002 
1003 	gtk_tree_model_get_iter(model1, &iter, path);
1004 	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1005 
1006 	if (event->type == GDK_2BUTTON_PRESS) {
1007 		toggle_sym_value(menu);
1008 		current = menu;
1009 		display_tree_part();
1010 	} else {
1011 		browsed = menu;
1012 		display_tree_part();
1013 	}
1014 
1015 	gtk_widget_realize(tree2_w);
1016 	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1017 	gtk_widget_grab_focus(tree2_w);
1018 
1019 	return FALSE;
1020 }
1021 
1022 
1023 /* Fill a row of strings */
1024 static gchar **fill_row(struct menu *menu)
1025 {
1026 	static gchar *row[COL_NUMBER];
1027 	struct symbol *sym = menu->sym;
1028 	const char *def;
1029 	int stype;
1030 	tristate val;
1031 	enum prop_type ptype;
1032 	int i;
1033 
1034 	for (i = COL_OPTION; i <= COL_COLOR; i++)
1035 		g_free(row[i]);
1036 	bzero(row, sizeof(row));
1037 
1038 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1039 
1040 	row[COL_OPTION] =
1041 	    g_strdup_printf("%s %s %s %s",
1042 			    ptype == P_COMMENT ? "***" : "",
1043 			    menu_get_prompt(menu),
1044 			    ptype == P_COMMENT ? "***" : "",
1045 			    sym && !sym_has_value(sym) ? "(NEW)" : "");
1046 
1047 	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1048 		row[COL_COLOR] = g_strdup("DarkGray");
1049 	else if (opt_mode == OPT_PROMPT &&
1050 			menu_has_prompt(menu) && !menu_is_visible(menu))
1051 		row[COL_COLOR] = g_strdup("DarkGray");
1052 	else
1053 		row[COL_COLOR] = g_strdup("Black");
1054 
1055 	switch (ptype) {
1056 	case P_MENU:
1057 		row[COL_PIXBUF] = (gchar *) xpm_menu;
1058 		if (view_mode == SINGLE_VIEW)
1059 			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1060 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1061 		break;
1062 	case P_COMMENT:
1063 		row[COL_PIXBUF] = (gchar *) xpm_void;
1064 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1065 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1066 		break;
1067 	default:
1068 		row[COL_PIXBUF] = (gchar *) xpm_void;
1069 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1070 		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1071 		break;
1072 	}
1073 
1074 	if (!sym)
1075 		return row;
1076 	row[COL_NAME] = g_strdup(sym->name);
1077 
1078 	sym_calc_value(sym);
1079 	sym->flags &= ~SYMBOL_CHANGED;
1080 
1081 	if (sym_is_choice(sym)) {	// parse childs for getting final value
1082 		struct menu *child;
1083 		struct symbol *def_sym = sym_get_choice_value(sym);
1084 		struct menu *def_menu = NULL;
1085 
1086 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1087 
1088 		for (child = menu->list; child; child = child->next) {
1089 			if (menu_is_visible(child)
1090 			    && child->sym == def_sym)
1091 				def_menu = child;
1092 		}
1093 
1094 		if (def_menu)
1095 			row[COL_VALUE] =
1096 			    g_strdup(menu_get_prompt(def_menu));
1097 	}
1098 	if (sym->flags & SYMBOL_CHOICEVAL)
1099 		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1100 
1101 	stype = sym_get_type(sym);
1102 	switch (stype) {
1103 	case S_BOOLEAN:
1104 		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1105 			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1106 		if (sym_is_choice(sym))
1107 			break;
1108 		/* fall through */
1109 	case S_TRISTATE:
1110 		val = sym_get_tristate_value(sym);
1111 		switch (val) {
1112 		case no:
1113 			row[COL_NO] = g_strdup("N");
1114 			row[COL_VALUE] = g_strdup("N");
1115 			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1116 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1117 			break;
1118 		case mod:
1119 			row[COL_MOD] = g_strdup("M");
1120 			row[COL_VALUE] = g_strdup("M");
1121 			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1122 			break;
1123 		case yes:
1124 			row[COL_YES] = g_strdup("Y");
1125 			row[COL_VALUE] = g_strdup("Y");
1126 			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1127 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1128 			break;
1129 		}
1130 
1131 		if (val != no && sym_tristate_within_range(sym, no))
1132 			row[COL_NO] = g_strdup("_");
1133 		if (val != mod && sym_tristate_within_range(sym, mod))
1134 			row[COL_MOD] = g_strdup("_");
1135 		if (val != yes && sym_tristate_within_range(sym, yes))
1136 			row[COL_YES] = g_strdup("_");
1137 		break;
1138 	case S_INT:
1139 	case S_HEX:
1140 	case S_STRING:
1141 		def = sym_get_string_value(sym);
1142 		row[COL_VALUE] = g_strdup(def);
1143 		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1144 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1145 		break;
1146 	}
1147 
1148 	return row;
1149 }
1150 
1151 
1152 /* Set the node content with a row of strings */
1153 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1154 {
1155 	GdkColor color;
1156 	gboolean success;
1157 	GdkPixbuf *pix;
1158 
1159 	pix = gdk_pixbuf_new_from_xpm_data((const char **)
1160 					   row[COL_PIXBUF]);
1161 
1162 	gdk_color_parse(row[COL_COLOR], &color);
1163 	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1164 				  FALSE, FALSE, &success);
1165 
1166 	gtk_tree_store_set(tree, node,
1167 			   COL_OPTION, row[COL_OPTION],
1168 			   COL_NAME, row[COL_NAME],
1169 			   COL_NO, row[COL_NO],
1170 			   COL_MOD, row[COL_MOD],
1171 			   COL_YES, row[COL_YES],
1172 			   COL_VALUE, row[COL_VALUE],
1173 			   COL_MENU, (gpointer) menu,
1174 			   COL_COLOR, &color,
1175 			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1176 			   COL_PIXBUF, pix,
1177 			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1178 			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1179 			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1180 			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1181 			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1182 			   -1);
1183 
1184 	g_object_unref(pix);
1185 }
1186 
1187 
1188 /* Add a node to the tree */
1189 static void place_node(struct menu *menu, char **row)
1190 {
1191 	GtkTreeIter *parent = parents[indent - 1];
1192 	GtkTreeIter *node = parents[indent];
1193 
1194 	gtk_tree_store_append(tree, node, parent);
1195 	set_node(node, menu, row);
1196 }
1197 
1198 
1199 /* Find a node in the GTK+ tree */
1200 static GtkTreeIter found;
1201 
1202 /*
1203  * Find a menu in the GtkTree starting at parent.
1204  */
1205 static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent,
1206 					   struct menu *tofind)
1207 {
1208 	GtkTreeIter iter;
1209 	GtkTreeIter *child = &iter;
1210 	gboolean valid;
1211 	GtkTreeIter *ret;
1212 
1213 	valid = gtk_tree_model_iter_children(model2, child, parent);
1214 	while (valid) {
1215 		struct menu *menu;
1216 
1217 		gtk_tree_model_get(model2, child, 6, &menu, -1);
1218 
1219 		if (menu == tofind) {
1220 			memcpy(&found, child, sizeof(GtkTreeIter));
1221 			return &found;
1222 		}
1223 
1224 		ret = gtktree_iter_find_node(child, tofind);
1225 		if (ret)
1226 			return ret;
1227 
1228 		valid = gtk_tree_model_iter_next(model2, child);
1229 	}
1230 
1231 	return NULL;
1232 }
1233 
1234 
1235 /*
1236  * Update the tree by adding/removing entries
1237  * Does not change other nodes
1238  */
1239 static void update_tree(struct menu *src, GtkTreeIter * dst)
1240 {
1241 	struct menu *child1;
1242 	GtkTreeIter iter, tmp;
1243 	GtkTreeIter *child2 = &iter;
1244 	gboolean valid;
1245 	GtkTreeIter *sibling;
1246 	struct symbol *sym;
1247 	struct menu *menu1, *menu2;
1248 
1249 	if (src == &rootmenu)
1250 		indent = 1;
1251 
1252 	valid = gtk_tree_model_iter_children(model2, child2, dst);
1253 	for (child1 = src->list; child1; child1 = child1->next) {
1254 
1255 		sym = child1->sym;
1256 
1257 	      reparse:
1258 		menu1 = child1;
1259 		if (valid)
1260 			gtk_tree_model_get(model2, child2, COL_MENU,
1261 					   &menu2, -1);
1262 		else
1263 			menu2 = NULL;	// force adding of a first child
1264 
1265 #ifdef DEBUG
1266 		printf("%*c%s | %s\n", indent, ' ',
1267 		       menu1 ? menu_get_prompt(menu1) : "nil",
1268 		       menu2 ? menu_get_prompt(menu2) : "nil");
1269 #endif
1270 
1271 		if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1272 		    (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1273 		    (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1274 
1275 			/* remove node */
1276 			if (gtktree_iter_find_node(dst, menu1) != NULL) {
1277 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1278 				valid = gtk_tree_model_iter_next(model2,
1279 								 child2);
1280 				gtk_tree_store_remove(tree2, &tmp);
1281 				if (!valid)
1282 					return;		/* next parent */
1283 				else
1284 					goto reparse;	/* next child */
1285 			} else
1286 				continue;
1287 		}
1288 
1289 		if (menu1 != menu2) {
1290 			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
1291 				if (!valid && !menu2)
1292 					sibling = NULL;
1293 				else
1294 					sibling = child2;
1295 				gtk_tree_store_insert_before(tree2,
1296 							     child2,
1297 							     dst, sibling);
1298 				set_node(child2, menu1, fill_row(menu1));
1299 				if (menu2 == NULL)
1300 					valid = TRUE;
1301 			} else {	// remove node
1302 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1303 				valid = gtk_tree_model_iter_next(model2,
1304 								 child2);
1305 				gtk_tree_store_remove(tree2, &tmp);
1306 				if (!valid)
1307 					return;	// next parent
1308 				else
1309 					goto reparse;	// next child
1310 			}
1311 		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1312 			set_node(child2, menu1, fill_row(menu1));
1313 		}
1314 
1315 		indent++;
1316 		update_tree(child1, child2);
1317 		indent--;
1318 
1319 		valid = gtk_tree_model_iter_next(model2, child2);
1320 	}
1321 }
1322 
1323 
1324 /* Display the whole tree (single/split/full view) */
1325 static void display_tree(struct menu *menu)
1326 {
1327 	struct symbol *sym;
1328 	struct property *prop;
1329 	struct menu *child;
1330 	enum prop_type ptype;
1331 
1332 	if (menu == &rootmenu) {
1333 		indent = 1;
1334 		current = &rootmenu;
1335 	}
1336 
1337 	for (child = menu->list; child; child = child->next) {
1338 		prop = child->prompt;
1339 		sym = child->sym;
1340 		ptype = prop ? prop->type : P_UNKNOWN;
1341 
1342 		if (sym)
1343 			sym->flags &= ~SYMBOL_CHANGED;
1344 
1345 		if ((view_mode == SPLIT_VIEW)
1346 		    && !(child->flags & MENU_ROOT) && (tree == tree1))
1347 			continue;
1348 
1349 		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1350 		    && (tree == tree2))
1351 			continue;
1352 
1353 		if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1354 		    (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1355 		    (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1356 			place_node(child, fill_row(child));
1357 #ifdef DEBUG
1358 		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1359 		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1360 		printf("%s", prop_get_type_name(ptype));
1361 		printf(" | ");
1362 		if (sym) {
1363 			printf("%s", sym_type_name(sym->type));
1364 			printf(" | ");
1365 			printf("%s", dbg_sym_flags(sym->flags));
1366 			printf("\n");
1367 		} else
1368 			printf("\n");
1369 #endif
1370 		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1371 		    && (tree == tree2))
1372 			continue;
1373 /*
1374 		if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1375 		    || (view_mode == FULL_VIEW)
1376 		    || (view_mode == SPLIT_VIEW))*/
1377 
1378 		/* Change paned position if the view is not in 'split mode' */
1379 		if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1380 			gtk_paned_set_position(GTK_PANED(hpaned), 0);
1381 		}
1382 
1383 		if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1384 		    || (view_mode == FULL_VIEW)
1385 		    || (view_mode == SPLIT_VIEW)) {
1386 			indent++;
1387 			display_tree(child);
1388 			indent--;
1389 		}
1390 	}
1391 }
1392 
1393 /* Display a part of the tree starting at current node (single/split view) */
1394 static void display_tree_part(void)
1395 {
1396 	if (tree2)
1397 		gtk_tree_store_clear(tree2);
1398 	if (view_mode == SINGLE_VIEW)
1399 		display_tree(current);
1400 	else if (view_mode == SPLIT_VIEW)
1401 		display_tree(browsed);
1402 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1403 }
1404 
1405 /* Display the list in the left frame (split view) */
1406 static void display_list(void)
1407 {
1408 	if (tree1)
1409 		gtk_tree_store_clear(tree1);
1410 
1411 	tree = tree1;
1412 	display_tree(&rootmenu);
1413 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1414 	tree = tree2;
1415 }
1416 
1417 static void fixup_rootmenu(struct menu *menu)
1418 {
1419 	struct menu *child;
1420 	static int menu_cnt = 0;
1421 
1422 	menu->flags |= MENU_ROOT;
1423 	for (child = menu->list; child; child = child->next) {
1424 		if (child->prompt && child->prompt->type == P_MENU) {
1425 			menu_cnt++;
1426 			fixup_rootmenu(child);
1427 			menu_cnt--;
1428 		} else if (!menu_cnt)
1429 			fixup_rootmenu(child);
1430 	}
1431 }
1432 
1433 
1434 /* Main */
1435 int main(int ac, char *av[])
1436 {
1437 	const char *name;
1438 	char *env;
1439 	gchar *glade_file;
1440 
1441 	/* GTK stuffs */
1442 	gtk_set_locale();
1443 	gtk_init(&ac, &av);
1444 	glade_init();
1445 
1446 	/* Determine GUI path */
1447 	env = getenv(SRCTREE);
1448 	if (env)
1449 		glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1450 	else if (av[0][0] == '/')
1451 		glade_file = g_strconcat(av[0], ".glade", NULL);
1452 	else
1453 		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1454 
1455 	/* Conf stuffs */
1456 	if (ac > 1 && av[1][0] == '-') {
1457 		switch (av[1][1]) {
1458 		case 'a':
1459 			//showAll = 1;
1460 			break;
1461 		case 's':
1462 			conf_set_message_callback(NULL);
1463 			break;
1464 		case 'h':
1465 		case '?':
1466 			printf("%s [-s] <config>\n", av[0]);
1467 			exit(0);
1468 		}
1469 		name = av[2];
1470 	} else
1471 		name = av[1];
1472 
1473 	conf_parse(name);
1474 	fixup_rootmenu(&rootmenu);
1475 	conf_read(NULL);
1476 
1477 	/* Load the interface and connect signals */
1478 	init_main_window(glade_file);
1479 	init_tree_model();
1480 	init_left_tree();
1481 	init_right_tree();
1482 
1483 	switch (view_mode) {
1484 	case SINGLE_VIEW:
1485 		display_tree_part();
1486 		break;
1487 	case SPLIT_VIEW:
1488 		display_list();
1489 		break;
1490 	case FULL_VIEW:
1491 		display_tree(&rootmenu);
1492 		break;
1493 	}
1494 
1495 	gtk_main();
1496 
1497 	return 0;
1498 }
1499 
1500 static void conf_changed(void)
1501 {
1502 	bool changed = conf_get_changed();
1503 	gtk_widget_set_sensitive(save_btn, changed);
1504 	gtk_widget_set_sensitive(save_menu_item, changed);
1505 }
1506