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