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