xref: /haiku/src/apps/mediaplayer/support/CommandStack.cpp (revision cc2fbed22d7db3b90ece4147cf5933a599fe39ee)
1*cc2fbed2SStephan Aßmus /*
2*cc2fbed2SStephan Aßmus  * Copyright 2006-2007, Haiku.
3*cc2fbed2SStephan Aßmus  * Distributed under the terms of the MIT License.
4*cc2fbed2SStephan Aßmus  *
5*cc2fbed2SStephan Aßmus  * Authors:
6*cc2fbed2SStephan Aßmus  *		Stephan Aßmus <superstippi@gmx.de>
7*cc2fbed2SStephan Aßmus  */
8*cc2fbed2SStephan Aßmus 
9*cc2fbed2SStephan Aßmus #include "CommandStack.h"
10*cc2fbed2SStephan Aßmus 
11*cc2fbed2SStephan Aßmus #include <stdio.h>
12*cc2fbed2SStephan Aßmus #include <string.h>
13*cc2fbed2SStephan Aßmus 
14*cc2fbed2SStephan Aßmus #include <Locker.h>
15*cc2fbed2SStephan Aßmus #include <String.h>
16*cc2fbed2SStephan Aßmus 
17*cc2fbed2SStephan Aßmus #include "Command.h"
18*cc2fbed2SStephan Aßmus #include "RWLocker.h"
19*cc2fbed2SStephan Aßmus 
20*cc2fbed2SStephan Aßmus // constructor
CommandStack(RWLocker * locker)21*cc2fbed2SStephan Aßmus CommandStack::CommandStack(RWLocker* locker)
22*cc2fbed2SStephan Aßmus 	: Notifier()
23*cc2fbed2SStephan Aßmus 	, fLocker(locker)
24*cc2fbed2SStephan Aßmus 	, fSavedCommand(NULL)
25*cc2fbed2SStephan Aßmus {
26*cc2fbed2SStephan Aßmus }
27*cc2fbed2SStephan Aßmus 
28*cc2fbed2SStephan Aßmus // destructor
~CommandStack()29*cc2fbed2SStephan Aßmus CommandStack::~CommandStack()
30*cc2fbed2SStephan Aßmus {
31*cc2fbed2SStephan Aßmus 	Clear();
32*cc2fbed2SStephan Aßmus }
33*cc2fbed2SStephan Aßmus 
34*cc2fbed2SStephan Aßmus // Perform
35*cc2fbed2SStephan Aßmus status_t
Perform(Command * command)36*cc2fbed2SStephan Aßmus CommandStack::Perform(Command* command)
37*cc2fbed2SStephan Aßmus {
38*cc2fbed2SStephan Aßmus 	if (!fLocker->WriteLock())
39*cc2fbed2SStephan Aßmus 		return B_ERROR;
40*cc2fbed2SStephan Aßmus 
41*cc2fbed2SStephan Aßmus 	status_t ret = command ? B_OK : B_BAD_VALUE;
42*cc2fbed2SStephan Aßmus 	if (ret == B_OK)
43*cc2fbed2SStephan Aßmus 		ret = command->InitCheck();
44*cc2fbed2SStephan Aßmus 
45*cc2fbed2SStephan Aßmus 	if (ret == B_OK)
46*cc2fbed2SStephan Aßmus 		ret = command->Perform();
47*cc2fbed2SStephan Aßmus 
48*cc2fbed2SStephan Aßmus 	if (ret == B_OK)
49*cc2fbed2SStephan Aßmus 		ret = _AddCommand(command);
50*cc2fbed2SStephan Aßmus 
51*cc2fbed2SStephan Aßmus 	if (ret != B_OK) {
52*cc2fbed2SStephan Aßmus 		// no one else feels responsible...
53*cc2fbed2SStephan Aßmus 		delete command;
54*cc2fbed2SStephan Aßmus 	}
55*cc2fbed2SStephan Aßmus 	fLocker->WriteUnlock();
56*cc2fbed2SStephan Aßmus 
57*cc2fbed2SStephan Aßmus 	Notify();
58*cc2fbed2SStephan Aßmus 
59*cc2fbed2SStephan Aßmus 	return ret;
60*cc2fbed2SStephan Aßmus }
61*cc2fbed2SStephan Aßmus 
62*cc2fbed2SStephan Aßmus // Undo
63*cc2fbed2SStephan Aßmus status_t
Undo()64*cc2fbed2SStephan Aßmus CommandStack::Undo()
65*cc2fbed2SStephan Aßmus {
66*cc2fbed2SStephan Aßmus 	if (!fLocker->WriteLock())
67*cc2fbed2SStephan Aßmus 		return B_ERROR;
68*cc2fbed2SStephan Aßmus 
69*cc2fbed2SStephan Aßmus 	status_t status = B_ERROR;
70*cc2fbed2SStephan Aßmus 	if (!fUndoHistory.empty()) {
71*cc2fbed2SStephan Aßmus 		Command* command = fUndoHistory.top();
72*cc2fbed2SStephan Aßmus 		fUndoHistory.pop();
73*cc2fbed2SStephan Aßmus 		status = command->Undo();
74*cc2fbed2SStephan Aßmus 		if (status == B_OK)
75*cc2fbed2SStephan Aßmus 			fRedoHistory.push(command);
76*cc2fbed2SStephan Aßmus 		else
77*cc2fbed2SStephan Aßmus 			fUndoHistory.push(command);
78*cc2fbed2SStephan Aßmus 	}
79*cc2fbed2SStephan Aßmus 	fLocker->WriteUnlock();
80*cc2fbed2SStephan Aßmus 
81*cc2fbed2SStephan Aßmus 	Notify();
82*cc2fbed2SStephan Aßmus 
83*cc2fbed2SStephan Aßmus 	return status;
84*cc2fbed2SStephan Aßmus }
85*cc2fbed2SStephan Aßmus 
86*cc2fbed2SStephan Aßmus // Redo
87*cc2fbed2SStephan Aßmus status_t
Redo()88*cc2fbed2SStephan Aßmus CommandStack::Redo()
89*cc2fbed2SStephan Aßmus {
90*cc2fbed2SStephan Aßmus 	if (!fLocker->WriteLock())
91*cc2fbed2SStephan Aßmus 		return B_ERROR;
92*cc2fbed2SStephan Aßmus 
93*cc2fbed2SStephan Aßmus 	status_t status = B_ERROR;
94*cc2fbed2SStephan Aßmus 	if (!fRedoHistory.empty()) {
95*cc2fbed2SStephan Aßmus 		Command* command = fRedoHistory.top();
96*cc2fbed2SStephan Aßmus 		fRedoHistory.pop();
97*cc2fbed2SStephan Aßmus 		status = command->Redo();
98*cc2fbed2SStephan Aßmus 		if (status == B_OK)
99*cc2fbed2SStephan Aßmus 			fUndoHistory.push(command);
100*cc2fbed2SStephan Aßmus 		else
101*cc2fbed2SStephan Aßmus 			fRedoHistory.push(command);
102*cc2fbed2SStephan Aßmus 	}
103*cc2fbed2SStephan Aßmus 	fLocker->WriteUnlock();
104*cc2fbed2SStephan Aßmus 
105*cc2fbed2SStephan Aßmus 	Notify();
106*cc2fbed2SStephan Aßmus 
107*cc2fbed2SStephan Aßmus 	return status;
108*cc2fbed2SStephan Aßmus }
109*cc2fbed2SStephan Aßmus 
110*cc2fbed2SStephan Aßmus // UndoName
111*cc2fbed2SStephan Aßmus bool
GetUndoName(BString & name)112*cc2fbed2SStephan Aßmus CommandStack::GetUndoName(BString& name)
113*cc2fbed2SStephan Aßmus {
114*cc2fbed2SStephan Aßmus 	bool success = false;
115*cc2fbed2SStephan Aßmus 	if (fLocker->ReadLock()) {
116*cc2fbed2SStephan Aßmus 		if (!fUndoHistory.empty()) {
117*cc2fbed2SStephan Aßmus 			name << " ";
118*cc2fbed2SStephan Aßmus 			fUndoHistory.top()->GetName(name);
119*cc2fbed2SStephan Aßmus 			success = true;
120*cc2fbed2SStephan Aßmus 		}
121*cc2fbed2SStephan Aßmus 		fLocker->ReadUnlock();
122*cc2fbed2SStephan Aßmus 	}
123*cc2fbed2SStephan Aßmus 	return success;
124*cc2fbed2SStephan Aßmus }
125*cc2fbed2SStephan Aßmus 
126*cc2fbed2SStephan Aßmus // RedoName
127*cc2fbed2SStephan Aßmus bool
GetRedoName(BString & name)128*cc2fbed2SStephan Aßmus CommandStack::GetRedoName(BString& name)
129*cc2fbed2SStephan Aßmus {
130*cc2fbed2SStephan Aßmus 	bool success = false;
131*cc2fbed2SStephan Aßmus 	if (fLocker->ReadLock()) {
132*cc2fbed2SStephan Aßmus 		if (!fRedoHistory.empty()) {
133*cc2fbed2SStephan Aßmus 			name << " ";
134*cc2fbed2SStephan Aßmus 			fRedoHistory.top()->GetName(name);
135*cc2fbed2SStephan Aßmus 			success = true;
136*cc2fbed2SStephan Aßmus 		}
137*cc2fbed2SStephan Aßmus 		fLocker->ReadUnlock();
138*cc2fbed2SStephan Aßmus 	}
139*cc2fbed2SStephan Aßmus 	return success;
140*cc2fbed2SStephan Aßmus }
141*cc2fbed2SStephan Aßmus 
142*cc2fbed2SStephan Aßmus // Clear
143*cc2fbed2SStephan Aßmus void
Clear()144*cc2fbed2SStephan Aßmus CommandStack::Clear()
145*cc2fbed2SStephan Aßmus {
146*cc2fbed2SStephan Aßmus 	if (fLocker->WriteLock()) {
147*cc2fbed2SStephan Aßmus 		while (!fUndoHistory.empty()) {
148*cc2fbed2SStephan Aßmus 			delete fUndoHistory.top();
149*cc2fbed2SStephan Aßmus 			fUndoHistory.pop();
150*cc2fbed2SStephan Aßmus 		}
151*cc2fbed2SStephan Aßmus 		while (!fRedoHistory.empty()) {
152*cc2fbed2SStephan Aßmus 			delete fRedoHistory.top();
153*cc2fbed2SStephan Aßmus 			fRedoHistory.pop();
154*cc2fbed2SStephan Aßmus 		}
155*cc2fbed2SStephan Aßmus 		fLocker->WriteUnlock();
156*cc2fbed2SStephan Aßmus 	}
157*cc2fbed2SStephan Aßmus 
158*cc2fbed2SStephan Aßmus 	Notify();
159*cc2fbed2SStephan Aßmus }
160*cc2fbed2SStephan Aßmus 
161*cc2fbed2SStephan Aßmus // Save
162*cc2fbed2SStephan Aßmus void
Save()163*cc2fbed2SStephan Aßmus CommandStack::Save()
164*cc2fbed2SStephan Aßmus {
165*cc2fbed2SStephan Aßmus 	if (fLocker->WriteLock()) {
166*cc2fbed2SStephan Aßmus 		if (!fUndoHistory.empty())
167*cc2fbed2SStephan Aßmus 			fSavedCommand = fUndoHistory.top();
168*cc2fbed2SStephan Aßmus 		fLocker->WriteUnlock();
169*cc2fbed2SStephan Aßmus 	}
170*cc2fbed2SStephan Aßmus 
171*cc2fbed2SStephan Aßmus 	Notify();
172*cc2fbed2SStephan Aßmus }
173*cc2fbed2SStephan Aßmus 
174*cc2fbed2SStephan Aßmus // IsSaved
175*cc2fbed2SStephan Aßmus bool
IsSaved()176*cc2fbed2SStephan Aßmus CommandStack::IsSaved()
177*cc2fbed2SStephan Aßmus {
178*cc2fbed2SStephan Aßmus 	bool saved = false;
179*cc2fbed2SStephan Aßmus 	if (fLocker->ReadLock()) {
180*cc2fbed2SStephan Aßmus 		saved = fUndoHistory.empty();
181*cc2fbed2SStephan Aßmus 		if (fSavedCommand && !saved) {
182*cc2fbed2SStephan Aßmus 			if (fSavedCommand == fUndoHistory.top())
183*cc2fbed2SStephan Aßmus 				saved = true;
184*cc2fbed2SStephan Aßmus 		}
185*cc2fbed2SStephan Aßmus 		fLocker->ReadUnlock();
186*cc2fbed2SStephan Aßmus 	}
187*cc2fbed2SStephan Aßmus 	return saved;
188*cc2fbed2SStephan Aßmus }
189*cc2fbed2SStephan Aßmus 
190*cc2fbed2SStephan Aßmus // #pragma mark -
191*cc2fbed2SStephan Aßmus 
192*cc2fbed2SStephan Aßmus // _AddCommand
193*cc2fbed2SStephan Aßmus status_t
_AddCommand(Command * command)194*cc2fbed2SStephan Aßmus CommandStack::_AddCommand(Command* command)
195*cc2fbed2SStephan Aßmus {
196*cc2fbed2SStephan Aßmus 	status_t status = B_OK;
197*cc2fbed2SStephan Aßmus 
198*cc2fbed2SStephan Aßmus 	bool add = true;
199*cc2fbed2SStephan Aßmus 	if (!fUndoHistory.empty()) {
200*cc2fbed2SStephan Aßmus 		// try to collapse commands to a single command
201*cc2fbed2SStephan Aßmus 		// or remove this and the previous command if
202*cc2fbed2SStephan Aßmus 		// they reverse each other
203*cc2fbed2SStephan Aßmus 		if (Command* top = fUndoHistory.top()) {
204*cc2fbed2SStephan Aßmus 			if (command->UndoesPrevious(top)) {
205*cc2fbed2SStephan Aßmus 				add = false;
206*cc2fbed2SStephan Aßmus 				fUndoHistory.pop();
207*cc2fbed2SStephan Aßmus 				delete top;
208*cc2fbed2SStephan Aßmus 				delete command;
209*cc2fbed2SStephan Aßmus 			} else if (top->CombineWithNext(command)) {
210*cc2fbed2SStephan Aßmus 				add = false;
211*cc2fbed2SStephan Aßmus 				delete command;
212*cc2fbed2SStephan Aßmus 				// after collapsing, the command might
213*cc2fbed2SStephan Aßmus 				// have changed it's mind about InitCheck()
214*cc2fbed2SStephan Aßmus 				// (the commands reversed each other)
215*cc2fbed2SStephan Aßmus 				if (top->InitCheck() < B_OK) {
216*cc2fbed2SStephan Aßmus 					fUndoHistory.pop();
217*cc2fbed2SStephan Aßmus 					delete top;
218*cc2fbed2SStephan Aßmus 				}
219*cc2fbed2SStephan Aßmus 			} else if (command->CombineWithPrevious(top)) {
220*cc2fbed2SStephan Aßmus 				fUndoHistory.pop();
221*cc2fbed2SStephan Aßmus 				delete top;
222*cc2fbed2SStephan Aßmus 				// after collapsing, the command might
223*cc2fbed2SStephan Aßmus 				// have changed it's mind about InitCheck()
224*cc2fbed2SStephan Aßmus 				// (the commands reversed each other)
225*cc2fbed2SStephan Aßmus 				if (command->InitCheck() < B_OK) {
226*cc2fbed2SStephan Aßmus 					delete command;
227*cc2fbed2SStephan Aßmus 					add = false;
228*cc2fbed2SStephan Aßmus 				}
229*cc2fbed2SStephan Aßmus 			}
230*cc2fbed2SStephan Aßmus 		}
231*cc2fbed2SStephan Aßmus 	}
232*cc2fbed2SStephan Aßmus 	if (add) {
233*cc2fbed2SStephan Aßmus 		try {
234*cc2fbed2SStephan Aßmus 			fUndoHistory.push(command);
235*cc2fbed2SStephan Aßmus 		} catch (...) {
236*cc2fbed2SStephan Aßmus 			status = B_ERROR;
237*cc2fbed2SStephan Aßmus 		}
238*cc2fbed2SStephan Aßmus 	}
239*cc2fbed2SStephan Aßmus 
240*cc2fbed2SStephan Aßmus 	if (status == B_OK) {
241*cc2fbed2SStephan Aßmus 		// the redo stack needs to be empty
242*cc2fbed2SStephan Aßmus 		// as soon as a command was added (also in case of collapsing)
243*cc2fbed2SStephan Aßmus 		while (!fRedoHistory.empty()) {
244*cc2fbed2SStephan Aßmus 			delete fRedoHistory.top();
245*cc2fbed2SStephan Aßmus 			fRedoHistory.pop();
246*cc2fbed2SStephan Aßmus 		}
247*cc2fbed2SStephan Aßmus 	}
248*cc2fbed2SStephan Aßmus 
249*cc2fbed2SStephan Aßmus 	return status;
250*cc2fbed2SStephan Aßmus }
251*cc2fbed2SStephan Aßmus 
252*cc2fbed2SStephan Aßmus 
253