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