[clutter/master-next: 2/3] Allow mixing old and new API without falling apart



commit 02b456d9194ba6b3954bc6dc547acd4117b81832
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Sat Jan 21 23:06:49 2012 +0000

    Allow mixing old and new API without falling apart
    
    Now that we reinstated Group to its "former glory", we need to ensure
    that applications using the deprecated containers with the new DOM API
    in ClutterActor can actually work - or, at least, not break horribly.
    
    This actually means making sure that ClutterStage and ClutterGroup can
    cope with the DOM, while retaining their old implementations, as well as
    their bizarre idiosyncrasies and their utter, utter brokenness.

 clutter/clutter-actor-private.h    |    2 +
 clutter/clutter-actor.c            |  542 ++++++++++++++++++++++++------------
 clutter/clutter-enums.h            |   49 +++-
 clutter/clutter-stage.c            |    3 +-
 clutter/deprecated/clutter-group.c |   62 ++++-
 5 files changed, 460 insertions(+), 198 deletions(-)
---
diff --git a/clutter/clutter-actor-private.h b/clutter/clutter-actor-private.h
index 968dbef..10444ce 100644
--- a/clutter/clutter-actor-private.h
+++ b/clutter/clutter-actor-private.h
@@ -215,6 +215,8 @@ void          _clutter_actor_traverse                   (ClutterActor *actor,
                                                          ClutterTraverseCallback before_children_callback,
                                                          ClutterTraverseCallback after_children_callback,
                                                          gpointer user_data);
+void          _clutter_actor_sort_children              (ClutterActor *actor,
+                                                         GCompareFunc  func);
 ClutterActor *_clutter_actor_get_stage_internal         (ClutterActor *actor);
 
 void _clutter_actor_apply_modelview_transform           (ClutterActor *self,
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 677ac39..f222e78 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -1968,36 +1968,92 @@ clutter_actor_set_allocation_internal (ClutterActor           *self,
   return retval;
 }
 
+static void clutter_actor_real_allocate (ClutterActor           *self,
+                                         const ClutterActorBox  *box,
+                                         ClutterAllocationFlags  flags);
+
 static inline void
-clutter_actor_maybe_layout_children (ClutterActor *self)
+clutter_actor_maybe_layout_children (ClutterActor           *self,
+                                     const ClutterActorBox  *allocation,
+                                     ClutterAllocationFlags  flags)
 {
   ClutterActorPrivate *priv = self->priv;
 
+  /* this is going to be a bit hard to follow, so let's put an explanation
+   * here.
+   *
+   * we want ClutterActor to have a default layout manager if the actor was
+   * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)".
+   *
+   * we also want any subclass of ClutterActor that does not override the
+   * ::allocate() virtual function to delegate to a layout manager.
+   *
+   * finally, we want to allow people subclassing ClutterActor and overriding
+   * the ::allocate() vfunc to let Clutter delegate to the layout manager.
+   *
+   * on the other hand, we want existing actor subclasses overriding the
+   * ::allocate() virtual function and chaining up to the parent's
+   * implementation to continue working without allocating their children
+   * twice, or without entering an allocation loop.
+   *
+   * for the first two points, we check if the class of the actor is
+   * overridding the ::allocate() virtual function; if it isn't, then we
+   * follow through with checking whether we have children and a layout
+   * manager, and eventually calling clutter_layout_manager_allocate().
+   *
+   * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the
+   * allocation flags that we got passed, and if it is present, we continue
+   * with the check above.
+   *
+   * if neither of these two checks yields a positive result, we just
+   * assume that the ::allocate() virtual function that resulted in this
+   * function being called will also allocate the children of the actor.
+   */
+
+  if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate)
+    goto check_layout;
+
+  if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0)
+    goto check_layout;
+
+  return;
+
+check_layout:
   if (priv->n_children != 0 &&
       priv->layout_manager != NULL)
     {
       ClutterContainer *container = CLUTTER_CONTAINER (self);
-      gfloat width, height;
+      ClutterAllocationFlags children_flags;
       ClutterActorBox children_box;
 
-      clutter_actor_box_get_size (&priv->allocation, &width, &height);
+      /* normalize the box passed to the layout manager */
+      children_box.x1 = children_box.y1 = 0.f;
+      children_box.x2 = (allocation->x2 - allocation->x1);
+      children_box.y2 = (allocation->y2 - allocation->y1);
 
-      clutter_actor_box_set_origin (&children_box, 0, 0);
-      clutter_actor_box_set_size (&children_box, width, height);
+      /* remove the DELEGATE_LAYOUT flag; this won't be passed to
+       * the actor's children, since it refers only to the current
+       * actor's allocation.
+       */
+      children_flags = flags;
+      children_flags &= ~CLUTTER_DELEGATE_LAYOUT;
 
       CLUTTER_NOTE (LAYOUT,
-                    "Allocating children of %s at { %.2f, %.2f - %.2f x %.2f } using %s",
+                    "Allocating %d children of %s "
+                    "at { %.2f, %.2f - %.2f x %.2f } "
+                    "using %s",
+                    priv->n_children,
                     _clutter_actor_get_debug_name (self),
-                    priv->allocation.x1,
-                    priv->allocation.y1,
-                    width,
-                    height,
+                    allocation->x1,
+                    allocation->y1,
+                    (allocation->x2 - allocation->x1),
+                    (allocation->y2 - allocation->y1),
                     G_OBJECT_TYPE_NAME (priv->layout_manager));
 
       clutter_layout_manager_allocate (priv->layout_manager,
                                        container,
                                        &children_box,
-                                       priv->allocation_flags);
+                                       children_flags);
     }
 }
 
