[gimp/wip/animation: 66/197] plug-ins: serialization for cel animations.



commit ac3a7d1910aa9c9c59adac256ec75117537b1a4f
Author: Jehan <jehan girinstud io>
Date:   Sun Aug 7 02:16:45 2016 +0200

    plug-ins: serialization for cel animations.

 .../animation-play/core/animation-celanimation.c   |  621 ++++++++++++++++++--
 .../animation-play/core/animation-celanimation.h   |    4 +
 plug-ins/animation-play/core/animation.c           |   30 +-
 plug-ins/animation-play/core/animationanimatic.c   |   12 +-
 plug-ins/animation-play/widgets/animation-dialog.c |   28 +-
 5 files changed, 621 insertions(+), 74 deletions(-)
---
diff --git a/plug-ins/animation-play/core/animation-celanimation.c 
b/plug-ins/animation-play/core/animation-celanimation.c
index 7b3dd58..8457a2a 100644
--- a/plug-ins/animation-play/core/animation-celanimation.c
+++ b/plug-ins/animation-play/core/animation-celanimation.c
@@ -68,31 +68,78 @@ struct _AnimationCelAnimationPrivate
   GList *tracks;
 };
 
+typedef enum
+{
+  START_STATE,
+  ANIMATION_STATE,
+  SEQUENCE_STATE,
+  FRAME_STATE,
+  LAYER_STATE,
+  COMMENTS_STATE,
+  COMMENT_STATE,
+  END_STATE
+} AnimationParseState;
+
+typedef struct
+{
+  Animation           *animation;
+  AnimationParseState  state;
+
+  Track               *track;
+  gint                 frame_position;
+  gint                 frame_duration;
+} ParseStatus;
+
 #define GET_PRIVATE(animation) \
         G_TYPE_INSTANCE_GET_PRIVATE (animation, \
                                      ANIMATION_TYPE_CEL_ANIMATION, \
                                      AnimationCelAnimationPrivate)
 
-static void         animation_cel_animation_finalize   (GObject           *object);
+static void         animation_cel_animation_finalize     (GObject           *object);
 
 /* Virtual methods */
 
-static gint         animation_cel_animation_get_length  (Animation         *animation);
-
-static void         animation_cel_animation_load        (Animation         *animation);
-static GeglBuffer * animation_cel_animation_get_frame   (Animation         *animation,
-                                                         gint               pos);
-static gboolean     animation_cel_animation_same        (Animation         *animation,
-                                                         gint               previous_pos,
-                                                         gint               next_pos);
+static gint         animation_cel_animation_get_length   (Animation         *animation);
+
+static void         animation_cel_animation_load         (Animation         *animation);
+static void         animation_cel_animation_load_xml     (Animation         *animation,
+                                                          const gchar       *xml);
+static GeglBuffer * animation_cel_animation_get_frame    (Animation         *animation,
+                                                          gint               pos);
+static gchar      * animation_cel_animation_serialize    (Animation         *animation);
+
+static gboolean     animation_cel_animation_same         (Animation         *animation,
+                                                          gint               previous_pos,
+                                                          gint               next_pos);
+
+/* XML parsing */
+
+static void      animation_cel_animation_start_element   (GMarkupParseContext *context,
+                                                          const gchar         *element_name,
+                                                          const gchar        **attribute_names,
+                                                          const gchar        **attribute_values,
+                                                          gpointer             user_data,
+                                                          GError             **error);
+static void      animation_cel_animation_end_element     (GMarkupParseContext *context,
+                                                          const gchar         *element_name,
+                                                          gpointer             user_data,
+                                                          GError             **error);
+
+static void      animation_cel_animation_text            (GMarkupParseContext  *context,
+                                                          const gchar          *text,
+                                                          gsize                 text_len,
+                                                          gpointer              user_data,
+                                                          GError              **error);
 
 /* Utils */
 
