Logo Search packages:      
Sourcecode: manedit version File versions  Download package

editorundo.c

#include <gtk/gtk.h>

#include "../include/string.h"

#include "editor.h"
#include "editorop.h"
#include "editorundo.h"

#include "manedit.h"


gpointer EditorUndoNew(
      editor_struct *editor, editor_undo_type type,
      const gchar *comment
);
void EditorUndoDelete(editor_struct *editor, gpointer u);

static editor_undo_insert_chars_struct *EditorUndoApplyRemove(
      editor_undo_remove_chars_struct *u_remove
);
static editor_undo_remove_chars_struct *EditorUndoApplyInsert(
      editor_undo_insert_chars_struct *u_insert
);

gint EditorUndoDoApply(
      editor_struct *editor,
      gpointer **in_list, gint *in_total,
      gpointer **out_list, gint *out_total,
      gint max
);


#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))


/*
 *    Allocates a new undo structure with the given type and initial
 *    values.
 */
gpointer EditorUndoNew(
      editor_struct *editor, editor_undo_type type,
      const gchar *comment
)
{
      gint len = 0;
      gpointer u = NULL;

      switch(type)
      {
        case EDITOR_UNDO_REMOVE_CHARS:
          len = sizeof(editor_undo_remove_chars_struct);
          break;

        case EDITOR_UNDO_INSERT_CHARS:
          len = sizeof(editor_undo_insert_chars_struct);
          break;
      }

      if(len > 0)
      {
          u = g_malloc0(len);
          if(u != NULL)
          {
            editor_undo_common_struct *common = EDITOR_UNDO_COMMON(u);

            /* Set common member values */
            common->type = type;
            common->comment = (comment != NULL) ?
                g_strdup(comment) : NULL;
            common->editor_ptr = editor;
          }
      }

      return(u);
}

/*
 *    Deletes the undo structure and all its allocated resources.
 */
void EditorUndoDelete(editor_struct *editor, gpointer u)
{
      editor_undo_remove_chars_struct *u_remove;
      editor_undo_insert_chars_struct *u_insert;
      editor_undo_common_struct *u_common;

      if(u == NULL)
          return;

      /* Delete type specific data */
      switch(EDITOR_UNDO_COMMON(u)->type)
      {
        case EDITOR_UNDO_REMOVE_CHARS:
          u_remove = EDITOR_UNDO_REMOVE_CHARS(u);
          break;

        case EDITOR_UNDO_INSERT_CHARS:
          u_insert = EDITOR_UNDO_INSERT_CHARS(u);
          g_free(u_insert->characters);
          break;
      }

      /* Delete common members */
      u_common = EDITOR_UNDO_COMMON(u);
      g_free(u_common->comment);

      g_free(u);
}



/*
 *    Procedure to remove characters (undo insert characters).
 *
 *    Returns an inverse insert characters operation or NULL on error.
 *
 *    Given input undo structure will be deleted and should not be
 *    referenced again after this call.
 */
