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