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

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) 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)

About the Author

zdf

Romania Romania
Just a humble programmer.

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