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