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