1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4 
5 #include "uiundomanager.h"
6 
7 #if VSTGUI_LIVE_EDITING
8 
9 #include "iaction.h"
10 #include <string>
11 
12 namespace VSTGUI {
13 
14 //-----------------------------------------------------------------------------
15 class UndoStackTop : public IAction
16 {
17 public:
18 	UTF8StringPtr getName () override { return nullptr; }
19 	void perform () override {}
20 	void undo () override {}
21 };
22 
23 //----------------------------------------------------------------------------------------------------
24 class UIGroupAction : public IAction, public std::list<IAction*>
25 {
26 public:
27 	static void doPerform (IAction* action) { action->perform (); }
28 	static void doUndo (IAction* action) { action->undo (); }
29 	static void doDelete (IAction* action) { delete action; }
30 
31 	UIGroupAction (UTF8StringPtr name) : name (name) {}
32 	~UIGroupAction () override
33 	{
34 		std::for_each (begin (), end (), doDelete);
35 	}
36 
37 	UTF8StringPtr getName () override { return name.c_str (); }
38 
39 	void perform () override
40 	{
41 		std::for_each (begin (), end (), doPerform);
42 	}
43 
44 	void undo () override
45 	{
46 		std::for_each (rbegin (), rend (), doUndo);
47 	}
48 
49 protected:
50 	std::string name;
51 };
52 
53 //----------------------------------------------------------------------------------------------------
54 UIUndoManager::UIUndoManager ()
55 {
56 	emplace_back (new UndoStackTop);
57 	position = begin ();
58 	savePosition = begin ();
59 }
60 
61 //----------------------------------------------------------------------------------------------------
62 UIUndoManager::~UIUndoManager ()
63 {
64 	std::for_each (begin (), end (), [] (IAction* action) { delete action; });
65 }
66 
67 //----------------------------------------------------------------------------------------------------
68 void UIUndoManager::pushAndPerform (IAction* action)
69 {
70 	if (groupQueue.empty () == false)
71 	{
72 		groupQueue.back ()->emplace_back (action);
73 		return;
74 	}
75 	if (position != end ())
76 	{
77 		position++;
78 		iterator oldStack = position;
79 		while (position != end ())
80 		{
81 			if (position == savePosition)
82 				savePosition = end ();
83 			delete (*position);
84 			position++;
85 		}
86 		erase (oldStack, end ());
87 	}
88 	emplace_back (action);
89 	position = end ();
90 	position--;
91 	action->perform ();
92 	forEachListener ([] (IUIUndoManagerListener* l) { l->onUndoManagerChange (); });
93 }
94 
95 //----------------------------------------------------------------------------------------------------
96 void UIUndoManager::performUndo ()
97 {
98 	if (position != end () && position != begin ())
99 	{
100 		(*position)->undo ();
101 		position--;
102 		forEachListener ([] (IUIUndoManagerListener* l) { l->onUndoManagerChange (); });
103 	}
104 }
105 
106 //----------------------------------------------------------------------------------------------------
107 void UIUndoManager::performRedo ()
108 {
109 	if (position != end ())
110 	{
111 		position++;
112 		if (position != end ())
113 		{
114 			(*position)->perform ();
115 			forEachListener ([] (IUIUndoManagerListener* l) { l->onUndoManagerChange (); });
116 		}
117 	}
118 }
119 
120 //----------------------------------------------------------------------------------------------------
121 bool UIUndoManager::canUndo ()
122 {
123 	return (position != end () && position != begin ());
124 }
125 
126 //----------------------------------------------------------------------------------------------------
127 bool UIUndoManager::canRedo ()
128 {
129 	if (position == end () && position != begin ())
130 		return false;
131 	position++;
132 	bool result = (position != end ());
133 	position--;
134 	return result;
135 }
136 
137 //----------------------------------------------------------------------------------------------------
138 UTF8StringPtr UIUndoManager::getUndoName ()
139 {
140 	if (position != end () && position != begin ())
141 		return (*position)->getName ();
142 	return nullptr;
143 }
144 
145 //----------------------------------------------------------------------------------------------------
146 UTF8StringPtr UIUndoManager::getRedoName ()
147 {
148 	UTF8StringPtr redoName = nullptr;
149 	if (position != end ())
150 	{
151 		position++;
152 		if (position != end ())
153 			redoName = (*position)->getName ();
154 		position--;
155 	}
156 	return redoName;
157 }
158 
159 //----------------------------------------------------------------------------------------------------
160 void UIUndoManager::clear ()
161 {
162 	std::for_each (begin (), end (), [] (IAction* action) { delete action; });
163 	std::list<IAction*>::clear ();
164 	emplace_back (new UndoStackTop);
165 	position = end ();
166 	savePosition = begin ();
167 	forEachListener ([] (IUIUndoManagerListener* l) { l->onUndoManagerChange (); });
168 }
169 
170 //----------------------------------------------------------------------------------------------------
171 void UIUndoManager::startGroupAction (UTF8StringPtr name)
172 {
173 	UIGroupAction* action = new UIGroupAction (name);
174 	groupQueue.emplace_back (action);
175 }
176 
177 //----------------------------------------------------------------------------------------------------
178 void UIUndoManager::endGroupAction ()
179 {
180 	UIGroupAction* action = groupQueue.back ();
181 	if (action)
182 	{
183 		groupQueue.pop_back ();
184 		if (action->empty ())
185 		{
186 			delete action;
187 		}
188 		else
189 		{
190 			pushAndPerform (action);
191 		}
192 	}
193 }
194 
195 //----------------------------------------------------------------------------------------------------
196 void UIUndoManager::cancelGroupAction ()
197 {
198 	UIGroupAction* action = groupQueue.back ();
199 	if (action)
200 	{
201 		groupQueue.pop_back ();
202 		delete action;
203 	}
204 }
205 
206 //----------------------------------------------------------------------------------------------------
207 void UIUndoManager::markSavePosition ()
208 {
209 	savePosition = position;
210 }
211 
212 //----------------------------------------------------------------------------------------------------
213 bool UIUndoManager::isSavePosition () const
214 {
215 	return savePosition == position;
216 }
217 
218 
219 } // VSTGUI
220 
221 #endif // VSTGUI_LIVE_EDITING
222