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