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