Add a simple validation demo. Make it possible to add arbitrary children

2007-04-26  Matthias Clasen  <mclasen@redhat.com>

        * demo/gtk-demo/combobox.c: Add a simple validation demo.
        * gtk/gtkcomboboxentry.c: Make it possible to add arbitrary
        children to a GtkComboBoxEntry.  (#426401, Paul Pogonyshev)


svn path=/trunk/; revision=17657
This commit is contained in:
Matthias Clasen
2007-04-26 15:55:48 +00:00
committed by Matthias Clasen
parent a63d6b564b
commit 790b009703
5 changed files with 198 additions and 43 deletions

View File

@ -1,3 +1,9 @@
2007-04-26 Matthias Clasen <mclasen@redhat.com>
* demo/gtk-demo/combobox.c: Add a simple validation demo.
* gtk/gtkcomboboxentry.c: Make it possible to add arbitrary
children to a GtkComboBoxEntry. (#426401, Paul Pogonyshev)
2007-04-26 Tor Lillqvist <tml@novell.com> 2007-04-26 Tor Lillqvist <tml@novell.com>
* gtk/gtkinputdialog.c (gtk_input_dialog_fill_axes) * gtk/gtkinputdialog.c (gtk_input_dialog_fill_axes)

View File

@ -246,11 +246,87 @@ fill_combo_entry (GtkWidget *entry)
gtk_combo_box_append_text (GTK_COMBO_BOX (entry), "Three"); gtk_combo_box_append_text (GTK_COMBO_BOX (entry), "Three");
} }
/* A simple validating entry */
#define TYPE_MASK_ENTRY (mask_entry_get_type ())
#define MASK_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MASK_ENTRY, MaskEntry))
#define MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), TYPE_MASK_ENTRY, MaskEntryClass))
#define IS_MASK_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MASK_ENTRY))
#define IS_MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), TYPE_MASK_ENTRY))
#define MASK_ENTRY_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), TYPE_MASK_ENTRY, MaskEntryClass))
typedef struct _MaskEntry MaskEntry;
struct _MaskEntry
{
GtkEntry entry;
gchar *mask;
};
typedef struct _MaskEntryClass MaskEntryClass;
struct _MaskEntryClass
{
GtkEntryClass parent_class;
};
static void mask_entry_editable_init (GtkEditableClass *iface);
G_DEFINE_TYPE_WITH_CODE (MaskEntry, mask_entry, GTK_TYPE_ENTRY,
G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
mask_entry_editable_init));
static void
mask_entry_set_background (MaskEntry *entry)
{
static const GdkColor error_color = { 0, 65535, 60000, 60000 };
if (entry->mask)
{
if (!g_regex_match_simple (entry->mask, gtk_entry_get_text (GTK_ENTRY (entry)), 0, 0))
{
gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, &error_color);
return;
}
}
gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, NULL);
}
static void
mask_entry_changed (GtkEditable *editable)
{
mask_entry_set_background (MASK_ENTRY (editable));
}
static void
mask_entry_init (MaskEntry *entry)
{
entry->mask = NULL;
}
static void
mask_entry_class_init (MaskEntryClass *klass)
{ }
static void
mask_entry_editable_init (GtkEditableClass *iface)
{
iface->changed = mask_entry_changed;
}
GtkWidget * GtkWidget *
do_combobox (GtkWidget *do_widget) do_combobox (GtkWidget *do_widget)
{ {
static GtkWidget *window = NULL; static GtkWidget *window = NULL;
GtkWidget *vbox, *frame, *box, *combo; GtkWidget *vbox, *frame, *box, *combo, *entry;
GtkTreeModel *model; GtkTreeModel *model;
GtkCellRenderer *renderer; GtkCellRenderer *renderer;
GtkTreePath *path; GtkTreePath *path;
@ -343,7 +419,7 @@ do_combobox (GtkWidget *do_widget)
gtk_tree_path_free (path); gtk_tree_path_free (path);
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
/* A GtkComboBoxEntry /* A GtkComboBoxEntry with validation.
*/ */
frame = gtk_frame_new ("Editable"); frame = gtk_frame_new ("Editable");
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
@ -356,6 +432,12 @@ do_combobox (GtkWidget *do_widget)
fill_combo_entry (combo); fill_combo_entry (combo);
gtk_container_add (GTK_CONTAINER (box), combo); gtk_container_add (GTK_CONTAINER (box), combo);
entry = g_object_new (TYPE_MASK_ENTRY, NULL);
MASK_ENTRY (entry)->mask = "^([0-9]*|One|Two|2\302\275|Three)$";
gtk_container_remove (GTK_CONTAINER (combo), GTK_BIN (combo)->child);
gtk_container_add (GTK_CONTAINER (combo), entry);
} }
if (!GTK_WIDGET_VISIBLE (window)) if (!GTK_WIDGET_VISIBLE (window))

