/* Finds the run and offset from the pixel position. * * x & y are pixel positions in virtual coordinates into the rich edit control, * so client coordinates must first be adjusted by the scroll position. * * returns TRUE if the result was exactly under the cursor, otherwise returns * FALSE, and result is set to the closest position to the coordinates. */ static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol) { ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para; BOOL isExact = TRUE; x -= editor->rcFormat.left; y -= editor->rcFormat.top; if (is_eol) *is_eol = FALSE; /* find paragraph */ for (; p != editor->pBuffer->pLast; p = p->member.para.next_para) { assert(p->type == diParagraph); if (y < p->member.para.pt.y + p->member.para.nHeight) { if (p->member.para.nFlags & MEPF_ROWSTART) p = ME_FindPixelPosInTableRow(x, y, p); y -= p->member.para.pt.y; p = ME_FindItemFwd(p, diStartRow); break; } else if (p->member.para.nFlags & MEPF_ROWSTART) { p = ME_GetTableRowEnd(p); } } /* find row */ for (; p != editor->pBuffer->pLast; ) { ME_DisplayItem *pp; assert(p->type == diStartRow); if (y < p->member.row.pt.y + p->member.row.nHeight) break; pp = ME_FindItemFwd(p, diStartRow); if (!pp) break; p = pp; } if (p == editor->pBuffer->pLast) { /* The position is below the last paragraph, so the last row will be used * rather than the end of the text, so the x position will be used to * determine the offset closest to the pixel position. */ isExact = FALSE; p = ME_FindItemBack(p, diStartRow); if (!p) p = editor->pBuffer->pLast; } assert( p->type == diStartRow || p == editor->pBuffer->pLast ); if( p->type == diStartRow ) return ME_FindRunInRow( editor, p, x, result, is_eol ) && isExact; result->pRun = ME_FindItemBack(p, diRun); result->pPara = ME_GetParagraph(result->pRun); result->nOffset = 0; assert(result->pRun->member.run.nFlags & MERF_ENDPARA); return FALSE; }
ME_DisplayItem* ME_AppendTableRow(ME_TextEditor *editor, ME_DisplayItem *table_row) { WCHAR endl = '\r', tab = '\t'; ME_DisplayItem *run; PARAFORMAT2 *pFmt; int i; assert(table_row); assert(table_row->type == diParagraph); if (!editor->bEmulateVersion10) { /* v4.1 */ ME_DisplayItem *insertedCell, *para, *cell, *prevTableEnd; cell = ME_FindItemFwd(ME_GetTableRowStart(table_row), diCell); prevTableEnd = ME_GetTableRowEnd(table_row); para = prevTableEnd->member.para.next_para; run = ME_FindItemFwd(para, diRun); editor->pCursors[0].pPara = para; editor->pCursors[0].pRun = run; editor->pCursors[0].nOffset = 0; editor->pCursors[1] = editor->pCursors[0]; para = ME_InsertTableRowStartFromCursor(editor); insertedCell = ME_FindItemFwd(para, diCell); /* Copy cell properties */ insertedCell->member.cell.nRightBoundary = cell->member.cell.nRightBoundary; insertedCell->member.cell.border = cell->member.cell.border; while (cell->member.cell.next_cell) { cell = cell->member.cell.next_cell; para = ME_InsertTableCellFromCursor(editor); insertedCell = ME_FindItemBack(para, diCell); /* Copy cell properties */ insertedCell->member.cell.nRightBoundary = cell->member.cell.nRightBoundary; insertedCell->member.cell.border = cell->member.cell.border; }; para = ME_InsertTableRowEndFromCursor(editor); *para->member.para.pFmt = *prevTableEnd->member.para.pFmt; /* return the table row start for the inserted paragraph */ return ME_FindItemFwd(cell, diParagraph)->member.para.next_para; } else { /* v1.0 - 3.0 */ run = ME_FindItemBack(table_row->member.para.next_para, diRun); pFmt = table_row->member.para.pFmt; assert(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE); editor->pCursors[0].pPara = table_row; editor->pCursors[0].pRun = run; editor->pCursors[0].nOffset = 0; editor->pCursors[1] = editor->pCursors[0]; ME_InsertTextFromCursor(editor, 0, &endl, 1, run->member.run.style); run = editor->pCursors[0].pRun; for (i = 0; i < pFmt->cTabCount; i++) { ME_InsertTextFromCursor(editor, 0, &tab, 1, run->member.run.style); } return table_row->member.para.next_para; } }
static void ME_BeginRow(ME_WrapContext *wc) { PARAFORMAT2 *pFmt; ME_DisplayItem *para = wc->pPara; pFmt = para->member.para.pFmt; wc->pRowStart = NULL; wc->bOverflown = FALSE; wc->pLastSplittableRun = NULL; wc->bWordWrap = wc->context->editor->bWordWrap; if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) { wc->nAvailWidth = 0; wc->bWordWrap = FALSE; if (para->member.para.nFlags & MEPF_ROWEND) { ME_Cell *cell = &ME_FindItemBack(para, diCell)->member.cell; cell->nWidth = 0; } } else if (para->member.para.pCell) { ME_Cell *cell = ¶->member.para.pCell->member.cell; int width; width = cell->nRightBoundary; if (cell->prev_cell) width -= cell->prev_cell->member.cell.nRightBoundary; if (!cell->prev_cell) { int rowIndent = ME_GetTableRowEnd(para)->member.para.pFmt->dxStartIndent; width -= rowIndent; } cell->nWidth = max(ME_twips2pointsX(wc->context, width), 0); wc->nAvailWidth = cell->nWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; wc->bWordWrap = TRUE; } else { wc->nAvailWidth = wc->context->nAvailWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; } wc->pt.x = wc->context->pt.x; if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) /* Shift the text down because of the border. */ wc->pt.y++; }
static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) { ME_DisplayItem *p; ME_WrapContext wc; int border = 0; int linespace = 0; PARAFORMAT2 *pFmt; assert(tp->type == diParagraph); if (!(tp->member.para.nFlags & MEPF_REWRAP)) { return; } ME_PrepareParagraphForWrapping(c, tp); /* For now treating all non-password text as complex for better testing */ if (!c->editor->cPasswordMask /* && ScriptIsComplex( tp->member.para.text->szData, tp->member.para.text->nLen, SIC_COMPLEX ) == S_OK */) { if (SUCCEEDED( itemize_para( c, tp ) )) shape_para( c, tp ); } pFmt = tp->member.para.pFmt; wc.context = c; wc.pPara = tp; /* wc.para_style = tp->member.para.style; */ wc.style = NULL; if (tp->member.para.nFlags & MEPF_ROWEND) { wc.nFirstMargin = wc.nLeftMargin = wc.nRightMargin = 0; } else { int dxStartIndent = pFmt->dxStartIndent; if (tp->member.para.pCell) { dxStartIndent += ME_GetTableRowEnd(tp)->member.para.pFmt->dxOffset; } wc.nFirstMargin = ME_twips2pointsX(c, dxStartIndent); wc.nLeftMargin = wc.nFirstMargin + ME_twips2pointsX(c, pFmt->dxOffset); wc.nRightMargin = ME_twips2pointsX(c, pFmt->dxRightIndent); } if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) { wc.nFirstMargin += ME_twips2pointsX(c, pFmt->dxOffset * 2); } wc.nRow = 0; wc.pt.y = 0; if (pFmt->dwMask & PFM_SPACEBEFORE) wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceBefore); if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) && pFmt->dwMask & PFM_BORDER) { border = ME_GetParaBorderWidth(c, tp->member.para.pFmt->wBorders); if (pFmt->wBorders & 1) { wc.nFirstMargin += border; wc.nLeftMargin += border; } if (pFmt->wBorders & 2) wc.nRightMargin -= border; if (pFmt->wBorders & 4) wc.pt.y += border; } linespace = ME_GetParaLineSpace(c, &tp->member.para); ME_BeginRow(&wc); for (p = tp->next; p!=tp->member.para.next_para; ) { assert(p->type != diStartRow); if (p->type == diRun) { p = ME_WrapHandleRun(&wc, p); } else p = p->next; if (wc.nRow && p == wc.pRowStart) wc.pt.y += linespace; } ME_WrapEndParagraph(&wc, p); if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) && (pFmt->dwMask & PFM_BORDER) && (pFmt->wBorders & 8)) wc.pt.y += border; if (tp->member.para.pFmt->dwMask & PFM_SPACEAFTER) wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceAfter); tp->member.para.nFlags &= ~MEPF_REWRAP; tp->member.para.nHeight = wc.pt.y; tp->member.para.nRows = wc.nRow; }
/* Finds the run and offset from the pixel position. * * x & y are pixel positions in virtual coordinates into the rich edit control, * so client coordinates must first be adjusted by the scroll position. * * returns TRUE if the result was exactly under the cursor, otherwise returns * FALSE, and result is set to the closest position to the coordinates. */ static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol) { ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para; ME_DisplayItem *last = NULL; int rx = 0; BOOL isExact = TRUE; x -= editor->rcFormat.left; y -= editor->rcFormat.top; if (is_eol) *is_eol = 0; /* find paragraph */ for (; p != editor->pBuffer->pLast; p = p->member.para.next_para) { assert(p->type == diParagraph); if (y < p->member.para.pt.y + p->member.para.nHeight) { if (p->member.para.nFlags & MEPF_ROWSTART) p = ME_FindPixelPosInTableRow(x, y, p); y -= p->member.para.pt.y; p = ME_FindItemFwd(p, diStartRow); break; } else if (p->member.para.nFlags & MEPF_ROWSTART) { p = ME_GetTableRowEnd(p); } } /* find row */ for (; p != editor->pBuffer->pLast; ) { ME_DisplayItem *pp; assert(p->type == diStartRow); if (y < p->member.row.pt.y + p->member.row.nHeight) { p = ME_FindItemFwd(p, diRun); break; } pp = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd); if (pp->type != diStartRow) { p = ME_FindItemFwd(p, diRun); break; } p = pp; } if (p == editor->pBuffer->pLast) { /* The position is below the last paragraph, so the last row will be used * rather than the end of the text, so the x position will be used to * determine the offset closest to the pixel position. */ isExact = FALSE; p = ME_FindItemBack(p, diStartRow); if (p != NULL){ p = ME_FindItemFwd(p, diRun); } else { p = editor->pBuffer->pLast; } } for (; p != editor->pBuffer->pLast; p = p->next) { switch (p->type) { case diRun: rx = x - p->member.run.pt.x; if (rx < p->member.run.nWidth) return ME_ReturnFoundPos(editor, p, result, rx, isExact); break; case diStartRow: isExact = FALSE; p = ME_FindItemFwd(p, diRun); if (is_eol) *is_eol = 1; rx = 0; /* FIXME not sure */ return ME_ReturnFoundPos(editor, p, result, rx, isExact); case diCell: case diParagraph: case diTextEnd: isExact = FALSE; rx = 0; /* FIXME not sure */ p = last; return ME_ReturnFoundPos(editor, p, result, rx, isExact); default: assert(0); } last = p; } result->pRun = ME_FindItemBack(p, diRun); result->nOffset = 0; assert(result->pRun->member.run.nFlags & MERF_ENDPARA); return FALSE; }
static void ME_MoveCursorLines(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) { ME_DisplayItem *pRun = pCursor->pRun; ME_DisplayItem *pItem, *pOldPara, *pNewPara; int x = ME_GetXForArrow(editor, pCursor); if (editor->bCaretAtEnd && !pCursor->nOffset) pRun = ME_FindItemBack(pRun, diRun); if (!pRun) return; pOldPara = ME_GetParagraph(pRun); 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 */ /* FIXME If diParagraph is before diStartRow, wrap the next paragraph? */ 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; } pCursor->pRun = ME_FindRunInRow(editor, pItem, x, &pCursor->nOffset, &editor->bCaretAtEnd); assert(pCursor->pRun); assert(pCursor->pRun->type == diRun); }
/* Selects the next table cell or appends a new table row if at end of table */ static void ME_SelectOrInsertNextCell(ME_TextEditor *editor, ME_DisplayItem *run) { ME_DisplayItem *para = ME_GetParagraph(run); int i; assert(run && run->type == diRun); assert(ME_IsInTable(run)); if (!editor->bEmulateVersion10) { /* v4.1 */ ME_DisplayItem *cell; /* Get the initial cell */ if (para->member.para.nFlags & MEPF_ROWSTART) { cell = para->member.para.next_para->member.para.pCell; } else if (para->member.para.nFlags & MEPF_ROWEND) { cell = para->member.para.prev_para->member.para.pCell; } else { cell = para->member.para.pCell; } assert(cell); /* Get the next cell. */ if (cell->member.cell.next_cell && cell->member.cell.next_cell->member.cell.next_cell) { cell = cell->member.cell.next_cell; } else { para = ME_GetTableRowEnd(ME_FindItemFwd(cell, diParagraph)); para = para->member.para.next_para; assert(para); if (para->member.para.nFlags & MEPF_ROWSTART) { cell = para->member.para.next_para->member.para.pCell; } else { /* Insert row */ para = para->member.para.prev_para; para = ME_AppendTableRow(editor, ME_GetTableRowStart(para)); /* Put cursor at the start of the new table row */ para = para->member.para.next_para; editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); editor->pCursors[0].nOffset = 0; editor->pCursors[1] = editor->pCursors[0]; ME_WrapMarkedParagraphs(editor); return; } } /* Select cell */ editor->pCursors[1].pRun = ME_FindItemFwd(cell, diRun); editor->pCursors[1].nOffset = 0; assert(editor->pCursors[0].pRun); cell = cell->member.cell.next_cell; editor->pCursors[0].pRun = ME_FindItemBack(cell, diRun); editor->pCursors[0].nOffset = 0; assert(editor->pCursors[1].pRun); } else { /* v1.0 - 3.0 */ if (run->member.run.nFlags & MERF_ENDPARA && ME_IsInTable(ME_FindItemFwd(run, diParagraphOrEnd))) { run = ME_FindItemFwd(run, diRun); assert(run); } for (i = 0; i < 2; i++) { while (!(run->member.run.nFlags & MERF_TAB)) { run = ME_FindItemFwd(run, diRunOrParagraphOrEnd); if (run->type != diRun) { para = run; if (ME_IsInTable(para)) { run = ME_FindItemFwd(para, diRun); assert(run); editor->pCursors[0].pRun = run; editor->pCursors[0].nOffset = 0; i = 1; } else { /* Insert table row */ para = ME_AppendTableRow(editor, para->member.para.prev_para); /* Put cursor at the start of the new table row */ editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); editor->pCursors[0].nOffset = 0; editor->pCursors[1] = editor->pCursors[0]; ME_WrapMarkedParagraphs(editor); return; } } } if (i == 0) run = ME_FindItemFwd(run, diRun); editor->pCursors[i].pRun = run; editor->pCursors[i].nOffset = 0; } } }
/* Table rows should either be deleted completely or not at all. */ void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, int nOfs,int *nChars) { ME_Cursor c, c2; ME_DisplayItem *this_para, *end_para; ME_CursorFromCharOfs(editor, nOfs, &c); this_para = ME_GetParagraph(c.pRun); ME_CursorFromCharOfs(editor, nOfs + *nChars, &c2); end_para = ME_GetParagraph(c2.pRun); if (c2.pRun->member.run.nFlags & MERF_ENDPARA) { /* End offset might be in the middle of the end paragraph run. * If this is the case, then we need to use the next paragraph as the last * paragraphs. */ int remaining = nOfs + *nChars - c2.pRun->member.run.nCharOfs - end_para->member.para.nCharOfs; if (remaining) { assert(remaining < c2.pRun->member.run.nCR + c2.pRun->member.run.nLF); end_para = end_para->member.para.next_para; } } if (!editor->bEmulateVersion10) { /* v4.1 */ if (this_para->member.para.pCell != end_para->member.para.pCell || ((this_para->member.para.nFlags|end_para->member.para.nFlags) & (MEPF_ROWSTART|MEPF_ROWEND))) { while (this_para != end_para) { ME_DisplayItem *next_para = this_para->member.para.next_para; BOOL bTruancateDeletion = FALSE; if (this_para->member.para.nFlags & MEPF_ROWSTART) { /* The following while loop assumes that next_para is MEPF_ROWSTART, * so moving back one paragraph let's it be processed as the start * of the row. */ next_para = this_para; this_para = this_para->member.para.prev_para; } else if (next_para->member.para.pCell != this_para->member.para.pCell || this_para->member.para.nFlags & MEPF_ROWEND) { /* Start of the deletion from after the start of the table row. */ bTruancateDeletion = TRUE; } while (!bTruancateDeletion && next_para->member.para.nFlags & MEPF_ROWSTART) { next_para = ME_GetTableRowEnd(next_para)->member.para.next_para; if (next_para->member.para.nCharOfs > nOfs + *nChars) { /* End of deletion is not past the end of the table row. */ next_para = this_para->member.para.next_para; /* Delete the end paragraph preceding the table row if the * preceding table row will be empty. */ if (this_para->member.para.nCharOfs >= nOfs) { next_para = next_para->member.para.next_para; } bTruancateDeletion = TRUE; } else { this_para = next_para->member.para.prev_para; } } if (bTruancateDeletion) { ME_Run *end_run = &ME_FindItemBack(next_para, diRun)->member.run; int nCharsNew = (next_para->member.para.nCharOfs - nOfs - end_run->nCR - end_run->nLF); nCharsNew = max(nCharsNew, 0); assert(nCharsNew <= *nChars); *nChars = nCharsNew; break; } this_para = next_para; } } } else { /* v1.0 - 3.0 */ ME_DisplayItem *pRun; int nCharsToBoundary; if ((this_para->member.para.nCharOfs != nOfs || this_para == end_para) && this_para->member.para.pFmt->dwMask & PFM_TABLE && this_para->member.para.pFmt->wEffects & PFE_TABLE) { pRun = c.pRun; /* Find the next tab or end paragraph to use as a delete boundary */ while (!(pRun->member.run.nFlags & (MERF_TAB|MERF_ENDPARA))) pRun = ME_FindItemFwd(pRun, diRun); nCharsToBoundary = pRun->member.run.nCharOfs - c.pRun->member.run.nCharOfs - c.nOffset; *nChars = min(*nChars, nCharsToBoundary); } else if (end_para->member.para.pFmt->dwMask & PFM_TABLE && end_para->member.para.pFmt->wEffects & PFE_TABLE) { /* The deletion starts from before the row, so don't join it with * previous non-empty paragraphs. */ pRun = NULL; if (nOfs > this_para->member.para.nCharOfs) pRun = ME_FindItemBack(end_para, diRun); if (!pRun) pRun = ME_FindItemFwd(end_para, diRun); if (pRun) { nCharsToBoundary = ME_GetParagraph(pRun)->member.para.nCharOfs + pRun->member.run.nCharOfs - nOfs; if (nCharsToBoundary >= 0) *nChars = min(*nChars, nCharsToBoundary); } } if (*nChars < 0) nChars = 0; } }