Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

A Simple, Action Based, Undo/Redo Framework

, 16 Feb 2013
How to use a simple, action based, undo/redo framework
kiswinbin_v_1.zip
kis
kis.dll
kis.lib
KisWinBin_v_1_r_1.zip
KisWinBin_v_1_r_1
kis
kis.dll
kis.lib
kiswinsrc_v_1.zip
KisActionDemo
res
KisActionDemo.ico
KisActionDemoDoc.ico
Toolbar.bmp
vssver.scc
KisWinSrc_v_1_r_1.zip
KisWinSrc_v_1_r_1
kis
KisActionDemo
res
KisActionDemo.ico
KisActionDemoDoc.ico
Toolbar.bmp
vssver.scc
/*
***************************************************************************

kis v 1

(c) 2002-2006, Florin DUMITRESCU

mailto: fdproxy@gmail.com

$Workfile: kis_Action.cpp $ 

***************************************************************************
*/


#include "stdafx.h"
#include <deque>
#include <vector>
#include <assert.h>

#define protected public
#include "kis.h"


namespace
{


using namespace kis;


//-------------------------------------------------------------------------
/** [To be supplied.] */ 
class C_StackOfSPAction
{

public:
  C_StackOfSPAction()
  :
  m_MaxBytesCount( 0xffff ),
  m_UsedBytesCount( 0 )
  {
    // nop
  }

  ~C_StackOfSPAction()
  {
    // nop
  }

  void SetMaxBytesCount( const unsigned int a_MaxBytesCount )
  {
    if ( m_MaxBytesCount == a_MaxBytesCount )
      return;
    m_MaxBytesCount = a_MaxBytesCount;
    while ( ! m_Deque.empty() && m_UsedBytesCount > m_MaxBytesCount )
      _PopFront();
  }

  unsigned int GetMaxBytesCount() const
  {
    return m_MaxBytesCount;
  }

  unsigned int GetUsedBytesCount() const
  {
    return m_UsedBytesCount;
  }

  unsigned int GetCount() const
  {
    return static_cast< unsigned int >( m_Deque.size() );
  }

  bool HasRoom( const SP_Action a_spAction ) const
  {
    if ( m_MaxBytesCount == 0 )
      return true;
    else
      return m_MaxBytesCount >= a_spAction->GetBytesCount();
  }

  /** Returns true if the action was stored. */
  bool Push( SP_Action a_spAction )
  {
    // fake store
    if ( m_MaxBytesCount == 0 )
      return true;
    // how much space needed?
    unsigned int ActionBytesCount = a_spAction->GetBytesCount();
    // make room
    while ( ! m_Deque.empty() && (m_MaxBytesCount - m_UsedBytesCount) < ActionBytesCount )
      _PopFront();
    // got enough space?
    if ( (m_MaxBytesCount - m_UsedBytesCount) < ActionBytesCount )
      return false; // i guess not
    m_Deque.push_back( a_spAction );
    m_UsedBytesCount += ActionBytesCount;
    return true;
  }

  void Pop()
  {
    if ( m_Deque.empty() )
      return;
    assert( m_UsedBytesCount );
    m_UsedBytesCount -= m_Deque.back()->GetBytesCount();
    m_Deque.pop_back();
  }

  SP_Action Top()
  {
    assert( ! IsEmpty() );
    return m_Deque.back();
  }

  bool IsEmpty() const
  {
    return m_Deque.size() <= 0;
  }

  void Empty()
  {
    while ( m_Deque.size() )
      Pop();
  }

  const SP_Action At( unsigned int a_Idx ) const
  {
    unsigned int Idx = static_cast< unsigned int >( m_Deque.size() - a_Idx - 1 );
    return m_Deque[ Idx ];
  }

protected:
  unsigned int m_MaxBytesCount;
  unsigned int m_UsedBytesCount;
  std::deque< SP_Action > m_Deque;

