xref: /linux/scripts/kconfig/gconf.c (revision f2ee442115c9b6219083c019939a9cc0c9abb2f8)
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 
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, 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, 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, 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 oldval, 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 		oldval = sym_get_tristate_value(sym);
851 		if (!sym_tristate_within_range(sym, newval))
852 			newval = yes;
853 		sym_set_tristate_value(sym, newval);
854 		if (view_mode == FULL_VIEW)
855 			update_tree(&rootmenu, NULL);
856 		else if (view_mode == SPLIT_VIEW) {
857 			update_tree(browsed, NULL);
858 			display_list();
859 		}
860 		else if (view_mode == SINGLE_VIEW)
861 			display_tree_part();	//fixme: keep exp/coll
862 		break;
863 	case S_INT:
864 	case S_HEX:
865 	case S_STRING:
866 	default:
867 		break;
868 	}
869 }
870 
871 static void toggle_sym_value(struct menu *menu)
872 {
873 	if (!menu->sym)
874 		return;
875 
876 	sym_toggle_tristate_value(menu->sym);
877 	if (view_mode == FULL_VIEW)
878 		update_tree(&rootmenu, NULL);
879 	else if (view_mode == SPLIT_VIEW) {
880 		update_tree(browsed, NULL);
881 		display_list();
882 	}
883 	else if (view_mode == SINGLE_VIEW)
884 		display_tree_part();	//fixme: keep exp/coll
885 }
886 
887 static gint column2index(GtkTreeViewColumn * column)
888 {
889 	gint i;
890 
891 	for (i = 0; i < COL_NUMBER; i++) {
892 		GtkTreeViewColumn *col;
893 
894 		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
895 		if (col == column)
896 			return i;
897 	}
898 
899 	return -1;
900 }
901 
902 
903 /* User click: update choice (full) or goes down (single) */
904 gboolean
905 on_treeview2_button_press_event(GtkWidget * widget,
906 				GdkEventButton * event, gpointer user_data)
907 {
908 	GtkTreeView *view = GTK_TREE_VIEW(widget);
909 	GtkTreePath *path;
910 	GtkTreeViewColumn *column;
911 	GtkTreeIter iter;
912 	struct menu *menu;
913 	gint col;
914 
915 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
916 	gint tx = (gint) event->x;
917 	gint ty = (gint) event->y;
918 	gint cx, cy;
919 
920 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
921 				      &cy);
922 #else
923 	gtk_tree_view_get_cursor(view, &path, &column);
924 #endif
925 	if (path == NULL)
926 		return FALSE;
927 
928 	if (!gtk_tree_model_get_iter(model2, &iter, path))
929 		return FALSE;
930 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
931 
932 	col = column2index(column);
933 	if (event->type == GDK_2BUTTON_PRESS) {
934 		enum prop_type ptype;
935 		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
936 
937 		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
938 			// goes down into menu
939 			current = menu;
940 			display_tree_part();
941 			gtk_widget_set_sensitive(back_btn, TRUE);
942 		} else if ((col == COL_OPTION)) {
943 			toggle_sym_value(menu);
944 			gtk_tree_view_expand_row(view, path, TRUE);
945 		}
946 	} else {
947 		if (col == COL_VALUE) {
948 			toggle_sym_value(menu);
949 			gtk_tree_view_expand_row(view, path, TRUE);
950 		} else if (col == COL_NO || col == COL_MOD
951 			   || col == COL_YES) {
952 			change_sym_value(menu, col);
953 			gtk_tree_view_expand_row(view, path, TRUE);
954 		}
955 	}
956 
957 	return FALSE;
958 }
959 
960 /* Key pressed: update choice */
961 gboolean
962 on_treeview2_key_press_event(GtkWidget * widget,
963 			     GdkEventKey * event, gpointer user_data)
964 {
965 	GtkTreeView *view = GTK_TREE_VIEW(widget);
966 	GtkTreePath *path;
967 	GtkTreeViewColumn *column;
968 	GtkTreeIter iter;
969 	struct menu *menu;
970 	gint col;
971 
972 	gtk_tree_view_get_cursor(view, &path, &column);
973 	if (path == NULL)
974 		return FALSE;
975 
976 	if (event->keyval == GDK_space) {
977 		if (gtk_tree_view_row_expanded(view, path))
978 			gtk_tree_view_collapse_row(view, path);
979 		else
980 			gtk_tree_view_expand_row(view, path, FALSE);
981 		return TRUE;
982 	}
983 	if (event->keyval == GDK_KP_Enter) {
984 	}
985 	if (widget == tree1_w)
986 		return FALSE;
987 
988 	gtk_tree_model_get_iter(model2, &iter, path);
989 	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
990 
991 	if (!strcasecmp(event->string, "n"))
992 		col = COL_NO;
993 	else if (!strcasecmp(event->string, "m"))
994 		col = COL_MOD;
995 	else if (!strcasecmp(event->string, "y"))
996 		col = COL_YES;
997 	else
998 		col = -1;
999 	change_sym_value(menu, col);
1000 
1001 	return FALSE;
1002 }
1003 
1004 
1005 /* Row selection changed: update help */
1006 void
1007 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
1008 {
1009 	GtkTreeSelection *selection;
1010 	GtkTreeIter iter;
1011 	struct menu *menu;
1012 
1013 	selection = gtk_tree_view_get_selection(treeview);
1014 	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
1015 		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
1016 		text_insert_help(menu);
1017 	}
1018 }
1019 
1020 
1021 /* User click: display sub-tree in the right frame. */
1022 gboolean
1023 on_treeview1_button_press_event(GtkWidget * widget,
1024 				GdkEventButton * event, gpointer user_data)
1025 {
1026 	GtkTreeView *view = GTK_TREE_VIEW(widget);
1027 	GtkTreePath *path;
1028 	GtkTreeViewColumn *column;
1029 	GtkTreeIter iter;
1030 	struct menu *menu;
1031 
1032 	gint tx = (gint) event->x;
1033 	gint ty = (gint) event->y;
1034 	gint cx, cy;
1035 
1036 	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
1037 				      &cy);
1038 	if (path == NULL)
1039 		return FALSE;
1040 
1041 	gtk_tree_model_get_iter(model1, &iter, path);
1042 	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1043 
1044 	if (event->type == GDK_2BUTTON_PRESS) {
1045 		toggle_sym_value(menu);
1046 		current = menu;
1047 		display_tree_part();
1048 	} else {
1049 		browsed = menu;
1050 		display_tree_part();
1051 	}
1052 
1053 	gtk_widget_realize(tree2_w);
1054 	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1055 	gtk_widget_grab_focus(tree2_w);
1056 
1057 	return FALSE;
1058 }
1059 
1060 
1061 /* Fill a row of strings */
1062 static gchar **fill_row(struct menu *menu)
1063 {
1064 	static gchar *row[COL_NUMBER];
1065 	struct symbol *sym = menu->sym;
1066 	const char *def;
1067 	int stype;
1068 	tristate val;
1069 	enum prop_type ptype;
1070 	int i;
1071 
1072 	for (i = COL_OPTION; i <= COL_COLOR; i++)
1073 		g_free(row[i]);
1074 	bzero(row, sizeof(row));
1075 
1076 	row[COL_OPTION] =
1077 	    g_strdup_printf("%s %s", _(menu_get_prompt(menu)),
1078 			    sym && !sym_has_value(sym) ? "(NEW)" : "");
1079 
1080 	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1081 		row[COL_COLOR] = g_strdup("DarkGray");
1082 	else if (opt_mode == OPT_PROMPT &&
1083 			menu_has_prompt(menu) && !menu_is_visible(menu))
1084 		row[COL_COLOR] = g_strdup("DarkGray");
1085 	else
1086 		row[COL_COLOR] = g_strdup("Black");
1087 
1088 	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1089 	switch (ptype) {
1090 	case P_MENU:
1091 		row[COL_PIXBUF] = (gchar *) xpm_menu;
1092 		if (view_mode == SINGLE_VIEW)
1093 			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1094 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1095 		break;
1096 	case P_COMMENT:
1097 		row[COL_PIXBUF] = (gchar *) xpm_void;
1098 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1099 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1100 		break;
1101 	default:
1102 		row[COL_PIXBUF] = (gchar *) xpm_void;
1103 		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1104 		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1105 		break;
1106 	}
1107 
1108 	if (!sym)
1109 		return row;
1110 	row[COL_NAME] = g_strdup(sym->name);
1111 
1112 	sym_calc_value(sym);
1113 	sym->flags &= ~SYMBOL_CHANGED;
1114 
1115 	if (sym_is_choice(sym)) {	// parse childs for getting final value
1116 		struct menu *child;
1117 		struct symbol *def_sym = sym_get_choice_value(sym);
1118 		struct menu *def_menu = NULL;
1119 
1120 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1121 
1122 		for (child = menu->list; child; child = child->next) {
1123 			if (menu_is_visible(child)
1124 			    && child->sym == def_sym)
1125 				def_menu = child;
1126 		}
1127 
1128 		if (def_menu)
1129 			row[COL_VALUE] =
1130 			    g_strdup(_(menu_get_prompt(def_menu)));
1131 	}
1132 	if (sym->flags & SYMBOL_CHOICEVAL)
1133 		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1134 
1135 	stype = sym_get_type(sym);
1136 	switch (stype) {
1137 	case S_BOOLEAN:
1138 		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1139 			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1140 		if (sym_is_choice(sym))
1141 			break;
1142 		/* fall through */
1143 	case S_TRISTATE:
1144 		val = sym_get_tristate_value(sym);
1145 		switch (val) {
1146 		case no:
1147 			row[COL_NO] = g_strdup("N");
1148 			row[COL_VALUE] = g_strdup("N");
1149 			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1150 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1151 			break;
1152 		case mod:
1153 			row[COL_MOD] = g_strdup("M");
1154 			row[COL_VALUE] = g_strdup("M");
1155 			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1156 			break;
1157 		case yes:
1158 			row[COL_YES] = g_strdup("Y");
1159 			row[COL_VALUE] = g_strdup("Y");
1160 			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1161 			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1162 			break;
1163 		}
1164 
1165 		if (val != no && sym_tristate_within_range(sym, no))
1166 			row[COL_NO] = g_strdup("_");
1167 		if (val != mod && sym_tristate_within_range(sym, mod))
1168 			row[COL_MOD] = g_strdup("_");
1169 		if (val != yes && sym_tristate_within_range(sym, yes))
1170 			row[COL_YES] = g_strdup("_");
1171 		break;
1172 	case S_INT:
1173 	case S_HEX:
1174 	case S_STRING:
1175 		def = sym_get_string_value(sym);
1176 		row[COL_VALUE] = g_strdup(def);
1177 		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1178 		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1179 		break;
1180 	}
1181 
1182 	return row;
1183 }
1184 
1185 
1186 /* Set the node content with a row of strings */
1187 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1188 {
1189 	GdkColor color;
1190 	gboolean success;
1191 	GdkPixbuf *pix;
1192 
1193 	pix = gdk_pixbuf_new_from_xpm_data((const char **)
1194 					   row[COL_PIXBUF]);
1195 
1196 	gdk_color_parse(row[COL_COLOR], &color);
1197 	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1198 				  FALSE, FALSE, &success);
1199 
1200 	gtk_tree_store_set(tree, node,
1201 			   COL_OPTION, row[COL_OPTION],
1202 			   COL_NAME, row[COL_NAME],
1203 			   COL_NO, row[COL_NO],
1204 			   COL_MOD, row[COL_MOD],
1205 			   COL_YES, row[COL_YES],
1206 			   COL_VALUE, row[COL_VALUE],
1207 			   COL_MENU, (gpointer) menu,
1208 			   COL_COLOR, &color,
1209 			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1210 			   COL_PIXBUF, pix,
1211 			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1212 			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1213 			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1214 			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1215 			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1216 			   -1);
1217 
1218 	g_object_unref(pix);
1219 }
1220 
1221 
1222 /* Add a node to the tree */
1223 static void place_node(struct menu *menu, char **row)
1224 {
1225 	GtkTreeIter *parent = parents[indent - 1];
1226 	GtkTreeIter *node = parents[indent];
1227 
1228 	gtk_tree_store_append(tree, node, parent);
1229 	set_node(node, menu, row);
1230 }
1231 
1232 
1233 /* Find a node in the GTK+ tree */
1234 static GtkTreeIter found;
1235 
1236 /*
1237  * Find a menu in the GtkTree starting at parent.
1238  */
1239 GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
1240 				    struct menu *tofind)
1241 {
1242 	GtkTreeIter iter;
1243 	GtkTreeIter *child = &iter;
1244 	gboolean valid;
1245 	GtkTreeIter *ret;
1246 
1247 	valid = gtk_tree_model_iter_children(model2, child, parent);
1248 	while (valid) {
1249 		struct menu *menu;
1250 
1251 		gtk_tree_model_get(model2, child, 6, &menu, -1);
1252 
1253 		if (menu == tofind) {
1254 			memcpy(&found, child, sizeof(GtkTreeIter));
1255 			return &found;
1256 		}
1257 
1258 		ret = gtktree_iter_find_node(child, tofind);
1259 		if (ret)
1260 			return ret;
1261 
1262 		valid = gtk_tree_model_iter_next(model2, child);
1263 	}
1264 
1265 	return NULL;
1266 }
1267 
1268 
1269 /*
1270  * Update the tree by adding/removing entries
1271  * Does not change other nodes
1272  */
1273 static void update_tree(struct menu *src, GtkTreeIter * dst)
1274 {
1275 	struct menu *child1;
1276 	GtkTreeIter iter, tmp;
1277 	GtkTreeIter *child2 = &iter;
1278 	gboolean valid;
1279 	GtkTreeIter *sibling;
1280 	struct symbol *sym;
1281 	struct property *prop;
1282 	struct menu *menu1, *menu2;
1283 
1284 	if (src == &rootmenu)
1285 		indent = 1;
1286 
1287 	valid = gtk_tree_model_iter_children(model2, child2, dst);
1288 	for (child1 = src->list; child1; child1 = child1->next) {
1289 
1290 		prop = child1->prompt;
1291 		sym = child1->sym;
1292 
1293 	      reparse:
1294 		menu1 = child1;
1295 		if (valid)
1296 			gtk_tree_model_get(model2, child2, COL_MENU,
1297 					   &menu2, -1);
1298 		else
1299 			menu2 = NULL;	// force adding of a first child
1300 
1301 #ifdef DEBUG
1302 		printf("%*c%s | %s\n", indent, ' ',
1303 		       menu1 ? menu_get_prompt(menu1) : "nil",
1304 		       menu2 ? menu_get_prompt(menu2) : "nil");
1305 #endif
1306 
1307 		if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1308 		    (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1309 		    (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1310 
1311 			/* remove node */
1312 			if (gtktree_iter_find_node(dst, menu1) != NULL) {
1313 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1314 				valid = gtk_tree_model_iter_next(model2,
1315 								 child2);
1316 				gtk_tree_store_remove(tree2, &tmp);
1317 				if (!valid)
1318 					return;		/* next parent */
1319 				else
1320 					goto reparse;	/* next child */
1321 			} else
1322 				continue;
1323 		}
1324 
1325 		if (menu1 != menu2) {
1326 			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
1327 				if (!valid && !menu2)
1328 					sibling = NULL;
1329 				else
1330 					sibling = child2;
1331 				gtk_tree_store_insert_before(tree2,
1332 							     child2,
1333 							     dst, sibling);
1334 				set_node(child2, menu1, fill_row(menu1));
1335 				if (menu2 == NULL)
1336 					valid = TRUE;
1337 			} else {	// remove node
1338 				memcpy(&tmp, child2, sizeof(GtkTreeIter));
1339 				valid = gtk_tree_model_iter_next(model2,
1340 								 child2);
1341 				gtk_tree_store_remove(tree2, &tmp);
1342 				if (!valid)
1343 					return;	// next parent
1344 				else
1345 					goto reparse;	// next child
1346 			}
1347 		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1348 			set_node(child2, menu1, fill_row(menu1));
1349 		}
1350 
1351 		indent++;
1352 		update_tree(child1, child2);
1353 		indent--;
1354 
1355 		valid = gtk_tree_model_iter_next(model2, child2);
1356 	}
1357 }
1358 
1359 
1360 /* Display the whole tree (single/split/full view) */
1361 static void display_tree(struct menu *menu)
1362 {
1363 	struct symbol *sym;
1364 	struct property *prop;
1365 	struct menu *child;
1366 	enum prop_type ptype;
1367 
1368 	if (menu == &rootmenu) {
1369 		indent = 1;
1370 		current = &rootmenu;
1371 	}
1372 
1373 	for (child = menu->list; child; child = child->next) {
1374 		prop = child->prompt;
1375 		sym = child->sym;
1376 		ptype = prop ? prop->type : P_UNKNOWN;
1377 
1378 		if (sym)
1379 			sym->flags &= ~SYMBOL_CHANGED;
1380 
1381 		if ((view_mode == SPLIT_VIEW)
1382 		    && !(child->flags & MENU_ROOT) && (tree == tree1))
1383 			continue;
1384 
1385 		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1386 		    && (tree == tree2))
1387 			continue;
1388 
1389 		if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1390 		    (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1391 		    (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1392 			place_node(child, fill_row(child));
1393 #ifdef DEBUG
1394 		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1395 		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1396 		printf("%s", prop_get_type_name(ptype));
1397 		printf(" | ");
1398 		if (sym) {
1399 			printf("%s", sym_type_name(sym->type));
1400 			printf(" | ");
1401 			printf("%s", dbg_sym_flags(sym->flags));
1402 			printf("\n");
1403 		} else
1404 			printf("\n");
1405 #endif
1406 		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1407 		    && (tree == tree2))
1408 			continue;
1409 /*
1410                 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1411 		    || (view_mode == FULL_VIEW)
1412 		    || (view_mode == SPLIT_VIEW))*/
1413 
1414 		/* Change paned position if the view is not in 'split mode' */
1415 		if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1416 			gtk_paned_set_position(GTK_PANED(hpaned), 0);
1417 		}
1418 
1419 		if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1420 		    || (view_mode == FULL_VIEW)
1421 		    || (view_mode == SPLIT_VIEW)) {
1422 			indent++;
1423 			display_tree(child);
1424 			indent--;
1425 		}
1426 	}
1427 }
1428 
1429 /* Display a part of the tree starting at current node (single/split view) */
1430 static void display_tree_part(void)
1431 {
1432 	if (tree2)
1433 		gtk_tree_store_clear(tree2);
1434 	if (view_mode == SINGLE_VIEW)
1435 		display_tree(current);
1436 	else if (view_mode == SPLIT_VIEW)
1437 		display_tree(browsed);
1438 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1439 }
1440 
1441 /* Display the list in the left frame (split view) */
1442 static void display_list(void)
1443 {
1444 	if (tree1)
1445 		gtk_tree_store_clear(tree1);
1446 
1447 	tree = tree1;
1448 	display_tree(&rootmenu);
1449 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1450 	tree = tree2;
1451 }
1452 
1453 void fixup_rootmenu(struct menu *menu)
1454 {
1455 	struct menu *child;
1456 	static int menu_cnt = 0;
1457 
1458 	menu->flags |= MENU_ROOT;
1459 	for (child = menu->list; child; child = child->next) {
1460 		if (child->prompt && child->prompt->type == P_MENU) {
1461 			menu_cnt++;
1462 			fixup_rootmenu(child);
1463 			menu_cnt--;
1464 		} else if (!menu_cnt)
1465 			fixup_rootmenu(child);
1466 	}
1467 }
1468 
1469 
1470 /* Main */
1471 int main(int ac, char *av[])
1472 {
1473 	const char *name;
1474 	char *env;
1475 	gchar *glade_file;
1476 
1477 	bindtextdomain(PACKAGE, LOCALEDIR);
1478 	bind_textdomain_codeset(PACKAGE, "UTF-8");
1479 	textdomain(PACKAGE);
1480 
1481 	/* GTK stuffs */
1482 	gtk_set_locale();
1483 	gtk_init(&ac, &av);
1484 	glade_init();
1485 
1486 	//add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1487 	//add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1488 
1489 	/* Determine GUI path */
1490 	env = getenv(SRCTREE);
1491 	if (env)
1492 		glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1493 	else if (av[0][0] == '/')
1494 		glade_file = g_strconcat(av[0], ".glade", NULL);
1495 	else
1496 		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1497 
1498 	/* Conf stuffs */
1499 	if (ac > 1 && av[1][0] == '-') {
1500 		switch (av[1][1]) {
1501 		case 'a':
1502 			//showAll = 1;
1503 			break;
1504 		case 'h':
1505 		case '?':
1506 			printf("%s <config>\n", av[0]);
1507 			exit(0);
1508 		}
1509 		name = av[2];
1510 	} else
1511 		name = av[1];
1512 
1513 	conf_parse(name);
1514 	fixup_rootmenu(&rootmenu);
1515 	conf_read(NULL);
1516 
1517 	/* Load the interface and connect signals */
1518 	init_main_window(glade_file);
1519 	init_tree_model();
1520 	init_left_tree();
1521 	init_right_tree();
1522 
1523 	switch (view_mode) {
1524 	case SINGLE_VIEW:
1525 		display_tree_part();
1526 		break;
1527 	case SPLIT_VIEW:
1528 		display_list();
1529 		break;
1530 	case FULL_VIEW:
1531 		display_tree(&rootmenu);
1532 		break;
1533 	}
1534 
1535 	gtk_main();
1536 
1537 	return 0;
1538 }
1539 
1540 static void conf_changed(void)
1541 {
1542 	bool changed = conf_get_changed();
1543 	gtk_widget_set_sensitive(save_btn, changed);
1544 	gtk_widget_set_sensitive(save_menu_item, changed);
1545 }
1546