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