[gimp-gap] edit movepath settings support via storyboard



commit 27509333212c24847a842b45227247e1d8529985
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Sun May 8 18:51:33 2011 +0200

    edit movepath settings support via storyboard

 ChangeLog                     |   20 ++
 gap/gap_lib.c                 |   40 +++
 gap/gap_lib.h                 |    1 +
 gap/gap_mov_dialog.c          |  740 ++++++++++++++++++++++++++++++++++-------
 gap/gap_mov_dialog.h          |   11 +
 gap/gap_mov_exec.c            |   89 ++++--
 gap/gap_story_att_trans_dlg.c |  253 ++++++++++++++-
 gap/gap_story_dialog.c        |   43 ++-
 gap/gap_story_main.h          |   17 +-
 gap/gap_story_undo.c          |  336 ++++++++++++++++++-
 gap/gap_story_undo.h          |    4 +
 gap/gap_story_undo_types.h    |   24 +-
 12 files changed, 1403 insertions(+), 175 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index f48fccb..ba66a01 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2011-05-08 Wolfgang Hofer <hof gimp org>
+
+- added edit button in the Storyboard attributes dialog
+  to pop up a movepath edit dialog window
+  for creation and edit of movepath settings.
+
+- handle undo / redo for edited movepath xml paramter file content
+  when edited via the new edit button in the Storyboard attributes dialog
+
+ * gap/gap_lib.c [.h]
+ * gap/gap_mov_dialog.c [.h]
+ * gap/gap_mov_exec.c
+ * gap/gap_story_att_trans_dlg.c
+ * gap/gap_story_main.h
+ * gap/gap_story_dialog.c
+ * gap/gap_story_undo.c [.h]
+ * gap/gap_story_undo_types.h
+ 
+
+
 2011-04-27 Wolfgang Hofer <hof gimp org>
 
 - fixed a bug in procedure gap_mov_xml_par_save that did write an invalid 
diff --git a/gap/gap_lib.c b/gap/gap_lib.c
index 04fcf9d..1fdfdbe 100644
--- a/gap/gap_lib.c
+++ b/gap/gap_lib.c
@@ -1353,6 +1353,46 @@ gap_lib_alloc_ainfo_from_name(const char *imagename, GimpRunMode run_mode)
 
 
 /* ============================================================================
+ * gap_lib_alloc_ainfo_unsaved_image
+ *
+ * allocate and init an ainfo structure from unsaved image.
+ * ============================================================================
+ */
+GapAnimInfo *
+gap_lib_alloc_ainfo_unsaved_image(gint32 image_id)
+{
+   GapAnimInfo   *l_ainfo_ptr;
+
+   l_ainfo_ptr = (GapAnimInfo*)g_malloc(sizeof(GapAnimInfo));
+   if(l_ainfo_ptr == NULL) return(NULL);
+
+   l_ainfo_ptr->basename = NULL;
+   l_ainfo_ptr->new_filename = NULL;
+   l_ainfo_ptr->extension = NULL;
+   l_ainfo_ptr->image_id = image_id;
+
+   l_ainfo_ptr->old_filename = NULL;
+
+   l_ainfo_ptr->ainfo_type = GAP_AINFO_IMAGE;
+   l_ainfo_ptr->extension = NULL;
+
+   l_ainfo_ptr->frame_nr = 0;
+   l_ainfo_ptr->curr_frame_nr = 0;
+   l_ainfo_ptr->first_frame_nr = -1;
+   l_ainfo_ptr->last_frame_nr = -1;
+   l_ainfo_ptr->frame_cnt = 0;
+   l_ainfo_ptr->run_mode = GIMP_RUN_NONINTERACTIVE;
+   l_ainfo_ptr->frame_nr_before_curr_frame_nr = -1;   /* -1 if no frame found before curr_frame_nr */
+   l_ainfo_ptr->frame_nr_after_curr_frame_nr = -1;    /* -1 if no frame found after curr_frame_nr */
+
+
+   return(l_ainfo_ptr);
+
+}    /* end gap_lib_alloc_ainfo_unsaved_image */
+
+
+
+/* ============================================================================
  * gap_lib_alloc_ainfo
  *
  * allocate and init an ainfo structure from the given image.
diff --git a/gap/gap_lib.h b/gap/gap_lib.h
index 8666384..8277542 100644
--- a/gap/gap_lib.h
+++ b/gap/gap_lib.h
@@ -63,6 +63,7 @@ char*        gap_lib_alloc_basename(const char *imagename, long *number);
 char*        gap_lib_alloc_extension(const char *imagename);
 GapAnimInfo* gap_lib_alloc_ainfo_from_name(const char *imagename, GimpRunMode run_mode);
 GapAnimInfo* gap_lib_alloc_ainfo(gint32 image_id, GimpRunMode run_mode);
+GapAnimInfo* gap_lib_alloc_ainfo_unsaved_image(gint32 image_id);
 int          gap_lib_dir_ainfo(GapAnimInfo *ainfo_ptr);
 int          gap_lib_chk_framerange(GapAnimInfo *ainfo_ptr);
 int          gap_lib_chk_framechange(GapAnimInfo *ainfo_ptr);
diff --git a/gap/gap_mov_dialog.c b/gap/gap_mov_dialog.c
old mode 100644
new mode 100755
index d674249..5deed7b
--- a/gap/gap_mov_dialog.c
+++ b/gap/gap_mov_dialog.c
@@ -296,13 +296,55 @@ typedef struct
   GtkWidget            *segLengthLabel;
   GtkWidget            *segSpeedLabel;
 
+  /* The movepath dialog has a RecordOnlyMode that is intended
+   * to edit and save parameter settings only.
+   *
+   * TRUE
+   *   (typically called from the storyboard for update xml file settings)
+   * - invoke from any image is tolerated
+   * - rendering of frames is DISABLED 
+   *   (Animated preview rendering is allowed)
+   * - frame range limits are 1 upto 999999.
+   * - the moving object (source image) is fixed by the caller,
+   *   stepmode is restricted to GAP_STEP_NONE
+   * - OK button triggers automatic save of the settings
+   *   to XML file in overwrite mode without confirm question
+   *   (but does not render anything)
+   *
+   * FALSE
+   *   (typically called as PDB procedure for productive render purpose)
+   * - invoke is limited to an image that represents a frame
+   *   (image with gap typical number part in the filename)
+   * - rendering of frames is enabled
+   * - frame range limits constraint to first and last frame number
+   *   found in frame imagefilenames on disc.
+   * - the moving object (source image) can be selected by the user
+   *   from the list of opened images in the current gimp session:
+   *   all stepmodes are available.
+   *
+   */
+  gboolean isRecordOnlyMode;
+  gboolean isStandaloneGui;
+  
+  t_close_movepath_edit_callback_fptr close_fptr;
+  gpointer callback_data;
+  
+  gchar        xml_paramfile[GAP_MOVPATH_XML_FILENAME_MAX_LENGTH];
+
 } t_mov_gui_stuff;
 
 
-/* Declare a local function.
+/* Declare local functions.
  */
-
-       long        gap_mov_dlg_move_dialog             (GapMovData *mov_ptr);
+static long        p_gap_mov_dlg_move_dialog(t_mov_gui_stuff *mgp
+                       , GapMovData *mov_ptr
+                       , gboolean isRecordOnlyMode
+                       , gboolean isStandaloneGui
+                       , t_close_movepath_edit_callback_fptr close_fptr
+                       , gpointer callback_data
+                       , gint32 nframes
+                       );
+static void        p_free_mgp_resources(t_mov_gui_stuff *mgp);
 static void        p_update_point_index_text (t_mov_gui_stuff *mgp);
 static void        p_set_sensitivity_by_adjustment(GtkAdjustment *adj, gboolean sensitive);
 static void        p_accel_widget_sensitivity(t_mov_gui_stuff *mgp);
@@ -479,31 +521,207 @@ static t_mov_interface mov_int =
 
 
 /* ============================================================================
- **********************
- *                    *
- *  Dialog interface  *
- *                    *
- **********************
+ ***********************
+ *                     *
+ *  Dialog interfaces  *
+ *                     *
+ ***********************
  * ============================================================================
  */
+long
+gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
+{
+  t_mov_gui_stuff *mgp;
+  gboolean isRecordOnlyMode;
+  gboolean isStandaloneGui;
+
+
+  mgp = g_new( t_mov_gui_stuff, 1 );
+  mgp->shell = NULL;
+  mgp->pointfile_name = NULL;
+  
+  isRecordOnlyMode = FALSE;
+  isStandaloneGui = TRUE;
+  p_gap_mov_dlg_move_dialog(mgp, mov_ptr, isRecordOnlyMode, isStandaloneGui, NULL, NULL, 1);
+  p_free_mgp_resources(mgp);
+  g_free(mgp);
+
+  
+  if(mov_int.run == TRUE)
+  {
+    return 0;  /* OK */
+  }
+  return  -1;  /* Cancel or error occured */
+ 
+}
+
 
