xref: /haiku/src/kits/interface/textview_support/UndoBuffer.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2003-2007, 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 	BTextView::FreeRunArray(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 {
133 	fPasteText = (char *)malloc(fPasteTextLength);
134 	memcpy(fPasteText, text, fPasteTextLength);
135 
136 	if (runArray)
137 		fPasteRunArray = BTextView::CopyRunArray(runArray);
138 }
139 
140 
141 _BPasteUndoBuffer_::~_BPasteUndoBuffer_()
142 {
143 	free(fPasteText);
144 	BTextView::FreeRunArray(fPasteRunArray);
145 }
146 
147 
148 void
149 _BPasteUndoBuffer_::UndoSelf(BClipboard *clipboard)
150 {
151 	fTextView->Select(fStart, fStart);
152 	fTextView->Delete(fStart, fStart + fPasteTextLength);
153 	fTextView->Insert(fTextData, fTextLength, fRunArray);
154 	fTextView->Select(fStart, fEnd);
155 }
156 
157 
158 void
159 _BPasteUndoBuffer_::RedoSelf(BClipboard *clipboard)
160 {
161 	fTextView->Select(fStart, fStart);
162 	fTextView->Delete(fStart, fEnd);
163 	fTextView->Insert(fPasteText, fPasteTextLength, fPasteRunArray);
164 	fTextView->Select(fStart + fPasteTextLength, fStart + fPasteTextLength);
165 }
166 
167 
168 //	#pragma mark - _BClearUndoBuffer_
169 
170 
171 _BClearUndoBuffer_::_BClearUndoBuffer_(BTextView *textView)
172 	: _BUndoBuffer_(textView, B_UNDO_CLEAR)
173 {
174 }
175 
176 
177 _BClearUndoBuffer_::~_BClearUndoBuffer_()
178 {
179 }
180 
181 
182 void
183 _BClearUndoBuffer_::RedoSelf(BClipboard *clipboard)
184 {
185 	fTextView->Select(fStart, fStart);
186 	fTextView->Delete(fStart, fEnd);
187 }
188 
189 
190 //	#pragma mark - _BDropUndoBuffer_
191 
192 
193 _BDropUndoBuffer_::_BDropUndoBuffer_(BTextView *textView, char const *text,
194 		int32 textLen, text_run_array *runArray, int32 runArrayLen,
195 		int32 location, bool internalDrop)
196 	: _BUndoBuffer_(textView, B_UNDO_DROP),
197 	fDropText(NULL),
198 	fDropTextLength(textLen),
199 	fDropRunArray(NULL)
200 {
201 	fInternalDrop = internalDrop;
202 	fDropLocation = location;
203 
204 	fDropText = (char *)malloc(fDropTextLength);
205 	memcpy(fDropText, text, fDropTextLength);
206 
207 	if (runArray)
208 		fDropRunArray = BTextView::CopyRunArray(runArray);
209 
210 	if (fInternalDrop && fDropLocation >= fEnd)
211 		fDropLocation -= fDropTextLength;
212 }
213 
214 
215 _BDropUndoBuffer_::~_BDropUndoBuffer_()
216 {
217 	free(fDropText);
218 	BTextView::FreeRunArray(fDropRunArray);
219 }
220 
221 
222 void
223 _BDropUndoBuffer_::UndoSelf(BClipboard *)
224 {
225 	fTextView->Select(fDropLocation, fDropLocation);
226 	fTextView->Delete(fDropLocation, fDropLocation + fDropTextLength);
227 	if (fInternalDrop) {
228 		fTextView->Select(fStart, fStart);
229 		fTextView->Insert(fTextData, fTextLength, fRunArray);
230 	}
231 	fTextView->Select(fStart, fEnd);
232 }
233 
234 
235 void
236 _BDropUndoBuffer_::RedoSelf(BClipboard *)
237 {
238 	if (fInternalDrop) {
239 		fTextView->Select(fStart, fStart);
240 		fTextView->Delete(fStart, fEnd);
241 	}
242 	fTextView->Select(fDropLocation, fDropLocation);
243 	fTextView->Insert(fDropText, fDropTextLength, fDropRunArray);
244 	fTextView->Select(fDropLocation, fDropLocation + fDropTextLength);
245 }
246 
247 
248 //	#pragma mark - _BTypingUndoBuffer_
249 
250 
251 _BTypingUndoBuffer_::_BTypingUndoBuffer_(BTextView *textView)
252 	: _BUndoBuffer_(textView, B_UNDO_TYPING),
253 	fTypedText(NULL),
254 	fTypedStart(fStart),
255 	fTypedEnd(fEnd),
256 	fUndone(0)
257 {
258 }
259 
260 
261 _BTypingUndoBuffer_::~_BTypingUndoBuffer_()
262 {
263 	free(fTypedText);
264 }
265 
266 
267 void
268 _BTypingUndoBuffer_::UndoSelf(BClipboard *clipboard)
269 {
270 	int32 len = fTypedEnd - fTypedStart;
271 
272 	free(fTypedText);
273 	fTypedText = NULL;
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 _BTypingUndoBuffer_::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 _BTypingUndoBuffer_::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 _BTypingUndoBuffer_::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 _BTypingUndoBuffer_::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 _BTypingUndoBuffer_::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