code:
/* Hey EMACS -*- linux-c -*- */
/*
 *
 * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
 * Released under the terms of the GNU GPL v2.0.
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdlib.h>
#include "lkc.h"
#include "images.c"

#include <glade/glade.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <gdk/gdkkeysyms.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

//#define DEBUG

enum {
	SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
};

enum {
	OPT_NORMAL, OPT_ALL, OPT_PROMPT
};

static gint view_mode = FULL_VIEW;
static gboolean show_name = TRUE;
static gboolean show_range = TRUE;
static gboolean show_value = TRUE;
static gboolean resizeable = FALSE;
static int opt_mode = OPT_NORMAL;

GtkWidget *main_wnd = NULL;
GtkWidget *tree1_w = NULL;	// left  frame
GtkWidget *tree2_w = NULL;	// right frame
GtkWidget *text_w = NULL;
GtkWidget *hpaned = NULL;
GtkWidget *vpaned = NULL;
GtkWidget *back_btn = NULL;
GtkWidget *save_btn = NULL;
GtkWidget *save_menu_item = NULL;

GtkTextTag *tag1, *tag2;
GdkColor color;

GtkTreeStore *tree1, *tree2, *tree;
GtkTreeModel *model1, *model2;
static GtkTreeIter *parents[256];
static gint indent;

static struct menu *current; // current node for SINGLE view
static struct menu *browsed; // browsed node for SPLIT view

enum {
	COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
	COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
	COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
	COL_NUMBER
};

static void display_list(void);
static void display_tree(struct menu *menu);
static void display_tree_part(void);
static void update_tree(struct menu *src, GtkTreeIter * dst);
static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
static gchar **fill_row(struct menu *menu);
static void conf_changed(void);

/* Helping/Debugging Functions */

const char *dbg_sym_flags(int val)
{
	static char buf[256];

	bzero(buf, 256);

	if (val & SYMBOL_CONST)
		strcat(buf, "const/");
	if (val & SYMBOL_CHECK)
		strcat(buf, "check/");
	if (val & SYMBOL_CHOICE)
		strcat(buf, "choice/");
	if (val & SYMBOL_CHOICEVAL)
		strcat(buf, "choiceval/");
	if (val & SYMBOL_VALID)
		strcat(buf, "valid/");
	if (val & SYMBOL_OPTIONAL)
		strcat(buf, "optional/");
	if (val & SYMBOL_WRITE)
		strcat(buf, "write/");
	if (val & SYMBOL_CHANGED)
		strcat(buf, "changed/");
	if (val & SYMBOL_AUTO)
		strcat(buf, "auto/");

	buf[strlen(buf) - 1] = '\0';

	return buf;
}

void replace_button_icon(GladeXML * xml, GdkDrawable * window,
			 GtkStyle * style, gchar * btn_name, gchar ** xpm)
{
	GdkPixmap *pixmap;
	GdkBitmap *mask;
	GtkToolButton *button;
	GtkWidget *image;

	pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
					      &style->bg[GTK_STATE_NORMAL],
					      xpm);

	button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
	image = gtk_image_new_from_pixmap(pixmap, mask);
	gtk_widget_show(image);
	gtk_tool_button_set_icon_widget(button, image);
}

/* Main Window Initialization */
void init_main_window(const gchar * glade_file)
{
	GladeXML *xml;
	GtkWidget *widget;
	GtkTextBuffer *txtbuf;
	GtkStyle *style;

	xml = glade_xml_new(glade_file, "window1", NULL);
	if (!xml)
		g_error(_("GUI loading failed !\n"));
	glade_xml_signal_autoconnect(xml);

	main_wnd = glade_xml_get_widget(xml, "window1");
	hpaned = glade_xml_get_widget(xml, "hpaned1");
	vpaned = glade_xml_get_widget(xml, "vpaned1");
	tree1_w = glade_xml_get_widget(xml, "treeview1");
	tree2_w = glade_xml_get_widget(xml, "treeview2");
	text_w = glade_xml_get_widget(xml, "textview3");

	back_btn = glade_xml_get_widget(xml, "button1");
	gtk_widget_set_sensitive(back_btn, FALSE);

	widget = glade_xml_get_widget(xml, "show_name1");
	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
				       show_name);

	widget = glade_xml_get_widget(xml, "show_range1");
	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
				       show_range);

	widget = glade_xml_get_widget(xml, "show_data1");
	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
				       show_value);

	save_btn = glade_xml_get_widget(xml, "button3");
	save_menu_item = glade_xml_get_widget(xml, "save1");
	conf_set_changed_callback(conf_changed);

	style = gtk_widget_get_style(main_wnd);
	widget = glade_xml_get_widget(xml, "toolbar1");

	replace_button_icon(xml, main_wnd->window, style,
			    "button4", (gchar **) xpm_single_view);
	replace_button_icon(xml, main_wnd->window, style,
			    "button5", (gchar **) xpm_split_view);
	replace_button_icon(xml, main_wnd->window, style,
			    "button6", (gchar **) xpm_tree_view);

	txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
	tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
					  "foreground", "red",
					  "weight", PANGO_WEIGHT_BOLD,
					  NULL);
	tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
					  /*"style", PANGO_STYLE_OBLIQUE, */
					  NULL);

	gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);

	gtk_widget_show(main_wnd);
}

void init_tree_model(void)
{
	gint i;

	tree = tree2 = gtk_tree_store_new(COL_NUMBER,
					  G_TYPE_STRING, G_TYPE_STRING,
					  G_TYPE_STRING, G_TYPE_STRING,
					  G_TYPE_STRING, G_TYPE_STRING,
					  G_TYPE_POINTER, GDK_TYPE_COLOR,
					  G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
					  G_TYPE_BOOLEAN);
	model2 = GTK_TREE_MODEL(tree2);

	for (parents[0] = NULL, i = 1; i < 256; i++)
		parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));

	tree1 = gtk_tree_store_new(COL_NUMBER,
				   G_TYPE_STRING, G_TYPE_STRING,
				   G_TYPE_STRING, G_TYPE_STRING,
				   G_TYPE_STRING, G_TYPE_STRING,
				   G_TYPE_POINTER, GDK_TYPE_COLOR,
				   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
				   G_TYPE_BOOLEAN);
	model1 = GTK_TREE_MODEL(tree1);
}

void init_left_tree(void)
{
	GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
	GtkCellRenderer *renderer;
	GtkTreeSelection *sel;
	GtkTreeViewColumn *column;

	gtk_tree_view_set_model(view, model1);
	gtk_tree_view_set_headers_visible(view, TRUE);
	gtk_tree_view_set_rules_hint(view, TRUE);

	column = gtk_tree_view_column_new();
	gtk_tree_view_append_column(view, column);
	gtk_tree_view_column_set_title(column, _("Options"));

	renderer = gtk_cell_renderer_toggle_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
					renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
					    renderer,
					    "active", COL_BTNACT,
					    "inconsistent", COL_BTNINC,
					    "visible", COL_BTNVIS,
					    "radio", COL_BTNRAD, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
					renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
					    renderer,
					    "text", COL_OPTION,
					    "foreground-gdk",
					    COL_COLOR, NULL);

	sel = gtk_tree_view_get_selection(view);
	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
	gtk_widget_realize(tree1_w);
}

static void renderer_edited(GtkCellRendererText * cell,
			    const gchar * path_string,
			    const gchar * new_text, gpointer user_data);

void init_right_tree(void)
{
	GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
	GtkCellRenderer *renderer;
	GtkTreeSelection *sel;
	GtkTreeViewColumn *column;
	gint i;

	gtk_tree_view_set_model(view, model2);
	gtk_tree_view_set_headers_visible(view, TRUE);
	gtk_tree_view_set_rules_hint(view, TRUE);

	column = gtk_tree_view_column_new();
	gtk_tree_view_append_column(view, column);
	gtk_tree_view_column_set_title(column, _("Options"));

	renderer = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
					renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
					    renderer,
					    "pixbuf", COL_PIXBUF,
					    "visible", COL_PIXVIS, NULL);
	renderer = gtk_cell_renderer_toggle_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
					renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
					    renderer,
					    "active", COL_BTNACT,
					    "inconsistent", COL_BTNINC,
					    "visible", COL_BTNVIS,
					    "radio", COL_BTNRAD, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
					renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
					    renderer,
					    "text", COL_OPTION,
					    "foreground-gdk",
					    COL_COLOR, NULL);

	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(view, -1,
						    _("Name"), renderer,
						    "text", COL_NAME,
						    "foreground-gdk",
						    COL_COLOR, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(view, -1,
						    "N", renderer,
						    "text", COL_NO,
						    "foreground-gdk",
						    COL_COLOR, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(view, -1,
						    "M", renderer,
						    "text", COL_MOD,
						    "foreground-gdk",
						    COL_COLOR, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(view, -1,
						    "Y", renderer,
						    "text", COL_YES,
						    "foreground-gdk",
						    COL_COLOR, NULL);
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(view, -1,
						    _("Value"), renderer,
						    "text", COL_VALUE,
						    "editable",
						    COL_EDIT,
						    "foreground-gdk",
						    COL_COLOR, NULL);
	g_signal_connect(G_OBJECT(renderer), "edited",
			 G_CALLBACK(renderer_edited), NULL);

	column = gtk_tree_view_get_column(view, COL_NAME);
	gtk_tree_view_column_set_visible(column, show_name);
	column = gtk_tree_view_get_column(view, COL_NO);
	gtk_tree_view_column_set_visible(column, show_range);
	column = gtk_tree_view_get_column(view, COL_MOD);
	gtk_tree_view_column_set_visible(column, show_range);
	column = gtk_tree_view_get_column(view, COL_YES);
	gtk_tree_view_column_set_visible(column, show_range);
	column = gtk_tree_view_get_column(view, COL_VALUE);
	gtk_tree_view_column_set_visible(column, show_value);

	if (resizeable) {
		for (i = 0; i < COL_VALUE; i++) {
			column = gtk_tree_view_get_column(view, i);
			gtk_tree_view_column_set_resizable(column, TRUE);
		}
	}

	sel = gtk_tree_view_get_selection(view);
	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
}

/* Utility Functions */

static void text_insert_help(struct menu *menu)
{
	GtkTextBuffer *buffer;
	GtkTextIter start, end;
	const char *prompt = _(menu_get_prompt(menu));
	struct gstr help = str_new();

	menu_get_ext_help(menu, &help);

	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	gtk_text_buffer_delete(buffer, &start, &end);
	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);

	gtk_text_buffer_get_end_iter(buffer, &end);
	gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
					 NULL);
	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
	gtk_text_buffer_get_end_iter(buffer, &end);
	gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
					 NULL);
	str_free(&help);
}

