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