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