@@ -2018,7 +2074,7 @@ clutter_actor_real_allocate (ClutterActor           *self,
    * data out of the sub-tree of the scene graph that has this actor at
    * the root.
    */
-  clutter_actor_maybe_layout_children (self);
+  clutter_actor_maybe_layout_children (self, box, flags);
 
   if (changed)
     g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
@@ -3299,6 +3355,171 @@ clutter_actor_continue_paint (ClutterActor *self)
     }
 }
 
+static ClutterActorTraverseVisitFlags
+invalidate_queue_redraw_entry (ClutterActor *self,
+                               int           depth,
+                               gpointer      user_data)
+{
+  ClutterActorPrivate *priv = self->priv;
+
+  if (priv->queue_redraw_entry != NULL)
+    {
+      _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
+      priv->queue_redraw_entry = NULL;
+    }
+
+  return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
+}
+
+static inline void
+remove_child (ClutterActor *self,
+              ClutterActor *child)
+{
+  ClutterActor *prev_sibling, *next_sibling;
+
+  prev_sibling = child->priv->prev_sibling;
+  next_sibling = child->priv->next_sibling;
+
+  if (prev_sibling != NULL)
+    prev_sibling->priv->next_sibling = next_sibling;
+
+  if (next_sibling != NULL)
+    next_sibling->priv->prev_sibling = prev_sibling;
+
+  if (self->priv->first_child == child)
+    self->priv->first_child = next_sibling;
+
+  if (self->priv->last_child == child)
+    self->priv->last_child = prev_sibling;
+
+  child->priv->parent = NULL;
+  child->priv->prev_sibling = NULL;
+  child->priv->next_sibling = NULL;
+}
+
+typedef enum {
+  REMOVE_CHILD_DESTROY_META       = 1 << 0,
+  REMOVE_CHILD_EMIT_PARENT_SET    = 1 << 1,
+  REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
+  REMOVE_CHILD_CHECK_STATE        = 1 << 3,
+  REMOVE_CHILD_FLUSH_QUEUE        = 1 << 4,
+  REMOVE_CHILD_NOTIFY_FIRST_LAST  = 1 << 5,
+
+  /* default flags for public API */
+  REMOVE_CHILD_DEFAULT_FLAGS      = REMOVE_CHILD_DESTROY_META |
+                                    REMOVE_CHILD_EMIT_PARENT_SET |
+                                    REMOVE_CHILD_EMIT_ACTOR_REMOVED |
+                                    REMOVE_CHILD_CHECK_STATE |
+                                    REMOVE_CHILD_FLUSH_QUEUE |
+                                    REMOVE_CHILD_NOTIFY_FIRST_LAST,
+
+  /* flags for legacy/deprecated API */
+  REMOVE_CHILD_LEGACY_FLAGS       = REMOVE_CHILD_CHECK_STATE |
+                                    REMOVE_CHILD_FLUSH_QUEUE |
+                                    REMOVE_CHILD_EMIT_PARENT_SET |
+                                    REMOVE_CHILD_NOTIFY_FIRST_LAST
+} ClutterActorRemoveChildFlags;
+
+/*< private >
+ * clutter_actor_remove_child_internal:
+ * @self: a #ClutterActor
+ * @child: the child of @self that has to be removed
+ * @flags: control the removal operations
+ *
+ * Removes @child from the list of children of @self.
+ */
+static void
+clutter_actor_remove_child_internal (ClutterActor                 *self,
+                                     ClutterActor                 *child,
+                                     ClutterActorRemoveChildFlags  flags)
+{
+  ClutterActor *old_first, *old_last;
+  gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
+  gboolean flush_queue;
+  gboolean notify_first_last;
+  gboolean was_mapped;
+
+  destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
+  emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
+  emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
+  check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
+  flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
+  notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
+
+  if (destroy_meta)
+    clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
+
+  if (check_state)
+    {
+      was_mapped = CLUTTER_ACTOR_IS_MAPPED (child);
+
+      /* we need to unrealize *before* we set parent_actor to NULL,
+       * because in an unrealize method actors are dissociating from the
+       * stage, which means they need to be able to
+       * clutter_actor_get_stage(). This should unmap and unrealize,
+       *  unless we're reparenting.
+       */
+      clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
+    }
+  else
+    was_mapped = FALSE;
+
+  if (flush_queue)
+    {
+      /* We take this opportunity to invalidate any queue redraw entry
+       * associated with the actor and descendants since we won't be able to
+       * determine the appropriate stage after this.
+       *
+       * we do this after we updated the mapped state because actors might
+       * end up queueing redraws inside their mapped/unmapped virtual
+       * functions, and if we invalidate the redraw entry we could end up
+       * with an inconsistent state and weird memory corruption. see
+       * bugs:
+       *
+       *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
+       *   https://bugzilla.gnome.org/show_bug.cgi?id=652036
+       */
+      _clutter_actor_traverse (child,
+                               0,
+                               invalidate_queue_redraw_entry,
+                               NULL,
+                               NULL);
+    }
+
+  old_first = self->priv->first_child;
+  old_last = self->priv->last_child;
+
+  remove_child (self, child);
+
+  self->priv->n_children -= 1;
+
+  /* clutter_actor_reparent() will emit ::parent-set for us */
+  if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
+    g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
+
+  /* if the child was mapped then we need to relayout ourselves to account
+   * for the removed child
+   */
+  if (was_mapped)
+    clutter_actor_queue_relayout (self);
+
+  /* we need to emit the signal before dropping the reference */
+  if (emit_actor_removed)
+    g_signal_emit_by_name (self, "actor-removed", child);
+
+  if (notify_first_last)
+    {
+      if (old_first != self->priv->first_child)
+        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+
+      if (old_last != self->priv->last_child)
+        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+    }
+
+  /* remove the reference we acquired in clutter_actor_add_child() */
+  g_object_unref (child);
+}
+
 static const ClutterTransformInfo default_transform_info = {
   0.0, { 0, },          /* rotation-x */
   0.0, { 0, },          /* rotation-y */
@@ -4306,7 +4527,8 @@ clutter_actor_dispose (GObject *object)
       if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
         clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
       else
-        clutter_actor_remove_child (parent, self);
+        clutter_actor_remove_child_internal (parent, self,
+                                             REMOVE_CHILD_LEGACY_FLAGS);
     }
 
   /* parent should be gone */
