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