~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to contrib/egg/egg-state-machine.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* egg-state-machine.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
5
 * This file is free software; you can redistribute it and/or modify it
 
6
 * under the terms of the GNU Lesser General Public License as
 
7
 * published by the Free Software Foundation; either version 3 of the
 
8
 * License, or (at your option) any later version.
 
9
 *
 
10
 * This file is distributed in the hope that it will be useful, but
 
11
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Lesser General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#include <glib/gi18n.h>
 
20
 
 
21
#include "egg-binding-set.h"
 
22
#include "egg-signal-group.h"
 
23
#include "egg-state-machine.h"
 
24
 
 
25
typedef struct
 
26
{
 
27
  gchar *state;
 
28
 
 
29
  /*
 
30
   * Containers for lazily bound signals and bindings.
 
31
   *
 
32
   * Each is a GHashTable indexed by state name, containing another GHashTable indexed by
 
33
   * source object.
 
34
   */
 
35
  GHashTable *binding_sets_by_state;
 
36
  GHashTable *signal_groups_by_state;
 
37
 
 
38
  /*
 
39
   * Container for actions which should have sensitivity mutated during state transitions.
 
40
   *
 
41
   * GHashTable of GPtrArray of ActionState.
 
42
   */
 
43
  GHashTable *actions_by_state;
 
44
 
 
45
  gsize       sequence;
 
46
} EggStateMachinePrivate;
 
47
 
 
48
typedef struct
 
49
{
 
50
  GSimpleAction *action;
 
51
  guint          invert_enabled : 1;
 
52
} ActionState;
 
53
 
 
54
G_DEFINE_TYPE_WITH_PRIVATE (EggStateMachine, egg_state_machine, G_TYPE_OBJECT)
 
55
G_DEFINE_QUARK (EggStateMachineError, egg_state_machine_error)
 
56
 
 
57
enum {
 
58
  PROP_0,
 
59
  PROP_STATE,
 
60
  LAST_PROP
 
61
};
 
62
 
 
63
enum {
 
64
  TRANSITION,
 
65
  LAST_SIGNAL
 
66
};
 
67
 
 
68
static GParamSpec *gParamSpecs [LAST_PROP];
 
69
static guint gSignals [LAST_SIGNAL];
 
70
 
 
71
static void
 
72
action_state_free (gpointer data)
 
73
{
 
74
  ActionState *state = data;
 
75
 
 
76
  g_clear_object (&state->action);
 
77
  g_slice_free (ActionState, state);
 
78
}
 
79
 
 
80
static gboolean
 
81
egg_state_transition_accumulator (GSignalInvocationHint *hint,
 
82
                                  GValue                *return_value,
 
83
                                  const GValue          *handler_return,
 
84
                                  gpointer               data)
 
85
{
 
86
  EggStateTransition ret;
 
87
 
 
88
  ret = g_value_get_enum (handler_return);
 
89
 
 
90
  if (ret == EGG_STATE_TRANSITION_INVALID)
 
91
    {
 
92
      g_value_set_enum (return_value, ret);
 
93
      return FALSE;
 
94
    }
 
95
 
 
96
  return TRUE;
 
97
}
 
98
 
 
99
const gchar *
 
100
egg_state_machine_get_state (EggStateMachine *self)
 
101
{
 
102
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
103
 
 
104
  g_return_val_if_fail (EGG_IS_STATE_MACHINE (self), NULL);
 
105
 
 
106
  return priv->state;
 
107
}
 
108
 
 
109
static void
 
110
egg_state_machine_do_transition (EggStateMachine *self,
 
111
                                 const gchar     *new_state)
 