static editor_undo_insert_chars_struct *EditorUndoApplyRemove(
      editor_undo_remove_chars_struct *u_remove
)
{
      editor_undo_insert_chars_struct *u_insert = NULL;
      editor_struct *editor;
      GtkEditable *editable;
      GtkText *text;
      gchar *text_buf = NULL;


      if(u_remove == NULL)
          return(u_insert);

      editor = EDITOR(u_remove->editor_ptr);
      editable = (GtkEditable *)u_remove->w;
      text = (GtkText *)u_remove->w;
      if((editor == NULL) || (editable == NULL))
          return(u_insert);

      /* Get characters from editable that we're about to remove */
      if(u_remove->length > 0)
          text_buf = gtk_editable_get_chars(
            editable,
            u_remove->position,
            u_remove->position + u_remove->length
          );

      /* Create inverse operation structure and set values */
      u_insert = EDITOR_UNDO_INSERT_CHARS(EditorUndoNew(
          editor, EDITOR_UNDO_INSERT_CHARS,
          u_remove->comment
      ));
      if(u_insert != NULL)
      {
          u_insert->w = GTK_WIDGET(editable);

          u_insert->position = u_remove->position;
          u_insert->length = (text_buf != NULL) ?
            u_remove->length : 0;

          g_free(u_insert->characters);
          u_insert->characters = (text_buf != NULL) ?
            g_strdup(text_buf) : NULL;
      }
      g_free(text_buf);
      text_buf = NULL;

      /* Now remove the characters */
      if(use_text_delete)
      {
          gtk_text_freeze(text);
          gtk_text_set_point(text, (guint)u_remove->position);
          gtk_text_forward_delete(text, (guint)u_remove->length);
          gtk_text_thaw(text);
      }
      else
      {
          gtk_text_freeze(text);
          gtk_editable_delete_text(
            editable,
            u_remove->position,
            u_remove->position + u_remove->length
          );
          gtk_text_thaw(text);
      }

      /* Delete the input structure */
      EditorUndoDelete(editor, u_remove);
      u_remove = NULL;

      return(u_insert);
}

/*
 *      Procedure to insert characters (undo remove characters).
 *
 *      Returns an inverse remove characters operation or NULL on error.
 * 
 *      Given input undo structure will be deleted and should not be
 *      referenced again after this call.
 */
static editor_undo_remove_chars_struct *EditorUndoApplyInsert(
      editor_undo_insert_chars_struct *u_insert
)
{
      editor_undo_remove_chars_struct *u_remove = NULL;
      editor_struct *editor;
      GtkEditable *editable;


      if(u_insert == NULL)
          return(u_remove);

      editor = EDITOR(u_insert->editor_ptr);
      editable = (GtkEditable *)u_insert->w;
      if((editor == NULL) || (editable == NULL))
          return(u_remove);

      /* Insert characters */
      if((u_insert->length > 0) && (u_insert->characters != NULL))
      {
          medit_core_struct *core_ptr = MEDIT_CORE(editor->core_ptr);
          medit_styles_list_struct *styles_list = NULL;
          GtkStyle *style_ptr = NULL;


          if(core_ptr != NULL)
          {
            styles_list = &core_ptr->styles_list;
            style_ptr = styles_list->edit_text_standard;
          }

          gtk_text_set_point(GTK_TEXT(editable), u_insert->position);

          gtk_text_freeze(GTK_TEXT(editable));
          gtk_text_insert(
            GTK_TEXT(editable),
            (style_ptr != NULL) ? style_ptr->font : NULL,
            NULL,       /* Foreground color */
            NULL,       /* Background color */
            u_insert->characters,
            u_insert->length
          );
          gtk_text_thaw(GTK_TEXT(editable));
          gtk_editable_select_region(
            editable,
            u_insert->position,
            u_insert->position + u_insert->length
          );
          gtk_text_set_point(
            GTK_TEXT(editable),
            u_insert->position + u_insert->length
          );

          /* Need to perform syntax highlighting, because the insert
           * callback is not called.
           */
          EditorSyntaxHighlight(
            editor, GTK_WIDGET(editable),
            u_insert->position, u_insert->position,
            u_insert->position + u_insert->length
          );
      }

      /* Create inverse operation structure and set values */
      u_remove = EDITOR_UNDO_REMOVE_CHARS(EditorUndoNew(
          editor, EDITOR_UNDO_REMOVE_CHARS,
          u_insert->comment
      ));
      if(u_remove != NULL)
      {
          u_remove->w = GTK_WIDGET(editable);

          u_remove->position = u_insert->position;
          u_remove->length = u_insert->length;
      }

      /* Delete the input structure */
      EditorUndoDelete(editor, u_insert);
      u_insert = NULL;

      return(u_remove);
}


