Click here to Skip to main content
15,892,575 members
Articles / Programming Languages / C++

A Simple, Action Based, Undo/Redo Framework

Rate me:
Please Sign up or sign in to vote.
4.51/5 (38 votes)
16 Feb 2013CPOL5 min read 97.8K   1.9K   104  
How to use a simple, action based, undo/redo framework
/*
***************************************************************************

kis v 1

(c) 2006, Florin DUMITRESCU

mailto: fdproxy@gmail.com

$Workfile: _Graphic.h $ 

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


#ifndef _GRAPHIC_H_
#define _GRAPHIC_H_


//-------------------------------------------------------------------------
/** [To be supplied.] */
inline std::string ReadSignature( std::istream& a_is )
{
  using namespace std;
  ws( a_is );
  char c;
  string Result;
  while ( a_is.get( c ) && std::isalnum( c, std::locale::classic() ) )
    Result += c;
  return Result;
}


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_BadSignature
{
  // empty
};


//-------------------------------------------------------------------------
/** C_Graphic is the base calss of all graphic objects.  */
class C_Graphic
{

public:

  virtual ~C_Graphic() = 0
  {
    // nop
  }

  virtual C_Graphic* Clone() const = 0;
  virtual bool IsEmpty() const = 0;
  virtual unsigned int GetBytesCount() const = 0;
  virtual void Draw( CDC* a_pDC ) const = 0;
  virtual void ReadData( std::istream& a_is ) = 0;
  virtual void ReadSignature( std::istream& a_os ) = 0;
  virtual void WriteData( std::ostream& a_os ) = 0;
  virtual void WriteSignature( std::ostream& a_os ) = 0;

  void Read( std::istream& a_is )
  {
    ReadSignature( a_is );
    ReadData( a_is );
  }

  void Write( std::ostream& a_os )
  {
    WriteSignature( a_os );
    WriteData( a_os );
  }

};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_Graphic_Simple : public C_Graphic
{

public:

  C_Graphic_Simple()
  :
  m_PenColor( RGB( 255, 127, 63 ) ),
  m_PenWidth( 50 )
  {
    // nop
  }

  virtual ~C_Graphic_Simple()
  {
    // nop
  }

  void SetPenWidth( const int a_PenWidth )
  {
    m_PenWidth = a_PenWidth;
  }

  int GetPenWidth() const
  {
    return m_PenWidth;
  }

protected:

  COLORREF m_PenColor;
  int m_PenWidth;

};


//-------------------------------------------------------------------------
/** C_Graphic_Simple is the base class of graphic objects that
    can be described by two points. */
class C_Graphic_TwoPoints : public C_Graphic_Simple
{

public:

  C_Graphic_TwoPoints( const CPoint& a_StartPoint, const CPoint& a_EndPoint )
  :
  m_StartPoint( a_StartPoint ),
  m_EndPoint( a_EndPoint )
  {
    // nop
  }

  C_Graphic_TwoPoints()
  :
  m_StartPoint( 0, 0 ),
  m_EndPoint( 0, 0 )
  {
    // nop
  }

  void SetStartPoint( const CPoint a_StartPoint )
  {
    m_StartPoint = a_StartPoint;
  }

  const CPoint& GetStartPoint() const
  {
    return m_StartPoint;
  }

  void SetEndPoint( const CPoint a_EndPoint )
  {
    m_EndPoint = a_EndPoint;
  }

  const CPoint& GetEndPoint() const
  {
    return m_EndPoint;
  }

  /*override*/ unsigned int GetBytesCount() const
  {
    return sizeof( *this );
  }

  /*override*/ bool IsEmpty() const
  {
    return !!(m_StartPoint == m_EndPoint);
  }

  /*override*/ void WriteData( std::ostream& a_os )
  {
    using namespace std;
    ASSERT( a_os.good() );
    a_os 
      << '\t' << m_StartPoint.x << endl
      << '\t' << m_StartPoint.y << endl
      << '\t' << m_EndPoint.x << endl
      << '\t' << m_EndPoint.y << endl;
  }

  /*override*/ void ReadData( std::istream& a_is )
  {
    ASSERT( a_is.good() );
    a_is 
      >> m_StartPoint.x
      >> m_StartPoint.y
      >> m_EndPoint.x
      >> m_EndPoint.y;
  }

protected:

