Add support for displaying an option icon with each radio button pixbuf.

* libnautilus-extensions/nautilus-radio-button-group.c:
	(nautilus_radio_button_group_initialize_class),
	(nautilus_radio_button_group_initialize),
	(nautilus_radio_button_group_destroy),
	(radio_button_group_free_button_group),
	(nautilus_radio_button_group_insert),
	(nautilus_radio_button_group_get_active_index),
	(nautilus_radio_button_group_set_active_index),
	(nautilus_radio_button_group_set_pixbuf):
	* libnautilus-extensions/nautilus-radio-button-group.h:
	Add support for displaying an option icon with each radio button
	pixbuf.  Use NautilusImage for this.

	* test/test-nautilus-widgets.c: (create_pixbuf),
	(test_radio_group):
	Update test to include icon support.
This commit is contained in:
Ramiro Estrugo 2000-07-18 08:12:43 +00:00
parent 6c03d6b6d9
commit a2c679cc19
6 changed files with 302 additions and 98 deletions

View file

@ -1,3 +1,22 @@
2000-07-18 Ramiro Estrugo <ramiro@eazel.com>
* libnautilus-extensions/nautilus-radio-button-group.c:
(nautilus_radio_button_group_initialize_class),
(nautilus_radio_button_group_initialize),
(nautilus_radio_button_group_destroy),
(radio_button_group_free_button_group),
(nautilus_radio_button_group_insert),
(nautilus_radio_button_group_get_active_index),
(nautilus_radio_button_group_set_active_index),
(nautilus_radio_button_group_set_pixbuf):
* libnautilus-extensions/nautilus-radio-button-group.h:
Add support for displaying an option icon with each radio button
pixbuf. Use NautilusImage for this.
* test/test-nautilus-widgets.c: (create_pixbuf),
(test_radio_group):
Update test to include icon support.
2000-07-17 Pavel Cisler <pavel@eazel.com>
* libnautilus-extensions/nautilus-list.c:

View file