/*
 *    Procedure to apply the first undo item in the in_list and then
 *    then convert it into the inverse operation and transfer it to
 *    the out_list.
 *
 *    If highest item on out_list would exceed the given max then
 *    it will be deleted.
 *
 *    Returns non-zero on error.
 */
gint EditorUndoDoApply(
      editor_struct *editor,
      gpointer **in_list, gint *in_total,
      gpointer **out_list, gint *out_total,
      gint max
)
{
      gint i, n;
      gpointer u_in, u_inverse = NULL;


      if((editor == NULL) || (in_list == NULL) || (in_total == NULL))
          return(-1);

      /* Nothing to do? */
      if((*in_list == NULL) || (*in_total < 1))
          return(0);


      /* Get first undo operation from input list, newest undo
       * operation is always the first in the list
       */
      u_in = (*in_list)[0];

      /* Reduce and shift input list */
      *in_total = (*in_total) - 1;
      for(i = 0; i < *in_total; i++)
          (*in_list)[i] = (*in_list)[i + 1];

      if(*in_total > 0)
      {
          *in_list = (gpointer *)g_realloc(
            *in_list, (*in_total) * sizeof(gpointer)
          );
          if(*in_list == NULL)
            *in_total = 0;
      }
      else
      {
          g_free(*in_list);
          *in_list = NULL;
          *in_total = 0;
      }



      /* Handle input undo operation by its type, note that the
       * given input undo structure will be deleted
       */
      switch(EDITOR_UNDO_COMMON(u_in)->type)
      {
        case EDITOR_UNDO_REMOVE_CHARS:
          u_inverse = EditorUndoApplyRemove(
            EDITOR_UNDO_REMOVE_CHARS(u_in)
          );
          u_in = NULL;
          break;

        case EDITOR_UNDO_INSERT_CHARS:
          u_inverse = EditorUndoApplyInsert(
            EDITOR_UNDO_INSERT_CHARS(u_in)
          );
          u_in = NULL;
          break;
      }

      /* Undo not processed? */
      if(u_in != NULL)
      {
          EditorUndoDelete(editor, u_in);
          u_in = NULL;
      }



      /* Did we get an inverse operation? */
      if(u_inverse != NULL)
      {
          /* Add it to the out list */
          if((out_list != NULL) && (out_total != NULL))
          {
            if(*out_total < 0)
               *out_total = 0;

            *out_total = (*out_total) + 1;
            *out_list = (gpointer *)g_realloc(
                *out_list,
                (*out_total) * sizeof(gpointer)
            );
            if(*out_list == NULL)
            {
                *out_total = 0;

                EditorUndoDelete(editor, u_inverse);
                u_inverse = NULL;
            }
            else
            {
                /* Shift */
                for(i = (*out_total) - 1; i > 0; i--)
                  (*out_list)[i] = (*out_list)[i - 1];

                /* Record newest undo inverse structure as first item */
                (*out_list)[0] = u_inverse;
                u_inverse = NULL;
            }

            /* Delete older undo structures on the out list */
            if(*out_total > max)
            {
                n = MAX(max, 0);

                /* Delete all undo structures greater than the max */
                for(i = (*out_total) - 1; i >= n; i--)
                  EditorUndoDelete(editor, (*out_list)[i]);

                (*out_total) = n;
                if((*out_total) > 0)
                {
                  (*out_list) = (gpointer*)g_realloc(
                      *out_list,
                      (*out_total) * sizeof(gpointer)
                  );
                  if((*out_list) == NULL)
                      (*out_total) = 0;
                }
                else
                {
                  g_free(*out_list);
                  (*out_list) = NULL;
                  (*out_total) = 0;
                }
            }
          }
          else
          {
            /* No out list available, delete inverse operation
             * structure.
             */
            EditorUndoDelete(editor, u_inverse);
            u_inverse = NULL;
          }
      }

      return(0);
}

Generated by  Doxygen 1.6.0   Back to index