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