112
{
 
113
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
114
  GHashTableIter iter;
 
115
  const gchar *key;
 
116
  GPtrArray *action_states;
 
117
  GHashTable *value;
 
118
  gsize i;
 
119
 
 
120
  g_assert (EGG_IS_STATE_MACHINE (self));
 
121
  g_assert (new_state != NULL);
 
122
 
 
123
  priv->sequence++;
 
124
 
 
125
  g_free (priv->state);
 
126
  priv->state = g_strdup (new_state);
 
127
 
 
128
  g_hash_table_iter_init (&iter, priv->signal_groups_by_state);
 
129
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
 
130
    {
 
131
      GHashTable *signal_groups = value;
 
132
      GHashTableIter groups_iter;
 
133
      EggSignalGroup *signal_group;
 
134
      gpointer instance;
 
135
      gboolean enabled = (g_strcmp0 (key, new_state) == 0);
 
136
 
 
137
      g_hash_table_iter_init (&groups_iter, signal_groups);
 
138
 
 
139
      while (g_hash_table_iter_next (&groups_iter, &instance, (gpointer *)&signal_group))
 
140
        {
 
141
          g_assert (G_IS_OBJECT (instance));
 
142
          g_assert (EGG_IS_SIGNAL_GROUP (signal_group));
 
143
 
 
144
          egg_signal_group_set_target (signal_group, enabled ? instance : NULL);
 
145
        }
 
146
    }
 
147
 
 
148
  g_hash_table_iter_init (&iter, priv->binding_sets_by_state);
 
149
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
 
150
    {
 
151
      GHashTable *binding_sets = value;
 
152
      GHashTableIter groups_iter;
 
153
      EggBindingSet *binding_set;
 
154
      gpointer instance;
 
155
      gboolean enabled = (g_strcmp0 (key, new_state) == 0);
 
156
 
 
157
      g_hash_table_iter_init (&groups_iter, binding_sets);
 
158
 
 
159
      while (g_hash_table_iter_next (&groups_iter, &instance, (gpointer *)&binding_set))
 
160
        {
 
161
          g_assert (G_IS_OBJECT (instance));
 
162
          g_assert (EGG_IS_BINDING_SET (binding_set));
 
163
 
 
164
          egg_binding_set_set_source (binding_set, enabled ? instance : NULL);
 
165
        }
 
166
    }
 
167
 
 
168
  /* apply GSimpleAction:enabled to non-matching states */
 
169
  g_hash_table_iter_init (&iter, priv->actions_by_state);
 
170
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&action_states))
 
171
    {
 
172
      if (g_strcmp0 (key, priv->state) == 0)
 
173
        continue;
 
174
 
 
175
      for (i = 0; i < action_states->len; i++)
 
176
        {
 
177
          ActionState *action_state;
 
178
 
 
179
          action_state = g_ptr_array_index (action_states, i);
 
180
          g_simple_action_set_enabled (action_state->action, action_state->invert_enabled);
 
181
        }
 
182
    }
 
183
 
 
184
  /* apply GSimpleAction:enabled to matching state */
 
185
  action_states = g_hash_table_lookup (priv->actions_by_state, priv->state);
 
186
  if (action_states != NULL)
 
187
    {
 
188
      for (i = 0; i < action_states->len; i++)
 
189
        {
 
190
          ActionState *action_state;
 
191
 
 
192
          action_state = g_ptr_array_index (action_states, i);
 
193
          g_simple_action_set_enabled (action_state->action, !action_state->invert_enabled);
 
194
        }
 
195
    }
 
196
}
 
197
 
 
198
/**
 
199
 * egg_state_machine_transition:
 
200
 * @self: A #EggStateMachine.
 
201
 * @new_state: The name of the new state.
 
202
 * @error: A location for a #GError, or %NULL.
 
203
 *
 
204
 * Attempts to change the state of the state machine to @new_state.
 
205
 *
 
206
 * This operation can fail, in which %EGG_STATE_TRANSITION_INVALID will be
 
207
 * returned and @error will be set.
 
208
 *
 
209
 * Upon success, %EGG_STATE_TRANSITION_SUCCESS is returned.
 
210
 *
 
211
 * Returns: An #EggStateTransition.
 
212
 */
 
213
EggStateTransition
 
214
egg_state_machine_transition (EggStateMachine  *self,
 
215
                              const gchar      *new_state,
 
216
                              GError          **error)
 