@ -24,10 +24,12 @@
#include <config.h>
#include "nautilus-radio-button-group.h"
#include "nautilus-image.h"
#include <gtk/gtkradiobutton.h>
#include <gtk/gtksignal.h>
#include "nautilus-gtk-macros.h"
#include "nautilus-glib-extensions.h"
static const gint RADIO_BUTTON_GROUP_INVALID = -1;
@ -40,10 +42,17 @@ typedef enum
struct _NautilusRadioButtonGroupDetails
{
GList *buttons;
GList *rows;
GSList *group;
guint num_rows;
};
typedef struct
{
GtkWidget *button;
GtkWidget *image;
} TableRow;
/* NautilusRadioButtonGroupClass methods */
static void nautilus_radio_button_group_initialize_class (NautilusRadioButtonGroupClass *klass);
static void nautilus_radio_button_group_initialize (NautilusRadioButtonGroup *button_group);
@ -66,7 +75,7 @@ static void button_toggled (GtkWidget
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusRadioButtonGroup,
nautilus_radio_button_group,
GTK_TYPE_VBOX)
GTK_TYPE_TABLE)
static guint radio_group_signals[LAST_SIGNAL] = { 0 };
@ -82,7 +91,7 @@ nautilus_radio_button_group_initialize_class (NautilusRadioButtonGroupClass *rad
object_class = GTK_OBJECT_CLASS (radio_button_group_class);
widget_class = GTK_WIDGET_CLASS (radio_button_group_class);
parent_class = gtk_type_class (gtk_vbox_get_type ());
parent_class = gtk_type_class (gtk_table_get_type ());
/* GtkObjectClass */
object_class->destroy = nautilus_radio_button_group_destroy;
@ -106,15 +115,16 @@ nautilus_radio_button_group_initialize (NautilusRadioButtonGroup *button_group)
{
button_group->details = g_new (NautilusRadioButtonGroupDetails, 1);
button_group->details->buttons = NULL;
button_group->details->rows = NULL;
button_group->details->group = NULL;
button_group->details->num_rows = 0;
}
/*
* GtkObjectClass methods
*/
static void
nautilus_radio_button_group_destroy(GtkObject* object)
nautilus_radio_button_group_destroy (GtkObject *object)
{
NautilusRadioButtonGroup * button_group;
@ -154,12 +164,11 @@ radio_button_group_free_button_group (NautilusRadioButtonGroup *button_group)
g_assert (button_group != NULL);
g_assert (button_group->details != NULL);
if (button_group->details->buttons)
{
g_list_free (button_group->details->buttons);
if (button_group->details->rows) {
nautilus_g_list_free_deep (button_group->details->rows);
button_group->details->rows = NULL;
}
button_group->details->buttons = NULL;
button_group->details->group = NULL;
}
@ -203,41 +212,55 @@ nautilus_radio_button_group_new (void)
* Returns: The index of the new button.
*/
guint
nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label)
nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label)
{
GtkWidget *button;
GtkTable *table;
TableRow *row;
g_return_val_if_fail (button_group != NULL, 0);
g_return_val_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group), 0);
g_return_val_if_fail (label != NULL, 0);
button = gtk_radio_button_new_with_label (button_group->details->group, label);
table = GTK_TABLE (button_group);
row = g_new (TableRow, 1);
row->button = gtk_radio_button_new_with_label (button_group->details->group, label);
row->image = NULL;
/*
* For some crazy reason I dont grok, the group has to be fetched each
* time from the previous button
*/
button_group->details->group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
button_group->details->group = gtk_radio_button_group (GTK_RADIO_BUTTON (row->button));
gtk_signal_connect (GTK_OBJECT (button),
gtk_signal_connect (GTK_OBJECT (row->button),
"toggled",
GTK_SIGNAL_FUNC (button_toggled),
(gpointer) button_group);
gtk_box_pack_start (GTK_BOX (button_group),
button,
TRUE,
TRUE,
0);
button_group->details->num_rows++;
gtk_widget_show (button);
gtk_table_resize (table, button_group->details->num_rows, 2);
/* Column 2 */
gtk_table_attach (table,
row->button, /* child */
1, /* left_attatch */
2, /* right_attatch */
button_group->details->num_rows - 1, /* top_attatch */
button_group->details->num_rows, /* bottom_attatch */
(GTK_FILL|GTK_EXPAND), /* xoptions */
(GTK_FILL|GTK_EXPAND), /* yoptions */
0, /* xpadding */
0); /* ypadding */
gtk_widget_show (row->button);
button_group->details->buttons = g_list_append (button_group->details->buttons,
(gpointer) button);
return g_list_length (button_group->details->buttons) - 1;
button_group->details->rows = g_list_append (button_group->details->rows, row);
return g_list_length (button_group->details->rows) - 1;
}
/**
@ -257,13 +280,18 @@ nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_g
g_assert (button_group != NULL);
button_iterator = button_group->details->buttons;
button_iterator = button_group->details->rows;
while (button_iterator)
{
g_assert (GTK_TOGGLE_BUTTON (button_iterator->data));
TableRow *row;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_iterator->data)))
row = button_iterator->data;
g_assert (row != NULL);
g_assert (row->button != NULL);
g_assert (GTK_TOGGLE_BUTTON (row->button));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (row->button)))
{
return i;
}
@ -273,7 +301,7 @@ nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_g
i++;
}
g_assert (0);
g_assert_not_reached ();
return 0;
}
@ -282,17 +310,57 @@ void
nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index)
{
GList *node;
TableRow *row;
g_return_if_fail (button_group != NULL);
g_return_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group));
node = g_list_nth (button_group->details->buttons, active_index);
row = g_list_nth_data (button_group->details->rows, active_index);
g_assert (row != NULL);
g_assert (row->button != NULL);
g_assert (GTK_TOGGLE_BUTTON (row->button));
g_assert (node != NULL);
g_assert (GTK_TOGGLE_BUTTON (node->data));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row->button), TRUE);
}
/* Set an item's pixbuf. */
void
nautilus_radio_button_group_set_pixbuf (NautilusRadioButtonGroup *button_group,
guint index,
GdkPixbuf *pixbuf)
{
GtkTable *table;
TableRow *row;
g_return_if_fail (button_group != NULL);
g_return_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group));
g_return_if_fail (index < g_list_length (button_group->details->rows));
table = GTK_TABLE (button_group);
row = g_list_nth_data (button_group->details->rows, index);
g_assert (row != NULL);
if (row->image == NULL)
{
row->image = nautilus_image_new ();
gtk_table_attach (table,
row->image, /* child */
0, /* left_attatch */
1, /* right_attatch */
index, /* top_attatch */
index + 1, /* bottom_attatch */
GTK_FILL, /* xoptions */
(GTK_FILL|GTK_EXPAND), /* yoptions */
0, /* xpadding */
0); /* ypadding */
gtk_widget_show (row->image);
}
g_assert (row->image != NULL);
nautilus_image_set_pixbuf (NAUTILUS_IMAGE (row->image), pixbuf);
}

