a11d5f7b9eeb1f793514fb8548b646a7414ca321
[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         case S_TRISTATE:
1176                 val = sym_get_tristate_value(sym);
1177                 switch (val) {
1178                 case no:
1179                         row[COL_NO] = g_strdup("N");
1180                         row[COL_VALUE] = g_strdup("N");
1181                         row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1182                         row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1183                         break;
1184                 case mod:
1185                         row[COL_MOD] = g_strdup("M");
1186                         row[COL_VALUE] = g_strdup("M");
1187                         row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1188                         break;
1189                 case yes:
1190                         row[COL_YES] = g_strdup("Y");
1191                         row[COL_VALUE] = g_strdup("Y");
1192                         row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1193                         row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1194                         break;
1195                 }
1196
1197                 if (val != no && sym_tristate_within_range(sym, no))
1198                         row[COL_NO] = g_strdup("_");
1199                 if (val != mod && sym_tristate_within_range(sym, mod))
1200                         row[COL_MOD] = g_strdup("_");
1201                 if (val != yes && sym_tristate_within_range(sym, yes))
1202                         row[COL_YES] = g_strdup("_");
1203                 break;
1204         case S_INT:
1205         case S_HEX:
1206         case S_STRING:
1207                 def = sym_get_string_value(sym);
1208                 row[COL_VALUE] = g_strdup(def);
1209                 row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1210                 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1211                 break;
1212         }
1213
1214         return row;
1215 }
1216
1217
1218 /* Set the node content with a row of strings */
1219 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1220 {
1221         GdkColor color;
1222         gboolean success;
1223         GdkPixbuf *pix;
1224
1225         pix = gdk_pixbuf_new_from_xpm_data((const char **)
1226                                            row[COL_PIXBUF]);
1227
1228         gdk_color_parse(row[COL_COLOR], &color);
1229         gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1230                                   FALSE, FALSE, &success);
1231
1232         gtk_tree_store_set(tree, node,
1233                            COL_OPTION, row[COL_OPTION],
1234                            COL_NAME, row[COL_NAME],
1235                            COL_NO, row[COL_NO],
1236                            COL_MOD, row[COL_MOD],
1237                            COL_YES, row[COL_YES],
1238                            COL_VALUE, row[COL_VALUE],
1239                            COL_MENU, (gpointer) menu,
1240                            COL_COLOR, &color,
1241                            COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1242                            COL_PIXBUF, pix,
1243                            COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1244                            COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1245                            COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1246                            COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1247                            COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1248                            -1);
1249
1250         g_object_unref(pix);
1251 }
1252
1253
1254 /* Add a node to the tree */
1255 static void place_node(struct menu *menu, char **row)
1256 {
1257         GtkTreeIter *parent = parents[indent - 1];
1258         GtkTreeIter *node = parents[indent];
1259
1260         gtk_tree_store_append(tree, node, parent);
1261         set_node(node, menu, row);
1262 }
1263
1264
1265 /* Find a node in the GTK+ tree */
1266 static GtkTreeIter found;
1267
1268 /*
1269  * Find a menu in the GtkTree starting at parent.
1270  */
1271 GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
1272                                     struct menu *tofind)
1273 {
1274         GtkTreeIter iter;
1275         GtkTreeIter *child = &iter;
1276         gboolean valid;
1277         GtkTreeIter *ret;
1278
1279         valid = gtk_tree_model_iter_children(model2, child, parent);
1280         while (valid) {
1281                 struct menu *menu;
1282
1283                 gtk_tree_model_get(model2, child, 6, &menu, -1);
1284
1285                 if (menu == tofind) {
1286                         memcpy(&found, child, sizeof(GtkTreeIter));
1287                         return &found;
1288                 }
1289
1290                 ret = gtktree_iter_find_node(child, tofind);
1291                 if (ret)
1292                         return ret;
1293
1294                 valid = gtk_tree_model_iter_next(model2, child);
1295         }
1296
1297         return NULL;
1298 }
1299
1300
1301 /*
1302  * Update the tree by adding/removing entries
1303  * Does not change other nodes
1304  */
1305 static void update_tree(struct menu *src, GtkTreeIter * dst)
1306 {
1307         struct menu *child1;
1308         GtkTreeIter iter, tmp;
1309         GtkTreeIter *child2 = &iter;
1310         gboolean valid;
1311         GtkTreeIter *sibling;
1312         struct symbol *sym;
1313         struct property *prop;
1314         struct menu *menu1, *menu2;
1315
1316         if (src == &rootmenu)
1317                 indent = 1;
1318
1319         valid = gtk_tree_model_iter_children(model2, child2, dst);
1320         for (child1 = src->list; child1; child1 = child1->next) {
1321
1322                 prop = child1->prompt;
1323                 sym = child1->sym;
1324
1325               reparse:
1326                 menu1 = child1;
1327                 if (valid)
1328                         gtk_tree_model_get(model2, child2, COL_MENU,
1329                                            &menu2, -1);
1330                 else
1331                         menu2 = NULL;   // force adding of a first child
1332
1333 #ifdef DEBUG
1334                 printf("%*c%s | %s\n", indent, ' ',
1335                        menu1 ? menu_get_prompt(menu1) : "nil",
1336                        menu2 ? menu_get_prompt(menu2) : "nil");
1337 #endif
1338
1339                 if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1340                     (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1341                     (opt_mode == OPT_ALL    && !menu_get_prompt(child1))) {
1342
1343                         /* remove node */
1344                         if (gtktree_iter_find_node(dst, menu1) != NULL) {
1345                                 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1346                                 valid = gtk_tree_model_iter_next(model2,
1347                                                                  child2);
1348                                 gtk_tree_store_remove(tree2, &tmp);
1349                                 if (!valid)
1350                                         return;         /* next parent */
1351                                 else
1352                                         goto reparse;   /* next child */
1353                         } else
1354                                 continue;
1355                 }
1356
1357                 if (menu1 != menu2) {
1358                         if (gtktree_iter_find_node(dst, menu1) == NULL) {       // add node
1359                                 if (!valid && !menu2)
1360                                         sibling = NULL;
1361                                 else
1362                                         sibling = child2;
1363                                 gtk_tree_store_insert_before(tree2,
1364                                                              child2,
1365                                                              dst, sibling);
1366                                 set_node(child2, menu1, fill_row(menu1));
1367                                 if (menu2 == NULL)
1368                                         valid = TRUE;
1369                         } else {        // remove node
1370                                 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1371                                 valid = gtk_tree_model_iter_next(model2,
1372                                                                  child2);
1373                                 gtk_tree_store_remove(tree2, &tmp);
1374                                 if (!valid)
1375                                         return; // next parent
1376                                 else
1377                                         goto reparse;   // next child
1378                         }
1379                 } else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1380                         set_node(child2, menu1, fill_row(menu1));
1381                 }
1382
1383                 indent++;
1384                 update_tree(child1, child2);
1385                 indent--;
1386
1387                 valid = gtk_tree_model_iter_next(model2, child2);
1388         }
1389 }
1390
1391
1392 /* Display the whole tree (single/split/full view) */
1393 static void display_tree(struct menu *menu)
1394 {
1395         struct symbol *sym;
1396         struct property *prop;
1397         struct menu *child;
1398         enum prop_type ptype;
1399
1400         if (menu == &rootmenu) {
1401                 indent = 1;
1402                 current = &rootmenu;
1403         }
1404
1405         for (child = menu->list; child; child = child->next) {
1406                 prop = child->prompt;
1407                 sym = child->sym;
1408                 ptype = prop ? prop->type : P_UNKNOWN;
1409
1410                 if (sym)
1411                         sym->flags &= ~SYMBOL_CHANGED;
1412
1413                 if ((view_mode == SPLIT_VIEW)
1414                     && !(child->flags & MENU_ROOT) && (tree == tree1))
1415                         continue;
1416
1417                 if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1418                     && (tree == tree2))
1419                         continue;
1420
1421                 if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1422                     (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1423                     (opt_mode == OPT_ALL    && menu_get_prompt(child)))
1424                         place_node(child, fill_row(child));
1425 #ifdef DEBUG
1426                 printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1427                 printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1428                 printf("%s", prop_get_type_name(ptype));
1429                 printf(" | ");
1430                 if (sym) {
1431                         printf("%s", sym_type_name(sym->type));
1432                         printf(" | ");
1433                         printf("%s", dbg_sym_flags(sym->flags));
1434                         printf("\n");
1435                 } else
1436                         printf("\n");
1437 #endif
1438                 if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1439                     && (tree == tree2))
1440                         continue;
1441 /*
1442                 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1443                     || (view_mode == FULL_VIEW)
1444                     || (view_mode == SPLIT_VIEW))*/
1445
1446                 /* Change paned position if the view is not in 'split mode' */
1447                 if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1448                         gtk_paned_set_position(GTK_PANED(hpaned), 0);
1449                 }
1450
1451                 if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1452                     || (view_mode == FULL_VIEW)
1453                     || (view_mode == SPLIT_VIEW)) {
1454                         indent++;
1455                         display_tree(child);
1456                         indent--;
1457                 }
1458         }
1459 }
1460
1461 /* Display a part of the tree starting at current node (single/split view) */
1462 static void display_tree_part(void)
1463 {
1464         if (tree2)
1465                 gtk_tree_store_clear(tree2);
1466         if (view_mode == SINGLE_VIEW)
1467                 display_tree(current);
1468         else if (view_mode == SPLIT_VIEW)
1469                 display_tree(browsed);
1470         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1471 }
1472
1473 /* Display the list in the left frame (split view) */
1474 static void display_list(void)
1475 {
1476         if (tree1)
1477                 gtk_tree_store_clear(tree1);
1478
1479         tree = tree1;
1480         display_tree(&rootmenu);
1481         gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1482         tree = tree2;
1483 }
1484
1485 void fixup_rootmenu(struct menu *menu)
1486 {
1487         struct menu *child;
1488         static int menu_cnt = 0;
1489
1490         menu->flags |= MENU_ROOT;
1491         for (child = menu->list; child; child = child->next) {
1492                 if (child->prompt && child->prompt->type == P_MENU) {
1493                         menu_cnt++;
1494                         fixup_rootmenu(child);
1495                         menu_cnt--;
1496                 } else if (!menu_cnt)
1497                         fixup_rootmenu(child);
1498         }
1499 }
1500
1501
1502 /* Main */
1503 int main(int ac, char *av[])
1504 {
1505         const char *name;
1506         char *env;
1507         gchar *glade_file;
1508
1509 #ifndef LKC_DIRECT_LINK
1510         kconfig_load();
1511 #endif
1512
1513         bindtextdomain(PACKAGE, LOCALEDIR);
1514         bind_textdomain_codeset(PACKAGE, "UTF-8");
1515         textdomain(PACKAGE);
1516
1517         /* GTK stuffs */
1518         gtk_set_locale();
1519         gtk_init(&ac, &av);
1520         glade_init();
1521
1522         //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1523         //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1524
1525         /* Determine GUI path */
1526         env = getenv(SRCTREE);
1527         if (env)
1528                 glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1529         else if (av[0][0] == '/')
1530                 glade_file = g_strconcat(av[0], ".glade", NULL);
1531         else
1532                 glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1533
1534         /* Conf stuffs */
1535         if (ac > 1 && av[1][0] == '-') {
1536                 switch (av[1][1]) {
1537                 case 'a':
1538                         //showAll = 1;
1539                         break;
1540                 case 'h':
1541                 case '?':
1542                         printf("%s <config>\n", av[0]);
1543                         exit(0);
1544                 }
1545                 name = av[2];
1546         } else
1547                 name = av[1];
1548
1549         conf_parse(name);
1550         fixup_rootmenu(&rootmenu);
1551         conf_read(NULL);
1552
1553         /* Load the interface and connect signals */
1554         init_main_window(glade_file);
1555         init_tree_model();
1556         init_left_tree();
1557         init_right_tree();
1558
1559         switch (view_mode) {
1560         case SINGLE_VIEW:
1561                 display_tree_part();
1562                 break;
1563         case SPLIT_VIEW:
1564                 display_list();
1565                 break;
1566         case FULL_VIEW:
1567                 display_tree(&rootmenu);
1568                 break;
1569         }
1570
1571         gtk_main();
1572
1573         return 0;
1574 }
1575
1576 static void conf_changed(void)
1577 {
1578         bool changed = conf_get_changed();
1579         gtk_widget_set_sensitive(save_btn, changed);
1580         gtk_widget_set_sensitive(save_menu_item, changed);
1581 }