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