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