ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

undo.c
Go to the documentation of this file.
00001 /*
00002  * RichEdit - functions dealing with editor object
00003  *
00004  * Copyright 2004 by Krzysztof Foltman
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 #include "editor.h"
00022 
00023 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
00024 
00025 void ME_EmptyUndoStack(ME_TextEditor *editor)
00026 {
00027   ME_DisplayItem *p, *pNext;
00028   
00029   if (editor->nUndoMode == umIgnore)
00030     return;
00031   
00032   TRACE("Emptying undo stack\n");
00033 
00034   p = editor->pUndoStack;
00035   editor->pUndoStack = editor->pUndoStackBottom = NULL;
00036   editor->nUndoStackSize = 0;
00037   while(p) {
00038     pNext = p->next;
00039     ME_DestroyDisplayItem(p);    
00040     p = pNext;
00041   } 
00042   p = editor->pRedoStack;
00043   editor->pRedoStack = NULL;
00044   while(p) {
00045     pNext = p->next;
00046     ME_DestroyDisplayItem(p);    
00047     p = pNext;
00048   } 
00049 }
00050 
00051 ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi) {
00052   if (editor->nUndoMode == umIgnore)
00053     return NULL;
00054   else if (editor->nUndoLimit == 0)
00055     return NULL;
00056   else
00057   {
00058     ME_DisplayItem *pItem = ALLOC_OBJ(ME_UndoItem);
00059     switch(type)
00060     {
00061     case diUndoPotentialEndTransaction:
00062         /* only should be added for manually typed chars, not undos or redos */
00063         assert(editor->nUndoMode == umAddToUndo);
00064         /* intentional fall-through to next case */
00065     case diUndoEndTransaction:
00066       break;
00067     case diUndoSetParagraphFormat:
00068       assert(pdi);
00069       pItem->member.para = pdi->member.para;
00070       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
00071       *pItem->member.para.pFmt = *pdi->member.para.pFmt;
00072       break;
00073     case diUndoInsertRun:
00074       assert(pdi);
00075       pItem->member.run = pdi->member.run;
00076       pItem->member.run.strText = ME_StrDup(pItem->member.run.strText);
00077       ME_AddRefStyle(pItem->member.run.style);
00078       if (pdi->member.run.ole_obj)
00079       {
00080         pItem->member.run.ole_obj = ALLOC_OBJ(*pItem->member.run.ole_obj);
00081         ME_CopyReObject(pItem->member.run.ole_obj, pdi->member.run.ole_obj);
00082       }
00083       else pItem->member.run.ole_obj = NULL;
00084       break;
00085     case diUndoSetCharFormat:
00086       break;
00087     case diUndoDeleteRun:
00088     case diUndoJoinParagraphs:
00089       break;
00090     case diUndoSplitParagraph:
00091     {
00092       ME_DisplayItem *prev_para = pdi->member.para.prev_para;
00093       assert(pdi->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
00094       pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
00095       pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
00096       pItem->member.para.pFmt->dwMask = 0;
00097       *pItem->member.para.pFmt = *pdi->member.para.pFmt;
00098       pItem->member.para.border = pdi->member.para.border;
00099       pItem->member.para.nFlags = prev_para->member.para.nFlags & ~MEPF_CELL;
00100       pItem->member.para.pCell = NULL;
00101       break;
00102     }
00103     default:
00104       assert(0 == "AddUndoItem, unsupported item type");
00105       return NULL;
00106     }
00107     pItem->type = type;
00108     pItem->prev = NULL;
00109     if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo)
00110     {
00111       if (editor->pUndoStack
00112           && editor->pUndoStack->type == diUndoPotentialEndTransaction)
00113       {
00114           editor->pUndoStack->type = diUndoEndTransaction;
00115       }
00116       if (editor->nUndoMode == umAddToUndo)
00117         TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
00118       else
00119         TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
00120 
00121       pItem->next = editor->pUndoStack;
00122       if (type == diUndoEndTransaction || type == diUndoPotentialEndTransaction)
00123         editor->nUndoStackSize++;
00124       if (editor->pUndoStack)
00125         editor->pUndoStack->prev = pItem;
00126       else
00127         editor->pUndoStackBottom = pItem;
00128       editor->pUndoStack = pItem;
00129       
00130       if (editor->nUndoStackSize > editor->nUndoLimit)
00131       { /* remove oldest undo from stack */
00132         ME_DisplayItem *p = editor->pUndoStackBottom;
00133         while (p->type !=diUndoEndTransaction)
00134           p = p->prev; /*find new stack bottom */
00135         editor->pUndoStackBottom = p->prev;
00136           editor->pUndoStackBottom->next = NULL;
00137         do
00138         {
00139           ME_DisplayItem *pp = p->next;
00140           ME_DestroyDisplayItem(p);
00141           p = pp;
00142         } while (p);
00143         editor->nUndoStackSize--;
00144       }
00145       /* any new operation (not redo) clears the redo stack */
00146       if (editor->nUndoMode == umAddToUndo) {
00147         ME_DisplayItem *p = editor->pRedoStack;
00148         while(p)
00149         {
00150           ME_DisplayItem *pp = p->next;
00151           ME_DestroyDisplayItem(p);
00152           p = pp;
00153         }
00154         editor->pRedoStack = NULL;
00155       }
00156     }
00157     else if (editor->nUndoMode == umAddToRedo)
00158     {
00159       TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
00160       pItem->next = editor->pRedoStack;
00161       if (editor->pRedoStack)
00162         editor->pRedoStack->prev = pItem;
00163       editor->pRedoStack = pItem;
00164     }
00165     else
00166       assert(0);
00167     return (ME_UndoItem *)pItem;
00168   }
00169 }
00170 
00183 void ME_CommitUndo(ME_TextEditor *editor) {
00184   if (editor->nUndoMode == umIgnore)
00185     return;
00186   
00187   assert(editor->nUndoMode == umAddToUndo);
00188   
00189   /* no transactions, no need to commit */
00190   if (!editor->pUndoStack)
00191     return;
00192 
00193   /* no need to commit empty transactions */
00194   if (editor->pUndoStack->type == diUndoEndTransaction)
00195     return;
00196     
00197   if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
00198   {
00199       /* Previous transaction was as a result of characters typed,
00200        * so the end of this transaction is confirmed. */
00201       editor->pUndoStack->type = diUndoEndTransaction;
00202       return;
00203   }
00204 
00205   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
00206 }
00207 
00219 void ME_ContinueCoalescingTransaction(ME_TextEditor *editor)
00220 {
00221   ME_DisplayItem* p;
00222 
00223   if (editor->nUndoMode == umIgnore)
00224     return;
00225 
00226   assert(editor->nUndoMode == umAddToUndo);
00227 
00228   p = editor->pUndoStack;
00229 
00230   if (p && p->type == diUndoPotentialEndTransaction) {
00231     assert(p->next); /* EndTransactions shouldn't be at bottom of undo stack */
00232     editor->pUndoStack = p->next;
00233     editor->pUndoStack->prev = NULL;
00234     editor->nUndoStackSize--;
00235     ME_DestroyDisplayItem(p);
00236   }
00237 }
00238 
00253 void ME_CommitCoalescingUndo(ME_TextEditor *editor)
00254 {
00255   if (editor->nUndoMode == umIgnore)
00256     return;
00257 
00258   assert(editor->nUndoMode == umAddToUndo);
00259 
00260   /* no transactions, no need to commit */
00261   if (!editor->pUndoStack)
00262     return;
00263 
00264   /* no need to commit empty transactions */
00265   if (editor->pUndoStack->type == diUndoEndTransaction)
00266     return;
00267   if (editor->pUndoStack->type == diUndoPotentialEndTransaction)
00268     return;
00269 
00270   ME_AddUndoItem(editor, diUndoPotentialEndTransaction, NULL);
00271 }
00272 
00273 static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
00274 {
00275   ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
00276 
00277   if (editor->nUndoMode == umIgnore)
00278     return;
00279   TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
00280 
00281   switch(pItem->type)
00282   {
00283   case diUndoPotentialEndTransaction:
00284   case diUndoEndTransaction:
00285     assert(0);
00286   case diUndoSetParagraphFormat:
00287   {
00288     ME_Cursor tmp;
00289     ME_DisplayItem *para;
00290     ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
00291     para = ME_FindItemBack(tmp.pRun, diParagraph);
00292     ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
00293     *para->member.para.pFmt = *pItem->member.para.pFmt;
00294     para->member.para.border = pItem->member.para.border;
00295     break;
00296   }
00297   case diUndoSetCharFormat:
00298   {
00299     ME_Cursor start, end;
00300     ME_CursorFromCharOfs(editor, pUItem->nStart, &start);
00301     end = start;
00302     ME_MoveCursorChars(editor, &end, pUItem->nLen);
00303     ME_SetCharFormat(editor, &start, &end, &pItem->member.ustyle->fmt);
00304     break;
00305   }
00306   case diUndoInsertRun:
00307   {
00308     ME_Cursor tmp;
00309     ME_CursorFromCharOfs(editor, pItem->member.run.nCharOfs, &tmp);
00310     ME_InsertRunAtCursor(editor, &tmp, pItem->member.run.style,
00311                          pItem->member.run.strText->szData,
00312                          pItem->member.run.strText->nLen,
00313                          pItem->member.run.nFlags);
00314     break;
00315   }
00316   case diUndoDeleteRun:
00317   {
00318     ME_Cursor tmp;
00319     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
00320     ME_InternalDeleteText(editor, &tmp, pUItem->nLen, TRUE);
00321     break;
00322   }
00323   case diUndoJoinParagraphs:
00324   {
00325     ME_Cursor tmp;
00326     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
00327     /* the only thing that's needed is paragraph offset, so no need to split runs */
00328     ME_JoinParagraphs(editor, tmp.pPara, TRUE);
00329     break;
00330   }
00331   case diUndoSplitParagraph:
00332   {
00333     ME_Cursor tmp;
00334     ME_DisplayItem *this_para, *new_para;
00335     BOOL bFixRowStart;
00336     int paraFlags = pItem->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND);
00337     ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
00338     if (tmp.nOffset)
00339       ME_SplitRunSimple(editor, &tmp);
00340     assert(pUItem->eol_str);
00341     this_para = tmp.pPara;
00342     bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART;
00343     if (bFixRowStart)
00344     {
00345       /* Re-insert the paragraph before the table, making sure the nFlag value
00346        * is correct. */
00347       this_para->member.para.nFlags &= ~MEPF_ROWSTART;
00348     }
00349     new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style,
00350                                  pUItem->eol_str, paraFlags);
00351     if (bFixRowStart)
00352       new_para->member.para.nFlags |= MEPF_ROWSTART;
00353     assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
00354     *new_para->member.para.pFmt = *pItem->member.para.pFmt;
00355     new_para->member.para.border = pItem->member.para.border;
00356     if (pItem->member.para.pCell)
00357     {
00358       ME_DisplayItem *pItemCell, *pCell;
00359       pItemCell = pItem->member.para.pCell;
00360       pCell = new_para->member.para.pCell;
00361       pCell->member.cell.nRightBoundary = pItemCell->member.cell.nRightBoundary;
00362       pCell->member.cell.border = pItemCell->member.cell.border;
00363     }
00364     break;
00365   }
00366   default:
00367     assert(0 == "PlayUndoItem, unexpected type");
00368   }
00369 }
00370 
00371 BOOL ME_Undo(ME_TextEditor *editor) {
00372   ME_DisplayItem *p;
00373   ME_UndoMode nMode = editor->nUndoMode;
00374   
00375   if (editor->nUndoMode == umIgnore)
00376     return FALSE;
00377   assert(nMode == umAddToUndo || nMode == umIgnore);
00378   
00379   /* no undo items ? */
00380   if (!editor->pUndoStack)
00381     return FALSE;
00382     
00383   /* watch out for uncommitted transactions ! */
00384   assert(editor->pUndoStack->type == diUndoEndTransaction
00385         || editor->pUndoStack->type == diUndoPotentialEndTransaction);
00386   
00387   editor->nUndoMode = umAddToRedo;
00388   p = editor->pUndoStack->next;
00389   ME_DestroyDisplayItem(editor->pUndoStack);
00390   editor->pUndoStack = p;
00391   do {
00392     p->prev = NULL;
00393     ME_PlayUndoItem(editor, p);
00394     editor->pUndoStack = p->next;
00395     ME_DestroyDisplayItem(p);
00396     p = editor->pUndoStack;
00397   } while(p && p->type != diUndoEndTransaction);
00398   if (p)
00399     p->prev = NULL;
00400   ME_MoveCursorFromTableRowStartParagraph(editor);
00401   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
00402   ME_CheckTablesForCorruption(editor);
00403   editor->nUndoStackSize--;
00404   editor->nUndoMode = nMode;
00405   ME_UpdateRepaint(editor, FALSE);
00406   return TRUE;
00407 }
00408 
00409 BOOL ME_Redo(ME_TextEditor *editor) {
00410   ME_DisplayItem *p;
00411   ME_UndoMode nMode = editor->nUndoMode;
00412   
00413   assert(nMode == umAddToUndo || nMode == umIgnore);
00414   
00415   if (editor->nUndoMode == umIgnore)
00416     return FALSE;
00417   /* no redo items ? */
00418   if (!editor->pRedoStack)
00419     return FALSE;
00420     
00421   /* watch out for uncommitted transactions ! */
00422   assert(editor->pRedoStack->type == diUndoEndTransaction);
00423   
00424   editor->nUndoMode = umAddBackToUndo;
00425   p = editor->pRedoStack->next;
00426   ME_DestroyDisplayItem(editor->pRedoStack);
00427   editor->pRedoStack = p;
00428   do {
00429     p->prev = NULL;
00430     ME_PlayUndoItem(editor, p);
00431     editor->pRedoStack = p->next;
00432     ME_DestroyDisplayItem(p);
00433     p = editor->pRedoStack;
00434   } while(p && p->type != diUndoEndTransaction);
00435   if (p)
00436     p->prev = NULL;
00437   ME_MoveCursorFromTableRowStartParagraph(editor);
00438   ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
00439   ME_CheckTablesForCorruption(editor);
00440   editor->nUndoMode = nMode;
00441   ME_UpdateRepaint(editor, FALSE);
00442   return TRUE;
00443 }

Generated on Sat May 26 2012 04:24:32 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.