xref: /haiku/src/kits/interface/textview_support/UndoBuffer.cpp (revision 9d6d3fcf5fe8308cd020cecf89dede440346f8c4)
1 /*
2  * Copyright 2003-2006, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stefano Ceccherini (burton666@libero.it)
7  */
8 
9 //!	_BUndoBuffer_ and its subclasses handle different types of Undo operations.
10 
11 
12 #include "UndoBuffer.h"
13 #include "utf8_functions.h"
14 
15 #include <Clipboard.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 
22 // TODO: properly document this file
23 
24 
25 //	#pragma mark - _BUndoBuffer_
26 
27 
28 _BUndoBuffer_::_BUndoBuffer_(BTextView *textView, undo_state state)
29 	:
30 	fTextView(textView),
31 	fTextData(NULL),
32 	fRunArray(NULL),
33 	fRunArrayLength(0),
34 	fRedo(false),
35 	fState(state)
36 {
37 	fTextView->GetSelection(&fStart, &fEnd);
38 	fTextLength = fEnd - fStart;
39 
40 	fTextData = (char *)malloc(fTextLength);
41 	memcpy(fTextData, fTextView->Text() + fStart, fTextLength);
42 
43 	if (fTextView->IsStylable())
44 		fRunArray =  fTextView->RunArray(fStart, fEnd, &fRunArrayLength);
45 }
46 
47 
48 _BUndoBuffer_::~_BUndoBuffer_()
49 {
50 	free(fTextData);
51 	free(fRunArray);
52 }
53 
54 
55 void
56 _BUndoBuffer_::Undo(BClipboard *clipboard)
57 {
58 	fRedo ? RedoSelf(clipboard) : UndoSelf(clipboard);
59 
60 	fRedo = !fRedo;
61 }
62 
63 
64 undo_state
65 _BUndoBuffer_::State(bool *redo)
66 {
67 	*redo = fRedo;
68 
69 	return fState;
70 }
71 
72 
73 void
74 _BUndoBuffer_::UndoSelf(BClipboard *clipboard)
75 {
76 	fTextView->Select(fStart, fStart);
77 	fTextView->Insert(fTextData, fTextLength, fRunArray);
78 	fTextView->Select(fStart, fStart);
79 }
80 
81 
82 void
83 _BUndoBuffer_::RedoSelf(BClipboard *clipboard)
84 {
85 }
86 
87 
88 //	#pragma mark - _BCutUndoBuffer_
89 
90 
91 _BCutUndoBuffer_::_BCutUndoBuffer_(BTextView *textView)
92 	: _BUndoBuffer_(textView, B_UNDO_CUT)
93 {
94 }
95 
96 
97 _BCutUndoBuffer_::~_BCutUndoBuffer_()
98 {
99 }
100 
101 
102 void
103 _BCutUndoBuffer_::RedoSelf(BClipboard *clipboard)
104 {
105 	BMessage *clip = NULL;
106 
107 	fTextView->Select(fStart, fStart);
108 	fTextView->Delete(fStart, fEnd);
109 	if (clipboard->Lock()) {
110 		clipboard->Clear();
111 		if ((clip = clipboard->Data())) {
112 			clip->AddData("text/plain", B_MIME_TYPE, fTextData, fTextLength);
113 			if (fRunArray)
114 				clip->AddData("application/x-vnd.Be-text_run_array", B_MIME_TYPE,
115 					fRunArray, fRunArrayLength);
116 			clipboard->Commit();
117 		}
118 		clipboard->Unlock();
119 	}
120 }
121 
122 
123 //	#pragma mark - _BPasteUndoBuffer_
124 
125 
126 _BPasteUndoBuffer_::_BPasteUndoBuffer_(BTextView *textView, const char *text,
127 		int32 textLen, text_run_array *runArray, int32 runArrayLen)
128 	: _BUndoBuffer_(textView, B_UNDO_PASTE),
129 	fPasteText(NULL),
130 	fPasteTextLength(textLen),
131 	fPasteRunArray(NULL),
132 	fPasteRunArrayLength(0)
133 {
134 	fPasteText = (char *)malloc(fPasteTextLength);
135 	memcpy(fPasteText, text, fPasteTextLength);
136 
137 	if (runArray) {
138 		fPasteRunArray = (text_run_array *)malloc(runArrayLen);
139 		fPasteRunArrayLength = runArrayLen;
140 		memcpy(fPasteRunArray, runArray, runArrayLen);
141 	}
142 }
143 
144 
145 _BPasteUndoBuffer_::~_BPasteUndoBuffer_()
146 {
147 	free(fPasteText);
148 	free(fPasteRunArray);
149 }
150 
151 
152 void
153 _BPasteUndoBuffer_::UndoSelf(BClipboard *clipboard)
154 {
155 	fTextView->Select(fStart, fStart);
156 	fTextView->Delete(fStart, fStart + fPasteTextLength);
157 	fTextView->Insert(fTextData, fTextLength, fRunArray);
158 	fTextView->Select(fStart, fEnd);
159 }
160 
161 
162 void
163 _BPasteUndoBuffer_::RedoSelf(BClipboard *clipboard)
164 {
165 	fTextView->Select(fStart, fStart);
166 	fTextView->Delete(fStart, fEnd);
167 	fTextView->Insert(fPasteText, fPasteTextLength, fPasteRunArray);
168 	fTextView->Select(fStart + fPasteTextLength, fStart + fPasteTextLength);
169 }
170 
171 
172 //	#pragma mark - _BClearUndoBuffer_
173 
174 
175 _BClearUndoBuffer_::_BClearUndoBuffer_(BTextView *textView)
176 	: _BUndoBuffer_(textView, B_UNDO_CLEAR)
177 {
178 }
179 
180 
181 _BClearUndoBuffer_::~_BClearUndoBuffer_()
182 {
183 }
184 
185 
186 void
187 _BClearUndoBuffer_::RedoSelf(BClipboard *clipboard)
188 {
189 	fTextView->Select(fStart, fStart);
190 	fTextView->Delete(fStart, fEnd);
191 }
192 
193 
194 //	#pragma mark - _BDropUndoBuffer_
195 
196 
197 _BDropUndoBuffer_::_BDropUndoBuffer_(BTextView *textView, char const *text,
198 		int32 textLen, text_run_array *runArray, int32 runArrayLen,
199 		int32 location, bool internalDrop)
200 	: _BUndoBuffer_(textView, B_UNDO_DROP),
201 	fDropText(NULL),
202 	fDropTextLength(textLen),
203 	fDropRunArray(NULL)
204 {
205 	fInternalDrop = internalDrop;
206 	fDropLocation = location;
207 
208 	fDropText = (char *)malloc(fDropTextLength);
209 	memcpy(fDropText, text, fDropTextLength);
210 
211 	if (runArray) {
212 		fDropRunArray = (text_run_array *)malloc(runArrayLen);
213 		fDropRunArrayLength = runArrayLen;
214 		memcpy(fDropRunArray, runArray, runArrayLen);
215 	}
216 
217 	if (fInternalDrop && fDropLocation >= fEnd)
218 		fDropLocation -= fDropTextLength;
219 }
220 
221 
222 _BDropUndoBuffer_::~_BDropUndoBuffer_()
223 {
224 	free(fDropText);
225 	free(fDropRunArray);
226 }
227 
228 
229 void
230 _BDropUndoBuffer_::UndoSelf(BClipboard *)
231 {
232 	fTextView->Select(fDropLocation, fDropLocation);
233 	fTextView->Delete(fDropLocation, fDropLocation + fDropTextLength);
234 	if (fInternalDrop) {
235 		fTextView->Select(fStart, fStart);
236 		fTextView->Insert(fTextData, fTextLength, fRunArray);
237 	}
238 	fTextView->Select(fStart, fEnd);
239 }
240 
241 
242 void
243 _BDropUndoBuffer_::RedoSelf(BClipboard *)
244 {
245 	if (fInternalDrop) {
246 		fTextView->Select(fStart, fStart);
247 		fTextView->Delete(fStart, fEnd);
248 	}
249 	fTextView->Select(fDropLocation, fDropLocation);
250 	fTextView->Insert(fDropText, fDropTextLength, fDropRunArray);
251 	fTextView->Select(fDropLocation, fDropLocation + fDropTextLength);
252 }
253 
254 
255 //	#pragma mark - _BTypingUndoBuffer_
256 
257 
258 _BTypingUndoBuffer_::_BTypingUndoBuffer_(BTextView *textView)
259 	: _BUndoBuffer_(textView, B_UNDO_TYPING),
260 	fTypedText(NULL),
261 	fTypedStart(fStart),
262 	fTypedEnd(fEnd),
263 	fUndone(0)
264 {
265 }
266 
267 
268 _BTypingUndoBuffer_::~_BTypingUndoBuffer_()
269 {
270 	free(fTypedText);
271 }
272 
273 
274 void
275 _BTypingUndoBuffer_::UndoSelf(BClipboard *clipboard)
276 {
277 	int32 len = fTypedEnd - fTypedStart;
278 
279 	free(fTypedText);
280 	fTypedText = NULL;
281 	fTypedText = (char *)malloc(len);
282 	memcpy(fTypedText, fTextView->Text() + fTypedStart, len);
283 
284 	fTextView->Select(fTypedStart, fTypedStart);
285 	fTextView->Delete(fTypedStart, fTypedEnd);
286 	fTextView->Insert(fTextData, fTextLength);
287 	fTextView->Select(fStart, fEnd);
288 	fUndone++;
289 }
290 
291 
292 void
293 _BTypingUndoBuffer_::RedoSelf(BClipboard *clipboard)
294 {
295 	fTextView->Select(fTypedStart, fTypedStart);
296 	fTextView->Delete(fTypedStart, fTypedStart + fTextLength);
297 	fTextView->Insert(fTypedText, fTypedEnd - fTypedStart);
298 	fUndone--;
299 }
300 
301 
302 void
303 _BTypingUndoBuffer_::InputCharacter(int32 len)
304 {
305 	int32 start, end;
306 	fTextView->GetSelection(&start, &end);
307 
308 	if (start != fTypedEnd || end != fTypedEnd)
309 		Reset();
310 
311 	fTypedEnd += len;
312 }
313 
314 
315 void
316 _BTypingUndoBuffer_::Reset()
317 {
318 	free(fTextData);
319 	fTextView->GetSelection(&fStart, &fEnd);
320 	fTextLength = fEnd - fStart;
321 	fTypedStart = fStart;
322 	fTypedEnd = fStart;
323 
324 	fTextData = (char *)malloc(fTextLength);
325 	memcpy(fTextData, fTextView->Text() + fStart, fTextLength);
326 
327 	free(fTypedText);
328 	fTypedText = NULL;
329 	fRedo = false;
330 	fUndone = 0;
331 }
332 
333 
334 void
335 _BTypingUndoBuffer_::BackwardErase()
336 {
337 	int32 start, end;
338 	fTextView->GetSelection(&start, &end);
339 
340 	const char *text = fTextView->Text();
341 	int32 charLen = UTF8PreviousCharLen(text + start, text);
342 
343 	if (start != fTypedEnd || end != fTypedEnd) {
344 		Reset();
345 		// if we've got a selection, we're already done
346 		if (start != end)
347 			return;
348 	}
349 
350 	char *buffer = (char *)malloc(fTextLength + charLen);
351 	memcpy(buffer + charLen, fTextData, fTextLength);
352 
353 	fTypedStart = start - charLen;
354 	start = fTypedStart;
355 	for (int32 x = 0; x < charLen; x++)
356 		buffer[x] = fTextView->ByteAt(start + x);
357 	free(fTextData);
358 	fTextData = buffer;
359 
360 	fTextLength += charLen;
361 	fTypedEnd -= charLen;
362 }
363 
364 
365 void
366 _BTypingUndoBuffer_::ForwardErase()
367 {
368 	// TODO: Cleanup
369 	int32 start, end;
370 
371 	fTextView->GetSelection(&start, &end);
372 
373 	int32 charLen = UTF8NextCharLen(fTextView->Text() + start);
374 
375 	if (start != fTypedEnd || end != fTypedEnd || fUndone > 0) {
376 		Reset();
377 		// if we've got a selection, we're already done
378 		if (fStart == fEnd) {
379 			free(fTextData);
380 			fTextLength = charLen;
381 			fTextData = (char *)malloc(fTextLength);
382 
383 			// store the erased character
384 			for (int32 x = 0; x < charLen; x++)
385 				fTextData[x] = fTextView->ByteAt(start + x);
386 		}
387 	} else {
388 		// Here we need to store the erased text, so we get the text that it's
389 		// already in the buffer, and we add the erased character.
390 		// a realloc + memmove would maybe be cleaner, but that way we spare a
391 		// copy (malloc + memcpy vs realloc + memmove).
392 
393 		int32 newLength = fTextLength + charLen;
394 		char *buffer = (char *)malloc(newLength);
395 
396 		// copy the already stored data
397 		memcpy(buffer, fTextData, fTextLength);
398 
399 		if (fTextLength < newLength) {
400 			// store the erased character
401 			for (int32 x = 0; x < charLen; x++)
402 				buffer[fTextLength] = fTextView->ByteAt(start + x);
403 		}
404 
405 		fTextLength = newLength;
406 		free(fTextData);
407 		fTextData = buffer;
408 	}
409 }
410