BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start, int nChars, BOOL bForce) { ME_Cursor c = *start; int nOfs = ME_GetCursorOfs(start), text_len = ME_GetTextLength( editor ); int shift = 0; int totalChars = nChars; ME_DisplayItem *start_para; BOOL delete_all = FALSE; /* Prevent deletion past last end of paragraph run. */ nChars = min(nChars, text_len - nOfs); if (nChars == text_len) delete_all = TRUE; start_para = c.pPara; if (!bForce) { ME_ProtectPartialTableDeletion(editor, &c, &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 + c.pPara->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. */ ME_PrevRun(&c.pPara, &c.pRun); c.nOffset = c.pRun->member.run.len; } run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = c.pRun->member.run.len; 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, c.pPara, 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_run( run ), run->len); /* nOfs is a character offset (from the start of the document to the current (deleted) run */ add_undo_insert_run( editor, nOfs + nChars, get_text( run, c.nOffset ), nCharsToDelete, run->nFlags, run->style ); ME_StrDeleteV(run->para->text, run->nCharOfs + c.nOffset, nCharsToDelete); run->len -= nCharsToDelete; TRACE("Post deletion string: %s (%d)\n", debugstr_run( run ), run->len); TRACE("Shift value: %d\n", shift); /* 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 <= run->len); } if (pThisCur->nOffset == run->len) { 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 (!cursor.pRun->member.run.len) { TRACE("Removing empty run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } if (delete_all) ME_SetDefaultParaFormat( editor, start_para->member.para.pFmt ); return TRUE; }
/* 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_MoveCursorLines(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) { ME_DisplayItem *pRun = pCursor->pRun; ME_DisplayItem *pOldPara = pCursor->pPara; ME_DisplayItem *pItem, *pNewPara; int x = ME_GetXForArrow(editor, pCursor); if (editor->bCaretAtEnd && !pCursor->nOffset) if (!ME_PrevRun(&pOldPara, &pRun)) return; if (nRelOfs == -1) { /* start of this row */ pItem = ME_FindItemBack(pRun, diStartRow); assert(pItem); /* start of the previous row */ pItem = ME_FindItemBack(pItem, diStartRow); if (!pItem) return; /* row not found - ignore */ pNewPara = ME_GetParagraph(pItem); if (pOldPara->member.para.nFlags & MEPF_ROWEND || (pOldPara->member.para.pCell && pOldPara->member.para.pCell != pNewPara->member.para.pCell)) { /* Brought out of a cell */ pNewPara = ME_GetTableRowStart(pOldPara)->member.para.prev_para; if (pNewPara->type == diTextStart) return; /* At the top, so don't go anywhere. */ pItem = ME_FindItemFwd(pNewPara, diStartRow); } if (pNewPara->member.para.nFlags & MEPF_ROWEND) { /* Brought into a table row */ ME_Cell *cell = &ME_FindItemBack(pNewPara, diCell)->member.cell; while (x < cell->pt.x && cell->prev_cell) cell = &cell->prev_cell->member.cell; if (cell->next_cell) /* else - we are still at the end of the row */ pItem = ME_FindItemBack(cell->next_cell, diStartRow); } } else { /* start of the next row */ pItem = ME_FindItemFwd(pRun, diStartRow); if (!pItem) return; /* row not found - ignore */ pNewPara = ME_GetParagraph(pItem); if (pOldPara->member.para.nFlags & MEPF_ROWSTART || (pOldPara->member.para.pCell && pOldPara->member.para.pCell != pNewPara->member.para.pCell)) { /* Brought out of a cell */ pNewPara = ME_GetTableRowEnd(pOldPara)->member.para.next_para; if (pNewPara->type == diTextEnd) return; /* At the bottom, so don't go anywhere. */ pItem = ME_FindItemFwd(pNewPara, diStartRow); } if (pNewPara->member.para.nFlags & MEPF_ROWSTART) { /* Brought into a table row */ ME_DisplayItem *cell = ME_FindItemFwd(pNewPara, diCell); while (cell->member.cell.next_cell && x >= cell->member.cell.next_cell->member.cell.pt.x) cell = cell->member.cell.next_cell; pItem = ME_FindItemFwd(cell, diStartRow); } } if (!pItem) { /* row not found - ignore */ return; } ME_FindRunInRow(editor, pItem, x, pCursor, &editor->bCaretAtEnd); assert(pCursor->pRun); assert(pCursor->pRun->type == diRun); }
/* 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; }