static void text_insert_msg(const char *title, const char *message)
{
	GtkTextBuffer *buffer;
	GtkTextIter start, end;
	const char *msg = message;

	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	gtk_text_buffer_delete(buffer, &start, &end);
	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);

	gtk_text_buffer_get_end_iter(buffer, &end);
	gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
					 NULL);
	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
	gtk_text_buffer_get_end_iter(buffer, &end);
	gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
					 NULL);
}

/* Main Windows Callbacks */

void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
				 gpointer user_data)
{
	GtkWidget *dialog, *label;
	gint result;

	if (!conf_get_changed())
		return FALSE;

	dialog = gtk_dialog_new_with_buttons(_("Warning !"),
					     GTK_WINDOW(main_wnd),
					     (GtkDialogFlags)
					     (GTK_DIALOG_MODAL |
					      GTK_DIALOG_DESTROY_WITH_PARENT),
					     GTK_STOCK_OK,
					     GTK_RESPONSE_YES,
					     GTK_STOCK_NO,
					     GTK_RESPONSE_NO,
					     GTK_STOCK_CANCEL,
					     GTK_RESPONSE_CANCEL, NULL);
	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
					GTK_RESPONSE_CANCEL);

	label = gtk_label_new(_("\nSave configuration ?\n"));
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
	gtk_widget_show(label);

	result = gtk_dialog_run(GTK_DIALOG(dialog));
	switch (result) {
	case GTK_RESPONSE_YES:
		on_save_activate(NULL, NULL);
		return FALSE;
	case GTK_RESPONSE_NO:
		return FALSE;
	case GTK_RESPONSE_CANCEL:
	case GTK_RESPONSE_DELETE_EVENT:
	default:
		gtk_widget_destroy(dialog);
		return TRUE;
	}

	return FALSE;
}

void on_window1_destroy(GtkObject * object, gpointer user_data)
{
	gtk_main_quit();
}

void
on_window1_size_request(GtkWidget * widget,
			GtkRequisition * requisition, gpointer user_data)
{
	static gint old_h;
	gint w, h;

	if (widget->window == NULL)
		gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
	else
		gdk_window_get_size(widget->window, &w, &h);

	if (h == old_h)
		return;
	old_h = h;

	gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
}

/* Menu & Toolbar Callbacks */

static void
load_filename(GtkFileSelection * file_selector, gpointer user_data)
{
	const gchar *fn;

	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
					     (user_data));

	if (conf_read(fn))
		text_insert_msg(_("Error"), _("Unable to load configuration !"));
	else
		display_tree(&rootmenu);
}

void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkWidget *fs;

	fs = gtk_file_selection_new(_("Load file..."));
	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
			 "clicked",
			 G_CALLBACK(load_filename), (gpointer) fs);
	g_signal_connect_swapped(GTK_OBJECT
				 (GTK_FILE_SELECTION(fs)->ok_button),
				 "clicked", G_CALLBACK(gtk_widget_destroy),
				 (gpointer) fs);
	g_signal_connect_swapped(GTK_OBJECT
				 (GTK_FILE_SELECTION(fs)->cancel_button),
				 "clicked", G_CALLBACK(gtk_widget_destroy),
				 (gpointer) fs);
	gtk_widget_show(fs);
}

void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	if (conf_write(NULL))
		text_insert_msg(_("Error"), _("Unable to save configuration !"));
}

static void
store_filename(GtkFileSelection * file_selector, gpointer user_data)
{
	const gchar *fn;

	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
					     (user_data));

	if (conf_write(fn))
		text_insert_msg(_("Error"), _("Unable to save configuration !"));

	gtk_widget_destroy(GTK_WIDGET(user_data));
}

void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkWidget *fs;

	fs = gtk_file_selection_new(_("Save file as..."));
	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
			 "clicked",
			 G_CALLBACK(store_filename), (gpointer) fs);
	g_signal_connect_swapped(GTK_OBJECT
				 (GTK_FILE_SELECTION(fs)->ok_button),
				 "clicked", G_CALLBACK(gtk_widget_destroy),
				 (gpointer) fs);
	g_signal_connect_swapped(GTK_OBJECT
				 (GTK_FILE_SELECTION(fs)->cancel_button),
				 "clicked", G_CALLBACK(gtk_widget_destroy),
				 (gpointer) fs);
	gtk_widget_show(fs);
}

void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	if (!on_window1_delete_event(NULL, NULL, NULL))
		gtk_widget_destroy(GTK_WIDGET(main_wnd));
}

void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkTreeViewColumn *col;

	show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
	if (col)
		gtk_tree_view_column_set_visible(col, show_name);
}

void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkTreeViewColumn *col;

	show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
	if (col)
		gtk_tree_view_column_set_visible(col, show_range);
	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
	if (col)
		gtk_tree_view_column_set_visible(col, show_range);
	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
	if (col)
		gtk_tree_view_column_set_visible(col, show_range);

}

void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkTreeViewColumn *col;

	show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
	if (col)
		gtk_tree_view_column_set_visible(col, show_value);
}

void
on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
	opt_mode = OPT_NORMAL;
	gtk_tree_store_clear(tree2);
	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
}

void
on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
{
	opt_mode = OPT_ALL;
	gtk_tree_store_clear(tree2);
	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
}

void
on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
{
	opt_mode = OPT_PROMPT;
	gtk_tree_store_clear(tree2);
	display_tree(&rootmenu);	/* instead of update_tree to speed-up */
}

void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkWidget *dialog;
	const gchar *intro_text = _(
	    "Welcome to gkc, the GTK+ graphical configuration tool\n"
	    "For each option, a blank box indicates the feature is disabled, a\n"
	    "check indicates it is enabled, and a dot indicates that it is to\n"
	    "be compiled as a module.  Clicking on the box will cycle through the three states.\n"
	    "\n"
	    "If you do not see an option (e.g., a device driver) that you\n"
	    "believe should be present, try turning on Show All Options\n"
	    "under the Options menu.\n"
	    "Although there is no cross reference yet to help you figure out\n"
	    "what other options must be enabled to support the option you\n"
	    "are interested in, you can still view the help of a grayed-out\n"
	    "option.\n"
	    "\n"
	    "Toggling Show Debug Info under the Options menu will show \n"
	    "the dependencies, which you can then match by examining other options.");

	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
					GTK_DIALOG_DESTROY_WITH_PARENT,
					GTK_MESSAGE_INFO,
					GTK_BUTTONS_CLOSE, "%s", intro_text);
	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
				 G_CALLBACK(gtk_widget_destroy),
				 GTK_OBJECT(dialog));
	gtk_widget_show_all(dialog);
}

void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkWidget *dialog;
	const gchar *about_text =
	    _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
	      "Based on the source code from Roman Zippel.\n");

	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
					GTK_DIALOG_DESTROY_WITH_PARENT,
					GTK_MESSAGE_INFO,
					GTK_BUTTONS_CLOSE, "%s", about_text);
	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
				 G_CALLBACK(gtk_widget_destroy),
				 GTK_OBJECT(dialog));
	gtk_widget_show_all(dialog);
}

void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
{
	GtkWidget *dialog;
	const gchar *license_text =
	    _("gkc is released under the terms of the GNU GPL v2.\n"
	      "For more information, please see the source code or\n"
	      "visit http://www.fsf.org/licenses/licenses.html\n");

	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
					GTK_DIALOG_DESTROY_WITH_PARENT,
					GTK_MESSAGE_INFO,
					GTK_BUTTONS_CLOSE, "%s", license_text);
	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
				 G_CALLBACK(gtk_widget_destroy),
				 GTK_OBJECT(dialog));
	gtk_widget_show_all(dialog);
}

void on_back_clicked(GtkButton * button, gpointer user_data)
{
	enum prop_type ptype;

	current = current->parent;
	ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
	if (ptype != P_MENU)
		current = current->parent;
	display_tree_part();

	if (current == &rootmenu)
		gtk_widget_set_sensitive(back_btn, FALSE);
}

void on_load_clicked(GtkButton * button, gpointer user_data)
{
	on_load1_activate(NULL, user_data);
}

void on_single_clicked(GtkButton * button, gpointer user_data)
{
	view_mode = SINGLE_VIEW;
	gtk_widget_hide(tree1_w);
	current = &rootmenu;
	display_tree_part();
}

void on_split_clicked(GtkButton * button, gpointer user_data)
{
	gint w, h;
	view_mode = SPLIT_VIEW;
	gtk_widget_show(tree1_w);
	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
	if (tree2)
		gtk_tree_store_clear(tree2);
	display_list();

	/* Disable back btn, like in full mode. */
	gtk_widget_set_sensitive(back_btn, FALSE);
}

