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