Linux Audio

Check our new training course

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