217
{
 
218
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
219
  g_autofree gchar *old_state = NULL;
 
220
  EggStateTransition ret = EGG_STATE_TRANSITION_IGNORED;
 
221
  g_autoptr(GError) local_error = NULL;
 
222
  gsize sequence;
 
223
 
 
224
  g_return_val_if_fail (EGG_IS_STATE_MACHINE (self), EGG_STATE_TRANSITION_INVALID);
 
225
  g_return_val_if_fail (new_state != NULL, EGG_STATE_TRANSITION_INVALID);
 
226
  g_return_val_if_fail (error == NULL || *error == NULL, EGG_STATE_TRANSITION_INVALID);
 
227
 
 
228
  if (g_strcmp0 (new_state, priv->state) == 0)
 
229
    return EGG_STATE_TRANSITION_SUCCESS;
 
230
 
 
231
  /* Be careful with reentrancy. */
 
232
 
 
233
  old_state = g_strdup (priv->state);
 
234
  sequence = priv->sequence;
 
235
 
 
236
  g_signal_emit (self, gSignals [TRANSITION], 0, old_state, new_state, &local_error, &ret);
 
237
 
 
238
  if (ret == EGG_STATE_TRANSITION_INVALID)
 
239
    {
 
240
      if (local_error == NULL)
 
241
        local_error = g_error_new_literal (EGG_STATE_MACHINE_ERROR,
 
242
                                           EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION,
 
243
                                           "Unknown error during state transition.");
 
244
      g_propagate_error (error, local_error);
 
245
      local_error = NULL;
 
246
      return ret;
 
247
    }
 
248
 
 
249
  if (sequence == priv->sequence)
 
250
    {
 
251
      egg_state_machine_do_transition (self, new_state);
 
252
      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_STATE]);
 
253
    }
 
254
 
 
255
  return EGG_STATE_TRANSITION_SUCCESS;
 
256
}
 
257
 
 
258
static EggStateTransition
 
259
egg_state_machine_real_transition (EggStateMachine  *self,
 
260
                                   const gchar      *old_state,
 
261
                                   const gchar      *new_state,
 
262
                                   GError          **error)
 
263
{
 
264
  return EGG_STATE_TRANSITION_IGNORED;
 
265
}
 
266
 
 
267
static void
 
268
egg_state_machine_finalize (GObject *object)
 
269
{
 
270
  EggStateMachine *self = (EggStateMachine *)object;
 
271
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
272
 
 
273
  g_clear_pointer (&priv->state, g_free);
 
274
  g_clear_pointer (&priv->binding_sets_by_state, g_hash_table_unref);
 
275
  g_clear_pointer (&priv->signal_groups_by_state, g_hash_table_unref);
 
276
  g_clear_pointer (&priv->actions_by_state, g_hash_table_unref);
 
277
 
 
278
  G_OBJECT_CLASS (egg_state_machine_parent_class)->finalize (object);
 
279
}
 
280
 
 
281
static void
 
282
egg_state_machine_get_property (GObject    *object,
 
283
                                guint       prop_id,
 
284
                                GValue     *value,
 
285
                                GParamSpec *pspec)
 
286
{
 
287
  EggStateMachine *self = EGG_STATE_MACHINE (object);
 
288
 
 
289
  switch (prop_id)
 
290
    {
 
291
    case PROP_STATE:
 
292
      g_value_set_string (value, egg_state_machine_get_state (self));
 
293
      break;
 
294
 
 
295
    default:
 
296
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
297
    }
 
298
}
 
299
 
 
300
static void
 
301
egg_state_machine_set_property (GObject      *object,
 
302
                                guint         prop_id,
 
303
                                const GValue *value,
 
304
                                GParamSpec   *pspec)
 
305
{
 
306
  EggStateMachine *self = EGG_STATE_MACHINE (object);
 
307
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
308
 
 
309
  switch (prop_id)
 
310
    {
 
311
    case PROP_STATE:
 
312
      priv->state = g_value_dup_string (value);
 
313
      break;
 
314
 
 
315
    default:
 
316
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
317
    }
 
318
}
 
319
 
 
320
static void
 
321
egg_state_machine_class_init (EggStateMachineClass *klass)
 
