1 /* 2 * RichEdit - functions dealing with editor object 3 * 4 * Copyright 2004 by Krzysztof Foltman 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include "editor.h" 22 23 WINE_DEFAULT_DEBUG_CHANNEL(richedit); 24 25 static void destroy_undo_item( struct undo_item *undo ) 26 { 27 switch( undo->type ) 28 { 29 case undo_insert_run: 30 heap_free( undo->u.insert_run.str ); 31 ME_ReleaseStyle( undo->u.insert_run.style ); 32 break; 33 case undo_split_para: 34 ME_DestroyString( undo->u.split_para.eol_str ); 35 break; 36 default: 37 break; 38 } 39 40 heap_free( undo ); 41 } 42 43 static void empty_redo_stack(ME_TextEditor *editor) 44 { 45 struct undo_item *cursor, *cursor2; 46 LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &editor->redo_stack, struct undo_item, entry ) 47 { 48 list_remove( &cursor->entry ); 49 destroy_undo_item( cursor ); 50 } 51 } 52 53 void ME_EmptyUndoStack(ME_TextEditor *editor) 54 { 55 struct undo_item *cursor, *cursor2; 56 if (editor->nUndoMode == umIgnore) 57 return; 58 59 TRACE("Emptying undo stack\n"); 60 61 editor->nUndoStackSize = 0; 62 63 LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &editor->undo_stack, struct undo_item, entry ) 64 { 65 list_remove( &cursor->entry ); 66 destroy_undo_item( cursor ); 67 } 68 69 empty_redo_stack( editor ); 70 } 71 72 static struct undo_item *add_undo( ME_TextEditor *editor, enum undo_type type ) 73 { 74 struct undo_item *undo, *item; 75 struct list *head; 76 77 if (editor->nUndoMode == umIgnore) return NULL; 78 if (editor->nUndoLimit == 0) return NULL; 79 80 undo = heap_alloc( sizeof(*undo) ); 81 if (!undo) return NULL; 82 undo->type = type; 83 84 if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo) 85 { 86 87 head = list_head( &editor->undo_stack ); 88 if (head) 89 { 90 item = LIST_ENTRY( head, struct undo_item, entry ); 91 if (item->type == undo_potential_end_transaction) 92 item->type = undo_end_transaction; 93 } 94 95 if (editor->nUndoMode == umAddToUndo) 96 TRACE("Pushing id=%d to undo stack, deleting redo stack\n", type); 97 else 98 TRACE("Pushing id=%d to undo stack\n", type); 99 100 list_add_head( &editor->undo_stack, &undo->entry ); 101 102 if (type == undo_end_transaction || type == undo_potential_end_transaction) 103 editor->nUndoStackSize++; 104 105 if (editor->nUndoStackSize > editor->nUndoLimit) 106 { 107 struct undo_item *cursor2; 108 /* remove oldest undo from stack */ 109 LIST_FOR_EACH_ENTRY_SAFE_REV( item, cursor2, &editor->undo_stack, struct undo_item, entry ) 110 { 111 BOOL done = (item->type == undo_end_transaction); 112 list_remove( &item->entry ); 113 destroy_undo_item( item ); 114 if (done) break; 115 } 116 editor->nUndoStackSize--; 117 } 118 119 /* any new operation (not redo) clears the redo stack */ 120 if (editor->nUndoMode == umAddToUndo) empty_redo_stack( editor ); 121 } 122 else if (editor->nUndoMode == umAddToRedo) 123 { 124 TRACE("Pushing id=%d to redo stack\n", type); 125 list_add_head( &editor->redo_stack, &undo->entry ); 126 } 127 128 return undo; 129 } 130 131 BOOL add_undo_insert_run( ME_TextEditor *editor, int pos, const WCHAR *str, int len, int flags, ME_Style *style ) 132 { 133 struct undo_item *undo = add_undo( editor, undo_insert_run ); 134 if (!undo) return FALSE; 135 136 undo->u.insert_run.str = heap_alloc( (len + 1) * sizeof(WCHAR) ); 137 if (!undo->u.insert_run.str) 138 { 139 ME_EmptyUndoStack( editor ); 140 return FALSE; 141 } 142 memcpy( undo->u.insert_run.str, str, len * sizeof(WCHAR) ); 143 undo->u.insert_run.str[len] = 0; 144 undo->u.insert_run.pos = pos; 145 undo->u.insert_run.len = len; 146 undo->u.insert_run.flags = flags; 147 undo->u.insert_run.style = style; 148 ME_AddRefStyle( style ); 149 return TRUE; 150 } 151 152 BOOL add_undo_set_para_fmt( ME_TextEditor *editor, const ME_Paragraph *para ) 153 { 154 struct undo_item *undo = add_undo( editor, undo_set_para_fmt ); 155 if (!undo) return FALSE; 156 157 undo->u.set_para_fmt.pos = para->nCharOfs; 158 undo->u.set_para_fmt.fmt = para->fmt; 159 undo->u.set_para_fmt.border = para->border; 160 161 return TRUE; 162 } 163 164 BOOL add_undo_set_char_fmt( ME_TextEditor *editor, int pos, int len, const CHARFORMAT2W *fmt ) 165 { 166 struct undo_item *undo = add_undo( editor, undo_set_char_fmt ); 167 if (!undo) return FALSE; 168 169 undo->u.set_char_fmt.pos = pos; 170 undo->u.set_char_fmt.len = len; 171 undo->u.set_char_fmt.fmt = *fmt; 172 173 return TRUE; 174 } 175 176 BOOL add_undo_join_paras( ME_TextEditor *editor, int pos ) 177 { 178 struct undo_item *undo = add_undo( editor, undo_join_paras ); 179 if (!undo) return FALSE; 180 181 undo->u.join_paras.pos = pos; 182 return TRUE; 183 } 184 185 BOOL add_undo_split_para( ME_TextEditor *editor, const ME_Paragraph *para, ME_String *eol_str, const ME_Cell *cell ) 186 { 187 struct undo_item *undo = add_undo( editor, undo_split_para ); 188 if (!undo) return FALSE; 189 190 undo->u.split_para.pos = para->nCharOfs - eol_str->nLen; 191 undo->u.split_para.eol_str = eol_str; 192 undo->u.split_para.fmt = para->fmt; 193 undo->u.split_para.border = para->border; 194 undo->u.split_para.flags = para->prev_para->member.para.nFlags & ~MEPF_CELL; 195 196 if (cell) 197 { 198 undo->u.split_para.cell_border = cell->border; 199 undo->u.split_para.cell_right_boundary = cell->nRightBoundary; 200 } 201 return TRUE; 202 } 203 204 BOOL add_undo_delete_run( ME_TextEditor *editor, int pos, int len ) 205 { 206 struct undo_item *undo = add_undo( editor, undo_delete_run ); 207 if (!undo) return FALSE; 208 209 undo->u.delete_run.pos = pos; 210 undo->u.delete_run.len = len; 211 212 return TRUE; 213 } 214 215 /** 216 * Commits preceding changes into a transaction that can be undone together. 217 * 218 * This should be called after all the changes occur associated with an event 219 * so that the group of changes can be undone atomically as a transaction. 220 * 221 * This will have no effect the undo mode is set to ignore changes, or if no 222 * changes preceded calling this function before the last time it was called. 223 * 224 * This can also be used to conclude a coalescing transaction (used for grouping 225 * typed characters). 226 */ 227 void ME_CommitUndo(ME_TextEditor *editor) 228 { 229 struct undo_item *item; 230 struct list *head; 231 232 if (editor->nUndoMode == umIgnore) 233 return; 234 235 assert(editor->nUndoMode == umAddToUndo); 236 237 /* no transactions, no need to commit */ 238 head = list_head( &editor->undo_stack ); 239 if (!head) return; 240 241 /* no need to commit empty transactions */ 242 item = LIST_ENTRY( head, struct undo_item, entry ); 243 if (item->type == undo_end_transaction) return; 244 245 if (item->type == undo_potential_end_transaction) 246 { 247 item->type = undo_end_transaction; 248 return; 249 } 250 251 add_undo( editor, undo_end_transaction ); 252 } 253 254 /** 255 * Groups subsequent changes with previous ones for an undo if coalescing. 256 * 257 * Has no effect if the previous changes were followed by a ME_CommitUndo. This 258 * function will only have an affect if the previous changes were followed by 259 * a call to ME_CommitCoalescingUndo, which allows the transaction to be 260 * continued. 261 * 262 * This allows multiple consecutively typed characters to be grouped together 263 * to be undone by a single undo operation. 264 */ 265 void ME_ContinueCoalescingTransaction(ME_TextEditor *editor) 266 { 267 struct undo_item *item; 268 struct list *head; 269 270 if (editor->nUndoMode == umIgnore) 271 return; 272 273 assert(editor->nUndoMode == umAddToUndo); 274 275 head = list_head( &editor->undo_stack ); 276 if (!head) return; 277 278 item = LIST_ENTRY( head, struct undo_item, entry ); 279 if (item->type == undo_potential_end_transaction) 280 { 281 list_remove( &item->entry ); 282 editor->nUndoStackSize--; 283 destroy_undo_item( item ); 284 } 285 } 286 287 /** 288 * Commits preceding changes into a undo transaction that can be expanded. 289 * 290 * This function allows the transaction to be reopened with 291 * ME_ContinueCoalescingTransaction in order to continue the transaction. If an 292 * undo item is added to the undo stack as a result of a change without the 293 * transaction being reopened, then the transaction will be ended, and the 294 * changes will become a part of the next transaction. 295 * 296 * This is used to allow typed characters to be grouped together since each 297 * typed character results in a single event, and each event adding undo items 298 * must be committed. Using this function as opposed to ME_CommitUndo allows 299 * multiple events to be grouped, and undone together. 300 */ 301 void ME_CommitCoalescingUndo(ME_TextEditor *editor) 302 { 303 struct undo_item *item; 304 struct list *head; 305 306 if (editor->nUndoMode == umIgnore) 307 return; 308 309 assert(editor->nUndoMode == umAddToUndo); 310 311 head = list_head( &editor->undo_stack ); 312 if (!head) return; 313 314 /* no need to commit empty transactions */ 315 item = LIST_ENTRY( head, struct undo_item, entry ); 316 if (item->type == undo_end_transaction || 317 item->type == undo_potential_end_transaction) 318 return; 319 320 add_undo( editor, undo_potential_end_transaction ); 321 } 322 323 static void ME_PlayUndoItem(ME_TextEditor *editor, struct undo_item *undo) 324 { 325 326 if (editor->nUndoMode == umIgnore) 327 return; 328 TRACE("Playing undo/redo item, id=%d\n", undo->type); 329 330 switch(undo->type) 331 { 332 case undo_potential_end_transaction: 333 case undo_end_transaction: 334 assert(0); 335 case undo_set_para_fmt: 336 { 337 ME_Cursor tmp; 338 ME_DisplayItem *para; 339 ME_CursorFromCharOfs(editor, undo->u.set_para_fmt.pos, &tmp); 340 para = ME_FindItemBack(tmp.pRun, diParagraph); 341 add_undo_set_para_fmt( editor, ¶->member.para ); 342 para->member.para.fmt = undo->u.set_para_fmt.fmt; 343 para->member.para.border = undo->u.set_para_fmt.border; 344 mark_para_rewrap(editor, para); 345 break; 346 } 347 case undo_set_char_fmt: 348 { 349 ME_Cursor start, end; 350 ME_CursorFromCharOfs(editor, undo->u.set_char_fmt.pos, &start); 351 end = start; 352 ME_MoveCursorChars(editor, &end, undo->u.set_char_fmt.len, FALSE); 353 ME_SetCharFormat(editor, &start, &end, &undo->u.set_char_fmt.fmt); 354 break; 355 } 356 case undo_insert_run: 357 { 358 ME_Cursor tmp; 359 ME_CursorFromCharOfs(editor, undo->u.insert_run.pos, &tmp); 360 ME_InsertRunAtCursor(editor, &tmp, undo->u.insert_run.style, 361 undo->u.insert_run.str, 362 undo->u.insert_run.len, 363 undo->u.insert_run.flags); 364 break; 365 } 366 case undo_delete_run: 367 { 368 ME_Cursor tmp; 369 ME_CursorFromCharOfs(editor, undo->u.delete_run.pos, &tmp); 370 ME_InternalDeleteText(editor, &tmp, undo->u.delete_run.len, TRUE); 371 break; 372 } 373 case undo_join_paras: 374 { 375 ME_Cursor tmp; 376 ME_CursorFromCharOfs(editor, undo->u.join_paras.pos, &tmp); 377 ME_JoinParagraphs(editor, tmp.pPara, TRUE); 378 break; 379 } 380 case undo_split_para: 381 { 382 ME_Cursor tmp; 383 ME_DisplayItem *this_para, *new_para; 384 BOOL bFixRowStart; 385 int paraFlags = undo->u.split_para.flags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND); 386 ME_CursorFromCharOfs(editor, undo->u.split_para.pos, &tmp); 387 if (tmp.nOffset) 388 ME_SplitRunSimple(editor, &tmp); 389 this_para = tmp.pPara; 390 bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART; 391 if (bFixRowStart) 392 { 393 /* Re-insert the paragraph before the table, making sure the nFlag value 394 * is correct. */ 395 this_para->member.para.nFlags &= ~MEPF_ROWSTART; 396 } 397 new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style, 398 undo->u.split_para.eol_str->szData, undo->u.split_para.eol_str->nLen, paraFlags); 399 if (bFixRowStart) 400 new_para->member.para.nFlags |= MEPF_ROWSTART; 401 new_para->member.para.fmt = undo->u.split_para.fmt; 402 new_para->member.para.border = undo->u.split_para.border; 403 if (paraFlags) 404 { 405 ME_DisplayItem *pCell = new_para->member.para.pCell; 406 pCell->member.cell.nRightBoundary = undo->u.split_para.cell_right_boundary; 407 pCell->member.cell.border = undo->u.split_para.cell_border; 408 } 409 break; 410 } 411 } 412 } 413 414 BOOL ME_Undo(ME_TextEditor *editor) 415 { 416 ME_UndoMode nMode = editor->nUndoMode; 417 struct list *head; 418 struct undo_item *undo, *cursor2; 419 420 if (editor->nUndoMode == umIgnore) return FALSE; 421 assert(nMode == umAddToUndo || nMode == umIgnore); 422 423 head = list_head( &editor->undo_stack ); 424 if (!head) return FALSE; 425 426 /* watch out for uncommitted transactions ! */ 427 undo = LIST_ENTRY( head, struct undo_item, entry ); 428 assert(undo->type == undo_end_transaction 429 || undo->type == undo_potential_end_transaction); 430 431 editor->nUndoMode = umAddToRedo; 432 433 list_remove( &undo->entry ); 434 destroy_undo_item( undo ); 435 436 LIST_FOR_EACH_ENTRY_SAFE( undo, cursor2, &editor->undo_stack, struct undo_item, entry ) 437 { 438 if (undo->type == undo_end_transaction) break; 439 ME_PlayUndoItem( editor, undo ); 440 list_remove( &undo->entry ); 441 destroy_undo_item( undo ); 442 } 443 444 ME_MoveCursorFromTableRowStartParagraph(editor); 445 add_undo( editor, undo_end_transaction ); 446 ME_CheckTablesForCorruption(editor); 447 editor->nUndoStackSize--; 448 editor->nUndoMode = nMode; 449 ME_UpdateRepaint(editor, FALSE); 450 return TRUE; 451 } 452 453 BOOL ME_Redo(ME_TextEditor *editor) 454 { 455 ME_UndoMode nMode = editor->nUndoMode; 456 struct list *head; 457 struct undo_item *undo, *cursor2; 458 459 assert(nMode == umAddToUndo || nMode == umIgnore); 460 461 if (editor->nUndoMode == umIgnore) return FALSE; 462 463 head = list_head( &editor->redo_stack ); 464 if (!head) return FALSE; 465 466 /* watch out for uncommitted transactions ! */ 467 undo = LIST_ENTRY( head, struct undo_item, entry ); 468 assert( undo->type == undo_end_transaction ); 469 470 editor->nUndoMode = umAddBackToUndo; 471 list_remove( &undo->entry ); 472 destroy_undo_item( undo ); 473 474 LIST_FOR_EACH_ENTRY_SAFE( undo, cursor2, &editor->redo_stack, struct undo_item, entry ) 475 { 476 if (undo->type == undo_end_transaction) break; 477 ME_PlayUndoItem( editor, undo ); 478 list_remove( &undo->entry ); 479 destroy_undo_item( undo ); 480 } 481 ME_MoveCursorFromTableRowStartParagraph(editor); 482 add_undo( editor, undo_end_transaction ); 483 ME_CheckTablesForCorruption(editor); 484 editor->nUndoMode = nMode; 485 ME_UpdateRepaint(editor, FALSE); 486 return TRUE; 487 } 488