View file

@ -25,13 +25,15 @@
#ifndef NAUTILUS_RADIO_BUTTON_GROUP_H
#define NAUTILUS_RADIO_BUTTON_GROUP_H
#include <gtk/gtkvbox.h>
#include <gtk/gtktable.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gnome.h>
/*
* NautilusRadioButtonGroup is a collection of radio buttons.
* NautilusRadioButtonGroup is a collection of radio buttons
* arranged into rows. An optional icon can be displayed
* with each radio button entry.
*/
BEGIN_GNOME_DECLS
#define NAUTILUS_TYPE_RADIO_BUTTON_GROUP (nautilus_radio_button_group_get_type ())
@ -46,7 +48,7 @@ typedef struct _NautilusRadioButtonGroupDetails NautilusRadioButtonGroupDetai
struct _NautilusRadioButtonGroup
{
/* Super Class */
GtkVBox vbox;
GtkTable table;
/* Private stuff */
NautilusRadioButtonGroupDetails *details;
@ -54,7 +56,7 @@ struct _NautilusRadioButtonGroup
struct _NautilusRadioButtonGroupClass
{
GtkVBoxClass parent_class;
GtkTableClass parent_class;
};
typedef struct
@ -65,16 +67,23 @@ typedef struct
GtkType nautilus_radio_button_group_get_type (void);
GtkWidget* nautilus_radio_button_group_new (void);
/* Insert a new item to the group. Returns the new item's index */
guint nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label);
guint nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label);
/* Get the active item index. By law there always is an active item */
guint nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_group);
guint nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_group);
/* Set the active item index. */
void nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index);
void nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index);
/* Set an item's pixbuf. */
void nautilus_radio_button_group_set_pixbuf (NautilusRadioButtonGroup *button_group,
guint index,
GdkPixbuf *pixbuf);
BEGIN_GNOME_DECLS

View file

