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