@@ -4486,6 +4708,32 @@ clutter_actor_real_has_overlaps (ClutterActor *self)
   return TRUE;
 }
 
+static GObject *
+clutter_actor_constructor (GType gtype,
+                           guint n_props,
+                           GObjectConstructParam *props)
+{
+  GObjectClass *gobject_class;
+  ClutterActor *self;
+  GObject *retval;
+
+  gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
+  retval = gobject_class->constructor (gtype, n_props, props);
+  self = CLUTTER_ACTOR (retval);
+
+  if (self->priv->layout_manager == NULL)
+    {
+      ClutterLayoutManager *default_layout;
+
+      CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
+
+      default_layout = clutter_fixed_layout_new ();
+      clutter_actor_set_layout_manager (self, default_layout);
+    }
+
+  return retval;
+}
+
 static void
 clutter_actor_class_init (ClutterActorClass *klass)
 {
@@ -4495,6 +4743,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
   quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
   quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
 
+  object_class->constructor = clutter_actor_constructor;
   object_class->set_property = clutter_actor_set_property;
   object_class->get_property = clutter_actor_get_property;
   object_class->dispose = clutter_actor_dispose;
@@ -7533,7 +7782,7 @@ clutter_actor_set_allocation (ClutterActor           *self,
    * data out of the sub-tree of the scene graph that has this actor at
    * the root.
    */
-  clutter_actor_maybe_layout_children (self);
+  clutter_actor_maybe_layout_children (self, box, flags);
 
   if (changed)
     g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
@@ -9117,6 +9366,11 @@ clutter_actor_set_depth (ClutterActor *self,
 
       priv->transform_valid = FALSE;
 
+      /* FIXME - remove this crap; sadly, there are still containers
+       * in Clutter that depend on this utter brain damage
+       */
+      clutter_container_sort_depth_order (CLUTTER_CONTAINER (self));
+
       clutter_actor_queue_redraw (self);
 
       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]);
@@ -9494,6 +9748,9 @@ insert_child_at_depth (ClutterActor *self,
 {
   ClutterActor *iter;
 
+  child->priv->parent = self;
+
+  /* special-case the first child */
   if (self->priv->n_children == 0)
     {
       self->priv->first_child = child;
@@ -9535,6 +9792,7 @@ insert_child_at_depth (ClutterActor *self,
       if (tmp != NULL)
         tmp->priv->next_sibling = child;
 
+      /* insert the node at the end of the list */
       child->priv->prev_sibling = self->priv->last_child;
       child->priv->next_sibling = NULL;
     }
@@ -9553,6 +9811,8 @@ insert_child_at_index (ClutterActor *self,
 {
   gint index_ = GPOINTER_TO_INT (data_);
 
+  child->priv->parent = self;
+
   if (index_ == 0)
     {
       ClutterActor *tmp = self->priv->first_child;
@@ -9586,7 +9846,7 @@ insert_child_at_index (ClutterActor *self,
             {
               ClutterActor *tmp = iter->priv->prev_sibling;
 
-              child->priv->prev_sibling = iter->priv->prev_sibling;
+              child->priv->prev_sibling = tmp;
               child->priv->next_sibling = iter;
 
               iter->priv->prev_sibling = child;
@@ -9613,6 +9873,8 @@ insert_child_above (ClutterActor *self,
 {
   ClutterActor *sibling = data;
 
+  child->priv->parent = self;
+
   if (sibling == NULL)
     sibling = self->priv->last_child;
 
@@ -9677,20 +9939,23 @@ typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
                                            gpointer      data);
 
 typedef enum {
-  ADD_CHILD_CREATE_META      = 1 << 0,
-  ADD_CHILD_EMIT_PARENT_SET  = 1 << 1,
-  ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
-  ADD_CHILD_CHECK_STATE      = 1 << 3,
+  ADD_CHILD_CREATE_META       = 1 << 0,
+  ADD_CHILD_EMIT_PARENT_SET   = 1 << 1,
+  ADD_CHILD_EMIT_ACTOR_ADDED  = 1 << 2,
+  ADD_CHILD_CHECK_STATE       = 1 << 3,
+  ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
 
   /* default flags for public API */
   ADD_CHILD_DEFAULT_FLAGS    = ADD_CHILD_CREATE_META |
                                ADD_CHILD_EMIT_PARENT_SET |
                                ADD_CHILD_EMIT_ACTOR_ADDED |
-                               ADD_CHILD_CHECK_STATE,
+                               ADD_CHILD_CHECK_STATE |
+                               ADD_CHILD_NOTIFY_FIRST_LAST,
 
   /* flags for legacy/deprecated API */
   ADD_CHILD_LEGACY_FLAGS     = ADD_CHILD_EMIT_PARENT_SET |
-                               ADD_CHILD_CHECK_STATE
+                               ADD_CHILD_CHECK_STATE |
+                               ADD_CHILD_NOTIFY_FIRST_LAST
 } ClutterActorAddChildFlags;
 
 /*< private >
@@ -9717,7 +9982,10 @@ clutter_actor_add_child_internal (ClutterActor              *self,
                                   gpointer                   data)
 {
   ClutterTextDirection text_dir;
-  gboolean create_meta, emit_parent_set, emit_actor_added, check_state;
+  gboolean create_meta;
+  gboolean emit_parent_set, emit_actor_added;
+  gboolean check_state;
+  gboolean notify_first_last;
   ClutterActor *old_first_child, *old_last_child;
 
   if (child->priv->parent != NULL)
@@ -9743,6 +10011,7 @@ clutter_actor_add_child_internal (ClutterActor              *self,
   emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
   emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
   check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
+  notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
 
   old_first_child = self->priv->first_child;
   old_last_child = self->priv->last_child;
@@ -9751,7 +10020,7 @@ clutter_actor_add_child_internal (ClutterActor              *self,
     clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child);
 
   g_object_ref_sink (child);
-  child->priv->parent = self;
+  child->priv->parent = NULL;
   child->priv->next_sibling = NULL;
   child->priv->prev_sibling = NULL;
 
@@ -9809,11 +10078,14 @@ clutter_actor_add_child_internal (ClutterActor              *self,
   if (emit_actor_added)
     g_signal_emit_by_name (self, "actor-added", child);
 
-  if (old_first_child != self->priv->first_child)
-    g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+  if (notify_first_last)
+    {
+      if (old_first_child != self->priv->first_child)
+        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
 
-  if (old_last_child != self->priv->last_child)
-    g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+      if (old_last_child != self->priv->last_child)
+        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+    }
 }
 
 /**
@@ -10042,154 +10314,6 @@ clutter_actor_get_paint_visibility (ClutterActor *actor)
   return CLUTTER_ACTOR_IS_MAPPED (actor);
 }
 
-static ClutterActorTraverseVisitFlags
-invalidate_queue_redraw_entry (ClutterActor *self,
-                               int           depth,
-                               gpointer      user_data)
-{
-  ClutterActorPrivate *priv = self->priv;
-
-  if (priv->queue_redraw_entry != NULL)
-    {
-      _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
-      priv->queue_redraw_entry = NULL;
-    }
-
-  return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
-}
-
-static inline void
-remove_child (ClutterActor *self,
-              ClutterActor *child)
-{
-  ClutterActor *prev_sibling, *next_sibling;
-
-  prev_sibling = child->priv->prev_sibling;
-  next_sibling = child->priv->next_sibling;
-
-  if (prev_sibling != NULL)
-    prev_sibling->priv->next_sibling = next_sibling;
-
-  if (next_sibling != NULL)
-    next_sibling->priv->prev_sibling = prev_sibling;
-
-  if (self->priv->first_child == child)
-    self->priv->first_child = next_sibling;
-
-  if (self->priv->last_child == child)
-    self->priv->last_child = prev_sibling;
-
-  child->priv->prev_sibling = NULL;
-  child->priv->next_sibling = NULL;
-}
-
-typedef enum {
-  REMOVE_CHILD_DESTROY_META       = 1 << 0,
-  REMOVE_CHILD_EMIT_PARENT_SET    = 1 << 1,
-  REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
-  REMOVE_CHILD_CHECK_STATE        = 1 << 3,
-  REMOVE_CHILD_FLUSH_QUEUE        = 1 << 4,
-
-  /* default flags for public API */
-  REMOVE_CHILD_DEFAULT_FLAGS      = REMOVE_CHILD_DESTROY_META |
-                                    REMOVE_CHILD_EMIT_PARENT_SET |
-                                    REMOVE_CHILD_EMIT_ACTOR_REMOVED |
-                                    REMOVE_CHILD_CHECK_STATE |
-                                    REMOVE_CHILD_FLUSH_QUEUE,
-
-  /* flags for legacy/deprecated API */
-  REMOVE_CHILD_LEGACY_FLAGS       = REMOVE_CHILD_CHECK_STATE |
-                                    REMOVE_CHILD_FLUSH_QUEUE |
-                                    REMOVE_CHILD_EMIT_PARENT_SET
-} ClutterActorRemoveChildFlags;
-
-/*< private >
- * clutter_actor_remove_child_internal:
- * @self: a #ClutterActor
- * @child: the child of @self that has to be removed
- * @flags: control the removal operations
- *
- * Removes @child from the list of children of @self.
- */
-static void
-clutter_actor_remove_child_internal (ClutterActor                 *self,
-                                     ClutterActor                 *child,
-                                     ClutterActorRemoveChildFlags  flags)
-{
-  gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
-  gboolean flush_queue;
-  gboolean was_mapped;
-
-  destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
-  emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
-  emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
-  check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
-  flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
-
-  if (destroy_meta)
-    clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
-
-  if (check_state)
-    {
-      was_mapped = CLUTTER_ACTOR_IS_MAPPED (child);
-
-      /* we need to unrealize *before* we set parent_actor to NULL,
-       * because in an unrealize method actors are dissociating from the
-       * stage, which means they need to be able to
-       * clutter_actor_get_stage(). This should unmap and unrealize,
-       *  unless we're reparenting.
-       */
-      clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
-    }
-  else
-    was_mapped = FALSE;
-
-  if (flush_queue)
-    {
-      /* We take this opportunity to invalidate any queue redraw entry
-       * associated with the actor and descendants since we won't be able to
-       * determine the appropriate stage after this.
-       *
-       * we do this after we updated the mapped state because actors might
-       * end up queueing redraws inside their mapped/unmapped virtual
-       * functions, and if we invalidate the redraw entry we could end up
-       * with an inconsistent state and weird memory corruption. see
-       * bugs:
-       *
-       *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
-       *   https://bugzilla.gnome.org/show_bug.cgi?id=652036
-       */
-      _clutter_actor_traverse (child,
-                               0,
-                               invalidate_queue_redraw_entry,
-                               NULL,
-                               NULL);
-    }
-
-  child->priv->parent = NULL;
-
-  /* clutter_actor_reparent() will emit ::parent-set for us */
-  if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
-    g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
-
-  remove_child (self, child);
-
-  self->priv->n_children -= 1;
-
-  /* if the child was mapped then we need to relayout ourselves to account
-   * for the removed child
-   */
-  if (was_mapped)
-    clutter_actor_queue_relayout (self);
-
-  /* we need to emit the signal before dropping the reference */
-  if (emit_actor_removed)
-    g_signal_emit_by_name (self, "actor-removed", child);
-
-  /* remove the reference we acquired in clutter_actor_add_child() */
-  g_object_unref (child);
-}
-
 /**
  * clutter_actor_remove_child:
  * @self: a #ClutterActor
@@ -10272,6 +10396,7 @@ insert_child_between (ClutterActor *self,
   ClutterActor *prev_sibling = data->prev_sibling;
   ClutterActor *next_sibling = data->next_sibling;
 
+  child->priv->parent = self;
   child->priv->prev_sibling = prev_sibling;
   child->priv->next_sibling = next_sibling;
 
@@ -10316,7 +10441,6 @@ clutter_actor_replace_child (ClutterActor *self,
 
   prev_sibling = old_child->priv->prev_sibling;
   next_sibling = old_child->priv->next_sibling;
-
   clutter_actor_remove_child_internal (self, old_child,
                                        REMOVE_CHILD_DEFAULT_FLAGS);
 
@@ -10523,7 +10647,8 @@ clutter_actor_set_child_above_sibling (ClutterActor *self,
    */
   g_object_ref (child);
   clutter_actor_remove_child_internal (self, child, 0);
-  clutter_actor_add_child_internal (self, child, 0,
+  clutter_actor_add_child_internal (self, child,
+                                    ADD_CHILD_NOTIFY_FIRST_LAST,
                                     insert_child_above,
                                     sibling);
 
@@ -10563,7 +10688,8 @@ clutter_actor_set_child_below_sibling (ClutterActor *self,
   /* see the comment in set_child_above_sibling() */
   g_object_ref (child);
   clutter_actor_remove_child_internal (self, child, 0);
-  clutter_actor_add_child_internal (self, child, 0,
+  clutter_actor_add_child_internal (self, child,
+                                    ADD_CHILD_NOTIFY_FIRST_LAST,
                                     insert_child_below,
                                     sibling);
 
@@ -10596,7 +10722,8 @@ clutter_actor_set_child_at_index (ClutterActor *self,
 
   g_object_ref (child);
   clutter_actor_remove_child_internal (self, child, 0);
-  clutter_actor_add_child_internal (self, child, 0,
+  clutter_actor_add_child_internal (self, child,
+                                    ADD_CHILD_NOTIFY_FIRST_LAST,
                                     insert_child_at_index,
                                     GINT_TO_POINTER (index_));
 
@@ -15632,3 +15759,60 @@ clutter_actor_get_last_child (ClutterActor *self)
 
   return self->priv->last_child;
 }
+
+/*< private >
+ * @self: a #ClutterActor
+ * @func: a comparison function
+ *
+ * Sorts the list of children of @self using the provided comparison
+ * function.
+ */
+void
+_clutter_actor_sort_children (ClutterActor *self,
+                              GCompareFunc  func)
+{
+  ClutterActor *old_first, *old_last;
+  ClutterActor *iter;
+  GList *tmp, *l;
+
+  old_first = self->priv->first_child;
+  old_last = self->priv->last_child;
+
+  /* build a list from the list of children, while removing them
+   * at the same time; removal is O(1), as well as prepending it
+   * to the temporary list
+   */
+  tmp = NULL;
+  iter = self->priv->first_child;
+  while (iter != NULL)
+    {
+      ClutterActor *next = iter->priv->next_sibling;
+
+      tmp = g_list_prepend (tmp, g_object_ref (iter));
+
+      clutter_actor_remove_child_internal (self, iter, 0);
+
+      iter = next;
+    }
+
+  tmp = g_list_sort (tmp, func);
+
+  for (l = tmp; l != NULL; l = l->next)
+    {
+      clutter_actor_add_child_internal (self, l->data, 0,
+                                        insert_child_above,
+                                        NULL);
+      g_object_unref (l->data);
+    }
+
+  g_list_free (tmp);
+
+  /* we don't notify :first-child and :last-child until the end, to avoid
+   * spurious signal emissions
+   */
+  if (old_first != self->priv->first_child)
+    g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+
+  if (old_last != self->priv->last_child)
+    g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+}
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index cbd450e..a75f45c 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -400,16 +400,55 @@ typedef enum { /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/
  * @CLUTTER_ALLOCATION_NONE: No flag set
  * @CLUTTER_ABSOLUTE_ORIGIN_CHANGED: Whether the absolute origin of the
  *   actor has changed; this implies that any ancestor of the actor has
- *   been moved
- *
- * Flags passed to the #ClutterActor::allocate() virtual function and
- * to the clutter_actor_allocate() function
+ *   been moved.
+ * @CLUTTER_DELEGATE_LAYOUT: Whether the allocation should be delegated
+ *   to the #ClutterLayoutManager instance stored inside the
+ *   #ClutterActor:layout-manager property of #ClutterActor. This flag
+ *   should only be used if you are subclassing #ClutterActor and
+ *   overriding the #ClutterActorClass.allocate() virtual function, but
+ *   you wish to use the default implementation of the virtual function
+ *   inside #ClutterActor, for instance:
+ * |[
+ *   static void
+ *   my_actor_allocate (ClutterActor *actor,
+ *                      const ClutterActorBox *alloc,
+ *                      ClutterAllocationFlags flags)
+ *   {
+ *     ClutterActorBox new_alloc;
+ *     ClutterAllocationFlags new_flags;
+ *
+ *     /&ast; change the allocation &ast;/
+ *     new_alloc = *alloc;
+ *     new_alloc.x1 += 6;
+ *     new_alloc.y1 += 3;
+ *     new_alloc.x2 -= 6;
+ *     new_alloc.y2 -= 3;
+ *
+ *     /&ast; change the flags &ast;/
+ *     new_flags = flags | CLUTTER_DELEGATE_LAYOUT;
+ *
+ *     /&ast; store the allocation and delegate the children layout
+ *      &ast; to the ClutterLayoutManager used by the actor.
+ *      &ast;/
+ *     clutter_actor_set_allocation (actor, &amp;new_alloc, flags);
+ *
+ *     /&ast; alternatively, instead of using this flags, you could
+ *      &ast; retrieve the layout manager and call the
+ *      &ast; clutter_layout_manager_allocate() yourself.
+ *      &ast;/
+ *   }
+ * ]|
+ *   the %CLUTTER_DELEGATE_LAYOUT was added in Clutter 1.10.
+ *
+ * Flags passed to the #ClutterActorClass.allocate() virtual function
+ * and to the clutter_actor_allocate() function
  *
  * Since: 1.0
  */
 typedef enum {
   CLUTTER_ALLOCATION_NONE         = 0,
-  CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1
+  CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1,
+  CLUTTER_DELEGATE_LAYOUT         = 1 << 2
 } ClutterAllocationFlags;
 
 /**
diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c
index df7535a..e01f91b 100644
--- a/clutter/clutter-stage.c
+++ b/clutter/clutter-stage.c
@@ -55,6 +55,7 @@
 
 #include "clutter-stage.h"
 #include "deprecated/clutter-stage.h"
+#include "deprecated/clutter-container.h"
 
 #include "clutter-actor-private.h"
 #include "clutter-backend-private.h"
@@ -604,6 +605,7 @@ clutter_stage_paint (ClutterActor *self)
   CoglBufferBit clear_flags;
   CoglColor stage_color;
   guint8 real_alpha;
+
   CLUTTER_STATIC_TIMER (stage_clear_timer,
                         "Painting actors", /* parent */
                         "Stage clear",
@@ -655,7 +657,6 @@ clutter_stage_paint (ClutterActor *self)
     cogl_disable_fog ();
 #endif
 
-  /* this will take care of painting every child */
   CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self);
 }
 
diff --git a/clutter/deprecated/clutter-group.c b/clutter/deprecated/clutter-group.c
index 121e47a..faec5cc 100644
--- a/clutter/deprecated/clutter-group.c
+++ b/clutter/deprecated/clutter-group.c
@@ -54,6 +54,7 @@
 #include "clutter-group.h"
 
 #include "clutter-actor.h"
+#include "clutter-actor-private.h"
 #include "clutter-container.h"
 #include "clutter-fixed-layout.h"
 #include "clutter-main.h"
@@ -114,9 +115,6 @@ clutter_group_real_add (ClutterContainer *container,
   priv->children = g_list_append (priv->children, actor);
   clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
 
-  /* queue a relayout, to get the correct positioning inside
-   * the ::actor-added signal handlers
-   */
   clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
 
   g_signal_emit_by_name (container, "actor-added", actor);
@@ -127,6 +125,27 @@ clutter_group_real_add (ClutterContainer *container,
 }
 
 static void
+clutter_group_real_actor_added (ClutterContainer *container,
+                                ClutterActor     *actor)
+{
+  ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
+
+  /* XXX - children added using clutter_actor_add_child() will
+   * cause actor-added to be emitted without going through the
+   * add() virtual function.
+   *
+   * if we get an actor-added for a child that is not in our
+   * list of children already, then we go in compatibility
+   * mode.
+   */
+  if (g_list_find (priv->children, actor) != NULL)
+    return;
+
+  priv->children = g_list_append (priv->children, actor);
+  clutter_container_sort_depth_order (container);
+}
+
+static void
 clutter_group_real_remove (ClutterContainer *container,
                            ClutterActor     *actor)
 {
@@ -137,15 +156,8 @@ clutter_group_real_remove (ClutterContainer *container,
   priv->children = g_list_remove (priv->children, actor);
   clutter_actor_unparent (actor);
 
-  /* queue a relayout, to get the correct positioning inside
-   * the ::actor-removed signal handlers
-   */
   clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
 
-  /* at this point, the actor passed to the "actor-removed" signal
-   * handlers is not parented anymore to the container but since we
-   * are holding a reference on it, it's still valid
-   */
   g_signal_emit_by_name (container, "actor-removed", actor);
 
   clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
@@ -154,6 +166,19 @@ clutter_group_real_remove (ClutterContainer *container,
 }
 
 static void
+clutter_group_real_actor_removed (ClutterContainer *container,
+                                  ClutterActor     *actor)
+{
+  ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
+
+  /* XXX - same compatibility mode of the ::actor-added implementation */
+  if (g_list_find (priv->children, actor) == NULL)
+    return;
+
+  priv->children = g_list_remove (priv->children, actor);
+}
+
+static void
 clutter_group_real_foreach (ClutterContainer *container,
                             ClutterCallback   callback,
                             gpointer          user_data)
@@ -199,7 +224,9 @@ clutter_group_real_raise (ClutterContainer *container,
    * as values are equal ordering shouldn't change but Z
    * values will be correct.
    *
-   * FIXME: optimise
+   * FIXME: get rid of this crap; this is so utterly broken and wrong on
+   * so many levels it's not even funny. sadly, we get to keep this until
+   * we can break API and remove Group for good.
    */
   if (sibling &&
       clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor))
@@ -256,6 +283,13 @@ clutter_group_real_sort_depth_order (ClutterContainer *container)
 
   priv->children = g_list_sort (priv->children, sort_by_depth);
 
+  /* XXX - this is a hack, to ensure that the list of children that is stored
+   * inside ClutterActor itself is kept in sync with the list of children held
+   * by ClutterGroup. this is needed so we can use the old deprecated API and
+   * mix it with the Actor API.
+   */
+  _clutter_actor_sort_children (CLUTTER_ACTOR (container), sort_by_depth);
+
   clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
 }
 
@@ -263,7 +297,9 @@ static void
 clutter_container_iface_init (ClutterContainerIface *iface)
 {
   iface->add = clutter_group_real_add;
+  iface->actor_added = clutter_group_real_actor_added;
   iface->remove = clutter_group_real_remove;
+  iface->actor_removed = clutter_group_real_actor_removed;
   iface->foreach = clutter_group_real_foreach;
   iface->raise = clutter_group_real_raise;
   iface->lower = clutter_group_real_lower;
@@ -444,8 +480,8 @@ clutter_group_init (ClutterGroup *self)
   self->priv->layout = clutter_fixed_layout_new ();
   g_object_ref_sink (self->priv->layout);
 
-  clutter_layout_manager_set_container (self->priv->layout,
-                                        CLUTTER_CONTAINER (self));
+  clutter_actor_set_layout_manager (CLUTTER_ACTOR (self),
+                                    self->priv->layout);
 }
 
 /**



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]