void on_full_clicked(GtkButton * button, gpointer user_data)
{
	view_mode = FULL_VIEW;
	gtk_widget_hide(tree1_w);
	if (tree2)
		gtk_tree_store_clear(tree2);
	display_tree(&rootmenu);
	gtk_widget_set_sensitive(back_btn, FALSE);
}

void on_collapse_clicked(GtkButton * button, gpointer user_data)
{
	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
}

void on_expand_clicked(GtkButton * button, gpointer user_data)
{
	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
}

/* CTree Callbacks */

/* Change hex/int/string value in the cell */
static void renderer_edited(GtkCellRendererText * cell,
			    const gchar * path_string,
			    const gchar * new_text, gpointer user_data)
{
	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
	GtkTreeIter iter;
	const char *old_def, *new_def;
	struct menu *menu;
	struct symbol *sym;

	if (!gtk_tree_model_get_iter(model2, &iter, path))
		return;

	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
	sym = menu->sym;

	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
	new_def = new_text;

	sym_set_string_value(sym, new_def);

	update_tree(&rootmenu, NULL);

	gtk_tree_path_free(path);
}

/* Change the value of a symbol and update the tree */
static void change_sym_value(struct menu *menu, gint col)
{
	struct symbol *sym = menu->sym;
	tristate newval;

	if (!sym)
		return;

	if (col == COL_NO)
		newval = no;
	else if (col == COL_MOD)
		newval = mod;
	else if (col == COL_YES)
		newval = yes;
	else
		return;

	switch (sym_get_type(sym)) {
	case S_BOOLEAN:
	case S_TRISTATE:
		if (!sym_tristate_within_range(sym, newval))
			newval = yes;
		sym_set_tristate_value(sym, newval);
		if (view_mode == FULL_VIEW)
			update_tree(&rootmenu, NULL);
		else if (view_mode == SPLIT_VIEW) {
			update_tree(browsed, NULL);
			display_list();
		}
		else if (view_mode == SINGLE_VIEW)
			display_tree_part();	//fixme: keep exp/coll
		break;
	case S_INT:
	case S_HEX:
	case S_STRING:
	default:
		break;
	}
}

static void toggle_sym_value(struct menu *menu)
{
	if (!menu->sym)
		return;

	sym_toggle_tristate_value(menu->sym);
	if (view_mode == FULL_VIEW)
		update_tree(&rootmenu, NULL);
	else if (view_mode == SPLIT_VIEW) {
		update_tree(browsed, NULL);
		display_list();
	}
	else if (view_mode == SINGLE_VIEW)
		display_tree_part();	//fixme: keep exp/coll
}

static gint column2index(GtkTreeViewColumn * column)
{
	gint i;

	for (i = 0; i < COL_NUMBER; i++) {
		GtkTreeViewColumn *col;

		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
		if (col == column)
			return i;
	}

	return -1;
}

/* User click: update choice (full) or goes down (single) */
gboolean
on_treeview2_button_press_event(GtkWidget * widget,
				GdkEventButton * event, gpointer user_data)
{
	GtkTreeView *view = GTK_TREE_VIEW(widget);
	GtkTreePath *path;
	GtkTreeViewColumn *column;
	GtkTreeIter iter;
	struct menu *menu;
	gint col;

#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
	gint tx = (gint) event->x;
	gint ty = (gint) event->y;
	gint cx, cy;

	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
				      &cy);
#else
	gtk_tree_view_get_cursor(view, &path, &column);
#endif
	if (path == NULL)
		return FALSE;

	if (!gtk_tree_model_get_iter(model2, &iter, path))
		return FALSE;
	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);

	col = column2index(column);
	if (event->type == GDK_2BUTTON_PRESS) {
		enum prop_type ptype;
		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;

		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
			// goes down into menu
			current = menu;
			display_tree_part();
			gtk_widget_set_sensitive(back_btn, TRUE);
		} else if (col == COL_OPTION) {
			toggle_sym_value(menu);
			gtk_tree_view_expand_row(view, path, TRUE);
		}
	} else {
		if (col == COL_VALUE) {
			toggle_sym_value(menu);
			gtk_tree_view_expand_row(view, path, TRUE);
		} else if (col == COL_NO || col == COL_MOD
			   || col == COL_YES) {
			change_sym_value(menu, col);
			gtk_tree_view_expand_row(view, path, TRUE);
		}
	}

	return FALSE;
}

/* Key pressed: update choice */
gboolean
on_treeview2_key_press_event(GtkWidget * widget,
			     GdkEventKey * event, gpointer user_data)
{
	GtkTreeView *view = GTK_TREE_VIEW(widget);
	GtkTreePath *path;
	GtkTreeViewColumn *column;
	GtkTreeIter iter;
	struct menu *menu;
	gint col;

	gtk_tree_view_get_cursor(view, &path, &column);
	if (path == NULL)
		return FALSE;

	if (event->keyval == GDK_space) {
		if (gtk_tree_view_row_expanded(view, path))
			gtk_tree_view_collapse_row(view, path);
		else
			gtk_tree_view_expand_row(view, path, FALSE);
		return TRUE;
	}
	if (event->keyval == GDK_KP_Enter) {
	}
	if (widget == tree1_w)
		return FALSE;

	gtk_tree_model_get_iter(model2, &iter, path);
	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);

	if (!strcasecmp(event->string, "n"))
		col = COL_NO;
	else if (!strcasecmp(event->string, "m"))
		col = COL_MOD;
	else if (!strcasecmp(event->string, "y"))
		col = COL_YES;
	else
		col = -1;
	change_sym_value(menu, col);

	return FALSE;
}

/* Row selection changed: update help */
void
on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
{
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	struct menu *menu;

	selection = gtk_tree_view_get_selection(treeview);
	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
		text_insert_help(menu);
	}
}

/* User click: display sub-tree in the right frame. */
gboolean
on_treeview1_button_press_event(GtkWidget * widget,
				GdkEventButton * event, gpointer user_data)
{
	GtkTreeView *view = GTK_TREE_VIEW(widget);
	GtkTreePath *path;
	GtkTreeViewColumn *column;
	GtkTreeIter iter;
	struct menu *menu;

	gint tx = (gint) event->x;
	gint ty = (gint) event->y;
	gint cx, cy;

	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
				      &cy);
	if (path == NULL)
		return FALSE;

	gtk_tree_model_get_iter(model1, &iter, path);
	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);

	if (event->type == GDK_2BUTTON_PRESS) {
		toggle_sym_value(menu);
		current = menu;
		display_tree_part();
	} else {
		browsed = menu;
		display_tree_part();
	}

	gtk_widget_realize(tree2_w);
	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
	gtk_widget_grab_focus(tree2_w);

	return FALSE;
}

/* Fill a row of strings */
static gchar **fill_row(struct menu *menu)
{
	static gchar *row[COL_NUMBER];
	struct symbol *sym = menu->sym;
	const char *def;
	int stype;
	tristate val;
	enum prop_type ptype;
	int i;

	for (i = COL_OPTION; i <= COL_COLOR; i++)
		g_free(row[i]);
	bzero(row, sizeof(row));

	row[COL_OPTION] =
	    g_strdup_printf("%s %s", _(menu_get_prompt(menu)),
			    sym && !sym_has_value(sym) ? "(NEW)" : "");

	if (opt_mode == OPT_ALL && !menu_is_visible(menu))
		row[COL_COLOR] = g_strdup("DarkGray");
	else if (opt_mode == OPT_PROMPT &&
			menu_has_prompt(menu) && !menu_is_visible(menu))
		row[COL_COLOR] = g_strdup("DarkGray");
	else
		row[COL_COLOR] = g_strdup("Black");

	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
	switch (ptype) {
	case P_MENU:
		row[COL_PIXBUF] = (gchar *) xpm_menu;
		if (view_mode == SINGLE_VIEW)
			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
		break;
	case P_COMMENT:
		row[COL_PIXBUF] = (gchar *) xpm_void;
		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
		break;
	default:
		row[COL_PIXBUF] = (gchar *) xpm_void;
		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
		break;
	}

	if (!sym)
		return row;
	row[COL_NAME] = g_strdup(sym->name);

	sym_calc_value(sym);
	sym->flags &= ~SYMBOL_CHANGED;

	if (sym_is_choice(sym)) {	// parse childs for getting final value
		struct menu *child;
		struct symbol *def_sym = sym_get_choice_value(sym);
		struct menu *def_menu = NULL;

		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);

		for (child = menu->list; child; child = child->next) {
			if (menu_is_visible(child)
			    && child->sym == def_sym)
				def_menu = child;
		}

		if (def_menu)
			row[COL_VALUE] =
			    g_strdup(_(menu_get_prompt(def_menu)));
	}
	if (sym->flags & SYMBOL_CHOICEVAL)
		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);

	stype = sym_get_type(sym);
	switch (stype) {
	case S_BOOLEAN:
		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
		if (sym_is_choice(sym))
			break;
		/* fall through */
	case S_TRISTATE:
		val = sym_get_tristate_value(sym);
		switch (val) {
		case no:
			row[COL_NO] = g_strdup("N");
			row[COL_VALUE] = g_strdup("N");
			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
			break;
		case mod:
			row[COL_MOD] = g_strdup("M");
			row[COL_VALUE] = g_strdup("M");
			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
			break;
		case yes:
			row[COL_YES] = g_strdup("Y");
			row[COL_VALUE] = g_strdup("Y");
			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
			break;
		}

		if (val != no && sym_tristate_within_range(sym, no))
			row[COL_NO] = g_strdup("_");
		if (val != mod && sym_tristate_within_range(sym, mod))
			row[COL_MOD] = g_strdup("_");
		if (val != yes && sym_tristate_within_range(sym, yes))
			row[COL_YES] = g_strdup("_");
		break;
	case S_INT:
	case S_HEX:
	case S_STRING:
		def = sym_get_string_value(sym);
		row[COL_VALUE] = g_strdup(def);
		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
		break;
	}

	return row;
}

