xref: /openbmc/linux/scripts/kconfig/gconf.c (revision bbaf1ff0)
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 gkc, 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.\n"
651 	    "\n"
652 	    "Toggling Show Debug Info under the Options menu will show \n"
653 	    "the dependencies, which you can then match by examining other options.";
654 
655 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
656 					GTK_DIALOG_DESTROY_WITH_PARENT,
657 					GTK_MESSAGE_INFO,
658 					GTK_BUTTONS_CLOSE, "%s", intro_text);
659 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
660 				 G_CALLBACK(gtk_widget_destroy),
661 				 GTK_OBJECT(dialog));
662 	gtk_widget_show_all(dialog);
663 }
664 
665 
666 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
667 {
668 	GtkWidget *dialog;
669 	const gchar *about_text =
670 	    "gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
671 	      "Based on the source code from Roman Zippel.\n";
672 
673 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
674 					GTK_DIALOG_DESTROY_WITH_PARENT,
675 					GTK_MESSAGE_INFO,
676 					GTK_BUTTONS_CLOSE, "%s", about_text);
677 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
678 				 G_CALLBACK(gtk_widget_destroy),
679 				 GTK_OBJECT(dialog));
680 	gtk_widget_show_all(dialog);
681 }
682 
683 
684 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
685 {
686 	GtkWidget *dialog;
687 	const gchar *license_text =
688 	    "gkc is released under the terms of the GNU GPL v2.\n"
689 	      "For more information, please see the source code or\n"
690 	      "visit http://www.fsf.org/licenses/licenses.html\n";
691 
692 	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
693 					GTK_DIALOG_DESTROY_WITH_PARENT,
694 					GTK_MESSAGE_INFO,
695 					GTK_BUTTONS_CLOSE, "%s", license_text);
696 	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
697 				 G_CALLBACK(gtk_widget_destroy),
698 				 GTK_OBJECT(dialog));
699 	gtk_widget_show_all(dialog);
700 }
701 
702 
703 void on_back_clicked(GtkButton * button, gpointer user_data)
704 {
705 	enum prop_type ptype;
706 
707 	current = current->parent;
708 	ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
709 	if (ptype != P_MENU)
710 		current = current->parent;
711 	display_tree_part();
712 
713 	if (current == &rootmenu)
714 		gtk_widget_set_sensitive(back_btn, FALSE);
715 }
716 
717 
718 void on_load_clicked(GtkButton * button, gpointer user_data)
719 {
720 	on_load1_activate(NULL, user_data);
721 }
722 
723 
724 void on_single_clicked(GtkButton * button, gpointer user_data)
725 {
726 	view_mode = SINGLE_VIEW;
727 	gtk_widget_hide(tree1_w);
728 	current = &rootmenu;
729 	display_tree_part();
730 }
731 
732 
733 void on_split_clicked(GtkButton * button, gpointer user_data)
734 {
735 	gint w, h;
736 	view_mode = SPLIT_VIEW;
737 	gtk_widget_show(tree1_w);
738 	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
739 	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
740 	if (tree2)
741 		gtk_tree_store_clear(tree2);
742 	display_list();
743 
744 	/* Disable back btn, like in full mode. */
745 	gtk_widget_set_sensitive(back_btn, FALSE);
746 }
747 
748 
749 void on_full_clicked(GtkButton * button, gpointer user_data)
750 {
751 	view_mode = FULL_VIEW;
752 	gtk_widget_hide(tree1_w);
753 	if (tree2)
754 		gtk_tree_store_clear(tree2);
755 	display_tree(&rootmenu);
756 	gtk_widget_set_sensitive(back_btn, FALSE);
757 }
758 
759 
760 void on_collapse_clicked(GtkButton * button, gpointer user_data)
761 {
762 	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
763 }
764 
765 
766 void on_expand_clicked(GtkButton * button, gpointer user_data)
767 {
768 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
769 }
770 
771 
772 /* CTree Callbacks */
773 
774 /* Change hex/int/string value in the cell */
775 static void renderer_edited(GtkCellRendererText * cell,
776 			    const gchar * path_string,
777 			    const gchar * new_text, gpointer user_data)
778 {
779 	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
780 	GtkTreeIter iter;
781 	const char *old_def, *new_def;
782 	struct menu *menu;
783 	struct symbol *sym;
784 
785 	if (!gtk_tree_model_get_iter(model2, &iter, path))
786 		return;
787 
788 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
789 	sym = menu->sym;
790 
791 	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
792 	new_def = new_text;
793 
794 	sym_set_string_value(sym, new_def);
795 
796 	update_tree(&rootmenu, NULL);
797 
798 	gtk_tree_path_free(path);
799 }
800 
801 /* Change the value of a symbol and update the tree */
802 static void change_sym_value(struct menu *menu, gint col)
803 {
804 	struct symbol *sym = menu->sym;
805 	tristate newval;
806 
807 	if (!sym)
808 		return;
809 
810 	if (col == COL_NO)
811 		newval = no;
812 	else if (col == COL_MOD)
813 		newval = mod;
814 	else if (col == COL_YES)
815 		newval = yes;
816 	else
817 		return;
818 
819 	switch (sym_get_type(sym)) {
820 	case S_BOOLEAN:
821 	case S_TRISTATE:
822 		if (!sym_tristate_within_range(sym, newval))
823 			newval = yes;
824 		sym_set_tristate_value(sym, newval);
825 		if (view_mode == FULL_VIEW)
826 			update_tree(&rootmenu, NULL);
827 		else if (view_mode == SPLIT_VIEW) {
828 			update_tree(browsed, NULL);
829 			display_list();
830 		}
831 		else if (view_mode == SINGLE_VIEW)
832 			display_tree_part();	//fixme: keep exp/coll
833 		break;
834 	case S_INT:
835 	case S_HEX:
836 	case S_STRING:
837 	default:
838 		break;
839 	}
840 }
841 
842 static void toggle_sym_value(struct menu *menu)
843 {
844 	if (!menu->sym)
845 		return;
846 
847 	sym_toggle_tristate_value(menu->sym);
848 	if (view_mode == FULL_VIEW)
849 		update_tree(&rootmenu, NULL);
850 	else if (view_mode == SPLIT_VIEW) {
851 		update_tree(browsed, NULL);
852 		display_list();
853 	}
854 	else if (view_mode == SINGLE_VIEW)
855 		display_tree_part();	//fixme: keep exp/coll
856 }
857 
858 static gint column2index(GtkTreeViewColumn * column)
859 {
860 	gint i;
861 
862 	for (i = 0; i < COL_NUMBER; i++) {
863 		GtkTreeViewColumn *col;
864 
865 		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
866 		if (col == column)
867 			return i;
868 	}
869 
870 	return -1;
871 }
872 
873 
874 /* User click: update choice (full) or goes down (single) */
875 gboolean
876 on_treeview2_button_press_event(GtkWidget * widget,
877 				GdkEventButton * event, gpointer user_data)
878 {
879 	GtkTreeView *view = GTK_TREE_VIEW(widget);
880 	GtkTreePath *path;
881 	GtkTreeViewColumn *column;
882 	GtkTreeIter iter;
883 	struct menu *menu;
884 	gint col;
885 
886 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
887 	gint tx = (gint) event->x;
888 	gint ty = (gint) event->y;
889 	gint cx, cy;
890 
891 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
892 				      &cy);
893 #else
894 	gtk_tree_view_get_cursor(view, &path, &column);
895 #endif
896 	if (path == NULL)
897 		return FALSE;
898 
899 	if (!gtk_tree_model_get_iter(model2, &iter, path))
900 		return FALSE;
901 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
902 
903 	col = column2index(column);
904 	if (event->type == GDK_2BUTTON_PRESS) {
905 		enum prop_type ptype;
906 		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
907 
908 		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
909 			// goes down into menu
910 			current = menu;
911 			display_tree_part();
912 			gtk_widget_set_sensitive(back_btn, TRUE);
913 		} else if (col == COL_OPTION) {
914 			toggle_sym_value(menu);
915 			gtk_tree_view_expand_row(view, path, TRUE);
916 		}
917 	} else {
918 		if (col == COL_VALUE) {
919 			toggle_sym_value(menu);
920 			gtk_tree_view_expand_row(view, path, TRUE);
921 		} else if (col == COL_NO || col == COL_MOD
922 			   || col == COL_YES) {
923 			change_sym_value(menu, col);
924 			gtk_tree_view_expand_row(view, path, TRUE);
925 		}
926 	}
927 
928 	return FALSE;
929 }
930 
931 /* Key pressed: update choice */
932 gboolean
933 on_treeview2_key_press_event(GtkWidget * widget,
934 			     GdkEventKey * event, gpointer user_data)
935 {
936 	GtkTreeView *view = GTK_TREE_VIEW(widget);
937 	GtkTreePath *path;
938 	GtkTreeViewColumn *column;
939 	GtkTreeIter iter;
940 	struct menu *menu;
941 	gint col;
942 
943 	gtk_tree_view_get_cursor(view, &path, &column);
944 	if (path == NULL)
945 		return FALSE;
946 
947 	if (event->keyval == GDK_space) {
948 		if (gtk_tree_view_row_expanded(view, path))
949 			gtk_tree_view_collapse_row(view, path);
950 		else
951 			gtk_tree_view_expand_row(view, path, FALSE);
952 		return TRUE;
953 	}
954 	if (event->keyval == GDK_KP_Enter) {
955 	}
956 	if (widget == tree1_w)
957 		return FALSE;
958 
959 	gtk_tree_model_get_iter(model2, &iter, path);
960 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
961 
962 	if (!strcasecmp(event->string, "n"))
963 		col = COL_NO;
964 	else if (!strcasecmp(event->string, "m"))
965 		col = COL_MOD;
966 	else if (!strcasecmp(event->string, "y"))
967 		col = COL_YES;
968 	else
969 		col = -1;
970 	change_sym_value(menu, col);
971 
972 	return FALSE;
973 }
974 
975 
976 /* Row selection changed: update help */
977 void
978 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
979 {
980 	GtkTreeSelection *selection;
981 	GtkTreeIter iter;
982 	struct menu *menu;
983 
984 	selection = gtk_tree_view_get_selection(treeview);
985 	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
986 		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
987 		text_insert_help(menu);
988 	}
989 }
990 
991 
992 /* User click: display sub-tree in the right frame. */
993 gboolean
994 on_treeview1_button_press_event(GtkWidget * widget,
995 				GdkEventButton * event, gpointer user_data)
996 {
997 	GtkTreeView *view = GTK_TREE_VIEW(widget);
998 	GtkTreePath *path;
999 	GtkTreeViewColumn *column;
1000 	GtkTreeIter iter;
1001 	struct menu *menu;
1002 
1003 	gint tx = (gint) event->x;
1004 	gint ty = (gint) event->y;
1005 	gint cx, cy;
1006 
1007 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
1008 				      &cy);
1009 	if (path == NULL)
1010 		return FALSE;
1011 
1012 	gtk_tree_model_get_iter(model1, &iter, path);
1013 	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1014 
1015 	if (event->type == GDK_2BUTTON_PRESS) {
1016 		toggle_sym_value(menu);
1017 		current = menu;
1018 		display_tree_part();
1019 	} else {
1020 		browsed = menu;
1021 		display_tree_part();
1022 	}
1023 
1024 	gtk_widget_realize(tree2_w);
1025 	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1026 	gtk_widget_grab_focus(tree2_w);
1027 
1028 	return FALSE;
1029 }
1030 
1031 
1032 /* Fill a row of strings */
1033 static gchar **fill_row(struct menu *menu)
1034 {
1035 	static gchar *row[COL_NUMBER];
1036 	struct symbol *sym = menu->sym;
1037 	const char *def;
1038 	int stype;
1039 	tristate val;
1040 	enum prop_type ptype;
1041 	int i;
1042 
1043 	for (i = COL_OPTION; i <= COL_COLOR; i++)
1044 		g_free(row[i]);
1045 	bzero(row, sizeof(row));
1046 
1047 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1048 
1049 	row[COL_OPTION] =
1050 	    g_strdup_printf("%s %s %s %s",
1051 			    ptype == P_COMMENT ? "***" : "",
1052 			    menu_get_prompt(menu),
1053 			    ptype == P_COMMENT ? "***" : "",
1054 			    sym && !sym_has_value(sym) ? "(NEW)" : "");
1055 
1056 	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1057 		row[COL_COLOR] = g_strdup("DarkGray");
1058 	else if (opt_mode == OPT_PROMPT &&
1059 			menu_has_prompt(menu) && !menu_is_visible(menu))
1060 		row[COL_COLOR] = g_strdup("DarkGray");
1061 	else
1062 		row[COL_COLOR] = g_strdup("Black");
1063 
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 	/* Determine GUI path */
1456 	env = getenv(SRCTREE);
1457 	if (env)
1458 		glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1459 	else if (av[0][0] == '/')
1460 		glade_file = g_strconcat(av[0], ".glade", NULL);
1461 	else
1462 		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1463 
1464 	/* Conf stuffs */
1465 	if (ac > 1 && av[1][0] == '-') {
1466 		switch (av[1][1]) {
1467 		case 'a':
1468 			//showAll = 1;
1469 			break;
1470 		case 's':
1471 			conf_set_message_callback(NULL);
1472 			break;
1473 		case 'h':
1474 		case '?':
1475 			printf("%s [-s] <config>\n", av[0]);
1476 			exit(0);
1477 		}
1478 		name = av[2];
1479 	} else
1480 		name = av[1];
1481 
1482 	conf_parse(name);
1483 	fixup_rootmenu(&rootmenu);
1484 	conf_read(NULL);
1485 
1486 	/* Load the interface and connect signals */
1487 	init_main_window(glade_file);
1488 	init_tree_model();
1489 	init_left_tree();
1490 	init_right_tree();
1491 
1492 	switch (view_mode) {
1493 	case SINGLE_VIEW:
1494 		display_tree_part();
1495 		break;
1496 	case SPLIT_VIEW:
1497 		display_list();
1498 		break;
1499 	case FULL_VIEW:
1500 		display_tree(&rootmenu);
1501 		break;
1502 	}
1503 
1504 	gtk_main();
1505 
1506 	return 0;
1507 }
1508 
1509 static void conf_changed(void)
1510 {
1511 	bool changed = conf_get_changed();
1512 	gtk_widget_set_sensitive(save_btn, changed);
1513 	gtk_widget_set_sensitive(save_menu_item, changed);
1514 }
1515