ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi) { if (editor->nUndoMode == umIgnore) return NULL; else if (editor->nUndoLimit == 0) return NULL; else { ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem); ((ME_UndoItem *)pItem)->nCR = ((ME_UndoItem *)pItem)->nLF = -1; switch(type) { case diUndoPotentialEndTransaction: /* only should be added for manually typed chars, not undos or redos */ assert(editor->nUndoMode == umAddToUndo); /* intentional fall-through to next case */ case diUndoEndTransaction: break; case diUndoSetParagraphFormat: assert(pdi); pItem->member.para = pdi->member.para; pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); *pItem->member.para.pFmt = *pdi->member.para.pFmt; break; case diUndoInsertRun: assert(pdi); pItem->member.run = pdi->member.run; pItem->member.run.strText = ME_StrDup(pItem->member.run.strText); ME_AddRefStyle(pItem->member.run.style); if (pdi->member.run.ole_obj) { pItem->member.run.ole_obj = ALLOC_OBJ(*pItem->member.run.ole_obj); ME_CopyReObject(pItem->member.run.ole_obj, pdi->member.run.ole_obj); } else pItem->member.run.ole_obj = NULL; break; case diUndoSetCharFormat: break; case diUndoDeleteRun: case diUndoJoinParagraphs: break; case diUndoSplitParagraph: { ME_DisplayItem *prev_para = pdi->member.para.prev_para; assert(pdi->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2); pItem->member.para.pFmt->dwMask = 0; *pItem->member.para.pFmt = *pdi->member.para.pFmt; pItem->member.para.border = pdi->member.para.border; pItem->member.para.nFlags = prev_para->member.para.nFlags & ~MEPF_CELL; pItem->member.para.pCell = NULL; break; } default: assert(0 == "AddUndoItem, unsupported item type"); return NULL; } pItem->type = type; pItem->prev = NULL; if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo) { if (editor->pUndoStack && editor->pUndoStack->type == diUndoPotentialEndTransaction) { editor->pUndoStack->type = diUndoEndTransaction; } if (editor->nUndoMode == umAddToUndo) TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type)); else TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type)); pItem->next = editor->pUndoStack; if (type == diUndoEndTransaction || type == diUndoPotentialEndTransaction) editor->nUndoStackSize++; if (editor->pUndoStack) editor->pUndoStack->prev = pItem; else editor->pUndoStackBottom = pItem; editor->pUndoStack = pItem; if (editor->nUndoStackSize > editor->nUndoLimit) { /* remove oldest undo from stack */ ME_DisplayItem *p = editor->pUndoStackBottom; while (p->type !=diUndoEndTransaction) p = p->prev; /*find new stack bottom */ editor->pUndoStackBottom = p->prev; editor->pUndoStackBottom->next = NULL; do { ME_DisplayItem *pp = p->next; ME_DestroyDisplayItem(p); p = pp; } while (p); editor->nUndoStackSize--; } /* any new operation (not redo) clears the redo stack */ if (editor->nUndoMode == umAddToUndo) { ME_DisplayItem *p = editor->pRedoStack; while(p) { ME_DisplayItem *pp = p->next; ME_DestroyDisplayItem(p); p = pp; } editor->pRedoStack = NULL; } } else if (editor->nUndoMode == umAddToRedo) { TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type)); pItem->next = editor->pRedoStack; if (editor->pRedoStack) editor->pRedoStack->prev = pItem; editor->pRedoStack = pItem; } else assert(0); return (ME_UndoItem *)pItem; } }
static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem) { ME_UndoItem *pUItem = (ME_UndoItem *)pItem; if (editor->nUndoMode == umIgnore) return; TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type)); switch(pItem->type) { case diUndoEndTransaction: assert(0); case diUndoSetParagraphFormat: { ME_Cursor tmp; ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp); ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt); break; } case diUndoSetCharFormat: { ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt); break; } case diUndoSetDefaultCharFormat: { ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt); break; } case diUndoInsertRun: { ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem); break; } case diUndoDeleteRun: { ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen); break; } case diUndoJoinParagraphs: { ME_Cursor tmp; ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); /* the only thing that's needed is paragraph offset, so no need to split runs */ ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun)); break; } case diUndoSplitParagraph: { ME_Cursor tmp; ME_DisplayItem *new_para; ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); if (tmp.nOffset) tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); assert(pUItem->nCR >= 0); assert(pUItem->nLF >= 0); new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style, pUItem->nCR, pUItem->nLF); assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); *new_para->member.para.pFmt = *pItem->member.para.pFmt; break; } default: assert(0 == "PlayUndoItem, unexpected type"); } }
static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem) { ME_UndoItem *pUItem = (ME_UndoItem *)pItem; if (editor->nUndoMode == umIgnore) return; TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type)); switch(pItem->type) { case diUndoPotentialEndTransaction: case diUndoEndTransaction: assert(0); case diUndoSetParagraphFormat: { ME_Cursor tmp; ME_DisplayItem *para; ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp); para = ME_FindItemBack(tmp.pRun, diParagraph); ME_AddUndoItem(editor, diUndoSetParagraphFormat, para); *para->member.para.pFmt = *pItem->member.para.pFmt; para->member.para.border = pItem->member.para.border; break; } case diUndoSetCharFormat: { ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt); break; } case diUndoInsertRun: { ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem); break; } case diUndoDeleteRun: { ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen, TRUE); break; } case diUndoJoinParagraphs: { ME_Cursor tmp; ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); /* the only thing that's needed is paragraph offset, so no need to split runs */ ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun), TRUE); break; } case diUndoSplitParagraph: { ME_Cursor tmp; ME_DisplayItem *this_para, *new_para; BOOL bFixRowStart; int paraFlags = pItem->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND); ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); if (tmp.nOffset) tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); assert(pUItem->nCR >= 0); assert(pUItem->nLF >= 0); this_para = ME_GetParagraph(tmp.pRun); bFixRowStart = this_para->member.para.nFlags & MEPF_ROWSTART; if (bFixRowStart) { /* Re-insert the paragraph before the table, making sure the nFlag value * is correct. */ this_para->member.para.nFlags &= ~MEPF_ROWSTART; } new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style, pUItem->nCR, pUItem->nLF, paraFlags); if (bFixRowStart) new_para->member.para.nFlags |= MEPF_ROWSTART; assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); *new_para->member.para.pFmt = *pItem->member.para.pFmt; new_para->member.para.border = pItem->member.para.border; if (pItem->member.para.pCell) { ME_DisplayItem *pItemCell, *pCell; pItemCell = pItem->member.para.pCell; pCell = new_para->member.para.pCell; pCell->member.cell.nRightBoundary = pItemCell->member.cell.nRightBoundary; pCell->member.cell.border = pItemCell->member.cell.border; } break; } default: assert(0 == "PlayUndoItem, unexpected type"); } }
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) { if (editor->nUndoMode == umIgnore) return NULL; else if (editor->nUndoLimit == 0) return NULL; else { ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem); switch(type) { case diUndoEndTransaction: break; case diUndoSetParagraphFormat: assert(pdi); CopyMemory(&pItem->member.para, &pdi->member.para, sizeof(ME_Paragraph)); pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); CopyMemory(pItem->member.para.pFmt, pdi->member.para.pFmt, sizeof(PARAFORMAT2)); break; case diUndoInsertRun: assert(pdi); CopyMemory(&pItem->member.run, &pdi->member.run, sizeof(ME_Run)); pItem->member.run.strText = ME_StrDup(pItem->member.run.strText); ME_AddRefStyle(pItem->member.run.style); break; case diUndoSetCharFormat: case diUndoSetDefaultCharFormat: break; case diUndoDeleteRun: case diUndoJoinParagraphs: break; case diUndoSplitParagraph: pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2); pItem->member.para.pFmt->dwMask = 0; break; default: assert(0 == "AddUndoItem, unsupported item type"); return NULL; } pItem->type = type; pItem->prev = NULL; if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo) { if (editor->nUndoMode == umAddToUndo) TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type)); else TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type)); pItem->next = editor->pUndoStack; if (type == diUndoEndTransaction) editor->nUndoStackSize++; if (editor->pUndoStack) editor->pUndoStack->prev = pItem; else editor->pUndoStackBottom = pItem; editor->pUndoStack = pItem; if (editor->nUndoStackSize > editor->nUndoLimit) { /* remove oldest undo from stack */ ME_DisplayItem *p = editor->pUndoStackBottom; while (p->type !=diUndoEndTransaction) p = p->prev; /*find new stack bottom */ editor->pUndoStackBottom = p->prev; editor->pUndoStackBottom->next = NULL; do { ME_DisplayItem *pp = p->next; ME_DestroyDisplayItem(p); p = pp; } while (p); editor->nUndoStackSize--; } /* any new operation (not redo) clears the redo stack */ if (editor->nUndoMode == umAddToUndo) { ME_DisplayItem *p = editor->pRedoStack; while(p) { ME_DisplayItem *pp = p->next; ME_DestroyDisplayItem(p); p = pp; } editor->pRedoStack = NULL; } } else if (editor->nUndoMode == umAddToRedo) { TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type)); pItem->next = editor->pRedoStack; if (editor->pRedoStack) editor->pRedoStack->prev = pItem; editor->pRedoStack = pItem; } else assert(0); return (ME_UndoItem *)pItem; } }