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
28BTextView::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
48BTextView::UndoBuffer::~UndoBuffer()
49{
50	free(fTextData);
51	BTextView::FreeRunArray(fRunArray);
52}
53
54
55void
56BTextView::UndoBuffer::Undo(BClipboard* clipboard)
57{
58	fRedo ? RedoSelf(clipboard) : UndoSelf(clipboard);
59
60	fRedo = !fRedo;
61}
62
63
64undo_state
65BTextView::UndoBuffer::State(bool* _isRedo) const
66{
67	*_isRedo = fRedo;
68
69	return fState;
70}
71
72
73void
74BTextView::UndoBuffer::UndoSelf(BClipboard* clipboard)
75{
76	fTextView->Select(fStart, fStart);
77	fTextView->Insert(fTextData, fTextLength, fRunArray);
78	fTextView->Select(fStart, fStart);
79}
80
81
82void
83BTextView::UndoBuffer::RedoSelf(BClipboard* clipboard)
84{
85}
86
87
88//	#pragma mark - CutUndoBuffer
89
90
91BTextView::CutUndoBuffer::CutUndoBuffer(BTextView* textView)
92	: BTextView::UndoBuffer(textView, B_UNDO_CUT)
93{
94}
95
96
97BTextView::CutUndoBuffer::~CutUndoBuffer()
98{
99}
100
101
102void
103BTextView::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
126BTextView::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
142BTextView::PasteUndoBuffer::~PasteUndoBuffer()
143{
144	free(fPasteText);
145	BTextView::FreeRunArray(fPasteRunArray);
146}
147
148
149void
150BTextView::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
159void
160BTextView::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
172BTextView::ClearUndoBuffer::ClearUndoBuffer(BTextView* textView)
173	: BTextView::UndoBuffer(textView, B_UNDO_CLEAR)
174{
175}
176
177
178BTextView::ClearUndoBuffer::~ClearUndoBuffer()
179{
180}
181
182
183void
184BTextView::ClearUndoBuffer::RedoSelf(BClipboard* clipboard)
185{
186	fTextView->Select(fStart, fStart);
187	fTextView->Delete(fStart, fEnd);
188}
189
190
191//	#pragma mark - DropUndoBuffer
192
193
194BTextView::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
216BTextView::DropUndoBuffer::~DropUndoBuffer()
217{
218	free(fDropText);
219	BTextView::FreeRunArray(fDropRunArray);
220}
221
222
223void
224BTextView::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
236void
237BTextView::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
252BTextView::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
262BTextView::TypingUndoBuffer::~TypingUndoBuffer()
263{
264	free(fTypedText);
265}
266
267
268void
269BTextView::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
285void
286BTextView::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
295void
296BTextView::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
308void
309BTextView::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
327void
328BTextView::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
358void
359BTextView::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