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