/* Set the node content with a row of strings */
static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
{
	GdkColor color;
	gboolean success;
	GdkPixbuf *pix;

	pix = gdk_pixbuf_new_from_xpm_data((const char **)
					   row[COL_PIXBUF]);

	gdk_color_parse(row[COL_COLOR], &color);
	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
				  FALSE, FALSE, &success);

	gtk_tree_store_set(tree, node,
			   COL_OPTION, row[COL_OPTION],
			   COL_NAME, row[COL_NAME],
			   COL_NO, row[COL_NO],
			   COL_MOD, row[COL_MOD],
			   COL_YES, row[COL_YES],
			   COL_VALUE, row[COL_VALUE],
			   COL_MENU, (gpointer) menu,
			   COL_COLOR, &color,
			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
			   COL_PIXBUF, pix,
			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
			   -1);

	g_object_unref(pix);
}

/* Add a node to the tree */
static void place_node(struct menu *menu, char **row)
{
	GtkTreeIter *parent = parents[indent - 1];
	GtkTreeIter *node = parents[indent];

	gtk_tree_store_append(tree, node, parent);
	set_node(node, menu, row);
}

/* Find a node in the GTK+ tree */
static GtkTreeIter found;

/*
 * Find a menu in the GtkTree starting at parent.
 */
GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
				    struct menu *tofind)
{
	GtkTreeIter iter;
	GtkTreeIter *child = &iter;
	gboolean valid;
	GtkTreeIter *ret;

	valid = gtk_tree_model_iter_children(model2, child, parent);
	while (valid) {
		struct menu *menu;

		gtk_tree_model_get(model2, child, 6, &menu, -1);

		if (menu == tofind) {
			memcpy(&found, child, sizeof(GtkTreeIter));
			return &found;
		}

		ret = gtktree_iter_find_node(child, tofind);
		if (ret)
			return ret;

		valid = gtk_tree_model_iter_next(model2, child);
	}

	return NULL;
}

/*
 * Update the tree by adding/removing entries
 * Does not change other nodes
 */
static void update_tree(struct menu *src, GtkTreeIter * dst)
{
	struct menu *child1;
	GtkTreeIter iter, tmp;
	GtkTreeIter *child2 = &iter;
	gboolean valid;
	GtkTreeIter *sibling;
	struct symbol *sym;
	struct menu *menu1, *menu2;

	if (src == &rootmenu)
		indent = 1;

	valid = gtk_tree_model_iter_children(model2, child2, dst);
	for (child1 = src->list; child1; child1 = child1->next) {

		sym = child1->sym;

	      reparse:
		menu1 = child1;
		if (valid)
			gtk_tree_model_get(model2, child2, COL_MENU,
					   &menu2, -1);
		else
			menu2 = NULL;	// force adding of a first child

#ifdef DEBUG
		printf("%*c%s | %s\n", indent, ' ',
		       menu1 ? menu_get_prompt(menu1) : "nil",
		       menu2 ? menu_get_prompt(menu2) : "nil");
#endif

		if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
		    (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
		    (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {

			/* remove node */
			if (gtktree_iter_find_node(dst, menu1) != NULL) {
				memcpy(&tmp, child2, sizeof(GtkTreeIter));
				valid = gtk_tree_model_iter_next(model2,
								 child2);
				gtk_tree_store_remove(tree2, &tmp);
				if (!valid)
					return;		/* next parent */
				else
					goto reparse;	/* next child */
			} else
				continue;
		}

		if (menu1 != menu2) {
			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
				if (!valid && !menu2)
					sibling = NULL;
				else
					sibling = child2;
				gtk_tree_store_insert_before(tree2,
							     child2,
							     dst, sibling);
				set_node(child2, menu1, fill_row(menu1));
				if (menu2 == NULL)
					valid = TRUE;
			} else {	// remove node
				memcpy(&tmp, child2, sizeof(GtkTreeIter));
				valid = gtk_tree_model_iter_next(model2,
								 child2);
				gtk_tree_store_remove(tree2, &tmp);
				if (!valid)
					return;	// next parent
				else
					goto reparse;	// next child
			}
		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
			set_node(child2, menu1, fill_row(menu1));
		}

		indent++;
		update_tree(child1, child2);
		indent--;

		valid = gtk_tree_model_iter_next(model2, child2);
	}
}

/* Display the whole tree (single/split/full view) */
static void display_tree(struct menu *menu)
{
	struct symbol *sym;
	struct property *prop;
	struct menu *child;
	enum prop_type ptype;

	if (menu == &rootmenu) {
		indent = 1;
		current = &rootmenu;
	}

	for (child = menu->list; child; child = child->next) {
		prop = child->prompt;
		sym = child->sym;
		ptype = prop ? prop->type : P_UNKNOWN;

		if (sym)
			sym->flags &= ~SYMBOL_CHANGED;

		if ((view_mode == SPLIT_VIEW)
		    && !(child->flags & MENU_ROOT) && (tree == tree1))
			continue;

		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
		    && (tree == tree2))
			continue;

		if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
		    (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
		    (opt_mode == OPT_ALL    && menu_get_prompt(child)))
			place_node(child, fill_row(child));
#ifdef DEBUG
		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
		printf("%s", prop_get_type_name(ptype));
		printf(" | ");
		if (sym) {
			printf("%s", sym_type_name(sym->type));
			printf(" | ");
			printf("%s", dbg_sym_flags(sym->flags));
			printf("\n");
		} else
			printf("\n");
#endif
		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
		    && (tree == tree2))
			continue;
/*
		if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
		    || (view_mode == FULL_VIEW)
		    || (view_mode == SPLIT_VIEW))*/

		/* Change paned position if the view is not in 'split mode' */
		if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
			gtk_paned_set_position(GTK_PANED(hpaned), 0);
		}

		if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
		    || (view_mode == FULL_VIEW)
		    || (view_mode == SPLIT_VIEW)) {
			indent++;
			display_tree(child);
			indent--;
		}
	}
}

/* Display a part of the tree starting at current node (single/split view) */
static void display_tree_part(void)
{
	if (tree2)
		gtk_tree_store_clear(tree2);
	if (view_mode == SINGLE_VIEW)
		display_tree(current);
	else if (view_mode == SPLIT_VIEW)
		display_tree(browsed);
	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
}

/* Display the list in the left frame (split view) */
static void display_list(void)
{
	if (tree1)
		gtk_tree_store_clear(tree1);

	tree = tree1;
	display_tree(&rootmenu);
	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
	tree = tree2;
}

void fixup_rootmenu(struct menu *menu)
{
	struct menu *child;
	static int menu_cnt = 0;

	menu->flags |= MENU_ROOT;
	for (child = menu->list; child; child = child->next) {
		if (child->prompt && child->prompt->type == P_MENU) {
			menu_cnt++;
			fixup_rootmenu(child);
			menu_cnt--;
		} else if (!menu_cnt)
			fixup_rootmenu(child);
	}
}

/* Main */
int main(int ac, char *av[])
{
	const char *name;
	char *env;
	gchar *glade_file;

	bindtextdomain(PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(PACKAGE, "UTF-8");
	textdomain(PACKAGE);

	/* GTK stuffs */
	gtk_set_locale();
	gtk_init(&ac, &av);
	glade_init();

	//add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
	//add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");

	/* Determine GUI path */
	env = getenv(SRCTREE);
	if (env)
		glade_file = g_strconcat(env, "/tools/kconfig/gconf.glade", NULL);
	else if (av[0][0] == '/')
		glade_file = g_strconcat(av[0], ".glade", NULL);
	else
		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);

	/* Conf stuffs */
	if (ac > 1 && av[1][0] == '-') {
		switch (av[1][1]) {
		case 'a':
			//showAll = 1;
			break;
		case 's':
			conf_set_message_callback(NULL);
			break;
		case 'h':
		case '?':
			printf("%s [-s] <config>\n", av[0]);
			exit(0);
		}
		name = av[2];
	} else
		name = av[1];

	conf_parse(name);
	fixup_rootmenu(&rootmenu);
	conf_read(NULL);

	/* Load the interface and connect signals */
	init_main_window(glade_file);
	init_tree_model();
	init_left_tree();
	init_right_tree();

	switch (view_mode) {
	case SINGLE_VIEW:
		display_tree_part();
		break;
	case SPLIT_VIEW:
		display_list();
		break;
	case FULL_VIEW:
		display_tree(&rootmenu);
		break;
	}

	gtk_main();

	return 0;
}

static void conf_changed(void)
{
	bool changed = conf_get_changed();
	gtk_widget_set_sensitive(save_btn, changed);
	gtk_widget_set_sensitive(save_menu_item, changed);
}


code:
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->

<glade-interface>

