Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenundo.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
1.7.6.1
|