-static void         animation_cel_animation_cleanup   (AnimationCelAnimation  *animation);
-static void         animation_cel_animation_cache      (AnimationCelAnimation *animation,
-                                                        gint                   position);
-static gboolean     animation_cel_animation_cache_cmp (Cache *cache1,
-                                                       Cache *cache2);
+static void         animation_cel_animation_cleanup      (AnimationCelAnimation  *animation);
+static void         animation_cel_animation_cache        (AnimationCelAnimation  *animation,
+                                                          gint                    position);
+static gboolean     animation_cel_animation_cache_cmp    (Cache                   *cache1,
+                                                          Cache                   *cache2);
+static void         animation_cel_animation_clean_cache  (Cache                   *cache);
+static void         animation_cel_animation_clean_track  (Track                   *track);
 
 G_DEFINE_TYPE (AnimationCelAnimation, animation_cel_animation, ANIMATION_TYPE_ANIMATION)
 
@@ -108,7 +155,9 @@ animation_cel_animation_class_init (AnimationCelAnimationClass *klass)
 
   anim_class->get_length = animation_cel_animation_get_length;
   anim_class->load       = animation_cel_animation_load;
+  anim_class->load_xml   = animation_cel_animation_load_xml;
   anim_class->get_frame  = animation_cel_animation_get_frame;
+  anim_class->serialize  = animation_cel_animation_serialize;
   anim_class->same       = animation_cel_animation_same;
 
   g_type_class_add_private (klass, sizeof (AnimationCelAnimationPrivate));
@@ -125,6 +174,9 @@ animation_cel_animation_init (AnimationCelAnimation *animation)
 static void
 animation_cel_animation_finalize (GObject *object)
 {
+  /* Save first, before cleaning anything. */
+  animation_save_to_parasite (ANIMATION (object));
+
   animation_cel_animation_cleanup (ANIMATION_CEL_ANIMATION (object));
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -143,7 +195,7 @@ animation_cel_animation_set_comment (AnimationCelAnimation *animation,
                     position <= animation->priv->duration);
 
   item = g_list_nth (animation->priv->comments, position - 1);
-  if (item->data)
+  if (item && item->data)
     {
       g_free (item->data);
     }
@@ -162,6 +214,57 @@ animation_cel_animation_get_comment (AnimationCelAnimation *animation,
   return g_list_nth_data (animation->priv->comments, position - 1);
 }
 
+void
+animation_cel_animation_set_duration (AnimationCelAnimation *animation,
+                                      gint                   duration)
+{
+  if (duration < animation->priv->duration)
+    {
+      GList *iter;
+
+      /* Free memory. */
+      iter = g_list_nth (animation->priv->cache, duration);
+      if (iter && iter->prev)
+        {
+          iter->prev->next = NULL;
+          iter->prev = NULL;
+        }
+      g_list_free_full (iter, (GDestroyNotify) animation_cel_animation_clean_cache);
+
+      iter = g_list_nth (animation->priv->tracks, duration);
+      if (iter && iter->prev)
+        {
+          iter->prev->next = NULL;
+          iter->prev = NULL;
+        }
+      g_list_free_full (iter, (GDestroyNotify) animation_cel_animation_clean_track);
+
+      iter = g_list_nth (animation->priv->comments, duration);
+      if (iter && iter->prev)
+        {
+          iter->prev->next = NULL;
+          iter->prev = NULL;
+        }
+      g_list_free_full (iter, (GDestroyNotify) g_free);
+
+      for (iter = animation->priv->tracks; iter; iter = iter->next)
+        {
+          Track *track = iter->data;
+          GList *iter2;
+
+          iter2 = g_list_nth (track->frames, duration);
+          if (iter2 && iter2->prev)
+            {
+              iter2->prev->next = NULL;
+              iter2->prev = NULL;
+            }
+          g_list_free (iter2);
+        }
+    }
+
+  animation->priv->duration = duration;
+}
+
 /**** Virtual methods ****/
 
 static gint
@@ -224,6 +327,65 @@ animation_cel_animation_load (Animation *animation)
     }
 }
 
