Removed buffer overrun error (incrementing output pointer one time too
[wine] / dlls / riched20 / undo.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "editor.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
24
25 void ME_EmptyUndoStack(ME_TextEditor *editor)
26 {
27   ME_DisplayItem *p, *pNext;
28   
29   if (editor->nUndoMode == umIgnore)
30     return;
31   
32   TRACE("Emptying undo stack\n");
33
34   p = editor->pUndoStack;
35   editor->pUndoStack = NULL;
36   while(p) {
37     pNext = p->next;
38     ME_DestroyDisplayItem(p);    
39     p = pNext;
40   } 
41   p = editor->pRedoStack;
42   editor->pRedoStack = NULL;
43   while(p) {
44     pNext = p->next;
45     ME_DestroyDisplayItem(p);    
46     p = pNext;
47   } 
48 }
49
50 ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) {
51   if (editor->nUndoMode == umIgnore)
52     return NULL;
53   else
54   {
55     ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem);
56     switch(type)
57     {
58     case diUndoEndTransaction:
59       break;
60     case diUndoSetParagraphFormat:
61       assert(pdi);
62       CopyMemory(&pItem->member.para, &pdi->member.para, sizeof(ME_Paragraph));
63       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
64       CopyMemory(pItem->member.para.pFmt, pdi->member.para.pFmt, sizeof(PARAFORMAT2));
65       break;
66     case diUndoInsertRun:
67       assert(pdi);
68       CopyMemory(&pItem->member.run, &pdi->member.run, sizeof(ME_Run));
69       pItem->member.run.strText = ME_StrDup(pItem->member.run.strText);
70       ME_AddRefStyle(pItem->member.run.style);
71       break;
72     case diUndoSetCharFormat:
73     case diUndoSetDefaultCharFormat:
74       break;
75     case diUndoDeleteRun:
76     case diUndoJoinParagraphs:
77       break;
78     case diUndoSplitParagraph:
79       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
80       pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
81       pItem->member.para.pFmt->dwMask = 0;
82  
83       break;
84     default:
85       assert(0 == "AddUndoItem, unsupported item type");
86       return NULL;
87     }
88     pItem->type = type;
89     pItem->prev = NULL;
90     if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo)
91     {
92       if (editor->nUndoMode == umAddToUndo)
93         TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
94       else
95         TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
96       pItem->next = editor->pUndoStack;
97       if (editor->pUndoStack)
98         editor->pUndoStack->prev = pItem;
99       editor->pUndoStack = pItem;
100       /* any new operation (not redo) clears the redo stack */
101       if (editor->nUndoMode == umAddToUndo) {
102         ME_DisplayItem *p = editor->pRedoStack;
103         while(p)
104         {
105           ME_DisplayItem *pp = p->next;
106           ME_DestroyDisplayItem(p);
107           p = pp;
108         }
109         editor->pRedoStack = NULL;
110       }
111     }
112     else if (editor->nUndoMode == umAddToRedo)
113     {
114       TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
115       pItem->next = editor->pRedoStack;
116       if (editor->pRedoStack)
117         editor->pRedoStack->prev = pItem;
118       editor->pRedoStack = pItem;
119     }
120     else
121       assert(0);
122     return (ME_UndoItem *)pItem;
123   }
124 }
125
126 void ME_CommitUndo(ME_TextEditor *editor) {
127   
128   if (editor->nUndoMode == umIgnore)
129     return;
130   
131   assert(editor->nUndoMode == umAddToUndo);
132   
133   /* no transactions, no need to commit */
134   if (!editor->pUndoStack)
135     return;
136
137   /* no need to commit empty transactions */
138   if (editor->pUndoStack->type == diUndoEndTransaction)
139     return;
140     
141   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
142   ME_SendSelChange(editor);
143   editor->nModifyStep++;
144 }
145
146 void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
147 {
148   ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
149
150   if (editor->nUndoMode == umIgnore)
151     return;
152   TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
153
154   switch(pItem->type)
155   {
156   case diUndoEndTransaction:
157     assert(0);
158   case diUndoSetParagraphFormat:
159   {
160     ME_Cursor tmp;
161     ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
162     ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt);
163     break;
164   }
165   case diUndoSetCharFormat:
166   {
167     ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt);
168     break;
169   }
170   case diUndoSetDefaultCharFormat:
171   {
172     ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt);
173     break;
174   }
175   case diUndoInsertRun:
176   {
177     ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem);
178     break;
179   }
180   case diUndoDeleteRun:
181   {
182     ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen);
183     break;
184   }
185   case diUndoJoinParagraphs:
186   {
187     ME_Cursor tmp;
188     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
189     /* the only thing that's needed is paragraph offset, so no need to split runs */
190     ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun));
191     break;
192   }
193   case diUndoSplitParagraph:
194   {
195     ME_Cursor tmp;
196     ME_DisplayItem *new_para;
197     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
198     if (tmp.nOffset)
199       tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
200     new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style);
201     assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
202     CopyMemory(new_para->member.para.pFmt, pItem->member.para.pFmt, sizeof(PARAFORMAT2));
203     break;
204   }
205   default:
206     assert(0 == "PlayUndoItem, unexpected type");
207   }
208 }
209
210 void ME_Undo(ME_TextEditor *editor) {
211   ME_DisplayItem *p;
212   ME_UndoMode nMode = editor->nUndoMode;
213   
214   if (editor->nUndoMode == umIgnore)
215     return;
216   assert(nMode == umAddToUndo || nMode == umIgnore);
217   
218   /* no undo items ? */
219   if (!editor->pUndoStack)
220     return;
221     
222   /* watch out for uncommited transactions ! */
223   assert(editor->pUndoStack->type == diUndoEndTransaction);
224   
225   editor->nUndoMode = umAddToRedo;
226   p = editor->pUndoStack->next;
227   ME_DestroyDisplayItem(editor->pUndoStack);
228   do {
229     ME_DisplayItem *pp = p;
230     ME_PlayUndoItem(editor, p);
231     p = p->next;
232     ME_DestroyDisplayItem(pp);
233   } while(p && p->type != diUndoEndTransaction);
234   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
235   editor->pUndoStack = p;
236   if (p)
237     p->prev = NULL;
238   editor->nUndoMode = nMode;
239   editor->nModifyStep--;
240   ME_UpdateRepaint(editor);
241 }
242
243 void ME_Redo(ME_TextEditor *editor) {
244   ME_DisplayItem *p;
245   ME_UndoMode nMode = editor->nUndoMode;
246   
247   assert(nMode == umAddToUndo || nMode == umIgnore);
248   
249   if (editor->nUndoMode == umIgnore)
250     return;
251   /* no redo items ? */
252   if (!editor->pRedoStack)
253     return;
254     
255   /* watch out for uncommited transactions ! */
256   assert(editor->pRedoStack->type == diUndoEndTransaction);
257   
258   editor->nUndoMode = umAddBackToUndo;
259   p = editor->pRedoStack->next;
260   ME_DestroyDisplayItem(editor->pRedoStack);
261   do {
262     ME_DisplayItem *pp = p;
263     ME_PlayUndoItem(editor, p);
264     p = p->next;
265     ME_DestroyDisplayItem(pp);
266   } while(p && p->type != diUndoEndTransaction);
267   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
268   editor->pRedoStack = p;
269   if (p)
270     p->prev = NULL;
271   editor->nUndoMode = nMode;
272   editor->nModifyStep++;
273   ME_UpdateRepaint(editor);
274 }