@ -24,10 +24,12 @@
#include <config.h>
#include "nautilus-radio-button-group.h"
#include "nautilus-image.h"
#include <gtk/gtkradiobutton.h>
#include <gtk/gtksignal.h>
#include "nautilus-gtk-macros.h"
#include "nautilus-glib-extensions.h"
static const gint RADIO_BUTTON_GROUP_INVALID = -1;
@ -40,10 +42,17 @@ typedef enum
struct _NautilusRadioButtonGroupDetails
{
GList *buttons;
GList *rows;
GSList *group;
guint num_rows;
};
typedef struct
{
GtkWidget *button;
GtkWidget *image;
} TableRow;
/* NautilusRadioButtonGroupClass methods */
static void nautilus_radio_button_group_initialize_class (NautilusRadioButtonGroupClass *klass);
static void nautilus_radio_button_group_initialize (NautilusRadioButtonGroup *button_group);
@ -66,7 +75,7 @@ static void button_toggled (GtkWidget
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusRadioButtonGroup,
nautilus_radio_button_group,
GTK_TYPE_VBOX)
GTK_TYPE_TABLE)
static guint radio_group_signals[LAST_SIGNAL] = { 0 };
@ -82,7 +91,7 @@ nautilus_radio_button_group_initialize_class (NautilusRadioButtonGroupClass *rad
object_class = GTK_OBJECT_CLASS (radio_button_group_class);
widget_class = GTK_WIDGET_CLASS (radio_button_group_class);
parent_class = gtk_type_class (gtk_vbox_get_type ());
parent_class = gtk_type_class (gtk_table_get_type ());
/* GtkObjectClass */
object_class->destroy = nautilus_radio_button_group_destroy;
@ -106,15 +115,16 @@ nautilus_radio_button_group_initialize (NautilusRadioButtonGroup *button_group)
{
button_group->details = g_new (NautilusRadioButtonGroupDetails, 1);
button_group->details->buttons = NULL;
button_group->details->rows = NULL;
button_group->details->group = NULL;
button_group->details->num_rows = 0;
}
/*
* GtkObjectClass methods
*/
static void
nautilus_radio_button_group_destroy(GtkObject* object)
nautilus_radio_button_group_destroy (GtkObject *object)
{
NautilusRadioButtonGroup * button_group;
@ -154,12 +164,11 @@ radio_button_group_free_button_group (NautilusRadioButtonGroup *button_group)
g_assert (button_group != NULL);
g_assert (button_group->details != NULL);
if (button_group->details->buttons)
{
g_list_free (button_group->details->buttons);
if (button_group->details->rows) {
nautilus_g_list_free_deep (button_group->details->rows);
button_group->details->rows = NULL;
}
button_group->details->buttons = NULL;
button_group->details->group = NULL;
}
@ -203,41 +212,55 @@ nautilus_radio_button_group_new (void)
* Returns: The index of the new button.
*/
guint
nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label)
nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label)
{
GtkWidget *button;
GtkTable *table;
TableRow *row;
g_return_val_if_fail (button_group != NULL, 0);
g_return_val_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group), 0);
g_return_val_if_fail (label != NULL, 0);
button = gtk_radio_button_new_with_label (button_group->details->group, label);
table = GTK_TABLE (button_group);
row = g_new (TableRow, 1);
row->button = gtk_radio_button_new_with_label (button_group->details->group, label);
row->image = NULL;
/*
* For some crazy reason I dont grok, the group has to be fetched each
* time from the previous button
*/
button_group->details->group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
button_group->details->group = gtk_radio_button_group (GTK_RADIO_BUTTON (row->button));
gtk_signal_connect (GTK_OBJECT (button),
gtk_signal_connect (GTK_OBJECT (row->button),
"toggled",
GTK_SIGNAL_FUNC (button_toggled),
(gpointer) button_group);
gtk_box_pack_start (GTK_BOX (button_group),
button,
TRUE,
TRUE,
0);
button_group->details->num_rows++;
gtk_widget_show (button);
gtk_table_resize (table, button_group->details->num_rows, 2);
/* Column 2 */
gtk_table_attach (table,
row->button, /* child */
1, /* left_attatch */
2, /* right_attatch */
button_group->details->num_rows - 1, /* top_attatch */
button_group->details->num_rows, /* bottom_attatch */
(GTK_FILL|GTK_EXPAND), /* xoptions */
(GTK_FILL|GTK_EXPAND), /* yoptions */
0, /* xpadding */
0); /* ypadding */
gtk_widget_show (row->button);
button_group->details->buttons = g_list_append (button_group->details->buttons,
(gpointer) button);
return g_list_length (button_group->details->buttons) - 1;
button_group->details->rows = g_list_append (button_group->details->rows, row);
return g_list_length (button_group->details->rows) - 1;
}
/**
@ -257,13 +280,18 @@ nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_g
g_assert (button_group != NULL);
button_iterator = button_group->details->buttons;
button_iterator = button_group->details->rows;
while (button_iterator)
{
g_assert (GTK_TOGGLE_BUTTON (button_iterator->data));
TableRow *row;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_iterator->data)))
row = button_iterator->data;
g_assert (row != NULL);
g_assert (row->button != NULL);
g_assert (GTK_TOGGLE_BUTTON (row->button));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (row->button)))
{
return i;
}
@ -273,7 +301,7 @@ nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_g
i++;
}
g_assert (0);
g_assert_not_reached ();
return 0;
}
@ -282,17 +310,57 @@ void
nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index)
{
GList *node;
TableRow *row;
g_return_if_fail (button_group != NULL);
g_return_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group));
node = g_list_nth (button_group->details->buttons, active_index);
row = g_list_nth_data (button_group->details->rows, active_index);
g_assert (row != NULL);
g_assert (row->button != NULL);
g_assert (GTK_TOGGLE_BUTTON (row->button));
g_assert (node != NULL);
g_assert (GTK_TOGGLE_BUTTON (node->data));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row->button), TRUE);
}
/* Set an item's pixbuf. */
void
nautilus_radio_button_group_set_pixbuf (NautilusRadioButtonGroup *button_group,
guint index,
GdkPixbuf *pixbuf)
{
GtkTable *table;
TableRow *row;
g_return_if_fail (button_group != NULL);
g_return_if_fail (NAUTILUS_IS_RADIO_BUTTON_GROUP (button_group));
g_return_if_fail (index < g_list_length (button_group->details->rows));
table = GTK_TABLE (button_group);
row = g_list_nth_data (button_group->details->rows, index);
g_assert (row != NULL);
if (row->image == NULL)
{
row->image = nautilus_image_new ();
gtk_table_attach (table,
row->image, /* child */
0, /* left_attatch */
1, /* right_attatch */
index, /* top_attatch */
index + 1, /* bottom_attatch */
GTK_FILL, /* xoptions */
(GTK_FILL|GTK_EXPAND), /* yoptions */
0, /* xpadding */
0); /* ypadding */
gtk_widget_show (row->image);
}
g_assert (row->image != NULL);
nautilus_image_set_pixbuf (NAUTILUS_IMAGE (row->image), pixbuf);
}