View File

@ -1,3 +1,8 @@
2007-04-26 Matthias Clasen <mclasen@redhat.com>
* gtk/tmpl/gtkcomboboxentry.sgml: Mention that the entry
can be replaced.
2007-04-25 Matthias Clasen <mclasen@redhat.com> 2007-04-25 Matthias Clasen <mclasen@redhat.com>
* gtk/tmpl/gtkdialog.sgml: Fix a wrong cross reference. * gtk/tmpl/gtkdialog.sgml: Fix a wrong cross reference.

View File

@ -15,25 +15,34 @@ allow modifying it.
<para> <para>
In contrast to a #GtkComboBox, the underlying model of a #GtkComboBoxEntry In contrast to a #GtkComboBox, the underlying model of a #GtkComboBoxEntry
must always have a text column (see gtk_combo_box_entry_set_text_column()), must always have a text column (see gtk_combo_box_entry_set_text_column()),
and the entry will show the content of the text column in the selected row. To and the entry will show the content of the text column in the selected row.
get the text from the entry, use gtk_combo_box_get_active_text(). To get the text from the entry, use gtk_combo_box_get_active_text().
</para> </para>
<para>The changed signal will be emitted while typing into a GtkComboBoxEntry, <para>
The changed signal will be emitted while typing into a GtkComboBoxEntry,
as well as when selecting an item from the GtkComboBoxEntry's list. Use as well as when selecting an item from the GtkComboBoxEntry's list. Use
gtk_combo_box_get_active() or gtk_combo_box_get_active_iter() to discover gtk_combo_box_get_active() or gtk_combo_box_get_active_iter() to discover
whether an item was actually selected from the list. whether an item was actually selected from the list.
</para> </para>
<para>Connect to the activate signal of the GtkEntry (use gtk_bin_get_child()) to <para>
detect when the user actually finishes entering text.</para> Connect to the activate signal of the GtkEntry (use gtk_bin_get_child())
to detect when the user actually finishes entering text.
</para>
<para> <para>
The convenience API to construct simple text-only #GtkComboBox<!-- -->es can The convenience API to construct simple text-only #GtkComboBox<!-- -->es
also be used with #GtkComboBoxEntry<!-- -->s which have been constructed can also be used with #GtkComboBoxEntry<!-- -->s which have been constructed
with gtk_combo_box_entry_new_text(). with gtk_combo_box_entry_new_text().
</para> </para>
<para>
If you have special needs that go beyond a simple entry (e.g. input validation),
it is possible to replace the child entry by a different widget using
gtk_container_remove() and gtk_container_add().
</para>
<!-- ##### SECTION See_Also ##### --> <!-- ##### SECTION See_Also ##### -->
<para> <para>
#GtkComboBox #GtkComboBox

View File

