[gnome-builder] egg: add new EggStateMachine prototype
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] egg: add new EggStateMachine prototype
- Date: Thu, 21 May 2015 06:51:34 +0000 (UTC)
commit 30de5b7d19df07ae74e636354e8f244bffefe0fe
Author: Christian Hergert <christian hergert me>
Date: Tue May 19 20:36:41 2015 -0700
egg: add new EggStateMachine prototype
I'd like to be able to use this for situations like the greeter where we
need to manage potentially a bunch of little state changes. Having all of
that defined in UI would be very nice. It is also unlikely to be the
only place that is useful. I imagine dialogs like the new project dialog
and project properties could use this too. However, GtkAssistant may
be more appropriate for the create/clone/etc workflow.
This has very little project for references. So that definitely needs to
be iterated upon.
I think it shows enough promise to go forward with.
contrib/egg/Makefile.am | 5 +
contrib/egg/egg-state-machine-action.c | 260 +++++++++++
contrib/egg/egg-state-machine-action.h | 35 ++
contrib/egg/egg-state-machine-buildable.c | 665 ++++++++++++++++++++++++++++
contrib/egg/egg-state-machine-buildable.h | 30 ++
contrib/egg/egg-state-machine-private.h | 59 +++
contrib/egg/egg-state-machine.c | 679 ++++++++++++-----------------
contrib/egg/egg-state-machine.h | 81 ++--
tests/test-egg-state-machine.c | 63 +--
9 files changed, 1379 insertions(+), 498 deletions(-)
---
diff --git a/contrib/egg/Makefile.am b/contrib/egg/Makefile.am
index 562c65e..3f327be 100644
--- a/contrib/egg/Makefile.am
+++ b/contrib/egg/Makefile.am
@@ -15,6 +15,11 @@ libegg_la_SOURCES = \
egg-signal-group.h \
egg-state-machine.c \
egg-state-machine.h \
+ egg-state-machine-action.c \
+ egg-state-machine-action.h \
+ egg-state-machine-buildable.c \
+ egg-state-machine-buildable.h \
+ egg-state-machine-private.h \
egg-task-cache.c \
egg-task-cache.h \
$(NULL)
diff --git a/contrib/egg/egg-state-machine-action.c b/contrib/egg/egg-state-machine-action.c
new file mode 100644
index 0000000..879af83
--- /dev/null
+++ b/contrib/egg/egg-state-machine-action.c
@@ -0,0 +1,260 @@
+/* egg-state-machine-action.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "egg-state-machine.h"
+#include "egg-state-machine-action.h"
+
+struct _EggStateMachineAction
+{
+ GObject parent_instance;
+
+ gchar *name;
+ EggStateMachine *state_machine;
+};
+
+static void action_iface_init (GActionInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EggStateMachineAction, egg_state_machine_action, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, action_iface_init))
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_STATE_MACHINE,
+ LAST_PROP,
+
+ PROP_ENABLED,
+ PROP_PARAMETER_TYPE,
+ PROP_STATE,
+ PROP_STATE_TYPE,
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static gboolean
+egg_state_machine_action_get_enabled (GAction *action)
+{
+ return TRUE;
+}
+
+static const gchar *
+egg_state_machine_action_get_name (GAction *action)
+{
+ EggStateMachineAction *self = (EggStateMachineAction *)action;
+
+ g_return_val_if_fail (EGG_IS_STATE_MACHINE_ACTION (self), NULL);
+
+ return self->name;
+}
+
+static const GVariantType *
+egg_state_machine_action_get_parameter_type (GAction *action)
+{
+ return G_VARIANT_TYPE_STRING;
+}
+
+static const GVariantType *
+egg_state_machine_action_get_state_type (GAction *action)
+{
+ return G_VARIANT_TYPE_STRING;
+}
+
+static GVariant *
+egg_state_machine_action_get_state (GAction *action)
+{
+ EggStateMachineAction *self = (EggStateMachineAction *)action;
+ const gchar *state;
+
+ g_return_val_if_fail (EGG_IS_STATE_MACHINE_ACTION (self), NULL);
+
+ state = egg_state_machine_get_state (self->state_machine);
+
+ if (state != NULL)
+ return g_variant_ref_sink (g_variant_new_string (state));
+
+ return NULL;
+}
+
+static void
+egg_state_machine_action_state_set_cb (EggStateMachineAction *self,
+ GParamSpec *pspec,
+ EggStateMachine *state_machine)
+{
+ g_return_if_fail (EGG_IS_STATE_MACHINE_ACTION (self));
+
+ g_object_notify (G_OBJECT (self), "state");
+}
+
+static void
+egg_state_machine_action_set_state_machine (EggStateMachineAction *self,
+ EggStateMachine *state_machine)
+{
+ g_return_if_fail (EGG_IS_STATE_MACHINE_ACTION (self));
+ g_return_if_fail (EGG_IS_STATE_MACHINE (state_machine));
+ g_return_if_fail (self->state_machine == NULL);
+
+ if (g_set_object (&self->state_machine, state_machine))
+ {
+ g_signal_connect_object (state_machine,
+ "notify::state",
+ G_CALLBACK (egg_state_machine_action_state_set_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+}
+
+static void
+egg_state_machine_action_activate (GAction *action,
+ GVariant *param)
+{
+ EggStateMachineAction *self = (EggStateMachineAction *)action;
+
+ g_assert (EGG_IS_STATE_MACHINE_ACTION (self));
+ g_assert (EGG_IS_STATE_MACHINE (self->state_machine));
+
+ if ((param != NULL) && g_variant_is_of_type (param, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *state;
+
+ if ((state = g_variant_get_string (param, NULL)))
+ egg_state_machine_set_state (self->state_machine, state);
+ }
+}
+
+static void
+egg_state_machine_action_finalize (GObject *object)
+{
+ EggStateMachineAction *self = (EggStateMachineAction *)object;
+
+ g_clear_pointer (&self->name, g_free);
+ g_clear_object (&self->state_machine);
+
+ G_OBJECT_CLASS (egg_state_machine_action_parent_class)->finalize (object);
+}
+
+static void
+egg_state_machine_action_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggStateMachineAction *self = EGG_STATE_MACHINE_ACTION (object);
+ GAction *action = (GAction *)object;
+
+ switch (prop_id)
+ {
+ case PROP_ENABLED:
+ g_value_set_boolean (value, egg_state_machine_action_get_enabled (action));
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, egg_state_machine_action_get_name (action));
+ break;
+
+ case PROP_PARAMETER_TYPE:
+ g_value_set_boxed (value, egg_state_machine_action_get_parameter_type (action));
+ break;
+
+ case PROP_STATE:
+ g_value_set_boxed (value, egg_state_machine_action_get_state (action));
+ break;
+
+ case PROP_STATE_MACHINE:
+ g_value_set_object (value, self->state_machine);
+ break;
+
+ case PROP_STATE_TYPE:
+ g_value_set_boxed (value, egg_state_machine_action_get_state_type (action));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_state_machine_action_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggStateMachineAction *self = EGG_STATE_MACHINE_ACTION (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATE_MACHINE:
+ egg_state_machine_action_set_state_machine (self, g_value_get_object (value));
+ break;
+
+ case PROP_NAME:
+ self->name = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_state_machine_action_class_init (EggStateMachineActionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = egg_state_machine_action_finalize;
+ object_class->get_property = egg_state_machine_action_get_property;
+ object_class->set_property = egg_state_machine_action_set_property;
+
+ gParamSpecs [PROP_NAME] =
+ g_param_spec_string ("name",
+ _("Name"),
+ _("The name of the action"),
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ gParamSpecs [PROP_STATE_MACHINE] =
+ g_param_spec_object ("state-machine",
+ _("State Machine"),
+ _("State Machine"),
+ EGG_TYPE_STATE_MACHINE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
+
+ g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type");
+ g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
+ g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type");
+ g_object_class_override_property (object_class, PROP_STATE, "state");
+}
+
+static void
+egg_state_machine_action_init (EggStateMachineAction *self)
+{
+}
+
+static void
+action_iface_init (GActionInterface *iface)
+{
+ iface->get_enabled = egg_state_machine_action_get_enabled;
+ iface->get_name = egg_state_machine_action_get_name;
+ iface->get_parameter_type = egg_state_machine_action_get_parameter_type;
+ iface->get_state_type = egg_state_machine_action_get_state_type;
+ iface->get_state = egg_state_machine_action_get_state;
+ iface->activate = egg_state_machine_action_activate;
+}
diff --git a/contrib/egg/egg-state-machine-action.h b/contrib/egg/egg-state-machine-action.h
new file mode 100644
index 0000000..a806448
--- /dev/null
+++ b/contrib/egg/egg-state-machine-action.h
@@ -0,0 +1,35 @@
+/* egg-state-machine-action.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_STATE_MACHINE_ACTION_H
+#define EGG_STATE_MACHINE_ACTION_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_STATE_MACHINE_ACTION (egg_state_machine_action_get_type())
+
+G_DECLARE_FINAL_TYPE (EggStateMachineAction, egg_state_machine_action, EGG, STATE_MACHINE_ACTION, GObject)
+
+GAction *egg_state_machine_action_new (EggStateMachine *state_machine,
+ const gchar *name);
+
+G_END_DECLS
+
+#endif /* EGG_STATE_MACHINE_ACTION_H */
diff --git a/contrib/egg/egg-state-machine-buildable.c b/contrib/egg/egg-state-machine-buildable.c
new file mode 100644
index 0000000..f69c2f9
--- /dev/null
+++ b/contrib/egg/egg-state-machine-buildable.c
@@ -0,0 +1,665 @@
+/* egg-state-machine-buildable.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "egg-state-machine"
+
+#include <errno.h>
+#include <glib/gi18n.h>
+
+#include "egg-state-machine.h"
+#include "egg-state-machine-buildable.h"
+#include "egg-state-machine-private.h"
+
+typedef struct
+{
+ EggStateMachine *self;
+ GtkBuilder *builder;
+ GQueue *stack;
+} StatesParserData;
+
+typedef enum
+{
+ STACK_ITEM_OBJECT,
+ STACK_ITEM_STATE,
+ STACK_ITEM_PROPERTY,
+} StackItemType;
+
+typedef struct
+{
+ StackItemType type;
+ union {
+ struct {
+ gchar *id;
+ GSList *classes;
+ GSList *properties;
+ } object;
+ struct {
+ gchar *name;
+ GSList *objects;
+ } state;
+ struct {
+ gchar *name;
+ gchar *bind_source;
+ gchar *bind_property;
+ gchar *text;
+ GBindingFlags bind_flags;
+ } property;
+ } u;
+} StackItem;
+
+static GtkBuildableIface *egg_state_machine_parent_buildable;
+
+static void
+stack_item_free (StackItem *item)
+{
+ switch (item->type)
+ {
+ case STACK_ITEM_OBJECT:
+ g_free (item->u.object.id);
+ g_slist_free_full (item->u.object.classes, g_free);
+ break;
+
+ case STACK_ITEM_STATE:
+ g_free (item->u.state.name);
+ g_slist_free_full (item->u.state.objects, (GDestroyNotify)stack_item_free);
+ break;
+
+ case STACK_ITEM_PROPERTY:
+ g_free (item->u.property.name);
+ g_free (item->u.property.bind_source);
+ g_free (item->u.property.bind_property);
+ g_free (item->u.property.text);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_slice_free (StackItem, item);
+}
+
+static StackItem *
+stack_item_new (StackItemType type)
+{
+ StackItem *item;
+
+ item = g_slice_new0 (StackItem);
+ item->type = type;
+
+ return item;
+}
+
+static void
+add_state (StatesParserData *parser_data,
+ StackItem *item,
+ GError **error)
+{
+ GSList *iter;
+
+ g_assert (parser_data != NULL);
+ g_assert (item != NULL);
+ g_assert (item->type == STACK_ITEM_STATE);
+
+ for (iter = item->u.state.objects; iter; iter = iter->next)
+ {
+ StackItem *stack_obj = iter->data;
+ GObject *object;
+ GSList *prop_iter;
+ GSList *style_iter;
+
+ g_assert (stack_obj->type == STACK_ITEM_OBJECT);
+ g_assert (stack_obj->u.object.id != NULL);
+
+ object = gtk_builder_get_object (parser_data->builder, stack_obj->u.object.id);
+
+ if (object == NULL)
+ {
+ g_critical ("Failed to locate object %s for binding.", stack_obj->u.object.id);
+ continue;
+ }
+
+ if (GTK_IS_WIDGET (object))
+ for (style_iter = stack_obj->u.object.classes; style_iter; style_iter = style_iter->next)
+ egg_state_machine_add_style (parser_data->self,
+ item->u.state.name,
+ GTK_WIDGET (object),
+ style_iter->data);
+
+ for (prop_iter = stack_obj->u.object.properties; prop_iter; prop_iter = prop_iter->next)
+ {
+ StackItem *stack_prop = prop_iter->data;
+ GObject *bind_source;
+
+ g_assert (stack_prop->type == STACK_ITEM_PROPERTY);
+
+ if ((stack_prop->u.property.bind_source != NULL) &&
+ (stack_prop->u.property.bind_property != NULL) &&
+ (bind_source = gtk_builder_get_object (parser_data->builder,
stack_prop->u.property.bind_source)))
+ {
+ egg_state_machine_add_binding (parser_data->self,
+ item->u.state.name,
+ bind_source,
+ stack_prop->u.property.bind_property,
+ object,
+ stack_prop->u.property.name,
+ stack_prop->u.property.bind_flags);
+ }
+ else if (stack_prop->u.property.text != NULL)
+ {
+ GParamSpec *pspec;
+ GValue value = { 0 };
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
stack_prop->u.property.name);
+
+ if (pspec == NULL)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_PROPERTY,
+ "No such property: %s",
+ stack_prop->u.property.name);
+ return;
+ }
+
+ if (g_type_is_a (pspec->value_type, G_TYPE_OBJECT))
+ {
+ GObject *relative;
+
+ relative = gtk_builder_get_object (parser_data->builder, stack_prop->u.property.text);
+ g_value_init (&value, pspec->value_type);
+ g_value_set_object (&value, relative);
+ }
+ else if (!gtk_builder_value_from_string (parser_data->builder,
+ pspec,
+ stack_prop->u.property.text,
+ &value,
+ error))
+ {
+ return;
+ }
+
+ egg_state_machine_add_property (parser_data->self,
+ item->u.state.name,
+ object,
+ stack_prop->u.property.name,
+ &value);
+
+ g_value_unset (&value);
+ }
+ }
+ }
+}
+
+static void
+add_object (StatesParserData *parser_data,
+ StackItem *parent,
+ StackItem *item)
+{
+ g_assert (parser_data != NULL);
+ g_assert (parent != NULL);
+ g_assert (parent->type == STACK_ITEM_STATE);
+ g_assert (item != NULL);
+ g_assert (item->type == STACK_ITEM_OBJECT);
+
+ parent->u.state.objects = g_slist_prepend (parent->u.state.objects, item);
+}
+
+static void
+add_property (StatesParserData *parser_data,
+ StackItem *parent,
+ StackItem *item)
+{
+ g_assert (parser_data != NULL);
+ g_assert (parent != NULL);
+ g_assert (parent->type == STACK_ITEM_OBJECT);
+ g_assert (item != NULL);
+ g_assert (item->type == STACK_ITEM_PROPERTY);
+
+ parent->u.object.properties = g_slist_prepend (parent->u.object.properties, item);
+}
+
+static gboolean
+check_parent (GMarkupParseContext *context,
+ const gchar *element_name,
+ GError **error)
+{
+ const GSList *stack;
+ const gchar *parent_name;
+ const gchar *our_name;
+
+ stack = g_markup_parse_context_get_element_stack (context);
+ our_name = stack->data;
+ parent_name = stack->next ? stack->next->data : "";
+
+ if (g_strcmp0 (parent_name, element_name) != 0)
+ {
+ gint line;
+ gint col;
+
+ g_markup_parse_context_get_position (context, &line, &col);
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_TAG,
+ "%d:%d: Element <%s> found in <%s>, expected <%s>.",
+ line, col, our_name, parent_name, element_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * flags_from_string:
+ *
+ * gtkbuilder.c
+ *
+ * Copyright (C) 1998-2002 James Henstridge <james daa com au>
+ * Copyright (C) 2006-2007 Async Open Source,
+ * Johan Dahlin <jdahlin async com br>,
+ * Henrique Romano <henrique async com br>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+gboolean
+flags_from_string (GType type,
+ const gchar *string,
+ guint *flags_value,
+ GError **error)
+{
+ GFlagsClass *fclass;
+ gchar *endptr, *prevptr;
+ guint i, j, value;
+ gchar *flagstr;
+ GFlagsValue *fv;
+ const gchar *flag;
+ gunichar ch;
+ gboolean eos, ret;
+
+ g_return_val_if_fail (G_TYPE_IS_FLAGS (type), FALSE);
+ g_return_val_if_fail (string != 0, FALSE);
+
+ ret = TRUE;
+
+ endptr = NULL;
+ errno = 0;
+ value = g_ascii_strtoull (string, &endptr, 0);
+ if (errno == 0 && endptr != string) /* parsed a number */
+ *flags_value = value;
+ else
+ {
+ fclass = g_type_class_ref (type);
+
+ flagstr = g_strdup (string);
+ for (value = i = j = 0; ; i++)
+ {
+
+ eos = flagstr[i] == '\0';
+
+ if (!eos && flagstr[i] != '|')
+ continue;
+
+ flag = &flagstr[j];
+ endptr = &flagstr[i];
+
+ if (!eos)
+ {
+ flagstr[i++] = '\0';
+ j = i;
+ }
+
+ /* trim spaces */
+ for (;;)
+ {
+ ch = g_utf8_get_char (flag);
+ if (!g_unichar_isspace (ch))
+ break;
+ flag = g_utf8_next_char (flag);
+ }
+
+ while (endptr > flag)
+ {
+ prevptr = g_utf8_prev_char (endptr);
+ ch = g_utf8_get_char (prevptr);
+ if (!g_unichar_isspace (ch))
+ break;
+ endptr = prevptr;
+ }
+
+ if (endptr > flag)
+ {
+ *endptr = '\0';
+ fv = g_flags_get_value_by_name (fclass, flag);
+
+ if (!fv)
+ fv = g_flags_get_value_by_nick (fclass, flag);
+
+ if (fv)
+ value |= fv->value;
+ else
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Unknown flag: `%s'",
+ flag);
+ ret = FALSE;
+ break;
+ }
+ }
+
+ if (eos)
+ {
+ *flags_value = value;
+ break;
+ }
+ }
+
+ g_free (flagstr);
+
+ g_type_class_unref (fclass);
+ }
+
+ return ret;
+}
+
+static void
+states_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ StatesParserData *parser_data = user_data;
+ StackItem *item;
+
+ g_assert (context != NULL);
+ g_assert (element_name != NULL);
+ g_assert (parser_data != NULL);
+
+ if (g_strcmp0 (element_name, "state") == 0)
+ {
+ const gchar *name;
+
+ if (!check_parent (context, "states", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ item = stack_item_new (STACK_ITEM_STATE);
+ item->u.state.name = g_strdup (name);
+ g_queue_push_head (parser_data->stack, item);
+ }
+ else if (g_strcmp0 (element_name, "states") == 0)
+ {
+ if (!check_parent (context, "object", error))
+ return;
+ }
+ else if (g_strcmp0 (element_name, "object") == 0)
+ {
+ const gchar *id;
+
+ if (!check_parent (context, "state", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "id", &id,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ item = stack_item_new (STACK_ITEM_OBJECT);
+ item->u.object.id = g_strdup (id);
+ g_queue_push_head (parser_data->stack, item);
+ }
+ else if (g_strcmp0 (element_name, "property") == 0)
+ {
+ const gchar *name = NULL;
+ const gchar *bind_source = NULL;
+ const gchar *bind_property = NULL;
+ const gchar *bind_flags_str = NULL;
+ GBindingFlags bind_flags = 0;
+
+ if (!check_parent (context, "object", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-source",
&bind_source,
+ G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-property",
&bind_property,
+ G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-flags",
&bind_flags_str,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if ((bind_flags_str != NULL) && !flags_from_string (G_TYPE_BINDING_FLAGS, bind_flags_str, &bind_flags,
error))
+ return;
+
+ item = stack_item_new (STACK_ITEM_PROPERTY);
+ item->u.property.name = g_strdup (name);
+ item->u.property.bind_source = g_strdup (bind_source);
+ item->u.property.bind_property = g_strdup (bind_property);
+ item->u.property.bind_flags = bind_flags;
+ g_queue_push_head (parser_data->stack, item);
+ }
+ else if (g_strcmp0 (element_name, "style") == 0)
+ {
+ if (!check_parent (context, "object", error))
+ return;
+ }
+ else if (g_strcmp0 (element_name, "class") == 0)
+ {
+ const gchar *name = NULL;
+
+ if (!check_parent (context, "style", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ item = g_queue_peek_head (parser_data->stack);
+ g_assert (item->type == STACK_ITEM_OBJECT);
+
+ item->u.object.classes = g_slist_prepend (item->u.object.classes, g_strdup (name));
+ }
+ else
+ {
+ const GSList *stack;
+ const gchar *parent_name;
+ const gchar *our_name;
+ gint line;
+ gint col;
+
+ stack = g_markup_parse_context_get_element_stack (context);
+ our_name = stack->data;
+ parent_name = stack->next ? stack->next->data : "";
+
+ g_markup_parse_context_get_position (context, &line, &col);
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_TAG,
+ "%d:%d: Unknown element <%s> found in <%s>.",
+ line, col, our_name, parent_name);
+ }
+
+ return;
+}
+
+static void
+states_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ StatesParserData *parser_data = user_data;
+ StackItem *item;
+
+ g_assert (context != NULL);
+ g_assert (element_name != NULL);
+ g_assert (parser_data != NULL);
+
+ if (g_strcmp0 (element_name, "state") == 0)
+ {
+ item = g_queue_pop_head (parser_data->stack);
+ g_assert (item->type == STACK_ITEM_STATE);
+ add_state (parser_data, item, error);
+ stack_item_free (item);
+ }
+ else if (g_strcmp0 (element_name, "object") == 0)
+ {
+ StackItem *parent;
+
+ item = g_queue_pop_head (parser_data->stack);
+ g_assert (item->type == STACK_ITEM_OBJECT);
+
+ parent = g_queue_peek_head (parser_data->stack);
+ g_assert (parent->type == STACK_ITEM_STATE);
+
+ add_object (parser_data, parent, item);
+ }
+ else if (g_strcmp0 (element_name, "property") == 0)
+ {
+ StackItem *parent;
+
+ item = g_queue_pop_head (parser_data->stack);
+ g_assert (item->type == STACK_ITEM_PROPERTY);
+
+ parent = g_queue_peek_head (parser_data->stack);
+ g_assert (parent->type == STACK_ITEM_OBJECT);
+
+ add_property (parser_data, parent, item);
+ }
+}
+
+static void
+states_parser_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ StatesParserData *parser_data = user_data;
+ StackItem *item;
+
+ g_assert (parser_data != NULL);
+
+ item = g_queue_peek_head (parser_data->stack);
+ if ((item != NULL) && (item->type == STACK_ITEM_PROPERTY))
+ item->u.property.text = g_strndup (text, text_len);
+}
+
+static GMarkupParser StatesParser = {
+ states_parser_start_element,
+ states_parser_end_element,
+ states_parser_text,
+};
+
+static gboolean
+egg_state_machine_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data)
+{
+ EggStateMachine *self = (EggStateMachine *)buildable;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (tagname != NULL);
+ g_assert (parser != NULL);
+ g_assert (data != NULL);
+
+ if (g_strcmp0 (tagname, "states") == 0)
+ {
+ StatesParserData *parser_data;
+
+ parser_data = g_slice_new0 (StatesParserData);
+ parser_data->self = g_object_ref (buildable);
+ parser_data->builder = g_object_ref (builder);
+ parser_data->stack = g_queue_new ();
+
+ egg_state_machine_freeze (self);
+
+ *parser = StatesParser;
+ *data = parser_data;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+egg_state_machine_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data)
+{
+ EggStateMachine *self = (EggStateMachine *)buildable;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (tagname != NULL);
+
+ if (g_strcmp0 (tagname, "states") == 0)
+ {
+ StatesParserData *parser_data = user_data;
+ gchar *state;
+
+ g_object_unref (parser_data->self);
+ g_object_unref (parser_data->builder);
+ g_queue_free_full (parser_data->stack, (GDestroyNotify)stack_item_free);
+ g_slice_free (StatesParserData, parser_data);
+
+ egg_state_machine_thaw (self);
+
+ /* XXX: reapply current state */
+ state = g_strdup (egg_state_machine_get_state (self));
+ egg_state_machine_set_state (self, NULL);
+ egg_state_machine_set_state (self, state);
+ g_free (state);
+ }
+}
+
+void
+egg_state_machine_buildable_iface_init (GtkBuildableIface *iface)
+{
+ g_assert (iface != NULL);
+
+ egg_state_machine_parent_buildable = g_type_interface_peek_parent (iface);
+
+ iface->custom_tag_start = egg_state_machine_buildable_custom_tag_start;
+ iface->custom_finished = egg_state_machine_buildable_custom_finished;
+}
diff --git a/contrib/egg/egg-state-machine-buildable.h b/contrib/egg/egg-state-machine-buildable.h
new file mode 100644
index 0000000..636969e
--- /dev/null
+++ b/contrib/egg/egg-state-machine-buildable.h
@@ -0,0 +1,30 @@
+/* egg-state-machine-buildable.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_STATE_MACHINE_BUILDABLE_H
+#define EGG_STATE_MACHINE_BUILDABLE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void egg_state_machine_buildable_iface_init (GtkBuildableIface *iface);
+
+G_END_DECLS
+
+#endif /* EGG_STATE_MACHINE_BUILDABLE_H */
diff --git a/contrib/egg/egg-state-machine-private.h b/contrib/egg/egg-state-machine-private.h
new file mode 100644
index 0000000..dec731b
--- /dev/null
+++ b/contrib/egg/egg-state-machine-private.h
@@ -0,0 +1,59 @@
+/* egg-state-machine-private.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_STATE_MACHINE_PRIVATE_H
+#define EGG_STATE_MACHINE_PRIVATE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gchar *state;
+ GPtrArray *actions;
+ GHashTable *states;
+ gchar *freeze_state;
+ gint freeze_count;
+} EggStateMachinePrivate;
+
+typedef struct
+{
+ gchar *name;
+ GHashTable *signals;
+ GHashTable *bindings;
+ GPtrArray *properties;
+ GPtrArray *styles;
+} EggState;
+
+typedef struct
+{
+ gpointer object;
+ gchar *property;
+ GValue value;
+} EggStateProperty;
+
+typedef struct
+{
+ GtkWidget *widget;
+ gchar *name;
+} EggStateStyle;
+
+G_END_DECLS
+
+#endif /* EGG_STATE_MACHINE_PRIVATE_H */
diff --git a/contrib/egg/egg-state-machine.c b/contrib/egg/egg-state-machine.c
index 819fc38..1b4996e 100644
--- a/contrib/egg/egg-state-machine.c
+++ b/contrib/egg/egg-state-machine.c
@@ -16,43 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define G_LOG_DOMAIN "egg-state-machine"
+
#include <glib/gi18n.h>
#include "egg-binding-set.h"
#include "egg-signal-group.h"
+
#include "egg-state-machine.h"
+#include "egg-state-machine-action.h"
+#include "egg-state-machine-buildable.h"
+#include "egg-state-machine-private.h"
-typedef struct
-{
- gchar *state;
-
- /*
- * Containers for lazily bound signals and bindings.
- *
- * Each is a GHashTable indexed by state name, containing another GHashTable indexed by
- * source object.
- */
- GHashTable *binding_sets_by_state;
- GHashTable *signal_groups_by_state;
-
- /*
- * Container for actions which should have sensitivity mutated during state transitions.
- *
- * GHashTable of GPtrArray of ActionState.
- */
- GHashTable *actions_by_state;
-
- gsize sequence;
-} EggStateMachinePrivate;
-
-typedef struct
-{
- GSimpleAction *action;
- guint invert_enabled : 1;
-} ActionState;
+G_DEFINE_QUARK (egg_state_machine_error, egg_state_machine_error)
-G_DEFINE_TYPE_WITH_PRIVATE (EggStateMachine, egg_state_machine, G_TYPE_OBJECT)
-G_DEFINE_QUARK (EggStateMachineError, egg_state_machine_error)
+G_DEFINE_TYPE_WITH_CODE (EggStateMachine, egg_state_machine, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (EggStateMachine)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ egg_state_machine_buildable_iface_init))
enum {
PROP_0,
@@ -60,208 +41,155 @@ enum {
LAST_PROP
};
-enum {
- TRANSITION,
- LAST_SIGNAL
-};
-
static GParamSpec *gParamSpecs [LAST_PROP];
-static guint gSignals [LAST_SIGNAL];
static void
-action_state_free (gpointer data)
+egg_state_free (gpointer data)
{
- ActionState *state = data;
-
- g_clear_object (&state->action);
- g_slice_free (ActionState, state);
+ EggState *state = data;
+
+ g_free (state->name);
+ g_hash_table_unref (state->signals);
+ g_hash_table_unref (state->bindings);
+ g_ptr_array_unref (state->properties);
+ g_ptr_array_unref (state->styles);
+ g_slice_free (EggState, state);
}
-static gboolean
-egg_state_transition_accumulator (GSignalInvocationHint *hint,
- GValue *return_value,
- const GValue *handler_return,
- gpointer data)
+static void
+egg_state_property_free (gpointer data)
{
- EggStateTransition ret;
-
- ret = g_value_get_enum (handler_return);
+ EggStateProperty *prop = data;
- if (ret == EGG_STATE_TRANSITION_INVALID)
- {
- g_value_set_enum (return_value, ret);
- return FALSE;
- }
-
- return TRUE;
+ g_free (prop->property);
+ g_value_unset (&prop->value);
+ g_slice_free (EggStateProperty, prop);
}
-const gchar *
-egg_state_machine_get_state (EggStateMachine *self)
+static void
+egg_state_style_free (gpointer data)
{
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ EggStateStyle *style = data;
- g_return_val_if_fail (EGG_IS_STATE_MACHINE (self), NULL);
-
- return priv->state;
+ g_free (style->name);
+ g_slice_free (EggStateStyle, style);
}
static void
-egg_state_machine_do_transition (EggStateMachine *self,
- const gchar *new_state)
+egg_state_apply (EggStateMachine *self,
+ EggState *state)
{
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
GHashTableIter iter;
- const gchar *key;
- GPtrArray *action_states;
- GHashTable *value;
+ gpointer key;
+ gpointer value;
gsize i;
g_assert (EGG_IS_STATE_MACHINE (self));
- g_assert (new_state != NULL);
+ g_assert (state != NULL);
- priv->sequence++;
+ g_hash_table_iter_init (&iter, state->bindings);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ egg_binding_set_set_source (value, key);
- g_free (priv->state);
- priv->state = g_strdup (new_state);
+ g_hash_table_iter_init (&iter, state->signals);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ egg_signal_group_set_target (value, key);
- g_hash_table_iter_init (&iter, priv->signal_groups_by_state);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ for (i = 0; i < state->properties->len; i++)
{
- GHashTable *signal_groups = value;
- GHashTableIter groups_iter;
- EggSignalGroup *signal_group;
- gpointer instance;
- gboolean enabled = (g_strcmp0 (key, new_state) == 0);
-
- g_hash_table_iter_init (&groups_iter, signal_groups);
+ EggStateProperty *prop;
- while (g_hash_table_iter_next (&groups_iter, &instance, (gpointer *)&signal_group))
- {
- g_assert (G_IS_OBJECT (instance));
- g_assert (EGG_IS_SIGNAL_GROUP (signal_group));
-
- egg_signal_group_set_target (signal_group, enabled ? instance : NULL);
- }
+ prop = g_ptr_array_index (state->properties, i);
+ g_object_set_property (prop->object, prop->property, &prop->value);
}
- g_hash_table_iter_init (&iter, priv->binding_sets_by_state);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ for (i = 0; i < state->styles->len; i++)
{
- GHashTable *binding_sets = value;
- GHashTableIter groups_iter;
- EggBindingSet *binding_set;
- gpointer instance;
- gboolean enabled = (g_strcmp0 (key, new_state) == 0);
+ EggStateStyle *style;
+ GtkStyleContext *style_context;
- g_hash_table_iter_init (&groups_iter, binding_sets);
-
- while (g_hash_table_iter_next (&groups_iter, &instance, (gpointer *)&binding_set))
- {
- g_assert (G_IS_OBJECT (instance));
- g_assert (EGG_IS_BINDING_SET (binding_set));
-
- egg_binding_set_set_source (binding_set, enabled ? instance : NULL);
- }
+ style = g_ptr_array_index (state->styles, i);
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (style->widget));
+ gtk_style_context_add_class (style_context, style->name);
}
+}
- /* apply GSimpleAction:enabled to non-matching states */
- g_hash_table_iter_init (&iter, priv->actions_by_state);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&action_states))
- {
- if (g_strcmp0 (key, priv->state) == 0)
- continue;
+static void
+egg_state_unapply (EggStateMachine *self,
+ EggState *state)
+{
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ gsize i;
- for (i = 0; i < action_states->len; i++)
- {
- ActionState *action_state;
+ g_assert (EGG_IS_STATE_MACHINE (self));
+ g_assert (state != NULL);
- action_state = g_ptr_array_index (action_states, i);
- g_simple_action_set_enabled (action_state->action, action_state->invert_enabled);
- }
- }
+ g_hash_table_iter_init (&iter, state->bindings);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ egg_binding_set_set_source (value, NULL);
+
+ g_hash_table_iter_init (&iter, state->signals);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ egg_signal_group_set_target (value, NULL);
- /* apply GSimpleAction:enabled to matching state */
- action_states = g_hash_table_lookup (priv->actions_by_state, priv->state);
- if (action_states != NULL)
+ for (i = 0; i < state->styles->len; i++)
{
- for (i = 0; i < action_states->len; i++)
- {
- ActionState *action_state;
+ EggStateStyle *style;
+ GtkStyleContext *style_context;
- action_state = g_ptr_array_index (action_states, i);
- g_simple_action_set_enabled (action_state->action, !action_state->invert_enabled);
- }
+ style = g_ptr_array_index (state->styles, i);
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (style->widget));
+ gtk_style_context_remove_class (style_context, style->name);
}
}
-/**
- * egg_state_machine_transition:
- * @self: A #EggStateMachine.
- * @new_state: The name of the new state.
- * @error: A location for a #GError, or %NULL.
- *
- * Attempts to change the state of the state machine to @new_state.
- *
- * This operation can fail, in which %EGG_STATE_TRANSITION_INVALID will be
- * returned and @error will be set.
- *
- * Upon success, %EGG_STATE_TRANSITION_SUCCESS is returned.
- *
- * Returns: An #EggStateTransition.
- */
-EggStateTransition
-egg_state_machine_transition (EggStateMachine *self,
- const gchar *new_state,
- GError **error)
+static EggState *
+egg_state_machine_get_state_obj (EggStateMachine *self,
+ const gchar *state)
{
EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- g_autofree gchar *old_state = NULL;
- EggStateTransition ret = EGG_STATE_TRANSITION_IGNORED;
- g_autoptr(GError) local_error = NULL;
- gsize sequence;
-
- g_return_val_if_fail (EGG_IS_STATE_MACHINE (self), EGG_STATE_TRANSITION_INVALID);
- g_return_val_if_fail (new_state != NULL, EGG_STATE_TRANSITION_INVALID);
- g_return_val_if_fail (error == NULL || *error == NULL, EGG_STATE_TRANSITION_INVALID);
-
- if (g_strcmp0 (new_state, priv->state) == 0)
- return EGG_STATE_TRANSITION_SUCCESS;
+ EggState *state_obj;
- /* Be careful with reentrancy. */
-
- old_state = g_strdup (priv->state);
- sequence = priv->sequence;
-
- g_signal_emit (self, gSignals [TRANSITION], 0, old_state, new_state, &local_error, &ret);
+ g_assert (EGG_IS_STATE_MACHINE (self));
- if (ret == EGG_STATE_TRANSITION_INVALID)
- {
- if (local_error == NULL)
- local_error = g_error_new_literal (EGG_STATE_MACHINE_ERROR,
- EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION,
- "Unknown error during state transition.");
- g_propagate_error (error, local_error);
- local_error = NULL;
- return ret;
- }
+ state_obj = g_hash_table_lookup (priv->states, state);
- if (sequence == priv->sequence)
+ if (state_obj == NULL)
{
- egg_state_machine_do_transition (self, new_state);
- g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_STATE]);
+ state_obj = g_slice_new0 (EggState);
+ state_obj->name = g_strdup (state);
+ state_obj->signals = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+ state_obj->bindings = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+ state_obj->properties = g_ptr_array_new_with_free_func (egg_state_property_free);
+ state_obj->styles = g_ptr_array_new_with_free_func (egg_state_style_free);
+ g_hash_table_insert (priv->states, g_strdup (state), state_obj);
}
- return EGG_STATE_TRANSITION_SUCCESS;
+ return state_obj;
}
-static EggStateTransition
-egg_state_machine_real_transition (EggStateMachine *self,
- const gchar *old_state,
- const gchar *new_state,
- GError **error)
+static void
+egg_state_machine_transition (EggStateMachine *self,
+ const gchar *old_state,
+ const gchar *new_state)
{
- return EGG_STATE_TRANSITION_IGNORED;
+ EggState *state_obj;
+
+ g_assert (EGG_IS_STATE_MACHINE (self));
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ if (old_state && (state_obj = egg_state_machine_get_state_obj (self, old_state)))
+ egg_state_unapply (self, state_obj);
+
+ if (new_state && (state_obj = egg_state_machine_get_state_obj (self, new_state)))
+ egg_state_apply (self, state_obj);
+
+ g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_STATE]);
+
+ g_object_thaw_notify (G_OBJECT (self));
}
static void
@@ -270,10 +198,8 @@ egg_state_machine_finalize (GObject *object)
EggStateMachine *self = (EggStateMachine *)object;
EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+ g_clear_pointer (&priv->states, g_hash_table_unref);
g_clear_pointer (&priv->state, g_free);
- g_clear_pointer (&priv->binding_sets_by_state, g_hash_table_unref);
- g_clear_pointer (&priv->signal_groups_by_state, g_hash_table_unref);
- g_clear_pointer (&priv->actions_by_state, g_hash_table_unref);
G_OBJECT_CLASS (egg_state_machine_parent_class)->finalize (object);
}
@@ -304,12 +230,11 @@ egg_state_machine_set_property (GObject *object,
GParamSpec *pspec)
{
EggStateMachine *self = EGG_STATE_MACHINE (object);
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
switch (prop_id)
{
case PROP_STATE:
- priv->state = g_value_dup_string (value);
+ egg_state_machine_set_state (self, g_value_get_string (value));
break;
default:
@@ -326,42 +251,14 @@ egg_state_machine_class_init (EggStateMachineClass *klass)
object_class->get_property = egg_state_machine_get_property;
object_class->set_property = egg_state_machine_set_property;
- klass->transition = egg_state_machine_real_transition;
-
gParamSpecs [PROP_STATE] =
g_param_spec_string ("state",
_("State"),
_("The current state of the machine."),
NULL,
- (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
-
- /**
- * EggStateMachine::transition:
- * @self: An #EggStateMachine.
- * @old_state: The current state.
- * @new_state: The new state.
- * @error: (ctype GError**): A location for a #GError, or %NULL.
- *
- * Determines if the transition is allowed.
- *
- * If the state transition is invalid, @error should be set to a new #GError.
- *
- * Returns: %TRUE if the state transition is acceptable.
- */
- gSignals [TRANSITION] =
- g_signal_new ("transition",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EggStateMachineClass, transition),
- egg_state_transition_accumulator, NULL,
- NULL,
- EGG_TYPE_STATE_TRANSITION,
- 3,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_POINTER);
}
static void
@@ -369,268 +266,232 @@ egg_state_machine_init (EggStateMachine *self)
{
EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- priv->binding_sets_by_state =
- g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify)g_hash_table_destroy);
-
- priv->signal_groups_by_state =
- g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify)g_hash_table_destroy);
-
- priv->actions_by_state =
- g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify)g_ptr_array_unref);
+ priv->states = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, egg_state_free);
}
-static void
-egg_state_machine__connect_object_weak_notify (gpointer data,
- GObject *where_object_was)
+EggStateMachine *
+egg_state_machine_new (void)
{
- EggStateMachine *self = data;
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- GHashTableIter iter;
- const gchar *key;
- GHashTable *value;
+ return g_object_new (EGG_TYPE_STATE_MACHINE, NULL);
+}
- g_assert (EGG_IS_STATE_MACHINE (self));
- g_assert (where_object_was != NULL);
+/**
+ * egg_state_machine_get_state:
+ * @self: the #EggStateMachine.
+ *
+ * Gets the #EggStateMachine:state property. This is the name of the
+ * current state of the machine.
+ *
+ * Returns: The current state of the machine.
+ */
+const gchar *
+egg_state_machine_get_state (EggStateMachine *self)
+{
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- g_hash_table_iter_init (&iter, priv->signal_groups_by_state);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
- {
- GHashTable *signal_groups = value;
+ g_return_if_fail (EGG_IS_STATE_MACHINE (self));
- g_hash_table_remove (signal_groups, where_object_was);
- }
+ return priv->state;
}
+/**
+ * egg_state_machine_set_state:
+ * @self: the #EggStateMachine @self: the #
+ *
+ * Sets the #EggStateMachine:state property.
+ *
+ * Registered state transformations will be applied during the state
+ * transformation.
+ *
+ * If the transition results in a cyclic operation, the state will stop at
+ * the last state before the cycle was detected.
+ */
void
-egg_state_machine_connect_object (EggStateMachine *self,
- const gchar *state,
- gpointer instance,
- const gchar *detailed_signal,
- GCallback callback,
- gpointer user_data,
- GConnectFlags flags)
+egg_state_machine_set_state (EggStateMachine *self,
+ const gchar *state)
{
EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- GHashTable *signal_groups;
- EggSignalGroup *signal_group;
- gboolean created = FALSE;
g_return_if_fail (EGG_IS_STATE_MACHINE (self));
- g_return_if_fail (state != NULL);
- g_return_if_fail (G_IS_OBJECT (instance));
- g_return_if_fail (detailed_signal != NULL);
- g_return_if_fail (g_signal_parse_name (detailed_signal,
- G_TYPE_FROM_INSTANCE (instance),
- NULL, NULL, FALSE) != 0);
- g_return_if_fail (callback != NULL);
-
- signal_groups = g_hash_table_lookup (priv->signal_groups_by_state, state);
- if (signal_groups == NULL)
+ if (g_strcmp0 (priv->state, state) != 0)
{
- signal_groups = g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
- g_object_unref);
- g_hash_table_insert (priv->signal_groups_by_state, g_strdup (state), signal_groups);
- }
+ gchar *old_state = priv->state;
+ gchar *new_state = g_strdup (state);
- g_assert (signal_groups != NULL);
+ /*
+ * Steal ownership of old state and create a copy for new state
+ * to ensure that we own the references. State machines tend to
+ * get used in re-entrant fashion.
+ */
- signal_group = g_hash_table_lookup (signal_groups, instance);
+ priv->state = g_strdup (state);
- if (signal_group == NULL)
- {
- created = TRUE;
- signal_group = egg_signal_group_new (G_TYPE_FROM_INSTANCE (instance));
- g_hash_table_insert (signal_groups, instance, signal_group);
- g_object_weak_ref (instance,
- (GWeakNotify)egg_state_machine__connect_object_weak_notify,
- self);
+ if (priv->freeze_count == 0)
+ egg_state_machine_transition (self, old_state, state);
+
+ g_free (new_state);
+ g_free (old_state);
}
+}
- egg_signal_group_connect_object (signal_group, detailed_signal, callback, user_data, flags);
+GAction *
+egg_state_machine_create_action (EggStateMachine *self,
+ const gchar *name)
+{
+ g_return_val_if_fail (EGG_IS_STATE_MACHINE (self), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
- if ((created == TRUE) && (g_strcmp0 (state, priv->state) == 0))
- egg_signal_group_set_target (signal_group, instance);
+ return g_object_new (EGG_TYPE_STATE_MACHINE_ACTION,
+ "state-machine", self,
+ "name", name,
+ NULL);
}
-static void
-egg_state_machine__bind_source_weak_notify (gpointer data,
- GObject *where_object_was)
+void
+egg_state_machine_add_property (EggStateMachine *self,
+ const gchar *state,
+ gpointer object,
+ const gchar *property,
+ const GValue *value)
{
- EggStateMachine *self = data;
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- GHashTableIter iter;
- const gchar *key;
- GHashTable *value;
+ EggState *state_obj;
+ EggStateProperty *state_prop;
- g_assert (EGG_IS_STATE_MACHINE (self));
- g_assert (where_object_was != NULL);
+ g_return_if_fail (EGG_IS_STATE_MACHINE (self));
+ g_return_if_fail (state != NULL);
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (property != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
- g_hash_table_iter_init (&iter, priv->binding_sets_by_state);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
- {
- GHashTable *binding_sets = value;
+ state_obj = egg_state_machine_get_state_obj (self, state);
- g_hash_table_remove (binding_sets, where_object_was);
- }
+ state_prop = g_slice_new0 (EggStateProperty);
+ state_prop->object = object;
+ state_prop->property = g_strdup (property);
+ g_value_init (&state_prop->value, G_VALUE_TYPE (value));
+ g_value_copy (value, &state_prop->value);
+
+ g_ptr_array_add (state_obj->properties, state_prop);
}
void
-egg_state_machine_bind (EggStateMachine *self,
- const gchar *state,
- gpointer source,
- const gchar *source_property,
- gpointer target,
- const gchar *target_property,
- GBindingFlags flags)
+egg_state_machine_add_binding (EggStateMachine *self,
+ const gchar *state,
+ gpointer source_object,
+ const gchar *source_property,
+ gpointer target_object,
+ const gchar *target_property,
+ GBindingFlags flags)
{
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- GHashTable *binding_sets;
- EggBindingSet *binding_set;
- gboolean created = FALSE;
+ EggBindingSet *bindings;
+ EggState *state_obj;
g_return_if_fail (EGG_IS_STATE_MACHINE (self));
g_return_if_fail (state != NULL);
- g_return_if_fail (G_IS_OBJECT (source));
+ g_return_if_fail (source_object != NULL);
g_return_if_fail (source_property != NULL);
- g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (source),
- source_property) != NULL);
- g_return_if_fail (G_IS_OBJECT (target));
+ g_return_if_fail (target_object != NULL);
g_return_if_fail (target_property != NULL);
- g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (target),
- target_property) != NULL);
- /* Use G_BINDING_SYNC_CREATE as we lazily connect them. */
- flags |= G_BINDING_SYNC_CREATE;
+ state_obj = egg_state_machine_get_state_obj (self, state);
- binding_sets = g_hash_table_lookup (priv->binding_sets_by_state, state);
+ bindings = g_hash_table_lookup (state_obj->bindings, source_object);
- if (binding_sets == NULL)
+ if (bindings == NULL)
{
- binding_sets = g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
- g_object_unref);
- g_hash_table_insert (priv->binding_sets_by_state, g_strdup (state), binding_sets);
+ bindings = egg_binding_set_new ();
+ g_hash_table_insert (state_obj->bindings, source_object, bindings);
}
- g_assert (binding_sets != NULL);
+ egg_binding_set_bind (bindings, source_property, target_object, target_property, flags);
+}
- binding_set = g_hash_table_lookup (binding_sets, source);
+void
+egg_state_machine_add_style (EggStateMachine *self,
+ const gchar *state,
+ GtkWidget *widget,
+ const gchar *style)
+{
+ EggState *state_obj;
+ EggStateStyle *style_obj;
- if (binding_set == NULL)
- {
- created = TRUE;
- binding_set = egg_binding_set_new ();
- g_hash_table_insert (binding_sets, source, binding_set);
- g_object_weak_ref (source,
- (GWeakNotify)egg_state_machine__bind_source_weak_notify,
- self);
- }
+ g_return_if_fail (EGG_IS_STATE_MACHINE (self));
+ g_return_if_fail (state != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (style);
- egg_binding_set_bind (binding_set,
- source_property,
- target,
- target_property,
- flags);
+ state_obj = egg_state_machine_get_state_obj (self, state);
- if ((created == TRUE) && (g_strcmp0 (state, priv->state) == 0))
- egg_binding_set_set_source (binding_set, source);
+ style_obj = g_slice_new0 (EggStateStyle);
+ style_obj->name = g_strdup (style);
+ style_obj->widget = widget;
+
+ g_ptr_array_add (state_obj->styles, style_obj);
}
void
-egg_state_machine_add_action (EggStateMachine *self,
- const gchar *state,
- GSimpleAction *action,
- gboolean invert_enabled)
+egg_state_machine_connect_object (EggStateMachine *self,
+ const gchar *state,
+ gpointer source,
+ const gchar *detailed_signal,
+ GCallback callback,
+ gpointer user_data,
+ GConnectFlags flags)
{
- EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
- ActionState *action_state;
- GPtrArray *actions;
- gboolean enabled;
+ EggState *state_obj;
+ EggSignalGroup *signals;
g_return_if_fail (EGG_IS_STATE_MACHINE (self));
g_return_if_fail (state != NULL);
- g_return_if_fail (G_IS_SIMPLE_ACTION (action));
-
- action_state = g_slice_new0 (ActionState);
- action_state->action = g_object_ref (action);
- action_state->invert_enabled = invert_enabled;
+ g_return_if_fail (G_IS_OBJECT (source));
+ g_return_if_fail (detailed_signal != NULL);
+ g_return_if_fail (callback != NULL);
- actions = g_hash_table_lookup (priv->actions_by_state, state);
+ state_obj = egg_state_machine_get_state_obj (self, state);
- if (actions == NULL)
+ if (!(signals = g_hash_table_lookup (state_obj->signals, source)))
{
- actions = g_ptr_array_new_with_free_func (action_state_free);
- g_hash_table_insert (priv->actions_by_state, g_strdup (state), actions);
+ signals = egg_signal_group_new (G_OBJECT_TYPE (source));
+ g_hash_table_insert (state_obj->signals, source, signals);
}
- g_ptr_array_add (actions, action_state);
-
- enabled = (g_strcmp0 (state, priv->state) == 0);
- if (invert_enabled)
- enabled = !enabled;
-
- g_simple_action_set_enabled (action, enabled);
+ egg_signal_group_connect_object (signals, detailed_signal, callback, user_data, flags);
}
-EggStateMachine *
-egg_state_machine_new (void)
+void
+egg_state_machine_freeze (EggStateMachine *self)
{
- return g_object_new (EGG_TYPE_STATE_MACHINE, NULL);
-}
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
-GType
-egg_state_machine_error_get_type (void)
-{
- static gsize type_id;
+ g_return_if_fail (EGG_IS_STATE_MACHINE (self));
+ g_return_if_fail (priv->freeze_count >= 0);
- if (g_once_init_enter (&type_id))
+ if (++priv->freeze_count == 1)
{
- static const GEnumValue values[] = {
- { EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION,
- "EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION",
- "invalid-transition" },
- { 0 }
- };
- gsize _type_id;
-
- _type_id = g_enum_register_static ("EggStateMachineError", values);
- g_once_init_leave (&type_id, _type_id);
+ g_assert (priv->freeze_state == NULL);
+ priv->freeze_state = g_strdup (priv->state);
}
-
- return type_id;
}
-GType
-egg_state_transition_get_type (void)
+void
+egg_state_machine_thaw (EggStateMachine *self)
{
- static gsize type_id;
+ EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_STATE_MACHINE (self));
+ g_return_if_fail (priv->freeze_count > 0);
- if (g_once_init_enter (&type_id))
+ if (--priv->freeze_count == 0)
{
- static const GEnumValue values[] = {
- { EGG_STATE_TRANSITION_IGNORED, "EGG_STATE_TRANSITION_IGNORED", "ignored" },
- { EGG_STATE_TRANSITION_INVALID, "EGG_STATE_TRANSITION_INVALID", "invalid" },
- { EGG_STATE_TRANSITION_SUCCESS, "EGG_STATE_TRANSITION_SUCCESS", "success" },
- { 0 }
- };
- gsize _type_id;
-
- _type_id = g_enum_register_static ("EggStateTransition", values);
- g_once_init_leave (&type_id, _type_id);
- }
+ if (g_strcmp0 (priv->freeze_state, priv->state) != 0)
+ {
+ gchar *state;
- return type_id;
+ state = priv->freeze_state;
+ priv->freeze_state = NULL;
+ egg_state_machine_set_state (self, state);
+ g_free (state);
+ }
+ }
}
diff --git a/contrib/egg/egg-state-machine.h b/contrib/egg/egg-state-machine.h
index beb52a7..2a10520 100644
--- a/contrib/egg/egg-state-machine.h
+++ b/contrib/egg/egg-state-machine.h
@@ -19,65 +19,50 @@
#ifndef EGG_STATE_MACHINE_H
#define EGG_STATE_MACHINE_H
-#include <gio/gio.h>
+#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define EGG_TYPE_STATE_MACHINE (egg_state_machine_get_type())
-#define EGG_TYPE_STATE_MACHINE_ERROR (egg_state_machine_get_type())
-#define EGG_STATE_MACHINE_ERROR (egg_state_machine_error_quark())
-#define EGG_TYPE_STATE_TRANSITION (egg_state_transition_get_type())
+#define EGG_TYPE_STATE_MACHINE (egg_state_machine_get_type())
G_DECLARE_DERIVABLE_TYPE (EggStateMachine, egg_state_machine, EGG, STATE_MACHINE, GObject)
-typedef enum
-{
- EGG_STATE_TRANSITION_IGNORED = 0,
- EGG_STATE_TRANSITION_SUCCESS = 1,
- EGG_STATE_TRANSITION_INVALID = 2,
-} EggStateTransition;
-
-typedef enum
-{
- EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION = 1,
-} EggStateMachineError;
-
struct _EggStateMachineClass
{
GObjectClass parent;
-
- EggStateTransition (*transition) (EggStateMachine *self,
- const gchar *old_state,
- const gchar *new_state,
- GError **error);
};
-GType egg_state_transition_get_type (void);
-GType egg_state_machine_error_get_type (void);
-GQuark egg_state_machine_error_quark (void);
-EggStateMachine *egg_state_machine_new (void);
-EggStateTransition egg_state_machine_transition (EggStateMachine *self,
- const gchar *new_state,
- GError **error);
-const gchar *egg_state_machine_get_state (EggStateMachine *self);
-void egg_state_machine_bind (EggStateMachine *self,
- const gchar *state,
- gpointer source,
- const gchar *source_property,
- gpointer target,
- const gchar *target_property,
- GBindingFlags flags);
-void egg_state_machine_connect_object (EggStateMachine *self,
- const gchar *state,
- gpointer instance,
- const gchar *detailed_signal,
- GCallback callback,
- gpointer user_data,
- GConnectFlags flags);
-void egg_state_machine_add_action (EggStateMachine *self,
- const gchar *state,
- GSimpleAction *action,
- gboolean invert_enabled);
+EggStateMachine *egg_state_machine_new (void);
+const gchar *egg_state_machine_get_state (EggStateMachine *self);
+void egg_state_machine_set_state (EggStateMachine *self,
+ const gchar *state);
+GAction *egg_state_machine_create_action (EggStateMachine *self,
+ const gchar *name);
+void egg_state_machine_add_property (EggStateMachine *self,
+ const gchar *state,
+ gpointer object,
+ const gchar *property,
+ const GValue *value);
+void egg_state_machine_add_binding (EggStateMachine *self,
+ const gchar *state,
+ gpointer source_object,
+ const gchar *source_property,
+ gpointer target_object,
+ const gchar *target_property,
+ GBindingFlags flags);
+void egg_state_machine_add_style (EggStateMachine *self,
+ const gchar *state,
+ GtkWidget *widget,
+ const gchar *style);
+void egg_state_machine_freeze (EggStateMachine *self);
+void egg_state_machine_thaw (EggStateMachine *self);
+void egg_state_machine_connect_object (EggStateMachine *self,
+ const gchar *state,
+ gpointer source,
+ const gchar *detailed_signal,
+ GCallback callback,
+ gpointer user_data,
+ GConnectFlags flags);
G_END_DECLS
diff --git a/tests/test-egg-state-machine.c b/tests/test-egg-state-machine.c
index d221503..849a43b 100644
--- a/tests/test-egg-state-machine.c
+++ b/tests/test-egg-state-machine.c
@@ -188,30 +188,22 @@ assert_prop_equal (gpointer obja,
g_value_unset (&vb);
}
-static EggStateTransition
-transition_cb (EggStateMachine *machine,
- const gchar *old_state,
- const gchar *new_state,
- gpointer user_data)
-{
- /* allow any state to state3, except state2 */
-
- if ((g_strcmp0 (old_state, "state2") == 0) && (g_strcmp0 (new_state, "state3") == 0))
- return EGG_STATE_TRANSITION_INVALID;
-
- return EGG_STATE_TRANSITION_IGNORED;
-}
-
static void
test_state_machine_basic (void)
{
EggStateMachine *machine;
- EggStateTransition ret;
GSimpleAction *action;
TestObject *dummy;
TestObject *obj1;
TestObject *obj2;
- GError *error = NULL;
+ GValue vtrue = { 0 };
+ GValue vfalse = { 0 };
+
+ g_value_init (&vtrue, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&vtrue, TRUE);
+
+ g_value_init (&vfalse, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&vfalse, FALSE);
machine = egg_state_machine_new ();
g_object_add_weak_pointer (G_OBJECT (machine), (gpointer *)&machine);
@@ -221,6 +213,8 @@ test_state_machine_basic (void)
obj1 = g_object_new (TEST_TYPE_OBJECT, NULL);
obj2 = g_object_new (TEST_TYPE_OBJECT, NULL);
+ g_simple_action_set_enabled (action, FALSE);
+
#if 0
g_print ("obj1=%p obj2=%p dummy=%p\n", obj1, obj2, dummy);
#endif
@@ -230,18 +224,17 @@ test_state_machine_basic (void)
egg_state_machine_connect_object (machine, "state2", obj2, "frobnicate",
G_CALLBACK (obj2_frobnicate), dummy, G_CONNECT_SWAPPED);
- egg_state_machine_bind (machine, "state1", obj1, "string", dummy, "string", 0);
- egg_state_machine_bind (machine, "state2", obj2, "string", dummy, "string", 0);
+ egg_state_machine_add_binding (machine, "state1", obj1, "string", dummy, "string", 0);
+ egg_state_machine_add_binding (machine, "state2", obj2, "string", dummy, "string", 0);
- egg_state_machine_add_action (machine, "state1", action, FALSE);
-
- g_signal_connect (machine, "transition", G_CALLBACK (transition_cb), NULL);
+ egg_state_machine_add_property (machine, "state1", action, "enabled", &vtrue);
+ egg_state_machine_add_property (machine, "state2", action, "enabled", &vfalse);
+ egg_state_machine_add_property (machine, "state3", action, "enabled", &vfalse);
g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, FALSE);
- ret = egg_state_machine_transition (machine, "state1", &error);
- g_assert_no_error (error);
- g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
+ egg_state_machine_set_state (machine, "state1");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "state1");
g_assert_cmpint (dummy->obj1_count, ==, 0);
g_assert_cmpint (dummy->obj2_count, ==, 0);
@@ -255,9 +248,8 @@ test_state_machine_basic (void)
g_assert_cmpint (dummy->obj1_count, ==, 1);
g_assert_cmpint (dummy->obj2_count, ==, 0);
- ret = egg_state_machine_transition (machine, "state2", &error);
- g_assert_no_error (error);
- g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
+ egg_state_machine_set_state (machine, "state2");
+ g_assert_cmpstr (egg_state_machine_get_state (machine), ==, "state2");
g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, FALSE);
@@ -273,16 +265,8 @@ test_state_machine_basic (void)
g_object_set (obj1, "string", "obj1", NULL);
assert_prop_equal (obj2, dummy, "string");
- /* state2 -> state3 should fail */
- ret = egg_state_machine_transition (machine, "state3", &error);
- g_assert_error (error, EGG_STATE_MACHINE_ERROR,
- EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION);
- g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_INVALID);
- g_clear_error (&error);
-
- ret = egg_state_machine_transition (machine, "state1", &error);
- g_assert_no_error (error);
- g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
+ egg_state_machine_set_state (machine, "state3");
+ egg_state_machine_set_state (machine, "state1");
assert_prop_equal (obj1, dummy, "string");
g_object_set (obj1, "string", "obj1-1", NULL);
@@ -290,10 +274,7 @@ test_state_machine_basic (void)
g_object_set (obj2, "string", "obj2-1", NULL);
assert_prop_equal (obj1, dummy, "string");
- /* state1 -> state3 should succeed */
- ret = egg_state_machine_transition (machine, "state3", &error);
- g_assert_no_error (error);
- g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
+ egg_state_machine_set_state (machine, "state3");
g_object_unref (machine);
g_assert (machine == NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]