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