~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/riched20/undo.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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 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 = editor->pUndoStackBottom = NULL;
 36   editor->nUndoStackSize = 0;
 37   while(p) {
 38     pNext = p->next;
 39     ME_DestroyDisplayItem(p);    
 40     p = pNext;
 41   } 
 42   p = editor->pRedoStack;
 43   editor->pRedoStack = NULL;
 44   while(p) {
 45     pNext = p->next;
 46     ME_DestroyDisplayItem(p);    
 47     p = pNext;
 48   } 
 49 }
 50 
 51 ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi) {
 52   if (editor->nUndoMode == umIgnore)
 53     return NULL;
 54   else if (editor->nUndoLimit == 0)
 55     return NULL;
 56   else
 57   {
 58     ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem);
 59     ((ME_UndoItem *)pItem)->nCR = ((ME_UndoItem *)pItem)->nLF = -1;
 60     switch(type)
 61     {
 62     case diUndoPotentialEndTransaction:
 63         /* only should be added for manually typed chars, not undos or redos */
 64         assert(editor->nUndoMode == umAddToUndo);
 65         /* intentional fall-through to next case */
 66     case diUndoEndTransaction:
 67       break;
 68     case diUndoSetParagraphFormat:
 69       assert(pdi);
 70       pItem->member.para = pdi->member.para;
 71       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
 72       *pItem->member.para.pFmt = *pdi->member.para.pFmt;
 73       break;
 74     case diUndoInsertRun:
 75       assert(pdi);
 76       pItem->member.run = pdi->member.run;
 77       pItem->member.run.strText = ME_StrDup(pItem->member.run.strText);
 78       ME_AddRefStyle(pItem->member.run.style);
 79       if (pdi->member.run.ole_obj)
 80       {
 81         pItem->member.run.ole_obj = ALLOC_OBJ(*pItem->member.run.ole_obj);
 82         ME_CopyReObject(pItem->member.run.ole_obj, pdi->member.run.ole_obj);
 83       }
 84       else pItem->member.run.ole_obj = NULL;
 85       break;
 86     case diUndoSetCharFormat:
 87       break;
 88     case diUndoDeleteRun:
 89     case diUndoJoinParagraphs:
 90       break;
 91     case diUndoSplitParagraph:
 92     {
 93       ME_DisplayItem *prev_para = pdi->member.para.prev_para;
 94       assert(pdi->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
 95       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
 96       pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
 97       pItem->member.para.pFmt->dwMask = 0;
 98       *pItem->member.para.pFmt = *pdi->member.para.pFmt;
 99       pItem->member.para.border = pdi->member.para.border;
100       pItem->member.para.nFlags = prev_para->member.para.nFlags & ~MEPF_CELL;
101       pItem->member.para.pCell = NULL;
102       break;
103     }
104     default:
105       assert(0 == "AddUndoItem, unsupported item type");
106       return NULL;
107     }
108     pItem->type = type;
109     pItem->prev = NULL;
110     if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo)
111     {
112       if (editor->pUndoStack
113           && editor->pUndoStack->type == diUndoPotentialEndTransaction)
114       {
115           editor->pUndoStack->type = diUndoEndTransaction;
116       }
117       if (editor->nUndoMode == umAddToUndo)
118         TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
119       else
120         TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
121 
122       pItem->next = editor->pUndoStack;
123       if (type == diUndoEndTransaction || type == diUndoPotentialEndTransaction)
124         editor->nUndoStackSize++;
125       if (editor->pUndoStack)
126         editor->pUndoStack->prev = pItem;
127       else
128         editor->pUndoStackBottom = pItem;
129       editor->pUndoStack = pItem;
130       
131       if (editor->nUndoStackSize > editor->nUndoLimit)
132       { /* remove oldest undo from stack */
133         ME_DisplayItem *p = editor->pUndoStackBottom;
134         while (p->type !=diUndoEndTransaction)
135           p = p->prev; /*find new stack bottom */
136         editor->pUndoStackBottom = p->prev;
137           editor->pUndoStackBottom->next = NULL;
138         do
139         {
140           ME_DisplayItem *pp = p->next;
141           ME_DestroyDisplayItem(p);
142           p = pp;
143         } while (p);
144         editor->nUndoStackSize--;
145       }
146       /* any new operation (not redo) clears the redo stack */
147       if (editor->nUndoMode == umAddToUndo) {
148         ME_DisplayItem *p = editor->pRedoStack;
149         while(p)
150         {
151           ME_DisplayItem *pp = p->next;
152           ME_DestroyDisplayItem(p);
153           p = pp;
154         }
155         editor->pRedoStack = NULL;
156       }
157     }
158     else if (editor->nUndoMode == umAddToRedo)
159     {
160       TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
161       pItem->next = editor->pRedoStack;
162       if (editor->pRedoStack)
163         editor->pRedoStack->prev = pItem;
164       editor->pRedoStack = pItem;
165     }
166     else
167       assert(0);
168     return (ME_UndoItem *)pItem;
169   }
170 }
171 
172 /**
173  * Commits preceding changes into a transaction that can be undone together.
174  *
175  * This should be called after all the changes occur associated with an event
176  * so that the group of changes can be undone atomically as a transaction.
177  *
178  * This will have no effect the undo mode is set to ignore changes, or if no
179  * changes preceded calling this function before the last time it was called.
180  *
181  * This can also be used to conclude a coalescing transaction (used for grouping
182  * typed characters).
183  */
184 void ME_CommitUndo(ME_TextEditor *editor) {
185   if (editor->nUndoMode == umIgnore)
186     return;
187   
188   assert(editor->nUndoMode == umAddToUndo);
189   
190   /* no transactions, no need to commit */
191   if (!editor->pUndoStack)
192     return;
193 
194   /* no need to commit empty transactions */
195   if (editor->pUndoStack->type == diUndoEndTransaction)
196     return;
197     
198   if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
199   {
200       /* Previous transaction was as a result of characters typed,
201        * so the end of this transaction is confirmed. */
202       editor->pUndoStack->type = diUndoEndTransaction;
203       return;
204   }
205 
206   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
207   ME_SendSelChange(editor);
208 }
209 
210 /**
211  * Groups supsequent changes with previous ones for an undo if coalescing.
212  *
213  * Has no effect if the previous changes were followed by a ME_CommitUndo. This
214  * function will only have an affect if the previous changes were followed by
215  * a call to ME_CommitCoalescingUndo, which allows the transaction to be
216  * continued.
217  *
218  * This allows multiple consecutively typed characters to be grouped together
219  * to be undone by a single undo operation.
220  */
221 void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
222 {
223   ME_DisplayItem* p;
224 
225   if (editor->nUndoMode == umIgnore)
226     return;
227 
228   assert(editor->nUndoMode == umAddToUndo);
229 
230   p = editor->pUndoStack;
231 
232   if (p && p->type == diUndoPotentialEndTransaction) {
233     assert(p->next); /* EndTransactions shouldn't be at bottom of undo stack */
234     editor->pUndoStack = p->next;
235     editor->pUndoStack->prev = NULL;
236     editor->nUndoStackSize--;
237     ME_DestroyDisplayItem(p);
238   }
239 }
240 
241 /**
242  * Commits preceding changes into a undo transaction that can be expanded.
243  *
244  * This function allows the transaction to be reopened with
245  * ME_ContinueCoalescingTransaction in order to continue the transaction.  If an
246  * undo item is added to the undo stack as a result of a change without the
247  * transaction being reopened, then the transaction will be ended, and the
248  * changes will become a part of the next transaction.
249  *
250  * This is used to allow typed characters to be grouped together since each
251  * typed character results in a single event, and each event adding undo items
252  * must be committed.  Using this function as opposed to ME_CommitUndo allows
253  * multiple events to be grouped, and undone together.
254  */
255 void ME_CommitCoalescingUndo(ME_TextEditor *editor)
256 {
257   if (editor->nUndoMode == umIgnore)
258     return;
259 
260   assert(editor->nUndoMode == umAddToUndo);
261 
262   /* no transactions, no need to commit */
263   if (!editor->pUndoStack)
264     return;
265 
266   /* no need to commit empty transactions */
267   if (editor->pUndoStack->type == diUndoEndTransaction)
268     return;
269   if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
270     return;
271 
272   ME_AddUndoItem(editor, diUndoPotentialEndTransaction, NULL);
273   ME_SendSelChange(editor);
274 }
275 
276 static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
277 {
278   ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
279 
280   if (editor->nUndoMode == umIgnore)
281     return;
282   TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
283 
284   switch(pItem->type)
285   {
286   case diUndoPotentialEndTransaction:
287   case diUndoEndTransaction:
288     assert(0);
289   case diUndoSetParagraphFormat:
290   {
291     ME_Cursor tmp;
292     ME_DisplayItem *para;
293     ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
294     para = ME_FindItemBack(tmp.pRun, diParagraph);
295     ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
296     *para->member.para.pFmt = *pItem->member.para.pFmt;
297     para->member.para.border = pItem->member.para.border;
298     break;
299   }
300   case diUndoSetCharFormat:
301   {
302     ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt);
303     break;
304   }
305   case diUndoInsertRun:
306   {
307     ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem);
308     break;
309   }
310   case diUndoDeleteRun:
311   {
312     ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen, TRUE);
313     break;
314   }
315   case diUndoJoinParagraphs:
316   {
317     ME_Cursor tmp;
318     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
319     /* the only thing that's needed is paragraph offset, so no need to split runs */
320     ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun), TRUE);
321     break;
322   }
323   case diUndoSplitParagraph:
324   {
325     ME_Cursor tmp;
326     ME_DisplayItem *this_para, *new_para;
327     BOOL bFixRowStart;
328     int paraFlags = pItem->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND);
329     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
330     if (tmp.nOffset)
331       tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
332     assert(pUItem->nCR >= 0);
333     assert(pUItem->nLF >= 0);
334     this_para = ME_GetParagraph(tmp.pRun);
335     bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART;
336     if (bFixRowStart)
337     {
338       /* Re-insert the paragraph before the table, making sure the nFlag value
339        * is correct. */
340       this_para->member.para.nFlags &= ~MEPF_ROWSTART;
341     }
342     new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style,
343                                  pUItem->nCR, pUItem->nLF, paraFlags);
344     if (bFixRowStart)
345       new_para->member.para.nFlags |= MEPF_ROWSTART;
346     assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
347     *new_para->member.para.pFmt = *pItem->member.para.pFmt;
348     new_para->member.para.border = pItem->member.para.border;
349     if (pItem->member.para.pCell)
350     {
351       ME_DisplayItem *pItemCell, *pCell;
352       pItemCell = pItem->member.para.pCell;
353       pCell = new_para->member.para.pCell;
354       pCell->member.cell.nRightBoundary = pItemCell->member.cell.nRightBoundary;
355       pCell->member.cell.border = pItemCell->member.cell.border;
356     }
357     break;
358   }
359   default:
360     assert(0 == "PlayUndoItem, unexpected type");
361   }
362 }
363 
364 BOOL ME_Undo(ME_TextEditor *editor) {
365   ME_DisplayItem *p;
366   ME_UndoMode nMode = editor->nUndoMode;
367   
368   if (editor->nUndoMode == umIgnore)
369     return FALSE;
370   assert(nMode == umAddToUndo || nMode == umIgnore);
371   
372   /* no undo items ? */
373   if (!editor->pUndoStack)
374     return FALSE;
375     
376   /* watch out for uncommitted transactions ! */
377   assert(editor->pUndoStack->type == diUndoEndTransaction
378         || editor->pUndoStack->type == diUndoPotentialEndTransaction);
379   
380   editor->nUndoMode = umAddToRedo;
381   p = editor->pUndoStack->next;
382   ME_DestroyDisplayItem(editor->pUndoStack);
383   editor->pUndoStack = p;
384   do {
385     p->prev = NULL;
386     ME_PlayUndoItem(editor, p);
387     editor->pUndoStack = p->next;
388     ME_DestroyDisplayItem(p);
389     p = editor->pUndoStack;
390   } while(p && p->type != diUndoEndTransaction);
391   if (p)
392     p->prev = NULL;
393   ME_MoveCursorFromTableRowStartParagraph(editor);
394   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
395   ME_CheckTablesForCorruption(editor);
396   editor->nUndoStackSize--;
397   editor->nUndoMode = nMode;
398   ME_UpdateRepaint(editor);
399   return TRUE;
400 }
401 
402 BOOL ME_Redo(ME_TextEditor *editor) {
403   ME_DisplayItem *p;
404   ME_UndoMode nMode = editor->nUndoMode;
405   
406   assert(nMode == umAddToUndo || nMode == umIgnore);
407   
408   if (editor->nUndoMode == umIgnore)
409     return FALSE;
410   /* no redo items ? */
411   if (!editor->pRedoStack)
412     return FALSE;
413     
414   /* watch out for uncommitted transactions ! */
415   assert(editor->pRedoStack->type == diUndoEndTransaction);
416   
417   editor->nUndoMode = umAddBackToUndo;
418   p = editor->pRedoStack->next;
419   ME_DestroyDisplayItem(editor->pRedoStack);
420   editor->pRedoStack = p;
421   do {
422     p->prev = NULL;
423     ME_PlayUndoItem(editor, p);
424     editor->pRedoStack = p->next;
425     ME_DestroyDisplayItem(p);
426     p = editor->pRedoStack;
427   } while(p && p->type != diUndoEndTransaction);
428   if (p)
429     p->prev = NULL;
430   ME_MoveCursorFromTableRowStartParagraph(editor);
431   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
432   ME_CheckTablesForCorruption(editor);
433   editor->nUndoMode = nMode;
434   ME_UpdateRepaint(editor);
435   return TRUE;
436 }
437 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.