File: Stack.h

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (113 lines) | stat: -rw-r--r-- 2,558 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#pragma once

#include "debugging/debugging.h"
#include <list>
#include "Operation.h"

namespace undo
{

/** 
 * greebo: The UndoSystem keeps track of Undoable and Redoable operations,
 * which are kept in a chain-like data structure.
 *
 * Each named operation in this stack contains a snapshot of the undoable objects
 * that have been touched between start() and finish().
 *
 * On start() a new Operation is allocated  and on save(IUndoable) 
 * the IUndoable's memento is actually stored within the allocated Operation. 
 * The method finish() commits the operation to the stack.
 */
class UndoStack
{
private:
	// The list of Operations that can be undone
	//! Note: using std::list instead of vector/deque, to avoid copying of undos
    std::list<Operation::Ptr> _stack;

	// The pending undo operation (will be committed on finish, if not empty)
    Operation::Ptr _pending;

public:

	bool empty() const
	{
		return _stack.empty();
	}

	std::size_t size() const
	{
		return _stack.size();
	}

	const Operation::Ptr& back() const
	{
		return _stack.back();
	}

	const Operation::Ptr& front() const
	{
		return _stack.front();
	}

	void pop_front()
	{
		_stack.pop_front();
	}

	void pop_back()
	{
		_stack.pop_back();
	}

	void clear()
	{
		_stack.clear();
	}

	// Allocate a new Operation to work with
	void start(const std::string& command)
	{
		// When starting an operation, we create one and declare it as pending
		// It will not be added to the stack, it still might end up empty
		// We will also replace any previously pending operation with the new one
		// even though it should not happen by design
		ASSERT_MESSAGE(!_pending, "undo operation already started");

		_pending = std::make_shared<Operation>(command);
	}

    void cancel()
    {
        // discard the pending operation without storing it in the stack
        _pending.reset();
    }

	// Finish the current undo operation
	bool finish(const std::string& command)
	{
		if (!_pending || _pending->empty())
		{
			// The started operation has not been filled with any data
			// so just discard it without doing anything
			_pending.reset();
			return false;
		}
		
		// Rename the last undo operation (it may be "unnamed" till now)
        _pending->setName(command);

        // Move the pending operation into its place
        _stack.emplace_back(std::move(_pending));
		return true;
	}

    // Store an Undoable's state into the active operation
    void save(IUndoable& undoable)
    {
        assert(_pending);
        _pending->save(undoable);
    }
};

} // namespace