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