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