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