ReactOS  0.4.14-dev-342-gdc047f9
undo.c
Go to the documentation of this file.
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 
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 );
50  }
51 }
52 
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 );
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 );
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 
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 );
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 
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;
149  return TRUE;
150 }
151 
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 
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 
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 
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 
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 
246  {
247  item->type = undo_end_transaction;
248  return;
249  }
250 
251  add_undo( editor, undo_end_transaction );
252 }
253 
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 );
280  {
281  list_remove( &item->entry );
282  editor->nUndoStackSize--;
284  }
285 }
286 
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 ||
318  return;
319 
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  {
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, &para->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  {
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 
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 );
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 
445  add_undo( editor, undo_end_transaction );
447  editor->nUndoStackSize--;
448  editor->nUndoMode = nMode;
449  ME_UpdateRepaint(editor, FALSE);
450  return TRUE;
451 }
452 
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  }
482  add_undo( editor, undo_end_transaction );
484  editor->nUndoMode = nMode;
485  ME_UpdateRepaint(editor, FALSE);
486  return TRUE;
487 }
struct join_paras_item join_paras
Definition: editstr.h:353
#define MEPF_ROWEND
Definition: editstr.h:149
struct set_char_fmt_item set_char_fmt
Definition: editstr.h:356
static struct undo_item * add_undo(ME_TextEditor *editor, enum undo_type type)
Definition: undo.c:72
ME_Paragraph para
Definition: editstr.h:266
#define TRUE
Definition: types.h:120
void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor) DECLSPEC_HIDDEN
Definition: run.c:171
union undo_item::@522 u
struct outqueuenode * head
Definition: adnsresfilter.c:66
void ME_CommitCoalescingUndo(ME_TextEditor *editor)
Definition: undo.c:301
struct tagME_DisplayItem * pCell
Definition: editstr.h:211
const char * fmt
Definition: wsprintf.c:30
__WINE_SERVER_LIST_INLINE void list_add_head(struct list *list, struct list *elem)
Definition: list.h:96
BOOL ME_Undo(ME_TextEditor *editor)
Definition: undo.c:414
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
#define assert(x)
Definition: debug.h:53
ME_DisplayItem * ME_SplitRunSimple(ME_TextEditor *editor, ME_Cursor *cursor) DECLSPEC_HIDDEN
Definition: run.c:258
struct tagME_DisplayItem * prev_para
Definition: editstr.h:221
GLuint GLuint end
Definition: gl.h:1545
struct split_para_item split_para
Definition: editstr.h:354
ME_Style * style
Definition: editstr.h:164
void ME_MoveCursorFromTableRowStartParagraph(ME_TextEditor *editor) DECLSPEC_HIDDEN
Definition: table.c:623
ME_DisplayItem * ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style, const WCHAR *eol_str, int eol_len, int paraFlags) DECLSPEC_HIDDEN
Definition: para.c:546
static void * heap_alloc(size_t len)
Definition: appwiz.h:65
static void empty_redo_stack(ME_TextEditor *editor)
Definition: undo.c:43
struct delete_run_item delete_run
Definition: editstr.h:352
__WINE_SERVER_LIST_INLINE struct list * list_head(const struct list *list)
Definition: list.h:131
WINE_DEFAULT_DEBUG_CHANNEL(richedit)
void ME_UpdateRepaint(ME_TextEditor *editor, BOOL update_now) DECLSPEC_HIDDEN
Definition: paint.c:116
struct insert_run_item insert_run
Definition: editstr.h:351
BOOL ME_Redo(ME_TextEditor *editor)
Definition: undo.c:453
static void ME_PlayUndoItem(ME_TextEditor *editor, struct undo_item *undo)
Definition: undo.c:323
unsigned int BOOL
Definition: ntddk_ex.h:94
struct set_para_fmt_item set_para_fmt
Definition: editstr.h:355
#define MEPF_CELL
Definition: editstr.h:147
int nUndoStackSize
Definition: editstr.h:408
const WCHAR * str
smooth NULL
Definition: ftsmooth.c:416
ME_UndoMode
Definition: editstr.h:284
BOOL add_undo_join_paras(ME_TextEditor *editor, int pos)
Definition: undo.c:176
int nLen
Definition: editstr.h:61
ME_BorderRect border
Definition: editstr.h:212
struct list undo_stack
Definition: editstr.h:406
__WINE_SERVER_LIST_INLINE void list_remove(struct list *elem)
Definition: list.h:108
void ME_CommitUndo(ME_TextEditor *editor)
Definition: undo.c:227
BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start, int nChars, BOOL bForce)
Definition: caret.c:301
#define TRACE(s)
Definition: solgame.cpp:4
BOOL add_undo_insert_run(ME_TextEditor *editor, int pos, const WCHAR *str, int len, int flags, ME_Style *style)
Definition: undo.c:131
static void destroy_undo_item(struct undo_item *undo)
Definition: undo.c:25
__wchar_t WCHAR
Definition: xmlstorage.h:180
void ME_ReleaseStyle(ME_Style *item) DECLSPEC_HIDDEN
Definition: style.c:443
void ME_AddRefStyle(ME_Style *item) DECLSPEC_HIDDEN
Definition: style.c:435
ME_DisplayItem * pPara
Definition: editstr.h:279
BOOL add_undo_set_char_fmt(ME_TextEditor *editor, int pos, int len, const CHARFORMAT2W *fmt)
Definition: undo.c:164
GLbitfield flags
Definition: glext.h:7161
BOOL add_undo_delete_run(ME_TextEditor *editor, int pos, int len)
Definition: undo.c:204
void ME_CheckTablesForCorruption(ME_TextEditor *editor) DECLSPEC_HIDDEN
Definition: table.c:192
ME_BorderRect border
Definition: editstr.h:229
undo_type
Definition: editstr.h:291
#define MEPF_ROWSTART
Definition: editstr.h:148
void ME_EmptyUndoStack(ME_TextEditor *editor)
Definition: undo.c:53
int nRightBoundary
Definition: editstr.h:228
int nOffset
Definition: editstr.h:281
uint32_t entry
Definition: isohybrid.c:63
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
GLenum GLsizei len
Definition: glext.h:6722
void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
Definition: undo.c:265
Definition: _list.h:228
ME_Cell cell
Definition: editstr.h:265
void ME_DestroyString(ME_String *s) DECLSPEC_HIDDEN
Definition: string.c:96
#define LIST_FOR_EACH_ENTRY_SAFE_REV(cursor, cursor2, list, type, field)
Definition: list.h:228
enum undo_type type
Definition: editstr.h:348
#define LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, list, type, field)
Definition: list.h:204
static ATOM item
Definition: dde.c:856
const char cursor[]
Definition: icontest.c:13
struct list entry
Definition: editstr.h:347
PARAFORMAT2 fmt
Definition: editstr.h:208
GLuint start
Definition: gl.h:1545
BOOL add_undo_set_para_fmt(ME_TextEditor *editor, const ME_Paragraph *para)
Definition: undo.c:152
ME_DisplayItem * ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, BOOL keepFirstParaFormat) DECLSPEC_HIDDEN
Definition: para.c:688
BOOL add_undo_split_para(ME_TextEditor *editor, const ME_Paragraph *para, ME_String *eol_str, const ME_Cell *cell)
Definition: undo.c:185
ME_UndoMode nUndoMode
Definition: editstr.h:410
ME_DisplayItem * pRun
Definition: editstr.h:280
int ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs, BOOL final_eop)
Definition: caret.c:661
void ME_SetCharFormat(ME_TextEditor *editor, ME_Cursor *start, ME_Cursor *end, CHARFORMAT2W *pFmt) DECLSPEC_HIDDEN
Definition: run.c:726
ME_DisplayItem * ME_InsertRunAtCursor(ME_TextEditor *editor, ME_Cursor *cursor, ME_Style *style, const WCHAR *str, int len, int flags) DECLSPEC_HIDDEN
Definition: run.c:325
struct list redo_stack
Definition: editstr.h:407
void mark_para_rewrap(ME_TextEditor *editor, ME_DisplayItem *para) DECLSPEC_HIDDEN
Definition: para.c:26
#define LIST_ENTRY(type)
Definition: queue.h:175
Arabic default style
Definition: afstyles.h:93
Definition: dsound.c:943
union tagME_DisplayItem::@521 member
ME_DisplayItem * ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass) DECLSPEC_HIDDEN
Definition: list.c:111
static BOOL heap_free(void *mem)
Definition: appwiz.h:75