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
UndoBuffer(BTextView * textView,undo_state state)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
~UndoBuffer()48 BTextView::UndoBuffer::~UndoBuffer()
49 {
50 free(fTextData);
51 BTextView::FreeRunArray(fRunArray);
52 }
53
54
55 void
Undo(BClipboard * clipboard)56 BTextView::UndoBuffer::Undo(BClipboard* clipboard)
57 {
58 fRedo ? RedoSelf(clipboard) : UndoSelf(clipboard);
59
60 fRedo = !fRedo;
61 }
62
63
64 undo_state
State(bool * _isRedo) const65 BTextView::UndoBuffer::State(bool* _isRedo) const
66 {
67 *_isRedo = fRedo;
68
69 return fState;
70 }
71
72
73 void
UndoSelf(BClipboard * clipboard)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
RedoSelf(BClipboard * clipboard)83 BTextView::UndoBuffer::RedoSelf(BClipboard* clipboard)
84 {
85 }
86
87
88 // #pragma mark - CutUndoBuffer
89
90
CutUndoBuffer(BTextView * textView)91 BTextView::CutUndoBuffer::CutUndoBuffer(BTextView* textView)
92 : BTextView::UndoBuffer(textView, B_UNDO_CUT)
93 {
94 }
95
96
~CutUndoBuffer()97 BTextView::CutUndoBuffer::~CutUndoBuffer()
98 {
99 }
100
101
102 void
RedoSelf(BClipboard * clipboard)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
PasteUndoBuffer(BTextView * textView,const char * text,int32 textLen,text_run_array * runArray,int32 runArrayLen)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
~PasteUndoBuffer()142 BTextView::PasteUndoBuffer::~PasteUndoBuffer()
143 {
144 free(fPasteText);
145 BTextView::FreeRunArray(fPasteRunArray);
146 }
147
148
149 void
UndoSelf(BClipboard * clipboard)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
RedoSelf(BClipboard * clipboard)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
ClearUndoBuffer(BTextView * textView)172 BTextView::ClearUndoBuffer::ClearUndoBuffer(BTextView* textView)
173 : BTextView::UndoBuffer(textView, B_UNDO_CLEAR)
174 {
175 }
176
177
~ClearUndoBuffer()178 BTextView::ClearUndoBuffer::~ClearUndoBuffer()
179 {
180 }
181
182
183 void
RedoSelf(BClipboard * clipboard)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
DropUndoBuffer(BTextView * textView,char const * text,int32 textLen,text_run_array * runArray,int32 runArrayLen,int32 location,bool internalDrop)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
~DropUndoBuffer()216 BTextView::DropUndoBuffer::~DropUndoBuffer()
217 {
218 free(fDropText);
219 BTextView::FreeRunArray(fDropRunArray);
220 }
221
222
223 void
UndoSelf(BClipboard *)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
RedoSelf(BClipboard *)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
TypingUndoBuffer(BTextView * textView)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
~TypingUndoBuffer()262 BTextView::TypingUndoBuffer::~TypingUndoBuffer()
263 {
264 free(fTypedText);
265 }
266
267
268 void
UndoSelf(BClipboard * clipboard)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
RedoSelf(BClipboard * clipboard)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
InputCharacter(int32 len)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
_Reset()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
BackwardErase()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
ForwardErase()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