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