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.
17 #include <glade/glade.h>
20 #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_NO_WRITE)
105 strcat(buf, "no_write/");
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;
138 xml = glade_xml_new(glade_file, "window1", NULL);
140 g_error("GUI loading failed !\n");
141 glade_xml_signal_autoconnect(xml);
143 main_wnd = glade_xml_get_widget(xml, "window1");
144 hpaned = glade_xml_get_widget(xml, "hpaned1");
145 vpaned = glade_xml_get_widget(xml, "vpaned1");
146 tree1_w = glade_xml_get_widget(xml, "treeview1");
147 tree2_w = glade_xml_get_widget(xml, "treeview2");
148 text_w = glade_xml_get_widget(xml, "textview3");
150 back_btn = glade_xml_get_widget(xml, "button1");
151 gtk_widget_set_sensitive(back_btn, FALSE);
153 widget = glade_xml_get_widget(xml, "show_name1");
154 gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
157 widget = glade_xml_get_widget(xml, "show_range1");
158 gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
161 widget = glade_xml_get_widget(xml, "show_data1");
162 gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
165 save_btn = glade_xml_get_widget(xml, "button3");
166 save_menu_item = glade_xml_get_widget(xml, "save1");
167 conf_set_changed_callback(conf_changed);
169 style = gtk_widget_get_style(main_wnd);
170 widget = glade_xml_get_widget(xml, "toolbar1");
172 replace_button_icon(xml, main_wnd->window, style,
173 "button4", (gchar **) xpm_single_view);
174 replace_button_icon(xml, main_wnd->window, style,
175 "button5", (gchar **) xpm_split_view);
176 replace_button_icon(xml, main_wnd->window, style,
177 "button6", (gchar **) xpm_tree_view);
179 txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
180 tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
182 "weight", PANGO_WEIGHT_BOLD,
184 tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
185 /*"style", PANGO_STYLE_OBLIQUE, */
188 gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
190 gtk_widget_show(main_wnd);
193 void init_tree_model(void)
197 tree = tree2 = gtk_tree_store_new(COL_NUMBER,
198 G_TYPE_STRING, G_TYPE_STRING,
199 G_TYPE_STRING, G_TYPE_STRING,
200 G_TYPE_STRING, G_TYPE_STRING,
201 G_TYPE_POINTER, GDK_TYPE_COLOR,
202 G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
203 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
204 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
206 model2 = GTK_TREE_MODEL(tree2);
208 for (parents[0] = NULL, i = 1; i < 256; i++)
209 parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
211 tree1 = gtk_tree_store_new(COL_NUMBER,
212 G_TYPE_STRING, G_TYPE_STRING,
213 G_TYPE_STRING, G_TYPE_STRING,
214 G_TYPE_STRING, G_TYPE_STRING,
215 G_TYPE_POINTER, GDK_TYPE_COLOR,
216 G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
217 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
218 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
220 model1 = GTK_TREE_MODEL(tree1);
223 void init_left_tree(void)
225 GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
226 GtkCellRenderer *renderer;
227 GtkTreeSelection *sel;
228 GtkTreeViewColumn *column;
230 gtk_tree_view_set_model(view, model1);
231 gtk_tree_view_set_headers_visible(view, TRUE);
232 gtk_tree_view_set_rules_hint(view, TRUE);
234 column = gtk_tree_view_column_new();
235 gtk_tree_view_append_column(view, column);
236 gtk_tree_view_column_set_title(column, "Options");
238 renderer = gtk_cell_renderer_toggle_new();
239 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
241 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
243 "active", COL_BTNACT,
244 "inconsistent", COL_BTNINC,
245 "visible", COL_BTNVIS,
246 "radio", COL_BTNRAD, NULL);
247 renderer = gtk_cell_renderer_text_new();
248 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
250 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
256 sel = gtk_tree_view_get_selection(view);
257 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
258 gtk_widget_realize(tree1_w);
261 static void renderer_edited(GtkCellRendererText * cell,
262 const gchar * path_string,
263 const gchar * new_text, gpointer user_data);
265 void init_right_tree(void)
267 GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
268 GtkCellRenderer *renderer;
269 GtkTreeSelection *sel;
270 GtkTreeViewColumn *column;
273 gtk_tree_view_set_model(view, model2);
274 gtk_tree_view_set_headers_visible(view, TRUE);
275 gtk_tree_view_set_rules_hint(view, TRUE);
277 column = gtk_tree_view_column_new();
278 gtk_tree_view_append_column(view, column);
279 gtk_tree_view_column_set_title(column, "Options");
281 renderer = gtk_cell_renderer_pixbuf_new();
282 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
284 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
286 "pixbuf", COL_PIXBUF,
287 "visible", COL_PIXVIS, NULL);
288 renderer = gtk_cell_renderer_toggle_new();
289 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
291 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
293 "active", COL_BTNACT,
294 "inconsistent", COL_BTNINC,
295 "visible", COL_BTNVIS,
296 "radio", COL_BTNRAD, NULL);
297 renderer = gtk_cell_renderer_text_new();
298 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
300 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
306 renderer = gtk_cell_renderer_text_new();
307 gtk_tree_view_insert_column_with_attributes(view, -1,
312 renderer = gtk_cell_renderer_text_new();
313 gtk_tree_view_insert_column_with_attributes(view, -1,
318 renderer = gtk_cell_renderer_text_new();
319 gtk_tree_view_insert_column_with_attributes(view, -1,
324 renderer = gtk_cell_renderer_text_new();
325 gtk_tree_view_insert_column_with_attributes(view, -1,
330 renderer = gtk_cell_renderer_text_new();
331 gtk_tree_view_insert_column_with_attributes(view, -1,
338 g_signal_connect(G_OBJECT(renderer), "edited",
339 G_CALLBACK(renderer_edited), NULL);
341 column = gtk_tree_view_get_column(view, COL_NAME);
342 gtk_tree_view_column_set_visible(column, show_name);
343 column = gtk_tree_view_get_column(view, COL_NO);
344 gtk_tree_view_column_set_visible(column, show_range);
345 column = gtk_tree_view_get_column(view, COL_MOD);
346 gtk_tree_view_column_set_visible(column, show_range);
347 column = gtk_tree_view_get_column(view, COL_YES);
348 gtk_tree_view_column_set_visible(column, show_range);
349 column = gtk_tree_view_get_column(view, COL_VALUE);
350 gtk_tree_view_column_set_visible(column, show_value);
353 for (i = 0; i < COL_VALUE; i++) {
354 column = gtk_tree_view_get_column(view, i);
355 gtk_tree_view_column_set_resizable(column, TRUE);
359 sel = gtk_tree_view_get_selection(view);
360 gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
363 /* Utility Functions */
365 static void text_insert_help(struct menu *menu)
367 GtkTextBuffer *buffer;
368 GtkTextIter start, end;
369 const char *prompt = menu_get_prompt(menu);
370 struct gstr help = str_new();
372 menu_get_ext_help(menu, &help);
374 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
375 gtk_text_buffer_get_bounds(buffer, &start, &end);
376 gtk_text_buffer_delete(buffer, &start, &end);
377 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
379 gtk_text_buffer_get_end_iter(buffer, &end);
380 gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
382 gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
383 gtk_text_buffer_get_end_iter(buffer, &end);
384 gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
389 static void text_insert_msg(const char *title, const char *message)
391 GtkTextBuffer *buffer;
392 GtkTextIter start, end;
393 const char *msg = message;
395 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
396 gtk_text_buffer_get_bounds(buffer, &start, &end);
397 gtk_text_buffer_delete(buffer, &start, &end);
398 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
400 gtk_text_buffer_get_end_iter(buffer, &end);
401 gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
403 gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
404 gtk_text_buffer_get_end_iter(buffer, &end);
405 gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
409 /* Main Windows Callbacks */
411 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
412 gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
415 GtkWidget *dialog, *label;
418 if (!conf_get_changed())
421 dialog = gtk_dialog_new_with_buttons("Warning !",
422 GTK_WINDOW(main_wnd),
425 GTK_DIALOG_DESTROY_WITH_PARENT),
431 GTK_RESPONSE_CANCEL, NULL);
432 gtk_dialog_set_default_response(GTK_DIALOG(dialog),
433 GTK_RESPONSE_CANCEL);
435 label = gtk_label_new("\nSave configuration ?\n");
436 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
437 gtk_widget_show(label);
439 result = gtk_dialog_run(GTK_DIALOG(dialog));
441 case GTK_RESPONSE_YES:
442 on_save_activate(NULL, NULL);
444 case GTK_RESPONSE_NO:
446 case GTK_RESPONSE_CANCEL:
447 case GTK_RESPONSE_DELETE_EVENT:
449 gtk_widget_destroy(dialog);
456 void on_window1_destroy(GtkObject * object, gpointer user_data)
462 on_window1_size_request(GtkWidget * widget,
463 GtkRequisition * requisition, gpointer user_data)
468 if (widget->window == NULL)
469 gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
471 gdk_window_get_size(widget->window, &w, &h);
477 gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
480 /* Menu & Toolbar Callbacks */
483 load_filename(GtkFileSelection * file_selector, gpointer user_data)
487 fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
491 text_insert_msg("Error", "Unable to load configuration !");
493 display_tree(&rootmenu);
496 void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
500 fs = gtk_file_selection_new("Load file...");
501 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
503 G_CALLBACK(load_filename), (gpointer) fs);
504 g_signal_connect_swapped(GTK_OBJECT
505 (GTK_FILE_SELECTION(fs)->ok_button),
506 "clicked", G_CALLBACK(gtk_widget_destroy),
508 g_signal_connect_swapped(GTK_OBJECT
509 (GTK_FILE_SELECTION(fs)->cancel_button),
510 "clicked", G_CALLBACK(gtk_widget_destroy),
515 void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
517 if (conf_write(NULL))
518 text_insert_msg("Error", "Unable to save configuration !");
522 store_filename(GtkFileSelection * file_selector, gpointer user_data)
526 fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
530 text_insert_msg("Error", "Unable to save configuration !");
532 gtk_widget_destroy(GTK_WIDGET(user_data));
535 void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
539 fs = gtk_file_selection_new("Save file as...");
540 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
542 G_CALLBACK(store_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),
554 void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
556 if (!on_window1_delete_event(NULL, NULL, NULL))
557 gtk_widget_destroy(GTK_WIDGET(main_wnd));
560 void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
562 GtkTreeViewColumn *col;
564 show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
565 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
567 gtk_tree_view_column_set_visible(col, show_name);
570 void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
572 GtkTreeViewColumn *col;
574 show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
575 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
577 gtk_tree_view_column_set_visible(col, show_range);
578 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
580 gtk_tree_view_column_set_visible(col, show_range);
581 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
583 gtk_tree_view_column_set_visible(col, show_range);
587 void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
589 GtkTreeViewColumn *col;
591 show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
592 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
594 gtk_tree_view_column_set_visible(col, show_value);
598 on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
600 opt_mode = OPT_NORMAL;
601 gtk_tree_store_clear(tree2);
602 display_tree(&rootmenu); /* instead of update_tree to speed-up */
606 on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
609 gtk_tree_store_clear(tree2);
610 display_tree(&rootmenu); /* instead of update_tree to speed-up */
614 on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
616 opt_mode = OPT_PROMPT;
617 gtk_tree_store_clear(tree2);
618 display_tree(&rootmenu); /* instead of update_tree to speed-up */
621 void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
624 const gchar *intro_text =
625 "Welcome to gkc, the GTK+ graphical configuration tool\n"
626 "For each option, a blank box indicates the feature is disabled, a\n"
627 "check indicates it is enabled, and a dot indicates that it is to\n"
628 "be compiled as a module. Clicking on the box will cycle through the three states.\n"
630 "If you do not see an option (e.g., a device driver) that you\n"
631 "believe should be present, try turning on Show All Options\n"
632 "under the Options menu.\n"
633 "Although there is no cross reference yet to help you figure out\n"
634 "what other options must be enabled to support the option you\n"
635 "are interested in, you can still view the help of a grayed-out\n"
638 "Toggling Show Debug Info under the Options menu will show \n"
639 "the dependencies, which you can then match by examining other options.";
641 dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
642 GTK_DIALOG_DESTROY_WITH_PARENT,
644 GTK_BUTTONS_CLOSE, "%s", intro_text);
645 g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
646 G_CALLBACK(gtk_widget_destroy),
648 gtk_widget_show_all(dialog);
651 void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
654 const gchar *about_text =
655 "gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
656 "Based on the source code from Roman Zippel.\n";
658 dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
659 GTK_DIALOG_DESTROY_WITH_PARENT,
661 GTK_BUTTONS_CLOSE, "%s", about_text);
662 g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
663 G_CALLBACK(gtk_widget_destroy),
665 gtk_widget_show_all(dialog);
668 void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
671 const gchar *license_text =
672 "gkc is released under the terms of the GNU GPL v2.\n"
673 "For more information, please see the source code or\n"
674 "visit http://www.fsf.org/licenses/licenses.html\n";
676 dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
677 GTK_DIALOG_DESTROY_WITH_PARENT,
679 GTK_BUTTONS_CLOSE, "%s", license_text);
680 g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
681 G_CALLBACK(gtk_widget_destroy),
683 gtk_widget_show_all(dialog);
686 void on_back_clicked(GtkButton * button, gpointer user_data)
688 enum prop_type ptype;
690 current = current->parent;
691 ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
693 current = current->parent;
696 if (current == &rootmenu)
697 gtk_widget_set_sensitive(back_btn, FALSE);
700 void on_load_clicked(GtkButton * button, gpointer user_data)
702 on_load1_activate(NULL, user_data);
705 void on_single_clicked(GtkButton * button, gpointer user_data)
707 view_mode = SINGLE_VIEW;
708 gtk_widget_hide(tree1_w);
713 void on_split_clicked(GtkButton * button, gpointer user_data)
716 view_mode = SPLIT_VIEW;
717 gtk_widget_show(tree1_w);
718 gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
719 gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
721 gtk_tree_store_clear(tree2);
724 /* Disable back btn, like in full mode. */
725 gtk_widget_set_sensitive(back_btn, FALSE);
728 void on_full_clicked(GtkButton * button, gpointer user_data)
730 view_mode = FULL_VIEW;
731 gtk_widget_hide(tree1_w);
733 gtk_tree_store_clear(tree2);
734 display_tree(&rootmenu);
735 gtk_widget_set_sensitive(back_btn, FALSE);
738 void on_collapse_clicked(GtkButton * button, gpointer user_data)
740 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
743 void on_expand_clicked(GtkButton * button, gpointer user_data)
745 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
748 /* CTree Callbacks */
750 /* Change hex/int/string value in the cell */
751 static void renderer_edited(GtkCellRendererText * cell,
752 const gchar * path_string,
753 const gchar * new_text, gpointer user_data)
755 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
757 const char *old_def, *new_def;
761 if (!gtk_tree_model_get_iter(model2, &iter, path))
764 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
767 gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
770 sym_set_string_value(sym, new_def);
772 update_tree(&rootmenu, NULL);
774 gtk_tree_path_free(path);
777 /* Change the value of a symbol and update the tree */
778 static void change_sym_value(struct menu *menu, gint col)
780 struct symbol *sym = menu->sym;
788 else if (col == COL_MOD)
790 else if (col == COL_YES)
795 switch (sym_get_type(sym)) {
798 if (!sym_tristate_within_range(sym, newval))
800 sym_set_tristate_value(sym, newval);
801 if (view_mode == FULL_VIEW)
802 update_tree(&rootmenu, NULL);
803 else if (view_mode == SPLIT_VIEW) {
804 update_tree(browsed, NULL);
807 else if (view_mode == SINGLE_VIEW)
808 display_tree_part(); //fixme: keep exp/coll
818 static void toggle_sym_value(struct menu *menu)
823 sym_toggle_tristate_value(menu->sym);
824 if (view_mode == FULL_VIEW)
825 update_tree(&rootmenu, NULL);
826 else if (view_mode == SPLIT_VIEW) {
827 update_tree(browsed, NULL);
830 else if (view_mode == SINGLE_VIEW)
831 display_tree_part(); //fixme: keep exp/coll
834 static gint column2index(GtkTreeViewColumn * column)
838 for (i = 0; i < COL_NUMBER; i++) {
839 GtkTreeViewColumn *col;
841 col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
849 /* User click: update choice (full) or goes down (single) */
851 on_treeview2_button_press_event(GtkWidget * widget,
852 GdkEventButton * event, gpointer user_data)
854 GtkTreeView *view = GTK_TREE_VIEW(widget);
856 GtkTreeViewColumn *column;
861 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
862 gint tx = (gint) event->x;
863 gint ty = (gint) event->y;
866 gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
869 gtk_tree_view_get_cursor(view, &path, &column);
874 if (!gtk_tree_model_get_iter(model2, &iter, path))
876 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
878 col = column2index(column);
879 if (event->type == GDK_2BUTTON_PRESS) {
880 enum prop_type ptype;
881 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
883 if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
884 // goes down into menu
887 gtk_widget_set_sensitive(back_btn, TRUE);
888 } else if (col == COL_OPTION) {
889 toggle_sym_value(menu);
890 gtk_tree_view_expand_row(view, path, TRUE);
893 if (col == COL_VALUE) {
894 toggle_sym_value(menu);
895 gtk_tree_view_expand_row(view, path, TRUE);
896 } else if (col == COL_NO || col == COL_MOD
898 change_sym_value(menu, col);
899 gtk_tree_view_expand_row(view, path, TRUE);
906 /* Key pressed: update choice */
908 on_treeview2_key_press_event(GtkWidget * widget,
909 GdkEventKey * event, gpointer user_data)
911 GtkTreeView *view = GTK_TREE_VIEW(widget);
913 GtkTreeViewColumn *column;
918 gtk_tree_view_get_cursor(view, &path, &column);
922 if (event->keyval == GDK_space) {
923 if (gtk_tree_view_row_expanded(view, path))
924 gtk_tree_view_collapse_row(view, path);
926 gtk_tree_view_expand_row(view, path, FALSE);
929 if (event->keyval == GDK_KP_Enter) {
931 if (widget == tree1_w)
934 gtk_tree_model_get_iter(model2, &iter, path);
935 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
937 if (!strcasecmp(event->string, "n"))
939 else if (!strcasecmp(event->string, "m"))
941 else if (!strcasecmp(event->string, "y"))
945 change_sym_value(menu, col);
950 /* Row selection changed: update help */
952 on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
954 GtkTreeSelection *selection;
958 selection = gtk_tree_view_get_selection(treeview);
959 if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
960 gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
961 text_insert_help(menu);
965 /* User click: display sub-tree in the right frame. */
967 on_treeview1_button_press_event(GtkWidget * widget,
968 GdkEventButton * event, gpointer user_data)
970 GtkTreeView *view = GTK_TREE_VIEW(widget);
972 GtkTreeViewColumn *column;
976 gint tx = (gint) event->x;
977 gint ty = (gint) event->y;
980 gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
985 gtk_tree_model_get_iter(model1, &iter, path);
986 gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
988 if (event->type == GDK_2BUTTON_PRESS) {
989 toggle_sym_value(menu);
997 gtk_widget_realize(tree2_w);
998 gtk_tree_view_set_cursor(view, path, NULL, FALSE);
999 gtk_widget_grab_focus(tree2_w);
1004 /* Fill a row of strings */
1005 static gchar **fill_row(struct menu *menu)
1007 static gchar *row[COL_NUMBER];
1008 struct symbol *sym = menu->sym;
1012 enum prop_type ptype;
1015 for (i = COL_OPTION; i <= COL_COLOR; i++)
1017 bzero(row, sizeof(row));
1020 g_strdup_printf("%s %s", menu_get_prompt(menu),
1021 sym && !sym_has_value(sym) ? "(NEW)" : "");
1023 if (opt_mode == OPT_ALL && !menu_is_visible(menu))
1024 row[COL_COLOR] = g_strdup("DarkGray");
1025 else if (opt_mode == OPT_PROMPT &&
1026 menu_has_prompt(menu) && !menu_is_visible(menu))
1027 row[COL_COLOR] = g_strdup("DarkGray");
1029 row[COL_COLOR] = g_strdup("Black");
1031 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
1034 row[COL_PIXBUF] = (gchar *) xpm_menu;
1035 if (view_mode == SINGLE_VIEW)
1036 row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
1037 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1040 row[COL_PIXBUF] = (gchar *) xpm_void;
1041 row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1042 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1045 row[COL_PIXBUF] = (gchar *) xpm_void;
1046 row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
1047 row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1053 row[COL_NAME] = g_strdup(sym->name);
1055 sym_calc_value(sym);
1056 sym->flags &= ~SYMBOL_CHANGED;
1058 if (sym_is_choice(sym)) { // parse childs for getting final value
1060 struct symbol *def_sym = sym_get_choice_value(sym);
1061 struct menu *def_menu = NULL;
1063 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1065 for (child = menu->list; child; child = child->next) {
1066 if (menu_is_visible(child)
1067 && child->sym == def_sym)
1073 g_strdup(menu_get_prompt(def_menu));
1075 if (sym->flags & SYMBOL_CHOICEVAL)
1076 row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
1078 stype = sym_get_type(sym);
1081 if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
1082 row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
1083 if (sym_is_choice(sym))
1087 val = sym_get_tristate_value(sym);
1090 row[COL_NO] = g_strdup("N");
1091 row[COL_VALUE] = g_strdup("N");
1092 row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
1093 row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1096 row[COL_MOD] = g_strdup("M");
1097 row[COL_VALUE] = g_strdup("M");
1098 row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
1101 row[COL_YES] = g_strdup("Y");
1102 row[COL_VALUE] = g_strdup("Y");
1103 row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
1104 row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
1108 if (val != no && sym_tristate_within_range(sym, no))
1109 row[COL_NO] = g_strdup("_");
1110 if (val != mod && sym_tristate_within_range(sym, mod))
1111 row[COL_MOD] = g_strdup("_");
1112 if (val != yes && sym_tristate_within_range(sym, yes))
1113 row[COL_YES] = g_strdup("_");
1118 def = sym_get_string_value(sym);
1119 row[COL_VALUE] = g_strdup(def);
1120 row[COL_EDIT] = GINT_TO_POINTER(TRUE);
1121 row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
1128 /* Set the node content with a row of strings */
1129 static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
1135 pix = gdk_pixbuf_new_from_xpm_data((const char **)
1138 gdk_color_parse(row[COL_COLOR], &color);
1139 gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
1140 FALSE, FALSE, &success);
1142 gtk_tree_store_set(tree, node,
1143 COL_OPTION, row[COL_OPTION],
1144 COL_NAME, row[COL_NAME],
1145 COL_NO, row[COL_NO],
1146 COL_MOD, row[COL_MOD],
1147 COL_YES, row[COL_YES],
1148 COL_VALUE, row[COL_VALUE],
1149 COL_MENU, (gpointer) menu,
1151 COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
1153 COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
1154 COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
1155 COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
1156 COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
1157 COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
1160 g_object_unref(pix);
1163 /* Add a node to the tree */
1164 static void place_node(struct menu *menu, char **row)
1166 GtkTreeIter *parent = parents[indent - 1];
1167 GtkTreeIter *node = parents[indent];
1169 gtk_tree_store_append(tree, node, parent);
1170 set_node(node, menu, row);
1173 /* Find a node in the GTK+ tree */
1174 static GtkTreeIter found;
1177 * Find a menu in the GtkTree starting at parent.
1179 GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
1180 struct menu *tofind)
1183 GtkTreeIter *child = &iter;
1187 valid = gtk_tree_model_iter_children(model2, child, parent);
1191 gtk_tree_model_get(model2, child, 6, &menu, -1);
1193 if (menu == tofind) {
1194 memcpy(&found, child, sizeof(GtkTreeIter));
1198 ret = gtktree_iter_find_node(child, tofind);
1202 valid = gtk_tree_model_iter_next(model2, child);
1209 * Update the tree by adding/removing entries
1210 * Does not change other nodes
1212 static void update_tree(struct menu *src, GtkTreeIter * dst)
1214 struct menu *child1;
1215 GtkTreeIter iter, tmp;
1216 GtkTreeIter *child2 = &iter;
1218 GtkTreeIter *sibling;
1220 struct menu *menu1, *menu2;
1222 if (src == &rootmenu)
1225 valid = gtk_tree_model_iter_children(model2, child2, dst);
1226 for (child1 = src->list; child1; child1 = child1->next) {
1233 gtk_tree_model_get(model2, child2, COL_MENU,
1236 menu2 = NULL; // force adding of a first child
1239 printf("%*c%s | %s\n", indent, ' ',
1240 menu1 ? menu_get_prompt(menu1) : "nil",
1241 menu2 ? menu_get_prompt(menu2) : "nil");
1244 if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
1245 (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
1246 (opt_mode == OPT_ALL && !menu_get_prompt(child1))) {
1249 if (gtktree_iter_find_node(dst, menu1) != NULL) {
1250 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1251 valid = gtk_tree_model_iter_next(model2,
1253 gtk_tree_store_remove(tree2, &tmp);
1255 return; /* next parent */
1257 goto reparse; /* next child */
1262 if (menu1 != menu2) {
1263 if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node
1264 if (!valid && !menu2)
1268 gtk_tree_store_insert_before(tree2,
1271 set_node(child2, menu1, fill_row(menu1));
1274 } else { // remove node
1275 memcpy(&tmp, child2, sizeof(GtkTreeIter));
1276 valid = gtk_tree_model_iter_next(model2,
1278 gtk_tree_store_remove(tree2, &tmp);
1280 return; // next parent
1282 goto reparse; // next child
1284 } else if (sym && (sym->flags & SYMBOL_CHANGED)) {
1285 set_node(child2, menu1, fill_row(menu1));
1289 update_tree(child1, child2);
1292 valid = gtk_tree_model_iter_next(model2, child2);
1296 /* Display the whole tree (single/split/full view) */
1297 static void display_tree(struct menu *menu)
1300 struct property *prop;
1302 enum prop_type ptype;
1304 if (menu == &rootmenu) {
1306 current = &rootmenu;
1309 for (child = menu->list; child; child = child->next) {
1310 prop = child->prompt;
1312 ptype = prop ? prop->type : P_UNKNOWN;
1315 sym->flags &= ~SYMBOL_CHANGED;
1317 if ((view_mode == SPLIT_VIEW)
1318 && !(child->flags & MENU_ROOT) && (tree == tree1))
1321 if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
1325 if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
1326 (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
1327 (opt_mode == OPT_ALL && menu_get_prompt(child)))
1328 place_node(child, fill_row(child));
1330 printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
1331 printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
1332 printf("%s", prop_get_type_name(ptype));
1335 printf("%s", sym_type_name(sym->type));
1337 printf("%s", dbg_sym_flags(sym->flags));
1342 if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
1346 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1347 || (view_mode == FULL_VIEW)
1348 || (view_mode == SPLIT_VIEW))*/
1350 /* Change paned position if the view is not in 'split mode' */
1351 if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
1352 gtk_paned_set_position(GTK_PANED(hpaned), 0);
1355 if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
1356 || (view_mode == FULL_VIEW)
1357 || (view_mode == SPLIT_VIEW)) {
1359 display_tree(child);
1365 /* Display a part of the tree starting at current node (single/split view) */
1366 static void display_tree_part(void)
1369 gtk_tree_store_clear(tree2);
1370 if (view_mode == SINGLE_VIEW)
1371 display_tree(current);
1372 else if (view_mode == SPLIT_VIEW)
1373 display_tree(browsed);
1374 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
1377 /* Display the list in the left frame (split view) */
1378 static void display_list(void)
1381 gtk_tree_store_clear(tree1);
1384 display_tree(&rootmenu);
1385 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
1389 void fixup_rootmenu(struct menu *menu)
1392 static int menu_cnt = 0;
1394 menu->flags |= MENU_ROOT;
1395 for (child = menu->list; child; child = child->next) {
1396 if (child->prompt && child->prompt->type == P_MENU) {
1398 fixup_rootmenu(child);
1400 } else if (!menu_cnt)
1401 fixup_rootmenu(child);
1406 int main(int ac, char *av[])
1417 //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1418 //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1420 /* Determine GUI path */
1421 env = getenv(SRCTREE);
1423 glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
1424 else if (av[0][0] == '/')
1425 glade_file = g_strconcat(av[0], ".glade", NULL);
1427 glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
1430 if (ac > 1 && av[1][0] == '-') {
1436 conf_set_message_callback(NULL);
1440 printf("%s [-s] <config>\n", av[0]);
1448 fixup_rootmenu(&rootmenu);
1451 /* Load the interface and connect signals */
1452 init_main_window(glade_file);
1457 switch (view_mode) {
1459 display_tree_part();
1465 display_tree(&rootmenu);
1474 static void conf_changed(void)
1476 bool changed = conf_get_changed();
1477 gtk_widget_set_sensitive(save_btn, changed);
1478 gtk_widget_set_sensitive(save_menu_item, changed);