BOOL ME_Redo(ME_TextEditor *editor) { ME_DisplayItem *p; ME_UndoMode nMode = editor->nUndoMode; assert(nMode == umAddToUndo || nMode == umIgnore); if (editor->nUndoMode == umIgnore) return FALSE; /* no redo items ? */ if (!editor->pRedoStack) return FALSE; /* watch out for uncommitted transactions ! */ assert(editor->pRedoStack->type == diUndoEndTransaction); editor->nUndoMode = umAddBackToUndo; p = editor->pRedoStack->next; ME_DestroyDisplayItem(editor->pRedoStack); editor->pRedoStack = p; do { p->prev = NULL; ME_PlayUndoItem(editor, p); editor->pRedoStack = p->next; ME_DestroyDisplayItem(p); p = editor->pRedoStack; } while(p && p->type != diUndoEndTransaction); if (p) p->prev = NULL; ME_MoveCursorFromTableRowStartParagraph(editor); ME_AddUndoItem(editor, diUndoEndTransaction, NULL); ME_CheckTablesForCorruption(editor); editor->nUndoMode = nMode; ME_UpdateRepaint(editor); return TRUE; }
/** * Commits preceding changes into a transaction that can be undone together. * * This should be called after all the changes occur associated with an event * so that the group of changes can be undone atomically as a transaction. * * This will have no effect the undo mode is set to ignore changes, or if no * changes preceded calling this function before the last time it was called. * * This can also be used to conclude a coalescing transaction (used for grouping * typed characters). */ void ME_CommitUndo(ME_TextEditor *editor) { if (editor->nUndoMode == umIgnore) return; assert(editor->nUndoMode == umAddToUndo); /* no transactions, no need to commit */ if (!editor->pUndoStack) return; /* no need to commit empty transactions */ if (editor->pUndoStack->type == diUndoEndTransaction) return; if (editor->pUndoStack->type == diUndoPotentialEndTransaction) { /* Previous transaction was as a result of characters typed, * so the end of this transaction is confirmed. */ editor->pUndoStack->type = diUndoEndTransaction; return; } ME_AddUndoItem(editor, diUndoEndTransaction, NULL); ME_SendSelChange(editor); }
void ME_Redo(ME_TextEditor *editor) { ME_DisplayItem *p; ME_UndoMode nMode = editor->nUndoMode; assert(nMode == umAddToUndo || nMode == umIgnore); if (editor->nUndoMode == umIgnore) return; /* no redo items ? */ if (!editor->pRedoStack) return; /* watch out for uncommitted transactions ! */ assert(editor->pRedoStack->type == diUndoEndTransaction); editor->nUndoMode = umAddBackToUndo; p = editor->pRedoStack->next; ME_DestroyDisplayItem(editor->pRedoStack); do { ME_DisplayItem *pp = p; ME_PlayUndoItem(editor, p); p = p->next; ME_DestroyDisplayItem(pp); } while(p && p->type != diUndoEndTransaction); ME_AddUndoItem(editor, diUndoEndTransaction, NULL); editor->pRedoStack = p; if (p) p->prev = NULL; editor->nUndoMode = nMode; ME_UpdateRepaint(editor); }
void ME_CommitUndo(ME_TextEditor *editor) { if (editor->nUndoMode == umIgnore) return; assert(editor->nUndoMode == umAddToUndo); /* no transactions, no need to commit */ if (!editor->pUndoStack) return; /* no need to commit empty transactions */ if (editor->pUndoStack->type == diUndoEndTransaction) return; ME_AddUndoItem(editor, diUndoEndTransaction, NULL); ME_SendSelChange(editor); }
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"); } }
BOOL ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars, BOOL bForce) { ME_Cursor c; int shift = 0; int totalChars = nChars; ME_DisplayItem *start_para; { /* Prevent deletion past last end of paragraph run. */ ME_DisplayItem *pTextEnd = editor->pBuffer->pLast; int nMaxChars = pTextEnd->member.para.prev_para->member.para.nCharOfs; nMaxChars += ME_FindItemBack(pTextEnd, diRun)->member.run.nCharOfs; nMaxChars -= nOfs; nChars = min(nChars, nMaxChars); } ME_CursorFromCharOfs(editor, nOfs, &c); start_para = ME_GetParagraph(c.pRun); if (!bForce) { ME_ProtectPartialTableDeletion(editor, nOfs, &nChars); if (nChars == 0) return FALSE; } while(nChars > 0) { ME_Run *run; ME_CursorFromCharOfs(editor, nOfs+nChars, &c); if (!c.nOffset && nOfs+nChars == (c.pRun->member.run.nCharOfs + ME_GetParagraph(c.pRun)->member.para.nCharOfs)) { /* We aren't deleting anything in this run, so we will go back to the * last run we are deleting text in. */ c.pRun = ME_FindItemBack(c.pRun, diRun); if (c.pRun->member.run.nFlags & MERF_ENDPARA) c.nOffset = c.pRun->member.run.nCR + c.pRun->member.run.nLF; else c.nOffset = c.pRun->member.run.strText->nLen; } run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = run->nCR + run->nLF; BOOL keepFirstParaFormat; if (!ME_FindItemFwd(c.pRun, diParagraph)) { return TRUE; } keepFirstParaFormat = (totalChars == nChars && nChars <= eollen && run->nCharOfs); if (!editor->bEmulateVersion10) /* v4.1 */ { ME_DisplayItem *next_para = ME_FindItemFwd(c.pRun, diParagraphOrEnd); ME_DisplayItem *this_para = next_para->member.para.prev_para; /* The end of paragraph before a table row is only deleted if there * is nothing else on the line before it. */ if (this_para == start_para && next_para->member.para.nFlags & MEPF_ROWSTART) { /* If the paragraph will be empty, then it should be deleted, however * it still might have text right now which would inherit the * MEPF_STARTROW property if we joined it right now. * Instead we will delete it after the preceding text is deleted. */ if (nOfs > this_para->member.para.nCharOfs) { /* Skip this end of line. */ nChars -= (eollen < nChars) ? eollen : nChars; continue; } keepFirstParaFormat = TRUE; } } ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun), keepFirstParaFormat); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); nChars -= (eollen < nChars) ? eollen : nChars; continue; } else { ME_Cursor cursor; int nCharsToDelete = min(nChars, c.nOffset); int i; c.nOffset -= nCharsToDelete; ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP; cursor = c; /* nChars is the number of characters that should be deleted from the PRECEDING runs (these BEFORE cursor.pRun) nCharsToDelete is a number of chars to delete from THIS run */ nChars -= nCharsToDelete; shift -= nCharsToDelete; TRACE("Deleting %d (remaning %d) chars at %d in '%s' (%d)\n", nCharsToDelete, nChars, c.nOffset, debugstr_w(run->strText->szData), run->strText->nLen); if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete) { /* undo = reinsert whole run */ /* nOfs is a character offset (from the start of the document to the current (deleted) run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) pUndo->di.member.run.nCharOfs = nOfs+nChars; } else { /* undo = reinsert partial run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) { ME_DestroyString(pUndo->di.member.run.strText); pUndo->di.member.run.nCharOfs = nOfs+nChars; pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete); } } TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen); TRACE("Shift value: %d\n", shift); ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete); /* update cursors (including c) */ for (i=-1; i<editor->nCursors; i++) { ME_Cursor *pThisCur = editor->pCursors + i; if (i == -1) pThisCur = &c; if (pThisCur->pRun == cursor.pRun) { if (pThisCur->nOffset > cursor.nOffset) { if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) pThisCur->nOffset = cursor.nOffset; else pThisCur->nOffset -= nCharsToDelete; assert(pThisCur->nOffset >= 0); assert(pThisCur->nOffset <= ME_StrVLen(run->strText)); } if (pThisCur->nOffset == ME_StrVLen(run->strText)) { pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); assert(pThisCur->pRun->type == diRun); pThisCur->nOffset = 0; } } } /* c = updated data now */ if (c.pRun == cursor.pRun) ME_SkipAndPropagateCharOffset(c.pRun, shift); else ME_PropagateCharOffset(c.pRun, shift); if (!ME_StrVLen(cursor.pRun->member.run.strText)) { TRACE("Removing useless run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } return TRUE; }
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars) { ME_Cursor c; int shift = 0; while(nChars > 0) { ME_Run *run; ME_CursorFromCharOfs(editor, nOfs, &c); run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = run->nCR + run->nLF; if (!ME_FindItemFwd(c.pRun, diParagraph)) { return; } ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun)); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); nChars -= (eollen < nChars) ? eollen : nChars; continue; } else { ME_Cursor cursor; int nIntendedChars = nChars; int nCharsToDelete = nChars; int i; int loc = c.nOffset; ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP; cursor = c; ME_StrRelPos(run->strText, loc, &nChars); /* nChars is the number of characters that should be deleted from the FOLLOWING runs (these AFTER cursor.pRun) nCharsToDelete is a number of chars to delete from THIS run */ nCharsToDelete -= nChars; shift -= nCharsToDelete; TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n", nCharsToDelete, nIntendedChars, nChars, c.nOffset, debugstr_w(run->strText->szData), run->strText->nLen); if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete) { /* undo = reinsert whole run */ /* nOfs is a character offset (from the start of the document to the current (deleted) run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) pUndo->di.member.run.nCharOfs = nOfs; } else { /* undo = reinsert partial run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) { ME_DestroyString(pUndo->di.member.run.strText); pUndo->di.member.run.nCharOfs = nOfs; pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete); } } TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen); TRACE("Shift value: %d\n", shift); ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete); /* update cursors (including c) */ for (i=-1; i<editor->nCursors; i++) { ME_Cursor *pThisCur = editor->pCursors + i; if (i == -1) pThisCur = &c; if (pThisCur->pRun == cursor.pRun) { if (pThisCur->nOffset > cursor.nOffset) { if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) pThisCur->nOffset = cursor.nOffset; else pThisCur->nOffset -= nCharsToDelete; assert(pThisCur->nOffset >= 0); assert(pThisCur->nOffset <= ME_StrVLen(run->strText)); } if (pThisCur->nOffset == ME_StrVLen(run->strText)) { pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); assert(pThisCur->pRun->type == diRun); pThisCur->nOffset = 0; } } } /* c = updated data now */ if (c.pRun == cursor.pRun) ME_SkipAndPropagateCharOffset(c.pRun, shift); else ME_PropagateCharOffset(c.pRun, shift); if (!ME_StrVLen(cursor.pRun->member.run.strText)) { TRACE("Removing useless run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } }
/* 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; }
/* split paragraph at the beginning of the run */ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style, ME_String *eol_str, int paraFlags) { ME_DisplayItem *next_para = NULL; ME_DisplayItem *run_para = NULL; ME_DisplayItem *new_para = ME_MakeDI(diParagraph); ME_DisplayItem *end_run; ME_UndoItem *undo = NULL; int ofs, i; ME_DisplayItem *pp; int run_flags = MERF_ENDPARA; if (!editor->bEmulateVersion10) { /* v4.1 */ /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */ assert(!(paraFlags & ~(MEPF_CELL|MEPF_ROWSTART|MEPF_ROWEND))); assert(!(paraFlags & (paraFlags-1))); if (paraFlags == MEPF_CELL) run_flags |= MERF_ENDCELL; else if (paraFlags == MEPF_ROWSTART) run_flags |= MERF_TABLESTART|MERF_HIDDEN; } else { /* v1.0 - v3.0 */ assert(!(paraFlags & (MEPF_CELL|MEPF_ROWSTART|MEPF_ROWEND))); } end_run = ME_MakeRun(style, eol_str, run_flags); assert(run->type == diRun); run_para = ME_GetParagraph(run); assert(run_para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); ofs = end_run->member.run.nCharOfs = run->member.run.nCharOfs; next_para = run_para->member.para.next_para; assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd)); undo = ME_AddUndoItem(editor, diUndoJoinParagraphs, NULL); if (undo) undo->nStart = run_para->member.para.nCharOfs + ofs; /* Update selection cursors to point to the correct paragraph. */ for (i = 0; i < editor->nCursors; i++) { if (editor->pCursors[i].pPara == run_para && run->member.run.nCharOfs <= editor->pCursors[i].pRun->member.run.nCharOfs) { editor->pCursors[i].pPara = new_para; } } /* the new paragraph will have a different starting offset, so let's update its runs */ pp = run; while(pp->type == diRun) { pp->member.run.nCharOfs -= ofs; pp = ME_FindItemFwd(pp, diRunOrParagraphOrEnd); } new_para->member.para.nCharOfs = run_para->member.para.nCharOfs + ofs; new_para->member.para.nCharOfs += eol_str->nLen; new_para->member.para.nFlags = MEPF_REWRAP; /* FIXME initialize format style and call ME_SetParaFormat blah blah */ *new_para->member.para.pFmt = *run_para->member.para.pFmt; new_para->member.para.border = run_para->member.para.border; /* insert paragraph into paragraph double linked list */ new_para->member.para.prev_para = run_para; new_para->member.para.next_para = next_para; run_para->member.para.next_para = new_para; next_para->member.para.prev_para = new_para; /* insert end run of the old paragraph, and new paragraph, into DI double linked list */ ME_InsertBefore(run, new_para); ME_InsertBefore(new_para, end_run); if (!editor->bEmulateVersion10) { /* v4.1 */ if (paraFlags & (MEPF_ROWSTART|MEPF_CELL)) { ME_DisplayItem *cell = ME_MakeDI(diCell); ME_InsertBefore(new_para, cell); new_para->member.para.pCell = cell; cell->member.cell.next_cell = NULL; if (paraFlags & MEPF_ROWSTART) { run_para->member.para.nFlags |= MEPF_ROWSTART; cell->member.cell.prev_cell = NULL; cell->member.cell.parent_cell = run_para->member.para.pCell; if (run_para->member.para.pCell) cell->member.cell.nNestingLevel = run_para->member.para.pCell->member.cell.nNestingLevel + 1; else cell->member.cell.nNestingLevel = 1; } else { cell->member.cell.prev_cell = run_para->member.para.pCell; assert(cell->member.cell.prev_cell); cell->member.cell.prev_cell->member.cell.next_cell = cell; assert(run_para->member.para.nFlags & MEPF_CELL); assert(!(run_para->member.para.nFlags & MEPF_ROWSTART)); cell->member.cell.nNestingLevel = cell->member.cell.prev_cell->member.cell.nNestingLevel; cell->member.cell.parent_cell = cell->member.cell.prev_cell->member.cell.parent_cell; } } else if (paraFlags & MEPF_ROWEND) { run_para->member.para.nFlags |= MEPF_ROWEND; run_para->member.para.pCell = run_para->member.para.pCell->member.cell.parent_cell; new_para->member.para.pCell = run_para->member.para.pCell; assert(run_para->member.para.prev_para->member.para.nFlags & MEPF_CELL); assert(!(run_para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART)); if (new_para->member.para.pCell != new_para->member.para.next_para->member.para.pCell && new_para->member.para.next_para->member.para.pCell && !new_para->member.para.next_para->member.para.pCell->member.cell.prev_cell) { /* Row starts just after the row that was ended. */ new_para->member.para.nFlags |= MEPF_ROWSTART; } } else { new_para->member.para.pCell = run_para->member.para.pCell; } ME_UpdateTableFlags(run_para); ME_UpdateTableFlags(new_para); } /* force rewrap of the */ run_para->member.para.prev_para->member.para.nFlags |= MEPF_REWRAP; new_para->member.para.prev_para->member.para.nFlags |= MEPF_REWRAP; /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */ ME_PropagateCharOffset(next_para, eol_str->nLen); editor->nParagraphs++; return new_para; }
static BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFORMAT2 *pFmt) { PARAFORMAT2 copy; DWORD dwMask; assert(para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); dwMask = pFmt->dwMask; if (pFmt->cbSize < sizeof(PARAFORMAT)) return FALSE; else if (pFmt->cbSize < sizeof(PARAFORMAT2)) dwMask &= PFM_ALL; else dwMask &= PFM_ALL2; ME_AddUndoItem(editor, diUndoSetParagraphFormat, para); copy = *para->member.para.pFmt; #define COPY_FIELD(m, f) \ if (dwMask & (m)) { \ para->member.para.pFmt->dwMask |= m; \ para->member.para.pFmt->f = pFmt->f; \ } COPY_FIELD(PFM_NUMBERING, wNumbering); COPY_FIELD(PFM_STARTINDENT, dxStartIndent); if (dwMask & PFM_OFFSETINDENT) para->member.para.pFmt->dxStartIndent += pFmt->dxStartIndent; COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent); COPY_FIELD(PFM_OFFSET, dxOffset); COPY_FIELD(PFM_ALIGNMENT, wAlignment); if (dwMask & PFM_TABSTOPS) { para->member.para.pFmt->cTabCount = pFmt->cTabCount; memcpy(para->member.para.pFmt->rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG)); } if (dwMask & (PFM_ALL2 & ~PFM_ALL)) { /* PARAFORMAT2 fields */ #define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \ PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \ PFM_TABLE) /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */ if (dwMask & EFFECTS_MASK) { para->member.para.pFmt->dwMask |= dwMask & EFFECTS_MASK; para->member.para.pFmt->wEffects &= ~HIWORD(dwMask); para->member.para.pFmt->wEffects |= pFmt->wEffects & HIWORD(dwMask); } #undef EFFECTS_MASK COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore); COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter); COPY_FIELD(PFM_LINESPACING, dyLineSpacing); COPY_FIELD(PFM_STYLE, sStyle); COPY_FIELD(PFM_LINESPACING, bLineSpacingRule); COPY_FIELD(PFM_SHADING, wShadingWeight); COPY_FIELD(PFM_SHADING, wShadingStyle); COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart); COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle); COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab); COPY_FIELD(PFM_BORDER, wBorderSpace); COPY_FIELD(PFM_BORDER, wBorderWidth); COPY_FIELD(PFM_BORDER, wBorders); } para->member.para.pFmt->dwMask |= dwMask; #undef COPY_FIELD if (memcmp(©, para->member.para.pFmt, sizeof(PARAFORMAT2))) para->member.para.nFlags |= MEPF_REWRAP; return TRUE; }