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