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