-long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
+/* ----------------------------------
+ * gap_mov_dlg_edit_movepath_dialog
+ * ----------------------------------
+ * This procedure creates and shows the movepath edit dialog as widget and returns
+ * the created widget.
+ * The caller shall provide close_fptr and callback_data. This function callback
+ * will be called on close of the widget.
+ * Note that resources refered by pvals, ainfo_ptr xml_paramfile
+ * must not be freed until the edit dialog is closed.
+ *
+ * This procedure is typically called be the Storyboard transition attributes dialog
+ */
+GtkWidget * 
+gap_mov_dlg_edit_movepath_dialog (gint32 frame_image_id, gint32 drawable_id
+   , const char *xml_paramfile
+   , GapAnimInfo *ainfo_ptr
+   , GapMovValues *pvals_edit
+   , t_close_movepath_edit_callback_fptr close_fptr
+   , gpointer callback_data
+   , gint32 nframes
+   )
 {
-  GimpDrawable *l_drawable_ptr;
-  gint       l_first, l_last;
-  char      *l_str;
   t_mov_gui_stuff *mgp;
+  gboolean isRecordOnlyMode;
+  gboolean isStandaloneGui;
+  GapMovData  l_mov_data;
+  gboolean isXmlLoadOk;
+  gboolean l_rc;
+
+  if(gap_debug)
+  {
+     printf("gap_mov_dlg_edit_movepath_dialog START frame_image_id:%d drawable_id:%d xml:%s\n"
+           " fptr:%d data:%d\n"
+      ,(int)frame_image_id
+      ,(int)drawable_id
+      ,xml_paramfile
+      ,(int)close_fptr
+      ,(int)callback_data
+      );
+  }
+
+  isRecordOnlyMode = TRUE;
+  isStandaloneGui = FALSE;
+
 
   mgp = g_new( t_mov_gui_stuff, 1 );
+  mgp->shell = NULL;
+  mgp->pointfile_name = NULL;
+
+
+  pvals = pvals_edit;
+  
+  pvals->dst_image_id = frame_image_id;
+  pvals->bbp_pv = gap_bluebox_bbp_new(-1);
+
+  isXmlLoadOk = FALSE;
+  mgp->xml_paramfile[0] = '\0';
+  if(xml_paramfile[0] != '\0')
+  {
+    g_snprintf(mgp->xml_paramfile, sizeof(mgp->xml_paramfile), "%s", xml_paramfile);
+    mgp->pointfile_name  = g_strdup_printf("%s", xml_paramfile);
+ 
+    /* attempt to init settings in case the xml_paramfile
+     * already contains valid settings
+     */
+    isXmlLoadOk =  gap_mov_xml_par_load(xml_paramfile
+                                  , pvals
+                                  , gimp_image_width(frame_image_id)
+                                  , gimp_image_height(frame_image_id)
+                                  );
+  }
+
+  pvals->src_image_id = gimp_drawable_get_image(drawable_id);
+  pvals->src_layer_id = drawable_id;
+
+  if(isXmlLoadOk != TRUE)
+  {
+    pvals->src_handle = GAP_HANDLE_LEFT_TOP;
+    pvals->src_selmode = GAP_MOV_SEL_IGNORE;
+    pvals->src_paintmode = GIMP_NORMAL_MODE;
+    pvals->src_force_visible  = 1;
+    pvals->src_apply_bluebox  = 0;
+    pvals->bbp  = NULL;
+    pvals->bbp_pv  = NULL;
+    pvals->clip_to_img  = 0;
+    
+    pvals->step_speed_factor = 1.0;
+    pvals->tracelayer_enable = FALSE;
+    pvals->trace_opacity_initial = 100.0;
+    pvals->trace_opacity_desc = 80.0;
+    pvals->tween_steps = 0;
+    pvals->tween_opacity_initial = 80.0;
+    pvals->tween_opacity_desc = 80.0;
+
+    p_reset_points();
+  }
+
+
+  l_rc = FALSE;
+  if(ainfo_ptr != NULL)
+  {
+    l_mov_data.val_ptr = pvals_edit;
+    l_mov_data.singleFramePtr = NULL;
+    l_mov_data.val_ptr->cache_src_image_id = -1;
+    l_mov_data.dst_ainfo_ptr = ainfo_ptr;
+
+    p_gap_mov_dlg_move_dialog(mgp, &l_mov_data
+       , isRecordOnlyMode, isStandaloneGui
+       , close_fptr, callback_data
+       , nframes);
+  }
+
+  if(mgp->shell != NULL)
+  {
+    gtk_window_present(GTK_WINDOW(mgp->shell));
+  }
+
   if(gap_debug)
   {
-    printf("gap_mov_dlg_move_dialog START mgp:%d\n", (int)mgp);
+    printf("gap_mov_dlg_edit_movepath_dialog DONE\n");
+  }
+
+  return(mgp->shell);
+
+}  /* end gap_mov_dlg_edit_movepath_dialog */
+
+
+/* ------------------------------------------
+ * p_gap_mov_dlg_move_dialog
+ * ------------------------------------------
+ *
+ */
+static long
+p_gap_mov_dlg_move_dialog(t_mov_gui_stuff *mgp
+   , GapMovData *mov_ptr
+   , gboolean isRecordOnlyMode
+   , gboolean isStandaloneGui
+   , t_close_movepath_edit_callback_fptr close_fptr
+   , gpointer callback_data
+   , gint32 nframes
+   )
+{
+  GimpDrawable *l_drawable_ptr;
+  gint       l_first;
+  gint       l_last;
+  gint       l_max;
+  gint       l_curr;
+  char      *l_str;
+
+  if(gap_debug)
+  {
+    printf("gap_mov_dlg_move_dialog START mgp:%d isRecordOnlyMode:%d isStandaloneGui:%d\n"
+      , (int)mgp
+      , (int)isRecordOnlyMode
+      , (int)isStandaloneGui
+      );
   }
   if(mgp == NULL)
   {
     printf("error can't alloc path_preview structure\n");
     return -1;
   }
+  mgp->close_fptr = close_fptr;
+  mgp->callback_data = callback_data;
+  mgp->isRecordOnlyMode = isRecordOnlyMode;
+  mgp->isStandaloneGui = isStandaloneGui;
   mgp->shell_initial_width = -1;
   mgp->shell_initial_height = -1;
   mgp->show_path = TRUE;
@@ -544,13 +762,47 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
 
   pvals = mov_ptr->val_ptr;
 
-  l_str = gap_base_strdup_del_underscore(mov_ptr->dst_ainfo_ptr->basename);
-  mgp->pointfile_name  = g_strdup_printf("%s.path_points", l_str);
-  g_free(l_str);
+  
+  if(mgp->pointfile_name == NULL)
+  {
+    if(mov_ptr->dst_ainfo_ptr->basename != NULL)
+    {
+      l_str = gap_base_strdup_del_underscore(mov_ptr->dst_ainfo_ptr->basename);
+      mgp->pointfile_name  = g_strdup_printf("%s.movepath.xml", l_str);
+      g_free(l_str);
+    }
+    else
+    {
+      mgp->pointfile_name = g_strdup("movepath.xml");
+    }
+  }
 
+  if(mgp->isRecordOnlyMode)
+  {
+    l_first = 1;
+    l_max  = 9999;
+    l_last = nframes;
+    l_curr  = 1;
 
-  l_first = mov_ptr->dst_ainfo_ptr->first_frame_nr;
-  l_last  = mov_ptr->dst_ainfo_ptr->last_frame_nr;
+    mov_ptr->dst_ainfo_ptr->first_frame_nr = 1;
+    mov_ptr->dst_ainfo_ptr->last_frame_nr = nframes;
+    mov_ptr->dst_ainfo_ptr->curr_frame_nr = 1;
+
+    pvals->src_stepmode = GAP_STEP_NONE;
+    pvals->src_selmode = GAP_MOV_SEL_IGNORE;
+  }
+  else
+  {
+    l_first = mov_ptr->dst_ainfo_ptr->first_frame_nr;
+    l_last  = mov_ptr->dst_ainfo_ptr->last_frame_nr;
+    l_curr  = mov_ptr->dst_ainfo_ptr->curr_frame_nr;
+    l_max   = l_last;
+    
+    pvals->src_image_id = -1;
+    pvals->src_layer_id = -1;
+    pvals->src_stepmode = GAP_STEP_LOOP;
+ 
+  }
 
   /* init parameter values */
   pvals->dst_image_id = mov_ptr->dst_ainfo_ptr->image_id;
@@ -561,25 +813,6 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   pvals->tmp_alt_framenr = -1;
   pvals->tween_image_id = -1;
   pvals->trace_image_id = -1;
-  pvals->src_image_id = -1;
-  pvals->src_layer_id = -1;
-  pvals->src_paintmode = GIMP_NORMAL_MODE;
-  pvals->src_handle = GAP_HANDLE_LEFT_TOP;
-  pvals->src_stepmode = GAP_STEP_LOOP;
-  pvals->src_selmode = GAP_MOV_SEL_IGNORE;
-  pvals->src_force_visible  = 1;
-  pvals->src_apply_bluebox  = 0;
-  pvals->bbp  = NULL;
-  pvals->bbp_pv  = NULL;
-  pvals->clip_to_img  = 0;
-
-  pvals->step_speed_factor = 1.0;
-  pvals->tracelayer_enable = FALSE;
-  pvals->trace_opacity_initial = 100.0;
-  pvals->trace_opacity_desc = 80.0;
-  pvals->tween_steps = 0;
-  pvals->tween_opacity_initial = 80.0;
-  pvals->tween_opacity_desc = 80.0;
 
   pvals->apv_mode  = GAP_APV_QUICK;
   pvals->apv_src_frame  = -1;
@@ -594,17 +827,37 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   pvals->cache_frame_number  = -1;
   pvals->cache_ainfo_ptr = NULL;
 
-  p_reset_points();
+  if(mgp->isRecordOnlyMode != TRUE)
+  {
+    pvals->src_handle = GAP_HANDLE_LEFT_TOP;
+    pvals->src_selmode = GAP_MOV_SEL_IGNORE;
+    pvals->src_paintmode = GIMP_NORMAL_MODE;
+    pvals->src_force_visible  = 1;
+    pvals->src_apply_bluebox  = 0;
+    pvals->bbp  = NULL;
+    pvals->bbp_pv  = NULL;
+    pvals->clip_to_img  = 0;
+    
+    pvals->step_speed_factor = 1.0;
+    pvals->tracelayer_enable = FALSE;
+    pvals->trace_opacity_initial = 100.0;
+    pvals->trace_opacity_desc = 80.0;
+    pvals->tween_steps = 0;
+    pvals->tween_opacity_initial = 80.0;
+    pvals->tween_opacity_desc = 80.0;
+
+    p_reset_points();
+  }
 
   /* pvals->point[1].p_x = 100; */  /* default: move from 0/0 to 100/0 */
 
-  pvals->dst_range_start = mov_ptr->dst_ainfo_ptr->curr_frame_nr;
+  pvals->dst_range_start = l_curr;
   pvals->dst_range_end   = l_last;
   pvals->dst_layerstack = 0;   /* 0 ... insert layer on top of stack */
 
   mgp->filesel = NULL;   /* fileselector is not open */
   mgp->ainfo_ptr            = mov_ptr->dst_ainfo_ptr;
-  mgp->preview_frame_nr     = mov_ptr->dst_ainfo_ptr->curr_frame_nr;
+  mgp->preview_frame_nr     = l_curr;
   mgp->old_preview_frame_nr = mgp->preview_frame_nr;
   mgp->point_index_frame = NULL;
 
@@ -625,14 +878,44 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   l_drawable_ptr = p_get_prevw_drawable(mgp);
 
   /* do DIALOG window */
-  mov_dialog(l_drawable_ptr, mgp, l_first, l_last);
-  p_points_to_tab(mgp);
+  mov_dialog(l_drawable_ptr, mgp, l_first, l_max);
+
+
+  if(gap_debug)
+  {
+    printf("GAP-DEBUG: END gap_mov_dlg_move_dialog\n");
+  }
 
+}  /* end p_gap_mov_dlg_move_dialog */
+
+
+/* --------------------------------
+ * p_free_mgp_resources
+ * --------------------------------
+ */
+static void
+p_free_mgp_resources(t_mov_gui_stuff *mgp)
+{
+  if(mgp == NULL)
+  {
+    return;
+  }
+  
+  if(gap_debug)
+  {
+    printf("p_free_mgp_resources START\n");
+  }
+  
   /* destroy the tmp image(s) */
-  gimp_image_delete(pvals->tmp_image_id);
+  if(pvals->tmp_image_id >= 0)
+  {
+    gimp_image_delete(pvals->tmp_image_id);
+    pvals->tmp_image_id = -1;
+  }
   if(pvals->tmp_alt_image_id >= 0)
   {
     gimp_image_delete(pvals->tmp_alt_image_id);
+    pvals->tmp_alt_image_id = -1;
   }
 
   /* delete the temp selection image */
@@ -644,17 +927,16 @@ long      gap_mov_dlg_move_dialog    (GapMovData *mov_ptr)
   pvals->tmpsel_image_id = -1;
   pvals->tmpsel_channel_id = -1;
 
+  if(mgp->pointfile_name != NULL)
+  {
+    g_free(mgp->pointfile_name);
+    mgp->pointfile_name = NULL;
+  }
+
   /* remove timer if there is one */
   mov_remove_timer(mgp);
 
-  g_free(mgp);
-
-
-  if(gap_debug) printf("GAP-DEBUG: END gap_mov_dlg_move_dialog\n");
-
-  if(mov_int.run == TRUE)  return 0;
-  else                     return  -1;
-}
+}  /* end p_free_mgp_resources */
 
 
 /* ============================================================================
@@ -685,8 +967,11 @@ mov_dialog ( GimpDrawable *drawable, t_mov_gui_stuff *mgp,
 
   if(gap_debug) printf("GAP-DEBUG: START mov_dialog\n");
 
-  gimp_ui_init ("gap_move", FALSE);
-  gap_stock_init();
+  if(mgp->isStandaloneGui)
+  {
+    gimp_ui_init ("gap_move", FALSE);
+    gap_stock_init();
+  }
 
 #ifdef MOVE_PATH_LAYOUT_BIG_PREVIEW
   vertical_layout = TRUE;
@@ -700,7 +985,14 @@ mov_dialog ( GimpDrawable *drawable, t_mov_gui_stuff *mgp,
   mgp->first_nr = first_nr;
   mgp->last_nr = last_nr;
 
-  gtk_window_set_title (GTK_WINDOW (dlg), _("Move Path"));
+  if(mgp->isRecordOnlyMode)
+  {
+    gtk_window_set_title (GTK_WINDOW (dlg), _("Move Path Editor"));
+  }
+  else
+  {
+    gtk_window_set_title (GTK_WINDOW (dlg), _("Move Path"));
+  }
   gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
   g_signal_connect (G_OBJECT (dlg), "destroy",
                     G_CALLBACK (mov_close_callback),
@@ -861,9 +1153,12 @@ mov_dialog ( GimpDrawable *drawable, t_mov_gui_stuff *mgp,
                     G_CALLBACK (mov_shell_window_size_allocate),
                     mgp);
 
-  gtk_main ();
-  gdk_flush ();
-
+  if(mgp->isStandaloneGui)
+  {
+    gtk_main ();
+    gdk_flush ();
+  }
+  
   if(gap_debug) printf("GAP-DEBUG: END mov_dialog\n");
 
   return mov_int.run;
@@ -905,8 +1200,26 @@ mov_close_callback (GtkWidget *widget,
 {
   if(mgp)
   {
+    if(mgp->shell)
+    {
+      p_points_to_tab(mgp);
+      if(mgp->close_fptr != NULL)
+      {
+
+        if(gap_debug)
+        {
+          printf("calling close_fptr close notification\n");
+        }
+        /* run the callback procedure (that was provided by the caller)
+         * to notify the caller about closing of the movepath dialog
+         */
+        (*mgp->close_fptr)(mgp->callback_data);
+      }
+    }
+
     mov_remove_timer(mgp);
 
