1 /* Hey EMACS -*- linux-c -*- */
4 * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
5 * Released under the terms of the GNU GPL v2.0.
16 #include <glade/glade.h>
19 #include <gdk/gdkkeysyms.h>
30 SINGLE_VIEW
, SPLIT_VIEW
, FULL_VIEW
34 OPT_NORMAL
, OPT_ALL
, OPT_PROMPT
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
;
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
;
54 GtkTextTag
*tag1
, *tag2
;
57 GtkTreeStore
*tree1
, *tree2
, *tree
;
58 GtkTreeModel
*model1
, *model2
;
59 static GtkTreeIter
*parents
[256];
62 static struct menu
*current
; // current node for SINGLE view
63 static struct menu
*browsed
; // browsed node for SPLIT view
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
,
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);
80 /* Helping/Debugging Functions */
82 const char *dbg_sym_flags(int val
)
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/");
107 buf
[strlen(buf
) - 1] = '\0';
112 void replace_button_icon(GladeXML
* xml
, GdkDrawable
* window
,
113 GtkStyle
* style
, gchar
* btn_name
, gchar
** xpm
)
117 GtkToolButton
*button
;
120 pixmap
= gdk_pixmap_create_from_xpm_d(window
, &mask
,
121 &style
->bg
[GTK_STATE_NORMAL
],
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
);
130 /* Main Window Initialization */
131 void init_main_window(const gchar
* glade_file
)
135 GtkTextBuffer
*txtbuf
;
139 xml
= glade_xml_new(glade_file
, "window1", NULL
);
141 g_error(_("GUI loading failed !\n"));
142 glade_xml_signal_autoconnect(xml
);
144 main_wnd
= glade_xml_get_widget(xml
, "window1");
145 hpaned
= glade_xml_get_widget(xml
, "hpaned1");
146 vpaned
= glade_xml_get_widget(xml
, "vpaned1");
147 tree1_w
= glade_xml_get_widget(xml
, "treeview1");
148 tree2_w
= glade_xml_get_widget(xml
, "treeview2");
149 text_w
= glade_xml_get_widget(xml
, "textview3");
151 back_btn
= glade_xml_get_widget(xml
, "button1");
152 gtk_widget_set_sensitive(back_btn
, FALSE
);
154 widget
= glade_xml_get_widget(xml
, "show_name1");
155 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
158 widget
= glade_xml_get_widget(xml
, "show_range1");
159 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
162 widget
= glade_xml_get_widget(xml
, "show_data1");
163 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
166 save_btn
= glade_xml_get_widget(xml
, "button3");
167 save_menu_item
= glade_xml_get_widget(xml
, "save1");
168 conf_set_changed_callback(conf_changed
);
170 style
= gtk_widget_get_style(main_wnd
);
171 widget
= glade_xml_get_widget(xml
, "toolbar1");
173 #if 0 /* Use stock Gtk icons instead */
174 replace_button_icon(xml
, main_wnd
->window
, style
,
175 "button1", (gchar
**) xpm_back
);
176 replace_button_icon(xml
, main_wnd
->window
, style
,
177 "button2", (gchar
**) xpm_load
);
178 replace_button_icon(xml
, main_wnd
->window
, style
,
179 "button3", (gchar
**) xpm_save
);
181 replace_button_icon(xml
, main_wnd
->window
, style
,
182 "button4", (gchar
**) xpm_single_view
);
183 replace_button_icon(xml
, main_wnd
->window
, style
,
184 "button5", (gchar
**) xpm_split_view
);
185 replace_button_icon(xml
, main_wnd
->window
, style
,
186 "button6", (gchar
**) xpm_tree_view
);
191 widget
= glade_xml_get_widget(xml
, "button4");
192 g_signal_emit_by_name(widget
, "clicked");
195 widget
= glade_xml_get_widget(xml
, "button5");
196 g_signal_emit_by_name(widget
, "clicked");
199 widget
= glade_xml_get_widget(xml
, "button6");
200 g_signal_emit_by_name(widget
, "clicked");
204 txtbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
205 tag1
= gtk_text_buffer_create_tag(txtbuf
, "mytag1",
207 "weight", PANGO_WEIGHT_BOLD
,
209 tag2
= gtk_text_buffer_create_tag(txtbuf
, "mytag2",
210 /*"style", PANGO_STYLE_OBLIQUE, */
213 sprintf(title
, _("Linux Kernel v%s Configuration"),
214 getenv("KERNELVERSION"));
215 gtk_window_set_title(GTK_WINDOW(main_wnd
), title
);
217 gtk_widget_show(main_wnd
);
220 void init_tree_model(void)
224 tree
= tree2
= gtk_tree_store_new(COL_NUMBER
,
225 G_TYPE_STRING
, G_TYPE_STRING
,
226 G_TYPE_STRING
, G_TYPE_STRING
,
227 G_TYPE_STRING
, G_TYPE_STRING
,
228 G_TYPE_POINTER
, GDK_TYPE_COLOR
,
229 G_TYPE_BOOLEAN
, GDK_TYPE_PIXBUF
,
230 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
231 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
233 model2
= GTK_TREE_MODEL(tree2
);
235 for (parents
[0] = NULL
, i
= 1; i
< 256; i
++)
236 parents
[i
] = (GtkTreeIter
*) g_malloc(sizeof(GtkTreeIter
));
238 tree1
= gtk_tree_store_new(COL_NUMBER
,
239 G_TYPE_STRING
, G_TYPE_STRING
,
240 G_TYPE_STRING
, G_TYPE_STRING
,
241 G_TYPE_STRING
, G_TYPE_STRING
,
242 G_TYPE_POINTER
, GDK_TYPE_COLOR
,
243 G_TYPE_BOOLEAN
, GDK_TYPE_PIXBUF
,
244 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
245 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
247 model1
= GTK_TREE_MODEL(tree1
);
250 void init_left_tree(void)
252 GtkTreeView
*view
= GTK_TREE_VIEW(tree1_w
);
253 GtkCellRenderer
*renderer
;
254 GtkTreeSelection
*sel
;
255 GtkTreeViewColumn
*column
;
257 gtk_tree_view_set_model(view
, model1
);
258 gtk_tree_view_set_headers_visible(view
, TRUE
);
259 gtk_tree_view_set_rules_hint(view
, FALSE
);
261 column
= gtk_tree_view_column_new();
262 gtk_tree_view_append_column(view
, column
);
263 gtk_tree_view_column_set_title(column
, _("Options"));
265 renderer
= gtk_cell_renderer_toggle_new();
266 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
268 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
270 "active", COL_BTNACT
,
271 "inconsistent", COL_BTNINC
,
272 "visible", COL_BTNVIS
,
273 "radio", COL_BTNRAD
, NULL
);
274 renderer
= gtk_cell_renderer_text_new();
275 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
277 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
283 sel
= gtk_tree_view_get_selection(view
);
284 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_SINGLE
);
285 gtk_widget_realize(tree1_w
);
288 static void renderer_edited(GtkCellRendererText
* cell
,
289 const gchar
* path_string
,
290 const gchar
* new_text
, gpointer user_data
);
291 static void renderer_toggled(GtkCellRendererToggle
* cellrenderertoggle
,
292 gchar
* arg1
, gpointer user_data
);
294 void init_right_tree(void)
296 GtkTreeView
*view
= GTK_TREE_VIEW(tree2_w
);
297 GtkCellRenderer
*renderer
;
298 GtkTreeSelection
*sel
;
299 GtkTreeViewColumn
*column
;
302 gtk_tree_view_set_model(view
, model2
);
303 gtk_tree_view_set_headers_visible(view
, TRUE
);
304 gtk_tree_view_set_rules_hint(view
, FALSE
);
306 column
= gtk_tree_view_column_new();
307 gtk_tree_view_append_column(view
, column
);
308 gtk_tree_view_column_set_title(column
, _("Options"));
310 renderer
= gtk_cell_renderer_pixbuf_new();
311 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
313 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
315 "pixbuf", COL_PIXBUF
,
316 "visible", COL_PIXVIS
, NULL
);
317 renderer
= gtk_cell_renderer_toggle_new();
318 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
320 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
322 "active", COL_BTNACT
,
323 "inconsistent", COL_BTNINC
,
324 "visible", COL_BTNVIS
,
325 "radio", COL_BTNRAD
, NULL
);
326 /*g_signal_connect(G_OBJECT(renderer), "toggled",
327 G_CALLBACK(renderer_toggled), NULL); */
328 renderer
= gtk_cell_renderer_text_new();
329 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
331 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
337 renderer
= gtk_cell_renderer_text_new();
338 gtk_tree_view_insert_column_with_attributes(view
, -1,
343 renderer
= gtk_cell_renderer_text_new();
344 gtk_tree_view_insert_column_with_attributes(view
, -1,
349 renderer
= gtk_cell_renderer_text_new();
350 gtk_tree_view_insert_column_with_attributes(view
, -1,
355 renderer
= gtk_cell_renderer_text_new();
356 gtk_tree_view_insert_column_with_attributes(view
, -1,
361 renderer
= gtk_cell_renderer_text_new();
362 gtk_tree_view_insert_column_with_attributes(view
, -1,
363 _("Value"), renderer
,
369 g_signal_connect(G_OBJECT(renderer
), "edited",
370 G_CALLBACK(renderer_edited
), NULL
);
372 column
= gtk_tree_view_get_column(view
, COL_NAME
);
373 gtk_tree_view_column_set_visible(column
, show_name
);
374 column
= gtk_tree_view_get_column(view
, COL_NO
);
375 gtk_tree_view_column_set_visible(column
, show_range
);
376 column
= gtk_tree_view_get_column(view
, COL_MOD
);
377 gtk_tree_view_column_set_visible(column
, show_range
);
378 column
= gtk_tree_view_get_column(view
, COL_YES
);
379 gtk_tree_view_column_set_visible(column
, show_range
);
380 column
= gtk_tree_view_get_column(view
, COL_VALUE
);
381 gtk_tree_view_column_set_visible(column
, show_value
);
384 for (i
= 0; i
< COL_VALUE
; i
++) {
385 column
= gtk_tree_view_get_column(view
, i
);
386 gtk_tree_view_column_set_resizable(column
, TRUE
);
390 sel
= gtk_tree_view_get_selection(view
);
391 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_SINGLE
);
395 /* Utility Functions */
398 static void text_insert_help(struct menu
*menu
)
400 GtkTextBuffer
*buffer
;
401 GtkTextIter start
, end
;
402 const char *prompt
= _(menu_get_prompt(menu
));
403 struct gstr help
= str_new();
405 menu_get_ext_help(menu
, &help
);
407 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
408 gtk_text_buffer_get_bounds(buffer
, &start
, &end
);
409 gtk_text_buffer_delete(buffer
, &start
, &end
);
410 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w
), 15);
412 gtk_text_buffer_get_end_iter(buffer
, &end
);
413 gtk_text_buffer_insert_with_tags(buffer
, &end
, prompt
, -1, tag1
,
415 gtk_text_buffer_insert_at_cursor(buffer
, "\n\n", 2);
416 gtk_text_buffer_get_end_iter(buffer
, &end
);
417 gtk_text_buffer_insert_with_tags(buffer
, &end
, str_get(&help
), -1, tag2
,
423 static void text_insert_msg(const char *title
, const char *message
)
425 GtkTextBuffer
*buffer
;
426 GtkTextIter start
, end
;
427 const char *msg
= message
;
429 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
430 gtk_text_buffer_get_bounds(buffer
, &start
, &end
);
431 gtk_text_buffer_delete(buffer
, &start
, &end
);
432 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w
), 15);
434 gtk_text_buffer_get_end_iter(buffer
, &end
);
435 gtk_text_buffer_insert_with_tags(buffer
, &end
, title
, -1, tag1
,
437 gtk_text_buffer_insert_at_cursor(buffer
, "\n\n", 2);
438 gtk_text_buffer_get_end_iter(buffer
, &end
);
439 gtk_text_buffer_insert_with_tags(buffer
, &end
, msg
, -1, tag2
,
444 /* Main Windows Callbacks */
446 void on_save_activate(GtkMenuItem
* menuitem
, gpointer user_data
);
447 gboolean
on_window1_delete_event(GtkWidget
* widget
, GdkEvent
* event
,
450 GtkWidget
*dialog
, *label
;
453 if (!conf_get_changed())
456 dialog
= gtk_dialog_new_with_buttons(_("Warning !"),
457 GTK_WINDOW(main_wnd
),
460 GTK_DIALOG_DESTROY_WITH_PARENT
),
466 GTK_RESPONSE_CANCEL
, NULL
);
467 gtk_dialog_set_default_response(GTK_DIALOG(dialog
),
468 GTK_RESPONSE_CANCEL
);
470 label
= gtk_label_new(_("\nSave configuration ?\n"));
471 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), label
);
472 gtk_widget_show(label
);
474 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
476 case GTK_RESPONSE_YES
:
477 on_save_activate(NULL
, NULL
);
479 case GTK_RESPONSE_NO
:
481 case GTK_RESPONSE_CANCEL
:
482 case GTK_RESPONSE_DELETE_EVENT
:
484 gtk_widget_destroy(dialog
);
492 void on_window1_destroy(GtkObject
* object
, gpointer user_data
)
499 on_window1_size_request(GtkWidget
* widget
,
500 GtkRequisition
* requisition
, gpointer user_data
)
505 if (widget
->window
== NULL
)
506 gtk_window_get_default_size(GTK_WINDOW(main_wnd
), &w
, &h
);
508 gdk_window_get_size(widget
->window
, &w
, &h
);
514 gtk_paned_set_position(GTK_PANED(vpaned
), 2 * h
/ 3);
518 /* Menu & Toolbar Callbacks */
522 load_filename(GtkFileSelection
* file_selector
, gpointer user_data
)
526 fn
= gtk_file_selection_get_filename(GTK_FILE_SELECTION
530 text_insert_msg(_("Error"), _("Unable to load configuration !"));
532 display_tree(&rootmenu
);
535 void on_load1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
539 fs
= gtk_file_selection_new(_("Load file..."));
540 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
),
542 G_CALLBACK(load_filename
), (gpointer
) fs
);
543 g_signal_connect_swapped(GTK_OBJECT
544 (GTK_FILE_SELECTION(fs
)->ok_button
),
545 "clicked", G_CALLBACK(gtk_widget_destroy
),
547 g_signal_connect_swapped(GTK_OBJECT
548 (GTK_FILE_SELECTION(fs
)->cancel_button
),
549 "clicked", G_CALLBACK(gtk_widget_destroy
),
555 void on_save_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
557 if (conf_write(NULL
))
558 text_insert_msg(_("Error"), _("Unable to save configuration !"));
563 store_filename(GtkFileSelection
* file_selector
, gpointer user_data
)
567 fn
= gtk_file_selection_get_filename(GTK_FILE_SELECTION
571 text_insert_msg(_("Error"), _("Unable to save configuration !"));
573 gtk_widget_destroy(GTK_WIDGET(user_data
));
576 void on_save_as1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
580 fs
= gtk_file_selection_new(_("Save file as..."));
581 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
),
583 G_CALLBACK(store_filename
), (gpointer
) fs
);
584 g_signal_connect_swapped(GTK_OBJECT
585 (GTK_FILE_SELECTION(fs
)->ok_button
),
586 "clicked", G_CALLBACK(gtk_widget_destroy
),
588 g_signal_connect_swapped(GTK_OBJECT
589 (GTK_FILE_SELECTION(fs
)->cancel_button
),
590 "clicked", G_CALLBACK(gtk_widget_destroy
),
596 void on_quit1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
598 if (!on_window1_delete_event(NULL
, NULL
, NULL
))
599 gtk_widget_destroy(GTK_WIDGET(main_wnd
));
603 void on_show_name1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
605 GtkTreeViewColumn
*col
;
607 show_name
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
608 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_NAME
);
610 gtk_tree_view_column_set_visible(col
, show_name
);
614 void on_show_range1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
616 GtkTreeViewColumn
*col
;
618 show_range
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
619 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_NO
);
621 gtk_tree_view_column_set_visible(col
, show_range
);
622 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_MOD
);
624 gtk_tree_view_column_set_visible(col
, show_range
);
625 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_YES
);
627 gtk_tree_view_column_set_visible(col
, show_range
);
632 void on_show_data1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
634 GtkTreeViewColumn
*col
;
636 show_value
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
637 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_VALUE
);
639 gtk_tree_view_column_set_visible(col
, show_value
);
644 on_set_option_mode1_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
646 opt_mode
= OPT_NORMAL
;
647 gtk_tree_store_clear(tree2
);
648 display_tree(&rootmenu
); /* instead of update_tree to speed-up */
653 on_set_option_mode2_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
656 gtk_tree_store_clear(tree2
);
657 display_tree(&rootmenu
); /* instead of update_tree to speed-up */
662 on_set_option_mode3_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
664 opt_mode
= OPT_PROMPT
;
665 gtk_tree_store_clear(tree2
);
666 display_tree(&rootmenu
); /* instead of update_tree to speed-up */
670 void on_introduction1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
673 const gchar
*intro_text
= _(
674 "Welcome to gkc, the GTK+ graphical kernel configuration tool\n"
676 "For each option, a blank box indicates the feature is disabled, a\n"
677 "check indicates it is enabled, and a dot indicates that it is to\n"
678 "be compiled as a module. Clicking on the box will cycle through the three states.\n"
680 "If you do not see an option (e.g., a device driver) that you\n"
681 "believe should be present, try turning on Show All Options\n"
682 "under the Options menu.\n"
683 "Although there is no cross reference yet to help you figure out\n"
684 "what other options must be enabled to support the option you\n"
685 "are interested in, you can still view the help of a grayed-out\n"
688 "Toggling Show Debug Info under the Options menu will show \n"
689 "the dependencies, which you can then match by examining other options.");
691 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
692 GTK_DIALOG_DESTROY_WITH_PARENT
,
694 GTK_BUTTONS_CLOSE
, intro_text
);
695 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
696 G_CALLBACK(gtk_widget_destroy
),
698 gtk_widget_show_all(dialog
);
702 void on_about1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
705 const gchar
*about_text
=
706 _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
707 "Based on the source code from Roman Zippel.\n");
709 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
710 GTK_DIALOG_DESTROY_WITH_PARENT
,
712 GTK_BUTTONS_CLOSE
, about_text
);
713 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
714 G_CALLBACK(gtk_widget_destroy
),
716 gtk_widget_show_all(dialog
);
720 void on_license1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
723 const gchar
*license_text
=
724 _("gkc is released under the terms of the GNU GPL v2.\n"
725 "For more information, please see the source code or\n"
726 "visit http://www.fsf.org/licenses/licenses.html\n");
728 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
729 GTK_DIALOG_DESTROY_WITH_PARENT
,
731 GTK_BUTTONS_CLOSE
, license_text
);
732 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
733 G_CALLBACK(gtk_widget_destroy
),
735 gtk_widget_show_all(dialog
);
739 void on_back_clicked(GtkButton
* button
, gpointer user_data
)
741 enum prop_type ptype
;
743 current
= current
->parent
;
744 ptype
= current
->prompt
? current
->prompt
->type
: P_UNKNOWN
;
746 current
= current
->parent
;
749 if (current
== &rootmenu
)
750 gtk_widget_set_sensitive(back_btn
, FALSE
);
754 void on_load_clicked(GtkButton
* button
, gpointer user_data
)
756 on_load1_activate(NULL
, user_data
);
760 void on_single_clicked(GtkButton
* button
, gpointer user_data
)
762 view_mode
= SINGLE_VIEW
;
763 gtk_paned_set_position(GTK_PANED(hpaned
), 0);
764 gtk_widget_hide(tree1_w
);
770 void on_split_clicked(GtkButton
* button
, gpointer user_data
)
773 view_mode
= SPLIT_VIEW
;
774 gtk_widget_show(tree1_w
);
775 gtk_window_get_default_size(GTK_WINDOW(main_wnd
), &w
, &h
);
776 gtk_paned_set_position(GTK_PANED(hpaned
), w
/ 2);
778 gtk_tree_store_clear(tree2
);
781 /* Disable back btn, like in full mode. */
782 gtk_widget_set_sensitive(back_btn
, FALSE
);
786 void on_full_clicked(GtkButton
* button
, gpointer user_data
)
788 view_mode
= FULL_VIEW
;
789 gtk_paned_set_position(GTK_PANED(hpaned
), 0);
790 gtk_widget_hide(tree1_w
);
792 gtk_tree_store_clear(tree2
);
793 display_tree(&rootmenu
);
794 gtk_widget_set_sensitive(back_btn
, FALSE
);
798 void on_collapse_clicked(GtkButton
* button
, gpointer user_data
)
800 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w
));
804 void on_expand_clicked(GtkButton
* button
, gpointer user_data
)
806 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w
));
810 /* CTree Callbacks */
812 /* Change hex/int/string value in the cell */
813 static void renderer_edited(GtkCellRendererText
* cell
,
814 const gchar
* path_string
,
815 const gchar
* new_text
, gpointer user_data
)
817 GtkTreePath
*path
= gtk_tree_path_new_from_string(path_string
);
819 const char *old_def
, *new_def
;
823 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
826 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
829 gtk_tree_model_get(model2
, &iter
, COL_VALUE
, &old_def
, -1);
832 sym_set_string_value(sym
, new_def
);
834 update_tree(&rootmenu
, NULL
);
836 gtk_tree_path_free(path
);
839 /* Change the value of a symbol and update the tree */
840 static void change_sym_value(struct menu
*menu
, gint col
)
842 struct symbol
*sym
= menu
->sym
;
843 tristate oldval
, newval
;
850 else if (col
== COL_MOD
)
852 else if (col
== COL_YES
)
857 switch (sym_get_type(sym
)) {
860 oldval
= sym_get_tristate_value(sym
);
861 if (!sym_tristate_within_range(sym
, newval
))
863 sym_set_tristate_value(sym
, newval
);
864 if (view_mode
== FULL_VIEW
)
865 update_tree(&rootmenu
, NULL
);
866 else if (view_mode
== SPLIT_VIEW
) {
867 update_tree(browsed
, NULL
);
870 else if (view_mode
== SINGLE_VIEW
)
871 display_tree_part(); //fixme: keep exp/coll
881 static void toggle_sym_value(struct menu
*menu
)
886 sym_toggle_tristate_value(menu
->sym
);
887 if (view_mode
== FULL_VIEW
)
888 update_tree(&rootmenu
, NULL
);
889 else if (view_mode
== SPLIT_VIEW
) {
890 update_tree(browsed
, NULL
);
893 else if (view_mode
== SINGLE_VIEW
)
894 display_tree_part(); //fixme: keep exp/coll
897 static void renderer_toggled(GtkCellRendererToggle
* cell
,
898 gchar
* path_string
, gpointer user_data
)
900 GtkTreePath
*path
, *sel_path
= NULL
;
901 GtkTreeIter iter
, sel_iter
;
902 GtkTreeSelection
*sel
;
905 path
= gtk_tree_path_new_from_string(path_string
);
906 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
909 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w
));
910 if (gtk_tree_selection_get_selected(sel
, NULL
, &sel_iter
))
911 sel_path
= gtk_tree_model_get_path(model2
, &sel_iter
);
914 if (gtk_tree_path_compare(path
, sel_path
))
917 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
918 toggle_sym_value(menu
);
921 gtk_tree_path_free(sel_path
);
923 gtk_tree_path_free(path
);
926 static gint
column2index(GtkTreeViewColumn
* column
)
930 for (i
= 0; i
< COL_NUMBER
; i
++) {
931 GtkTreeViewColumn
*col
;
933 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), i
);
942 /* User click: update choice (full) or goes down (single) */
944 on_treeview2_button_press_event(GtkWidget
* widget
,
945 GdkEventButton
* event
, gpointer user_data
)
947 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
949 GtkTreeViewColumn
*column
;
954 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
955 gint tx
= (gint
) event
->x
;
956 gint ty
= (gint
) event
->y
;
959 gtk_tree_view_get_path_at_pos(view
, tx
, ty
, &path
, &column
, &cx
,
962 gtk_tree_view_get_cursor(view
, &path
, &column
);
967 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
969 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
971 col
= column2index(column
);
972 if (event
->type
== GDK_2BUTTON_PRESS
) {
973 enum prop_type ptype
;
974 ptype
= menu
->prompt
? menu
->prompt
->type
: P_UNKNOWN
;
976 if (ptype
== P_MENU
&& view_mode
!= FULL_VIEW
&& col
== COL_OPTION
) {
977 // goes down into menu
980 gtk_widget_set_sensitive(back_btn
, TRUE
);
981 } else if ((col
== COL_OPTION
)) {
982 toggle_sym_value(menu
);
983 gtk_tree_view_expand_row(view
, path
, TRUE
);
986 if (col
== COL_VALUE
) {
987 toggle_sym_value(menu
);
988 gtk_tree_view_expand_row(view
, path
, TRUE
);
989 } else if (col
== COL_NO
|| col
== COL_MOD
991 change_sym_value(menu
, col
);
992 gtk_tree_view_expand_row(view
, path
, TRUE
);
999 /* Key pressed: update choice */
1001 on_treeview2_key_press_event(GtkWidget
* widget
,
1002 GdkEventKey
* event
, gpointer user_data
)
1004 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
1006 GtkTreeViewColumn
*column
;
1011 gtk_tree_view_get_cursor(view
, &path
, &column
);
1015 if (event
->keyval
== GDK_space
) {
1016 if (gtk_tree_view_row_expanded(view
, path
))
1017 gtk_tree_view_collapse_row(view
, path
);
1019 gtk_tree_view_expand_row(view
, path
, FALSE
);
1022 if (event
->keyval
== GDK_KP_Enter
) {
1024 if (widget
== tree1_w
)
1027 gtk_tree_model_get_iter(model2
, &iter
, path
);
1028 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
1030 if (!strcasecmp(event
->string
, "n"))
1032 else if (!strcasecmp(event
->string
, "m"))
1034 else if (!strcasecmp(event
->string
, "y"))
1038 change_sym_value(menu
, col
);
1044 /* Row selection changed: update help */
1046 on_treeview2_cursor_changed(GtkTreeView
* treeview
, gpointer user_data
)
1048 GtkTreeSelection
*selection
;
1052 selection
= gtk_tree_view_get_selection(treeview
);
1053 if (gtk_tree_selection_get_selected(selection
, &model2
, &iter
)) {
1054 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
1055 text_insert_help(menu
);
1060 /* User click: display sub-tree in the right frame. */
1062 on_treeview1_button_press_event(GtkWidget
* widget
,
1063 GdkEventButton
* event
, gpointer user_data
)
1065 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
1067 GtkTreeViewColumn
*column
;
1071 gint tx
= (gint
) event
->x
;
1072 gint ty
= (gint
) event
->y
;
1075 gtk_tree_view_get_path_at_pos(view
, tx
, ty
, &path
, &column
, &cx
,
1080 gtk_tree_model_get_iter(model1
, &iter
, path
);
1081 gtk_tree_model_get(model1
, &iter
, COL_MENU
, &menu
, -1);
1083 if (event
->type
== GDK_2BUTTON_PRESS
) {
1084 toggle_sym_value(menu
);
1086 display_tree_part();
1089 display_tree_part();
1092 gtk_widget_realize(tree2_w
);
1093 gtk_tree_view_set_cursor(view
, path
, NULL
, FALSE
);
1094 gtk_widget_grab_focus(tree2_w
);
1100 /* Fill a row of strings */
1101 static gchar
**fill_row(struct menu
*menu
)
1103 static gchar
*row
[COL_NUMBER
];
1104 struct symbol
*sym
= menu
->sym
;
1108 enum prop_type ptype
;
1111 for (i
= COL_OPTION
; i
<= COL_COLOR
; i
++)
1113 bzero(row
, sizeof(row
));
1116 g_strdup_printf("%s %s", _(menu_get_prompt(menu
)),
1117 sym
&& !sym_has_value(sym
) ? "(NEW)" : "");
1119 if (opt_mode
== OPT_ALL
&& !menu_is_visible(menu
))
1120 row
[COL_COLOR
] = g_strdup("DarkGray");
1121 else if (opt_mode
== OPT_PROMPT
&&
1122 menu_has_prompt(menu
) && !menu_is_visible(menu
))
1123 row
[COL_COLOR
] = g_strdup("DarkGray");
1125 row
[COL_COLOR
] = g_strdup("Black");
1127 ptype
= menu
->prompt
? menu
->prompt
->type
: P_UNKNOWN
;
1130 row
[COL_PIXBUF
] = (gchar
*) xpm_menu
;
1131 if (view_mode
== SINGLE_VIEW
)
1132 row
[COL_PIXVIS
] = GINT_TO_POINTER(TRUE
);
1133 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1136 row
[COL_PIXBUF
] = (gchar
*) xpm_void
;
1137 row
[COL_PIXVIS
] = GINT_TO_POINTER(FALSE
);
1138 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1141 row
[COL_PIXBUF
] = (gchar
*) xpm_void
;
1142 row
[COL_PIXVIS
] = GINT_TO_POINTER(FALSE
);
1143 row
[COL_BTNVIS
] = GINT_TO_POINTER(TRUE
);
1149 row
[COL_NAME
] = g_strdup(sym
->name
);
1151 sym_calc_value(sym
);
1152 sym
->flags
&= ~SYMBOL_CHANGED
;
1154 if (sym_is_choice(sym
)) { // parse childs for getting final value
1156 struct symbol
*def_sym
= sym_get_choice_value(sym
);
1157 struct menu
*def_menu
= NULL
;
1159 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1161 for (child
= menu
->list
; child
; child
= child
->next
) {
1162 if (menu_is_visible(child
)
1163 && child
->sym
== def_sym
)
1169 g_strdup(_(menu_get_prompt(def_menu
)));
1171 if (sym
->flags
& SYMBOL_CHOICEVAL
)
1172 row
[COL_BTNRAD
] = GINT_TO_POINTER(TRUE
);
1174 stype
= sym_get_type(sym
);
1177 if (GPOINTER_TO_INT(row
[COL_PIXVIS
]) == FALSE
)
1178 row
[COL_BTNVIS
] = GINT_TO_POINTER(TRUE
);
1179 if (sym_is_choice(sym
))
1182 val
= sym_get_tristate_value(sym
);
1185 row
[COL_NO
] = g_strdup("N");
1186 row
[COL_VALUE
] = g_strdup("N");
1187 row
[COL_BTNACT
] = GINT_TO_POINTER(FALSE
);
1188 row
[COL_BTNINC
] = GINT_TO_POINTER(FALSE
);
1191 row
[COL_MOD
] = g_strdup("M");
1192 row
[COL_VALUE
] = g_strdup("M");
1193 row
[COL_BTNINC
] = GINT_TO_POINTER(TRUE
);
1196 row
[COL_YES
] = g_strdup("Y");
1197 row
[COL_VALUE
] = g_strdup("Y");
1198 row
[COL_BTNACT
] = GINT_TO_POINTER(TRUE
);
1199 row
[COL_BTNINC
] = GINT_TO_POINTER(FALSE
);
1203 if (val
!= no
&& sym_tristate_within_range(sym
, no
))
1204 row
[COL_NO
] = g_strdup("_");
1205 if (val
!= mod
&& sym_tristate_within_range(sym
, mod
))
1206 row
[COL_MOD
] = g_strdup("_");
1207 if (val
!= yes
&& sym_tristate_within_range(sym
, yes
))
1208 row
[COL_YES
] = g_strdup("_");
1213 def
= sym_get_string_value(sym
);
1214 row
[COL_VALUE
] = g_strdup(def
);
1215 row
[COL_EDIT
] = GINT_TO_POINTER(TRUE
);
1216 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1224 /* Set the node content with a row of strings */
1225 static void set_node(GtkTreeIter
* node
, struct menu
*menu
, gchar
** row
)
1231 pix
= gdk_pixbuf_new_from_xpm_data((const char **)
1234 gdk_color_parse(row
[COL_COLOR
], &color
);
1235 gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color
, 1,
1236 FALSE
, FALSE
, &success
);
1238 gtk_tree_store_set(tree
, node
,
1239 COL_OPTION
, row
[COL_OPTION
],
1240 COL_NAME
, row
[COL_NAME
],
1241 COL_NO
, row
[COL_NO
],
1242 COL_MOD
, row
[COL_MOD
],
1243 COL_YES
, row
[COL_YES
],
1244 COL_VALUE
, row
[COL_VALUE
],
1245 COL_MENU
, (gpointer
) menu
,
1247 COL_EDIT
, GPOINTER_TO_INT(row
[COL_EDIT
]),
1249 COL_PIXVIS
, GPOINTER_TO_INT(row
[COL_PIXVIS
]),
1250 COL_BTNVIS
, GPOINTER_TO_INT(row
[COL_BTNVIS
]),
1251 COL_BTNACT
, GPOINTER_TO_INT(row
[COL_BTNACT
]),
1252 COL_BTNINC
, GPOINTER_TO_INT(row
[COL_BTNINC
]),
1253 COL_BTNRAD
, GPOINTER_TO_INT(row
[COL_BTNRAD
]),
1256 g_object_unref(pix
);
1260 /* Add a node to the tree */
1261 static void place_node(struct menu
*menu
, char **row
)
1263 GtkTreeIter
*parent
= parents
[indent
- 1];
1264 GtkTreeIter
*node
= parents
[indent
];
1266 gtk_tree_store_append(tree
, node
, parent
);
1267 set_node(node
, menu
, row
);
1271 /* Find a node in the GTK+ tree */
1272 static GtkTreeIter found
;
1275 * Find a menu in the GtkTree starting at parent.
1277 GtkTreeIter
*gtktree_iter_find_node(GtkTreeIter
* parent
,
1278 struct menu
*tofind
)
1281 GtkTreeIter
*child
= &iter
;
1285 valid
= gtk_tree_model_iter_children(model2
, child
, parent
);
1289 gtk_tree_model_get(model2
, child
, 6, &menu
, -1);
1291 if (menu
== tofind
) {
1292 memcpy(&found
, child
, sizeof(GtkTreeIter
));
1296 ret
= gtktree_iter_find_node(child
, tofind
);
1300 valid
= gtk_tree_model_iter_next(model2
, child
);
1308 * Update the tree by adding/removing entries
1309 * Does not change other nodes
1311 static void update_tree(struct menu
*src
, GtkTreeIter
* dst
)
1313 struct menu
*child1
;
1314 GtkTreeIter iter
, tmp
;
1315 GtkTreeIter
*child2
= &iter
;
1317 GtkTreeIter
*sibling
;
1319 struct property
*prop
;
1320 struct menu
*menu1
, *menu2
;
1322 if (src
== &rootmenu
)
1325 valid
= gtk_tree_model_iter_children(model2
, child2
, dst
);
1326 for (child1
= src
->list
; child1
; child1
= child1
->next
) {
1328 prop
= child1
->prompt
;
1334 gtk_tree_model_get(model2
, child2
, COL_MENU
,
1337 menu2
= NULL
; // force adding of a first child
1340 printf("%*c%s | %s\n", indent
, ' ',
1341 menu1
? menu_get_prompt(menu1
) : "nil",
1342 menu2
? menu_get_prompt(menu2
) : "nil");
1345 if ((opt_mode
== OPT_NORMAL
&& !menu_is_visible(child1
)) ||
1346 (opt_mode
== OPT_PROMPT
&& !menu_has_prompt(child1
)) ||
1347 (opt_mode
== OPT_ALL
&& !menu_get_prompt(child1
))) {
1350 if (gtktree_iter_find_node(dst
, menu1
) != NULL
) {
1351 memcpy(&tmp
, child2
, sizeof(GtkTreeIter
));
1352 valid
= gtk_tree_model_iter_next(model2
,
1354 gtk_tree_store_remove(tree2
, &tmp
);
1356 return; /* next parent */
1358 goto reparse
; /* next child */
1363 if (menu1
!= menu2
) {
1364 if (gtktree_iter_find_node(dst
, menu1
) == NULL
) { // add node
1365 if (!valid
&& !menu2
)
1369 gtk_tree_store_insert_before(tree2
,
1372 set_node(child2
, menu1
, fill_row(menu1
));
1375 } else { // remove node
1376 memcpy(&tmp
, child2
, sizeof(GtkTreeIter
));
1377 valid
= gtk_tree_model_iter_next(model2
,
1379 gtk_tree_store_remove(tree2
, &tmp
);
1381 return; // next parent
1383 goto reparse
; // next child
1385 } else if (sym
&& (sym
->flags
& SYMBOL_CHANGED
)) {
1386 set_node(child2
, menu1
, fill_row(menu1
));
1390 update_tree(child1
, child2
);
1393 valid
= gtk_tree_model_iter_next(model2
, child2
);
1398 /* Display the whole tree (single/split/full view) */
1399 static void display_tree(struct menu
*menu
)
1402 struct property
*prop
;
1404 enum prop_type ptype
;
1406 if (menu
== &rootmenu
) {
1408 current
= &rootmenu
;
1411 for (child
= menu
->list
; child
; child
= child
->next
) {
1412 prop
= child
->prompt
;
1414 ptype
= prop
? prop
->type
: P_UNKNOWN
;
1417 sym
->flags
&= ~SYMBOL_CHANGED
;
1419 if ((view_mode
== SPLIT_VIEW
)
1420 && !(child
->flags
& MENU_ROOT
) && (tree
== tree1
))
1423 if ((view_mode
== SPLIT_VIEW
) && (child
->flags
& MENU_ROOT
)
1427 if ((opt_mode
== OPT_NORMAL
&& menu_is_visible(child
)) ||
1428 (opt_mode
== OPT_PROMPT
&& menu_has_prompt(child
)) ||
1429 (opt_mode
== OPT_ALL
&& menu_get_prompt(child
)))
1430 place_node(child
, fill_row(child
));
1432 printf("%*c%s: ", indent
, ' ', menu_get_prompt(child
));
1433 printf("%s", child
->flags
& MENU_ROOT
? "rootmenu | " : "");
1434 printf("%s", prop_get_type_name(ptype
));
1437 printf("%s", sym_type_name(sym
->type
));
1439 printf("%s", dbg_sym_flags(sym
->flags
));
1444 if ((view_mode
!= FULL_VIEW
) && (ptype
== P_MENU
)
1448 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1449 || (view_mode == FULL_VIEW)
1450 || (view_mode == SPLIT_VIEW))*/
1451 if (((view_mode
== SINGLE_VIEW
) && (menu
->flags
& MENU_ROOT
))
1452 || (view_mode
== FULL_VIEW
)
1453 || (view_mode
== SPLIT_VIEW
)) {
1455 display_tree(child
);
1461 /* Display a part of the tree starting at current node (single/split view) */
1462 static void display_tree_part(void)
1465 gtk_tree_store_clear(tree2
);
1466 if (view_mode
== SINGLE_VIEW
)
1467 display_tree(current
);
1468 else if (view_mode
== SPLIT_VIEW
)
1469 display_tree(browsed
);
1470 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w
));
1473 /* Display the list in the left frame (split view) */
1474 static void display_list(void)
1477 gtk_tree_store_clear(tree1
);
1480 display_tree(&rootmenu
);
1481 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w
));
1485 void fixup_rootmenu(struct menu
*menu
)
1488 static int menu_cnt
= 0;
1490 menu
->flags
|= MENU_ROOT
;
1491 for (child
= menu
->list
; child
; child
= child
->next
) {
1492 if (child
->prompt
&& child
->prompt
->type
== P_MENU
) {
1494 fixup_rootmenu(child
);
1496 } else if (!menu_cnt
)
1497 fixup_rootmenu(child
);
1503 int main(int ac
, char *av
[])
1509 #ifndef LKC_DIRECT_LINK
1513 bindtextdomain(PACKAGE
, LOCALEDIR
);
1514 bind_textdomain_codeset(PACKAGE
, "UTF-8");
1515 textdomain(PACKAGE
);
1522 //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1523 //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1525 /* Determine GUI path */
1526 env
= getenv(SRCTREE
);
1528 glade_file
= g_strconcat(env
, "/scripts/kconfig/gconf.glade", NULL
);
1529 else if (av
[0][0] == '/')
1530 glade_file
= g_strconcat(av
[0], ".glade", NULL
);
1532 glade_file
= g_strconcat(g_get_current_dir(), "/", av
[0], ".glade", NULL
);
1534 /* Load the interface and connect signals */
1535 init_main_window(glade_file
);
1541 if (ac
> 1 && av
[1][0] == '-') {
1548 printf("%s <config>\n", av
[0]);
1556 fixup_rootmenu(&rootmenu
);
1559 switch (view_mode
) {
1561 display_tree_part();
1567 display_tree(&rootmenu
);
1576 static void conf_changed(void)
1578 bool changed
= conf_get_changed();
1579 gtk_widget_set_sensitive(save_btn
, changed
);
1580 gtk_widget_set_sensitive(save_menu_item
, changed
);