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