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"); } }
/* join tp with tp->member.para.next_para, keeping tp's style; this * is consistent with the original */ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, BOOL keepFirstParaFormat) { ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp, *pCell = NULL; int i, shift; int end_len; CHARFORMAT2W fmt; ME_Cursor startCur, endCur; ME_String *eol_str; assert(tp->type == diParagraph); assert(tp->member.para.next_para); assert(tp->member.para.next_para->type == diParagraph); pNext = tp->member.para.next_para; /* Need to locate end-of-paragraph run here, in order to know end_len */ pRun = ME_FindItemBack(pNext, diRunOrParagraph); assert(pRun); assert(pRun->type == diRun); assert(pRun->member.run.nFlags & MERF_ENDPARA); end_len = pRun->member.run.len; eol_str = ME_VSplitString( tp->member.para.text, pRun->member.run.nCharOfs ); ME_AppendString( tp->member.para.text, pNext->member.para.text->szData, pNext->member.para.text->nLen ); /* null char format operation to store the original char format for the ENDPARA run */ ME_InitCharFormat2W(&fmt); endCur.pPara = pNext; endCur.pRun = ME_FindItemFwd(pNext, diRun); endCur.nOffset = 0; startCur = endCur; ME_PrevRun(&startCur.pPara, &startCur.pRun); ME_SetCharFormat(editor, &startCur, &endCur, &fmt); if (!editor->bEmulateVersion10) { /* v4.1 */ /* Table cell/row properties are always moved over from the removed para. */ tp->member.para.nFlags = pNext->member.para.nFlags; tp->member.para.pCell = pNext->member.para.pCell; /* Remove cell boundary if it is between the end paragraph run and the next * paragraph display item. */ for (pTmp = pRun->next; pTmp != pNext; pTmp = pTmp->next) { if (pTmp->type == diCell) { pCell = pTmp; break; } } } add_undo_split_para( editor, &pNext->member.para, eol_str, pCell ? &pCell->member.cell : NULL ); if (pCell) { ME_Remove( pCell ); if (pCell->member.cell.prev_cell) pCell->member.cell.prev_cell->member.cell.next_cell = pCell->member.cell.next_cell; if (pCell->member.cell.next_cell) pCell->member.cell.next_cell->member.cell.prev_cell = pCell->member.cell.prev_cell; ME_DestroyDisplayItem( pCell ); } if (!keepFirstParaFormat) { add_undo_set_para_fmt( editor, &tp->member.para ); *tp->member.para.pFmt = *pNext->member.para.pFmt; tp->member.para.border = pNext->member.para.border; } shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - end_len; pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); assert(pFirstRunInNext->type == diRun); /* Update selection cursors so they don't point to the removed end * paragraph run, and point to the correct paragraph. */ for (i=0; i < editor->nCursors; i++) { if (editor->pCursors[i].pRun == pRun) { editor->pCursors[i].pRun = pFirstRunInNext; editor->pCursors[i].nOffset = 0; } else if (editor->pCursors[i].pPara == pNext) { editor->pCursors[i].pPara = tp; } } pTmp = pNext; do { pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd); if (pTmp->type != diRun) break; TRACE("shifting %s by %d (previous %d)\n", debugstr_run( &pTmp->member.run ), shift, pTmp->member.run.nCharOfs); pTmp->member.run.nCharOfs += shift; pTmp->member.run.para = &tp->member.para; } while(1); ME_Remove(pRun); ME_DestroyDisplayItem(pRun); if (editor->pLastSelStartPara == pNext) editor->pLastSelStartPara = tp; if (editor->pLastSelEndPara == pNext) editor->pLastSelEndPara = tp; tp->member.para.next_para = pNext->member.para.next_para; pNext->member.para.next_para->member.para.prev_para = tp; ME_Remove(pNext); ME_DestroyDisplayItem(pNext); ME_PropagateCharOffset(tp->member.para.next_para, -end_len); ME_CheckCharOffsets(editor); editor->nParagraphs--; tp->member.para.nFlags |= MEPF_REWRAP; return tp; }
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"); } }
/* join tp with tp->member.para.next_para, keeping tp's style; this * is consistent with the original */ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, BOOL keepFirstParaFormat) { ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp; int i, shift; ME_UndoItem *undo = NULL; int end_len; CHARFORMAT2W fmt; ME_Cursor startCur, endCur; assert(tp->type == diParagraph); assert(tp->member.para.next_para); assert(tp->member.para.next_para->type == diParagraph); pNext = tp->member.para.next_para; /* Need to locate end-of-paragraph run here, in order to know end_len */ pRun = ME_FindItemBack(pNext, diRunOrParagraph); assert(pRun); assert(pRun->type == diRun); assert(pRun->member.run.nFlags & MERF_ENDPARA); end_len = pRun->member.run.strText->nLen; /* null char format operation to store the original char format for the ENDPARA run */ ME_InitCharFormat2W(&fmt); endCur.pPara = pNext; endCur.pRun = ME_FindItemFwd(pNext, diRun); endCur.nOffset = 0; startCur = endCur; ME_PrevRun(&startCur.pPara, &startCur.pRun); ME_SetCharFormat(editor, &startCur, &endCur, &fmt); undo = ME_AddUndoItem(editor, diUndoSplitParagraph, pNext); if (undo) { undo->nStart = pNext->member.para.nCharOfs - end_len; undo->eol_str = pRun->member.run.strText; pRun->member.run.strText = NULL; /* Avoid freeing the string */ } if (!keepFirstParaFormat) { ME_AddUndoItem(editor, diUndoSetParagraphFormat, tp); *tp->member.para.pFmt = *pNext->member.para.pFmt; tp->member.para.border = pNext->member.para.border; } if (!editor->bEmulateVersion10) { /* v4.1 */ /* Table cell/row properties are always moved over from the removed para. */ tp->member.para.nFlags = pNext->member.para.nFlags; tp->member.para.pCell = pNext->member.para.pCell; /* Remove cell boundary if it is between the end paragraph run and the next * paragraph display item. */ pTmp = pRun->next; while (pTmp != pNext) { if (pTmp->type == diCell) { ME_Cell *pCell = &pTmp->member.cell; if (undo) { assert(!(undo->di.member.para.nFlags & MEPF_ROWEND)); if (!(undo->di.member.para.nFlags & MEPF_ROWSTART)) undo->di.member.para.nFlags |= MEPF_CELL; undo->di.member.para.pCell = ALLOC_OBJ(ME_DisplayItem); *undo->di.member.para.pCell = *pTmp; undo->di.member.para.pCell->next = NULL; undo->di.member.para.pCell->prev = NULL; undo->di.member.para.pCell->member.cell.next_cell = NULL; undo->di.member.para.pCell->member.cell.prev_cell = NULL; } ME_Remove(pTmp); if (pCell->prev_cell) pCell->prev_cell->member.cell.next_cell = pCell->next_cell; if (pCell->next_cell) pCell->next_cell->member.cell.prev_cell = pCell->prev_cell; ME_DestroyDisplayItem(pTmp); break; } pTmp = pTmp->next; } } shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - end_len; pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); assert(pFirstRunInNext->type == diRun); /* Update selection cursors so they don't point to the removed end * paragraph run, and point to the correct paragraph. */ for (i=0; i < editor->nCursors; i++) { if (editor->pCursors[i].pRun == pRun) { editor->pCursors[i].pRun = pFirstRunInNext; editor->pCursors[i].nOffset = 0; } else if (editor->pCursors[i].pPara == pNext) { editor->pCursors[i].pPara = tp; } } pTmp = pNext; do { pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd); if (pTmp->type != diRun) break; TRACE("shifting \"%s\" by %d (previous %d)\n", debugstr_w(pTmp->member.run.strText->szData), shift, pTmp->member.run.nCharOfs); pTmp->member.run.nCharOfs += shift; } while(1); ME_Remove(pRun); ME_DestroyDisplayItem(pRun); if (editor->pLastSelStartPara == pNext) editor->pLastSelStartPara = tp; if (editor->pLastSelEndPara == pNext) editor->pLastSelEndPara = tp; tp->member.para.next_para = pNext->member.para.next_para; pNext->member.para.next_para->member.para.prev_para = tp; ME_Remove(pNext); ME_DestroyDisplayItem(pNext); ME_PropagateCharOffset(tp->member.para.next_para, -end_len); ME_CheckCharOffsets(editor); editor->nParagraphs--; tp->member.para.nFlags |= MEPF_REWRAP; return tp; }