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