Merge tag 'kbuild-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[linux-2.6-microblaze.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 gkc, 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.\n"
651             "\n"
652             "Toggling Show Debug Info under the Options menu will show \n"
653             "the dependencies, which you can then match by examining other options.";
654
655         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
656                                         GTK_DIALOG_DESTROY_WITH_PARENT,
657                                         GTK_MESSAGE_INFO,
658                                         GTK_BUTTONS_CLOSE, "%s", intro_text);
659         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
660                                  G_CALLBACK(gtk_widget_destroy),
661                                  GTK_OBJECT(dialog));
662         gtk_widget_show_all(dialog);
663 }
664
665
666 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
667 {
668         GtkWidget *dialog;
669         const gchar *about_text =
670             "gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
671               "Based on the source code from Roman Zippel.\n";
672
673         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
674                                         GTK_DIALOG_DESTROY_WITH_PARENT,
675                                         GTK_MESSAGE_INFO,
676                                         GTK_BUTTONS_CLOSE, "%s", about_text);
677         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
678                                  G_CALLBACK(gtk_widget_destroy),
679                                  GTK_OBJECT(dialog));
680         gtk_widget_show_all(dialog);
681 }
682
683
684 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
685 {
686         GtkWidget *dialog;
687         const gchar *license_text =
688             "gkc is released under the terms of the GNU GPL v2.\n"
689               "For more information, please see the source code or\n"
690               "visit http://www.fsf.org/licenses/licenses.html\n";
691
692         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
693                                         GTK_DIALOG_DESTROY_WITH_PARENT,
694                                         GTK_MESSAGE_INFO,
695                                         GTK_BUTTONS_CLOSE, "%s", license_text);
696         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
697                                  G_CALLBACK(gtk_widget_destroy),
698                                  GTK_OBJECT(dialog));
699         gtk_widget_show_all(dialog);
700 }
701
702
703 void on_back_clicked(GtkButton * button, gpointer user_data)
704 {
705         enum prop_type ptype;
706
707         current = current->parent;
708         ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
709         if (ptype != P_MENU)
710                 current = current->parent;
711         display_tree_part();
712
713         if (current == &rootmenu)
714                 gtk_widget_set_sensitive(back_btn, FALSE);
715 }
716
717
718 void on_load_clicked(GtkButton * button, gpointer user_data)
719 {
720         on_load1_activate(NULL, user_data);
721 }
722
723
724 void on_single_clicked(GtkButton * button, gpointer user_data)
725 {
726         view_mode = SINGLE_VIEW;
727         gtk_widget_hide(tree1_w);
728         current = &rootmenu;
729         display_tree_part();
730 }
731
732
733 void on_split_clicked(GtkButton * button, gpointer user_data)
734 {
735         gint w, h;
736         view_mode = SPLIT_VIEW;
737         gtk_widget_show(tree1_w);
738         gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
739         gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
740         if (tree2)
741                 gtk_tree_store_clear(tree2);
742         display_list();
743
744         /* Disable back btn, like in full mode. */
745         gtk_widget_set_sensitive(back_btn, FALSE);
746 }
747
748
749 void on_full_clicked(GtkButton * button, gpointer user_data)
750 {
751         view_mode = FULL_VIEW;
752         gtk_widget_hide(tree1_w);
753         if (tree2)
754                 gtk_tree_store_clear(tree2);
755         display_tree(&rootmenu);
756         gtk_widget_set_sensitive(back_btn, FALSE);
757 }
758
759
760 void on_collapse_clicked(GtkButton * button, gpointer user_data)
761 {
762         gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
763 }
764
765
766 void on_expand_clicked(GtkButton * button, gpointer user_data)
767 {
768         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
769 }
770
771
772 /* CTree Callbacks */
773
774 /* Change hex/int/string value in the cell */
775 static void renderer_edited(GtkCellRendererText * cell,
776                             const gchar * path_string,
777                             const gchar * new_text, gpointer user_data)
778 {
779         GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
780         GtkTreeIter iter;
781         const char *old_def, *new_def;
782         struct menu *menu;
783         struct symbol *sym;
784
785         if (!gtk_tree_model_get_iter(model2, &iter, path))
786                 return;
787
788         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
789         sym = menu->sym;
790
791         gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
792         new_def = new_text;
793
794         sym_set_string_value(sym, new_def);
795
796         update_tree(&rootmenu, NULL);
797
798         gtk_tree_path_free(path);
799 }
800
801 /* Change the value of a symbol and update the tree */
802 static void change_sym_value(struct menu *menu, gint col)
803 {
804         struct symbol *sym = menu->sym;
805         tristate newval;
806
807         if (!sym)
808                 return;
809
810         if (col == COL_NO)
811                 newval = no;
812         else if (col == COL_MOD)
813                 newval = mod;
814         else if (col == COL_YES)
815                 newval = yes;
816         else
817                 return;
818
819         switch (sym_get_type(sym)) {
820         case S_BOOLEAN:
821         case S_TRISTATE:
822                 if (!sym_tristate_within_range(sym, newval))
823                         newval = yes;
824                 sym_set_tristate_value(sym, newval);
825                 if (view_mode == FULL_VIEW)
826                         update_tree(&rootmenu, NULL);
827                 else if (view_mode == SPLIT_VIEW) {
828                         update_tree(browsed, NULL);
829                         display_list();
830                 }
831                 else if (view_mode == SINGLE_VIEW)
832                         display_tree_part();    //fixme: keep exp/coll
833                 break;
834         case S_INT:
835         case S_HEX:
836         case S_STRING:
837         default:
838                 break;
839         }
840 }
841
842 static void toggle_sym_value(struct menu *menu)
843 {
844         if (!menu->sym)
845                 return;
846
847         sym_toggle_tristate_value(menu->sym);
848         if (view_mode == FULL_VIEW)
849                 update_tree(&rootmenu, NULL);
850         else if (view_mode == SPLIT_VIEW) {
851                 update_tree(browsed, NULL);
852                 display_list();
853         }
854         else if (view_mode == SINGLE_VIEW)
855                 display_tree_part();    //fixme: keep exp/coll
856 }
857
858 static gint column2index(GtkTreeViewColumn * column)
859 {
860         gint i;
861
862         for (i = 0; i < COL_NUMBER; i++) {
863                 GtkTreeViewColumn *col;
864
865                 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
866                 if (col == column)
867                         return i;
868         }
869
870         return -1;
871 }
872
873
874 /* User click: update choice (full) or goes down (single) */
875 gboolean
876 on_treeview2_button_press_event(GtkWidget * widget,
877                                 GdkEventButton * event, gpointer user_data)
878 {
879         GtkTreeView *view = GTK_TREE_VIEW(widget);
880         GtkTreePath *path;
881         GtkTreeViewColumn *column;
882         GtkTreeIter iter;
883         struct menu *menu;
884         gint col;
885
886 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
887         gint tx = (gint) event->x;
888         gint ty = (gint) event->y;
889         gint cx, cy;
890
891         gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
892                                       &cy);
893 #else
894         gtk_tree_view_get_cursor(view, &path, &column);
895 #endif
896         if (path == NULL)
897                 return FALSE;
898
899         if (!gtk_tree_model_get_iter(model2, &iter, path))
900                 return FALSE;
901         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
902
903         col = column2index(column);
904         if (event->type == GDK_2BUTTON_PRESS) {
905                 enum prop_type ptype;
906                 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
907
908                 if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
909                         // goes down into menu
910                         current = menu;
911                         display_tree_part();
912                         gtk_widget_set_sensitive(back_btn, TRUE);
913                 } else if (col == COL_OPTION) {
914                         toggle_sym_value(menu);
915                         gtk_tree_view_expand_row(view, path, TRUE);
916                 }
917         } else {
918                 if (col == COL_VALUE) {
919                         toggle_sym_value(menu);
920                         gtk_tree_view_expand_row(view, path, TRUE);
921                 } else if (col == COL_NO || col == COL_MOD
922                            || col == COL_YES) {
923                         change_sym_value(menu, col);
924                         gtk_tree_view_expand_row(view, path, TRUE);
925                 }
926         }
927
928         return FALSE;
929 }
930
931 /* Key pressed: update choice */
932 gboolean
933 on_treeview2_key_press_event(GtkWidget * widget,
934                              GdkEventKey * event, gpointer user_data)
935 {
936         GtkTreeView *view = GTK_TREE_VIEW(widget);
937         GtkTreePath *path;
938         GtkTreeViewColumn *column;
939         GtkTreeIter iter;
940         struct menu *menu;
941         gint col;
942
943         gtk_tree_view_get_cursor(view, &path, &column);
944         if (path == NULL)
945                 return FALSE;
946
947         if (event->keyval == GDK_space) {
948                 if (gtk_tree_view_row_expanded(view, path))
949                         gtk_tree_view_collapse_row(view, path);
950                 else
951                         gtk_tree_view_expand_row(view, path, FALSE);
952                 return TRUE;
953         }
954         if (event->keyval == GDK_KP_Enter) {
955         }
956         if (widget == tree1_w)
957                 return FALSE;
958
959         gtk_tree_model_get_iter(model2, &iter, path);
960         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
961
962         if (!strcasecmp(event->string, "n"))
963                 col = COL_NO;
964         else if (!strcasecmp(event->string, "m"))
965                 col = COL_MOD;
966         else if (!strcasecmp(event->string, "y"))
967                 col = COL_YES;
968         else
969                 col = -1;
970         change_sym_value(menu, col);
971
972         return FALSE;
973 }
974
975
976 /* Row selection changed: update help */
977 void
978 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
979 {
980         GtkTreeSelection *selection;
981         GtkTreeIter iter;
982         struct menu *menu;
983
984         selection = gtk_tree_view_get_selection(treeview);
985         if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
986                 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
987                 text_insert_help(menu);
988         }
989 }
990
991
992 /* User click: display sub-tree in the right frame. */
993 gboolean
994 on_treeview1_button_press_event(GtkWidget * widget,
995                                 GdkEventButton * event, gpointer user_data)
996 {
997         GtkTreeView *view = GTK_TREE_VIEW(widget);
998         GtkTreePath *path;
999         GtkTreeViewColumn *column;
1000         GtkTreeIter iter;
1001         struct menu *menu;
1002
1003         gint tx = (gint) event->x;
1004         gint ty = (gint) event->y;
1005         gint cx, cy;
1006
1007         gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
1008                                       &cy);
1009         if (path == NULL)
1010                 return FALSE;
1011
1012         gtk_tree_model_get_iter(model1, &iter, path);
1013         gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1014
1015         if (event->type == GDK_2BUTTON_PRESS) {
1016                 toggle_sym_value(menu);
1017                 current = menu;
1018                 display_tree_part();
1019         } else {
1020                 browsed = menu;
1021                 display_tree_part();
1022         }
1023
1024         gtk_widget_realize(tree2_w);
1025         gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1026         gtk_widget_grab_focus(tree2_w);
1027
1028         return FALSE;
1029 }
1030
1031
1032 /* Fill a row of strings */
1033 static gchar **fill_row(struct menu *menu)
1034 {
1035         static gchar *row[COL_NUMBER];
1036         struct symbol *sym = menu->sym;
1037         const char *def;
1038         int stype;
1039         tristate val;
1040         enum prop_type ptype;
1041         int i;
1042
1043         for (i = COL_OPTION; i <= COL_COLOR; i++)
1044                 g_free(row[i]);
1045         bzero(row, sizeof(row));
1046
1047         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1048
1049         row[COL_OPTION] =
1050             g_strdup_printf("%s %s %s %s",
1051                             ptype == P_COMMENT ? "***" : "",
1052                             menu_get_prompt(menu),
1053                             ptype == P_COMMENT ? "***" : "",
1054                             sym && !sym_has_value(sym) ? "(NEW)" : "");
1055
1056         if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1057                 row[COL_COLOR] = g_strdup("DarkGray");
1058         else if (opt_mode == OPT_PROMPT &&
1059                         menu_has_prompt(menu) && !menu_is_visible(menu))
1060                 row[COL_COLOR] = g_strdup("DarkGray");
1061         else
1062                 row[COL_COLOR] = g_strdup("Black");
1063
1064         switch (ptype) {
1065         case P_MENU:
1066                 row[COL_PIXBUF] = (gchar *) xpm_menu;
1067                 if (view_mode == SINGLE_VIEW)
1068                         row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1069                 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1070                 break;
1071         case P_COMMENT:
1072                 row[COL_PIXBUF] = (gchar *) xpm_void;
1073                 row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1074                 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1075                 break;
1076         default:
1077                 row[COL_PIXBUF] = (gchar *) xpm_void;
1078                 row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1079                 row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1080                 break;
1081         }
1082
1083         if (!sym)
1084                 return row;
1085         row[COL_NAME] = g_strdup(sym->name);
1086
1087         sym_calc_value(sym);
1088         sym->flags &= ~SYMBOL_CHANGED;
1089
1090         if (sym_is_choice(sym)) {       // parse childs for getting final value
1091                 struct menu *child;
1092                 struct symbol *def_sym = sym_get_choice_value(sym);
1093                 struct menu *def_menu = NULL;
1094
1095                 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1096
1097                 for (child = menu->list; child; child = child->next) {
1098                         if (menu_is_visible(child)
1099                             && child->sym == def_sym)
1100                                 def_menu = child;
1101                 }
1102
1103                 if (def_menu)
1104                         row[COL_VALUE] =
1105                             g_strdup(menu_get_prompt(def_menu));
1106         }
1107         if (sym->flags & SYMBOL_CHOICEVAL)
1108                 row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1109
1110         stype = sym_get_type(sym);
1111         switch (stype) {
1112         case S_BOOLEAN:
1113                 if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1114                         row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1115                 if (sym_is_choice(sym))
1116                         break;
1117                 /* fall through */
1118         case S_TRISTATE:
1119                 val = sym_get_tristate_value(sym);
1120                 switch (val) {
1121                 case no:
1122                         row[COL_NO] = g_strdup("N");
1123                         row[COL_VALUE] = g_strdup("N");
1124                         row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1125                         row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1126                         break;
1127                 case mod:
1128                         row[COL_MOD] = g_strdup("M");
1129                         row[COL_VALUE] = g_strdup("M");
1130                         row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1131                         break;
1132                 case yes:
1133                         row[COL_YES] = g_strdup("Y");
1134                         row[COL_VALUE] = g_strdup("Y");
1135                         row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1136                         row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1137                         break;
1138                 }
1139
1140                 if (val != no && sym_tristate_within_range(sym, no))
1141                         row[COL_NO] = g_strdup("_");
1142                 if (val != mod && sym_tristate_within_range(sym, mod))
1143                         row[COL_MOD] = g_strdup("_");
1144                 if (val != yes && sym_tristate_within_range(sym, yes))
1145                         row[COL_YES] = g_strdup("_");
1146                 break;
1147         case S_INT:
1148         case S_HEX:
1149         case S_STRING:
1150                 def = sym_get_string_value(sym);
1151                 row[COL_VALUE] = g_strdup(def);
1152                 row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1153                 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1154                 break;
1155         }
1156
1157         return row;
1158 }
1159
1160
1161 /* Set the node content with a row of strings */
1162 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1163 {
1164         GdkColor color;
1165         gboolean success;
1166         GdkPixbuf *pix;
1167
1168         pix = gdk_pixbuf_new_from_xpm_data((const char **)
1169                                            row[COL_PIXBUF]);
1170
1171         gdk_color_parse(row[COL_COLOR], &color);
1172         gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1173                                   FALSE, FALSE, &success);
1174
1175         gtk_tree_store_set(tree, node,
1176                            COL_OPTION, row[COL_OPTION],
1177                            COL_NAME, row[COL_NAME],
1178                            COL_NO, row[COL_NO],
1179                            COL_MOD, row[COL_MOD],
1180                            COL_YES, row[COL_YES],
1181                            COL_VALUE, row[COL_VALUE],
1182                            COL_MENU, (gpointer) menu,
1183                            COL_COLOR, &color,
1184                            COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1185                            COL_PIXBUF, pix,
1186                            COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1187                            COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1188                            COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1189                            COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1190                            COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1191                            -1);
1192
1193         g_object_unref(pix);
1194 }
1195
1196
1197 /* Add a node to the tree */
1198 static void place_node(struct menu *menu, char **row)
1199 {
1200         GtkTreeIter *parent = parents[indent - 1];
1201         GtkTreeIter *node = parents[indent];
1202
1203         gtk_tree_store_append(tree, node, parent);
1204         set_node(node, menu, row);
1205 }
1206
1207
1208 /* Find a node in the GTK+ tree */
1209 static GtkTreeIter found;
1210
1211 /*
1212  * Find a menu in the GtkTree starting at parent.
1213  */
1214 static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent,
1215                                            struct menu *tofind)
1216 {
1217         GtkTreeIter iter;
1218         GtkTreeIter *child = &iter;
1219         gboolean valid;
1220         GtkTreeIter *ret;
1221
1222         valid = gtk_tree_model_iter_children(model2, child, parent);
1223         while (valid) {
1224                 struct menu *menu;
1225
1226                 gtk_tree_model_get(model2, child, 6, &menu, -1);
1227
1228                 if (menu == tofind) {
1229                         memcpy(&found, child, sizeof(GtkTreeIter));
1230                         return &found;
1231                 }
1232
1233                 ret = gtktree_iter_find_node(child, tofind);
1234                 if (ret)
1235                         return ret;
1236
1237                 valid = gtk_tree_model_iter_next(model2, child);
1238         }
1239
1240         return NULL;
1241 }
1242
1243
1244 /*
1245  * Update the tree by adding/removing entries
1246  * Does not change other nodes
1247  */
1248 static void update_tree(struct menu *src, GtkTreeIter * dst)
1249 {
1250         struct menu *child1;
1251         GtkTreeIter iter, tmp;
1252         GtkTreeIter *child2 = &iter;
1253         gboolean valid;
1254         GtkTreeIter *sibling;
1255         struct symbol *sym;
1256         struct menu *menu1, *menu2;
1257
1258         if (src == &rootmenu)
1259                 indent = 1;
1260
1261         valid = gtk_tree_model_iter_children(model2, child2, dst);
1262         for (child1 = src->list; child1; child1 = child1->next) {
1263
1264                 sym = child1->sym;
1265
1266               reparse:
1267                 menu1 = child1;
1268                 if (valid)
1269                         gtk_tree_model_get(model2, child2, COL_MENU,
1270                                            &menu2, -1);
1271                 else
1272                         menu2 = NULL;   // force adding of a first child
1273
1274 #ifdef DEBUG
1275                 printf("%*c%s | %s\n", indent, ' ',
1276                        menu1 ? menu_get_prompt(menu1) : "nil",
1277                        menu2 ? menu_get_prompt(menu2) : "nil");
1278 #endif
1279
1280                 if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1281                     (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1282                     (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1283
1284                         /* remove node */
1285                         if (gtktree_iter_find_node(dst, menu1) != NULL) {
1286                                 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1287                                 valid = gtk_tree_model_iter_next(model2,
1288                                                                  child2);
1289                                 gtk_tree_store_remove(tree2, &tmp);
1290                                 if (!valid)
1291                                         return;         /* next parent */
1292                                 else
1293                                         goto reparse;   /* next child */
1294                         } else
1295                                 continue;
1296                 }
1297
1298                 if (menu1 != menu2) {
1299                         if (gtktree_iter_find_node(dst, menu1) == NULL) {       // add node
1300                                 if (!valid && !menu2)
1301                                         sibling = NULL;
1302                                 else
1303                                         sibling = child2;
1304                                 gtk_tree_store_insert_before(tree2,
1305                                                              child2,
1306                                                              dst, sibling);
1307                                 set_node(child2, menu1, fill_row(menu1));
1308                                 if (menu2 == NULL)
1309                                         valid = TRUE;
1310                         } else {        // remove node
1311                                 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1312                                 valid = gtk_tree_model_iter_next(model2,
1313                                                                  child2);
1314                                 gtk_tree_store_remove(tree2, &tmp);
1315                                 if (!valid)
1316                                         return; // next parent
1317                                 else
1318                                         goto reparse;   // next child
1319                         }
1320                 } else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1321                         set_node(child2, menu1, fill_row(menu1));
1322                 }
1323
1324                 indent++;
1325                 update_tree(child1, child2);
1326                 indent--;
1327
1328                 valid = gtk_tree_model_iter_next(model2, child2);
1329         }
1330 }
1331
1332
1333 /* Display the whole tree (single/split/full view) */
1334 static void display_tree(struct menu *menu)
1335 {
1336         struct symbol *sym;
1337         struct property *prop;
1338         struct menu *child;
1339         enum prop_type ptype;
1340
1341         if (menu == &rootmenu) {
1342                 indent = 1;
1343                 current = &rootmenu;
1344         }
1345
1346         for (child = menu->list; child; child = child->next) {
1347                 prop = child->prompt;
1348                 sym = child->sym;
1349                 ptype = prop ? prop->type : P_UNKNOWN;
1350
1351                 if (sym)
1352                         sym->flags &= ~SYMBOL_CHANGED;
1353
1354                 if ((view_mode == SPLIT_VIEW)
1355                     && !(child->flags & MENU_ROOT) && (tree == tree1))
1356                         continue;
1357
1358                 if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1359                     && (tree == tree2))
1360                         continue;
1361
1362                 if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1363                     (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1364                     (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1365                         place_node(child, fill_row(child));
1366 #ifdef DEBUG
1367                 printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1368                 printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1369                 printf("%s", prop_get_type_name(ptype));
1370                 printf(" | ");
1371                 if (sym) {
1372                         printf("%s", sym_type_name(sym->type));
1373                         printf(" | ");
1374                         printf("%s", dbg_sym_flags(sym->flags));
1375                         printf("\n");
1376                 } else
1377                         printf("\n");
1378 #endif
1379                 if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1380                     && (tree == tree2))
1381                         continue;
1382 /*
1383                 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1384                     || (view_mode == FULL_VIEW)
1385                     || (view_mode == SPLIT_VIEW))*/
1386
1387                 /* Change paned position if the view is not in 'split mode' */
1388                 if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1389                         gtk_paned_set_position(GTK_PANED(hpaned), 0);
1390                 }
1391
1392                 if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1393                     || (view_mode == FULL_VIEW)
1394                     || (view_mode == SPLIT_VIEW)) {
1395                         indent++;
1396                         display_tree(child);
1397                         indent--;
1398                 }
1399         }
1400 }
1401
1402 /* Display a part of the tree starting at current node (single/split view) */
1403 static void display_tree_part(void)
1404 {
1405         if (tree2)
1406                 gtk_tree_store_clear(tree2);
1407         if (view_mode == SINGLE_VIEW)
1408                 display_tree(current);
1409         else if (view_mode == SPLIT_VIEW)
1410                 display_tree(browsed);
1411         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1412 }
1413
1414 /* Display the list in the left frame (split view) */
1415 static void display_list(void)
1416 {
1417         if (tree1)
1418                 gtk_tree_store_clear(tree1);
1419
1420         tree = tree1;
1421         display_tree(&rootmenu);
1422         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1423         tree = tree2;
1424 }
1425
1426 static void fixup_rootmenu(struct menu *menu)
1427 {
1428         struct menu *child;
1429         static int menu_cnt = 0;
1430
1431         menu->flags |= MENU_ROOT;
1432         for (child = menu->list; child; child = child->next) {
1433                 if (child->prompt && child->prompt->type == P_MENU) {
1434                         menu_cnt++;
1435                         fixup_rootmenu(child);
1436                         menu_cnt--;
1437                 } else if (!menu_cnt)
1438                         fixup_rootmenu(child);
1439         }
1440 }
1441
1442
1443 /* Main */
1444 int main(int ac, char *av[])
1445 {
1446         const char *name;
1447         char *env;
1448         gchar *glade_file;
1449
1450         /* GTK stuffs */
1451         gtk_set_locale();
1452         gtk_init(&ac, &av);
1453         glade_init();
1454
1455         /* Determine GUI path */
1456         env = getenv(SRCTREE);
1457         if (env)
1458                 glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1459         else if (av[0][0] == '/')
1460                 glade_file = g_strconcat(av[0], ".glade", NULL);
1461         else
1462                 glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1463
1464         /* Conf stuffs */
1465         if (ac > 1 && av[1][0] == '-') {
1466                 switch (av[1][1]) {
1467                 case 'a':
1468                         //showAll = 1;
1469                         break;
1470                 case 's':
1471                         conf_set_message_callback(NULL);
1472                         break;
1473                 case 'h':
1474                 case '?':
1475                         printf("%s [-s] <config>\n", av[0]);
1476                         exit(0);
1477                 }
1478                 name = av[2];
1479         } else
1480                 name = av[1];
1481
1482         conf_parse(name);
1483         fixup_rootmenu(&rootmenu);
1484         conf_read(NULL);
1485
1486         /* Load the interface and connect signals */
1487         init_main_window(glade_file);
1488         init_tree_model();
1489         init_left_tree();
1490         init_right_tree();
1491
1492         switch (view_mode) {
1493         case SINGLE_VIEW:
1494                 display_tree_part();
1495                 break;
1496         case SPLIT_VIEW:
1497                 display_list();
1498                 break;
1499         case FULL_VIEW:
1500                 display_tree(&rootmenu);
1501                 break;
1502         }
1503
1504         gtk_main();
1505
1506         return 0;
1507 }
1508
1509 static void conf_changed(void)
1510 {
1511         bool changed = conf_get_changed();
1512         gtk_widget_set_sensitive(save_btn, changed);
1513         gtk_widget_set_sensitive(save_menu_item, changed);
1514 }