Need help writing a cancel / redo function for a text editor program

I wrote a C ++ text editor program that has simple commands: LEFT, RIGHT, HOME, END, BACKSPACE, DELETE, INSERT, and now I need to execute the UNDO and REDO functions. In my program, the user should be able to cancel no more than the last ten commands. I would like to use a vector implementation for this, but I have no idea how to set this. I am not sure how to keep the cursor position and character in a vector. Can anyone help?

#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 
+2
c ++
Oct 05 '10 at 2:11
source share
6 answers

You want to use deque so you can add and remove front or back; when adding a command, add it back when cancel deletion from the back, and when you reach 11 teams, delete one of them in front.

+3
Oct 05 2018-10-10T00:
source share

Take a look at the Memento Design Pattern as well as in GOF

It exists for this very specific requirement. You may need to use it in combination with other design patterns (e.g. Command, Iterator, FlyWeight, etc.)

Appointment Memento

Without breaking encapsulation, capture and externalize the object's internal state so that the object can be restored to that state later.

Team assignment

Encapsulate the request as an object, thereby allowing you to parameterize clients with different requests, queue or log requests, and support for canceled operations.

+2
Oct 05 2018-10-10T00:
source share

Some other things to consider:

In general, you do not want to apply undo / redo to cursor movements (i.e., they will not affect the limitation of ten command limits). When canceling / re-deleting or inserting text, of course, before performing the operation, you must place the cursor in the right place. If the user enters several characters without any cursor movements or corrections (backspace), they are usually treated as a whole when applying undo / redo.

+1
Oct 05 2018-10-10T00:
source share

Congratulations on enabling undo / redo. This is a great feature in any editor. It can get complicated, still. Here are some thoughts for you (all manual, no code).

I recommend that you familiarize yourself with the team design pattern . What you want to do is create a class “Command”, an instance of which can “execute” one command (for example, insert the letter “A”), as well as “Cancel”.

When the user invokes some command (for example, to add the letter "A"), you are "new", the command "Define" to insert "A" to insert "A", also define it "Cancel" to delete A, then add it at the top of your cancellation list, and then do.

Do not limit your cancellations to only 10. Why not make it endless?

Regardless of the structure you use to compile the list of canceled commands, the usual behavior is that if you cancel a level and then start editing at that point, then all repetitions above the current level should be discarded.

+1
05 Oct '10 at 3:29
source share

For each operation that you want to cancel (i.e. supposedly insert, return, and move, but not move the cursor), we can list the cancellation procedure:

  • insert → place the cursor on this character and type del
  • del → place the cursor on the next character and type in the insert
  • backspace → place the cursor on the next character and paste

Unfortunately, your cursors use pointers, and when you cancel deletion / backspace, the newly assigned CNode may not be at the same address as before, which may invalidate the previous undo step trying to use this pointer address. Options include:

  • some additional data structures to keep track of which pointers were invalid in this way and re-populate them with new values ​​if the corresponding elements are recreated (painful)

  • find a more deterministic way to find the correct index in the CNode list

    • absolute index in the document (but it can be difficult to calculate and slow down the transition)
    • save cursor movement in cancel history
      • mixing 10 basic changes (del, backspace, insert) with an arbitrary number of alternating cursor movements requires a container with dynamic size, and you will carry quite a lot of “detailed baggage”
      • you can hang a dynamic list of cursor movements from each item in a fixed-size list of each main edit (not much better)

(As is, your Cnode list is not displayed with a double link, so I don’t see how you can move to the left without very painful repetition through the “document” from the title element ...?)

After sorting this problem with indexing / moving the cursor, you should decide between:

  • after each operation, use deque to save the cancellation information:

    struct History {indicator of which operation is canceled (for example, enum Op {left, right, insert, del ...}) only for insert operations: object value}

    that is, some cancellation processing function that reads these history records and coordinates the operations that they describe, or

  • when the operation is performed, click on the function object on your deque, which encodes the cancel and redo operations (in terms of calls to the methods of the Cursor object), so in fact the execution of the cancel or redo operation simply involves the execution of this object (i.e. the black box scattered during editing) / it is more elegant and flexible, but probably less familiar to beginners / intermediate programmers, so it can be more difficult to get the right one. The boost library has good support functions for this.

0
Oct 05 '10 at 2:52
source share

I agree with the rest about capturing canceled commands when the do command is executed.

I also suggest periodically reviewing the list and combining undo commands.

For example, if your cancel commands:

Delete A, Delete B, Delete C, Left Cursor, Left Cursor, Right Cursor, Left Cursor.

Transform it easy

Delete "ABC", left cursor (2).

Thus, when the user cancels the actions, they do not see every keystroke. Instead, cancellation occurs in logical groups.

0
Oct 05 '10 at 4:26
source share



All Articles