  CPoint m_StartPoint;
  CPoint m_EndPoint;

};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_Graphic_Segment : public C_Graphic_TwoPoints
{

public:

  C_Graphic_Segment()
  {
    // nop
  }

  C_Graphic_Segment( const CPoint& a_StartPoint, const CPoint& a_EndPoint )
  :
  C_Graphic_TwoPoints( a_StartPoint, a_EndPoint )
  {
    // nop
  }

  /*override*/ void Draw( CDC* a_pDC ) const
  {
    int OldMM = a_pDC->SetMapMode( MM_HIMETRIC );
    CPen Pen( PS_SOLID, m_PenWidth, m_PenColor );
    CPen* pOldPen = a_pDC->SelectObject( &Pen );
    a_pDC->MoveTo( m_StartPoint );
    a_pDC->LineTo( m_EndPoint );
    a_pDC->SelectObject( pOldPen );
    a_pDC->SetMapMode( OldMM );
  }

  /*override*/ C_Graphic* Clone() const
  {
    return new C_Graphic_Segment( *this );
  }

  /*override*/ void WriteSignature( std::ostream& a_os )
  {
    a_os << Signature() << std::endl;
  }

  /*override*/ void ReadSignature( std::istream& a_is )
  {
    if ( ::ReadSignature( a_is ) != Signature() )
      throw C_BadSignature();
  }

  static std::string Signature()
  {
    return "Segment";
  }

  static C_Graphic* New()
  {
    return new C_Graphic_Segment();
  }

};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_Graphic_Rectangle : public C_Graphic_TwoPoints
{

public:

  /*override*/ void Draw( CDC* a_pDC ) const
  {
    int OldMM = a_pDC->SetMapMode( MM_HIMETRIC );
    CPen Pen( PS_SOLID, m_PenWidth, m_PenColor );
    CBrush* pOldBrush = (CBrush*)a_pDC->SelectStockObject( HOLLOW_BRUSH );
    CPen* pOldPen = a_pDC->SelectObject( &Pen );
    a_pDC->Rectangle( m_StartPoint.x, m_StartPoint.y, m_EndPoint.x, m_EndPoint.y );
    a_pDC->SelectObject( pOldPen );
    a_pDC->SelectObject( pOldBrush );
    a_pDC->SetMapMode( OldMM );
  }

  /*override*/ C_Graphic* Clone() const
  {
    return new C_Graphic_Rectangle( *this );
  }

  /*override*/ void WriteSignature( std::ostream& a_os )
  {
    a_os << Signature() << std::endl;
  }

  /*override*/ void ReadSignature( std::istream& a_is )
  {
    if ( ::ReadSignature( a_is ) != Signature() )
      throw C_BadSignature();
  }

  static std::string Signature()
  {
    return "Rectangle";
  }

  static C_Graphic* New()
  {
    return new C_Graphic_Rectangle();
  }
};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_Graphic_Ellipse : public C_Graphic_TwoPoints
{

public:

  /*override*/ void Draw( CDC* a_pDC ) const
  {
    int OldMM = a_pDC->SetMapMode( MM_HIMETRIC );
    CPen Pen( PS_SOLID, m_PenWidth, m_PenColor );
    CBrush* pOldBrush = (CBrush*)a_pDC->SelectStockObject( HOLLOW_BRUSH );
    CPen* pOldPen = a_pDC->SelectObject( &Pen );
    a_pDC->Ellipse( m_StartPoint.x, m_StartPoint.y, m_EndPoint.x, m_EndPoint.y );
    a_pDC->SelectObject( pOldPen );
    a_pDC->SelectObject( pOldBrush );
    a_pDC->SetMapMode( OldMM );
  }

  /*override*/ C_Graphic* Clone() const
  {
    return new C_Graphic_Ellipse( *this );
  }

  /*override*/ void WriteSignature( std::ostream& a_os )
  {
    a_os << Signature() << std::endl;
  }

  /*override*/ void ReadSignature( std::istream& a_is )
  {
    if ( ::ReadSignature( a_is ) != Signature() )
      throw C_BadSignature();
  }

  static std::string Signature()
  {
    return "Ellipse";
  }

  static C_Graphic* New()
  {
    return new C_Graphic_Ellipse();
  }

};


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

