Merge branch 'zynq' of git://www.denx.de/git/u-boot-microblaze
[platform/kernel/u-boot.git] / scripts / kconfig / gconf.c
1 /* Hey EMACS -*- linux-c -*- */
2 /*
3  *
4  * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
5  * Released under the terms of the GNU GPL v2.0.
6  *
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #  include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include "lkc.h"
15 #include "images.c"
16
17 #include <glade/glade.h>
18 #include <gtk/gtk.h>
19 #include <glib.h>
20 #include <gdk/gdkkeysyms.h>
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <time.h>
26
27 //#define DEBUG
28
29 enum {
30         SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
31 };
32
33 enum {
34         OPT_NORMAL, OPT_ALL, OPT_PROMPT
35 };
36
37 static gint view_mode = FULL_VIEW;
38 static gboolean show_name = TRUE;
39 static gboolean show_range = TRUE;
40 static gboolean show_value = TRUE;
41 static gboolean resizeable = FALSE;
42 static int opt_mode = OPT_NORMAL;
43
44 GtkWidget *main_wnd = NULL;
45 GtkWidget *tree1_w = NULL;      // left  frame
46 GtkWidget *tree2_w = NULL;      // right frame
47 GtkWidget *text_w = NULL;
48 GtkWidget *hpaned = NULL;
49 GtkWidget *vpaned = NULL;
50 GtkWidget *back_btn = NULL;
51 GtkWidget *save_btn = NULL;
52 GtkWidget *save_menu_item = NULL;
53
54 GtkTextTag *tag1, *tag2;
55 GdkColor color;
56
57 GtkTreeStore *tree1, *tree2, *tree;
58 GtkTreeModel *model1, *model2;
59 static GtkTreeIter *parents[256];
60 static gint indent;
61
62 static struct menu *current; // current node for SINGLE view
63 static struct menu *browsed; // browsed node for SPLIT view
64
65 enum {
66         COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
67         COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
68         COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
69         COL_NUMBER
70 };
71
72 static void display_list(void);
73 static void display_tree(struct menu *menu);
74 static void display_tree_part(void);
75 static void update_tree(struct menu *src, GtkTreeIter * dst);
76 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
77 static gchar **fill_row(struct menu *menu);
78 static void conf_changed(void);
79
80 /* Helping/Debugging Functions */
81
82 const char *dbg_sym_flags(int val)
83 {
84         static char buf[256];
85
86         bzero(buf, 256);
87
88         if (val & SYMBOL_CONST)
89                 strcat(buf, "const/");
90         if (val & SYMBOL_CHECK)
91                 strcat(buf, "check/");
92         if (val & SYMBOL_CHOICE)
93                 strcat(buf, "choice/");
94         if (val & SYMBOL_CHOICEVAL)
95                 strcat(buf, "choiceval/");
96         if (val & SYMBOL_VALID)
97                 strcat(buf, "valid/");
98         if (val & SYMBOL_OPTIONAL)
99                 strcat(buf, "optional/");
100         if (val & SYMBOL_WRITE)
101                 strcat(buf, "write/");
102         if (val & SYMBOL_CHANGED)
103                 strcat(buf, "changed/");
104         if (val & SYMBOL_AUTO)
105                 strcat(buf, "auto/");
106
107         buf[strlen(buf) - 1] = '\0';
108
109         return buf;
110 }
111
112 void replace_button_icon(GladeXML * xml, GdkDrawable * window,
113                          GtkStyle * style, gchar * btn_name, gchar ** xpm)
114 {
115         GdkPixmap *pixmap;
116         GdkBitmap *mask;
117         GtkToolButton *button;
118         GtkWidget *image;
119
120         pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
121                                               &style->bg[GTK_STATE_NORMAL],
122                                               xpm);
123
124         button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
125         image = gtk_image_new_from_pixmap(pixmap, mask);
126         gtk_widget_show(image);
127         gtk_tool_button_set_icon_widget(button, image);
128 }
129
130 /* Main Window Initialization */
131 void init_main_window(const gchar * glade_file)
132 {
133         GladeXML *xml;
134         GtkWidget *widget;
135         GtkTextBuffer *txtbuf;
136         GtkStyle *style;
137
138         xml = glade_xml_new(glade_file, "window1", NULL);
139         if (!xml)
140                 g_error(_("GUI loading failed !\n"));
141         glade_xml_signal_autoconnect(xml);
142
143         main_wnd = glade_xml_get_widget(xml, "window1");
144         hpaned = glade_xml_get_widget(xml, "hpaned1");
145         vpaned = glade_xml_get_widget(xml, "vpaned1");
146         tree1_w = glade_xml_get_widget(xml, "treeview1");
147         tree2_w = glade_xml_get_widget(xml, "treeview2");
148         text_w = glade_xml_get_widget(xml, "textview3");
149
150         back_btn = glade_xml_get_widget(xml, "button1");
151         gtk_widget_set_sensitive(back_btn, FALSE);
152
153         widget = glade_xml_get_widget(xml, "show_name1");
154         gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
155                                        show_name);
156
157         widget = glade_xml_get_widget(xml, "show_range1");
158         gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
159                                        show_range);
160
161         widget = glade_xml_get_widget(xml, "show_data1");
162         gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
163                                        show_value);
164
165         save_btn = glade_xml_get_widget(xml, "button3");
166         save_menu_item = glade_xml_get_widget(xml, "save1");
167         conf_set_changed_callback(conf_changed);
168
169         style = gtk_widget_get_style(main_wnd);
170         widget = glade_xml_get_widget(xml, "toolbar1");
171
172         replace_button_icon(xml, main_wnd->window, style,
173                             "button4", (gchar **) xpm_single_view);
174         replace_button_icon(xml, main_wnd->window, style,
175                             "button5", (gchar **) xpm_split_view);
176         replace_button_icon(xml, main_wnd->window, style,
177                             "button6", (gchar **) xpm_tree_view);
178
179         txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
180         tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
181                                           "foreground", "red",
182                                           "weight", PANGO_WEIGHT_BOLD,
183                                           NULL);
184         tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
185                                           /*"style", PANGO_STYLE_OBLIQUE, */
186                                           NULL);
187
188         gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
189
190         gtk_widget_show(main_wnd);
191 }
192
193 void init_tree_model(void)
194 {
195         gint i;
196
197         tree = tree2 = gtk_tree_store_new(COL_NUMBER,
198                                           G_TYPE_STRING, G_TYPE_STRING,
199                                           G_TYPE_STRING, G_TYPE_STRING,
200                                           G_TYPE_STRING, G_TYPE_STRING,
201                                           G_TYPE_POINTER, GDK_TYPE_COLOR,
202                                           G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
203                                           G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
204                                           G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
205                                           G_TYPE_BOOLEAN);
206         model2 = GTK_TREE_MODEL(tree2);
207
208         for (parents[0] = NULL, i = 1; i < 256; i++)
209                 parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
210
211         tree1 = gtk_tree_store_new(COL_NUMBER,
212                                    G_TYPE_STRING, G_TYPE_STRING,
213                                    G_TYPE_STRING, G_TYPE_STRING,
214                                    G_TYPE_STRING, G_TYPE_STRING,
215                                    G_TYPE_POINTER, GDK_TYPE_COLOR,
216                                    G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
217                                    G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
218                                    G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
219                                    G_TYPE_BOOLEAN);
220         model1 = GTK_TREE_MODEL(tree1);
221 }
222
223 void init_left_tree(void)
224 {
225         GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
226         GtkCellRenderer *renderer;
227         GtkTreeSelection *sel;
228         GtkTreeViewColumn *column;
229
230         gtk_tree_view_set_model(view, model1);
231         gtk_tree_view_set_headers_visible(view, TRUE);
232         gtk_tree_view_set_rules_hint(view, TRUE);
233
234         column = gtk_tree_view_column_new();
235         gtk_tree_view_append_column(view, column);
236         gtk_tree_view_column_set_title(column, _("Options"));
237
238         renderer = gtk_cell_renderer_toggle_new();
239         gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
240                                         renderer, FALSE);
241         gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
242                                             renderer,
243                                             "active", COL_BTNACT,
244                                             "inconsistent", COL_BTNINC,
245                                             "visible", COL_BTNVIS,
246                                             "radio", COL_BTNRAD, NULL);
247         renderer = gtk_cell_renderer_text_new();
248         gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
249                                         renderer, FALSE);
250         gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
251                                             renderer,
252                                             "text", COL_OPTION,
253                                             "foreground-gdk",
254                                             COL_COLOR, NULL);
255
256         sel = gtk_tree_view_get_selection(view);
257         gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
258         gtk_widget_realize(tree1_w);
259 }
260
261 static void renderer_edited(GtkCellRendererText * cell,
262                             const gchar * path_string,
263                             const gchar * new_text, gpointer user_data);
264
265 void init_right_tree(void)
266 {
267         GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
268         GtkCellRenderer *renderer;
269         GtkTreeSelection *sel;
270         GtkTreeViewColumn *column;
271         gint i;
272
273         gtk_tree_view_set_model(view, model2);
274         gtk_tree_view_set_headers_visible(view, TRUE);
275         gtk_tree_view_set_rules_hint(view, TRUE);
276
277         column = gtk_tree_view_column_new();
278         gtk_tree_view_append_column(view, column);
279         gtk_tree_view_column_set_title(column, _("Options"));
280
281         renderer = gtk_cell_renderer_pixbuf_new();
282         gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
283                                         renderer, FALSE);
284         gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
285                                             renderer,
286                                             "pixbuf", COL_PIXBUF,
287                                             "visible", COL_PIXVIS, NULL);
288         renderer = gtk_cell_renderer_toggle_new();
289         gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
290                                         renderer, FALSE);
291         gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
292                                             renderer,
293                                             "active", COL_BTNACT,
294                                             "inconsistent", COL_BTNINC,
295                                             "visible", COL_BTNVIS,
296                                             "radio", COL_BTNRAD, NULL);
297         renderer = gtk_cell_renderer_text_new();
298         gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
299                                         renderer, FALSE);
300         gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
301                                             renderer,
302                                             "text", COL_OPTION,
303                                             "foreground-gdk",
304                                             COL_COLOR, NULL);
305
306         renderer = gtk_cell_renderer_text_new();
307         gtk_tree_view_insert_column_with_attributes(view, -1,
308                                                     _("Name"), renderer,
309                                                     "text", COL_NAME,
310                                                     "foreground-gdk",
311                                                     COL_COLOR, NULL);
312         renderer = gtk_cell_renderer_text_new();
313         gtk_tree_view_insert_column_with_attributes(view, -1,
314                                                     "N", renderer,
315                                                     "text", COL_NO,
316                                                     "foreground-gdk",
317                                                     COL_COLOR, NULL);
318         renderer = gtk_cell_renderer_text_new();
319         gtk_tree_view_insert_column_with_attributes(view, -1,
320                                                     "M", renderer,
321                                                     "text", COL_MOD,
322                                                     "foreground-gdk",
323                                                     COL_COLOR, NULL);
324         renderer = gtk_cell_renderer_text_new();
325         gtk_tree_view_insert_column_with_attributes(view, -1,
326                                                     "Y", renderer,
327                                                     "text", COL_YES,
328                                                     "foreground-gdk",
329                                                     COL_COLOR, NULL);
330         renderer = gtk_cell_renderer_text_new();
331         gtk_tree_view_insert_column_with_attributes(view, -1,
332                                                     _("Value"), renderer,
333                                                     "text", COL_VALUE,
334                                                     "editable",
335                                                     COL_EDIT,
336                                                     "foreground-gdk",
337                                                     COL_COLOR, NULL);
338         g_signal_connect(G_OBJECT(renderer), "edited",
339                          G_CALLBACK(renderer_edited), NULL);
340
341         column = gtk_tree_view_get_column(view, COL_NAME);
342         gtk_tree_view_column_set_visible(column, show_name);
343         column = gtk_tree_view_get_column(view, COL_NO);
344         gtk_tree_view_column_set_visible(column, show_range);
345         column = gtk_tree_view_get_column(view, COL_MOD);
346         gtk_tree_view_column_set_visible(column, show_range);
347         column = gtk_tree_view_get_column(view, COL_YES);
348         gtk_tree_view_column_set_visible(column, show_range);
349         column = gtk_tree_view_get_column(view, COL_VALUE);
350         gtk_tree_view_column_set_visible(column, show_value);
351
352         if (resizeable) {
353                 for (i = 0; i < COL_VALUE; i++) {
354                         column = gtk_tree_view_get_column(view, i);
355                         gtk_tree_view_column_set_resizable(column, TRUE);
356                 }
357         }
358
359         sel = gtk_tree_view_get_selection(view);
360         gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
361 }
362
363
364 /* Utility Functions */
365
366
367 static void text_insert_help(struct menu *menu)
368 {
369         GtkTextBuffer *buffer;
370         GtkTextIter start, end;
371         const char *prompt = _(menu_get_prompt(menu));
372         struct gstr help = str_new();
373
374         menu_get_ext_help(menu, &help);
375
376         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
377         gtk_text_buffer_get_bounds(buffer, &start, &end);
378         gtk_text_buffer_delete(buffer, &start, &end);
379         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
380
381         gtk_text_buffer_get_end_iter(buffer, &end);
382         gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
383                                          NULL);
384         gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
385         gtk_text_buffer_get_end_iter(buffer, &end);
386         gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
387                                          NULL);
388         str_free(&help);
389 }
390
391
392 static void text_insert_msg(const char *title, const char *message)
393 {
394         GtkTextBuffer *buffer;
395         GtkTextIter start, end;
396         const char *msg = message;
397
398         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
399         gtk_text_buffer_get_bounds(buffer, &start, &end);
400         gtk_text_buffer_delete(buffer, &start, &end);
401         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
402
403         gtk_text_buffer_get_end_iter(buffer, &end);
404         gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
405                                          NULL);
406         gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
407         gtk_text_buffer_get_end_iter(buffer, &end);
408         gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
409                                          NULL);
410 }
411
412
413 /* Main Windows Callbacks */
414
415 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
416 gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
417                                  gpointer user_data)
418 {
419         GtkWidget *dialog, *label;
420         gint result;
421
422         if (!conf_get_changed())
423                 return FALSE;
424
425         dialog = gtk_dialog_new_with_buttons(_("Warning !"),
426                                              GTK_WINDOW(main_wnd),
427                                              (GtkDialogFlags)
428                                              (GTK_DIALOG_MODAL |
429                                               GTK_DIALOG_DESTROY_WITH_PARENT),
430                                              GTK_STOCK_OK,
431                                              GTK_RESPONSE_YES,
432                                              GTK_STOCK_NO,
433                                              GTK_RESPONSE_NO,
434                                              GTK_STOCK_CANCEL,
435                                              GTK_RESPONSE_CANCEL, NULL);
436         gtk_dialog_set_default_response(GTK_DIALOG(dialog),
437                                         GTK_RESPONSE_CANCEL);
438
439         label = gtk_label_new(_("\nSave configuration ?\n"));
440         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
441         gtk_widget_show(label);
442
443         result = gtk_dialog_run(GTK_DIALOG(dialog));
444         switch (result) {
445         case GTK_RESPONSE_YES:
446                 on_save_activate(NULL, NULL);
447                 return FALSE;
448         case GTK_RESPONSE_NO:
449                 return FALSE;
450         case GTK_RESPONSE_CANCEL:
451         case GTK_RESPONSE_DELETE_EVENT:
452         default:
453                 gtk_widget_destroy(dialog);
454                 return TRUE;
455         }
456
457         return FALSE;
458 }
459
460
461 void on_window1_destroy(GtkObject * object, gpointer user_data)
462 {
463         gtk_main_quit();
464 }
465
466
467 void
468 on_window1_size_request(GtkWidget * widget,
469                         GtkRequisition * requisition, gpointer user_data)
470 {
471         static gint old_h;
472         gint w, h;
473
474         if (widget->window == NULL)
475                 gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
476         else
477                 gdk_window_get_size(widget->window, &w, &h);
478
479         if (h == old_h)
480                 return;
481         old_h = h;
482
483         gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
484 }
485
486
487 /* Menu & Toolbar Callbacks */
488
489
490 static void
491 load_filename(GtkFileSelection * file_selector, gpointer user_data)
492 {
493         const gchar *fn;
494
495         fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
496                                              (user_data));
497
498         if (conf_read(fn))
499                 text_insert_msg(_("Error"), _("Unable to load configuration !"));
500         else
501                 display_tree(&rootmenu);
502 }
503
504 void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
505 {
506         GtkWidget *fs;
507
508         fs = gtk_file_selection_new(_("Load file..."));
509         g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
510                          "clicked",
511                          G_CALLBACK(load_filename), (gpointer) fs);
512         g_signal_connect_swapped(GTK_OBJECT
513                                  (GTK_FILE_SELECTION(fs)->ok_button),
514                                  "clicked", G_CALLBACK(gtk_widget_destroy),
515                                  (gpointer) fs);
516         g_signal_connect_swapped(GTK_OBJECT
517                                  (GTK_FILE_SELECTION(fs)->cancel_button),
518                                  "clicked", G_CALLBACK(gtk_widget_destroy),
519                                  (gpointer) fs);
520         gtk_widget_show(fs);
521 }
522
523
524 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
525 {
526         if (conf_write(NULL))
527                 text_insert_msg(_("Error"), _("Unable to save configuration !"));
528 }
529
530
531 static void
532 store_filename(GtkFileSelection * file_selector, gpointer user_data)
533 {
534         const gchar *fn;
535
536         fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
537                                              (user_data));
538
539         if (conf_write(fn))
540                 text_insert_msg(_("Error"), _("Unable to save configuration !"));
541
542         gtk_widget_destroy(GTK_WIDGET(user_data));
543 }
544
545 void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
546 {
547         GtkWidget *fs;
548
549         fs = gtk_file_selection_new(_("Save file as..."));
550         g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
551                          "clicked",
552                          G_CALLBACK(store_filename), (gpointer) fs);
553         g_signal_connect_swapped(GTK_OBJECT
554                                  (GTK_FILE_SELECTION(fs)->ok_button),
555                                  "clicked", G_CALLBACK(gtk_widget_destroy),
556                                  (gpointer) fs);
557         g_signal_connect_swapped(GTK_OBJECT
558                                  (GTK_FILE_SELECTION(fs)->cancel_button),
559                                  "clicked", G_CALLBACK(gtk_widget_destroy),
560                                  (gpointer) fs);
561         gtk_widget_show(fs);
562 }
563
564
565 void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
566 {
567         if (!on_window1_delete_event(NULL, NULL, NULL))
568                 gtk_widget_destroy(GTK_WIDGET(main_wnd));
569 }
570
571
572 void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
573 {
574         GtkTreeViewColumn *col;
575
576         show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
577         col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
578         if (col)
579                 gtk_tree_view_column_set_visible(col, show_name);
580 }
581
582
583 void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
584 {
585         GtkTreeViewColumn *col;
586
587         show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
588         col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
589         if (col)
590                 gtk_tree_view_column_set_visible(col, show_range);
591         col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
592         if (col)
593                 gtk_tree_view_column_set_visible(col, show_range);
594         col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
595         if (col)
596                 gtk_tree_view_column_set_visible(col, show_range);
597
598 }
599
600
601 void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
602 {
603         GtkTreeViewColumn *col;
604
605         show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
606         col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
607         if (col)
608                 gtk_tree_view_column_set_visible(col, show_value);
609 }
610
611
612 void
613 on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
614 {
615         opt_mode = OPT_NORMAL;
616         gtk_tree_store_clear(tree2);
617         display_tree(&rootmenu);        /* instead of update_tree to speed-up */
618 }
619
620
621 void
622 on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
623 {
624         opt_mode = OPT_ALL;
625         gtk_tree_store_clear(tree2);
626         display_tree(&rootmenu);        /* instead of update_tree to speed-up */
627 }
628
629
630 void
631 on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
632 {
633         opt_mode = OPT_PROMPT;
634         gtk_tree_store_clear(tree2);
635         display_tree(&rootmenu);        /* instead of update_tree to speed-up */
636 }
637
638
639 void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
640 {
641         GtkWidget *dialog;
642         const gchar *intro_text = _(
643             "Welcome to gkc, the GTK+ graphical configuration tool\n"
644             "For each option, a blank box indicates the feature is disabled, a\n"
645             "check indicates it is enabled, and a dot indicates that it is to\n"
646             "be compiled as a module.  Clicking on the box will cycle through the three states.\n"
647             "\n"
648             "If you do not see an option (e.g., a device driver) that you\n"
649             "believe should be present, try turning on Show All Options\n"
650             "under the Options menu.\n"
651             "Although there is no cross reference yet to help you figure out\n"
652             "what other options must be enabled to support the option you\n"
653             "are interested in, you can still view the help of a grayed-out\n"
654             "option.\n"
655             "\n"
656             "Toggling Show Debug Info under the Options menu will show \n"
657             "the dependencies, which you can then match by examining other options.");
658
659         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
660                                         GTK_DIALOG_DESTROY_WITH_PARENT,
661                                         GTK_MESSAGE_INFO,
662                                         GTK_BUTTONS_CLOSE, "%s", intro_text);
663         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
664                                  G_CALLBACK(gtk_widget_destroy),
665                                  GTK_OBJECT(dialog));
666         gtk_widget_show_all(dialog);
667 }
668
669
670 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
671 {
672         GtkWidget *dialog;
673         const gchar *about_text =
674             _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
675               "Based on the source code from Roman Zippel.\n");
676
677         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
678                                         GTK_DIALOG_DESTROY_WITH_PARENT,
679                                         GTK_MESSAGE_INFO,
680                                         GTK_BUTTONS_CLOSE, "%s", about_text);
681         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
682                                  G_CALLBACK(gtk_widget_destroy),
683                                  GTK_OBJECT(dialog));
684         gtk_widget_show_all(dialog);
685 }
686
687
688 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
689 {
690         GtkWidget *dialog;
691         const gchar *license_text =
692             _("gkc is released under the terms of the GNU GPL v2.\n"
693               "For more information, please see the source code or\n"
694               "visit http://www.fsf.org/licenses/licenses.html\n");
695
696         dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
697                                         GTK_DIALOG_DESTROY_WITH_PARENT,
698                                         GTK_MESSAGE_INFO,
699                                         GTK_BUTTONS_CLOSE, "%s", license_text);
700         g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
701                                  G_CALLBACK(gtk_widget_destroy),
702                                  GTK_OBJECT(dialog));
703         gtk_widget_show_all(dialog);
704 }
705
706
707 void on_back_clicked(GtkButton * button, gpointer user_data)
708 {
709         enum prop_type ptype;
710
711         current = current->parent;
712         ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
713         if (ptype != P_MENU)
714                 current = current->parent;
715         display_tree_part();
716
717         if (current == &rootmenu)
718                 gtk_widget_set_sensitive(back_btn, FALSE);
719 }
720
721
722 void on_load_clicked(GtkButton * button, gpointer user_data)
723 {
724         on_load1_activate(NULL, user_data);
725 }
726
727
728 void on_single_clicked(GtkButton * button, gpointer user_data)
729 {
730         view_mode = SINGLE_VIEW;
731         gtk_widget_hide(tree1_w);
732         current = &rootmenu;
733         display_tree_part();
734 }
735
736
737 void on_split_clicked(GtkButton * button, gpointer user_data)
738 {
739         gint w, h;
740         view_mode = SPLIT_VIEW;
741         gtk_widget_show(tree1_w);
742         gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
743         gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
744         if (tree2)
745                 gtk_tree_store_clear(tree2);
746         display_list();
747
748         /* Disable back btn, like in full mode. */
749         gtk_widget_set_sensitive(back_btn, FALSE);
750 }
751
752
753 void on_full_clicked(GtkButton * button, gpointer user_data)
754 {
755         view_mode = FULL_VIEW;
756         gtk_widget_hide(tree1_w);
757         if (tree2)
758                 gtk_tree_store_clear(tree2);
759         display_tree(&rootmenu);
760         gtk_widget_set_sensitive(back_btn, FALSE);
761 }
762
763
764 void on_collapse_clicked(GtkButton * button, gpointer user_data)
765 {
766         gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
767 }
768
769
770 void on_expand_clicked(GtkButton * button, gpointer user_data)
771 {
772         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
773 }
774
775
776 /* CTree Callbacks */
777
778 /* Change hex/int/string value in the cell */
779 static void renderer_edited(GtkCellRendererText * cell,
780                             const gchar * path_string,
781                             const gchar * new_text, gpointer user_data)
782 {
783         GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
784         GtkTreeIter iter;
785         const char *old_def, *new_def;
786         struct menu *menu;
787         struct symbol *sym;
788
789         if (!gtk_tree_model_get_iter(model2, &iter, path))
790                 return;
791
792         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
793         sym = menu->sym;
794
795         gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
796         new_def = new_text;
797
798         sym_set_string_value(sym, new_def);
799
800         update_tree(&rootmenu, NULL);
801
802         gtk_tree_path_free(path);
803 }
804
805 /* Change the value of a symbol and update the tree */
806 static void change_sym_value(struct menu *menu, gint col)
807 {
808         struct symbol *sym = menu->sym;
809         tristate newval;
810
811         if (!sym)
812                 return;
813
814         if (col == COL_NO)
815                 newval = no;
816         else if (col == COL_MOD)
817                 newval = mod;
818         else if (col == COL_YES)
819                 newval = yes;
820         else
821                 return;
822
823         switch (sym_get_type(sym)) {
824         case S_BOOLEAN:
825         case S_TRISTATE:
826                 if (!sym_tristate_within_range(sym, newval))
827                         newval = yes;
828                 sym_set_tristate_value(sym, newval);
829                 if (view_mode == FULL_VIEW)
830                         update_tree(&rootmenu, NULL);
831                 else if (view_mode == SPLIT_VIEW) {
832                         update_tree(browsed, NULL);
833                         display_list();
834                 }
835                 else if (view_mode == SINGLE_VIEW)
836                         display_tree_part();    //fixme: keep exp/coll
837                 break;
838         case S_INT:
839         case S_HEX:
840         case S_STRING:
841         default:
842                 break;
843         }
844 }
845
846 static void toggle_sym_value(struct menu *menu)
847 {
848         if (!menu->sym)
849                 return;
850
851         sym_toggle_tristate_value(menu->sym);
852         if (view_mode == FULL_VIEW)
853                 update_tree(&rootmenu, NULL);
854         else if (view_mode == SPLIT_VIEW) {
855                 update_tree(browsed, NULL);
856                 display_list();
857         }
858         else if (view_mode == SINGLE_VIEW)
859                 display_tree_part();    //fixme: keep exp/coll
860 }
861
862 static gint column2index(GtkTreeViewColumn * column)
863 {
864         gint i;
865
866         for (i = 0; i < COL_NUMBER; i++) {
867                 GtkTreeViewColumn *col;
868
869                 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
870                 if (col == column)
871                         return i;
872         }
873
874         return -1;
875 }
876
877
878 /* User click: update choice (full) or goes down (single) */
879 gboolean
880 on_treeview2_button_press_event(GtkWidget * widget,
881                                 GdkEventButton * event, gpointer user_data)
882 {
883         GtkTreeView *view = GTK_TREE_VIEW(widget);
884         GtkTreePath *path;
885         GtkTreeViewColumn *column;
886         GtkTreeIter iter;
887         struct menu *menu;
888         gint col;
889
890 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
891         gint tx = (gint) event->x;
892         gint ty = (gint) event->y;
893         gint cx, cy;
894
895         gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
896                                       &cy);
897 #else
898         gtk_tree_view_get_cursor(view, &path, &column);
899 #endif
900         if (path == NULL)
901                 return FALSE;
902
903         if (!gtk_tree_model_get_iter(model2, &iter, path))
904                 return FALSE;
905         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
906
907         col = column2index(column);
908         if (event->type == GDK_2BUTTON_PRESS) {
909                 enum prop_type ptype;
910                 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
911
912                 if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
913                         // goes down into menu
914                         current = menu;
915                         display_tree_part();
916                         gtk_widget_set_sensitive(back_btn, TRUE);
917                 } else if ((col == COL_OPTION)) {
918                         toggle_sym_value(menu);
919                         gtk_tree_view_expand_row(view, path, TRUE);
920                 }
921         } else {
922                 if (col == COL_VALUE) {
923                         toggle_sym_value(menu);
924                         gtk_tree_view_expand_row(view, path, TRUE);
925                 } else if (col == COL_NO || col == COL_MOD
926                            || col == COL_YES) {
927                         change_sym_value(menu, col);
928                         gtk_tree_view_expand_row(view, path, TRUE);
929                 }
930         }
931
932         return FALSE;
933 }
934
935 /* Key pressed: update choice */
936 gboolean
937 on_treeview2_key_press_event(GtkWidget * widget,
938                              GdkEventKey * event, gpointer user_data)
939 {
940         GtkTreeView *view = GTK_TREE_VIEW(widget);
941         GtkTreePath *path;
942         GtkTreeViewColumn *column;
943         GtkTreeIter iter;
944         struct menu *menu;
945         gint col;
946
947         gtk_tree_view_get_cursor(view, &path, &column);
948         if (path == NULL)
949                 return FALSE;
950
951         if (event->keyval == GDK_space) {
952                 if (gtk_tree_view_row_expanded(view, path))
953                         gtk_tree_view_collapse_row(view, path);
954                 else
955                         gtk_tree_view_expand_row(view, path, FALSE);
956                 return TRUE;
957         }
958         if (event->keyval == GDK_KP_Enter) {
959         }
960         if (widget == tree1_w)
961                 return FALSE;
962
963         gtk_tree_model_get_iter(model2, &iter, path);
964         gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
965
966         if (!strcasecmp(event->string, "n"))
967                 col = COL_NO;
968         else if (!strcasecmp(event->string, "m"))
969                 col = COL_MOD;
970         else if (!strcasecmp(event->string, "y"))
971                 col = COL_YES;
972         else
973                 col = -1;
974         change_sym_value(menu, col);
975
976         return FALSE;
977 }
978
979
980 /* Row selection changed: update help */
981 void
982 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
983 {
984         GtkTreeSelection *selection;
985         GtkTreeIter iter;
986         struct menu *menu;
987
988         selection = gtk_tree_view_get_selection(treeview);
989         if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
990                 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
991                 text_insert_help(menu);
992         }
993 }
994
995
996 /* User click: display sub-tree in the right frame. */
997 gboolean
998 on_treeview1_button_press_event(GtkWidget * widget,
999                                 GdkEventButton * event, gpointer user_data)
1000 {
1001         GtkTreeView *view = GTK_TREE_VIEW(widget);
1002         GtkTreePath *path;
1003         GtkTreeViewColumn *column;
1004         GtkTreeIter iter;
1005         struct menu *menu;
1006
1007         gint tx = (gint) event->x;
1008         gint ty = (gint) event->y;
1009         gint cx, cy;
1010
1011         gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
1012                                       &cy);
1013         if (path == NULL)
1014                 return FALSE;
1015
1016         gtk_tree_model_get_iter(model1, &iter, path);
1017         gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
1018
1019         if (event->type == GDK_2BUTTON_PRESS) {
1020                 toggle_sym_value(menu);
1021                 current = menu;
1022                 display_tree_part();
1023         } else {
1024                 browsed = menu;
1025                 display_tree_part();
1026         }
1027
1028         gtk_widget_realize(tree2_w);
1029         gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1030         gtk_widget_grab_focus(tree2_w);
1031
1032         return FALSE;
1033 }
1034
1035
1036 /* Fill a row of strings */
1037 static gchar **fill_row(struct menu *menu)
1038 {
1039         static gchar *row[COL_NUMBER];
1040         struct symbol *sym = menu->sym;
1041         const char *def;
1042         int stype;
1043         tristate val;
1044         enum prop_type ptype;
1045         int i;
1046
1047         for (i = COL_OPTION; i <= COL_COLOR; i++)
1048                 g_free(row[i]);
1049         bzero(row, sizeof(row));
1050
1051         row[COL_OPTION] =
1052             g_strdup_printf("%s %s", _(menu_get_prompt(menu)),
1053                             sym && !sym_has_value(sym) ? "(NEW)" : "");
1054
1055         if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1056                 row[COL_COLOR] = g_strdup("DarkGray");
1057         else if (opt_mode == OPT_PROMPT &&
1058                         menu_has_prompt(menu) && !menu_is_visible(menu))
1059                 row[COL_COLOR] = g_strdup("DarkGray");
1060         else
1061                 row[COL_COLOR] = g_strdup("Black");
1062
1063         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
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 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 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         bindtextdomain(PACKAGE, LOCALEDIR);
1451         bind_textdomain_codeset(PACKAGE, "UTF-8");
1452         textdomain(PACKAGE);
1453
1454         /* GTK stuffs */
1455         gtk_set_locale();
1456         gtk_init(&ac, &av);
1457         glade_init();
1458
1459         //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1460         //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1461
1462         /* Determine GUI path */
1463         env = getenv(SRCTREE);
1464         if (env)
1465                 glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1466         else if (av[0][0] == '/')
1467                 glade_file = g_strconcat(av[0], ".glade", NULL);
1468         else
1469                 glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1470
1471         /* Conf stuffs */
1472         if (ac > 1 && av[1][0] == '-') {
1473                 switch (av[1][1]) {
1474                 case 'a':
1475                         //showAll = 1;
1476                         break;
1477                 case 's':
1478                         conf_set_message_callback(NULL);
1479                         break;
1480                 case 'h':
1481                 case '?':
1482                         printf("%s [-s] <config>\n", av[0]);
1483                         exit(0);
1484                 }
1485                 name = av[2];
1486         } else
1487                 name = av[1];
1488
1489         conf_parse(name);
1490         fixup_rootmenu(&rootmenu);
1491         conf_read(NULL);
1492
1493         /* Load the interface and connect signals */
1494         init_main_window(glade_file);
1495         init_tree_model();
1496         init_left_tree();
1497         init_right_tree();
1498
1499         switch (view_mode) {
1500         case SINGLE_VIEW:
1501                 display_tree_part();
1502                 break;
1503         case SPLIT_VIEW:
1504                 display_list();
1505                 break;
1506         case FULL_VIEW:
1507                 display_tree(&rootmenu);
1508                 break;
1509         }
1510
1511         gtk_main();
1512
1513         return 0;
1514 }
1515
1516 static void conf_changed(void)
1517 {
1518         bool changed = conf_get_changed();
1519         gtk_widget_set_sensitive(save_btn, changed);
1520         gtk_widget_set_sensitive(save_menu_item, changed);
1521 }