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