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