+
     if(mgp->shell)
     {
       GtkWidget *l_shell;
@@ -920,11 +1233,29 @@ mov_close_callback (GtkWidget *widget,
         * (for this reason the mgp->shell is set to NULL
         *  before the gtk_widget_destroy call)
         */
+      gtk_widget_hide (l_shell);
       gtk_widget_destroy (l_shell);
+      p_free_mgp_resources(mgp);
+    }
+    
+    if(mgp->isStandaloneGui)
+    {
+      if(gap_debug)
+      {
+        printf("mov_close_callback isStandaloneGui == TRUE, calling gtk_main_quit\n");
+      }
+      gtk_main_quit ();
     }
   }
-
-  gtk_main_quit ();
+  else
+  {
+    if(gap_debug)
+    {
+      printf("mov_close_callback mgp is NULL, calling gtk_main_quit\n");
+    }
+    gtk_main_quit ();
+  }
+  
 }  /* end mov_close_callback */
 
 
@@ -932,8 +1263,30 @@ static void
 mov_ok_callback (GtkWidget *widget,
                  t_mov_gui_stuff *mgp)
 {
+
   if(pvals != NULL)
   {
+    if(mgp != NULL)
+    {
+      if(mgp->isRecordOnlyMode == TRUE)
+      {
+        if(mgp->xml_paramfile[0] != '\0')
+        {
+          gint l_rc;
+          if(gap_debug)
+          {
+            printf("Saving xml_paramfile:%s\n", mgp->xml_paramfile);
+          }
+          l_rc = gap_mov_xml_par_save(mgp->xml_paramfile, pvals);
+          if(l_rc != 0)
+          {
+            printf("** Error failed to save xml_paramfile:%s\n", mgp->xml_paramfile);
+          }
+        }
+      }
+    }
+
+
     if(!mov_check_valid_src_layer(mgp))
     {
       return;
@@ -947,6 +1300,7 @@ mov_ok_callback (GtkWidget *widget,
 
   mov_int.run = TRUE;
 
+
   if(pvals->point_idx_max == 0)
   {
     /* if we have only one point duplicate that point
@@ -973,23 +1327,51 @@ mov_upvw_callback (GtkWidget *widget,
   gint32              l_new_tmp_image_id;
   gint32              l_old_tmp_image_id;
 
-  if(gap_debug) printf("mov_upvw_callback nr: %d old_nr: %d\n",
-         (int)mgp->preview_frame_nr , (int)mgp->old_preview_frame_nr);
-
-  l_frame_nr = (long)mgp->preview_frame_nr;
-  l_filename = gap_lib_alloc_fname(mgp->ainfo_ptr->basename,
+  if(gap_debug)
+  {
+    printf("mov_upvw_callback nr: %d old_nr: %d\n"
+         , (int)mgp->preview_frame_nr
+         , (int)mgp->old_preview_frame_nr
+         );
+  }
+  l_filename = NULL;
+  
+  if(mgp->ainfo_ptr->ainfo_type == GAP_AINFO_FRAMES)
+  {
+    if(gap_debug)
+    {
+      printf("mov_upvw_callback ainfo_type == GAP_AINFO_FRAMES nr: %d old_nr: %d\n"
+           , (int)mgp->preview_frame_nr
+           , (int)mgp->old_preview_frame_nr
+           );
+    }
+    l_frame_nr = (long)mgp->preview_frame_nr;
+    l_filename = gap_lib_alloc_fname(mgp->ainfo_ptr->basename,
                              l_frame_nr,
                              mgp->ainfo_ptr->extension);
+  }
+  else
+  {
+    if(gap_debug)
+    {
+      printf("mov_upvw_callback ainfo_type != GAP_AINFO_FRAMES using image_id %d\n"
+           , (int)mgp->ainfo_ptr->image_id
+           );
+    }
+  }
+  
+  
+  if(!mgp->instant_apply)
+  {
+     /* dont show waiting cursor at instant_apply
+      * (cursor flickering is boring on fast machines,
+      *  and users with slow machines should not touch instant_apply at all)
+      */
+     mov_set_waiting_cursor(mgp);
+  }
+
   if(l_filename != NULL)
   {
-     if(!mgp->instant_apply)
-     {
-       /* dont show waiting cursor at instant_apply
-        * (cursor flickering is boring on fast machines,
-        *  and users with slow machines should not touch instant_apply at all)
-        */
-       mov_set_waiting_cursor(mgp);
-     }
      /* replace the temporary image */
      if(mgp->preview_frame_nr  == mgp->ainfo_ptr->curr_frame_nr)
      {
@@ -1022,34 +1404,47 @@ mov_upvw_callback (GtkWidget *widget,
      gimp_image_undo_disable(l_new_tmp_image_id);
 
      g_free(l_filename);
-     if (l_new_tmp_image_id >= 0)
-     {
-        /* use the new loaded temporary image */
-        l_old_tmp_image_id  = pvals->tmp_image_id;
-        pvals->tmp_image_id = l_new_tmp_image_id;
+  }
+  else
+  {
+    /* in case move path dialog was not called from a frame image
+     * always show up the image from where the dialog was invoked from
+     * as frame (and ignore the preview_frame_nr setting)
+     */
+    l_new_tmp_image_id = gimp_image_duplicate(mgp->ainfo_ptr->image_id);
+  }
+     
+     
+     
+  if (l_new_tmp_image_id >= 0)
+  {
+     /* use the new loaded temporary image */
+     l_old_tmp_image_id  = pvals->tmp_image_id;
+     pvals->tmp_image_id = l_new_tmp_image_id;
 
-        /* flatten image, and get the (only) resulting drawable */
-        mgp->drawable = p_get_prevw_drawable(mgp);
+     /* flatten image, and get the (only) resulting drawable */
+     mgp->drawable = p_get_prevw_drawable(mgp);
 
-        /* gimp_display_new(pvals->tmp_image_id); */ /* add a display for debugging only */
+     /* gimp_display_new(pvals->tmp_image_id); */ /* add a display for debugging only */
 
-        /* re initialize preview image */
-        mov_path_prevw_preview_init(mgp);
-        p_point_refresh(mgp);
+     /* re initialize preview image */
+     mov_path_prevw_preview_init(mgp);
+     p_point_refresh(mgp);
 
-        mgp->old_preview_frame_nr = mgp->preview_frame_nr;
+     mgp->old_preview_frame_nr = mgp->preview_frame_nr;
 
-        gtk_widget_queue_draw(mgp->pv_ptr->da_widget);
-        mov_path_prevw_draw ( mgp, CURSOR | PATH_LINE );
-        gdk_flush();
+     gtk_widget_queue_draw(mgp->pv_ptr->da_widget);
+     mov_path_prevw_draw ( mgp, CURSOR | PATH_LINE );
+     gdk_flush();
 
-        /* destroy the old tmp image */
-        gimp_image_delete(l_old_tmp_image_id);
+     /* destroy the old tmp image */
+     gimp_image_delete(l_old_tmp_image_id);
 
-        mgp->instant_apply_request = FALSE;
-     }
-     mov_set_active_cursor(mgp);
+     mgp->instant_apply_request = FALSE;
   }
+
+  mov_set_active_cursor(mgp);
+
 }  /* end mov_upvw_callback */
 
 
@@ -2201,7 +2596,10 @@ p_points_load_from_file (GtkWidget *widget,
 {
   const gchar        *filename;
 
-  if(gap_debug) printf("p_points_load_from_file\n");
+  if(gap_debug)
+  {
+    printf("p_points_load_from_file\n");
+  }
   if(mgp->filesel == NULL)
   {
     return;
@@ -2211,7 +2609,10 @@ p_points_load_from_file (GtkWidget *widget,
   g_free(mgp->pointfile_name);
   mgp->pointfile_name = g_strdup(filename);
 
-  if(gap_debug) printf("p_points_load_from_file %s\n", mgp->pointfile_name);
+  if(gap_debug)
+  {
+    printf("p_points_load_from_file %s\n", mgp->pointfile_name);
+  }
 
   gtk_widget_destroy(GTK_WIDGET(mgp->filesel));
   mgp->filesel = NULL;
@@ -2232,7 +2633,10 @@ p_points_save_to_file (GtkWidget *widget,
 {
   const gchar        *filename;
 
-  if(gap_debug) printf("p_points_save_to_file\n");
+  if(gap_debug)
+  {
+    printf("p_points_save_to_file\n");
+  }
   if(mgp->filesel == NULL)
   {
     return;  /* filesel is already open */
@@ -2242,7 +2646,10 @@ p_points_save_to_file (GtkWidget *widget,
   g_free(mgp->pointfile_name);
   mgp->pointfile_name = g_strdup(filename);
 
-  if(gap_debug) printf("p_points_save_to_file %s\n", mgp->pointfile_name);
+  if(gap_debug)
+  {
+    printf("p_points_save_to_file %s\n", mgp->pointfile_name);
+  }
 
   gtk_widget_destroy(GTK_WIDGET(mgp->filesel));
   mgp->filesel = NULL;
@@ -2694,6 +3101,11 @@ mov_install_timer(t_mov_gui_stuff *mgp)
 static void
 mov_remove_timer(t_mov_gui_stuff *mgp)
 {
+  if(mgp == NULL)
+  {
+    return;
+  }
+  
   if(mgp->instant_timertag >= 0)
   {
     g_source_remove(mgp->instant_timertag);
@@ -2873,7 +3285,22 @@ p_points_from_tab(t_mov_gui_stuff *mgp)
 static void
 p_points_to_tab(t_mov_gui_stuff *mgp)
 {
-  if(gap_debug) printf("p_points_to_tab: idx=%d, rotation=%f\n", (int)pvals->point_idx , (float)mgp->rotation);
+  if(mgp == NULL)
+  {
+    return;
+  }
+  if(pvals == NULL)
+  {
+    return;
+  }
+
+  if(gap_debug)
+  {
+    printf("p_points_to_tab: idx=%d, rotation=%f\n"
+       , (int)pvals->point_idx
+       , (float)mgp->rotation
+       );
+  }
 
   pvals->point[pvals->point_idx].p_x       = mgp->p_x;
   pvals->point[pvals->point_idx].p_y       = mgp->p_y;
@@ -3235,8 +3662,17 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
 
   gtk_widget_show(combo);
   mgp->src_layer_combo = combo;
-  mov_refresh_src_layer_menu(mgp);
-  gtk_widget_show(combo);
+  
+  if(mgp->isRecordOnlyMode)
+  {
+    gtk_widget_hide(label);
+    gtk_widget_hide(combo);
+  }
+  else
+  {
+    mov_refresh_src_layer_menu(mgp);
+    gtk_widget_show(combo);
+  }
 
 
   /* Paintmode combo (menu) */
@@ -3246,7 +3682,7 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
   gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 4, 0);
   gtk_widget_show(label);
 
-   combo = gimp_int_combo_box_new (_("Normal"),         GIMP_NORMAL_MODE,
+  combo = gimp_int_combo_box_new (_("Normal"),         GIMP_NORMAL_MODE,
                                    _("Dissolve"),       GIMP_DISSOLVE_MODE,
                                    _("Behind"),         GIMP_BEHIND_MODE,
                                    _("Multiply"),       GIMP_MULTIPLY_MODE,
@@ -3272,10 +3708,22 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                                    _("Keep Paintmode"), GAP_MOV_KEEP_SRC_PAINTMODE,
                                    NULL);
 
-  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
-                              GIMP_NORMAL_MODE,              /* initial int value */
+  {
+    gint initialValue;
+    initialValue = GIMP_NORMAL_MODE;
+    
+    if(pvals)
+    {
+      initialValue = pvals->src_paintmode;
+    }
+    
+    gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+                              initialValue,
                               G_CALLBACK (mov_paintmode_menu_callback),
                               mgp);
+    
+  }
+
   gtk_table_attach(GTK_TABLE(table), combo, 3, 4, 0, 1,
                    GTK_EXPAND | GTK_FILL, 0, 0, 0);
   gimp_help_set_help_data(combo,
@@ -3323,7 +3771,17 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                     G_CALLBACK (gimp_double_adjustment_update),
                     &pvals->step_speed_factor);
   mgp->step_speed_factor_adj = GTK_ADJUSTMENT(adj);
-
+  
+  if(mgp->isRecordOnlyMode)
+  {
+    GtkWidget *widget;
+    
+    widget = g_object_get_data(G_OBJECT (adj), "label");
+    gtk_widget_hide(widget);
+    widget = g_object_get_data(G_OBJECT (adj), "spinbutton");
+    gtk_widget_hide(widget);
+    
+  }
 
 
   /* Loop Stepmode combo  */
@@ -3341,17 +3799,35 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                                   _("Frame None"),           GAP_STEP_FRAME_NONE,
                                   NULL);
 
-  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+  if(mgp->isRecordOnlyMode)
+  {
+    gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+                              GAP_STEP_NONE,              /* initial int value */
+                              G_CALLBACK (mov_stepmode_menu_callback),
+                              mgp);
+  }
+  else
+  {
+    gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
                               GAP_STEP_LOOP,              /* initial int value */
                               G_CALLBACK (mov_stepmode_menu_callback),
                               mgp);
-
+  }
+  
   gtk_table_attach(GTK_TABLE(sub_table), combo, 0, 1, 0, 1,
                    GTK_EXPAND | GTK_FILL, 0, 0, 0);
   gimp_help_set_help_data(combo,
                        _("How to fetch the next source layer at the next handled frame")
                        , NULL);
-  gtk_widget_show(combo);
+  if(mgp->isRecordOnlyMode)
+  {
+    gtk_widget_hide(label);
+    gtk_widget_hide(combo);
+  }
+  else
+  {
+    gtk_widget_show(combo);
+  }
   mgp->stepmode_combo = combo;
 
 
@@ -3369,11 +3845,30 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
                                   _("Center"),        GAP_HANDLE_CENTER,
                                   NULL);
 
-  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
-                              GAP_HANDLE_LEFT_TOP,              /* initial int value */
+  
+  {
+    gint initialValue;
+
+    initialValue = GAP_HANDLE_LEFT_TOP;
+    if(pvals)
+    {
+      switch(pvals->src_handle)
+      {
+        case GAP_HANDLE_LEFT_TOP:
+        case GAP_HANDLE_LEFT_BOT:
+        case GAP_HANDLE_RIGHT_TOP:
+        case GAP_HANDLE_RIGHT_BOT:
+        case GAP_HANDLE_CENTER:
+          initialValue = pvals->src_handle;
+          break;
+      }
+    }
+    
+    gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+                              initialValue,
                               G_CALLBACK (mov_handmode_menu_callback),
                               mgp);
-
+  }
 
   gtk_table_attach(GTK_TABLE(table), combo, 3, 4, 1, 2,
                    GTK_EXPAND | GTK_FILL, 0, 0, 0);
@@ -3393,7 +3888,7 @@ mov_src_sel_create(t_mov_gui_stuff *mgp)
  * Create set of widgets for the advanced Move Path features
  *   A frame that contains:
  *   in the 1.st row
- *   - 3x spionbutton  for tween_steps, tween_opacity_init, tween_opacity_desc
+ *   - 3x spinbutton  for tween_steps, tween_opacity_init, tween_opacity_desc
  *   in the 2.nd row
  *   - checkbutton  make_tracelayer
  *   - 2x spinbutton   for trace_opacity_initial, trace_opacity_desc
@@ -3902,7 +4397,6 @@ mov_path_framerange_box_create(t_mov_gui_stuff *mgp
 {
   GtkWidget *master_table;
   GtkWidget *table;
-  //GtkObject *adj;
   GtkAdjustment *adj;
   GtkWidget *check_button;
   gint  master_rows;
@@ -5038,6 +5532,28 @@ mov_path_prevw_create ( GimpDrawable *drawable, t_mov_gui_stuff *mgp, gboolean v
                     G_CALLBACK (mov_instant_int_adjustment_update),
                     &mgp->preview_frame_nr);
   mgp->preview_frame_nr_adj = GTK_ADJUSTMENT(adj);
+  
+  if(mgp->ainfo_ptr->ainfo_type != GAP_AINFO_FRAMES)
+  {
+    GtkWidget *widget;
+
+    widget = GTK_WIDGET(g_object_get_data (G_OBJECT (adj), "label"));
+    if(widget)
+    {
+      gtk_widget_hide(widget);
+    }
+    widget = GTK_WIDGET(g_object_get_data (G_OBJECT (adj), "spinbutton"));
+    if(widget)
+    {
+      gtk_widget_hide(widget);
+    }
+    widget = GTK_WIDGET(g_object_get_data (G_OBJECT (adj), "scale"));
+    if(widget)
+    {
+      gtk_widget_hide(widget);
+    }
+    
+  }
 
 
   gtk_table_attach( GTK_TABLE(pv_table), pv_sub_table, 0, 1, 3, 4,
@@ -5825,6 +6341,7 @@ p_get_prevw_drawable (t_mov_gui_stuff *mgp)
   gint      l_nlayers;
 
   l_curr.isSingleFrame = FALSE;
+  l_curr.singleMovObjLayerId = pvals->src_layer_id;
 
   /* check if we have a source layer (to add to the preview) */
   if((pvals->src_layer_id >= 0) && (pvals->src_image_id >= 0))
@@ -5899,7 +6416,6 @@ p_get_prevw_drawable (t_mov_gui_stuff *mgp)
      }
     }
 
-
     /* set offsets (in cur_ptr)
      *  according to handle_mode and src_img dimension (pvals)
      */
diff --git a/gap/gap_mov_dialog.h b/gap/gap_mov_dialog.h
old mode 100644
new mode 100755
index 7b73e2b..23f0577
--- a/gap/gap_mov_dialog.h
+++ b/gap/gap_mov_dialog.h
@@ -298,8 +298,19 @@ typedef struct {
 } GapMovQuery;
 
 
+typedef void (*t_close_movepath_edit_callback_fptr)(gpointer callback_data);
+
 long  gap_mov_dlg_move_dialog (GapMovData *mov_ptr);
 gint  gap_mov_dlg_move_dialog_singleframe(GapMovSingleFrame *singleFramePtr);
 
+GtkWidget * gap_mov_dlg_edit_movepath_dialog (gint32 frame_image_id, gint32 drawable_id
+   , const char *xml_paramfile
+   , GapAnimInfo *ainfo_ptr
+   , GapMovValues *pvals
+   , t_close_movepath_edit_callback_fptr close_fptr
+   , gpointer callback_data
+   , gint32 nframes
+   );
+
 
 #endif
diff --git a/gap/gap_mov_exec.c b/gap/gap_mov_exec.c
old mode 100644
new mode 100755
index 282bb1f..f4de61e
--- a/gap/gap_mov_exec.c
+++ b/gap/gap_mov_exec.c
@@ -2632,6 +2632,16 @@ p_mov_execute(GapMovData *mov_ptr)
  * gap_mov_exec_anim_preview
  *   Generate an animated preview for the move path
  * ============================================================================
+ * the animate preview is rendered as new multilayer image
+ * where each processed frame results in one layer.
+ * processing can be done on empty frame, multiple copies of one frame
+ * or by reading all affected frames as input (from frame images on disc)
+ * Note that animated preview can be rendered at original size
+ * or downscaled to a percentage less than 100%
+ *
+ * In case ainfo_ptr refers to an image that is not part of a sequence
+ * of frameimages (stored on disc) a copy of the invoke image (ainfo_ptr->image_id)
+ * will be used as input instead of reading frame images from disc.
  */
 gint32
 gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint preview_frame_nr)
@@ -2774,23 +2784,56 @@ gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint
            , (int) l_pvals->apv_mlayer_image);
   }
 
-  /* APV_MODE (Wich frames to use in the preview?)  */
-  switch(l_pvals->apv_mode)
+  l_tmp_frame_id = -1;
+
   {
     gchar *l_filename;
+    gboolean useOneFrame;
 
-    case GAP_APV_QUICK:
-      /* use an empty dummy frame for all frames */
-      l_tmp_frame_id = gimp_image_new(l_width, l_height,l_type);
-      gimp_image_set_resolution(l_tmp_frame_id, l_xresoulution, l_yresoulution);
-      gimp_image_undo_disable (l_tmp_frame_id);
-      break;
-    case GAP_APV_ONE_FRAME:
-      /* use only one frame in the preview */
-      l_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
+    useOneFrame = FALSE;
+    l_filename = gap_lib_alloc_fname(ainfo_ptr->basename,
                                  preview_frame_nr,
                                  ainfo_ptr->extension);
-      l_tmp_frame_id =  gap_lib_load_image(l_filename);
+
+    /* APV_MODE (Wich frames to use in the preview?)  */
+    switch(l_pvals->apv_mode)
+    {
+      case GAP_APV_QUICK:
+        /* use an empty dummy frame for all frames */
+        l_tmp_frame_id = gimp_image_new(l_width, l_height,l_type);
+        gimp_image_set_resolution(l_tmp_frame_id, l_xresoulution, l_yresoulution);
+        gimp_image_undo_disable (l_tmp_frame_id);
+        break;
+      case GAP_APV_ONE_FRAME:
+        /* use only one frame in the preview */
+        useOneFrame = TRUE;
+        break;
+      default:  /* GAP_APV_EXACT */
+        /* read the original frames for the preview (slow) */
+        l_tmp_frame_id = -1;
+        if(l_filename == NULL)
+        {
+          /* the frame for preview_frame_nr resulted in NULL filename
+           * in this case we assume that there will be no valid frames available
+           * and use only one frame (as copy of the invoker image)
+           * for rendering the animated preview
+           */
+          useOneFrame = TRUE;
+        }
+        break;
+    }
+    
+    if(useOneFrame)
+    {
+      if((l_filename != NULL)
+      && (preview_frame_nr  != ainfo_ptr->curr_frame_nr))
+      {
+        l_tmp_frame_id =  gap_lib_load_image(l_filename);
+      }
+      if(l_tmp_frame_id < 0)
+      {
+        l_tmp_frame_id = gimp_image_duplicate(ainfo_ptr->image_id);
+      }
       gimp_image_undo_disable (l_tmp_frame_id);
       if((l_pvals->apv_scalex != 100.0) || (l_pvals->apv_scaley != 100.0))
       {
@@ -2798,13 +2841,15 @@ gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint
         l_size_y = (gimp_image_height(l_tmp_frame_id) * l_pvals->apv_scaley) / 100;
         gimp_image_scale(l_tmp_frame_id, l_size_x, l_size_y);
       }
+    }
+    
+    
+    if(l_filename != NULL)
+    {
       g_free(l_filename);
-      break;
-    default:  /* GAP_APV_EXACT */
-      /* read the original frames for the preview (slow) */
-      l_tmp_frame_id = -1;
-      break;
+    }
   }
+
   l_pvals->apv_src_frame = l_tmp_frame_id;
 
   if(gap_debug)
@@ -2854,11 +2899,11 @@ gap_mov_exec_anim_preview(GapMovValues *pvals_orig, GapAnimInfo *ainfo_ptr, gint
   return(l_mlayer_image_id);
 }       /* end gap_mov_exec_anim_preview */
 
+
 /* ============================================================================
- * p_con_keyframe
+ * p_conv_keyframe
  * ============================================================================
  */
-
 gint
 gap_mov_exec_conv_keyframe_to_rel(gint abs_keyframe, GapMovValues *pvals)
 {
@@ -3928,12 +3973,6 @@ gap_mov_exec_move_path_singleframe(GimpRunMode run_mode, gint32 image_id
              printf("Execution Error: could not load MovePath settings from file: %s\n",
                      singleFramePtr->xml_paramfile);
            }
-           
-           //if(gap_debug)
-           //{
-           //  gap_mov_xml_par_save("pvals_after_load.xml", pvals);
-           //}
-
          }
 
       }
diff --git a/gap/gap_story_att_trans_dlg.c b/gap/gap_story_att_trans_dlg.c
old mode 100644
new mode 100755
index fdb9990..3816484
--- a/gap/gap_story_att_trans_dlg.c
+++ b/gap/gap_story_att_trans_dlg.c
@@ -52,6 +52,8 @@
 #include "gap_timeconv.h"
 #include "gap_layer_copy.h"
 #include "gap_accel_da.h"
+#include "gap_mov_dialog.h"
+#include "gap_mov_exec.h"
 
 
 #include "gap-intl.h"
@@ -223,6 +225,11 @@ static void     p_attw_movepath_filesel_pw_close_cb ( GtkWidget *widget
                       , GapStbAttrWidget *attw);
 static void     p_attw_movepath_filesel_button_cb ( GtkWidget *w
                        , GapStbAttrWidget *attw);
+
+static void     p_create_movepath_edit_resources(GapStbAttrWidget *attw);
+static void     p_edit_movepath_closed_callback(gpointer ptr);
+static void     p_attw_movepath_edit_button_cb ( GtkWidget *w
+                       , GapStbAttrWidget *attw);
 static void     p_attw_movepath_file_validity_check(GapStbAttrWidget *attw);
 static void     p_attw_movepath_file_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw);
 static void     p_attw_comment_entry_update_cb(GtkWidget *widget, GapStbAttrWidget *attw);
@@ -324,6 +331,13 @@ p_attw_prop_response(GtkWidget *widget
           /* force close in case file selection dialog is still open */
           p_attw_movepath_filesel_pw_close_cb(attw->movepath_filesel, attw);
         }
+        
+        if(attw->movepath_edit_dialog != NULL)
+        {
+          /* force close of the movepath edit dialog that is still open */
+          gtk_widget_destroy(attw->movepath_edit_dialog);
+          attw->movepath_edit_dialog = NULL;
+        }
       
         p_delete_gfx_images(attw);
         if(attw->go_timertag >= 0)
@@ -356,9 +370,10 @@ p_attw_push_undo_and_set_unsaved_changes(GapStbAttrWidget *attw)
   {
     if((attw->stb_elem_refptr != NULL) && (attw->stb_refptr != NULL))
     {
-      gap_stb_undo_push_clip(attw->tabw
+      gap_stb_undo_push_clip_with_file_snapshot(attw->tabw
           , GAP_STB_FEATURE_PROPERTIES_TRANSITION
           , attw->stb_elem_refptr->story_id
+          , &attw->stb_elem_refptr->att_movepath_file_xml
           );
 
       attw->stb_refptr->unsaved_changes = TRUE;
@@ -589,6 +604,17 @@ p_attw_update_sensitivity(GapStbAttrWidget *attw)
   sensitive = ((attw->stb_elem_refptr->att_fit_width == TRUE)
             || (attw->stb_elem_refptr->att_fit_height == TRUE));
   gtk_widget_set_sensitive(attw->keep_proportions_toggle, sensitive);
+  
+  sensitive = FALSE;
+  if(attw->stb_elem_refptr->att_movepath_file_xml != NULL)
+  {
+    if(attw->stb_elem_refptr->att_movepath_file_xml[0] != '\0')
+    {
+      sensitive = TRUE;
+    }
+  }
+
+  gtk_widget_set_sensitive(attw->movepath_edit_button, sensitive);
 
 }  /* end p_attw_update_sensitivity */
 
@@ -2758,7 +2784,15 @@ p_attw_movepath_filesel_button_cb ( GtkWidget *w
      gtk_window_present(GTK_WINDOW(attw->movepath_filesel));
      return;   /* filesel is already open */
   }
-  if(attw->stb_elem_refptr == NULL) { return; }
+  if(attw->movepath_edit_dialog != NULL)
+  {
+     gtk_window_present(GTK_WINDOW(attw->movepath_edit_dialog));
+     return;   /* edit dialog is already open */
+  }
+  if(attw->stb_elem_refptr == NULL)
+  {
+    return;
+  }
 
   filesel = gtk_file_selection_new ( _("Set Movepath Parameterfile (XML)"));
   attw->movepath_filesel = filesel;
@@ -2787,6 +2821,204 @@ p_attw_movepath_filesel_button_cb ( GtkWidget *w
 
 /* ==================================================== END MOVEPATH FILESEL stuff ======  */
 
+/* -----------------------------------------
+ * p_create_movepath_edit_resources
+ * -----------------------------------------
+ * create frame_image, moving object image
+ * ainfo and pvals resources that are used
+ * to call the movepath edit dialog.
+ */
+static void
+p_create_movepath_edit_resources(GapStbAttrWidget *attw)
+{
+  gint32   image_id;
+  gint32   bg_layer_id;
+  gint32   origsize_layer_id;
+  
+  /* create the frame image */
+  image_id = gimp_image_new(attw->stb_refptr->master_width
+                           ,attw->stb_refptr->master_height
+                           ,GIMP_RGB
+                           );
+  attw->movepath_frame_image_id = image_id;
+  gimp_image_undo_disable (image_id);
+ 
+  /* add a transparent layer */ 
+  bg_layer_id = gimp_layer_new(image_id
+                  , "background"
+                  , gimp_image_width(image_id)
+                  , gimp_image_height(image_id)
+                  , GIMP_RGBA_IMAGE
+                  , 100.0   /* opacity */
+                  , 0       /* normal mode */
+                  );
+  gimp_image_add_layer (image_id, bg_layer_id, 0);
+  gap_layer_clear_to_color(bg_layer_id, 0.0, 0.0, 0.0, 0.0);
+  gimp_drawable_set_visible(bg_layer_id, TRUE);
+
+  // TODO: in case the storyboard has more tracks
+  // the frame image should be rendered by the storyboard processor at master size
+  // based on a modified storyboard that contains only tracks that render behind the current track 
+
+  
+  /* create an image that holds the moving object layer */
+  origsize_layer_id = attw->gfx_tab[0].orig_layer_id;
+  attw->movepath_obj_image_id = gimp_image_new( gimp_drawable_width(origsize_layer_id)
+                       , gimp_drawable_height(origsize_layer_id)
+                       , GIMP_RGB
+                       );
+  gimp_image_undo_disable (attw->movepath_obj_image_id);
+  attw->movepath_obj_layer_id = gimp_layer_new_from_drawable(origsize_layer_id, attw->movepath_obj_image_id);
+  gimp_image_add_layer (attw->movepath_obj_image_id, attw->movepath_obj_layer_id, 0);
+  gimp_drawable_set_visible(attw->movepath_obj_layer_id, TRUE);
+
+
+  /* create default values for movepath 
+   * (will be overwritten in case xml_paramfile contains already valid settings)
+   */
+  attw->pvals = gap_mov_exec_new_GapMovValues();
+  attw->pvals->dst_image_id = attw->movepath_frame_image_id;
+
+  attw->ainfo_ptr = gap_lib_alloc_ainfo_unsaved_image(attw->movepath_frame_image_id);
+  
+}  /* end p_create_movepath_edit_resources */
+
+
+/* -----------------------------------------
+ * p_edit_movepath_closed_callback
+ * -----------------------------------------
+ * is called on close of the movepath edit dialog
+ */
+static void
+p_edit_movepath_closed_callback(gpointer ptr)
+{
+  GapStbAttrWidget *attw;
+  
+  attw = (GapStbAttrWidget *)ptr;
+  
+  if(attw != NULL)
+  {
+    if(gap_debug)
+    {
+      printf("p_edit_movepath_closed_callback frame_image_id:%d obj_image_id:%d\n"
+        ,(int)attw->movepath_frame_image_id
+        ,(int)attw->movepath_obj_image_id
+	);
+    }
+    attw->movepath_edit_dialog = NULL;
+    
+    if(attw->pvals != NULL)
+    {
+      g_free(attw->pvals);
+      attw->pvals = NULL;
+    }
+    
+    if(attw->ainfo_ptr != NULL)
+    {
+      gap_lib_free_ainfo(&attw->ainfo_ptr);
+      attw->ainfo_ptr = NULL;
+    }
+    
+    if(attw->movepath_frame_image_id >= 0)
+    {
+      gimp_image_delete(attw->movepath_frame_image_id);
+      attw->movepath_frame_image_id = -1;
+    }
+
+    if(attw->movepath_obj_image_id >= 0)
+    {
+      gimp_image_delete(attw->movepath_obj_image_id);
+      attw->movepath_obj_image_id = -1;
+    }
+    p_attw_movepath_file_validity_check(attw);
+    p_update_full_preview_gfx(attw);
+    
+    /* make attributes dialog sensitive again (after movepath edit dialog was closed) */ 
+    gtk_widget_set_sensitive(attw->attw_prop_dialog, TRUE);
+
+  }
+  
+}  /* end p_edit_movepath_closed_callback */
+
+
+
+/* ---------------------------------
+ * p_attw_movepath_edit_button_cb
+ * ---------------------------------
+ * invoke the movepath editor dialog window
+ * (in case it is not yet open
+ * and the file selection dialog is not open)
+ */
+static void
+p_attw_movepath_edit_button_cb ( GtkWidget *w
+                       , GapStbAttrWidget *attw)
+{
+  gint32 nframes;
+  
+  if(attw->attw_prop_dialog == NULL)
+  {
+     return;
+  }
+
+  if(attw->movepath_filesel != NULL)
+  {
+     gtk_window_present(GTK_WINDOW(attw->movepath_filesel));
+     return;   /* filesel is already open */
+  }
+  if(attw->stb_elem_refptr == NULL)
+  {
+    return;
+  }
+
+  if(attw->movepath_edit_dialog != NULL)
+  {
+     gtk_window_present(GTK_WINDOW(attw->movepath_edit_dialog));
+     return;   /* edit dialog is already open */
+  }
+
+  if(attw->stb_elem_refptr->att_movepath_file_xml == NULL)
+  {
+    return;
+  }
+  if(attw->stb_elem_refptr->att_movepath_file_xml[0] == '\0')
+  {
+    return;
+  }
+
+  p_attw_push_undo_and_set_unsaved_changes(attw);
+  p_create_movepath_edit_resources(attw);
+
+  if(gap_debug)
+  {
+      printf("p_attw_movepath_edit_button_cb frame_image_id:%d obj_image_id:%d obj_layer_id:%d attw:%d\n"
+        ,(int)attw->movepath_frame_image_id
+        ,(int)attw->movepath_obj_image_id
+	,(int)attw->movepath_obj_layer_id
+	,(int)attw
+	);
+  }
+
+
+  /* make attributes dialog insensitive (while movepath edit dialog is open) */
+  gtk_widget_set_sensitive(attw->attw_prop_dialog, FALSE);
+
+  nframes = MAX(attw->stb_elem_refptr->att_arr_value_to[GAP_STB_ATT_TYPE_MOVEPATH]
+               ,attw->stb_elem_refptr->att_arr_value_from[GAP_STB_ATT_TYPE_MOVEPATH]);
+
+  attw->movepath_edit_dialog = gap_mov_dlg_edit_movepath_dialog(
+                                   attw->movepath_frame_image_id
+                                 , attw->movepath_obj_layer_id
+                                 , attw->stb_elem_refptr->att_movepath_file_xml
+                                 , attw->ainfo_ptr
+                                 , attw->pvals
+                                 , p_edit_movepath_closed_callback
+                                 ,(gpointer)attw
+                                 , nframes
+                                 );
+
+}  /* end p_attw_movepath_edit_button_cb */
+
+
 /* ------------------------------------
  * p_attw_movepath_file_validity_check
  * ------------------------------------
@@ -3283,6 +3515,12 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
   if(tabw == NULL) { return (NULL); }
 
   attw->movepath_filesel = NULL;
+  attw->movepath_edit_dialog = NULL;
+  attw->ainfo_ptr = NULL;
+  attw->pvals = NULL;
+  attw->movepath_frame_image_id = -1;
+  attw->movepath_obj_image_id = -1;
+  attw->movepath_obj_layer_id = -1;
 
   if(attw->stb_elem_bck)
   {
@@ -3305,6 +3543,7 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
                          ,GTK_STOCK_CLOSE,  GTK_RESPONSE_CLOSE
                          ,NULL);
   }
+  gtk_window_set_type_hint (dlg, GDK_WINDOW_TYPE_HINT_NORMAL);
 
   attw->attw_prop_dialog = dlg;
 
@@ -3724,6 +3963,16 @@ gap_story_attw_properties_dialog (GapStbAttrWidget *attw)
                      G_CALLBACK(p_attw_movepath_filesel_button_cb),
                      attw);
     gtk_widget_show (button);
+
+    
+    /* the movepath record/edit dialog invoker button */
+    button = gtk_button_new_with_label ("edit");
+    attw->movepath_edit_button = button;
+    gtk_table_attach_defaults (GTK_TABLE(table), button, 10, 11, row, row+1);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(p_attw_movepath_edit_button_cb),
+                     attw);
+    gtk_widget_show (button);
     
   }
 
diff --git a/gap/gap_story_dialog.c b/gap/gap_story_dialog.c
index db51cfc..7e68e0c 100644
--- a/gap/gap_story_dialog.c
+++ b/gap/gap_story_dialog.c
@@ -478,8 +478,8 @@ p_is_debug_menu_enabled(void)
 /* ---------------------------------------
  * p_is_debug_feature_item_enabled
  * ---------------------------------------
- * print the list of video elements
- * to stdout (typical used for logging and debug purpose)
+ * return TRUE if the specified debug_item name
+ * is enabled (e.g. is present as text line in the debug configuration file)
  */
 static gboolean
 p_is_debug_feature_item_enabled(const char *debug_item)
@@ -500,10 +500,7 @@ p_is_debug_feature_item_enabled(const char *debug_item)
       FILE *l_fp;
       char         l_buf[400];
 
-      if(gap_debug)
-      {
-        printf("check for item:'%s'\n", debug_item);
-      }
+      printf("debug_item:'%s'", debug_item);
 
       l_fp = g_fopen(filename, "r");
       if(l_fp)
@@ -526,12 +523,21 @@ p_is_debug_feature_item_enabled(const char *debug_item)
         }
 
         fclose(l_fp);
+        if(enable)
+        {
+          printf(" IS ENABLED\n");
+        }
+        else
+        {
+          printf(" is disabled\n");
+        }
       }
 
     }
     g_free(filename);
   }
 
+
   return(enable);
 
 }  /* end p_is_debug_feature_item_enabled */
@@ -2134,7 +2140,13 @@ static void
 p_tabw_process_undo(GapStbTabWidgets *tabw)
 {
   GapStoryBoard *stb;
+  GapStoryBoard *old_stb;
 
+  old_stb = p_tabw_get_stb_ptr(tabw);
+  if (old_stb)
+  {
+    p_tabw_destroy_all_popup_dlg(tabw);
+  }
   stb = gap_stb_undo_pop(tabw);
   if (stb)
   {
@@ -2150,6 +2162,13 @@ static void
 p_tabw_process_redo(GapStbTabWidgets *tabw)
 {
   GapStoryBoard *stb;
+  GapStoryBoard *old_stb;
+
+  old_stb = p_tabw_get_stb_ptr(tabw);
+  if (old_stb)
+  {
+    p_tabw_destroy_all_popup_dlg(tabw);
+  }
 
   stb = gap_stb_undo_redo(tabw);
   if (stb)
@@ -4020,6 +4039,12 @@ p_tabw_destroy_attw_dlg (GapStbTabWidgets *tabw, gboolean destroy_all)
 
         if(attw->attw_prop_dialog)
         {
+          if(attw->movepath_edit_dialog != NULL)
+          {
+            /* force close of the movepath edit dialog that is still open */
+            gtk_widget_destroy(attw->movepath_edit_dialog);
+            attw->movepath_edit_dialog = NULL;
+          }
           gtk_widget_destroy(attw->attw_prop_dialog);
         }
         if(attw_prev)
@@ -5380,8 +5405,8 @@ p_menu_win_debug_log_to_stdout_cb (GtkWidget *widget, GapStbMainGlobalParams *sg
     if (selection_found != TRUE)
     {
        printf("INFO p_menu_win_debug_log_to_stdout_cb:"
-              "The file: %s does not exist or does not contain"
-              "any valid selction what to print for debug purpose.\n"
+              " The file: %s does not exist or does not contain"
+              " any valid selction what to print for debug purpose.\n"
               , GAP_DEBUG_STORYBOARD_CONFIG_FILE);
     }
 
@@ -6104,7 +6129,7 @@ p_make_item_with_image(GtkWidget *parent, const gchar *stock_id,
    GtkWidget *item;
 
    item = gtk_image_menu_item_new_from_stock(stock_id
-                                            , NULL         // accelerator_group
+                                            , NULL /* accelerator_group */
                                             );
 
    gtk_menu_shell_append(GTK_MENU_SHELL(parent), item);
diff --git a/gap/gap_story_main.h b/gap/gap_story_main.h
old mode 100644
new mode 100755
index 48a908a..056ec41
--- a/gap/gap_story_main.h
+++ b/gap/gap_story_main.h
@@ -36,6 +36,7 @@
 #include "gap_story_file.h"
 #include "gap_story_undo_types.h"
 #include "gap_player_main.h"
+#include "gap_mov_dialog.h"
 
 #define GAP_STORY_PLUG_IN_PROC            "plug_in_gap_storyboard_edit"
 #define GAP_STORYBOARD_EDIT_HELP_ID       "plug-in-gap-storyboard-edit"
@@ -321,10 +322,18 @@ typedef struct GapStbAttrWidget  /* nickname: attw */
   GtkWidget  *spinbutton_overlap_dur;
   GtkWidget  *button_overlap_dur;
 
-  GtkWidget  *comment_entry;
-  GtkWidget  *movepath_file_entry;
-  GtkWidget  *movepath_filesel;
-  gboolean    movepath_file_xml_is_valid;
+  GtkWidget   *comment_entry;
+  
+  GtkWidget   *movepath_edit_button;
+  GtkWidget   *movepath_file_entry;
+  GtkWidget   *movepath_filesel;
+  gboolean     movepath_file_xml_is_valid;
+  GtkWidget   *movepath_edit_dialog;
+  GapAnimInfo *ainfo_ptr;
+  GapMovValues *pvals;
+  gint32       movepath_frame_image_id;
+  gint32       movepath_obj_image_id;
+  gint32       movepath_obj_layer_id;
 
   struct GapStbAttrWidget *next;
 } GapStbAttrWidget;
diff --git a/gap/gap_story_undo.c b/gap/gap_story_undo.c
old mode 100644
new mode 100755
index 35a6692..fbadd42
--- a/gap/gap_story_undo.c
+++ b/gap/gap_story_undo.c
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <glib/gstdio.h>
 #include <gtk/gtk.h>
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
@@ -42,6 +43,7 @@
 #include "gap_story_undo.h"
 #include "gap_story_dialog.h"
 #include "gap_story_file.h"
+#include "gap_libgapbase.h"
 
 
 extern int gap_debug;  /* 1 == print debug infos , 0 dont print debug infos */
@@ -75,14 +77,33 @@ gap_stb_undo_debug_print_stack(GapStbTabWidgets *tabw)
 
   for(undo_elem = tabw->undo_stack_list; undo_elem != NULL; undo_elem = undo_elem->next)
   {
-    printf("  %d %s"
+    printf("  addr:%d fPtr:%d %s"
           , (int)undo_elem
+          , (int)undo_elem->filenamePtr
           , gap_stb_undo_feature_to_string(undo_elem->feature_id)
           );
     if(undo_elem == tabw->undo_stack_ptr)
     {
       printf(" <-- stack_ptr");
     }
+    
+    if(undo_elem->fileSnapshotBefore != NULL)
+    {
+      printf(" (BEFORE: fname:%s size:%d mtime:%d)"
+            ,undo_elem->fileSnapshotBefore->filename
+            ,(int)undo_elem->fileSnapshotBefore->filesize
+            ,(int)undo_elem->fileSnapshotBefore->mtimefile
+            );
+    }
+    if(undo_elem->fileSnapshotAfter != NULL)
+    {
+      printf(" (AFTER: fname:%s size:%d mtime:%d)"
+            ,undo_elem->fileSnapshotAfter->filename
+            ,(int)undo_elem->fileSnapshotAfter->filesize
+            ,(int)undo_elem->fileSnapshotAfter->mtimefile
+            );
+    }
+    
     printf("\n");
     fflush(stdout);
   }
@@ -123,6 +144,122 @@ gap_stb_undo_feature_to_string(GapStoryFeatureEnum feature_id)
 }  /* end gap_stb_undo_feature_to_string */
 
 
+/* ---------------------------------------
+ * p_free_file_snapshot
+ * ---------------------------------------
+ */
+static void
+p_free_file_snapshot(GapStoryUndoFileSnapshot *fileSnapshot)
+{
+  if(fileSnapshot == NULL)
+  {
+    return;
+  }
+  if(fileSnapshot->filename)
+  {
+    g_free(fileSnapshot->filename);
+  }
+  if(fileSnapshot->filecontent)
+  {
+    g_free(fileSnapshot->filecontent);
+  }
+  g_free(fileSnapshot);
+
+}  /* end p_free_file_snapshot */
+
+
+/* -----------------------------------------
+ * p_create_file_snapshot
+ * -----------------------------------------
+ */
+static GapStoryUndoFileSnapshot*
+p_create_file_snapshot(char *filename)
+{
+  GapStoryUndoFileSnapshot *fileSnapshot;
+  const char               *filecontent;
+
+  fileSnapshot = NULL;
+  
+  if(g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+  {
+    gint32 filesize;
+    
+    filecontent = gap_file_load_file_len(filename, &filesize);
+  
+    if(filecontent != NULL)
+    {
+      fileSnapshot = g_new(GapStoryUndoFileSnapshot, 1);
+      fileSnapshot->mtimefile   = gap_file_get_mtime(filename);
+      fileSnapshot->filename    = g_strdup(filename);
+      fileSnapshot->filecontent = filecontent;
+      fileSnapshot->filesize    = filesize;
+    }
+  }
+  
+  return (fileSnapshot);
+}  /* end p_create_file_snapshot */
+
+
+
+/* -----------------------------------------
+ * p_replace_file_from_snapshot
+ * -----------------------------------------
+ */
+static void
+p_replace_file_from_snapshot(GapStoryUndoFileSnapshot* fileSnapshot)
+{
+  FILE *fp;
+  
+  if(fileSnapshot == NULL)
+  {
+    return;
+  }
+  if(fileSnapshot->filename == NULL)
+  {
+    return;
+  }
+  
+  if(g_file_test(fileSnapshot->filename, G_FILE_TEST_IS_REGULAR))
+  {
+    if(fileSnapshot->mtimefile == gap_file_get_mtime(fileSnapshot->filename))
+    {
+      /* the file still exists with same mtime stamp
+       * no further action required in this case..
+       */
+      if(gap_debug)
+      {
+        printf("p_replace_file_from_snapshot:%s skipped due to EQUAL mtime:%d\n"
+            , fileSnapshot->filename
+            ,(int)fileSnapshot->mtimefile
+            );
+      }
+      return;
+    }
+  }
+  
+  if(gap_debug)
+  {
+    printf("p_replace_file_from_snapshot:%s\n", fileSnapshot->filename);
+  }
+
+  /* open write binary (create or replace) */
+  fp = g_fopen(fileSnapshot->filename, "wb");
+  if(fp != NULL)
+  {
+    fwrite(fileSnapshot->filecontent,  fileSnapshot->filesize, 1, fp);
+    fclose(fp);
+    /* refresh the modification timestamp after rewrite */
+    fileSnapshot->mtimefile = gap_file_get_mtime(fileSnapshot->filename);
+  }
+  
+
+}  /* end p_replace_file_from_snapshot */
+
+
+
+
+
+
 
 /* ---------------------------------------
  * gap_stb_undo_pop
@@ -188,6 +325,11 @@ gap_stb_undo_pop(GapStbTabWidgets *tabw)
 
   stb = gap_story_duplicate_full(tabw->undo_stack_ptr->stb);
   
+  if(tabw->undo_stack_ptr->fileSnapshotBefore != NULL)
+  {
+    p_replace_file_from_snapshot(tabw->undo_stack_ptr->fileSnapshotBefore);
+  }
+  
   if(gap_debug)
   {
     printf("gap_stb_undo_pop returning feature_id:%d %s grp_count:%.2f next:%d\n"
@@ -218,12 +360,20 @@ gap_stb_undo_pop(GapStbTabWidgets *tabw)
  * feature (that now shall be undone) was applied.
  *
  *           
- * stack_list -->latest                             latest 
- *               EEEE                               EEEE
- *               DDDD                               DDDD <--- stack_ptr after redo
- *               CCCC  <-- stack_ptr before redo    CCCC        returns (EEEE)
- *               BBBB                               BBBB 
- *               AAAA                               AAAA
+ * stack_list -->latest                                  latest 
+ *               EEEE                                    EEEE
+ *               DDDD                                    DDDD <--- stack_ptr after redo (DDDD)
+ *               CCCC  <-- stack_ptr before redo (DDDD)  CCCC        returns (EEEE)
+ *               BBBB                                    BBBB 
+ *               AAAA                                    AAAA
+ * --------------------------------------------------------------------------------
+ *
+ * Example with one feature AAAA on the stack
+ *
+ * stack_ptr is NULL before redo(AAAA)           
+ * stack_list -->latest                                  latest 
+ *               AAAA                                    AAAA <--- stack_ptr after redo  AAAA
+ *                                                                   returns (latest)
  * --------------------------------------------------------------------------------
  */
 GapStoryBoard *
@@ -256,6 +406,14 @@ gap_stb_undo_redo(GapStbTabWidgets *tabw)
   if (redo_elem != NULL)
   {
     stb = gap_story_duplicate_full(redo_elem->stb);
+    if(tabw->undo_stack_ptr != NULL)
+    {
+      if(tabw->undo_stack_ptr->fileSnapshotAfter != NULL)
+      {
+        p_replace_file_from_snapshot(tabw->undo_stack_ptr->fileSnapshotAfter);
+      }
+    }
+
     gap_story_dlg_tabw_undo_redo_sensitivity(tabw);
 
     if(gap_debug)
@@ -289,10 +447,27 @@ p_free_undo_elem(GapStoryUndoElem    *undo_elem)
 
   if(gap_debug)
   {
-    printf("p_free_undo_elem: %d %s\n"
+    printf("p_free_undo_elem: %d %s"
           , (int)undo_elem
           , gap_stb_undo_feature_to_string(undo_elem->feature_id)
           );
+    if(undo_elem->fileSnapshotBefore != NULL)
+    {
+      printf(" fileSnapshotBefore:%s filesize:%d mtime:%d"
+        ,undo_elem->fileSnapshotBefore->filename
+        ,(int)undo_elem->fileSnapshotBefore->filesize
+        ,(int)undo_elem->fileSnapshotBefore->mtimefile
+        );
+    }
+    if(undo_elem->fileSnapshotAfter != NULL)
+    {
+      printf(" fileSnapshotAfter:%s filesize:%d mtime:%d"
+        ,undo_elem->fileSnapshotAfter->filename
+        ,(int)undo_elem->fileSnapshotAfter->filesize
+        ,(int)undo_elem->fileSnapshotAfter->mtimefile
+        );
+    }
+    printf("\n");
     fflush(stdout);
   }
 
@@ -301,6 +476,18 @@ p_free_undo_elem(GapStoryUndoElem    *undo_elem)
   {
     gap_story_free_storyboard(&undo_elem->stb);
   }
+  
+  if(undo_elem->fileSnapshotBefore != NULL)
+  {
+    p_free_file_snapshot(undo_elem->fileSnapshotBefore);
+    undo_elem->fileSnapshotBefore = NULL;
+  }
+  if(undo_elem->fileSnapshotAfter != NULL)
+  {
+    p_free_file_snapshot(undo_elem->fileSnapshotAfter);
+    undo_elem->fileSnapshotAfter = NULL;
+  }
+  
   g_free(undo_elem);
   
 }  /* end p_free_undo_elem */
@@ -397,28 +584,71 @@ gap_stb_undo_destroy_undo_stack(GapStbTabWidgets *tabw)
 }  /* end gap_stb_undo_destroy_undo_stack */
 
 
-/* ---------------------------------------
- * gap_stb_undo_push_clip
- * ---------------------------------------
+/* -----------------------------------------
+ * gap_stb_undo_push_clip_with_file_snapshot
+ * -----------------------------------------
  * create a new undo element (according to specified parameters)
- * and place it at top (first) of the und stack.
+ * and place it at top (first) of the undo stack.
  * if the stackpointer is NOT equal to the stack_list root,
- * the delete all undo elements from stack_list up to stackpointer.
+ * then delete all undo elements from stack_list up to stackpointer.
  *
  * move the stackpointer to next element. (keep the element
- * on the stack for redo purpose)
+ * on the stack list for undo purpose)
+ *
+ * if *filenamePtr points to an existing readable file, this procedure 
+ * attaches a copy of the filename and its full content to the pushed element.
+ * (fileSnapshotBefore)
+ * Note that the current implementation keeps the filecontent in memory
+ * because it is intended to hold small parameterfiles (xml settings for movepath transistion)
+ * The filename Must NOT be used to store large data (as image or movie content)
+ *
+ * If the element at stack_ptr (that represents the previous procesing step)
+ * has a fileSnapshot attached then this is also recorded as fileSnapshotAfter
+ * 
+ * Example
+ * =======
+ *  push element EEEE,  *filenamePtr points to "a.xml"
  *           
  * stack_list -->DDDD                               
- *               CCCC                                   EEEE <--- stack_ptr after push (EEEE)
- *               BBBB  <-- stack_ptr before push(EEEE)  BBBB      (deletes CCCC, DDDD)
- *               AAAA                                   AAAA
+ *               CCCC                                          EEEE <--- stack_ptr after push (EEEE, a.xml)
+ *               BBBB  <-- stack_ptr before push(EEEE, a.xml)  BBBB      (deletes CCCC, DDDD)
+ *               AAAA                                          AAAA
  * --------------------------------------------------------------------------------
+ *
+ * content of elem EEEE after the push EEEE operation:
+ *
+ *  EEEE.fileSnapshotBefore holds filename and content of a.xml (before processing of step EEEE)
+ *  EEEE.fileSnapshotAfter is NULL
+ *  EEEE.filenamePtr holds addr of the pointer that points to filename "a.xml"
+ *
+ *
+ * Example continued: 
+ * ==================
+ *
+ *
+ *                                                             FFFF <--- stack_ptr after push (FFFF, NULL)
+ * stack_list -->EEEE  <-- stack_ptr before push(FFFF, NULL)   EEEE ..... updated fileSnapshotAfter                              
+ *               BBBB                                          BBBB                           
+ *               AAAA                                          AAAA
+ * --------------------------------------------------------------------------------
+ *
+ * content of elem EEEE after the push FFFF operation:
+ *
+ *  EEEE.fileSnapshotBefore holds filename and content of a.xml
+ *          (before processing of step EEEE, that is relevant for undo EEEE purpose)
+ *  EEEE.fileSnapshotAfter holds filename and content at time of push FFFF 
+ *          (e.g. after processing EEEE is finished, that is relevant for redo EEEE purpose)
+ *  EEEE.filenamePtr is rest to NULL
+ *
  */
 void
-gap_stb_undo_push_clip(GapStbTabWidgets *tabw, GapStoryFeatureEnum feature_id, gint32 story_id)
+gap_stb_undo_push_clip_with_file_snapshot(GapStbTabWidgets *tabw
+   , GapStoryFeatureEnum feature_id, gint32 story_id
+   , char **filenamePtr)
 {
   GapStoryBoard       *stb;
   GapStoryUndoElem    *new_undo_elem;
+  GapStoryUndoElem    *top_undo_elem;
 
 
   if(tabw == NULL)
@@ -478,6 +708,50 @@ gap_stb_undo_push_clip(GapStbTabWidgets *tabw, GapStoryFeatureEnum feature_id, g
   new_undo_elem = g_new(GapStoryUndoElem, 1);
   new_undo_elem->clip_story_id = story_id;
   new_undo_elem->feature_id = feature_id;
+
+  new_undo_elem->filenamePtr = NULL;
+  new_undo_elem->fileSnapshotBefore = NULL;
+  new_undo_elem->fileSnapshotAfter = NULL;
+  if(filenamePtr != NULL)
+  {
+    char *filename;
+    
+    new_undo_elem->filenamePtr = filenamePtr;
+    filename = *filenamePtr;
+    if(filename != NULL)
+    {
+      new_undo_elem->fileSnapshotBefore = p_create_file_snapshot(filename);
+    }
+  }
+  
+  top_undo_elem = tabw->undo_stack_list;
+  if (top_undo_elem != NULL)
+  {
+    if(top_undo_elem->filenamePtr != NULL)
+    {
+      char *filename;
+
+      /* at this point the top_undo_elem represents the previous step
+       * that just has been finished, since the next step (new_undo_elem)
+       * is ready to be pushed on the stack, and top_undo_elem->filenamePtr != NULL
+       * indicates that the previous step may have edited a file.
+       * now record this fileSnapshotAfter for redo purpose
+       * Note that both filename and content may have changed in the previous step
+       * since recording the fileSnapshotBefore.
+       */
+      filename = *top_undo_elem->filenamePtr;
+      if(filename != NULL)
+      {
+        top_undo_elem->fileSnapshotAfter = p_create_file_snapshot(filename);
+      }
+    }
+    /* clear the filenamePtr to disable multiple recording of fileSnapshotAfter
+     * Note that filenamePtr refers to a clip that may only exist until the next processing step
+     * that already can delete the clip (this can also happen in undo processing
+     * where the storyboard is replaced by a duplicate and the adress)
+     */
+    top_undo_elem->filenamePtr = NULL;
+  }
   
   new_undo_elem->next = tabw->undo_stack_list;
 
@@ -490,6 +764,32 @@ gap_stb_undo_push_clip(GapStbTabWidgets *tabw, GapStoryFeatureEnum feature_id, g
 
   gap_story_dlg_tabw_undo_redo_sensitivity(tabw);
   
+}  /* end gap_stb_undo_push_clip_with_file_snapshot */
+
+
+
+/* ---------------------------------------
+ * gap_stb_undo_push_clip
+ * ---------------------------------------
+ * create a new undo element (according to specified parameters)
+ * and place it at top (first) of the undo stack.
+ * if the stackpointer is NOT equal to the stack_list root,
+ * the delete all undo elements from stack_list up to stackpointer.
+ *
+ * move the stackpointer to next element. (keep the element
+ * on the stack for redo purpose)
+ *           
+ * stack_list -->DDDD                               
+ *               CCCC                                   EEEE <--- stack_ptr after push (EEEE)
+ *               BBBB  <-- stack_ptr before push(EEEE)  BBBB      (deletes CCCC, DDDD)
+ *               AAAA                                   AAAA
+ * --------------------------------------------------------------------------------
+ */
+void
+gap_stb_undo_push_clip(GapStbTabWidgets *tabw, GapStoryFeatureEnum feature_id, gint32 story_id)
+{
+  gap_stb_undo_push_clip_with_file_snapshot(tabw, feature_id, story_id, NULL);
+
 }  /* end gap_stb_undo_push_clip */
 
 
diff --git a/gap/gap_story_undo.h b/gap/gap_story_undo.h
old mode 100644
new mode 100755
index c911943..af76851
--- a/gap/gap_story_undo.h
+++ b/gap/gap_story_undo.h
@@ -45,6 +45,10 @@ void                    gap_stb_undo_push_clip(GapStbTabWidgets *tabw
                            , GapStoryFeatureEnum feature_id
                            , gint32 story_id
                            );
+void                    gap_stb_undo_push_clip_with_file_snapshot(GapStbTabWidgets *tabw
+                           , GapStoryFeatureEnum feature_id, gint32 story_id
+                           , char **filenamePtr);
+
 void                    gap_stb_undo_push(GapStbTabWidgets *tabw, GapStoryFeatureEnum feature_id);
 void                    gap_stb_undo_group_begin(GapStbTabWidgets *tabw);
 void                    gap_stb_undo_group_end(GapStbTabWidgets *tabw);
diff --git a/gap/gap_story_undo_types.h b/gap/gap_story_undo_types.h
old mode 100644
new mode 100755
index bd9f50a..b989a66
--- a/gap/gap_story_undo_types.h
+++ b/gap/gap_story_undo_types.h
@@ -54,18 +54,32 @@ typedef enum
   } GapStoryFeatureEnum;
 
 
+/* storyboard undo file snapshot element
+ */
+typedef struct GapStoryUndoFileSnapshot {
+  char       *filename;
+  char       *filecontent;
+  gint32      filesize;
+  gint32      mtimefile;
+}  GapStoryUndoFileSnapshot;
+
+
+
 /* storyboard undo element
  */
 typedef struct GapStoryUndoElem {
-  GapStoryFeatureEnum  feature_id;     
+  GapStoryFeatureEnum  feature_id;
   gint32 clip_story_id;            /* -1 if feature modifies more than 1 clip */
-  GapStoryBoard       *stb;        /* storyboard backup before 
-                                    * feature with feature_id was applied 
+  GapStoryBoard       *stb;        /* storyboard backup before
+                                    * feature with feature_id was applied
                                     */
-  struct GapStoryUndoElem  *next;
+  GapStoryUndoFileSnapshot  *fileSnapshotBefore;
+  GapStoryUndoFileSnapshot  *fileSnapshotAfter;
+  char                     **filenamePtr;
+  struct GapStoryUndoElem   *next;
 }  GapStoryUndoElem;
 
 
 
 
-#endif 
+#endif



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