zoukankan      html  css  js  c++  java
  • 手把手教你写Undo、Redo程序(续)

    手把手教你写UndoRedo程序(续)

        在第一篇文章“手把手教你写UndoRedo程序”里,我介绍了如何实现一个基于图像操作的Undo, Redo框架结构。但是我们现在所讲的还只是Undo类的结构。还有一个非常重要的部分没有说明:我们的这些Undo类应该在什么地方使用呢?

        还记得我们的第一个CImageData类吗?在ExcuteOperation函数的说明曾提到需要修改函数的参数。现在就是需要修改的时候了。

    bool CImageData::ExecuteOperation(CImageOperation * pCmd, CUndoData * pData)

    {

        pCmd->Execute(this, pData);

    }

    bool CImageOperation::Execute(CImageData * pData, CUndoData *pData)

    {

        if (pData)

        {

            // 如果外部传入的Undo数据接口pData不空

            // 我们就在进行真正的操作前先用pData构造好Undo数据

        }

        // 根据不同的派生类修改pData的图像数据

    }

        下面我们还是以图像旋转这个具体操作来看如何实现CImageRotate

    class CImageRatate : public CImageOperation

    {

    public:

        CImageRatate(float  fAngle) : m_fRotateAngel(fAngle) {}

     

        virtual bool    Execute(CImageData * pData, CUndoData * pData)

        {

            if (pData)  // 根据不同的操作类型,pData实际上指向的是不同的派生类

                pData->m_fRotateAngle = -m_fRotateAngle;

            pData->Rotate(m_fRotateAngle);

            // 这里假定Rotate函数是一个旋转图像的成员函数

        }

    private:

        float       m_fRotateAngle;

    };

     

    在这种结构里有许多值得改进的地方。下面我就三个方面对前面介绍的结构进行改进:

    ·支持单步操作的多级Undo/Redo

    ·支持基于图像SelectUndo/Redo

    ·实现Undo数据的磁盘保存

        下面分别对每个方面进行单独说明。

     

    1 单步操作的多级Undo/Redo

        先看看什么是单步操作的多级Undo/Redo: 在一些比较特殊的软件中,软件提供给用户的是执行某一步操作,但是在内部程序实现的时候会包含多个子操作。这多个子操作就形成了多级的Undo/Redo。当然,这种情况比较特殊,比如在绣花编辑软件中,用户的针脚操作在内部可能需要多个子操作,这样才能方便用户在后来根据子操作进行修改(比如针脚编辑).

        特别说明:我下面所做的改进只能是体现了单步操作的多级Undo/Redo实现思想。之所以这么做主要是为了能利用前面的框架。为了把多个子操作的Undo/Redo封装起来。我们还需要一个Undo接口类。

    它的基本实现方法如下:

    class CUndoInfo

    {

    public:

        // 默认值直接设置为1,因为m_pSaved至少有一个

        CUndoInfo() : m_UndoNum(1) {}

        virtual ~CUndoInfo() {}

       

        virtual bool UndoAction(CImageData* pData)

        {

            do{

                m_pSaved[m_UndoNum-1].UndoAction(pData);

                m_UndoNum--;

            }while(m_UndoNum>0 );

            return true;

        }

        // 这里的CUndoData指针实际上可以是一个指针列表, 表示多个子操作

        CUndoData *     m_pSaved;

        // 表示m_pSaved保存的指针个数

        unsigned int    m_UndoNum;

    };

        现在我们已经有了新的Undo接口类,所以给外部使用的文档接口类也需要改变。现在我们只需要把CUndoList中所有的CUndoData全部替换为CUndoInfo即可。怎么样,还算比较简单吧!

     

    2 基于图像SelectUndo/Redo

        下面就实现带SelectUndo/Redo功能。在图像编辑时,如果用户在进行操作时,先选择了部分图像。那么我们在保存数据时就应该只保存修改的部分数据。这样才能节省空间,提高效率。

     

        首先我们需要在添加一个矩形值成员变量,用来保存操作的范围。这个矩形值的保存位置有两个:如果你不想对前面定义的类做过多的修改,这个矩形值可直接放入CUndoInfo类;另一个位置是保存在CUndoData

        如果保存在CUndoInfo,我们就只需在CUndoInfo::UndoAction进行简单修改。

    class CUndoInfo

    {

        // 保持原来的定义不变

        RECT    m_Rect;

        virtual bool    UndoAction(CImageData * pData)

        {

            // 根据m_Rect的值修改pData,也就是取pData部分数据形成新的pData

            // 然后用新的数据调用内部Undo.

            do{

                m_pSaved[m_UndoNum-1].UndoAction(pData);

                m_UndoNum--;

            }while(m_UndoNum>0 );

            return true;

        }

    };

        如果保存在CUndoData抽象类中,我们就需要对每个派生类进行修改。很明显,这样的代价要比上一种方法大很多。所以我也不多做介绍。

     

    3 实现Undo数据的磁盘保存

        节省内存空间我们才需要实现Undo数据的磁盘保存。保存在磁盘,更容易实现无穷级数的UndoRedo操作。从前面的描述知道,可逆操作保存的是操作算法,这类操作一般占用的磁盘空间不多。我这里也就暂时不考虑实现对此类操作的磁盘保存。所以我们把保存在CFullImageUndoCImageData数据直接保存在磁盘。我们对CFullImageUndo类做如下修改:

    class CFullImageUndo

    {

    public:

        virtual bool    UndoAction(CImageData * pData)

        {

            // 先从m_pszFilePath读取文件的内容到临时内存

            // 把读取的数据与pData的数据进行交换

            // 把交换后的数据重新写入文件,必要时释放临时内存

        }

    public:

        // 指向CImageData保存的图像数据位置

        char * m_pszFilePath;

    };

        也许这些实现和改进都不值一提。如果能给部分初学者提供部分帮助足矣。 
  • 相关阅读:
    【原创】Algorithms:原地归并排序
    Java中相等测试
    测试可变字符序列stringBuilder
    呃。。第一篇随笔。
    hiho兄弟的字典树之争(hiho1014)
    字典树
    最大公约数
    素数
    递归算法1
    logging模块,collections模块,random模块
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330102.html
Copyright © 2011-2022 走看看