<widget class="GtkWindow" id="window1">
  <property name="visible">True</property>
  <property name="title" translatable="yes">Gtk Kernel Configurator</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="default_width">640</property>
  <property name="default_height">480</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="decorated">True</property>
  <property name="skip_taskbar_hint">False</property>
  <property name="skip_pager_hint">False</property>
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
  <signal name="destroy" handler="on_window1_destroy" object="window1"/>
  <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/>
  <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/>

  <child>
    <widget class="GtkVBox" id="vbox1">
      <property name="visible">True</property>
      <property name="homogeneous">False</property>
      <property name="spacing">0</property>

      <child>
	<widget class="GtkMenuBar" id="menubar1">
	  <property name="visible">True</property>

	  <child>
	    <widget class="GtkMenuItem" id="file1">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">_File</property>
	      <property name="use_underline">True</property>

	      <child>
		<widget class="GtkMenu" id="file1_menu">

		  <child>
		    <widget class="GtkImageMenuItem" id="load1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Load a config file</property>
		      <property name="label" translatable="yes">_Load</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_load1_activate"/>
		      <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image39">
			  <property name="visible">True</property>
			  <property name="stock">gtk-open</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="save1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Save the config in .config</property>
		      <property name="label" translatable="yes">_Save</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_save_activate"/>
		      <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image40">
			  <property name="visible">True</property>
			  <property name="stock">gtk-save</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="save_as1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Save the config in a file</property>
		      <property name="label" translatable="yes">Save _as</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_save_as1_activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image41">
			  <property name="visible">True</property>
			  <property name="stock">gtk-save-as</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkSeparatorMenuItem" id="separator1">
		      <property name="visible">True</property>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="quit1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_Quit</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_quit1_activate"/>
		      <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image42">
			  <property name="visible">True</property>
			  <property name="stock">gtk-quit</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="options1">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">_Options</property>
	      <property name="use_underline">True</property>

	      <child>
		<widget class="GtkMenu" id="options1_menu">

		  <child>
		    <widget class="GtkCheckMenuItem" id="show_name1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show name</property>
		      <property name="label" translatable="yes">Show _name</property>
		      <property name="use_underline">True</property>
		      <property name="active">False</property>
		      <signal name="activate" handler="on_show_name1_activate"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkCheckMenuItem" id="show_range1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show range (Y/M/N)</property>
		      <property name="label" translatable="yes">Show _range</property>
		      <property name="use_underline">True</property>
		      <property name="active">False</property>
		      <signal name="activate" handler="on_show_range1_activate"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkCheckMenuItem" id="show_data1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show value of the option</property>
		      <property name="label" translatable="yes">Show _data</property>
		      <property name="use_underline">True</property>
		      <property name="active">False</property>
		      <signal name="activate" handler="on_show_data1_activate"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkSeparatorMenuItem" id="separator2">
		      <property name="visible">True</property>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkRadioMenuItem" id="set_option_mode1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show normal options</property>
		      <property name="label" translatable="yes">Show normal options</property>
		      <property name="use_underline">True</property>
		      <property name="active">True</property>
		      <signal name="activate" handler="on_set_option_mode1_activate"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkRadioMenuItem" id="set_option_mode2">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show all options</property>
		      <property name="label" translatable="yes">Show all _options</property>
		      <property name="use_underline">True</property>
		      <property name="active">False</property>
		      <property name="group">set_option_mode1</property>
		      <signal name="activate" handler="on_set_option_mode2_activate"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkRadioMenuItem" id="set_option_mode3">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Show all options with prompts</property>
		      <property name="label" translatable="yes">Show all prompt options</property>
		      <property name="use_underline">True</property>
		      <property name="active">False</property>
		      <property name="group">set_option_mode1</property>
		      <signal name="activate" handler="on_set_option_mode3_activate"/>
		    </widget>
		  </child>

		</widget>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="help1">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">_Help</property>
	      <property name="use_underline">True</property>

	      <child>
		<widget class="GtkMenu" id="help1_menu">

		  <child>
		    <widget class="GtkImageMenuItem" id="introduction1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_Introduction</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
		      <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image43">
			  <property name="visible">True</property>
			  <property name="stock">gtk-dialog-question</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="about1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_About</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
		      <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image44">
			  <property name="visible">True</property>
			  <property name="stock">gtk-properties</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="license1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_License</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>

		      <child internal-child="image">
			<widget class="GtkImage" id="image45">
			  <property name="visible">True</property>
			  <property name="stock">gtk-justify-fill</property>
			  <property name="icon_size">1</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
		      </child>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">False</property>
	</packing>
      </child>

      <child>
	<widget class="GtkHandleBox" id="handlebox1">
	  <property name="visible">True</property>
	  <property name="shadow_type">GTK_SHADOW_OUT</property>
	  <property name="handle_position">GTK_POS_LEFT</property>
	  <property name="snap_edge">GTK_POS_TOP</property>

	  <child>
	    <widget class="GtkToolbar" id="toolbar1">
	      <property name="visible">True</property>
	      <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
	      <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
	      <property name="tooltips">True</property>
	      <property name="show_arrow">True</property>

	      <child>
		<widget class="GtkToolButton" id="button1">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Goes up of one level (single view)</property>
		  <property name="label" translatable="yes">Back</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-undo</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_back_clicked"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolItem" id="toolitem1">
		  <property name="visible">True</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>

		  <child>
		    <widget class="GtkVSeparator" id="vseparator1">
		      <property name="visible">True</property>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button2">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Load a config file</property>
		  <property name="label" translatable="yes">Load</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-open</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_load_clicked"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button3">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Save a config file</property>
		  <property name="label" translatable="yes">Save</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-save</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_save_activate"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolItem" id="toolitem2">
		  <property name="visible">True</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>

		  <child>
		    <widget class="GtkVSeparator" id="vseparator2">
		      <property name="visible">True</property>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button4">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Single view</property>
		  <property name="label" translatable="yes">Single</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-missing-image</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button5">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Split view</property>
		  <property name="label" translatable="yes">Split</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-missing-image</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button6">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Full view</property>
		  <property name="label" translatable="yes">Full</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-missing-image</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolItem" id="toolitem3">
		  <property name="visible">True</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>

		  <child>
		    <widget class="GtkVSeparator" id="vseparator3">
		      <property name="visible">True</property>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button7">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property>
		  <property name="label" translatable="yes">Collapse</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-remove</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_collapse_clicked"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkToolButton" id="button8">
		  <property name="visible">True</property>
		  <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property>
		  <property name="label" translatable="yes">Expand</property>
		  <property name="use_underline">True</property>
		  <property name="stock_id">gtk-add</property>
		  <property name="visible_horizontal">True</property>
		  <property name="visible_vertical">True</property>
		  <property name="is_important">False</property>
		  <signal name="clicked" handler="on_expand_clicked"/>
		</widget>
		<packing>
		  <property name="expand">False</property>
		  <property name="homogeneous">True</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">False</property>
	</packing>
      </child>

      <child>
	<widget class="GtkHPaned" id="hpaned1">
	  <property name="width_request">1</property>
	  <property name="visible">True</property>
	  <property name="can_focus">True</property>
	  <property name="position">0</property>

	  <child>
	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
	      <property name="visible">True</property>
	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
	      <property name="shadow_type">GTK_SHADOW_IN</property>
	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>

	      <child>
		<widget class="GtkTreeView" id="treeview1">
		  <property name="visible">True</property>
		  <property name="can_focus">True</property>
		  <property name="headers_visible">True</property>
		  <property name="rules_hint">False</property>
		  <property name="reorderable">False</property>
		  <property name="enable_search">False</property>
		  <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/>
		  <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/>
		  <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/>
		</widget>
	      </child>
	    </widget>
	    <packing>
	      <property name="shrink">True</property>
	      <property name="resize">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkVPaned" id="vpaned1">
	      <property name="visible">True</property>
	      <property name="can_focus">True</property>
	      <property name="position">0</property>

	      <child>
		<widget class="GtkScrolledWindow" id="scrolledwindow2">
		  <property name="visible">True</property>
		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		  <property name="shadow_type">GTK_SHADOW_IN</property>
		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>

		  <child>
		    <widget class="GtkTreeView" id="treeview2">
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="has_focus">True</property>
		      <property name="headers_visible">True</property>
		      <property name="rules_hint">False</property>
		      <property name="reorderable">False</property>
		      <property name="enable_search">False</property>
		      <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/>
		      <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/>
		      <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="shrink">True</property>
		  <property name="resize">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkScrolledWindow" id="scrolledwindow3">
		  <property name="visible">True</property>
		  <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		  <property name="shadow_type">GTK_SHADOW_IN</property>
		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>

		  <child>
		    <widget class="GtkTextView" id="textview3">
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="editable">False</property>
		      <property name="overwrite">False</property>
		      <property name="accepts_tab">True</property>
		      <property name="justification">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap_mode">GTK_WRAP_WORD</property>
		      <property name="cursor_visible">True</property>
		      <property name="pixels_above_lines">0</property>
		      <property name="pixels_below_lines">0</property>
		      <property name="pixels_inside_wrap">0</property>
		      <property name="left_margin">0</property>
		      <property name="right_margin">0</property>
		      <property name="indent">0</property>
		      <property name="text" translatable="yes">Sorry, no help available for this option yet.</property>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="shrink">True</property>
		  <property name="resize">True</property>
		</packing>
	      </child>
	    </widget>
	    <packing>
	      <property name="shrink">True</property>
	      <property name="resize">True</property>
	    </packing>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">True</property>
	  <property name="fill">True</property>
	</packing>
      </child>
    </widget>
  </child>
</widget>

</glade-interface>

license: mit


code:
/*
 * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2005
 *
 * Released under the terms of the GNU GPL v2.0
 */

#include <stdlib.h>
#include <string.h>

#include "lkc.h"

static char *escape(const char* text, char *bf, int len)
{
	char *bfp = bf;
	int multiline = strchr(text, '\n') != NULL;
	int eol = 0;
	int textlen = strlen(text);

	if ((textlen > 0) && (text[textlen-1] == '\n'))
		eol = 1;

	*bfp++ = '"';
	--len;

	if (multiline) {
		*bfp++ = '"';
		*bfp++ = '\n';
		*bfp++ = '"';
		len -= 3;
	}

	while (*text != '\0' && len > 1) {
		if (*text == '"')
			*bfp++ = '\\';
		else if (*text == '\n') {
			*bfp++ = '\\';
			*bfp++ = 'n';
			*bfp++ = '"';
			*bfp++ = '\n';
			*bfp++ = '"';
			len -= 5;
			++text;
			goto next;
		}
		else if (*text == '\\') {
			*bfp++ = '\\';
			len--;
		}
		*bfp++ = *text++;
next:
		--len;
	}

	if (multiline && eol)
		bfp -= 3;

	*bfp++ = '"';
	*bfp = '\0';

	return bf;
}