+static void
+animation_cel_animation_load_xml (Animation   *animation,
+                                  const gchar *xml)
+{
+  const GMarkupParser markup_parser =
+    {
+      animation_cel_animation_start_element,
+      animation_cel_animation_end_element,
+      animation_cel_animation_text,
+      NULL,  /*  passthrough  */
+      NULL   /*  error        */
+    };
+  GMarkupParseContext *context;
+  ParseStatus          status = { 0, };
+  GError              *error  = NULL;
+
+  g_return_if_fail (xml != NULL);
+
+  /* Parse XML to update. */
+  status.state = START_STATE;
+  status.animation = animation;
+
+  context = g_markup_parse_context_new (&markup_parser,
+                                        0, &status, NULL);
+  g_markup_parse_context_parse (context, xml, strlen (xml), &error);
+  if (error)
+    {
+      g_warning ("Error parsing XML: %s", error->message);
+    }
+  else
+    {
+      g_markup_parse_context_end_parse (context, &error);
+      if (error)
+        g_warning ("Error parsing XML: %s", error->message);
+    }
+  g_markup_parse_context_free (context);
+
+  /* If XML parsing failed, just reset the animation. */
+  if (error)
+    {
+      animation_cel_animation_load (animation);
+    }
+  else
+    {
+      /* cache. */
+      gint i;
+      gint duration = animation_get_length (animation);
+
+      for (i = 0; i < duration; i++)
+        {
+          g_signal_emit_by_name (animation, "loading",
+                                 (gdouble) i / ((gdouble) duration - 0.999));
+
+          /* Panel image. */
+          animation_cel_animation_cache (ANIMATION_CEL_ANIMATION (animation), i);
+        }
+    }
+}
+
 static GeglBuffer *
 animation_cel_animation_get_frame (Animation *animation,
                                    gint       pos)
@@ -243,6 +405,94 @@ animation_cel_animation_get_frame (Animation *animation,
   return frame;
 }
 
