2 * RichEdit - functions dealing with editor object
4 * Copyright 2004 by Krzysztof Foltman
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.
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.
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
23 WINE_DEFAULT_DEBUG_CHANNEL(richedit
);
25 static void destroy_undo_item( struct undo_item
*undo
)
30 heap_free( undo
->u
.insert_run
.str
);
31 ME_ReleaseStyle( undo
->u
.insert_run
.style
);
34 ME_DestroyString( undo
->u
.split_para
.eol_str
);
43 static void empty_redo_stack(ME_TextEditor
*editor
)
45 struct undo_item
*cursor
, *cursor2
;
46 LIST_FOR_EACH_ENTRY_SAFE( cursor
, cursor2
, &editor
->redo_stack
, struct undo_item
, entry
)
48 list_remove( &cursor
->entry
);
49 destroy_undo_item( cursor
);
53 void ME_EmptyUndoStack(ME_TextEditor
*editor
)
55 struct undo_item
*cursor
, *cursor2
;
56 if (editor
->nUndoMode
== umIgnore
)
59 TRACE("Emptying undo stack\n");
61 editor
->nUndoStackSize
= 0;
63 LIST_FOR_EACH_ENTRY_SAFE( cursor
, cursor2
, &editor
->undo_stack
, struct undo_item
, entry
)
65 list_remove( &cursor
->entry
);
66 destroy_undo_item( cursor
);
69 empty_redo_stack( editor
);
72 static struct undo_item
*add_undo( ME_TextEditor
*editor
, enum undo_type type
)
74 struct undo_item
*undo
, *item
;
77 if (editor
->nUndoMode
== umIgnore
) return NULL
;
78 if (editor
->nUndoLimit
== 0) return NULL
;
80 undo
= heap_alloc( sizeof(*undo
) );
81 if (!undo
) return NULL
;
84 if (editor
->nUndoMode
== umAddToUndo
|| editor
->nUndoMode
== umAddBackToUndo
)
87 head
= list_head( &editor
->undo_stack
);
90 item
= LIST_ENTRY( head
, struct undo_item
, entry
);
91 if (item
->type
== undo_potential_end_transaction
)
92 item
->type
= undo_end_transaction
;
95 if (editor
->nUndoMode
== umAddToUndo
)
96 TRACE("Pushing id=%d to undo stack, deleting redo stack\n", type
);
98 TRACE("Pushing id=%d to undo stack\n", type
);
100 list_add_head( &editor
->undo_stack
, &undo
->entry
);
102 if (type
== undo_end_transaction
|| type
== undo_potential_end_transaction
)
103 editor
->nUndoStackSize
++;
105 if (editor
->nUndoStackSize
> editor
->nUndoLimit
)
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
)
111 BOOL done
= (item
->type
== undo_end_transaction
);
112 list_remove( &item
->entry
);
113 destroy_undo_item( item
);
116 editor
->nUndoStackSize
--;
119 /* any new operation (not redo) clears the redo stack */
120 if (editor
->nUndoMode
== umAddToUndo
) empty_redo_stack( editor
);
122 else if (editor
->nUndoMode
== umAddToRedo
)
124 TRACE("Pushing id=%d to redo stack\n", type
);
125 list_add_head( &editor
->redo_stack
, &undo
->entry
);
131 BOOL
add_undo_insert_run( ME_TextEditor
*editor
, int pos
, const WCHAR
*str
, int len
, int flags
, ME_Style
*style
)
133 struct undo_item
*undo
= add_undo( editor
, undo_insert_run
);
134 if (!undo
) return FALSE
;
136 undo
->u
.insert_run
.str
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) );
137 if (!undo
->u
.insert_run
.str
)
139 ME_EmptyUndoStack( editor
);
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
);
152 BOOL
add_undo_set_para_fmt( ME_TextEditor
*editor
, const ME_Paragraph
*para
)
154 struct undo_item
*undo
= add_undo( editor
, undo_set_para_fmt
);
155 if (!undo
) return FALSE
;
157 undo
->u
.set_para_fmt
.pos
= para
->nCharOfs
;
158 undo
->u
.set_para_fmt
.fmt
= *para
->pFmt
;
159 undo
->u
.set_para_fmt
.border
= para
->border
;
164 BOOL
add_undo_set_char_fmt( ME_TextEditor
*editor
, int pos
, int len
, const CHARFORMAT2W
*fmt
)
166 struct undo_item
*undo
= add_undo( editor
, undo_set_char_fmt
);
167 if (!undo
) return FALSE
;
169 undo
->u
.set_char_fmt
.pos
= pos
;
170 undo
->u
.set_char_fmt
.len
= len
;
171 undo
->u
.set_char_fmt
.fmt
= *fmt
;
176 BOOL
add_undo_join_paras( ME_TextEditor
*editor
, int pos
)
178 struct undo_item
*undo
= add_undo( editor
, undo_join_paras
);
179 if (!undo
) return FALSE
;
181 undo
->u
.join_paras
.pos
= pos
;
185 BOOL
add_undo_split_para( ME_TextEditor
*editor
, const ME_Paragraph
*para
, ME_String
*eol_str
, const ME_Cell
*cell
)
187 struct undo_item
*undo
= add_undo( editor
, undo_split_para
);
188 if (!undo
) return FALSE
;
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
->pFmt
;
193 undo
->u
.split_para
.border
= para
->border
;
194 undo
->u
.split_para
.flags
= para
->prev_para
->member
.para
.nFlags
& ~MEPF_CELL
;
198 undo
->u
.split_para
.cell_border
= cell
->border
;
199 undo
->u
.split_para
.cell_right_boundary
= cell
->nRightBoundary
;
204 BOOL
add_undo_delete_run( ME_TextEditor
*editor
, int pos
, int len
)
206 struct undo_item
*undo
= add_undo( editor
, undo_delete_run
);
207 if (!undo
) return FALSE
;
209 undo
->u
.delete_run
.pos
= pos
;
210 undo
->u
.delete_run
.len
= len
;
216 * Commits preceding changes into a transaction that can be undone together.
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.
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.
224 * This can also be used to conclude a coalescing transaction (used for grouping
227 void ME_CommitUndo(ME_TextEditor
*editor
)
229 struct undo_item
*item
;
232 if (editor
->nUndoMode
== umIgnore
)
235 assert(editor
->nUndoMode
== umAddToUndo
);
237 /* no transactions, no need to commit */
238 head
= list_head( &editor
->undo_stack
);
241 /* no need to commit empty transactions */
242 item
= LIST_ENTRY( head
, struct undo_item
, entry
);
243 if (item
->type
== undo_end_transaction
) return;
245 if (item
->type
== undo_potential_end_transaction
)
247 item
->type
= undo_end_transaction
;
251 add_undo( editor
, undo_end_transaction
);
255 * Groups subsequent changes with previous ones for an undo if coalescing.
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
262 * This allows multiple consecutively typed characters to be grouped together
263 * to be undone by a single undo operation.
265 void ME_ContinueCoalescingTransaction(ME_TextEditor
*editor
)
267 struct undo_item
*item
;
270 if (editor
->nUndoMode
== umIgnore
)
273 assert(editor
->nUndoMode
== umAddToUndo
);
275 head
= list_head( &editor
->undo_stack
);
278 item
= LIST_ENTRY( head
, struct undo_item
, entry
);
279 if (item
->type
== undo_potential_end_transaction
)
281 list_remove( &item
->entry
);
282 editor
->nUndoStackSize
--;
283 destroy_undo_item( item
);
288 * Commits preceding changes into a undo transaction that can be expanded.
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.
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.
301 void ME_CommitCoalescingUndo(ME_TextEditor
*editor
)
303 struct undo_item
*item
;
306 if (editor
->nUndoMode
== umIgnore
)
309 assert(editor
->nUndoMode
== umAddToUndo
);
311 head
= list_head( &editor
->undo_stack
);
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
)
320 add_undo( editor
, undo_potential_end_transaction
);
323 static void ME_PlayUndoItem(ME_TextEditor
*editor
, struct undo_item
*undo
)
326 if (editor
->nUndoMode
== umIgnore
)
328 TRACE("Playing undo/redo item, id=%d\n", undo
->type
);
332 case undo_potential_end_transaction
:
333 case undo_end_transaction
:
335 case undo_set_para_fmt
:
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
.pFmt
= undo
->u
.set_para_fmt
.fmt
;
343 para
->member
.para
.border
= undo
->u
.set_para_fmt
.border
;
346 case undo_set_char_fmt
:
348 ME_Cursor start
, end
;
349 ME_CursorFromCharOfs(editor
, undo
->u
.set_char_fmt
.pos
, &start
);
351 ME_MoveCursorChars(editor
, &end
, undo
->u
.set_char_fmt
.len
);
352 ME_SetCharFormat(editor
, &start
, &end
, &undo
->u
.set_char_fmt
.fmt
);
355 case undo_insert_run
:
358 ME_CursorFromCharOfs(editor
, undo
->u
.insert_run
.pos
, &tmp
);
359 ME_InsertRunAtCursor(editor
, &tmp
, undo
->u
.insert_run
.style
,
360 undo
->u
.insert_run
.str
,
361 undo
->u
.insert_run
.len
,
362 undo
->u
.insert_run
.flags
);
365 case undo_delete_run
:
368 ME_CursorFromCharOfs(editor
, undo
->u
.delete_run
.pos
, &tmp
);
369 ME_InternalDeleteText(editor
, &tmp
, undo
->u
.delete_run
.len
, TRUE
);
372 case undo_join_paras
:
375 ME_CursorFromCharOfs(editor
, undo
->u
.join_paras
.pos
, &tmp
);
376 ME_JoinParagraphs(editor
, tmp
.pPara
, TRUE
);
379 case undo_split_para
:
382 ME_DisplayItem
*this_para
, *new_para
;
384 int paraFlags
= undo
->u
.split_para
.flags
& (MEPF_ROWSTART
|MEPF_CELL
|MEPF_ROWEND
);
385 ME_CursorFromCharOfs(editor
, undo
->u
.split_para
.pos
, &tmp
);
387 ME_SplitRunSimple(editor
, &tmp
);
388 this_para
= tmp
.pPara
;
389 bFixRowStart
= this_para
->member
.para
.nFlags
& MEPF_ROWSTART
;
392 /* Re-insert the paragraph before the table, making sure the nFlag value
394 this_para
->member
.para
.nFlags
&= ~MEPF_ROWSTART
;
396 new_para
= ME_SplitParagraph(editor
, tmp
.pRun
, tmp
.pRun
->member
.run
.style
,
397 undo
->u
.split_para
.eol_str
->szData
, undo
->u
.split_para
.eol_str
->nLen
, paraFlags
);
399 new_para
->member
.para
.nFlags
|= MEPF_ROWSTART
;
400 *new_para
->member
.para
.pFmt
= undo
->u
.split_para
.fmt
;
401 new_para
->member
.para
.border
= undo
->u
.split_para
.border
;
404 ME_DisplayItem
*pCell
= new_para
->member
.para
.pCell
;
405 pCell
->member
.cell
.nRightBoundary
= undo
->u
.split_para
.cell_right_boundary
;
406 pCell
->member
.cell
.border
= undo
->u
.split_para
.cell_border
;
413 BOOL
ME_Undo(ME_TextEditor
*editor
)
415 ME_UndoMode nMode
= editor
->nUndoMode
;
417 struct undo_item
*undo
, *cursor2
;
419 if (editor
->nUndoMode
== umIgnore
) return FALSE
;
420 assert(nMode
== umAddToUndo
|| nMode
== umIgnore
);
422 head
= list_head( &editor
->undo_stack
);
423 if (!head
) return FALSE
;
425 /* watch out for uncommitted transactions ! */
426 undo
= LIST_ENTRY( head
, struct undo_item
, entry
);
427 assert(undo
->type
== undo_end_transaction
428 || undo
->type
== undo_potential_end_transaction
);
430 editor
->nUndoMode
= umAddToRedo
;
432 list_remove( &undo
->entry
);
433 destroy_undo_item( undo
);
435 LIST_FOR_EACH_ENTRY_SAFE( undo
, cursor2
, &editor
->undo_stack
, struct undo_item
, entry
)
437 if (undo
->type
== undo_end_transaction
) break;
438 ME_PlayUndoItem( editor
, undo
);
439 list_remove( &undo
->entry
);
440 destroy_undo_item( undo
);
443 ME_MoveCursorFromTableRowStartParagraph(editor
);
444 add_undo( editor
, undo_end_transaction
);
445 ME_CheckTablesForCorruption(editor
);
446 editor
->nUndoStackSize
--;
447 editor
->nUndoMode
= nMode
;
448 ME_UpdateRepaint(editor
, FALSE
);
452 BOOL
ME_Redo(ME_TextEditor
*editor
)
454 ME_UndoMode nMode
= editor
->nUndoMode
;
456 struct undo_item
*undo
, *cursor2
;
458 assert(nMode
== umAddToUndo
|| nMode
== umIgnore
);
460 if (editor
->nUndoMode
== umIgnore
) return FALSE
;
462 head
= list_head( &editor
->redo_stack
);
463 if (!head
) return FALSE
;
465 /* watch out for uncommitted transactions ! */
466 undo
= LIST_ENTRY( head
, struct undo_item
, entry
);
467 assert( undo
->type
== undo_end_transaction
);
469 editor
->nUndoMode
= umAddBackToUndo
;
470 list_remove( &undo
->entry
);
471 destroy_undo_item( undo
);
473 LIST_FOR_EACH_ENTRY_SAFE( undo
, cursor2
, &editor
->redo_stack
, struct undo_item
, entry
)
475 if (undo
->type
== undo_end_transaction
) break;
476 ME_PlayUndoItem( editor
, undo
);
477 list_remove( &undo
->entry
);
478 destroy_undo_item( undo
);
480 ME_MoveCursorFromTableRowStartParagraph(editor
);
481 add_undo( editor
, undo_end_transaction
);
482 ME_CheckTablesForCorruption(editor
);
483 editor
->nUndoMode
= nMode
;
484 ME_UpdateRepaint(editor
, FALSE
);