322
{
 
323
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
324
 
 
325
  object_class->finalize = egg_state_machine_finalize;
 
326
  object_class->get_property = egg_state_machine_get_property;
 
327
  object_class->set_property = egg_state_machine_set_property;
 
328
 
 
329
  klass->transition = egg_state_machine_real_transition;
 
330
 
 
331
  gParamSpecs [PROP_STATE] =
 
332
    g_param_spec_string ("state",
 
333
                         _("State"),
 
334
                         _("The current state of the machine."),
 
335
                         NULL,
 
336
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
337
 
 
338
  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
339
 
 
340
  /**
 
341
   * EggStateMachine::transition:
 
342
   * @self: An #EggStateMachine.
 
343
   * @old_state: The current state.
 
344
   * @new_state: The new state.
 
345
   * @error: (ctype GError**): A location for a #GError, or %NULL.
 
346
   *
 
347
   * Determines if the transition is allowed.
 
348
   *
 
349
   * If the state transition is invalid, @error should be set to a new #GError.
 
350
   *
 
351
   * Returns: %TRUE if the state transition is acceptable.
 
352
   */
 
353
  gSignals [TRANSITION] =
 
354
    g_signal_new ("transition",
 
355
                  G_TYPE_FROM_CLASS (klass),
 
356
                  G_SIGNAL_RUN_LAST,
 
357
                  G_STRUCT_OFFSET (EggStateMachineClass, transition),
 
358
                  egg_state_transition_accumulator, NULL,
 
359
                  NULL,
 
360
                  EGG_TYPE_STATE_TRANSITION,
 
361
                  3,
 
362
                  G_TYPE_STRING,
 
363
                  G_TYPE_STRING,
 
364
                  G_TYPE_POINTER);
 
365
}
 
366
 
 
367
static void
 
368
egg_state_machine_init (EggStateMachine *self)
 
369
{
 
370
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
371
 
 
372
  priv->binding_sets_by_state =
 
373
    g_hash_table_new_full (g_str_hash,
 
374
                           g_str_equal,
 
375
                           g_free,
 
376
                           (GDestroyNotify)g_hash_table_destroy);
 
377
 
 
378
  priv->signal_groups_by_state =
 
379
    g_hash_table_new_full (g_str_hash,
 
380
                           g_str_equal,
 
381
                           g_free,
 
382
                           (GDestroyNotify)g_hash_table_destroy);
 
383
 
 
384
  priv->actions_by_state =
 
385
    g_hash_table_new_full (g_str_hash,
 
386
                           g_str_equal,
 
387
                           g_free,
 
388
                           (GDestroyNotify)g_ptr_array_unref);
 
389
}
 
390
 
 
391
static void
 
392
egg_state_machine__connect_object_weak_notify (gpointer  data,
 
393
                                               GObject  *where_object_was)
 
394
{
 
395
  EggStateMachine *self = data;
 
396
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
397
  GHashTableIter iter;
 
398
  const gchar *key;
 
399
  GHashTable *value;
 
400
 
 
401
  g_assert (EGG_IS_STATE_MACHINE (self));
 
402
  g_assert (where_object_was != NULL);
 
403
 
 
404
  g_hash_table_iter_init (&iter, priv->signal_groups_by_state);
 
405
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
 
406
    {
 
407
      GHashTable *signal_groups = value;
 
408
 
 
409
      g_hash_table_remove (signal_groups, where_object_was);
 
410
    }
 
411
}
 
412
 
 
413
void
 
414
egg_state_machine_connect_object (EggStateMachine *self,
 
415
                                  const gchar     *state,
 
416
                                  gpointer         instance,
 
417
                                  const gchar     *detailed_signal,
 
418
                                  GCallback        callback,
 
419
                                  gpointer         user_data,
 
420
                                  GConnectFlags    flags)
 
421
{
 
422
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
423
  GHashTable *signal_groups;
 
424
  EggSignalGroup *signal_group;
 
425
  gboolean created = FALSE;
 
426
 
 
427
  g_return_if_fail (EGG_IS_STATE_MACHINE (self));
 
428
  g_return_if_fail (state != NULL);
 
429
  g_return_if_fail (G_IS_OBJECT (instance));
 
430
  g_return_if_fail (detailed_signal != NULL);
 
431
  g_return_if_fail (g_signal_parse_name (detailed_signal,
 
432
                                         G_TYPE_FROM_INSTANCE (instance),
 
433
                                         NULL, NULL, FALSE) != 0);
 
434
  g_return_if_fail (callback != NULL);
 
435
 
 
436
  signal_groups = g_hash_table_lookup (priv->signal_groups_by_state, state);
 
437
 
 
438
  if (signal_groups == NULL)
 
439
    {
 
440
      signal_groups = g_hash_table_new_full (g_direct_hash,
 
441
                                             g_direct_equal,
 
442
                                             NULL,
 
443
                                             g_object_unref);
 
444
      g_hash_table_insert (priv->signal_groups_by_state, g_strdup (state), signal_groups);
 
445
    }
 
446
 
 
447
  g_assert (signal_groups != NULL);
 
448
 
 
449
  signal_group = g_hash_table_lookup (signal_groups, instance);
 
450
 
 
451
  if (signal_group == NULL)
 
452
    {
 
453
      created = TRUE;
 
454
      signal_group = egg_signal_group_new (G_TYPE_FROM_INSTANCE (instance));
 
455
      g_hash_table_insert (signal_groups, instance, signal_group);
 
456
      g_object_weak_ref (instance,
 
457
                         (GWeakNotify)egg_state_machine__connect_object_weak_notify,
 
458
                         self);
 
459
    }
 
460
 
 
461
  egg_signal_group_connect_object (signal_group, detailed_signal, callback, user_data, flags);
 
462
 
 
463
  if ((created == TRUE) && (g_strcmp0 (state, priv->state) == 0))
 
464
    egg_signal_group_set_target (signal_group, instance);
 
465
}
 
466
 
 
467
static void
 
468
egg_state_machine__bind_source_weak_notify (gpointer  data,
 
469
                                            GObject  *where_object_was)
 
470
{
 
471
  EggStateMachine *self = data;
 
472
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
473
  GHashTableIter iter;
 
474
  const gchar *key;
 
475
  GHashTable *value;
 
476
 
 
477
  g_assert (EGG_IS_STATE_MACHINE (self));
 
478
  g_assert (where_object_was != NULL);
 
479
 
 
480
  g_hash_table_iter_init (&iter, priv->binding_sets_by_state);
 
481
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
 
482
    {
 
483
      GHashTable *binding_sets = value;
 
484
 
 
485
      g_hash_table_remove (binding_sets, where_object_was);
 
486
    }
 
487
}
 
488
 
 
489
void
 
490
egg_state_machine_bind (EggStateMachine *self,
 
491
                        const gchar     *state,
 
492
                        gpointer         source,
 
493
                        const gchar     *source_property,
 
494
                        gpointer         target,
 
495
                        const gchar     *target_property,
 
496
                        GBindingFlags    flags)
 
497
{
 
498
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
499
  GHashTable *binding_sets;
 
500
  EggBindingSet *binding_set;
 
501
  gboolean created = FALSE;
 
502
 
 
503
  g_return_if_fail (EGG_IS_STATE_MACHINE (self));
 
504
  g_return_if_fail (state != NULL);
 
505
  g_return_if_fail (G_IS_OBJECT (source));
 
506
  g_return_if_fail (source_property != NULL);
 
507
  g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (source),
 
508
                                                  source_property) != NULL);
 
