Ejemplo n.º 1
0
/* 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;
}
Ejemplo n.º 2
0
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;
  }
}
Ejemplo n.º 3
0
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 = &para->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++;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
/* 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;
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 7
0
/* 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;
        }
    }
}
Ejemplo n.º 8
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;
    }
}