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