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