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