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