@ -32,8 +32,6 @@
struct _GtkComboBoxEntryPrivate struct _GtkComboBoxEntryPrivate
{ {
GtkWidget *entry;
GtkCellRenderer *text_renderer; GtkCellRenderer *text_renderer;
gint text_column; gint text_column;
}; };
@ -46,6 +44,10 @@ static void gtk_combo_box_entry_get_property (GObject *object,
guint prop_id, guint prop_id,
GValue *value, GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
static void gtk_combo_box_entry_add (GtkContainer *container,
GtkWidget *child);
static void gtk_combo_box_entry_remove (GtkContainer *container,
GtkWidget *child);
static gchar *gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box); static gchar *gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box);
static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
@ -72,6 +74,7 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
{ {
GObjectClass *object_class; GObjectClass *object_class;
GtkWidgetClass *widget_class; GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
GtkComboBoxClass *combo_class; GtkComboBoxClass *combo_class;
object_class = (GObjectClass *)klass; object_class = (GObjectClass *)klass;
@ -82,6 +85,10 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
widget_class->mnemonic_activate = gtk_combo_box_entry_mnemonic_activate; widget_class->mnemonic_activate = gtk_combo_box_entry_mnemonic_activate;
widget_class->grab_focus = gtk_combo_box_entry_grab_focus; widget_class->grab_focus = gtk_combo_box_entry_grab_focus;
container_class = (GtkContainerClass *)klass;
container_class->add = gtk_combo_box_entry_add;
container_class->remove = gtk_combo_box_entry_remove;
combo_class = (GtkComboBoxClass *)klass; combo_class = (GtkComboBoxClass *)klass;
combo_class->get_active_text = gtk_combo_box_entry_get_active_text; combo_class->get_active_text = gtk_combo_box_entry_get_active_text;
@ -102,15 +109,14 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
static void static void
gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box) gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box)
{ {
GtkWidget *entry;
entry_box->priv = GTK_COMBO_BOX_ENTRY_GET_PRIVATE (entry_box); entry_box->priv = GTK_COMBO_BOX_ENTRY_GET_PRIVATE (entry_box);
entry_box->priv->text_column = -1; entry_box->priv->text_column = -1;
entry_box->priv->entry = gtk_entry_new (); entry = gtk_entry_new ();
/* this flag is a hack to tell the entry to fill its allocation. gtk_widget_show (entry);
*/ gtk_container_add (GTK_CONTAINER (entry_box), entry);
GTK_ENTRY (entry_box->priv->entry)->is_cell_renderer = TRUE;
gtk_container_add (GTK_CONTAINER (entry_box), entry_box->priv->entry);
gtk_widget_show (entry_box->priv->entry);
entry_box->priv->text_renderer = gtk_cell_renderer_text_new (); entry_box->priv->text_renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry_box), gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry_box),
@ -118,12 +124,8 @@ gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box)
gtk_combo_box_set_active (GTK_COMBO_BOX (entry_box), -1); gtk_combo_box_set_active (GTK_COMBO_BOX (entry_box), -1);
g_signal_connect (entry_box->priv->entry, "changed",
G_CALLBACK (gtk_combo_box_entry_contents_changed),
entry_box);
g_signal_connect (entry_box, "changed", g_signal_connect (entry_box, "changed",
G_CALLBACK (gtk_combo_box_entry_active_changed), NULL); G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
has_frame_changed (entry_box, NULL, NULL);
g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL); g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL);
} }
@ -167,6 +169,47 @@ gtk_combo_box_entry_get_property (GObject *object,
} }
} }
static void
gtk_combo_box_entry_add (GtkContainer *container,
GtkWidget *child)
{
GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (container);
if (!GTK_IS_ENTRY (child))
{
g_warning ("Attempting to add a widget with type %s to a GtkComboBoxEntry "
"(need an instance of GtkEntry or of a subclass)",
G_OBJECT_TYPE_NAME (child));
return;
}
GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->add (container, child);
/* this flag is a hack to tell the entry to fill its allocation.
*/
GTK_ENTRY (child)->is_cell_renderer = TRUE;
g_signal_connect (child, "changed",
G_CALLBACK (gtk_combo_box_entry_contents_changed),
entry_box);
has_frame_changed (entry_box, NULL, NULL);
}
static void
gtk_combo_box_entry_remove (GtkContainer *container,
GtkWidget *child)
{
if (child && child == GTK_BIN (container)->child)
{
g_signal_handlers_disconnect_by_func (child,
gtk_combo_box_entry_contents_changed,
container);
GTK_ENTRY (child)->is_cell_renderer = FALSE;
}
GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->remove (container, child);
}
static void static void
gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
gpointer user_data) gpointer user_data)
@ -178,7 +221,11 @@ gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
if (gtk_combo_box_get_active_iter (combo_box, &iter)) if (gtk_combo_box_get_active_iter (combo_box, &iter))
{ {
g_signal_handlers_block_by_func (entry_box->priv->entry, GtkEntry *entry = GTK_ENTRY (GTK_BIN (combo_box)->child);
if (entry)
{
g_signal_handlers_block_by_func (entry,
gtk_combo_box_entry_contents_changed, gtk_combo_box_entry_contents_changed,
combo_box); combo_box);
@ -187,25 +234,29 @@ gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
gtk_tree_model_get (model, &iter, gtk_tree_model_get (model, &iter,
entry_box->priv->text_column, &str, entry_box->priv->text_column, &str,
-1); -1);
gtk_entry_set_text (GTK_ENTRY (entry_box->priv->entry), str); gtk_entry_set_text (entry, str);
g_free (str); g_free (str);
g_signal_handlers_unblock_by_func (entry_box->priv->entry, g_signal_handlers_unblock_by_func (entry,
gtk_combo_box_entry_contents_changed, gtk_combo_box_entry_contents_changed,
combo_box); combo_box);
} }
} }
}
static void static void
has_frame_changed (GtkComboBoxEntry *entry_box, has_frame_changed (GtkComboBoxEntry *entry_box,
GParamSpec *pspec, GParamSpec *pspec,
gpointer data) gpointer data)
{
if (GTK_BIN (entry_box)->child)
{ {
gboolean has_frame; gboolean has_frame;
g_object_get (entry_box, "has-frame", &has_frame, NULL); g_object_get (entry_box, "has-frame", &has_frame, NULL);
gtk_entry_set_has_frame (GTK_ENTRY (entry_box->priv->entry), has_frame); gtk_entry_set_has_frame (GTK_ENTRY (GTK_BIN (entry_box)->child), has_frame);
}
} }
static void static void
@ -323,9 +374,10 @@ static gboolean
gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget, gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling) gboolean group_cycling)
{ {
GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget); GtkBin *entry_box = GTK_BIN (widget);
gtk_widget_grab_focus (entry_box->priv->entry); if (entry_box->child)
gtk_widget_grab_focus (entry_box->child);
return TRUE; return TRUE;
} }
@ -333,9 +385,10 @@ gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget,
static void static void
gtk_combo_box_entry_grab_focus (GtkWidget *widget) gtk_combo_box_entry_grab_focus (GtkWidget *widget)
{ {
GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget); GtkBin *entry_box = GTK_BIN (widget);
gtk_widget_grab_focus (entry_box->priv->entry); if (entry_box->child)
gtk_widget_grab_focus (entry_box->child);
} }
@ -372,10 +425,10 @@ gtk_combo_box_entry_new_text (void)
static gchar * static gchar *
gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box) gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box)
{ {
GtkComboBoxEntry *combo = GTK_COMBO_BOX_ENTRY (combo_box); GtkBin *combo = GTK_BIN (combo_box);
if (combo->priv->entry) if (combo->child)
return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->priv->entry))); return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->child)));
return NULL; return NULL;
} }