509
  g_return_if_fail (G_IS_OBJECT (target));
 
510
  g_return_if_fail (target_property != NULL);
 
511
  g_return_if_fail (g_object_class_find_property (G_OBJECT_GET_CLASS (target),
 
512
                                                  target_property) != NULL);
 
513
 
 
514
  /* Use G_BINDING_SYNC_CREATE as we lazily connect them. */
 
515
  flags |= G_BINDING_SYNC_CREATE;
 
516
 
 
517
  binding_sets = g_hash_table_lookup (priv->binding_sets_by_state, state);
 
518
 
 
519
  if (binding_sets == NULL)
 
520
    {
 
521
      binding_sets = g_hash_table_new_full (g_direct_hash,
 
522
                                            g_direct_equal,
 
523
                                            NULL,
 
524
                                            g_object_unref);
 
525
      g_hash_table_insert (priv->binding_sets_by_state, g_strdup (state), binding_sets);
 
526
    }
 
527
 
 
528
  g_assert (binding_sets != NULL);
 
529
 
 
530
  binding_set = g_hash_table_lookup (binding_sets, source);
 
531
 
 
532
  if (binding_set == NULL)
 
533
    {
 
534
      created = TRUE;
 
535
      binding_set = egg_binding_set_new ();
 
536
      g_hash_table_insert (binding_sets, source, binding_set);
 
537
      g_object_weak_ref (source,
 
538
                         (GWeakNotify)egg_state_machine__bind_source_weak_notify,
 
539
                         self);
 
540
    }
 
