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