public:

  C_Reader()
  {
    m_Map[C_Graphic_Segment::Signature()] = &C_Graphic_Segment::New;
    m_Map[C_Graphic_Rectangle::Signature()] = &C_Graphic_Rectangle::New;
    m_Map[C_Graphic_Ellipse::Signature()] = &C_Graphic_Ellipse::New;
  }

  C_Graphic* Read( std::istream& a_is )
  {
    using namespace std;
    string Signature = ::ReadSignature( a_is );
    P_NewFun New = m_Map[ Signature ];
    if ( ! New )
      return 0;
    C_Graphic* pGraphic = New();
    pGraphic->ReadData( a_is );
    return pGraphic;
  }

protected:

  typedef C_Graphic* (*P_NewFun)();
  std::map< std::string, P_NewFun > m_Map;

};


//-------------------------------------------------------------------------
/** [To be supplied.] */
class C_Drawing : public C_Graphic
{

public:

  C_Drawing()
  {
    // nop
  }

  C_Drawing( const C_Drawing& a_Src )
  {
    Copy( a_Src );
  }

  ~C_Drawing()
  {
    Delete();
  }

  void Delete()
  {
    for ( std::size_t i = 0; i < m_Graphics.size(); ++i )
      delete m_Graphics[i];
    m_Graphics.clear();
  }

  void Copy( const C_Drawing& a_Src )
  {
    Delete();
    for ( std::size_t i = 0; i < a_Src.m_Graphics.size(); ++i )
      if ( C_Graphic* pGraphic = dynamic_cast<C_Graphic*>( a_Src.m_Graphics[i]->Clone() ) )
        m_Graphics.push_back( pGraphic );
  }

  C_Drawing& operator=( const C_Drawing& a_Rhs )
  {
    Copy( a_Rhs );
    return *this;
  }

  /*override*/ void Draw( CDC* a_pDC ) const
  {
    for ( std::size_t i = 0; i < m_Graphics.size(); ++i )
      m_Graphics[i]->Draw( a_pDC );
  }

  /*override*/ C_Graphic* Clone() const
  {
    return new C_Drawing( *this );
  }

  unsigned int Add( const C_Graphic& a_Graphic )
  {
    m_Graphics.push_back( a_Graphic.Clone() );
    return unsigned int( m_Graphics.size() - 1 );
  }

  void RemoveAt( unsigned int a_ZIdx )
  {
    delete m_Graphics[ a_ZIdx ];
    m_Graphics[ a_ZIdx ] = 0;
    m_Graphics.erase( m_Graphics.begin() + a_ZIdx );
  }

  unsigned int GetCount() const
  {
    return unsigned int( m_Graphics.size() );
  }

  C_Graphic& At( int a_ZIdx )
  {
    return *m_Graphics[a_ZIdx];
  }

  /*override*/ unsigned int GetBytesCount() const
  {
    unsigned int Result = sizeof( *this );
    for ( std::size_t i = 0; i < m_Graphics.size(); ++i )
      Result += m_Graphics[i]->GetBytesCount();
    return Result;
  }

  /*override*/ bool IsEmpty() const
  {
    return m_Graphics.empty();
  }

  /*override*/ void WriteData( std::ostream& a_os )
  {
    for ( std::size_t i = 0; i < m_Graphics.size(); ++i )
      m_Graphics[i]->Write( a_os );
  }

  /*override*/ void ReadData( std::istream& a_is )
  {
    using namespace std;
    C_Reader Reader;
    while ( C_Graphic* pGraphic = Reader.Read( a_is ) )
      m_Graphics.push_back( pGraphic );
  }

  /*override*/ void WriteSignature( std::ostream& a_os )
  {
    a_os << Signature() << std::endl;
  }

  /*override*/ void ReadSignature( std::istream& a_is )
  {
    if ( ::ReadSignature( a_is ) != Signature() )
      throw C_BadSignature();
  }

  static std::string Signature()
  {
    return "Drawing";
  }

  static C_Graphic* New()
  {
    return new C_Drawing();
  }

protected:

  std::vector< C_Graphic* > m_Graphics;

};



#endif _GRAPHIC_H_

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)


Written By
zdf
Romania Romania
Just a humble programmer.

Comments and Discussions