xref: /haiku/src/kits/interface/textview_support/UndoBuffer.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 = (char*)malloc(len);
275 	memcpy(fTypedText, fTextView->Text() + fTypedStart, len);
276 
277 	fTextView->Select(fTypedStart, fTypedStart);
278 	fTextView->Delete(fTypedStart, fTypedEnd);
279 	fTextView->Insert(fTextData, fTextLength);
280 	fTextView->Select(fStart, fEnd);
281 	fUndone++;
282 }
283 
284 
285 void
286 BTextView::TypingUndoBuffer::RedoSelf(BClipboard* clipboard)
287 {
288 	fTextView->Select(fTypedStart, fTypedStart);
289 	fTextView->Delete(fTypedStart, fTypedStart + fTextLength);
290 	fTextView->Insert(fTypedText, fTypedEnd - fTypedStart);
291 	fUndone--;
292 }
293 
294 
295 void
296 BTextView::TypingUndoBuffer::InputCharacter(int32 len)
297 {
298 	int32 start, end;
299 	fTextView->GetSelection(&start, &end);
300 
301 	if (start != fTypedEnd || end != fTypedEnd)
302 		_Reset();
303 
304 	fTypedEnd += len;
305 }
306 
307 
308 void
309 BTextView::TypingUndoBuffer::_Reset()
310 {
311 	free(fTextData);
312 	fTextView->GetSelection(&fStart, &fEnd);
313 	fTextLength = fEnd - fStart;
314 	fTypedStart = fStart;
315 	fTypedEnd = fStart;
316 
317 	fTextData = (char*)malloc(fTextLength);
318 	memcpy(fTextData, fTextView->Text() + fStart, fTextLength);
319 
320 	free(fTypedText);
321 	fTypedText = NULL;
322 	fRedo = false;
323 	fUndone = 0;
324 }
325 
326 
327 void
328 BTextView::TypingUndoBuffer::BackwardErase()
329 {
330 	int32 start, end;
331 	fTextView->GetSelection(&start, &end);
332 
333 	const char* text = fTextView->Text();
334 	int32 charLen = UTF8PreviousCharLen(text + start, text);
335 
336 	if (start != fTypedEnd || end != fTypedEnd) {
337 		_Reset();
338 		// if we've got a selection, we're already done
339 		if (start != end)
340 			return;
341 	}
342 
343 	char* buffer = (char*)malloc(fTextLength + charLen);
344 	memcpy(buffer + charLen, fTextData, fTextLength);
345 
346 	fTypedStart = start - charLen;
347 	start = fTypedStart;
348 	for (int32 x = 0; x < charLen; x++)
349 		buffer[x] = fTextView->ByteAt(start + x);
350 	free(fTextData);
351 	fTextData = buffer;
352 
353 	fTextLength += charLen;
354 	fTypedEnd -= charLen;
355 }
356 
357 
358 void
359 BTextView::TypingUndoBuffer::ForwardErase()
360 {
361 	// TODO: Cleanup
362 	int32 start, end;
363 
364 	fTextView->GetSelection(&start, &end);
365 
366 	int32 charLen = UTF8NextCharLen(fTextView->Text() + start);
367 
368 	if (start != fTypedEnd || end != fTypedEnd || fUndone > 0) {
369 		_Reset();
370 		// if we've got a selection, we're already done
371 		if (fStart == fEnd) {
372 			free(fTextData);
373 			fTextLength = charLen;
374 			fTextData = (char*)malloc(fTextLength);
375 
376 			// store the erased character
377 			for (int32 x = 0; x < charLen; x++)
378 				fTextData[x] = fTextView->ByteAt(start + x);
379 		}
380 	} else {
381 		// Here we need to store the erased text, so we get the text that it's
382 		// already in the buffer, and we add the erased character.
383 		// a realloc + memmove would maybe be cleaner, but that way we spare a
384 		// copy (malloc + memcpy vs realloc + memmove).
385 
386 		int32 newLength = fTextLength + charLen;
387 		char* buffer = (char*)malloc(newLength);
388 
389 		// copy the already stored data
390 		memcpy(buffer, fTextData, fTextLength);
391 
392 		if (fTextLength < newLength) {
393 			// store the erased character
394 			for (int32 x = 0; x < charLen; x++)
395 				buffer[fTextLength + x] = fTextView->ByteAt(start + x);
396 		}
397 
398 		fTextLength = newLength;
399 		free(fTextData);
400 		fTextData = buffer;
401 	}
402 }
403