  void _PopFront()
  {
    if ( m_Deque.empty() )
      return;
    assert( m_UsedBytesCount );
    m_UsedBytesCount -= m_Deque.back()->GetBytesCount();
    m_Deque.pop_front();
  }

};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_ActionExecutor_Default : public kis::C_ActionExecutor
{

public:
  
  C_ActionExecutor_Default()
  :
  m_RefCount( 0 )
  {
    //
  }

  /*override*/ unsigned long AddRef()
  {
    return ++m_RefCount;
  }

  /*override*/ unsigned long Release()
  {
    if ( ! --m_RefCount )
    {
      delete this;
      return 0;
    }
    return m_RefCount;
  }

  /*override*/ void SetMaxBytesCount( unsigned int a_MaxBytesCount )
  {
    m_UnexecuteStack.SetMaxBytesCount( a_MaxBytesCount );
    m_ReexecuteStack.SetMaxBytesCount( a_MaxBytesCount );
  }

  /*override*/ unsigned int GetMaxBytesCount() const
  {
    return m_UnexecuteStack.GetMaxBytesCount();
  }

  /*override*/ void Execute( const SP_Action a_spAction )
  {
    if ( ! a_spAction )
      return;

    SP_Action spAction = a_spAction->Clone();

    if ( ! spAction->HasInput() )
      if ( ! spAction->AcquireInput() )
        return;

    if ( spAction->IsNull() )
      return;

    if ( spAction->Execute() )
    {
      if ( spAction->IsUnexecutable() )
      {
        m_ReexecuteStack.Empty(); // the action was executed: the reexecute stack is now invalid
        if ( spAction->HasUnexecute() )
        {
          if ( m_UnexecuteStack.HasRoom( spAction ) )
          {
            m_UnexecuteStack.Push( spAction ); // unexecute information has been built: store it
          }
          else
          {
            // todo: call back: not enough space
            m_UnexecuteStack.Empty(); // unexecute information could not be pushed: the unexecute stack is invalid
          }
        }
        else
        {
          m_UnexecuteStack.Empty(); // unexecute information has not been built: the unexecute stack is invalid
        }
      }
    }
  }

  /*override*/ void Unexecute( const unsigned int a_ActionsCount )
  {
    for ( unsigned int i = 0; i < a_ActionsCount && ! m_UnexecuteStack.IsEmpty(); ++i )
    {
      SP_Action spAction = m_UnexecuteStack.Top();
      m_UnexecuteStack.Pop();
      spAction->Unexecute();
      if ( spAction->HasReexecute() )
      {
        if ( ! m_ReexecuteStack.Push( spAction ) )
          m_ReexecuteStack.Empty();
      }
      else
      {
        m_ReexecuteStack.Empty();
      }
    }
  }

  /*override*/ unsigned int GetUnexecuteCount() const
  {
    return m_UnexecuteStack.GetCount();
  }

  /*override*/ void GetUnexecuteName( unsigned char* a_Name, const unsigned char a_MaxNameLen, const unsigned int a_Idx ) const
  {
    m_UnexecuteStack.At( a_Idx )->GetName( a_Name, a_MaxNameLen );
  }

  /*override*/ void Reexecute( const unsigned int a_ActionsCount )
  {
    for ( unsigned int i = 0; i < a_ActionsCount && ! m_ReexecuteStack.IsEmpty(); ++i )
    {
      SP_Action spAction = m_ReexecuteStack.Top();
      m_ReexecuteStack.Pop();
      spAction->Reexecute();
      if ( spAction->HasUnexecute() )
      {
        if ( ! m_UnexecuteStack.Push( spAction ) )
          m_UnexecuteStack.Empty();
      }
      else
      {
        m_UnexecuteStack.Empty();
      }
    }
  }

  /*override*/ unsigned int GetReexecuteCount() const
  {
    return m_ReexecuteStack.GetCount();
  }

  /*override*/ void GetReexecuteName( unsigned char* a_Name, const unsigned char a_MaxNameLen, const unsigned int a_Idx ) const
  {
    m_ReexecuteStack.At( a_Idx )->GetName( a_Name, a_MaxNameLen );
  }

protected:
  
  unsigned long m_RefCount;
  C_StackOfSPAction m_UnexecuteStack;
  C_StackOfSPAction m_ReexecuteStack;
  
  /*override*/ ~C_ActionExecutor_Default()
  {
    //
  }

};


} // namespace


KIS_BEG


//-------------------------------------------------------------------------
/** [To be supplied.] */
KIS_API SP_ActionExecutor CreateActionExecutor_Default()
{
  return SP_ActionExecutor( new C_ActionExecutor_Default );
}


KIS_END

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

zdf

Romania Romania
Just a humble programmer.

You may also be interested in...

| Advertise | Privacy | Mobile
Web03 | 2.8.140921.1 | Last Updated 16 Feb 2013
Article Copyright 2006 by zdf
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid