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