需要帮助为文本编辑器程序编写撤消/重做功能

时间:2010-10-05 02:11:34

标签: c++

我用C ++编写了一个文本编辑器程序,它有简单的命令:LEFT,RIGHT,HOME,END,BACKSPACE,DELETE,INSERT,现在我需要执行UNDO和REDO函数。在我的程序中,用户必须能够撤消不超过最后十个命令。我想使用矢量实现来实现这一点,但我不知道如何设置它。我不确定如何将光标位置和字符存储到矢量中。有人可以提供一些帮助吗?

#ifndef CURSOR_H

#define CURSOR_H



#include <stdlib.h>
#include <iostream>

template <class Object>
class Cursor;
// Incomplete Declaration

template <class Object>
class CNode
{
        public:

                CNode( const Object & theElement = Object( ), CNode * n = NULL ) : element( theElement ), next( n ) { }
                Object  element;
                CNode *next;
                friend class Cursor<Object>;
};

template <class Object>
class Cursor
{
 public:
  Cursor( );
  bool isEmpty( ) const;
  void makeEmpty( );
  void left ( );
  void right ( );
  void del ( ); //This is the delete operation. I named it del instead of delete as delete conflicts with a C++ keyword.
  void back ( );
  void insert( const Object & x );
  void home ( );
  void end ( );
  void undo ( );


 private:

  void printText ( ) ;

  CNode<Object> *header;
  CNode<Object> *cursorPosition;

};
//#include "Cursor.cpp"
#endif

6 个答案:

答案 0 :(得分:3)

您想使用deque,以便可以从正面或背面添加和删除;添加命令时,将其添加到后面,撤消时将其从后面删除,当你达到11个命令时,从前面删除一个命令。

答案 1 :(得分:2)

查看Memento Design Pattern以及GOF

它存在于这个非常具体的要求。您可能必须将其与其他设计模式(例如Command,Iterator,FlyWeight等)结合使用

  

Memento Intent

     

不违反封装,   捕获并外化对象   内部状态使对象可以   稍后恢复到这个状态。

     

命令意图

     

封装请求   作为一个对象,从而让你   使用不同的参数化客户端   请求,队列或日志请求,以及   支持可撤销操作。

答案 2 :(得分:1)

还需要考虑其他一些事项:

通常,您不希望对游标移动应用撤消/重做(即它们不会影响十个命令限制的限制)。撤消/重做删除或插入文本时,当然必须在执行操作之前将光标放在适当的位置。如果用户在不执行任何光标移动或更正(退格)的情况下键入多个字符,则在应用撤消/重做时通常会将这些字符视为单个单元。

答案 3 :(得分:1)

祝贺包含undo / redo。这是任何编辑器的一个很棒的功能。它仍然会变得棘手。这里有一些想法(所有的挥手,没有代码)。

我建议您了解Command Design Pattern。你想要做的是设计一个'Command'类,其中一个实例可以“执行”一个命令(比如插入字母'A'),以及“撤消”本身。

当用户调用某个命令(比如添加字母'A')时,你'新'命令,定义其“Do”以插入'A',同时定义其“Undo”以删除A,然后添加它在撤消列表的顶部,然后“执行”。

不要将你的地址仅限制在10.为什么不把它设为无限?

无论您使用哪种结构来制作可撤销命令列表,通常的行为是,如果您已撤消某个级别,然后在该点开始编辑,则应丢弃当前级别之上的所有重做

答案 4 :(得分:0)

对于您希望能够撤消的每个操作(即推测插入,退格和del,但不是光标移动),我们可以列出“撤消”过程:

  • insert - &gt;将光标定位在该角色并发出del
  • del - &gt;将光标定位在以下字符并发出插入
  • 退格 - &gt;将光标定位在以下字符并发出插入

不幸的是,你的游标使用指针,当你撤消删除/退格时,新分配的CNode可能与之前的地址不同,这可能会使尝试使用该指针地址的先前撤消步骤无效。选项包括:

  • 一些额外的数据结构,用于跟踪哪些指针以这种方式失效,如果重新创建相应的元素(痛苦),则用新值重新填充它们

  • 找到一种更确定的方法来在CNode列表中找到正确的索引

    • 文档的绝对索引(但可能难以计算并且移动缓慢)
    • 将光标移动保存在撤消历史记录中
      • 混合10个实质性编辑(del,backspace,insert)和任意数量的散布光标移动需要一个动态大小的容器,你将携带相当多的“冗长的包袱”
      • 你可以在每个实质性编辑的固定大小的列表中挂起每个元素的动态光标移动列表(尽管不是更好)

(因为你的Cnode列表看起来没有双重链接,所以我不知道如何通过标题元素中的“文档”没有非常痛苦的重复向左移动......?)

在整理出这个索引/光标移动问题后,您必须在:

之间做出决定
  • 每次操作后,使用deque保存撤消信息:

    结构历史 {     撤消操作的指示器(例如enum Op {left,right,insert,del ...})     仅用于插入操作:对象值 }

    然后有一些on-undo处理函数读取这些历史记录并协调它们描述的操作,或者

  • 执行操作时,将函数对象推送到对deque和redo操作进行编码的deque上(对Cursor对象方法的调用),以便实际执行撤消或重做操作只涉及执行该对象(即撤消/重做操作是在编辑时分配的“黑匣子”)/这是更优雅和灵活,但可能不太熟悉初级/中级程序员所以可能更难走吧boost库具有良好的支持功能。

答案 5 :(得分:0)

我同意其他人在执行“执行”命令时捕获可撤销命令。

我还建议定期处理列表并组合撤消命令。

例如,如果您的撤消命令是:

删除A,删除B,删除C,左光标,左光标,右光标,左光标。

将其转换为仅

删除“ABC”,左光标(2)。

这样,当用户撤消操作时,他们看不到每次按键击中。相反,撤消发生在逻辑组中。