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