+static gchar *
+animation_cel_animation_serialize (Animation *animation)
+{
+  AnimationCelAnimationPrivate *priv;
+  gchar                        *xml;
+  gchar                        *xml2;
+  gchar                        *tmp;
+  GList                        *iter;
+  gint                          i;
+
+  priv = ANIMATION_CEL_ANIMATION (animation)->priv;
+
+  xml = g_strdup_printf ("<animation type=\"cels\" framerate=\"%f\" "
+                          " duration=\"%d\" width=\"\" height=\"\">",
+                          animation_get_framerate (animation),
+                          priv->duration);
+
+  for (iter = priv->tracks; iter; iter = iter->next)
+    {
+      Track *track = iter->data;
+      GList *iter2;
+      gint   pos;
+      gint   duration;
+
+      xml2 = g_markup_printf_escaped ("<sequence name=\"%s\">",
+                                      track->title);
+      tmp = xml;
+      xml = g_strconcat (xml, xml2, NULL);
+      g_free (tmp);
+      g_free (xml2);
+
+      pos = 1;
+      duration = 0;
+      for (iter2 = track->frames; iter2; iter2 = iter2->next)
+        {
+          if (GPOINTER_TO_INT (iter2->data))
+            {
+              duration++;
+              if (! iter2->next || iter2->next->data != iter2->data)
+                {
+                  xml2 = g_markup_printf_escaped ("<frame position=\"%d\""
+                                                  " duration=\"%d\">"
+                                                  "<layer id=\"%d\"/>"
+                                                  "</frame>",
+                                                  pos - duration, duration,
+                                                  GPOINTER_TO_INT (iter2->data));
+                  tmp = xml;
+                  xml = g_strconcat (xml, xml2, NULL);
+                  g_free (tmp);
+                  g_free (xml2);
+                }
+            }
+          pos++;
+        }
+
+      tmp = xml;
+      xml = g_strconcat (xml, "</sequence>", NULL);
+      g_free (tmp);
+    }
+
+  tmp = xml;
+  xml = g_strconcat (xml, "<comments title=\"\">", NULL);
+  g_free (tmp);
+
+  /* New loop for comments. */
+  for (iter = priv->comments, i = 0; iter; iter = iter->next, i++)
+    {
+      if (iter->data)
+        {
+          gchar *comment = iter->data;
+
+          /* Comments are for a given panel, not for a frame position. */
+          xml2 = g_markup_printf_escaped ("<comment frame-position=\"%d\">%s</comment>",
+                                          i + 1,
+                                          comment);
+          tmp = xml;
+          xml = g_strconcat (xml, xml2, NULL);
+          g_free (tmp);
+          g_free (xml2);
+        }
+    }
+  tmp = xml;
+  xml = g_strconcat (xml, "</comments></animation>", NULL);
+  g_free (tmp);
+
+  return xml;
+}
+
 static gboolean
 animation_cel_animation_same (Animation *animation,
                               gint       pos1,
@@ -254,10 +504,10 @@ animation_cel_animation_same (Animation *animation,
 
   cel_animation = ANIMATION_CEL_ANIMATION (animation);
 
-  g_return_val_if_fail (pos1 > 0                              &&
-                        pos1 <= cel_animation->priv->duration &&
-                        pos2 > 0                              &&
-                        pos2 <= cel_animation->priv->duration,
+  g_return_val_if_fail (pos1 >= 0                            &&
+                        pos1 < cel_animation->priv->duration &&
+                        pos2 >= 0                            &&
+                        pos2 < cel_animation->priv->duration,
                         FALSE);
 
   cache1 = g_list_nth_data (cel_animation->priv->cache,
@@ -268,59 +518,305 @@ animation_cel_animation_same (Animation *animation,
   return animation_cel_animation_cache_cmp (cache1, cache2);
 }
 
-/**** Utils ****/
-
 static void
-animation_cel_animation_cleanup (AnimationCelAnimation *animation)
+animation_cel_animation_start_element (GMarkupParseContext  *context,
+                                       const gchar          *element_name,
+                                       const gchar         **attribute_names,
+                                       const gchar         **attribute_values,
+                                       gpointer              user_data,
+                                       GError              **error)
 {
-  AnimationCelAnimationPrivate *priv;
-  GList                        *iter;
-
-  priv = ANIMATION_CEL_ANIMATION (animation)->priv;
+  const gchar                  **names  = attribute_names;
+  const gchar                  **values = attribute_values;
+  ParseStatus                   *status = (ParseStatus *) user_data;
+  AnimationCelAnimation         *animation = ANIMATION_CEL_ANIMATION (status->animation);
+  AnimationCelAnimationPrivate  *priv   = GET_PRIVATE (status->animation);
 
-  if (priv->cache)
+  switch (status->state)
     {
-      for (iter = priv->cache; iter; iter = iter->next)
+    case START_STATE:
+      if (g_strcmp0 (element_name, "animation") != 0)
         {
-          if (iter->data)
+          g_set_error (error, 0, 0,
+                       _("Tag <animation> expected. "
+                         "Got \"%s\" instead."),
+                       element_name);
+          return;
+        }
+      while (*names && *values)
+        {
+          if (strcmp (*names, "type") == 0)
             {
-              Cache *cache = iter->data;
+              if (! **values || strcmp (*values, "cels") != 0)
+                {
+                  g_set_error (error, 0, 0,
+                               _("Unknown animation type: \"%s\"."),
+                               *values);
+                  return;
+                }
+            }
+          else if (strcmp (*names, "framerate") == 0 && **values)
+            {
+              gdouble fps = g_strtod (*values, NULL);
+              if (fps >= MAX_FRAMERATE)
+                {
+                  /* Let's avoid huge frame rates. */
+                  fps = MAX_FRAMERATE;
+                }
+              else if (fps <= 0)
+                {
+                  /* Null or negative framerates are impossible. */
+                  fps = DEFAULT_FRAMERATE;
+                }
+              animation_set_framerate (status->animation, fps);
+            }
+          else if (strcmp (*names, "duration") == 0 && **values)
+            {
+              gint duration = (gint) g_ascii_strtoull (*values, NULL, 10);
+
+              animation_cel_animation_set_duration (animation, duration);
+            }
 
-              if (--(cache->refs) == 0)
+          names++;
+          values++;
+        }
+      status->state = ANIMATION_STATE;
+      break;
+    case ANIMATION_STATE:
+      if (g_strcmp0 (element_name, "sequence") == 0)
+        {
+          status->track = g_new0 (Track, 1);
+          while (*names && *values)
+            {
+              if (strcmp (*names, "name") == 0)
                 {
-                  g_object_unref (cache->buffer);
-                  g_free (cache->composition);
+                  status->track->title = g_strdup (*values);
                 }
+              names++;
+              values++;
             }
+          priv->tracks = g_list_prepend (priv->tracks, status->track);
+          status->state = SEQUENCE_STATE;
         }
-      g_list_free (priv->cache);
-    }
-  if (priv->comments)
-    {
-      for (iter = priv->comments; iter; iter = iter->next)
+      else if (g_strcmp0 (element_name, "comments") == 0)
         {
-          if (iter->data)
+          status->state = COMMENTS_STATE;
+        }
+      else
+        {
+          g_set_error (error, 0, 0,
+                       _("Tags <sequence> or <comments> expected. "
+                         "Got \"%s\" instead."),
+                       element_name);
+          return;
+        }
+      break;
+    case SEQUENCE_STATE:
+      if (g_strcmp0 (element_name, "frame") != 0)
+        {
+          g_set_error (error, 0, 0,
+                       _("Tag <frame> expected. "
+                         "Got \"%s\" instead."),
+                       element_name);
+          return;
+        }
+      status->frame_position = -1;
+      status->frame_duration = -1;
+
+      while (*names && *values)
+        {
+          if (strcmp (*names, "position") == 0 && **values)
             {
-              g_free (iter->data);
+              gint position = g_ascii_strtoll (*values, NULL, 10);
+
+              if (position >= 0)
+                status->frame_position = position;
             }
+          else if (strcmp (*names, "duration") == 0 && **values)
+            {
+              gint duration = g_ascii_strtoll (*values, NULL, 10);
+
+              if (duration > 0)
+                status->frame_duration = duration;
+            }
+
+          names++;
+          values++;
         }
-      g_list_free (priv->comments);
-    }
-  if (priv->tracks)
-    {
-      for (iter = priv->tracks; iter; iter = iter->next)
+      if (status->frame_position == -1 ||
+          status->frame_duration == -1)
         {
-          Track *track = iter->data;
+          g_set_error (error, 0, 0,
+                       _("Tag <frame> expects the properties: "
+                         "position, duration."));
+        }
+      status->state = FRAME_STATE;
+      break;
+    case FRAME_STATE:
+      if (g_strcmp0 (element_name, "layer") != 0)
+        {
+          g_set_error (error, 0, 0,
+                       _("Tag <layer> expected. "
+                         "Got \"%s\" instead."),
+                       element_name);
+          return;
+        }
+      while (*names && *values)
+        {
+          if (strcmp (*names, "id") == 0 && **values)
+            {
+              GList *iter;
+              gint   tattoo = g_ascii_strtoll (*values, NULL, 10);
+              gint   track_length;
+              gint   i;
+
+              track_length = g_list_length (status->track->frames);
 
-          /* The item's data must always exist. */
-          g_free (track->title);
-          g_list_free (track->frames);
+              if (track_length < status->frame_position + status->frame_duration)
+                {
+                  /* Make sure the list is long enough. */
+                  for (i = track_length; i < status->frame_position + status->frame_duration; i++)
+                    {
+                      status->track->frames = g_list_prepend (status->track->frames,
+                                                              NULL);
+                    }
+                }
+              iter = status->track->frames;
+              for (i = 0; i < status->frame_position + status->frame_duration; i++)
+                {
+                  if (i >= status->frame_position)
+                    {
+                      iter->data = GINT_TO_POINTER (tattoo);
+                    }
+                  iter = iter->next;
+                }
+            }
+
+          names++;
+          values++;
+        }
+      status->state = LAYER_STATE;
+      break;
+    case LAYER_STATE:
+      /* <layer> should have no child tag. */
+      g_set_error (error, 0, 0,
+                   _("Unexpected child of <layer>: \"%s\"."),
+                   element_name);
+      return;
+    case COMMENTS_STATE:
+      if (g_strcmp0 (element_name, "comment") != 0)
+        {
+          g_set_error (error, 0, 0,
+                       _("Tag <comment> expected. "
+                         "Got \"%s\" instead."),
+                       element_name);
+          return;
+        }
+      status->frame_position = -1;
+      status->frame_duration = -1;
+
+      while (*names && *values)
+        {
+          if (strcmp (*names, "position") == 0 && **values)
+            {
+              gint position = (gint) g_ascii_strtoll (*values, NULL, 10);
+
+              status->frame_position = position;
+              break;
+            }
+
+          names++;
+          values++;
         }
-      g_list_free (priv->tracks);
+      status->state = COMMENT_STATE;
+      break;
+    case COMMENT_STATE:
+      /* <comment> should have no child tag. */
+      g_set_error (error, 0, 0,
+                   _("Unexpected child of <comment>: <\"%s\">."),
+                   element_name);
+      return;
+    default:
+      g_set_error (error, 0, 0,
+                   _("Unknown state!"));
+      break;
+    }
+}
+
+static void
+animation_cel_animation_end_element (GMarkupParseContext *context,
+                                const gchar         *element_name,
+                                gpointer             user_data,
+                                GError             **error)
+{
+  ParseStatus *status = (ParseStatus *) user_data;
+
+  switch (status->state)
+    {
+    case SEQUENCE_STATE:
+    case COMMENTS_STATE:
+      status->state = ANIMATION_STATE;
+      break;
+    case FRAME_STATE:
+      status->state = SEQUENCE_STATE;
+      break;
+    case LAYER_STATE:
+      status->state = FRAME_STATE;
+      break;
+    case ANIMATION_STATE:
+      status->state = END_STATE;
+      break;
+    case COMMENT_STATE:
+      status->state = COMMENTS_STATE;
+      break;
+    default: /* START/END_STATE */
+      /* invalid XML. I expect the parser to raise an error anyway.*/
+      break;
+    }
+}
+
+static void
+animation_cel_animation_text (GMarkupParseContext  *context,
+                         const gchar          *text,
+                         gsize                 text_len,
+                         gpointer              user_data,
+                         GError              **error)
+{
+  ParseStatus *status = (ParseStatus *) user_data;
+  AnimationCelAnimation *cel_animation = ANIMATION_CEL_ANIMATION (status->animation);
+
+  switch (status->state)
+    {
+    case COMMENT_STATE:
+      if (status->frame_position == -1)
+        /* invalid comment tag. */
+        break;
+      /* Setting comment to a panel. */
+      animation_cel_animation_set_comment (cel_animation,
+                                           status->frame_position,
+                                           text);
+      status->frame_position = -1;
+      break;
+    default:
+      /* Ignoring text everywhere else. */
+      break;
     }
-  priv->cache    = NULL;
-  priv->comments = NULL;
-  priv->tracks   = NULL;
+}
+
+/**** Utils ****/
+
+static void
+animation_cel_animation_cleanup (AnimationCelAnimation *animation)
+{
+  g_list_free_full (animation->priv->cache,
+                    (GDestroyNotify) animation_cel_animation_clean_cache);
+  animation->priv->cache    = NULL;
+  g_list_free_full (animation->priv->comments,
+                    (GDestroyNotify) g_free);
+  animation->priv->comments = NULL;
+  g_list_free_full (animation->priv->tracks,
+                    (GDestroyNotify) animation_cel_animation_clean_track);
+  animation->priv->tracks   = NULL;
 }
 
 static void
@@ -491,3 +987,22 @@ animation_cel_animation_cache_cmp (Cache *cache1,
     }
   return identical;
 }
+
+static void
+animation_cel_animation_clean_cache (Cache *cache)
+{
+  if (--(cache->refs) == 0)
+    {
+      g_object_unref (cache->buffer);
+      g_free (cache->composition);
+      g_free (cache);
+    }
+}
+
+static void
+animation_cel_animation_clean_track (Track *track)
+{
+  g_free (track->title);
+  g_list_free (track->frames);
+  g_free (track);
+}
diff --git a/plug-ins/animation-play/core/animation-celanimation.h 
b/plug-ins/animation-play/core/animation-celanimation.h
index a33e403..3e30fc4 100644
--- a/plug-ins/animation-play/core/animation-celanimation.h
+++ b/plug-ins/animation-play/core/animation-celanimation.h
@@ -55,4 +55,8 @@ void             animation_cel_animation_set_comment  (AnimationCelAnimation *an
 const gchar    * animation_cel_animation_get_comment  (AnimationCelAnimation *animation,
                                                        gint                   position);
 
+void             animation_cel_animation_set_duration (AnimationCelAnimation *animation,
+                                                       gint                   duration);
+
+
 #endif  /*  __ANIMATION_CEL_ANIMATION_H__  */
diff --git a/plug-ins/animation-play/core/animation.c b/plug-ins/animation-play/core/animation.c
index 5ca9569..7b9b8eb 100644
--- a/plug-ins/animation-play/core/animation.c
+++ b/plug-ins/animation-play/core/animation.c
@@ -442,6 +442,7 @@ animation_save_to_parasite (Animation *animation)
   AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
   GimpParasite     *old_parasite;
   const gchar      *parasite_name;
+  const gchar      *selected;
   gchar            *xml;
   gboolean          undo_step_started = FALSE;
   CachedSettings    settings;
@@ -455,10 +456,12 @@ animation_save_to_parasite (Animation *animation)
 
   if (ANIMATION_IS_ANIMATIC (animation))
     {
+      selected = "animatic";
       parasite_name = PLUG_IN_PROC "/animatic";
     }
   else /* ANIMATION_IS_CEL_ANIMATION */
     {
+      selected = "cel-animation";
       parasite_name = PLUG_IN_PROC "/cel-animation";
     }
   /* If there was already parasites and they were all the same as the
@@ -470,6 +473,7 @@ animation_save_to_parasite (Animation *animation)
               g_strcmp0 ((gchar *) gimp_parasite_data (old_parasite), xml)))
     {
       GimpParasite *parasite;
+
       if (! undo_step_started)
         {
           gimp_image_undo_group_start (priv->image_id);
@@ -483,6 +487,26 @@ animation_save_to_parasite (Animation *animation)
     }
   gimp_parasite_free (old_parasite);
 
+  old_parasite = gimp_image_get_parasite (priv->image_id,
+                                          PLUG_IN_PROC "/selected");
+  if (! old_parasite ||
+      g_strcmp0 ((gchar *) gimp_parasite_data (old_parasite), selected))
+    {
+      GimpParasite *parasite;
+
+      if (! undo_step_started)
+        {
+          gimp_image_undo_group_start (priv->image_id);
+          undo_step_started = TRUE;
+        }
+      parasite = gimp_parasite_new (PLUG_IN_PROC "/selected",
+                                    GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE,
+                                    strlen (selected) + 1, selected);
+      gimp_image_attach_parasite (priv->image_id, parasite);
+      gimp_parasite_free (parasite);
+    }
+  gimp_parasite_free (old_parasite);
+
   if (undo_step_started)
     {
       gimp_image_undo_group_end (priv->image_id);
@@ -598,7 +622,7 @@ animation_play (Animation *animation)
 }
 
 void
-animation_stop (Animation   *animation)
+animation_stop (Animation *animation)
 {
   AnimationPrivate *priv = ANIMATION_GET_PRIVATE (animation);
 
@@ -672,8 +696,8 @@ void
 animation_jump (Animation *animation,
                 gint       index)
 {
-  AnimationPrivate *priv           = ANIMATION_GET_PRIVATE (animation);
-  GeglBuffer       *buffer         = NULL;
+  AnimationPrivate *priv     = ANIMATION_GET_PRIVATE (animation);
+  GeglBuffer       *buffer   = NULL;
   gint              prev_pos = priv->position;
   gboolean          identical;
 
diff --git a/plug-ins/animation-play/core/animationanimatic.c 
b/plug-ins/animation-play/core/animationanimatic.c
index 356036f..84abb0d 100644
--- a/plug-ins/animation-play/core/animationanimatic.c
+++ b/plug-ins/animation-play/core/animationanimatic.c
@@ -93,8 +93,8 @@ static GeglBuffer * animation_animatic_get_frame   (Animation         *animation
 static gchar      * animation_animatic_serialize   (Animation         *animation);
 
 static gboolean     animation_animatic_same        (Animation         *animation,
-                                                    gint               previous_pos,
-                                                    gint               next_pos);
+                                                    gint               pos1,
+                                                    gint               pos2);
 
 /* XML parsing */
 
@@ -598,8 +598,8 @@ animation_animatic_serialize (Animation *animation)
 
 static gboolean
 animation_animatic_same (Animation *animation,
-                         gint       previous_pos,
-                         gint       next_pos)
+                         gint       pos1,
+                         gint       pos2)
 {
   AnimationAnimaticPrivate *priv = GET_PRIVATE (animation);
   gint                      count = 0;
@@ -609,12 +609,12 @@ animation_animatic_same (Animation *animation,
   for (i = 0; i < priv->n_panels; i++)
     {
       count += priv->durations[i];
-      if (count >= previous_pos && count >= next_pos)
+      if (count >= pos1 && count >= pos2)
         {
           identical = TRUE;
           break;
         }
-      else if (count >= previous_pos || count >= next_pos)
+      else if (count >= pos1 || count >= pos2)
         {
           identical = FALSE;
           break;
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c 
b/plug-ins/animation-play/widgets/animation-dialog.c
index 744c8c7..232d613 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -337,6 +337,7 @@ animation_dialog_new (gint32 image_id)
           animatic_selected = FALSE;
         }
       gimp_parasite_free (parasite);
+      parasite = NULL;
     }
 
   if (animatic_selected)
@@ -1200,11 +1201,6 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                      G_CALLBACK (zoomcombo_changed),
                                      dialog);
 
-  /* Animation type. */
-  g_signal_handlers_unblock_by_func (priv->animation_type_combo,
-                                     G_CALLBACK (animation_type_changed),
-                                     dialog);
-
   /* Progress bar. */
   g_signal_handlers_unblock_by_func (priv->progress,
                                      G_CALLBACK (progress_entered),
@@ -1223,12 +1219,12 @@ animation_dialog_set_animation (AnimationDialog *dialog,
   if (right_pane)
     gtk_widget_destroy (right_pane);
 
-  /* The Storyboard view. */
   if (ANIMATION_IS_ANIMATIC (animation))
     {
       GtkWidget *scrolled_win;
       GtkWidget *frame;
 
+      /* The Storyboard view. */
       frame = gtk_frame_new (_("Storyboard"));
       gtk_paned_pack2 (GTK_PANED (priv->hpaned), frame,
                        TRUE, TRUE);
@@ -1243,8 +1239,21 @@ animation_dialog_set_animation (AnimationDialog *dialog,
                                              right_pane);
 
       gtk_widget_show (right_pane);
+
+      /* The animation type box. */
+      gtk_combo_box_set_active (GTK_COMBO_BOX (priv->animation_type_combo), 0);
+    }
+  else
+    {
+      /* The animation type box. */
+      gtk_combo_box_set_active (GTK_COMBO_BOX (priv->animation_type_combo), 1);
     }
 
+  /* Animation type. */
+  g_signal_handlers_unblock_by_func (priv->animation_type_combo,
+                                     G_CALLBACK (animation_type_changed),
+                                     dialog);
+
   /* Animation. */
   g_signal_connect (priv->animation, "proxy",
                     G_CALLBACK (proxy_changed),
@@ -1364,7 +1373,7 @@ animation_type_changed (GtkWidget       *combo,
 {
   AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
   Animation              *animation;
-  GimpParasite           *parasite;
+  GimpParasite           *parasite = NULL;
   const gchar            *xml = NULL;
   gint                    index;
 
@@ -1378,11 +1387,6 @@ animation_type_changed (GtkWidget       *combo,
     {
       parasite = gimp_image_get_parasite (priv->image_id,
                                           PLUG_IN_PROC "/animatic");
-      if (parasite)
-        {
-          xml = g_strdup (gimp_parasite_data (parasite));
-          gimp_parasite_free (parasite);
-        }
     }
   else
     {


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