541
 
 
542
  egg_binding_set_bind (binding_set,
 
543
                        source_property,
 
544
                        target,
 
545
                        target_property,
 
546
                        flags);
 
547
 
 
548
  if ((created == TRUE) && (g_strcmp0 (state, priv->state) == 0))
 
549
    egg_binding_set_set_source (binding_set, source);
 
550
}
 
551
 
 
552
void
 
553
egg_state_machine_add_action (EggStateMachine *self,
 
554
                              const gchar     *state,
 
555
                              GSimpleAction   *action,
 
556
                              gboolean         invert_enabled)
 
557
{
 
558
  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
 
559
  ActionState *action_state;
 
560
  GPtrArray *actions;
 
561
  gboolean enabled;
 
562
 
 
563
  g_return_if_fail (EGG_IS_STATE_MACHINE (self));
 
564
  g_return_if_fail (state != NULL);
 
565
  g_return_if_fail (G_IS_SIMPLE_ACTION (action));
 
566
 
 
567
  action_state = g_slice_new0 (ActionState);
 
568
  action_state->action = g_object_ref (action);
 
569
  action_state->invert_enabled = invert_enabled;
 
570
 
 
571
  actions = g_hash_table_lookup (priv->actions_by_state, state);
 
572
 
 
573
  if (actions == NULL)
 
574
    {
 
575
      actions = g_ptr_array_new_with_free_func (action_state_free);
 
576
      g_hash_table_insert (priv->actions_by_state, g_strdup (state), actions);
 
577
    }
 
578
 
 
579
  g_ptr_array_add (actions, action_state);
 
580
 
 
581
  enabled = (g_strcmp0 (state, priv->state) == 0);
 
582
  if (invert_enabled)
 
583
    enabled = !enabled;
 
584
 
 
585
  g_simple_action_set_enabled (action, enabled);
 
586
}
 
587
 
 
588
EggStateMachine *
 
589
egg_state_machine_new (void)
 
590
{
 
591
  return g_object_new (EGG_TYPE_STATE_MACHINE, NULL);
 
592
}
 
593
 
 
594
GType
 
595
egg_state_machine_error_get_type (void)
 
596
{
 
597
  static gsize type_id;
 
598
 
 
599
  if (g_once_init_enter (&type_id))
 
600
    {
 
601
      static const GEnumValue values[] = {
 
602
        { EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION,
 
603
          "EGG_STATE_MACHINE_ERROR_INVALID_TRANSITION",
 
604
          "invalid-transition" },
 
605
        { 0 }
 
606
      };
 
607
      gsize _type_id;
 
608
 
 
609
      _type_id = g_enum_register_static ("EggStateMachineError", values);
 
610
      g_once_init_leave (&type_id, _type_id);
 
611
    }
 
612
 
 
613
  return type_id;
 
614
}
 
615
 
 
616
GType
 
617
egg_state_transition_get_type (void)
 
618
{
 
619
  static gsize type_id;
 
620
 
 
621
  if (g_once_init_enter (&type_id))
 
622
    {
 
623
      static const GEnumValue values[] = {
 
624
        { EGG_STATE_TRANSITION_IGNORED, "EGG_STATE_TRANSITION_IGNORED", "ignored" },
 
625
        { EGG_STATE_TRANSITION_INVALID, "EGG_STATE_TRANSITION_INVALID", "invalid" },
 
626
        { EGG_STATE_TRANSITION_SUCCESS, "EGG_STATE_TRANSITION_SUCCESS", "success" },
 
627
        { 0 }
 
628
      };
 
629
      gsize _type_id;
 
630
 
 
631
      _type_id = g_enum_register_static ("EggStateTransition", values);
 
632
      g_once_init_leave (&type_id, _type_id);
 
633
    }
 
634
 
 
635
  return type_id;
 
636
}