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