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