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