1 /* 2 * Copyright 2003-2006, 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 free(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 fPasteRunArrayLength(0) 133 { 134 fPasteText = (char *)malloc(fPasteTextLength); 135 memcpy(fPasteText, text, fPasteTextLength); 136 137 if (runArray) { 138 fPasteRunArray = (text_run_array *)malloc(runArrayLen); 139 fPasteRunArrayLength = runArrayLen; 140 memcpy(fPasteRunArray, runArray, runArrayLen); 141 } 142 } 143 144 145 _BPasteUndoBuffer_::~_BPasteUndoBuffer_() 146 { 147 free(fPasteText); 148 free(fPasteRunArray); 149 } 150 151 152 void 153 _BPasteUndoBuffer_::UndoSelf(BClipboard *clipboard) 154 { 155 fTextView->Select(fStart, fStart); 156 fTextView->Delete(fStart, fStart + fPasteTextLength); 157 fTextView->Insert(fTextData, fTextLength, fRunArray); 158 fTextView->Select(fStart, fEnd); 159 } 160 161 162 void 163 _BPasteUndoBuffer_::RedoSelf(BClipboard *clipboard) 164 { 165 fTextView->Select(fStart, fStart); 166 fTextView->Delete(fStart, fEnd); 167 fTextView->Insert(fPasteText, fPasteTextLength, fPasteRunArray); 168 fTextView->Select(fStart + fPasteTextLength, fStart + fPasteTextLength); 169 } 170 171 172 // #pragma mark - _BClearUndoBuffer_ 173 174 175 _BClearUndoBuffer_::_BClearUndoBuffer_(BTextView *textView) 176 : _BUndoBuffer_(textView, B_UNDO_CLEAR) 177 { 178 } 179 180 181 _BClearUndoBuffer_::~_BClearUndoBuffer_() 182 { 183 } 184 185 186 void 187 _BClearUndoBuffer_::RedoSelf(BClipboard *clipboard) 188 { 189 fTextView->Select(fStart, fStart); 190 fTextView->Delete(fStart, fEnd); 191 } 192 193 194 // #pragma mark - _BDropUndoBuffer_ 195 196 197 _BDropUndoBuffer_::_BDropUndoBuffer_(BTextView *textView, char const *text, 198 int32 textLen, text_run_array *runArray, int32 runArrayLen, 199 int32 location, bool internalDrop) 200 : _BUndoBuffer_(textView, B_UNDO_DROP), 201 fDropText(NULL), 202 fDropTextLength(textLen), 203 fDropRunArray(NULL) 204 { 205 fInternalDrop = internalDrop; 206 fDropLocation = location; 207 208 fDropText = (char *)malloc(fDropTextLength); 209 memcpy(fDropText, text, fDropTextLength); 210 211 if (runArray) { 212 fDropRunArray = (text_run_array *)malloc(runArrayLen); 213 fDropRunArrayLength = runArrayLen; 214 memcpy(fDropRunArray, runArray, runArrayLen); 215 } 216 217 if (fInternalDrop && fDropLocation >= fEnd) 218 fDropLocation -= fDropTextLength; 219 } 220 221 222 _BDropUndoBuffer_::~_BDropUndoBuffer_() 223 { 224 free(fDropText); 225 free(fDropRunArray); 226 } 227 228 229 void 230 _BDropUndoBuffer_::UndoSelf(BClipboard *) 231 { 232 fTextView->Select(fDropLocation, fDropLocation); 233 fTextView->Delete(fDropLocation, fDropLocation + fDropTextLength); 234 if (fInternalDrop) { 235 fTextView->Select(fStart, fStart); 236 fTextView->Insert(fTextData, fTextLength, fRunArray); 237 } 238 fTextView->Select(fStart, fEnd); 239 } 240 241 242 void 243 _BDropUndoBuffer_::RedoSelf(BClipboard *) 244 { 245 if (fInternalDrop) { 246 fTextView->Select(fStart, fStart); 247 fTextView->Delete(fStart, fEnd); 248 } 249 fTextView->Select(fDropLocation, fDropLocation); 250 fTextView->Insert(fDropText, fDropTextLength, fDropRunArray); 251 fTextView->Select(fDropLocation, fDropLocation + fDropTextLength); 252 } 253 254 255 // #pragma mark - _BTypingUndoBuffer_ 256 257 258 _BTypingUndoBuffer_::_BTypingUndoBuffer_(BTextView *textView) 259 : _BUndoBuffer_(textView, B_UNDO_TYPING), 260 fTypedText(NULL), 261 fTypedStart(fStart), 262 fTypedEnd(fEnd), 263 fUndone(0) 264 { 265 } 266 267 268 _BTypingUndoBuffer_::~_BTypingUndoBuffer_() 269 { 270 free(fTypedText); 271 } 272 273 274 void 275 _BTypingUndoBuffer_::UndoSelf(BClipboard *clipboard) 276 { 277 int32 len = fTypedEnd - fTypedStart; 278 279 free(fTypedText); 280 fTypedText = NULL; 281 fTypedText = (char *)malloc(len); 282 memcpy(fTypedText, fTextView->Text() + fTypedStart, len); 283 284 fTextView->Select(fTypedStart, fTypedStart); 285 fTextView->Delete(fTypedStart, fTypedEnd); 286 fTextView->Insert(fTextData, fTextLength); 287 fTextView->Select(fStart, fEnd); 288 fUndone++; 289 } 290 291 292 void 293 _BTypingUndoBuffer_::RedoSelf(BClipboard *clipboard) 294 { 295 fTextView->Select(fTypedStart, fTypedStart); 296 fTextView->Delete(fTypedStart, fTypedStart + fTextLength); 297 fTextView->Insert(fTypedText, fTypedEnd - fTypedStart); 298 fUndone--; 299 } 300 301 302 void 303 _BTypingUndoBuffer_::InputCharacter(int32 len) 304 { 305 int32 start, end; 306 fTextView->GetSelection(&start, &end); 307 308 if (start != fTypedEnd || end != fTypedEnd) 309 Reset(); 310 311 fTypedEnd += len; 312 } 313 314 315 void 316 _BTypingUndoBuffer_::Reset() 317 { 318 free(fTextData); 319 fTextView->GetSelection(&fStart, &fEnd); 320 fTextLength = fEnd - fStart; 321 fTypedStart = fStart; 322 fTypedEnd = fStart; 323 324 fTextData = (char *)malloc(fTextLength); 325 memcpy(fTextData, fTextView->Text() + fStart, fTextLength); 326 327 free(fTypedText); 328 fTypedText = NULL; 329 fRedo = false; 330 fUndone = 0; 331 } 332 333 334 void 335 _BTypingUndoBuffer_::BackwardErase() 336 { 337 int32 start, end; 338 fTextView->GetSelection(&start, &end); 339 340 const char *text = fTextView->Text(); 341 int32 charLen = UTF8PreviousCharLen(text + start, text); 342 343 if (start != fTypedEnd || end != fTypedEnd) { 344 Reset(); 345 // if we've got a selection, we're already done 346 if (start != end) 347 return; 348 } 349 350 char *buffer = (char *)malloc(fTextLength + charLen); 351 memcpy(buffer + charLen, fTextData, fTextLength); 352 353 fTypedStart = start - charLen; 354 start = fTypedStart; 355 for (int32 x = 0; x < charLen; x++) 356 buffer[x] = fTextView->ByteAt(start + x); 357 free(fTextData); 358 fTextData = buffer; 359 360 fTextLength += charLen; 361 fTypedEnd -= charLen; 362 } 363 364 365 void 366 _BTypingUndoBuffer_::ForwardErase() 367 { 368 // TODO: Cleanup 369 int32 start, end; 370 371 fTextView->GetSelection(&start, &end); 372 373 int32 charLen = UTF8NextCharLen(fTextView->Text() + start); 374 375 if (start != fTypedEnd || end != fTypedEnd || fUndone > 0) { 376 Reset(); 377 // if we've got a selection, we're already done 378 if (fStart == fEnd) { 379 free(fTextData); 380 fTextLength = charLen; 381 fTextData = (char *)malloc(fTextLength); 382 383 // store the erased character 384 for (int32 x = 0; x < charLen; x++) 385 fTextData[x] = fTextView->ByteAt(start + x); 386 } 387 } else { 388 // Here we need to store the erased text, so we get the text that it's 389 // already in the buffer, and we add the erased character. 390 // a realloc + memmove would maybe be cleaner, but that way we spare a 391 // copy (malloc + memcpy vs realloc + memmove). 392 393 int32 newLength = fTextLength + charLen; 394 char *buffer = (char *)malloc(newLength); 395 396 // copy the already stored data 397 memcpy(buffer, fTextData, fTextLength); 398 399 if (fTextLength < newLength) { 400 // store the erased character 401 for (int32 x = 0; x < charLen; x++) 402 buffer[fTextLength] = fTextView->ByteAt(start + x); 403 } 404 405 fTextLength = newLength; 406 free(fTextData); 407 fTextData = buffer; 408 } 409 } 410