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 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
) /* NOTE don't use editor_undo_ignored() here! */
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_undo_ignored(editor
)) return NULL
;
78 if (editor
->nUndoLimit
== 0) return NULL
;
80 undo
= malloc( 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
= malloc( (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
->fmt
;
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
->fmt
;
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_undo_ignored(editor
))
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_undo_ignored(editor
))
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_undo_ignored(editor
))
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_undo_ignored(editor
))
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 cursor_from_char_ofs( editor
, undo
->u
.set_para_fmt
.pos
, &tmp
);
339 add_undo_set_para_fmt( editor
, tmp
.para
);
340 tmp
.para
->fmt
= undo
->u
.set_para_fmt
.fmt
;
341 tmp
.para
->border
= undo
->u
.set_para_fmt
.border
;
342 para_mark_rewrap( editor
, tmp
.para
);
345 case undo_set_char_fmt
:
347 ME_Cursor start
, end
;
348 cursor_from_char_ofs( editor
, undo
->u
.set_char_fmt
.pos
, &start
);
350 ME_MoveCursorChars(editor
, &end
, undo
->u
.set_char_fmt
.len
, FALSE
);
351 ME_SetCharFormat(editor
, &start
, &end
, &undo
->u
.set_char_fmt
.fmt
);
354 case undo_insert_run
:
357 cursor_from_char_ofs( editor
, undo
->u
.insert_run
.pos
, &tmp
);
358 run_insert( editor
, &tmp
, undo
->u
.insert_run
.style
,
359 undo
->u
.insert_run
.str
, undo
->u
.insert_run
.len
,
360 undo
->u
.insert_run
.flags
);
363 case undo_delete_run
:
366 cursor_from_char_ofs( editor
, undo
->u
.delete_run
.pos
, &tmp
);
367 ME_InternalDeleteText(editor
, &tmp
, undo
->u
.delete_run
.len
, TRUE
);
370 case undo_join_paras
:
373 cursor_from_char_ofs( editor
, undo
->u
.join_paras
.pos
, &tmp
);
374 para_join( editor
, tmp
.para
, TRUE
);
377 case undo_split_para
:
380 ME_Paragraph
*this_para
, *new_para
;
382 int paraFlags
= undo
->u
.split_para
.flags
& (MEPF_ROWSTART
|MEPF_CELL
|MEPF_ROWEND
);
384 cursor_from_char_ofs( editor
, undo
->u
.split_para
.pos
, &tmp
);
385 if (tmp
.nOffset
) run_split( editor
, &tmp
);
386 this_para
= tmp
.para
;
387 bFixRowStart
= this_para
->nFlags
& MEPF_ROWSTART
;
390 /* Re-insert the paragraph before the table, making sure the nFlag value
392 this_para
->nFlags
&= ~MEPF_ROWSTART
;
394 new_para
= para_split( editor
, tmp
.run
, tmp
.run
->style
,
395 undo
->u
.split_para
.eol_str
->szData
, undo
->u
.split_para
.eol_str
->nLen
, paraFlags
);
397 new_para
->nFlags
|= MEPF_ROWSTART
;
398 new_para
->fmt
= undo
->u
.split_para
.fmt
;
399 new_para
->border
= undo
->u
.split_para
.border
;
402 para_cell( new_para
)->nRightBoundary
= undo
->u
.split_para
.cell_right_boundary
;
403 para_cell( new_para
)->border
= undo
->u
.split_para
.cell_border
;
410 BOOL
ME_Undo(ME_TextEditor
*editor
)
412 ME_UndoMode nMode
= editor
->nUndoMode
;
414 struct undo_item
*undo
, *cursor2
;
416 if (editor_undo_ignored(editor
)) return FALSE
;
417 assert(nMode
== umAddToUndo
|| nMode
== umIgnore
);
419 head
= list_head( &editor
->undo_stack
);
420 if (!head
) return FALSE
;
422 /* watch out for uncommitted transactions ! */
423 undo
= LIST_ENTRY( head
, struct undo_item
, entry
);
424 assert(undo
->type
== undo_end_transaction
425 || undo
->type
== undo_potential_end_transaction
);
427 editor
->nUndoMode
= umAddToRedo
;
429 list_remove( &undo
->entry
);
430 destroy_undo_item( undo
);
432 LIST_FOR_EACH_ENTRY_SAFE( undo
, cursor2
, &editor
->undo_stack
, struct undo_item
, entry
)
434 if (undo
->type
== undo_end_transaction
) break;
435 ME_PlayUndoItem( editor
, undo
);
436 list_remove( &undo
->entry
);
437 destroy_undo_item( undo
);
440 table_move_from_row_start( editor
);
441 add_undo( editor
, undo_end_transaction
);
442 editor
->nUndoStackSize
--;
443 editor
->nUndoMode
= nMode
;
444 ME_UpdateRepaint(editor
, FALSE
);
448 BOOL
ME_Redo(ME_TextEditor
*editor
)
450 ME_UndoMode nMode
= editor
->nUndoMode
;
452 struct undo_item
*undo
, *cursor2
;
454 assert(nMode
== umAddToUndo
|| nMode
== umIgnore
);
456 if (editor_undo_ignored(editor
)) return FALSE
;
458 head
= list_head( &editor
->redo_stack
);
459 if (!head
) return FALSE
;
461 /* watch out for uncommitted transactions ! */
462 undo
= LIST_ENTRY( head
, struct undo_item
, entry
);
463 assert( undo
->type
== undo_end_transaction
);
465 editor
->nUndoMode
= umAddBackToUndo
;
466 list_remove( &undo
->entry
);
467 destroy_undo_item( undo
);
469 LIST_FOR_EACH_ENTRY_SAFE( undo
, cursor2
, &editor
->redo_stack
, struct undo_item
, entry
)
471 if (undo
->type
== undo_end_transaction
) break;
472 ME_PlayUndoItem( editor
, undo
);
473 list_remove( &undo
->entry
);
474 destroy_undo_item( undo
);
476 table_move_from_row_start( editor
);
477 add_undo( editor
, undo_end_transaction
);
478 editor
->nUndoMode
= nMode
;
479 ME_UpdateRepaint(editor
, FALSE
);
483 void editor_disable_undo(ME_TextEditor
*editor
)
485 ME_EmptyUndoStack(editor
);
486 editor
->undo_ctl_state
= undoDisabled
;
489 void editor_enable_undo(ME_TextEditor
*editor
)
491 if (editor
->undo_ctl_state
== undoDisabled
)
493 editor
->undo_ctl_state
= undoActive
;