struct file_line {
	struct file_line *next;
	const char *file;
	int lineno;
};

static struct file_line *file_line__new(const char *file, int lineno)
{
	struct file_line *self = malloc(sizeof(*self));

	if (self == NULL)
		goto out;

	self->file   = file;
	self->lineno = lineno;
	self->next   = NULL;
out:
	return self;
}

struct message {
	const char	 *msg;
	const char	 *option;
	struct message	 *next;
	struct file_line *files;
};

static struct message *message__list;

static struct message *message__new(const char *msg, char *option,
				    const char *file, int lineno)
{
	struct message *self = malloc(sizeof(*self));

	if (self == NULL)
		goto out;

	self->files = file_line__new(file, lineno);
	if (self->files == NULL)
		goto out_fail;

	self->msg = strdup(msg);
	if (self->msg == NULL)
		goto out_fail_msg;

	self->option = option;
	self->next = NULL;
out:
	return self;
out_fail_msg:
	free(self->files);
out_fail:
	free(self);
	self = NULL;
	goto out;
}

static struct message *mesage__find(const char *msg)
{
	struct message *m = message__list;

	while (m != NULL) {
		if (strcmp(m->msg, msg) == 0)
			break;
		m = m->next;
	}

	return m;
}

static int message__add_file_line(struct message *self, const char *file,
				  int lineno)
{
	int rc = -1;
	struct file_line *fl = file_line__new(file, lineno);

	if (fl == NULL)
		goto out;

	fl->next    = self->files;
	self->files = fl;
	rc = 0;
out:
	return rc;
}

static int message__add(const char *msg, char *option, const char *file,
			int lineno)
{
	int rc = 0;
	char bf[16384];
	char *escaped = escape(msg, bf, sizeof(bf));
	struct message *m = mesage__find(escaped);

	if (m != NULL)
		rc = message__add_file_line(m, file, lineno);
	else {
		m = message__new(escaped, option, file, lineno);

		if (m != NULL) {
			m->next	      = message__list;
			message__list = m;
		} else
			rc = -1;
	}
	return rc;
}

static void menu_build_message_list(struct menu *menu)
{
	struct menu *child;

	message__add(menu_get_prompt(menu), NULL,
		     menu->file == NULL ? "Root Menu" : menu->file->name,
		     menu->lineno);

	if (menu->sym != NULL && menu_has_help(menu))
		message__add(menu_get_help(menu), menu->sym->name,
			     menu->file == NULL ? "Root Menu" : menu->file->name,
			     menu->lineno);

	for (child = menu->list; child != NULL; child = child->next)
		if (child->prompt != NULL)
			menu_build_message_list(child);
}

static void message__print_file_lineno(struct message *self)
{
	struct file_line *fl = self->files;

	putchar('\n');
	if (self->option != NULL)
		printf("# %s:00000\n", self->option);

	printf("#: %s:%d", fl->file, fl->lineno);
	fl = fl->next;

	while (fl != NULL) {
		printf(", %s:%d", fl->file, fl->lineno);
		fl = fl->next;
	}

	putchar('\n');
}

static void message__print_gettext_msgid_msgstr(struct message *self)
{
	message__print_file_lineno(self);

	printf("msgid %s\n"
	       "msgstr \"\"\n", self->msg);
}

static void menu__xgettext(void)
{
	struct message *m = message__list;

	while (m != NULL) {
		/* skip empty lines ("") */
		if (strlen(m->msg) > sizeof("\"\""))
			message__print_gettext_msgid_msgstr(m);
		m = m->next;
	}
}

int main(int ac, char **av)
{
	conf_parse(av[1]);

	menu_build_message_list(menu_get_root_menu(NULL));
	menu__xgettext();
	return 0;
}


code:
#ifndef LIST_H
#define LIST_H

/*
 * Copied from include/linux/...
 */

#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:        the pointer to the member.
 * @type:       the type of the container struct this is embedded in.
 * @member:     the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({                      \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = list_entry((head)->next, typeof(*pos), member),	\
		n = list_entry(pos->member.next, typeof(*pos), member);	\
	     &pos->member != (head);					\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
	return head->next == head;
}

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *_new,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = _new;
	_new->next = next;
	_new->prev = prev;
	prev->next = _new;
}

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *_new, struct list_head *head)
{
	__list_add(_new, head->prev, head);
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
	next->prev = prev;
	prev->next = next;
}

#define LIST_POISON1  ((void *) 0x00100100)
#define LIST_POISON2  ((void *) 0x00200200)
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = (struct list_head*)LIST_POISON1;
	entry->prev = (struct list_head*)LIST_POISON2;
}
#endif


code:
/*
 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
 * Released under the terms of the GNU GPL v2.0.
 */

#ifndef LKC_H
#define LKC_H

#include "expr.h"

#ifndef KBUILD_NO_NLS
# include <libintl.h>
#else
static inline const char *gettext(const char *txt) { return txt; }
static inline void textdomain(const char *domainname) {}
static inline void bindtextdomain(const char *name, const char *dir) {}
static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; }
#endif

#ifdef __cplusplus
extern "C" {
#endif

#include "lkc_proto.h"

#define SRCTREE "srctree"

#ifndef PACKAGE
#define PACKAGE "linux"
#endif

#define LOCALEDIR "/usr/share/locale"

#define _(text) gettext(text)
#define N_(text) (text)

#ifndef CONFIG_
#define CONFIG_ "CONFIG_"
#endif
static inline const char *CONFIG_prefix(void)
{
	return getenv( "CONFIG_" ) ?: CONFIG_;
}
#undef CONFIG_
#define CONFIG_ CONFIG_prefix()

#define TF_COMMAND	0x0001
#define TF_PARAM	0x0002
#define TF_OPTION	0x0004

enum conf_def_mode {
	def_default,
	def_yes,
	def_mod,
	def_no,
	def_random
};

#define T_OPT_MODULES		1
#define T_OPT_DEFCONFIG_LIST	2
#define T_OPT_ENV		3
#define T_OPT_ALLNOCONFIG_Y	4

struct kconf_id {
	int name;
	int token;
	unsigned int flags;
	enum symbol_type stype;
};

void zconfdump(FILE *out);
void zconf_starthelp(void);
FILE *zconf_fopen(const char *name);
void zconf_initscan(const char *name);
void zconf_nextfile(const char *name);
int zconf_lineno(void);
const char *zconf_curname(void);

/* confdata.c */
const char *conf_get_configname(void);
const char *conf_get_autoconfig_name(void);
char *conf_get_default_confname(void);
void sym_set_change_count(int count);
void sym_add_change_count(int count);
bool conf_set_all_new_symbols(enum conf_def_mode mode);
void set_all_choice_values(struct symbol *csym);

/* confdata.c and expr.c */
static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
{
	assert(len != 0);

	if (fwrite(str, len, count, out) != count)
		fprintf(stderr, "Error in writing or end of file.\n");
}

/* menu.c */
void _menu_init(void);
void menu_warn(struct menu *menu, const char *fmt, ...);
struct menu *menu_add_menu(void);
void menu_end_menu(void);
void menu_add_entry(struct symbol *sym);
void menu_end_entry(void);
void menu_add_dep(struct expr *dep);
void menu_add_visibility(struct expr *dep);
struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
void menu_add_option(int token, char *arg);
void menu_finalize(struct menu *parent);
void menu_set_type(int type);

/* util.c */
struct file *file_lookup(const char *name);
int file_write_dep(const char *name);
void *xmalloc(size_t size);
void *xcalloc(size_t nmemb, size_t size);

struct gstr {
	size_t len;
	char  *s;
	/*
	* when max_width is not zero long lines in string s (if any) get
	* wrapped not to exceed the max_width value
	*/
	int max_width;
};
struct gstr str_new(void);
void str_free(struct gstr *gs);
void str_append(struct gstr *gs, const char *s);
void str_printf(struct gstr *gs, const char *fmt, ...);
const char *str_get(struct gstr *gs);

/* symbol.c */
extern struct expr *sym_env_list;

void sym_init(void);
void sym_clear_all_valid(void);
struct symbol *sym_choice_default(struct symbol *sym);
const char *sym_get_string_default(struct symbol *sym);
struct symbol *sym_check_deps(struct symbol *sym);
struct property *prop_alloc(enum prop_type type, struct symbol *sym);
struct symbol *prop_get_symbol(struct property *prop);
struct property *sym_get_env_prop(struct symbol *sym);

static inline tristate sym_get_tristate_value(struct symbol *sym)
{
	return sym->curr.tri;
}

static inline struct symbol *sym_get_choice_value(struct symbol *sym)
{
	return (struct symbol *)sym->curr.val;
}

static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
{
	return sym_set_tristate_value(chval, yes);
}

static inline bool sym_is_choice(struct symbol *sym)
{
	return sym->flags & SYMBOL_CHOICE ? true : false;
}

static inline bool sym_is_choice_value(struct symbol *sym)
{
	return sym->flags & SYMBOL_CHOICEVAL ? true : false;
}

static inline bool sym_is_optional(struct symbol *sym)
{
	return sym->flags & SYMBOL_OPTIONAL ? true : false;
}

static inline bool sym_has_value(struct symbol *sym)
{
	return sym->flags & SYMBOL_DEF_USER ? true : false;
}

#ifdef __cplusplus
}
#endif

#endif /* LKC_H */


code:
#include <stdarg.h>

/* confdata.c */
void conf_parse(const char *name);
int conf_read(const char *name);
int conf_read_simple(const char *name, int);
int conf_write_defconfig(const char *name);
int conf_write(const char *name);
int conf_write_autoconf(void);
bool conf_get_changed(void);
void conf_set_changed_callback(void (*fn)(void));
void conf_set_message_callback(void (*fn)(const char *fmt, va_list ap));

