xref: /openbmc/linux/scripts/kconfig/gconf.c (revision 81d67439)
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, TRUE);
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, TRUE);
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_widget_hide(tree1_w);
760 	current = &rootmenu;
761 	display_tree_part();
762 }
763 
764 
765 void on_split_clicked(GtkButton * button, gpointer user_data)
766 {
767 	gint w, h;
768 	view_mode = SPLIT_VIEW;
769 	gtk_widget_show(tree1_w);
770 	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
771 	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
772 	if (tree2)
773 		gtk_tree_store_clear(tree2);
774 	display_list();
775 
776 	/* Disable back btn, like in full mode. */
777 	gtk_widget_set_sensitive(back_btn, FALSE);
778 }
779 
780 
781 void on_full_clicked(GtkButton * button, gpointer user_data)
782 {
783 	view_mode = FULL_VIEW;
784 	gtk_widget_hide(tree1_w);
785 	if (tree2)
786 		gtk_tree_store_clear(tree2);
787 	display_tree(&rootmenu);
788 	gtk_widget_set_sensitive(back_btn, FALSE);
789 }
790 
791 
792 void on_collapse_clicked(GtkButton * button, gpointer user_data)
793 {
794 	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
795 }
796 
797 
798 void on_expand_clicked(GtkButton * button, gpointer user_data)
799 {
800 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
801 }
802 
803 
804 /* CTree Callbacks */
805 
806 /* Change hex/int/string value in the cell */
807 static void renderer_edited(GtkCellRendererText * cell,
808 			    const gchar * path_string,
809 			    const gchar * new_text, gpointer user_data)
810 {
811 	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
812 	GtkTreeIter iter;
813 	const char *old_def, *new_def;
814 	struct menu *menu;
815 	struct symbol *sym;
816 
817 	if (!gtk_tree_model_get_iter(model2, &iter, path))
818 		return;
819 
820 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
821 	sym = menu->sym;
822 
823 	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
824 	new_def = new_text;
825 
826 	sym_set_string_value(sym, new_def);
827 
828 	update_tree(&rootmenu, NULL);
829 
830 	gtk_tree_path_free(path);
831 }
832 
833 /* Change the value of a symbol and update the tree */
834 static void change_sym_value(struct menu *menu, gint col)
835 {
836 	struct symbol *sym = menu->sym;
837 	tristate oldval, newval;
838 
839 	if (!sym)
840 		return;
841 
842 	if (col == COL_NO)
843 		newval = no;
844 	else if (col == COL_MOD)
845 		newval = mod;
846 	else if (col == COL_YES)
847 		newval = yes;
848 	else
849 		return;
850 
851 	switch (sym_get_type(sym)) {
852 	case S_BOOLEAN:
853 	case S_TRISTATE:
854 		oldval = sym_get_tristate_value(sym);
855 		if (!sym_tristate_within_range(sym, newval))
856 			newval = yes;
857 		sym_set_tristate_value(sym, newval);
858 		if (view_mode == FULL_VIEW)
859 			update_tree(&rootmenu, NULL);
860 		else if (view_mode == SPLIT_VIEW) {
861 			update_tree(browsed, NULL);
862 			display_list();
863 		}
864 		else if (view_mode == SINGLE_VIEW)
865 			display_tree_part();	//fixme: keep exp/coll
866 		break;
867 	case S_INT:
868 	case S_HEX:
869 	case S_STRING:
870 	default:
871 		break;
872 	}
873 }
874 
875 static void toggle_sym_value(struct menu *menu)
876 {
877 	if (!menu->sym)
878 		return;
879 
880 	sym_toggle_tristate_value(menu->sym);
881 	if (view_mode == FULL_VIEW)
882 		update_tree(&rootmenu, NULL);
883 	else if (view_mode == SPLIT_VIEW) {
884 		update_tree(browsed, NULL);
885 		display_list();
886 	}
887 	else if (view_mode == SINGLE_VIEW)
888 		display_tree_part();	//fixme: keep exp/coll
889 }
890 
891 static void renderer_toggled(GtkCellRendererToggle * cell,
892 			     gchar * path_string, gpointer user_data)
893 {
894 	GtkTreePath *path, *sel_path = NULL;
895 	GtkTreeIter iter, sel_iter;
896 	GtkTreeSelection *sel;
897 	struct menu *menu;
898 
899 	path = gtk_tree_path_new_from_string(path_string);
900 	if (!gtk_tree_model_get_iter(model2, &iter, path))
901 		return;
902 
903 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w));
904 	if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter))
905 		sel_path = gtk_tree_model_get_path(model2, &sel_iter);
906 	if (!sel_path)
907 		goto out1;
908 	if (gtk_tree_path_compare(path, sel_path))
909 		goto out2;
910 
911 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
912 	toggle_sym_value(menu);
913 
914       out2:
915 	gtk_tree_path_free(sel_path);
916       out1:
917 	gtk_tree_path_free(path);
918 }
919 
920 static gint column2index(GtkTreeViewColumn * column)
921 {
922 	gint i;
923 
924 	for (i = 0; i < COL_NUMBER; i++) {
925 		GtkTreeViewColumn *col;
926 
927 		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
928 		if (col == column)
929 			return i;
930 	}
931 
932 	return -1;
933 }
934 
935 
936 /* User click: update choice (full) or goes down (single) */
937 gboolean
938 on_treeview2_button_press_event(GtkWidget * widget,
939 				GdkEventButton * event, gpointer user_data)
940 {
941 	GtkTreeView *view = GTK_TREE_VIEW(widget);
942 	GtkTreePath *path;
943 	GtkTreeViewColumn *column;
944 	GtkTreeIter iter;
945 	struct menu *menu;
946 	gint col;
947 
948 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
949 	gint tx = (gint) event->x;
950 	gint ty = (gint) event->y;
951 	gint cx, cy;
952 
953 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
954 				      &cy);
955 #else
956 	gtk_tree_view_get_cursor(view, &path, &column);
957 #endif
958 	if (path == NULL)
959 		return FALSE;
960 
961 	if (!gtk_tree_model_get_iter(model2, &iter, path))
962 		return FALSE;
963 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
964 
965 	col = column2index(column);
966 	if (event->type == GDK_2BUTTON_PRESS) {
967 		enum prop_type ptype;
968 		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
969 
970 		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
971 			// goes down into menu
972 			current = menu;
973 			display_tree_part();
974 			gtk_widget_set_sensitive(back_btn, TRUE);
975 		} else if ((col == COL_OPTION)) {
976 			toggle_sym_value(menu);
977 			gtk_tree_view_expand_row(view, path, TRUE);
978 		}
979 	} else {
980 		if (col == COL_VALUE) {
981 			toggle_sym_value(menu);
982 			gtk_tree_view_expand_row(view, path, TRUE);
983 		} else if (col == COL_NO || col == COL_MOD
984 			   || col == COL_YES) {
985 			change_sym_value(menu, col);
986 			gtk_tree_view_expand_row(view, path, TRUE);
987 		}
988 	}
989 
990 	return FALSE;
991 }
992 
993 /* Key pressed: update choice */
994 gboolean
995 on_treeview2_key_press_event(GtkWidget * widget,
996 			     GdkEventKey * event, gpointer user_data)
997 {
998 	GtkTreeView *view = GTK_TREE_VIEW(widget);
999 	GtkTreePath *path;
1000 	GtkTreeViewColumn *column;
1001 	GtkTreeIter iter;
1002 	struct menu *menu;
1003 	gint col;
1004 
1005 	gtk_tree_view_get_cursor(view, &path, &column);
1006 	if (path == NULL)
1007 		return FALSE;
1008 
1009 	if (event->keyval == GDK_space) {
1010 		if (gtk_tree_view_row_expanded(view, path))
1011 			gtk_tree_view_collapse_row(view, path);
1012 		else
1013 			gtk_tree_view_expand_row(view, path, FALSE);
1014 		return TRUE;
1015 	}
1016 	if (event->keyval == GDK_KP_Enter) {
1017 	}
1018 	if (widget == tree1_w)
1019 		return FALSE;
1020 
1021 	gtk_tree_model_get_iter(model2, &iter, path);
1022 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
1023 
1024 	if (!strcasecmp(event->string, "n"))
1025 		col = COL_NO;
1026 	else if (!strcasecmp(event->string, "m"))
1027 		col = COL_MOD;
1028 	else if (!strcasecmp(event->string, "y"))
1029 		col = COL_YES;
1030 	else
1031 		col = -1;
1032 	change_sym_value(menu, col);
1033 
1034 	return FALSE;
1035 }
1036 
1037 
1038 /* Row selection changed: update help */
1039 void
1040 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
1041 {
1042 	GtkTreeSelection *selection;
1043 	GtkTreeIter iter;
1044 	struct menu *menu;
1045 
1046 	selection = gtk_tree_view_get_selection(treeview);
1047 	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
1048 		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
1049 		text_insert_help(menu);
1050 	}
1051 }
1052 
1053 
1054 /* User click: display sub-tree in the right frame. */
1055 gboolean
1056 on_treeview1_button_press_event(GtkWidget * widget,
1057 				GdkEventButton * event, gpointer user_data)
1058 {
1059 	GtkTreeView *view = GTK_TREE_VIEW(widget);
1060 	GtkTreePath *path;
1061 	GtkTreeViewColumn *column;
1062 	GtkTreeIter iter;
1063 	struct menu *menu;
1064 
1065 	gint tx = (gint) event->x;
1066 	gint ty = (gint) event->y;
1067 	gint cx, cy;
1068 
1069 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
1070 				      &cy);
1071 	if (path == NULL)
1072 		return FALSE;
1073 
1074 	gtk_tree_model_get_iter(model1, &iter, path);
1075 	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1076 
1077 	if (event->type == GDK_2BUTTON_PRESS) {
1078 		toggle_sym_value(menu);
1079 		current = menu;
1080 		display_tree_part();
1081 	} else {
1082 		browsed = menu;
1083 		display_tree_part();
1084 	}
1085 
1086 	gtk_widget_realize(tree2_w);
1087 	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1088 	gtk_widget_grab_focus(tree2_w);
1089 
1090 	return FALSE;
1091 }
1092 
1093 
1094 /* Fill a row of strings */
1095 static gchar **fill_row(struct menu *menu)
1096 {
1097 	static gchar *row[COL_NUMBER];
1098 	struct symbol *sym = menu->sym;
1099 	const char *def;
1100 	int stype;
1101 	tristate val;
1102 	enum prop_type ptype;
1103 	int i;
1104 
1105 	for (i = COL_OPTION; i <= COL_COLOR; i++)
1106 		g_free(row[i]);
1107 	bzero(row, sizeof(row));
1108 
1109 	row[COL_OPTION] =
1110 	    g_strdup_printf("%s %s", _(menu_get_prompt(menu)),
1111 			    sym && !sym_has_value(sym) ? "(NEW)" : "");
1112 
1113 	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1114 		row[COL_COLOR] = g_strdup("DarkGray");
1115 	else if (opt_mode == OPT_PROMPT &&
1116 			menu_has_prompt(menu) && !menu_is_visible(menu))
1117 		row[COL_COLOR] = g_strdup("DarkGray");
1118 	else
1119 		row[COL_COLOR] = g_strdup("Black");
1120 
1121 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1122 	switch (ptype) {
1123 	case P_MENU:
1124 		row[COL_PIXBUF] = (gchar *) xpm_menu;
1125 		if (view_mode == SINGLE_VIEW)
1126 			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1127 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1128 		break;
1129 	case P_COMMENT:
1130 		row[COL_PIXBUF] = (gchar *) xpm_void;
1131 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1132 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1133 		break;
1134 	default:
1135 		row[COL_PIXBUF] = (gchar *) xpm_void;
1136 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1137 		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1138 		break;
1139 	}
1140 
1141 	if (!sym)
1142 		return row;
1143 	row[COL_NAME] = g_strdup(sym->name);
1144 
1145 	sym_calc_value(sym);
1146 	sym->flags &= ~SYMBOL_CHANGED;
1147 
1148 	if (sym_is_choice(sym)) {	// parse childs for getting final value
1149 		struct menu *child;
1150 		struct symbol *def_sym = sym_get_choice_value(sym);
1151 		struct menu *def_menu = NULL;
1152 
1153 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1154 
1155 		for (child = menu->list; child; child = child->next) {
1156 			if (menu_is_visible(child)
1157 			    && child->sym == def_sym)
1158 				def_menu = child;
1159 		}
1160 
1161 		if (def_menu)
1162 			row[COL_VALUE] =
1163 			    g_strdup(_(menu_get_prompt(def_menu)));
1164 	}
1165 	if (sym->flags & SYMBOL_CHOICEVAL)
1166 		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1167 
1168 	stype = sym_get_type(sym);
1169 	switch (stype) {
1170 	case S_BOOLEAN:
1171 		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1172 			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1173 		if (sym_is_choice(sym))
1174 			break;
1175 	case S_TRISTATE:
1176 		val = sym_get_tristate_value(sym);
1177 		switch (val) {
1178 		case no:
1179 			row[COL_NO] = g_strdup("N");
1180 			row[COL_VALUE] = g_strdup("N");
1181 			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1182 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1183 			break;
1184 		case mod:
1185 			row[COL_MOD] = g_strdup("M");
1186 			row[COL_VALUE] = g_strdup("M");
1187 			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1188 			break;
1189 		case yes:
1190 			row[COL_YES] = g_strdup("Y");
1191 			row[COL_VALUE] = g_strdup("Y");
1192 			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1193 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1194 			break;
1195 		}
1196 
1197 		if (val != no && sym_tristate_within_range(sym, no))
1198 			row[COL_NO] = g_strdup("_");
1199 		if (val != mod && sym_tristate_within_range(sym, mod))
1200 			row[COL_MOD] = g_strdup("_");
1201 		if (val != yes && sym_tristate_within_range(sym, yes))
1202 			row[COL_YES] = g_strdup("_");
1203 		break;
1204 	case S_INT:
1205 	case S_HEX:
1206 	case S_STRING:
1207 		def = sym_get_string_value(sym);
1208 		row[COL_VALUE] = g_strdup(def);
1209 		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1210 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1211 		break;
1212 	}
1213 
1214 	return row;
1215 }
1216 
1217 
1218 /* Set the node content with a row of strings */
1219 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1220 {
1221 	GdkColor color;
1222 	gboolean success;
1223 	GdkPixbuf *pix;
1224 
1225 	pix = gdk_pixbuf_new_from_xpm_data((const char **)
1226 					   row[COL_PIXBUF]);
1227 
1228 	gdk_color_parse(row[COL_COLOR], &color);
1229 	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1230 				  FALSE, FALSE, &success);
1231 
1232 	gtk_tree_store_set(tree, node,
1233 			   COL_OPTION, row[COL_OPTION],
1234 			   COL_NAME, row[COL_NAME],
1235 			   COL_NO, row[COL_NO],
1236 			   COL_MOD, row[COL_MOD],
1237 			   COL_YES, row[COL_YES],
1238 			   COL_VALUE, row[COL_VALUE],
1239 			   COL_MENU, (gpointer) menu,
1240 			   COL_COLOR, &color,
1241 			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1242 			   COL_PIXBUF, pix,
1243 			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1244 			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1245 			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1246 			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1247 			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1248 			   -1);
1249 
1250 	g_object_unref(pix);
1251 }
1252 
1253 
1254 /* Add a node to the tree */
1255 static void place_node(struct menu *menu, char **row)
1256 {
1257 	GtkTreeIter *parent = parents[indent - 1];
1258 	GtkTreeIter *node = parents[indent];
1259 
1260 	gtk_tree_store_append(tree, node, parent);
1261 	set_node(node, menu, row);
1262 }
1263 
1264 
1265 /* Find a node in the GTK+ tree */
1266 static GtkTreeIter found;
1267 
1268 /*
1269  * Find a menu in the GtkTree starting at parent.
1270  */
1271 GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
1272 				    struct menu *tofind)
1273 {
1274 	GtkTreeIter iter;
1275 	GtkTreeIter *child = &iter;
1276 	gboolean valid;
1277 	GtkTreeIter *ret;
1278 
1279 	valid = gtk_tree_model_iter_children(model2, child, parent);
1280 	while (valid) {
1281 		struct menu *menu;
1282 
1283 		gtk_tree_model_get(model2, child, 6, &menu, -1);
1284 
1285 		if (menu == tofind) {
1286 			memcpy(&found, child, sizeof(GtkTreeIter));
1287 			return &found;
1288 		}
1289 
1290 		ret = gtktree_iter_find_node(child, tofind);
1291 		if (ret)
1292 			return ret;
1293 
1294 		valid = gtk_tree_model_iter_next(model2, child);
1295 	}
1296 
1297 	return NULL;
1298 }
1299 
1300 
1301 /*
1302  * Update the tree by adding/removing entries
1303  * Does not change other nodes
1304  */
1305 static void update_tree(struct menu *src, GtkTreeIter * dst)
1306 {
1307 	struct menu *child1;
1308 	GtkTreeIter iter, tmp;
1309 	GtkTreeIter *child2 = &iter;
1310 	gboolean valid;
1311 	GtkTreeIter *sibling;
1312 	struct symbol *sym;
1313 	struct property *prop;
1314 	struct menu *menu1, *menu2;
1315 
1316 	if (src == &rootmenu)
1317 		indent = 1;
1318 
1319 	valid = gtk_tree_model_iter_children(model2, child2, dst);
1320 	for (child1 = src->list; child1; child1 = child1->next) {
1321 
1322 		prop = child1->prompt;
1323 		sym = child1->sym;
1324 
1325 	      reparse:
1326 		menu1 = child1;
1327 		if (valid)
1328 			gtk_tree_model_get(model2, child2, COL_MENU,
1329 					   &menu2, -1);
1330 		else
1331 			menu2 = NULL;	// force adding of a first child
1332 
1333 #ifdef DEBUG
1334 		printf("%*c%s | %s\n", indent, ' ',
1335 		       menu1 ? menu_get_prompt(menu1) : "nil",
1336 		       menu2 ? menu_get_prompt(menu2) : "nil");
1337 #endif
1338 
1339 		if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1340 		    (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1341 		    (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1342 
1343 			/* remove node */
1344 			if (gtktree_iter_find_node(dst, menu1) != NULL) {
1345 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1346 				valid = gtk_tree_model_iter_next(model2,
1347 								 child2);
1348 				gtk_tree_store_remove(tree2, &tmp);
1349 				if (!valid)
1350 					return;		/* next parent */
1351 				else
1352 					goto reparse;	/* next child */
1353 			} else
1354 				continue;
1355 		}
1356 
1357 		if (menu1 != menu2) {
1358 			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
1359 				if (!valid && !menu2)
1360 					sibling = NULL;
1361 				else
1362 					sibling = child2;
1363 				gtk_tree_store_insert_before(tree2,
1364 							     child2,
1365 							     dst, sibling);
1366 				set_node(child2, menu1, fill_row(menu1));
1367 				if (menu2 == NULL)
1368 					valid = TRUE;
1369 			} else {	// remove node
1370 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1371 				valid = gtk_tree_model_iter_next(model2,
1372 								 child2);
1373 				gtk_tree_store_remove(tree2, &tmp);
1374 				if (!valid)
1375 					return;	// next parent
1376 				else
1377 					goto reparse;	// next child
1378 			}
1379 		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1380 			set_node(child2, menu1, fill_row(menu1));
1381 		}
1382 
1383 		indent++;
1384 		update_tree(child1, child2);
1385 		indent--;
1386 
1387 		valid = gtk_tree_model_iter_next(model2, child2);
1388 	}
1389 }
1390 
1391 
1392 /* Display the whole tree (single/split/full view) */
1393 static void display_tree(struct menu *menu)
1394 {
1395 	struct symbol *sym;
1396 	struct property *prop;
1397 	struct menu *child;
1398 	enum prop_type ptype;
1399 
1400 	if (menu == &rootmenu) {
1401 		indent = 1;
1402 		current = &rootmenu;
1403 	}
1404 
1405 	for (child = menu->list; child; child = child->next) {
1406 		prop = child->prompt;
1407 		sym = child->sym;
1408 		ptype = prop ? prop->type : P_UNKNOWN;
1409 
1410 		if (sym)
1411 			sym->flags &= ~SYMBOL_CHANGED;
1412 
1413 		if ((view_mode == SPLIT_VIEW)
1414 		    && !(child->flags & MENU_ROOT) && (tree == tree1))
1415 			continue;
1416 
1417 		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1418 		    && (tree == tree2))
1419 			continue;
1420 
1421 		if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1422 		    (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1423 		    (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1424 			place_node(child, fill_row(child));
1425 #ifdef DEBUG
1426 		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1427 		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1428 		printf("%s", prop_get_type_name(ptype));
1429 		printf(" | ");
1430 		if (sym) {
1431 			printf("%s", sym_type_name(sym->type));
1432 			printf(" | ");
1433 			printf("%s", dbg_sym_flags(sym->flags));
1434 			printf("\n");
1435 		} else
1436 			printf("\n");
1437 #endif
1438 		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1439 		    && (tree == tree2))
1440 			continue;
1441 /*
1442                 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1443 		    || (view_mode == FULL_VIEW)
1444 		    || (view_mode == SPLIT_VIEW))*/
1445 
1446 		/* Change paned position if the view is not in 'split mode' */
1447 		if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1448 			gtk_paned_set_position(GTK_PANED(hpaned), 0);
1449 		}
1450 
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 	/* Conf stuffs */
1535 	if (ac > 1 && av[1][0] == '-') {
1536 		switch (av[1][1]) {
1537 		case 'a':
1538 			//showAll = 1;
1539 			break;
1540 		case 'h':
1541 		case '?':
1542 			printf("%s <config>\n", av[0]);
1543 			exit(0);
1544 		}
1545 		name = av[2];
1546 	} else
1547 		name = av[1];
1548 
1549 	conf_parse(name);
1550 	fixup_rootmenu(&rootmenu);
1551 	conf_read(NULL);
1552 
1553 	/* Load the interface and connect signals */
1554 	init_main_window(glade_file);
1555 	init_tree_model();
1556 	init_left_tree();
1557 	init_right_tree();
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