View file

@ -25,13 +25,15 @@
#ifndef NAUTILUS_RADIO_BUTTON_GROUP_H
#define NAUTILUS_RADIO_BUTTON_GROUP_H
#include <gtk/gtkvbox.h>
#include <gtk/gtktable.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gnome.h>
/*
* NautilusRadioButtonGroup is a collection of radio buttons.
* NautilusRadioButtonGroup is a collection of radio buttons
* arranged into rows. An optional icon can be displayed
* with each radio button entry.
*/
BEGIN_GNOME_DECLS
#define NAUTILUS_TYPE_RADIO_BUTTON_GROUP (nautilus_radio_button_group_get_type ())
@ -46,7 +48,7 @@ typedef struct _NautilusRadioButtonGroupDetails NautilusRadioButtonGroupDetai
struct _NautilusRadioButtonGroup
{
/* Super Class */
GtkVBox vbox;
GtkTable table;
/* Private stuff */
NautilusRadioButtonGroupDetails *details;
@ -54,7 +56,7 @@ struct _NautilusRadioButtonGroup
struct _NautilusRadioButtonGroupClass
{
GtkVBoxClass parent_class;
GtkTableClass parent_class;
};
typedef struct
@ -65,16 +67,23 @@ typedef struct
GtkType nautilus_radio_button_group_get_type (void);
GtkWidget* nautilus_radio_button_group_new (void);
/* Insert a new item to the group. Returns the new item's index */
guint nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label);
guint nautilus_radio_button_group_insert (NautilusRadioButtonGroup *button_group,
const gchar *label);
/* Get the active item index. By law there always is an active item */
guint nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_group);
guint nautilus_radio_button_group_get_active_index (NautilusRadioButtonGroup *button_group);
/* Set the active item index. */
void nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index);
void nautilus_radio_button_group_set_active_index (NautilusRadioButtonGroup *button_group,
guint active_index);
/* Set an item's pixbuf. */
void nautilus_radio_button_group_set_pixbuf (NautilusRadioButtonGroup *button_group,
guint index,
GdkPixbuf *pixbuf);
BEGIN_GNOME_DECLS

View file

@ -8,6 +8,24 @@
#include <gtk/gtk.h>
static GdkPixbuf*
create_pixbuf (const char *name)
{
char *path;
GdkPixbuf *pixbuf;
g_return_val_if_fail (name != NULL, NULL);
path = g_strdup_printf ("/gnome/share/pixmaps/nautilus/%s", name);
pixbuf = gdk_pixbuf_new_from_file (path);
g_free (path);
g_assert (pixbuf != NULL);
return pixbuf;
}
static void test_radio_group (void);
static void test_caption_table (void);
static void test_string_picker (void);
@ -42,18 +60,31 @@ main (int argc, char * argv[])
static void
test_radio_group (void)
{
GtkWidget * window;
GtkWidget * buttons;
GtkWidget *window;
GtkWidget *buttons;
GdkPixbuf *pixbufs[3];
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
buttons = nautilus_radio_button_group_new ();
pixbufs[0] = create_pixbuf ("novice.png");
pixbufs[1] = create_pixbuf ("intermediate.png");
pixbufs[2] = create_pixbuf ("expert.png");
nautilus_radio_button_group_insert (NAUTILUS_RADIO_BUTTON_GROUP (buttons), "Apples");
nautilus_radio_button_group_insert (NAUTILUS_RADIO_BUTTON_GROUP (buttons), "Oranges");
nautilus_radio_button_group_insert (NAUTILUS_RADIO_BUTTON_GROUP (buttons), "Strawberries");
nautilus_radio_button_group_set_pixbuf (NAUTILUS_RADIO_BUTTON_GROUP (buttons), 0, pixbufs[0]);
nautilus_radio_button_group_set_pixbuf (NAUTILUS_RADIO_BUTTON_GROUP (buttons), 1, pixbufs[1]);
nautilus_radio_button_group_set_pixbuf (NAUTILUS_RADIO_BUTTON_GROUP (buttons), 2, pixbufs[2]);
gdk_pixbuf_unref (pixbufs[0]);
gdk_pixbuf_unref (pixbufs[1]);
gdk_pixbuf_unref (pixbufs[2]);
gtk_signal_connect (GTK_OBJECT (buttons),
"changed",
GTK_SIGNAL_FUNC (test_radio_changed_callback),