/* menu.c */
extern struct menu rootmenu;

bool menu_is_empty(struct menu *menu);
bool menu_is_visible(struct menu *menu);
bool menu_has_prompt(struct menu *menu);
const char * menu_get_prompt(struct menu *menu);
struct menu * menu_get_root_menu(struct menu *menu);
struct menu * menu_get_parent_menu(struct menu *menu);
bool menu_has_help(struct menu *menu);
const char * menu_get_help(struct menu *menu);
struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
void menu_get_ext_help(struct menu *menu, struct gstr *help);

/* symbol.c */
extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];

struct symbol * sym_lookup(const char *name, int flags);
struct symbol * sym_find(const char *name);
const char * sym_expand_string_value(const char *in);
const char * sym_escape_string_value(const char *in);
struct symbol ** sym_re_search(const char *pattern);
const char * sym_type_name(enum symbol_type type);
void sym_calc_value(struct symbol *sym);
enum symbol_type sym_get_type(struct symbol *sym);
bool sym_tristate_within_range(struct symbol *sym,tristate tri);
bool sym_set_tristate_value(struct symbol *sym,tristate tri);
tristate sym_toggle_tristate_value(struct symbol *sym);
bool sym_string_valid(struct symbol *sym, const char *newval);
bool sym_string_within_range(struct symbol *sym, const char *str);
bool sym_set_string_value(struct symbol *sym, const char *newval);
bool sym_is_changable(struct symbol *sym);
struct property * sym_get_choice_prop(struct symbol *sym);
const char * sym_get_string_value(struct symbol *sym);

const char * prop_get_type_name(enum prop_type type);

/* expr.c */
void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken);


code:
This is NOT the official version of dialog.  This version has been
significantly modified from the original.  It is for use by the Linux
kernel configuration script.  Please do not bother Savio Lam with
questions about this program.


code:
#!/bin/sh
# Check ncurses compatibility

# What library to link
ldflags()
{
	pkg-config --libs ncursesw 2>/dev/null && exit
	pkg-config --libs ncurses 2>/dev/null && exit
	for ext in so a dll.a dylib ; do
		for lib in ncursesw ncurses curses ; do
			$cc -print-file-name=lib${lib}.${ext} | grep -q /
			if [ $? -eq 0 ]; then
				echo "-l${lib}"
				exit
			fi
		done
	done
	exit 1
}

# Where is ncurses.h?
ccflags()
{
	if pkg-config --cflags ncursesw 2>/dev/null; then
		echo '-DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1'
	elif pkg-config --cflags ncurses 2>/dev/null; then
		echo '-DCURSES_LOC="<ncurses.h>"'
	elif [ -f /usr/include/ncursesw/curses.h ]; then
		echo '-I/usr/include/ncursesw -DCURSES_LOC="<curses.h>"'
		echo ' -DNCURSES_WIDECHAR=1'
	elif [ -f /usr/include/ncurses/ncurses.h ]; then
		echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
	elif [ -f /usr/include/ncurses/curses.h ]; then
		echo '-I/usr/include/ncurses -DCURSES_LOC="<curses.h>"'
	elif [ -f /usr/include/ncurses.h ]; then
		echo '-DCURSES_LOC="<ncurses.h>"'
	else
		echo '-DCURSES_LOC="<curses.h>"'
	fi
}

# Temp file, try to clean up after us
tmp=.lxdialog.tmp
trap "rm -f $tmp" 0 1 2 3 15

# Check if we can link to ncurses
check() {
        $cc -x c - -o $tmp 2>/dev/null <<'EOF'
#include CURSES_LOC
main() {}
EOF
	if [ $? != 0 ]; then
	    echo " *** Unable to find the ncurses libraries or the"       1>&2
	    echo " *** required header files."                            1>&2
	    echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2
	    echo " *** "                                                  1>&2
	    echo " *** Install ncurses (ncurses-devel) and try again."    1>&2
	    echo " *** "                                                  1>&2
	    exit 1
	fi
}

usage() {
	printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n"
}

if [ $# -eq 0 ]; then
	usage
	exit 1
fi

cc=""
case "$1" in
	"-check")
		shift
		cc="$@"
		check
		;;
	"-ccflags")
		ccflags
		;;
	"-ldflags")
		shift
		cc="$@"
		ldflags
		;;
	"*")
		usage
		exit 1
		;;
esac


code:
/*
 *  checklist.c -- implements the checklist box
 *
 *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
 *     Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
 *     Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
 *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "dialog.h"

static int list_width, check_x, item_x;

/*
 * Print list item
 */
static void print_item(WINDOW * win, int choice, int selected)
{
	int i;
	char *list_item = malloc(list_width + 1);

	strncpy(list_item, item_str(), list_width - item_x);
	list_item[list_width - item_x] = '\0';

	/* Clear 'residue' of last item */
	wattrset(win, dlg.menubox.atr);
	wmove(win, choice, 0);
	for (i = 0; i < list_width; i++)
		waddch(win, ' ');

	wmove(win, choice, check_x);
	wattrset(win, selected ? dlg.check_selected.atr
		 : dlg.check.atr);
	if (!item_is_tag(':'))
		wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' ');

	wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr);
	mvwaddch(win, choice, item_x, list_item[0]);
	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
	waddstr(win, list_item + 1);
	if (selected) {
		wmove(win, choice, check_x + 1);
		wrefresh(win);
	}
	free(list_item);
}

/*
 * Print the scroll indicators.
 */
static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
	     int y, int x, int height)
{
	wmove(win, y, x);

	if (scroll > 0) {
		wattrset(win, dlg.uarrow.atr);
		waddch(win, ACS_UARROW);
		waddstr(win, "(-)");
	} else {
		wattrset(win, dlg.menubox.atr);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
	}

	y = y + height + 1;
	wmove(win, y, x);

	if ((height < item_no) && (scroll + choice < item_no - 1)) {
		wattrset(win, dlg.darrow.atr);
		waddch(win, ACS_DARROW);
		waddstr(win, "(+)");
	} else {
		wattrset(win, dlg.menubox_border.atr);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
	}
}

/*
 *  Display the termination buttons
 */
static void print_buttons(WINDOW * dialog, int height, int width, int selected)
{
	int x = width / 2 - 11;
	int y = height - 2;

	print_button(dialog, gettext("Select"), y, x, selected == 0);
	print_button(dialog, gettext(" Help "), y, x + 14, selected == 1);

	wmove(dialog, y, x + 1 + 14 * selected);
	wrefresh(dialog);
}

/*
 * Display a dialog box with a list of options that can be turned on or off
 * in the style of radiolist (only one option turned on at a time).
 */
int dialog_checklist(const char *title, const char *prompt, int height,
		     int width, int list_height)
{
	int i, x, y, box_x, box_y;
	int key = 0, button = 0, choice = 0, scroll = 0, max_choice;
	WINDOW *dialog, *list;

	/* which item to highlight */
	item_foreach() {
		if (item_is_tag('X'))
			choice = item_n();
		if (item_is_selected()) {
			choice = item_n();
			break;
		}
	}

do_resize:
	if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN))
		return -ERRDISPLAYTOOSMALL;
	if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN))
		return -ERRDISPLAYTOOSMALL;

	max_choice = MIN(list_height, item_count());

	/* center dialog box on screen */
	x = (getmaxx(stdscr) - width) / 2;
	y = (getmaxy(stdscr) - height) / 2;

	draw_shadow(stdscr, y, x, height, width);

	dialog = newwin(height, width, y, x);
	keypad(dialog, TRUE);

	draw_box(dialog, 0, 0, height, width,
		 dlg.dialog.atr, dlg.border.atr);
	wattrset(dialog, dlg.border.atr);
	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
	for (i = 0; i < width - 2; i++)
		waddch(dialog, ACS_HLINE);
	wattrset(dialog, dlg.dialog.atr);
	waddch(dialog, ACS_RTEE);

	print_title(dialog, title, width);

	wattrset(dialog, dlg.dialog.atr);
	print_autowrap(dialog, prompt, width - 2, 1, 3);

	list_width = width - 6;
	box_y = height - list_height - 5;
	box_x = (width - list_width) / 2 - 1;

	/* create new window for the list */
	list = subwin(dialog, list_height, list_width, y + box_y + 1,
		      x + box_x + 1);

	keypad(list, TRUE);

	/* draw a box around the list items */
	draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
		 dlg.menubox_border.atr, dlg.menubox.atr);

	/* Find length of longest item in order to center checklist */
	check_x = 0;
	item_foreach()
		check_x = MAX(check_x, strlen(item_str()) + 4);
	check_x = MIN(check_x, list_width);

	check_x = (list_width - check_x) / 2;
	item_x = check_x + 4;

	if (choice >= list_height) {
		scroll = choice - list_height + 1;
		choice -= scroll;
	}

	/* Print the list */
	for (i = 0; i < max_choice; i++) {
		item_set(scroll + i);
		print_item(list, i, i == choice);
	}

	print_arrows(dialog, choice, item_count(), scroll,
		     box_y, box_x + check_x + 5, list_height);

	print_buttons(dialog, height, width, 0);

	wnoutrefresh(dialog);
	wnoutrefresh(list);
	doupdate();

	while (key != KEY_ESC) {
		key = wgetch(dialog);

		for (i = 0; i < max_choice; i++) {
			item_set(i + scroll);
			if (toupper(key) == toupper(item_str()[0]))
				break;
		}

		if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
		    key == '+' || key == '-') {
			if (key == KEY_UP || key == '-') {
				if (!choice) {
					if (!scroll)
						continue;
					/* Scroll list down */
					if (list_height > 1) {
						/* De-highlight current first item */
						item_set(scroll);
						print_item(list, 0, FALSE);
						scrollok(list, TRUE);
						wscrl(list, -1);
						scrollok(list, FALSE);
					}
					scroll--;
					item_set(scroll);
					print_item(list, 0, TRUE);
					print_arrows(dialog, choice, item_count(),
						     scroll, box_y, box_x + check_x + 5, list_height);

					wnoutrefresh(dialog);
					wrefresh(list);

					continue;	/* wait for another key press */
				} else
					i = choice - 1;
			} else if (key == KEY_DOWN || key == '+') {
				if (choice == max_choice - 1) {
					if (scroll + choice >= item_count() - 1)
						continue;
					/* Scroll list up */
					if (list_height > 1) {
						/* De-highlight current last item before scrolling up */
						item_set(scroll + max_choice - 1);
						print_item(list,
							    max_choice - 1,
							    FALSE);
						scrollok(list, TRUE);
						wscrl(list, 1);
						scrollok(list, FALSE);
					}
					scroll++;
					item_set(scroll + max_choice - 1);
					print_item(list, max_choice - 1, TRUE);

					print_arrows(dialog, choice, item_count(),
						     scroll, box_y, box_x + check_x + 5, list_height);

					wnoutrefresh(dialog);
					wrefresh(list);

					continue;	/* wait for another key press */
				} else
					i = choice + 1;
			}
			if (i != choice) {
				/* De-highlight current item */
				item_set(scroll + choice);
				print_item(list, choice, FALSE);
				/* Highlight new item */
				choice = i;
				item_set(scroll + choice);
				print_item(list, choice, TRUE);
				wnoutrefresh(dialog);
				wrefresh(list);
			}
			continue;	/* wait for another key press */
		}
		switch (key) {
		case 'H':
		case 'h':
		case '?':
			button = 1;
			/* fall-through */
		case 'S':
		case 's':
		case ' ':
		case '\n':
			item_foreach()
				item_set_selected(0);
			item_set(scroll + choice);
			item_set_selected(1);
			delwin(list);
			delwin(dialog);
			return button;
		case TAB:
		case KEY_LEFT:
		case KEY_RIGHT:
			button = ((key == KEY_LEFT ? --button : ++button) < 0)
			    ? 1 : (button > 1 ? 0 : button);

			print_buttons(dialog, height, width, button);
			wrefresh(dialog);
			break;
		case 'X':
		case 'x':
			key = KEY_ESC;
			break;
		case KEY_ESC:
			key = on_key_esc(dialog);
			break;
		case KEY_RESIZE:
			delwin(list);
			delwin(dialog);
			on_key_resize();
			goto do_resize;
		}

		/* Now, update everything... */
		doupdate();
	}
	delwin(list);
	delwin(dialog);
	return key;		/* ESC pressed */
}


