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