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