code:
/*
 *  dialog.h -- common declarations for all dialog modules
 *
 *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#ifndef KBUILD_NO_NLS
# include <libintl.h>
#else
# define gettext(Msgid) ((const char *) (Msgid))
#endif

#ifdef __sun__
#define CURS_MACROS
#endif
#include CURSES_LOC

/*
 * Colors in ncurses 1.9.9e do not work properly since foreground and
 * background colors are OR'd rather than separately masked.  This version
 * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
 * with standard curses.  The simplest fix (to make this work with standard
 * curses) uses the wbkgdset() function, not used in the original hack.
 * Turn it off if we're building with 1.9.9e, since it just confuses things.
 */
#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
#define OLD_NCURSES 1
#undef  wbkgdset
#define wbkgdset(w,p)		/*nothing */
#else
#define OLD_NCURSES 0
#endif

#define TR(params) _tracef params

#define KEY_ESC 27
#define TAB 9
#define MAX_LEN 2048
#define BUF_SIZE (10*1024)
#define MIN(x,y) (x < y ? x : y)
#define MAX(x,y) (x > y ? x : y)

#ifndef ACS_ULCORNER
#define ACS_ULCORNER '+'
#endif
#ifndef ACS_LLCORNER
#define ACS_LLCORNER '+'
#endif
#ifndef ACS_URCORNER
#define ACS_URCORNER '+'
#endif
#ifndef ACS_LRCORNER
#define ACS_LRCORNER '+'
#endif
#ifndef ACS_HLINE
#define ACS_HLINE '-'
#endif
#ifndef ACS_VLINE
#define ACS_VLINE '|'
#endif
#ifndef ACS_LTEE
#define ACS_LTEE '+'
#endif
#ifndef ACS_RTEE
#define ACS_RTEE '+'
#endif
#ifndef ACS_UARROW
#define ACS_UARROW '^'
#endif
#ifndef ACS_DARROW
#define ACS_DARROW 'v'
#endif

/* error return codes */
#define ERRDISPLAYTOOSMALL (KEY_MAX + 1)

/*
 *   Color definitions
 */
struct dialog_color {
	chtype atr;	/* Color attribute */
	int fg;		/* foreground */
	int bg;		/* background */
	int hl;		/* highlight this item */
};

struct subtitle_list {
	struct subtitle_list *next;
	const char *text;
};

struct dialog_info {
	const char *backtitle;
	struct subtitle_list *subtitles;
	struct dialog_color screen;
	struct dialog_color shadow;
	struct dialog_color dialog;
	struct dialog_color title;
	struct dialog_color border;
	struct dialog_color button_active;
	struct dialog_color button_inactive;
	struct dialog_color button_key_active;
	struct dialog_color button_key_inactive;
	struct dialog_color button_label_active;
	struct dialog_color button_label_inactive;
	struct dialog_color inputbox;
	struct dialog_color inputbox_border;
	struct dialog_color searchbox;
	struct dialog_color searchbox_title;
	struct dialog_color searchbox_border;
	struct dialog_color position_indicator;
	struct dialog_color menubox;
	struct dialog_color menubox_border;
	struct dialog_color item;
	struct dialog_color item_selected;
	struct dialog_color tag;
	struct dialog_color tag_selected;
	struct dialog_color tag_key;
	struct dialog_color tag_key_selected;
	struct dialog_color check;
	struct dialog_color check_selected;
	struct dialog_color uarrow;
	struct dialog_color darrow;
};

/*
 * Global variables
 */
extern struct dialog_info dlg;
extern char dialog_input_result[];
extern int saved_x, saved_y;		/* Needed in signal handler in mconf.c */

/*
 * Function prototypes
 */

/* item list as used by checklist and menubox */
void item_reset(void);
void item_make(const char *fmt, ...);
void item_add_str(const char *fmt, ...);
void item_set_tag(char tag);
void item_set_data(void *p);
void item_set_selected(int val);
int item_activate_selected(void);
void *item_data(void);
char item_tag(void);

/* item list manipulation for lxdialog use */
#define MAXITEMSTR 200
struct dialog_item {
	char str[MAXITEMSTR];	/* prompt displayed */
	char tag;
	void *data;	/* pointer to menu item - used by menubox+checklist */
	int selected;	/* Set to 1 by dialog_*() function if selected. */
};

/* list of lialog_items */
struct dialog_list {
	struct dialog_item node;
	struct dialog_list *next;
};

extern struct dialog_list *item_cur;
extern struct dialog_list item_nil;
extern struct dialog_list *item_head;

int item_count(void);
void item_set(int n);
int item_n(void);
const char *item_str(void);
int item_is_selected(void);
int item_is_tag(char tag);
#define item_foreach() \
	for (item_cur = item_head ? item_head: item_cur; \
	     item_cur && (item_cur != &item_nil); item_cur = item_cur->next)

/* generic key handlers */
int on_key_esc(WINDOW *win);
int on_key_resize(void);

/* minimum (re)size values */
#define CHECKLIST_HEIGTH_MIN 6	/* For dialog_checklist() */
#define CHECKLIST_WIDTH_MIN 6
#define INPUTBOX_HEIGTH_MIN 2	/* For dialog_inputbox() */
#define INPUTBOX_WIDTH_MIN 2
#define MENUBOX_HEIGTH_MIN 15	/* For dialog_menu() */
#define MENUBOX_WIDTH_MIN 65
#define TEXTBOX_HEIGTH_MIN 8	/* For dialog_textbox() */
#define TEXTBOX_WIDTH_MIN 8
#define YESNO_HEIGTH_MIN 4	/* For dialog_yesno() */
#define YESNO_WIDTH_MIN 4
#define WINDOW_HEIGTH_MIN 19	/* For init_dialog() */
#define WINDOW_WIDTH_MIN 80

int init_dialog(const char *backtitle);
void set_dialog_backtitle(const char *backtitle);
void set_dialog_subtitles(struct subtitle_list *subtitles);
void end_dialog(int x, int y);
void attr_clear(WINDOW * win, int height, int width, chtype attr);
void dialog_clear(void);
void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
void print_button(WINDOW * win, const char *label, int y, int x, int selected);
void print_title(WINDOW *dialog, const char *title, int width);
void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
	      chtype border);
void draw_shadow(WINDOW * win, int y, int x, int height, int width);

int first_alpha(const char *string, const char *exempt);
int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
		  int width, int pause);

typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
			       *_data);
int dialog_textbox(const char *title, char *tbuf, int initial_height,
		   int initial_width, int *keys, int *_vscroll, int *_hscroll,
		   update_text_fn update_text, void *data);
int dialog_menu(const char *title, const char *prompt,
		const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
		     int width, int list_height);
int dialog_inputbox(const char *title, const char *prompt, int height,
		    int width, const char *init);

/*
 * This is the base for fictitious keys, which activate
 * the buttons.
 *
 * Mouse-generated keys are the following:
 *   -- the first 32 are used as numbers, in addition to '0'-'9'
 *   -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
 *   -- uppercase chars are used to invoke the button (M_EVENT + 'O')
 */
#define M_EVENT (KEY_MAX+1)