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
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
;
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
;
51 GtkTextTag
*tag1
, *tag2
;
54 GtkTreeStore
*tree1
, *tree2
, *tree
;
55 GtkTreeModel
*model1
, *model2
;
56 static GtkTreeIter
*parents
[256];
59 static struct menu
*current
; // current node for SINGLE view
60 static struct menu
*browsed
; // browsed node for SPLIT view
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
,
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);
77 /* Helping/Debugging Functions */
80 const char *dbg_print_stype(int val
)
87 strcpy(buf
, "unknown");
89 strcpy(buf
, "boolean");
90 if (val
== S_TRISTATE
)
91 strcpy(buf
, "tristate");
97 strcpy(buf
, "string");
108 const char *dbg_print_flags(int val
)
110 static char buf
[256];
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_VALID
)
123 strcat(buf
, "valid/");
124 if (val
& SYMBOL_OPTIONAL
)
125 strcat(buf
, "optional/");
126 if (val
& SYMBOL_WRITE
)
127 strcat(buf
, "write/");
128 if (val
& SYMBOL_CHANGED
)
129 strcat(buf
, "changed/");
130 if (val
& SYMBOL_AUTO
)
131 strcat(buf
, "auto/");
133 buf
[strlen(buf
) - 1] = '\0';
141 const char *dbg_print_ptype(int val
)
143 static char buf
[256];
147 if (val
== P_UNKNOWN
)
148 strcpy(buf
, "unknown");
150 strcpy(buf
, "prompt");
151 if (val
== P_COMMENT
)
152 strcpy(buf
, "comment");
155 if (val
== P_DEFAULT
)
156 strcpy(buf
, "default");
158 strcpy(buf
, "choice");
168 void replace_button_icon(GladeXML
* xml
, GdkDrawable
* window
,
169 GtkStyle
* style
, gchar
* btn_name
, gchar
** xpm
)
173 GtkToolButton
*button
;
176 pixmap
= gdk_pixmap_create_from_xpm_d(window
, &mask
,
177 &style
->bg
[GTK_STATE_NORMAL
],
180 button
= GTK_TOOL_BUTTON(glade_xml_get_widget(xml
, btn_name
));
181 image
= gtk_image_new_from_pixmap(pixmap
, mask
);
182 gtk_widget_show(image
);
183 gtk_tool_button_set_icon_widget(button
, image
);
186 /* Main Window Initialization */
187 void init_main_window(const gchar
* glade_file
)
191 GtkTextBuffer
*txtbuf
;
195 xml
= glade_xml_new(glade_file
, "window1", NULL
);
197 g_error(_("GUI loading failed !\n"));
198 glade_xml_signal_autoconnect(xml
);
200 main_wnd
= glade_xml_get_widget(xml
, "window1");
201 hpaned
= glade_xml_get_widget(xml
, "hpaned1");
202 vpaned
= glade_xml_get_widget(xml
, "vpaned1");
203 tree1_w
= glade_xml_get_widget(xml
, "treeview1");
204 tree2_w
= glade_xml_get_widget(xml
, "treeview2");
205 text_w
= glade_xml_get_widget(xml
, "textview3");
207 back_btn
= glade_xml_get_widget(xml
, "button1");
208 gtk_widget_set_sensitive(back_btn
, FALSE
);
210 widget
= glade_xml_get_widget(xml
, "show_name1");
211 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
214 widget
= glade_xml_get_widget(xml
, "show_range1");
215 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
218 widget
= glade_xml_get_widget(xml
, "show_data1");
219 gtk_check_menu_item_set_active((GtkCheckMenuItem
*) widget
,
222 save_btn
= glade_xml_get_widget(xml
, "button3");
223 save_menu_item
= glade_xml_get_widget(xml
, "save1");
224 conf_set_changed_callback(conf_changed
);
226 style
= gtk_widget_get_style(main_wnd
);
227 widget
= glade_xml_get_widget(xml
, "toolbar1");
229 #if 0 /* Use stock Gtk icons instead */
230 replace_button_icon(xml
, main_wnd
->window
, style
,
231 "button1", (gchar
**) xpm_back
);
232 replace_button_icon(xml
, main_wnd
->window
, style
,
233 "button2", (gchar
**) xpm_load
);
234 replace_button_icon(xml
, main_wnd
->window
, style
,
235 "button3", (gchar
**) xpm_save
);
237 replace_button_icon(xml
, main_wnd
->window
, style
,
238 "button4", (gchar
**) xpm_single_view
);
239 replace_button_icon(xml
, main_wnd
->window
, style
,
240 "button5", (gchar
**) xpm_split_view
);
241 replace_button_icon(xml
, main_wnd
->window
, style
,
242 "button6", (gchar
**) xpm_tree_view
);
247 widget
= glade_xml_get_widget(xml
, "button4");
248 g_signal_emit_by_name(widget
, "clicked");
251 widget
= glade_xml_get_widget(xml
, "button5");
252 g_signal_emit_by_name(widget
, "clicked");
255 widget
= glade_xml_get_widget(xml
, "button6");
256 g_signal_emit_by_name(widget
, "clicked");
260 txtbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
261 tag1
= gtk_text_buffer_create_tag(txtbuf
, "mytag1",
263 "weight", PANGO_WEIGHT_BOLD
,
265 tag2
= gtk_text_buffer_create_tag(txtbuf
, "mytag2",
266 /*"style", PANGO_STYLE_OBLIQUE, */
269 sprintf(title
, _("Linux Kernel v%s Configuration"),
270 getenv("KERNELVERSION"));
271 gtk_window_set_title(GTK_WINDOW(main_wnd
), title
);
273 gtk_widget_show(main_wnd
);
276 void init_tree_model(void)
280 tree
= tree2
= gtk_tree_store_new(COL_NUMBER
,
281 G_TYPE_STRING
, G_TYPE_STRING
,
282 G_TYPE_STRING
, G_TYPE_STRING
,
283 G_TYPE_STRING
, G_TYPE_STRING
,
284 G_TYPE_POINTER
, GDK_TYPE_COLOR
,
285 G_TYPE_BOOLEAN
, GDK_TYPE_PIXBUF
,
286 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
287 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
289 model2
= GTK_TREE_MODEL(tree2
);
291 for (parents
[0] = NULL
, i
= 1; i
< 256; i
++)
292 parents
[i
] = (GtkTreeIter
*) g_malloc(sizeof(GtkTreeIter
));
294 tree1
= gtk_tree_store_new(COL_NUMBER
,
295 G_TYPE_STRING
, G_TYPE_STRING
,
296 G_TYPE_STRING
, G_TYPE_STRING
,
297 G_TYPE_STRING
, G_TYPE_STRING
,
298 G_TYPE_POINTER
, GDK_TYPE_COLOR
,
299 G_TYPE_BOOLEAN
, GDK_TYPE_PIXBUF
,
300 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
301 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
,
303 model1
= GTK_TREE_MODEL(tree1
);
306 void init_left_tree(void)
308 GtkTreeView
*view
= GTK_TREE_VIEW(tree1_w
);
309 GtkCellRenderer
*renderer
;
310 GtkTreeSelection
*sel
;
311 GtkTreeViewColumn
*column
;
313 gtk_tree_view_set_model(view
, model1
);
314 gtk_tree_view_set_headers_visible(view
, TRUE
);
315 gtk_tree_view_set_rules_hint(view
, FALSE
);
317 column
= gtk_tree_view_column_new();
318 gtk_tree_view_append_column(view
, column
);
319 gtk_tree_view_column_set_title(column
, _("Options"));
321 renderer
= gtk_cell_renderer_toggle_new();
322 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
324 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
326 "active", COL_BTNACT
,
327 "inconsistent", COL_BTNINC
,
328 "visible", COL_BTNVIS
,
329 "radio", COL_BTNRAD
, NULL
);
330 renderer
= gtk_cell_renderer_text_new();
331 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
333 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
339 sel
= gtk_tree_view_get_selection(view
);
340 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_SINGLE
);
341 gtk_widget_realize(tree1_w
);
344 static void renderer_edited(GtkCellRendererText
* cell
,
345 const gchar
* path_string
,
346 const gchar
* new_text
, gpointer user_data
);
347 static void renderer_toggled(GtkCellRendererToggle
* cellrenderertoggle
,
348 gchar
* arg1
, gpointer user_data
);
350 void init_right_tree(void)
352 GtkTreeView
*view
= GTK_TREE_VIEW(tree2_w
);
353 GtkCellRenderer
*renderer
;
354 GtkTreeSelection
*sel
;
355 GtkTreeViewColumn
*column
;
358 gtk_tree_view_set_model(view
, model2
);
359 gtk_tree_view_set_headers_visible(view
, TRUE
);
360 gtk_tree_view_set_rules_hint(view
, FALSE
);
362 column
= gtk_tree_view_column_new();
363 gtk_tree_view_append_column(view
, column
);
364 gtk_tree_view_column_set_title(column
, _("Options"));
366 renderer
= gtk_cell_renderer_pixbuf_new();
367 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
369 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
371 "pixbuf", COL_PIXBUF
,
372 "visible", COL_PIXVIS
, NULL
);
373 renderer
= gtk_cell_renderer_toggle_new();
374 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
376 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
378 "active", COL_BTNACT
,
379 "inconsistent", COL_BTNINC
,
380 "visible", COL_BTNVIS
,
381 "radio", COL_BTNRAD
, NULL
);
382 /*g_signal_connect(G_OBJECT(renderer), "toggled",
383 G_CALLBACK(renderer_toggled), NULL); */
384 renderer
= gtk_cell_renderer_text_new();
385 gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column
),
387 gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column
),
393 renderer
= gtk_cell_renderer_text_new();
394 gtk_tree_view_insert_column_with_attributes(view
, -1,
399 renderer
= gtk_cell_renderer_text_new();
400 gtk_tree_view_insert_column_with_attributes(view
, -1,
405 renderer
= gtk_cell_renderer_text_new();
406 gtk_tree_view_insert_column_with_attributes(view
, -1,
411 renderer
= gtk_cell_renderer_text_new();
412 gtk_tree_view_insert_column_with_attributes(view
, -1,
417 renderer
= gtk_cell_renderer_text_new();
418 gtk_tree_view_insert_column_with_attributes(view
, -1,
419 _("Value"), renderer
,
425 g_signal_connect(G_OBJECT(renderer
), "edited",
426 G_CALLBACK(renderer_edited
), NULL
);
428 column
= gtk_tree_view_get_column(view
, COL_NAME
);
429 gtk_tree_view_column_set_visible(column
, show_name
);
430 column
= gtk_tree_view_get_column(view
, COL_NO
);
431 gtk_tree_view_column_set_visible(column
, show_range
);
432 column
= gtk_tree_view_get_column(view
, COL_MOD
);
433 gtk_tree_view_column_set_visible(column
, show_range
);
434 column
= gtk_tree_view_get_column(view
, COL_YES
);
435 gtk_tree_view_column_set_visible(column
, show_range
);
436 column
= gtk_tree_view_get_column(view
, COL_VALUE
);
437 gtk_tree_view_column_set_visible(column
, show_value
);
440 for (i
= 0; i
< COL_VALUE
; i
++) {
441 column
= gtk_tree_view_get_column(view
, i
);
442 gtk_tree_view_column_set_resizable(column
, TRUE
);
446 sel
= gtk_tree_view_get_selection(view
);
447 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_SINGLE
);
451 /* Utility Functions */
454 static void text_insert_help(struct menu
*menu
)
456 GtkTextBuffer
*buffer
;
457 GtkTextIter start
, end
;
458 const char *prompt
= _(menu_get_prompt(menu
));
459 struct gstr help
= str_new();
461 menu_get_ext_help(menu
, &help
);
463 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
464 gtk_text_buffer_get_bounds(buffer
, &start
, &end
);
465 gtk_text_buffer_delete(buffer
, &start
, &end
);
466 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w
), 15);
468 gtk_text_buffer_get_end_iter(buffer
, &end
);
469 gtk_text_buffer_insert_with_tags(buffer
, &end
, prompt
, -1, tag1
,
471 gtk_text_buffer_insert_at_cursor(buffer
, "\n\n", 2);
472 gtk_text_buffer_get_end_iter(buffer
, &end
);
473 gtk_text_buffer_insert_with_tags(buffer
, &end
, str_get(&help
), -1, tag2
,
479 static void text_insert_msg(const char *title
, const char *message
)
481 GtkTextBuffer
*buffer
;
482 GtkTextIter start
, end
;
483 const char *msg
= message
;
485 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w
));
486 gtk_text_buffer_get_bounds(buffer
, &start
, &end
);
487 gtk_text_buffer_delete(buffer
, &start
, &end
);
488 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w
), 15);
490 gtk_text_buffer_get_end_iter(buffer
, &end
);
491 gtk_text_buffer_insert_with_tags(buffer
, &end
, title
, -1, tag1
,
493 gtk_text_buffer_insert_at_cursor(buffer
, "\n\n", 2);
494 gtk_text_buffer_get_end_iter(buffer
, &end
);
495 gtk_text_buffer_insert_with_tags(buffer
, &end
, msg
, -1, tag2
,
500 /* Main Windows Callbacks */
502 void on_save_activate(GtkMenuItem
* menuitem
, gpointer user_data
);
503 gboolean
on_window1_delete_event(GtkWidget
* widget
, GdkEvent
* event
,
506 GtkWidget
*dialog
, *label
;
509 if (!conf_get_changed())
512 dialog
= gtk_dialog_new_with_buttons(_("Warning !"),
513 GTK_WINDOW(main_wnd
),
516 GTK_DIALOG_DESTROY_WITH_PARENT
),
522 GTK_RESPONSE_CANCEL
, NULL
);
523 gtk_dialog_set_default_response(GTK_DIALOG(dialog
),
524 GTK_RESPONSE_CANCEL
);
526 label
= gtk_label_new(_("\nSave configuration ?\n"));
527 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), label
);
528 gtk_widget_show(label
);
530 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
532 case GTK_RESPONSE_YES
:
533 on_save_activate(NULL
, NULL
);
535 case GTK_RESPONSE_NO
:
537 case GTK_RESPONSE_CANCEL
:
538 case GTK_RESPONSE_DELETE_EVENT
:
540 gtk_widget_destroy(dialog
);
548 void on_window1_destroy(GtkObject
* object
, gpointer user_data
)
555 on_window1_size_request(GtkWidget
* widget
,
556 GtkRequisition
* requisition
, gpointer user_data
)
561 if (widget
->window
== NULL
)
562 gtk_window_get_default_size(GTK_WINDOW(main_wnd
), &w
, &h
);
564 gdk_window_get_size(widget
->window
, &w
, &h
);
570 gtk_paned_set_position(GTK_PANED(vpaned
), 2 * h
/ 3);
574 /* Menu & Toolbar Callbacks */
578 load_filename(GtkFileSelection
* file_selector
, gpointer user_data
)
582 fn
= gtk_file_selection_get_filename(GTK_FILE_SELECTION
586 text_insert_msg(_("Error"), _("Unable to load configuration !"));
588 display_tree(&rootmenu
);
591 void on_load1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
595 fs
= gtk_file_selection_new(_("Load file..."));
596 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
),
598 G_CALLBACK(load_filename
), (gpointer
) fs
);
599 g_signal_connect_swapped(GTK_OBJECT
600 (GTK_FILE_SELECTION(fs
)->ok_button
),
601 "clicked", G_CALLBACK(gtk_widget_destroy
),
603 g_signal_connect_swapped(GTK_OBJECT
604 (GTK_FILE_SELECTION(fs
)->cancel_button
),
605 "clicked", G_CALLBACK(gtk_widget_destroy
),
611 void on_save_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
613 if (conf_write(NULL
))
614 text_insert_msg(_("Error"), _("Unable to save configuration !"));
619 store_filename(GtkFileSelection
* file_selector
, gpointer user_data
)
623 fn
= gtk_file_selection_get_filename(GTK_FILE_SELECTION
627 text_insert_msg(_("Error"), _("Unable to save configuration !"));
629 gtk_widget_destroy(GTK_WIDGET(user_data
));
632 void on_save_as1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
636 fs
= gtk_file_selection_new(_("Save file as..."));
637 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
),
639 G_CALLBACK(store_filename
), (gpointer
) fs
);
640 g_signal_connect_swapped(GTK_OBJECT
641 (GTK_FILE_SELECTION(fs
)->ok_button
),
642 "clicked", G_CALLBACK(gtk_widget_destroy
),
644 g_signal_connect_swapped(GTK_OBJECT
645 (GTK_FILE_SELECTION(fs
)->cancel_button
),
646 "clicked", G_CALLBACK(gtk_widget_destroy
),
652 void on_quit1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
654 if (!on_window1_delete_event(NULL
, NULL
, NULL
))
655 gtk_widget_destroy(GTK_WIDGET(main_wnd
));
659 void on_show_name1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
661 GtkTreeViewColumn
*col
;
663 show_name
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
664 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_NAME
);
666 gtk_tree_view_column_set_visible(col
, show_name
);
670 void on_show_range1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
672 GtkTreeViewColumn
*col
;
674 show_range
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
675 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_NO
);
677 gtk_tree_view_column_set_visible(col
, show_range
);
678 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_MOD
);
680 gtk_tree_view_column_set_visible(col
, show_range
);
681 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_YES
);
683 gtk_tree_view_column_set_visible(col
, show_range
);
688 void on_show_data1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
690 GtkTreeViewColumn
*col
;
692 show_value
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
693 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), COL_VALUE
);
695 gtk_tree_view_column_set_visible(col
, show_value
);
700 on_show_all_options1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
702 show_all
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
704 gtk_tree_store_clear(tree2
);
705 display_tree(&rootmenu
); // instead of update_tree to speed-up
710 on_show_debug_info1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
712 show_debug
= GTK_CHECK_MENU_ITEM(menuitem
)->active
;
713 update_tree(&rootmenu
, NULL
);
717 void on_introduction1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
720 const gchar
*intro_text
= _(
721 "Welcome to gkc, the GTK+ graphical kernel configuration tool\n"
723 "For each option, a blank box indicates the feature is disabled, a\n"
724 "check indicates it is enabled, and a dot indicates that it is to\n"
725 "be compiled as a module. Clicking on the box will cycle through the three states.\n"
727 "If you do not see an option (e.g., a device driver) that you\n"
728 "believe should be present, try turning on Show All Options\n"
729 "under the Options menu.\n"
730 "Although there is no cross reference yet to help you figure out\n"
731 "what other options must be enabled to support the option you\n"
732 "are interested in, you can still view the help of a grayed-out\n"
735 "Toggling Show Debug Info under the Options menu will show \n"
736 "the dependencies, which you can then match by examining other options.");
738 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
739 GTK_DIALOG_DESTROY_WITH_PARENT
,
741 GTK_BUTTONS_CLOSE
, intro_text
);
742 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
743 G_CALLBACK(gtk_widget_destroy
),
745 gtk_widget_show_all(dialog
);
749 void on_about1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
752 const gchar
*about_text
=
753 _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
754 "Based on the source code from Roman Zippel.\n");
756 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
757 GTK_DIALOG_DESTROY_WITH_PARENT
,
759 GTK_BUTTONS_CLOSE
, about_text
);
760 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
761 G_CALLBACK(gtk_widget_destroy
),
763 gtk_widget_show_all(dialog
);
767 void on_license1_activate(GtkMenuItem
* menuitem
, gpointer user_data
)
770 const gchar
*license_text
=
771 _("gkc is released under the terms of the GNU GPL v2.\n"
772 "For more information, please see the source code or\n"
773 "visit http://www.fsf.org/licenses/licenses.html\n");
775 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_wnd
),
776 GTK_DIALOG_DESTROY_WITH_PARENT
,
778 GTK_BUTTONS_CLOSE
, license_text
);
779 g_signal_connect_swapped(GTK_OBJECT(dialog
), "response",
780 G_CALLBACK(gtk_widget_destroy
),
782 gtk_widget_show_all(dialog
);
786 void on_back_clicked(GtkButton
* button
, gpointer user_data
)
788 enum prop_type ptype
;
790 current
= current
->parent
;
791 ptype
= current
->prompt
? current
->prompt
->type
: P_UNKNOWN
;
793 current
= current
->parent
;
796 if (current
== &rootmenu
)
797 gtk_widget_set_sensitive(back_btn
, FALSE
);
801 void on_load_clicked(GtkButton
* button
, gpointer user_data
)
803 on_load1_activate(NULL
, user_data
);
807 void on_single_clicked(GtkButton
* button
, gpointer user_data
)
809 view_mode
= SINGLE_VIEW
;
810 gtk_paned_set_position(GTK_PANED(hpaned
), 0);
811 gtk_widget_hide(tree1_w
);
817 void on_split_clicked(GtkButton
* button
, gpointer user_data
)
820 view_mode
= SPLIT_VIEW
;
821 gtk_widget_show(tree1_w
);
822 gtk_window_get_default_size(GTK_WINDOW(main_wnd
), &w
, &h
);
823 gtk_paned_set_position(GTK_PANED(hpaned
), w
/ 2);
825 gtk_tree_store_clear(tree2
);
828 /* Disable back btn, like in full mode. */
829 gtk_widget_set_sensitive(back_btn
, FALSE
);
833 void on_full_clicked(GtkButton
* button
, gpointer user_data
)
835 view_mode
= FULL_VIEW
;
836 gtk_paned_set_position(GTK_PANED(hpaned
), 0);
837 gtk_widget_hide(tree1_w
);
839 gtk_tree_store_clear(tree2
);
840 display_tree(&rootmenu
);
841 gtk_widget_set_sensitive(back_btn
, FALSE
);
845 void on_collapse_clicked(GtkButton
* button
, gpointer user_data
)
847 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w
));
851 void on_expand_clicked(GtkButton
* button
, gpointer user_data
)
853 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w
));
857 /* CTree Callbacks */
859 /* Change hex/int/string value in the cell */
860 static void renderer_edited(GtkCellRendererText
* cell
,
861 const gchar
* path_string
,
862 const gchar
* new_text
, gpointer user_data
)
864 GtkTreePath
*path
= gtk_tree_path_new_from_string(path_string
);
866 const char *old_def
, *new_def
;
870 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
873 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
876 gtk_tree_model_get(model2
, &iter
, COL_VALUE
, &old_def
, -1);
879 sym_set_string_value(sym
, new_def
);
881 update_tree(&rootmenu
, NULL
);
883 gtk_tree_path_free(path
);
886 /* Change the value of a symbol and update the tree */
887 static void change_sym_value(struct menu
*menu
, gint col
)
889 struct symbol
*sym
= menu
->sym
;
890 tristate oldval
, newval
;
897 else if (col
== COL_MOD
)
899 else if (col
== COL_YES
)
904 switch (sym_get_type(sym
)) {
907 oldval
= sym_get_tristate_value(sym
);
908 if (!sym_tristate_within_range(sym
, newval
))
910 sym_set_tristate_value(sym
, newval
);
911 if (view_mode
== FULL_VIEW
)
912 update_tree(&rootmenu
, NULL
);
913 else if (view_mode
== SPLIT_VIEW
) {
914 update_tree(browsed
, NULL
);
917 else if (view_mode
== SINGLE_VIEW
)
918 display_tree_part(); //fixme: keep exp/coll
928 static void toggle_sym_value(struct menu
*menu
)
933 sym_toggle_tristate_value(menu
->sym
);
934 if (view_mode
== FULL_VIEW
)
935 update_tree(&rootmenu
, NULL
);
936 else if (view_mode
== SPLIT_VIEW
) {
937 update_tree(browsed
, NULL
);
940 else if (view_mode
== SINGLE_VIEW
)
941 display_tree_part(); //fixme: keep exp/coll
944 static void renderer_toggled(GtkCellRendererToggle
* cell
,
945 gchar
* path_string
, gpointer user_data
)
947 GtkTreePath
*path
, *sel_path
= NULL
;
948 GtkTreeIter iter
, sel_iter
;
949 GtkTreeSelection
*sel
;
952 path
= gtk_tree_path_new_from_string(path_string
);
953 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
956 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w
));
957 if (gtk_tree_selection_get_selected(sel
, NULL
, &sel_iter
))
958 sel_path
= gtk_tree_model_get_path(model2
, &sel_iter
);
961 if (gtk_tree_path_compare(path
, sel_path
))
964 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
965 toggle_sym_value(menu
);
968 gtk_tree_path_free(sel_path
);
970 gtk_tree_path_free(path
);
973 static gint
column2index(GtkTreeViewColumn
* column
)
977 for (i
= 0; i
< COL_NUMBER
; i
++) {
978 GtkTreeViewColumn
*col
;
980 col
= gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w
), i
);
989 /* User click: update choice (full) or goes down (single) */
991 on_treeview2_button_press_event(GtkWidget
* widget
,
992 GdkEventButton
* event
, gpointer user_data
)
994 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
996 GtkTreeViewColumn
*column
;
1001 #if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
1002 gint tx
= (gint
) event
->x
;
1003 gint ty
= (gint
) event
->y
;
1006 gtk_tree_view_get_path_at_pos(view
, tx
, ty
, &path
, &column
, &cx
,
1009 gtk_tree_view_get_cursor(view
, &path
, &column
);
1014 if (!gtk_tree_model_get_iter(model2
, &iter
, path
))
1016 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
1018 col
= column2index(column
);
1019 if (event
->type
== GDK_2BUTTON_PRESS
) {
1020 enum prop_type ptype
;
1021 ptype
= menu
->prompt
? menu
->prompt
->type
: P_UNKNOWN
;
1023 if (ptype
== P_MENU
&& view_mode
!= FULL_VIEW
&& col
== COL_OPTION
) {
1024 // goes down into menu
1026 display_tree_part();
1027 gtk_widget_set_sensitive(back_btn
, TRUE
);
1028 } else if ((col
== COL_OPTION
)) {
1029 toggle_sym_value(menu
);
1030 gtk_tree_view_expand_row(view
, path
, TRUE
);
1033 if (col
== COL_VALUE
) {
1034 toggle_sym_value(menu
);
1035 gtk_tree_view_expand_row(view
, path
, TRUE
);
1036 } else if (col
== COL_NO
|| col
== COL_MOD
1037 || col
== COL_YES
) {
1038 change_sym_value(menu
, col
);
1039 gtk_tree_view_expand_row(view
, path
, TRUE
);
1046 /* Key pressed: update choice */
1048 on_treeview2_key_press_event(GtkWidget
* widget
,
1049 GdkEventKey
* event
, gpointer user_data
)
1051 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
1053 GtkTreeViewColumn
*column
;
1058 gtk_tree_view_get_cursor(view
, &path
, &column
);
1062 if (event
->keyval
== GDK_space
) {
1063 if (gtk_tree_view_row_expanded(view
, path
))
1064 gtk_tree_view_collapse_row(view
, path
);
1066 gtk_tree_view_expand_row(view
, path
, FALSE
);
1069 if (event
->keyval
== GDK_KP_Enter
) {
1071 if (widget
== tree1_w
)
1074 gtk_tree_model_get_iter(model2
, &iter
, path
);
1075 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
1077 if (!strcasecmp(event
->string
, "n"))
1079 else if (!strcasecmp(event
->string
, "m"))
1081 else if (!strcasecmp(event
->string
, "y"))
1085 change_sym_value(menu
, col
);
1091 /* Row selection changed: update help */
1093 on_treeview2_cursor_changed(GtkTreeView
* treeview
, gpointer user_data
)
1095 GtkTreeSelection
*selection
;
1099 selection
= gtk_tree_view_get_selection(treeview
);
1100 if (gtk_tree_selection_get_selected(selection
, &model2
, &iter
)) {
1101 gtk_tree_model_get(model2
, &iter
, COL_MENU
, &menu
, -1);
1102 text_insert_help(menu
);
1107 /* User click: display sub-tree in the right frame. */
1109 on_treeview1_button_press_event(GtkWidget
* widget
,
1110 GdkEventButton
* event
, gpointer user_data
)
1112 GtkTreeView
*view
= GTK_TREE_VIEW(widget
);
1114 GtkTreeViewColumn
*column
;
1118 gint tx
= (gint
) event
->x
;
1119 gint ty
= (gint
) event
->y
;
1122 gtk_tree_view_get_path_at_pos(view
, tx
, ty
, &path
, &column
, &cx
,
1127 gtk_tree_model_get_iter(model1
, &iter
, path
);
1128 gtk_tree_model_get(model1
, &iter
, COL_MENU
, &menu
, -1);
1130 if (event
->type
== GDK_2BUTTON_PRESS
) {
1131 toggle_sym_value(menu
);
1133 display_tree_part();
1136 display_tree_part();
1139 gtk_widget_realize(tree2_w
);
1140 gtk_tree_view_set_cursor(view
, path
, NULL
, FALSE
);
1141 gtk_widget_grab_focus(tree2_w
);
1147 /* Fill a row of strings */
1148 static gchar
**fill_row(struct menu
*menu
)
1150 static gchar
*row
[COL_NUMBER
];
1151 struct symbol
*sym
= menu
->sym
;
1155 enum prop_type ptype
;
1158 for (i
= COL_OPTION
; i
<= COL_COLOR
; i
++)
1160 bzero(row
, sizeof(row
));
1163 g_strdup_printf("%s %s", _(menu_get_prompt(menu
)),
1164 sym
&& sym_has_value(sym
) ? "(NEW)" : "");
1166 if (show_all
&& !menu_is_visible(menu
))
1167 row
[COL_COLOR
] = g_strdup("DarkGray");
1169 row
[COL_COLOR
] = g_strdup("Black");
1171 ptype
= menu
->prompt
? menu
->prompt
->type
: P_UNKNOWN
;
1174 row
[COL_PIXBUF
] = (gchar
*) xpm_menu
;
1175 if (view_mode
== SINGLE_VIEW
)
1176 row
[COL_PIXVIS
] = GINT_TO_POINTER(TRUE
);
1177 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1180 row
[COL_PIXBUF
] = (gchar
*) xpm_void
;
1181 row
[COL_PIXVIS
] = GINT_TO_POINTER(FALSE
);
1182 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1185 row
[COL_PIXBUF
] = (gchar
*) xpm_void
;
1186 row
[COL_PIXVIS
] = GINT_TO_POINTER(FALSE
);
1187 row
[COL_BTNVIS
] = GINT_TO_POINTER(TRUE
);
1193 row
[COL_NAME
] = g_strdup(sym
->name
);
1195 sym_calc_value(sym
);
1196 sym
->flags
&= ~SYMBOL_CHANGED
;
1198 if (sym_is_choice(sym
)) { // parse childs for getting final value
1200 struct symbol
*def_sym
= sym_get_choice_value(sym
);
1201 struct menu
*def_menu
= NULL
;
1203 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1205 for (child
= menu
->list
; child
; child
= child
->next
) {
1206 if (menu_is_visible(child
)
1207 && child
->sym
== def_sym
)
1213 g_strdup(_(menu_get_prompt(def_menu
)));
1215 if (sym
->flags
& SYMBOL_CHOICEVAL
)
1216 row
[COL_BTNRAD
] = GINT_TO_POINTER(TRUE
);
1218 stype
= sym_get_type(sym
);
1221 if (GPOINTER_TO_INT(row
[COL_PIXVIS
]) == FALSE
)
1222 row
[COL_BTNVIS
] = GINT_TO_POINTER(TRUE
);
1223 if (sym_is_choice(sym
))
1226 val
= sym_get_tristate_value(sym
);
1229 row
[COL_NO
] = g_strdup("N");
1230 row
[COL_VALUE
] = g_strdup("N");
1231 row
[COL_BTNACT
] = GINT_TO_POINTER(FALSE
);
1232 row
[COL_BTNINC
] = GINT_TO_POINTER(FALSE
);
1235 row
[COL_MOD
] = g_strdup("M");
1236 row
[COL_VALUE
] = g_strdup("M");
1237 row
[COL_BTNINC
] = GINT_TO_POINTER(TRUE
);
1240 row
[COL_YES
] = g_strdup("Y");
1241 row
[COL_VALUE
] = g_strdup("Y");
1242 row
[COL_BTNACT
] = GINT_TO_POINTER(TRUE
);
1243 row
[COL_BTNINC
] = GINT_TO_POINTER(FALSE
);
1247 if (val
!= no
&& sym_tristate_within_range(sym
, no
))
1248 row
[COL_NO
] = g_strdup("_");
1249 if (val
!= mod
&& sym_tristate_within_range(sym
, mod
))
1250 row
[COL_MOD
] = g_strdup("_");
1251 if (val
!= yes
&& sym_tristate_within_range(sym
, yes
))
1252 row
[COL_YES
] = g_strdup("_");
1257 def
= sym_get_string_value(sym
);
1258 row
[COL_VALUE
] = g_strdup(def
);
1259 row
[COL_EDIT
] = GINT_TO_POINTER(TRUE
);
1260 row
[COL_BTNVIS
] = GINT_TO_POINTER(FALSE
);
1268 /* Set the node content with a row of strings */
1269 static void set_node(GtkTreeIter
* node
, struct menu
*menu
, gchar
** row
)
1275 pix
= gdk_pixbuf_new_from_xpm_data((const char **)
1278 gdk_color_parse(row
[COL_COLOR
], &color
);
1279 gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color
, 1,
1280 FALSE
, FALSE
, &success
);
1282 gtk_tree_store_set(tree
, node
,
1283 COL_OPTION
, row
[COL_OPTION
],
1284 COL_NAME
, row
[COL_NAME
],
1285 COL_NO
, row
[COL_NO
],
1286 COL_MOD
, row
[COL_MOD
],
1287 COL_YES
, row
[COL_YES
],
1288 COL_VALUE
, row
[COL_VALUE
],
1289 COL_MENU
, (gpointer
) menu
,
1291 COL_EDIT
, GPOINTER_TO_INT(row
[COL_EDIT
]),
1293 COL_PIXVIS
, GPOINTER_TO_INT(row
[COL_PIXVIS
]),
1294 COL_BTNVIS
, GPOINTER_TO_INT(row
[COL_BTNVIS
]),
1295 COL_BTNACT
, GPOINTER_TO_INT(row
[COL_BTNACT
]),
1296 COL_BTNINC
, GPOINTER_TO_INT(row
[COL_BTNINC
]),
1297 COL_BTNRAD
, GPOINTER_TO_INT(row
[COL_BTNRAD
]),
1300 g_object_unref(pix
);
1304 /* Add a node to the tree */
1305 static void place_node(struct menu
*menu
, char **row
)
1307 GtkTreeIter
*parent
= parents
[indent
- 1];
1308 GtkTreeIter
*node
= parents
[indent
];
1310 gtk_tree_store_append(tree
, node
, parent
);
1311 set_node(node
, menu
, row
);
1315 /* Find a node in the GTK+ tree */
1316 static GtkTreeIter found
;
1319 * Find a menu in the GtkTree starting at parent.
1321 GtkTreeIter
*gtktree_iter_find_node(GtkTreeIter
* parent
,
1322 struct menu
*tofind
)
1325 GtkTreeIter
*child
= &iter
;
1329 valid
= gtk_tree_model_iter_children(model2
, child
, parent
);
1333 gtk_tree_model_get(model2
, child
, 6, &menu
, -1);
1335 if (menu
== tofind
) {
1336 memcpy(&found
, child
, sizeof(GtkTreeIter
));
1340 ret
= gtktree_iter_find_node(child
, tofind
);
1344 valid
= gtk_tree_model_iter_next(model2
, child
);
1352 * Update the tree by adding/removing entries
1353 * Does not change other nodes
1355 static void update_tree(struct menu
*src
, GtkTreeIter
* dst
)
1357 struct menu
*child1
;
1358 GtkTreeIter iter
, tmp
;
1359 GtkTreeIter
*child2
= &iter
;
1361 GtkTreeIter
*sibling
;
1363 struct property
*prop
;
1364 struct menu
*menu1
, *menu2
;
1366 if (src
== &rootmenu
)
1369 valid
= gtk_tree_model_iter_children(model2
, child2
, dst
);
1370 for (child1
= src
->list
; child1
; child1
= child1
->next
) {
1372 prop
= child1
->prompt
;
1378 gtk_tree_model_get(model2
, child2
, COL_MENU
,
1381 menu2
= NULL
; // force adding of a first child
1384 printf("%*c%s | %s\n", indent
, ' ',
1385 menu1
? menu_get_prompt(menu1
) : "nil",
1386 menu2
? menu_get_prompt(menu2
) : "nil");
1389 if (!menu_is_visible(child1
) && !show_all
) { // remove node
1390 if (gtktree_iter_find_node(dst
, menu1
) != NULL
) {
1391 memcpy(&tmp
, child2
, sizeof(GtkTreeIter
));
1392 valid
= gtk_tree_model_iter_next(model2
,
1394 gtk_tree_store_remove(tree2
, &tmp
);
1396 return; // next parent
1398 goto reparse
; // next child
1403 if (menu1
!= menu2
) {
1404 if (gtktree_iter_find_node(dst
, menu1
) == NULL
) { // add node
1405 if (!valid
&& !menu2
)
1409 gtk_tree_store_insert_before(tree2
,
1412 set_node(child2
, menu1
, fill_row(menu1
));
1415 } else { // remove node
1416 memcpy(&tmp
, child2
, sizeof(GtkTreeIter
));
1417 valid
= gtk_tree_model_iter_next(model2
,
1419 gtk_tree_store_remove(tree2
, &tmp
);
1421 return; // next parent
1423 goto reparse
; // next child
1425 } else if (sym
&& (sym
->flags
& SYMBOL_CHANGED
)) {
1426 set_node(child2
, menu1
, fill_row(menu1
));
1430 update_tree(child1
, child2
);
1433 valid
= gtk_tree_model_iter_next(model2
, child2
);
1438 /* Display the whole tree (single/split/full view) */
1439 static void display_tree(struct menu
*menu
)
1442 struct property
*prop
;
1444 enum prop_type ptype
;
1446 if (menu
== &rootmenu
) {
1448 current
= &rootmenu
;
1451 for (child
= menu
->list
; child
; child
= child
->next
) {
1452 prop
= child
->prompt
;
1454 ptype
= prop
? prop
->type
: P_UNKNOWN
;
1457 sym
->flags
&= ~SYMBOL_CHANGED
;
1459 if ((view_mode
== SPLIT_VIEW
)
1460 && !(child
->flags
& MENU_ROOT
) && (tree
== tree1
))
1463 if ((view_mode
== SPLIT_VIEW
) && (child
->flags
& MENU_ROOT
)
1467 if (menu_is_visible(child
) || show_all
)
1468 place_node(child
, fill_row(child
));
1470 printf("%*c%s: ", indent
, ' ', menu_get_prompt(child
));
1471 printf("%s", child
->flags
& MENU_ROOT
? "rootmenu | " : "");
1472 dbg_print_ptype(ptype
);
1475 dbg_print_stype(sym
->type
);
1477 dbg_print_flags(sym
->flags
);
1482 if ((view_mode
!= FULL_VIEW
) && (ptype
== P_MENU
)
1486 if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
1487 || (view_mode == FULL_VIEW)
1488 || (view_mode == SPLIT_VIEW))*/
1489 if (((view_mode
== SINGLE_VIEW
) && (menu
->flags
& MENU_ROOT
))
1490 || (view_mode
== FULL_VIEW
)
1491 || (view_mode
== SPLIT_VIEW
)) {
1493 display_tree(child
);
1499 /* Display a part of the tree starting at current node (single/split view) */
1500 static void display_tree_part(void)
1503 gtk_tree_store_clear(tree2
);
1504 if (view_mode
== SINGLE_VIEW
)
1505 display_tree(current
);
1506 else if (view_mode
== SPLIT_VIEW
)
1507 display_tree(browsed
);
1508 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w
));
1511 /* Display the list in the left frame (split view) */
1512 static void display_list(void)
1515 gtk_tree_store_clear(tree1
);
1518 display_tree(&rootmenu
);
1519 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w
));
1523 void fixup_rootmenu(struct menu
*menu
)
1526 static int menu_cnt
= 0;
1528 menu
->flags
|= MENU_ROOT
;
1529 for (child
= menu
->list
; child
; child
= child
->next
) {
1530 if (child
->prompt
&& child
->prompt
->type
== P_MENU
) {
1532 fixup_rootmenu(child
);
1534 } else if (!menu_cnt
)
1535 fixup_rootmenu(child
);
1541 int main(int ac
, char *av
[])
1547 #ifndef LKC_DIRECT_LINK
1551 bindtextdomain(PACKAGE
, LOCALEDIR
);
1552 bind_textdomain_codeset(PACKAGE
, "UTF-8");
1553 textdomain(PACKAGE
);
1560 //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
1561 //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
1563 /* Determine GUI path */
1564 env
= getenv(SRCTREE
);
1566 glade_file
= g_strconcat(env
, "/scripts/kconfig/gconf.glade", NULL
);
1567 else if (av
[0][0] == '/')
1568 glade_file
= g_strconcat(av
[0], ".glade", NULL
);
1570 glade_file
= g_strconcat(g_get_current_dir(), "/", av
[0], ".glade", NULL
);
1572 /* Load the interface and connect signals */
1573 init_main_window(glade_file
);
1579 if (ac
> 1 && av
[1][0] == '-') {
1586 printf("%s <config>\n", av
[0]);
1594 fixup_rootmenu(&rootmenu
);
1597 switch (view_mode
) {
1599 display_tree_part();
1605 display_tree(&rootmenu
);
1614 static void conf_changed(void)
1616 bool changed
= conf_get_changed();
1617 gtk_widget_set_sensitive(save_btn
, changed
);
1618 gtk_widget_set_sensitive(save_menu_item
, changed
);