diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c index 90a5aa8..f266a6d 100644 --- a/dlls/riched20/caret.c +++ b/dlls/riched20/caret.c @@ -215,7 +215,7 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, *height = pSizeRun->member.run.nAscent + pSizeRun->member.run.nDescent; *x = run->member.run.pt.x + sz.cx; - *y = para->member.para.nYPos + row->member.row.nBaseline + run->member.run.pt.y - pSizeRun->member.run.nAscent - ME_GetYScrollPos(editor); + *y = para->member.para.pt.y + row->member.row.nBaseline + run->member.run.pt.y - pSizeRun->member.run.nAscent - ME_GetYScrollPos(editor); ME_DestroyContext(&c, editor->hWnd); return; } @@ -316,6 +316,28 @@ BOOL ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars, } 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); @@ -583,7 +605,7 @@ void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, pos++; numCR = 1; numLF = 0; } - tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style, numCR, numLF); + tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style, numCR, numLF, 0); p->pRun = ME_FindItemFwd(tp, diRun); end_run = ME_FindItemBack(tp, diRun); ME_ReleaseStyle(end_run->member.run.style); @@ -630,7 +652,8 @@ ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) assert(pRun->type != diRun && pRun->type != diParagraph); return FALSE; } - } while (RUN_IS_HIDDEN(&pRun->member.run)); + } while (RUN_IS_HIDDEN(&pRun->member.run) || + pRun->member.run.nFlags & MERF_HIDDEN); pCursor->pRun = pRun; if (pRun->member.run.nFlags & MERF_ENDPARA) pCursor->nOffset = 0; @@ -656,7 +679,8 @@ ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) } do { pRun = ME_FindItemFwd(pRun, diRun); - } while (pRun && RUN_IS_HIDDEN(&pRun->member.run)); + } while (pRun && (RUN_IS_HIDDEN(&pRun->member.run) || + pRun->member.run.nFlags & MERF_HIDDEN)); if (pRun) { pCursor->pRun = pRun; @@ -701,9 +725,13 @@ ME_MoveCursorWords(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs) { if (cursor->pRun == pRun && cursor->nOffset == 0) { + /* Skip empty start of table row paragraph */ + if (pOtherRun->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART) + pOtherRun = pOtherRun->member.para.prev_para; /* Paragraph breaks are treated as separate words */ if (pOtherRun->member.para.prev_para->type == diTextStart) return FALSE; + pRun = ME_FindItemBack(pOtherRun, diRunOrParagraph); } break; @@ -734,6 +762,8 @@ ME_MoveCursorWords(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs) } else if (pOtherRun->type == diParagraph) { + if (pOtherRun->member.para.nFlags & MEPF_ROWSTART) + pOtherRun = pOtherRun->member.para.next_para; if (cursor->pRun == pRun) pRun = ME_FindItemFwd(pOtherRun, diRun); nOffset = 0; @@ -820,6 +850,45 @@ int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor) + pCursor->pRun->member.run.nCharOfs + pCursor->nOffset; } +/* Helper function for ME_FindPixelPos to find paragraph within tables */ +static ME_DisplayItem* ME_FindPixelPosInTableRow(int x, int y, + ME_DisplayItem *para) +{ + ME_DisplayItem *cell, *next_cell; + assert(para->member.para.nFlags & MEPF_ROWSTART); + cell = para->member.para.next_para->member.para.pCell; + assert(cell); + + /* find the cell we are in */ + while ((next_cell = cell->member.cell.next_cell) != NULL) { + if (x < next_cell->member.cell.pt.x) + { + para = ME_FindItemFwd(cell, diParagraph); + /* Found the cell, but there might be multiple paragraphs in + * the cell, so need to search down the cell for the paragraph. */ + while (cell == para->member.para.pCell) { + if (y < para->member.para.pt.y + para->member.para.nHeight) + { + if (para->member.para.nFlags & MEPF_ROWSTART) + return ME_FindPixelPosInTableRow(x, y, para); + else + return para; + } + para = para->member.para.next_para; + } + /* Past the end of the cell, so go back to the last cell paragraph */ + return para->member.para.prev_para; + } + cell = next_cell; + }; + /* Return table row delimiter */ + para = ME_FindItemFwd(cell, diParagraph); + assert(para->member.para.nFlags & MEPF_ROWEND); + assert(para->member.para.pFmt->dwMask & PFM_TABLEROWDELIMITER); + assert(para->member.para.pFmt->wEffects & PFE_TABLEROWDELIMITER); + return para; +} + /* Finds the run and offset from the pixel position. * * x & y are pixel positions in virtual coordinates into the rich edit control, @@ -843,11 +912,15 @@ static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y, for (; p != editor->pBuffer->pLast; p = p->member.para.next_para) { assert(p->type == diParagraph); - if (y < p->member.para.nYPos + p->member.para.nHeight) + if (y < p->member.para.pt.y + p->member.para.nHeight) { - y -= p->member.para.nYPos; + 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 */ @@ -855,7 +928,7 @@ static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y, { ME_DisplayItem *pp; assert(p->type == diStartRow); - if (y < p->member.row.nYPos + p->member.row.nHeight) + if (y < p->member.row.pt.y + p->member.row.nHeight) { p = ME_FindItemFwd(p, diRun); break; @@ -911,6 +984,7 @@ static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y, if (is_eol) *is_eol = 1; rx = 0; /* FIXME not sure */ goto found_here; + case diCell: case diParagraph: case diTextEnd: isExact = FALSE; @@ -1185,13 +1259,14 @@ static void ME_MoveCursorLines(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) { ME_DisplayItem *pRun = pCursor->pRun; - ME_DisplayItem *pItem; + 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 */ @@ -1199,13 +1274,57 @@ ME_MoveCursorLines(ME_TextEditor *editor, ME_Cursor *pCursor, int nRelOfs) 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) { @@ -1231,8 +1350,8 @@ static void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor) p = ME_FindItemBack(pRun, diStartRowOrParagraph); assert(p->type == diStartRow); - yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos; - yprev = ys = y = yp + p->member.row.nYPos; + yp = ME_FindItemBack(p, diParagraph)->member.para.pt.y; + yprev = ys = y = yp + p->member.row.pt.y; yd = y - editor->sizeWindow.cy; pLast = p; @@ -1243,10 +1362,10 @@ static void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor) if (p->type == diParagraph) { /* crossing paragraphs */ if (p->member.para.prev_para == NULL) break; - yp = p->member.para.prev_para->member.para.nYPos; + yp = p->member.para.prev_para->member.para.pt.y; continue; } - y = yp + p->member.row.nYPos; + y = yp + p->member.row.pt.y; if (y < yd) break; pLast = p; @@ -1286,8 +1405,8 @@ static void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor) p = ME_FindItemBack(pRun, diStartRowOrParagraph); assert(p->type == diStartRow); - yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos; - yprev = ys = y = yp + p->member.row.nYPos; + yp = ME_FindItemBack(p, diParagraph)->member.para.pt.y; + yprev = ys = y = yp + p->member.row.pt.y; yd = y + editor->sizeWindow.cy; pLast = p; @@ -1296,10 +1415,10 @@ static void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor) if (!p) break; if (p->type == diParagraph) { - yp = p->member.para.nYPos; + yp = p->member.para.pt.y; continue; } - y = yp + p->member.row.nYPos; + y = yp + p->member.row.pt.y; if (y >= yd) break; pLast = p; diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index 4cdc14b..5a9b18d 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -451,8 +451,9 @@ static void ME_RTFParAttrHook(RTF_Info *info) switch(info->rtfMinor) { case rtfParDef: /* restores default paragraph attributes */ - fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS | PFM_OFFSET | - PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE | PFM_STARTINDENT | PFM_TABLE; + fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS | + PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE | + PFM_STARTINDENT; /* TODO: numbering, shading */ fmt.wAlignment = PFA_LEFT; fmt.cTabCount = 0; @@ -462,12 +463,67 @@ static void ME_RTFParAttrHook(RTF_Info *info) fmt.bLineSpacingRule = 0; fmt.dySpaceBefore = fmt.dySpaceAfter = 0; fmt.dyLineSpacing = 0; - fmt.wEffects &= ~PFE_TABLE; + if (!info->editor->bEmulateVersion10) /* v4.1 */ + { + if (info->tableDef && info->tableDef->tableRowStart && + info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_Cursor cursor; + ME_DisplayItem *para; + /* We are just after a table row. */ + RTFFlushOutputBuffer(info); + cursor = info->editor->pCursors[0]; + para = ME_GetParagraph(cursor.pRun); + if (para == info->tableDef->tableRowStart->member.para.next_para + && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs) + { + /* Since the table row end, no text has been inserted, and the \intbl + * control word has not be used. We can confirm that we are not in a + * table anymore. + */ + info->tableDef->tableRowStart = NULL; + } + } + } else { /* v1.0 - v3.0 */ + fmt.dwMask |= PFM_TABLE; + fmt.wEffects &= ~PFE_TABLE; + } break; case rtfInTable: { - fmt.dwMask |= PFM_TABLE; - fmt.wEffects |= PFE_TABLE; + ME_Cursor cursor; + if (!info->editor->bEmulateVersion10) /* v4.1 */ + { + if (!info->tableDef || !info->tableDef->tableRowStart || + info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + RTFTable *tableDef; + if (!info->tableDef) + { + info->tableDef = ALLOC_OBJ(RTFTable); + ZeroMemory(info->tableDef, sizeof(RTFTable)); + } + tableDef = info->tableDef; + RTFFlushOutputBuffer(info); + if (!tableDef->tableRowStart) + { + WCHAR endl = '\r'; + cursor = info->editor->pCursors[0]; + if (cursor.nOffset || cursor.pRun->member.run.nCharOfs) + ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); + } + + /* FIXME: Remove the following condition once nested tables are supported */ + if (ME_GetParagraph(info->editor->pCursors[0].pRun)->member.para.pCell) + break; + + tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor); + } + return; + } else { /* v1.0 - v3.0 */ + fmt.dwMask |= PFM_TABLE; + fmt.wEffects |= PFE_TABLE; + } break; } case rtfFirstIndent: @@ -664,10 +720,16 @@ static void ME_RTFTblAttrHook(RTF_Info *info) switch (info->rtfMinor) { case rtfRowDef: - if (!info->tableDef) + { + if (!info->tableDef) { info->tableDef = ALLOC_OBJ(RTFTable); - ZeroMemory(info->tableDef, sizeof(RTFTable)); + ZeroMemory(info->tableDef, sizeof(RTFTable)); + } else { + ZeroMemory(info->tableDef->cells, sizeof(info->tableDef->cells)); + info->tableDef->numCellsDefined = 0; + } break; + } case rtfCellPos: if (!info->tableDef) { @@ -678,7 +740,8 @@ static void ME_RTFTblAttrHook(RTF_Info *info) break; info->tableDef->cells[info->tableDef->numCellsDefined].rightBoundary = info->rtfParam; { - /* Tab stops store the cell positions. */ + /* Tab stops were used to store cell positions before v4.1 but v4.1 + * still seems to set the tabstops without using them. */ ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); PARAFORMAT2 *pFmt = para->member.para.pFmt; int cellNum = info->tableDef->numCellsDefined; @@ -699,7 +762,19 @@ static void ME_RTFSpecialCharHook(RTF_Info *info) if (!tableDef) break; RTFFlushOutputBuffer(info); - { + if (!info->editor->bEmulateVersion10) { /* v4.1 */ + if (tableDef->tableRowStart) + { + if (tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_DisplayItem *para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + } + ME_InsertTableCellFromCursor(info->editor); + } + } else { /* v1.0 - v3.0 */ ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); PARAFORMAT2 *pFmt = para->member.para.pFmt; if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE && @@ -713,11 +788,70 @@ static void ME_RTFSpecialCharHook(RTF_Info *info) break; case rtfRow: { + ME_DisplayItem *para, *cell, *run; + int i; + if (!tableDef) break; RTFFlushOutputBuffer(info); + if (!info->editor->bEmulateVersion10) { /* v4.1 */ + if (!tableDef->tableRowStart) + break; + if (tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + } + para = tableDef->tableRowStart; + cell = ME_FindItemFwd(para, diCell); + assert(cell && !cell->member.cell.prev_cell); + if (tableDef->numCellsDefined < 1) + { + /* 2000 twips appears to be the cell size that native richedit uses + * when no cell sizes are specified. */ + const int defaultCellSize = 2000; + int nRightBoundary = defaultCellSize; + cell->member.cell.nRightBoundary = nRightBoundary; + while (cell->member.cell.next_cell) { + cell = cell->member.cell.next_cell; + nRightBoundary += defaultCellSize; + cell->member.cell.nRightBoundary = nRightBoundary; + } + para = ME_InsertTableCellFromCursor(info->editor); + cell = para->member.para.pCell; + cell->member.cell.nRightBoundary = nRightBoundary; + } else { + for (i = 0; i < tableDef->numCellsDefined; i++) + { + cell->member.cell.nRightBoundary = tableDef->cells[i].rightBoundary; + cell = cell->member.cell.next_cell; + if (!cell) + { + para = ME_InsertTableCellFromCursor(info->editor); + cell = para->member.para.pCell; + } + } + /* Cell for table row delimiter is empty */ + cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary; + } - { + run = ME_FindItemFwd(cell, diRun); + if (info->editor->pCursors[0].pRun != run || + info->editor->pCursors[0].nOffset) + { + int nOfs, nChars; + /* Delete inserted cells that aren't defined. */ + info->editor->pCursors[1].pRun = run; + info->editor->pCursors[1].nOffset = 0; + nOfs = ME_GetCursorOfs(info->editor, 1); + nChars = ME_GetCursorOfs(info->editor, 0) - nOfs; + ME_InternalDeleteText(info->editor, nOfs, nChars, TRUE); + } + + tableDef->tableRowStart = ME_InsertTableRowEndFromCursor(info->editor); + } else { /* v1.0 - v3.0 */ WCHAR endl = '\r'; ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); PARAFORMAT2 *pFmt = para->member.para.pFmt; @@ -1123,6 +1257,8 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre if (!invalidRTF && !inStream.editstream->dwError) { if (format & SF_RTF) { + ME_DisplayItem *para; + /* setup the RTF parser */ memset(&parser, 0, sizeof parser); RTFSetEditStream(&parser, &inStream); @@ -1140,6 +1276,36 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre /* do the parsing */ RTFRead(&parser); RTFFlushOutputBuffer(&parser); + if (!editor->bEmulateVersion10) { /* v4.1 */ + if (parser.tableDef && parser.tableDef->tableRowStart) + { + /* Delete any incomplete table row at the end of the rich text. */ + int nOfs, nChars; + ME_DisplayItem *pCell; + + para = parser.tableDef->tableRowStart; + + parser.rtfMinor = rtfRow; + /* Complete the table row before deleting it. + * By doing it this way we will have the current paragraph format set + * properly to reflect that is not in the complete table, and undo items + * will be added for this change to the current paragraph format. */ + ME_RTFSpecialCharHook(&parser); + if (para->member.para.nFlags & MEPF_ROWEND) + { + para = para->member.para.next_para; + } + pCell = para->member.para.pCell; + + editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun); + editor->pCursors[1].nOffset = 0; + nOfs = ME_GetCursorOfs(editor, 1); + nChars = ME_GetCursorOfs(editor, 0) - nOfs; + ME_InternalDeleteText(editor, nOfs, nChars, TRUE); + parser.tableDef->tableRowStart = NULL; + } + } + ME_CheckTablesForCorruption(editor); RTFDestroy(&parser); if (parser.lpRichEditOle) IRichEditOle_Release(parser.lpRichEditOle); @@ -1619,10 +1785,17 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) } else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE)) { - /* Backspace can be grouped for a single undo */ - ME_ContinueCoalescingTransaction(editor); - ME_DeleteTextAtCursor(editor, 1, 1); - ME_CommitCoalescingUndo(editor); + BOOL bDeletionSucceeded; + /* Backspace can be grouped for a single undo */ + ME_ContinueCoalescingTransaction(editor); + bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1); + if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */ + /* Deletion was prevented so the cursor is moved back to where it was. + * (e.g. this happens when trying to delete cell boundaries) + */ + ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE); + } + ME_CommitCoalescingUndo(editor); } else return TRUE; @@ -2498,10 +2671,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (p->type == diTextEnd) break; if (p->type == diParagraph) { - ypara = p->member.para.nYPos; + ypara = p->member.para.pt.y; continue; } - ystart = ypara + p->member.row.nYPos; + ystart = ypara + p->member.row.pt.y; yend = ystart + p->member.row.nHeight; if (y < yend) { break; @@ -2567,7 +2740,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, nPos = ME_GetYScrollPos(editor); row = ME_RowStart(editor->pCursors[0].pRun); para = ME_GetParagraph(row); - top = para->member.para.nYPos + row->member.row.nYPos; + top = para->member.para.pt.y + row->member.row.pt.y; bottom = top + row->member.row.nHeight; if (top < nPos) /* caret above window */ @@ -3084,10 +3257,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, assert(pRun->type == diRun); pt.y = pRun->member.run.pt.y; pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset); - pt.y += ME_GetParagraph(pRun)->member.para.nYPos; + pt.y += ME_GetParagraph(pRun)->member.para.pt.y; } else { pt.x = 0; - pt.y = editor->pBuffer->pLast->member.para.nYPos; + pt.y = editor->pBuffer->pLast->member.para.pt.y; } pt.x += editor->selofs; @@ -3282,8 +3455,13 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, || (wstr=='\r' && (GetWindowLongW(hWnd, GWL_STYLE) & ES_MULTILINE)) || wstr=='\t') { int from, to; + BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; ME_GetSelection(editor, &from, &to); - if (wstr=='\t') { + if (wstr=='\t' + /* v4.1 allows tabs to be inserted with ctrl key down */ + && !(ctrl_is_down && !editor->bEmulateVersion10) + ) + { ME_Cursor cursor = editor->pCursors[0]; ME_DisplayItem *para; BOOL bSelectedRow = FALSE; @@ -3721,7 +3899,12 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in if (nLen > nChars) nLen = nChars; - if (item->member.run.nFlags & MERF_ENDPARA) + if (item->member.run.nFlags & MERF_ENDCELL && + item->member.run.nFlags & MERF_ENDPARA) + { + *buffer = '\t'; + } + else if (item->member.run.nFlags & MERF_ENDPARA) { if (!ME_FindItemFwd(item, diRun)) /* No '\r' is appended to the last paragraph. */ @@ -3733,10 +3916,16 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in if (bCRLF) { /* richedit 2.0 case - actual line-break is \r but should report \r\n */ - assert(nLen == 1); + if (ME_GetParagraph(item)->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) + assert(nLen == 2); + else + assert(nLen == 1); *buffer++ = '\r'; *buffer = '\n'; /* Later updated by nLen==1 at the end of the loop */ - nWritten++; + if (nLen == 1) + nWritten++; + else + buffer--; } else { diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index ed0175f..85dbd18 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -217,7 +217,7 @@ void ME_SendRequestResize(ME_TextEditor *editor, BOOL force); ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run); void ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayItem **para_end); void ME_MakeFirstParagraph(ME_TextEditor *editor); -ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style, int numCR, int numLF); +ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style, int numCR, int numLF, int paraFlags); ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, BOOL keepFirstParaFormat); void ME_DumpParaStyle(ME_Paragraph *s); @@ -287,6 +287,14 @@ void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor); /* table.c */ BOOL ME_IsInTable(ME_DisplayItem *pItem); +ME_DisplayItem *ME_InsertTableRowStartFromCursor(ME_TextEditor *editor); +ME_DisplayItem *ME_InsertTableRowStartAtParagraph(ME_TextEditor *editor, + ME_DisplayItem *para); +ME_DisplayItem *ME_InsertTableCellFromCursor(ME_TextEditor *editor); +ME_DisplayItem *ME_InsertTableRowEndFromCursor(ME_TextEditor *editor); +ME_DisplayItem *ME_GetTableRowEnd(ME_DisplayItem *para); +ME_DisplayItem *ME_GetTableRowStart(ME_DisplayItem *para); +void ME_CheckTablesForCorruption(ME_TextEditor *editor); void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, int nOfs,int *nChars); void ME_TabPressedInTable(ME_TextEditor *editor, BOOL bSelectedRow); diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index 9e2e84f..105ce05 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -67,26 +67,27 @@ typedef enum { diInvalid, diTextStart, /* start of the text buffer */ diParagraph, /* paragraph start */ + diCell, /* cell start */ diRun, /* run (sequence of chars with the same character format) */ diStartRow, /* start of the row (line of text on the screen) */ diTextEnd, /* end of the text buffer */ /********************* these below are meant for finding only *********************/ - diStartRowOrParagraph, /* 5 */ + diStartRowOrParagraph, /* 7 */ diStartRowOrParagraphOrEnd, diRunOrParagraph, diRunOrStartRow, diParagraphOrEnd, - diRunOrParagraphOrEnd, /* 10 */ + diRunOrParagraphOrEnd, /* 12 */ - diUndoInsertRun, /* 11 */ - diUndoDeleteRun, /* 12 */ - diUndoJoinParagraphs, /* 13 */ - diUndoSplitParagraph, /* 14 */ - diUndoSetParagraphFormat, /* 15 */ - diUndoSetCharFormat, /* 16 */ - diUndoEndTransaction, /* 17 - marks the end of a group of changes for undo */ - diUndoPotentialEndTransaction, /* 18 - allows grouping typed chars for undo */ + diUndoInsertRun, /* 13 */ + diUndoDeleteRun, /* 14 */ + diUndoJoinParagraphs, /* 15 */ + diUndoSplitParagraph, /* 16 */ + diUndoSetParagraphFormat, /* 17 */ + diUndoSetCharFormat, /* 18 */ + diUndoEndTransaction, /* 19 - marks the end of a group of changes for undo */ + diUndoPotentialEndTransaction, /* 20 - allows grouping typed chars for undo */ } ME_DIType; #define SELECTIONBAR_WIDTH 9 @@ -97,8 +98,10 @@ typedef enum { #define MERF_GRAPHICS 0x001 /* run is a tab (or, in future, any kind of content whose size is dependent on run position) */ #define MERF_TAB 0x002 +/* run is a cell boundary */ +#define MERF_ENDCELL 0x004 /* v4.1 */ -#define MERF_NONTEXT (MERF_GRAPHICS | MERF_TAB) +#define MERF_NONTEXT (MERF_GRAPHICS | MERF_TAB | MERF_ENDCELL) /* run is splittable (contains white spaces in the middle or end) */ #define MERF_SPLITTABLE 0x001000 @@ -118,6 +121,8 @@ typedef enum { #define MERF_ENDROW 0x200000 /* run is hidden */ #define MERF_HIDDEN 0x400000 +/* start of a table row has an empty paragraph that should be skipped over. */ +#define MERF_TABLESTART 0x800000 /* v4.1 */ /* runs with any of these flags set cannot be joined */ #define MERF_NOJOIN (MERF_GRAPHICS|MERF_TAB|MERF_ENDPARA|MERF_ENDROW) @@ -130,8 +135,12 @@ typedef enum { /******************************** para flags *************************/ /* this paragraph was already wrapped and hasn't changed, every change resets that flag */ -#define MEPF_REWRAP 1 -#define MEPF_REPAINT 2 +#define MEPF_REWRAP 0x01 +#define MEPF_REPAINT 0x02 +/* v4.1 */ +#define MEPF_CELL 0x04 /* The paragraph is nested in a cell */ +#define MEPF_ROWSTART 0x08 /* Hidden empty paragraph at the start of the row */ +#define MEPF_ROWEND 0x10 /* Visible empty paragraph at the end of the row */ /******************************** structures *************************/ @@ -160,14 +169,26 @@ typedef struct tagME_Paragraph { PARAFORMAT2 *pFmt; + struct tagME_DisplayItem *pCell; /* v4.1 */ + int nCharOfs; int nFlags; - int nYPos, nHeight; + POINT pt; + int nHeight; int nLastPaintYPos, nLastPaintHeight; int nRows; struct tagME_DisplayItem *prev_para, *next_para, *document; } ME_Paragraph; +typedef struct tagME_Cell /* v4.1 */ +{ + int nNestingLevel; /* 0 for normal cells, and greater for nested cells */ + int nRightBoundary; + POINT pt; + int nHeight, nWidth; + struct tagME_DisplayItem *prev_cell, *next_cell, *parent_cell; +} ME_Cell; + typedef struct tagME_Row { int nHeight; @@ -175,7 +196,7 @@ typedef struct tagME_Row int nWidth; int nLMargin; int nRMargin; - int nYPos; + POINT pt; } ME_Row; /* the display item list layout is like this: @@ -197,6 +218,7 @@ typedef struct tagME_DisplayItem union { ME_Run run; ME_Row row; + ME_Cell cell; ME_Paragraph para; ME_Document doc; /* not used */ ME_Style *ustyle; /* used by diUndoSetCharFormat */ @@ -270,6 +292,9 @@ typedef struct tagME_OutStream { COLORREF colortbl[STREAMOUT_COLORTBL_SIZE]; UINT nDefaultFont; UINT nDefaultCodePage; + /* nNestingLevel = 0 means we aren't in a cell, 1 means we are in a cell, + * an greater numbers mean we are in a cell nested within a cell. */ + UINT nNestingLevel; } ME_OutStream; typedef struct tagME_FontCacheItem diff --git a/dlls/riched20/list.c b/dlls/riched20/list.c index 87f5513..092fde4 100644 --- a/dlls/riched20/list.c +++ b/dlls/riched20/list.c @@ -121,8 +121,11 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item) { if (item->type==diUndoSetCharFormat) { ME_ReleaseStyle(item->member.ustyle); } - if (item->type==diUndoSplitParagraph) + if (item->type==diUndoSplitParagraph) { FREE_OBJ(item->member.para.pFmt); + if (item->member.para.pCell) + FREE_OBJ(item->member.para.pCell); + } FREE_OBJ(item); } @@ -146,6 +149,7 @@ const char *ME_GetDITypeName(ME_DIType type) { case diParagraph: return "diParagraph"; case diRun: return "diRun"; + case diCell: return "diCell"; case diTextStart: return "diTextStart"; case diTextEnd: return "diTextEnd"; case diStartRow: return "diStartRow"; @@ -172,8 +176,17 @@ void ME_DumpDocument(ME_TextBuffer *buffer) case diTextStart: TRACE("Start\n"); break; + case diCell: + TRACE("Cell(level=%d%s)\n", pItem->member.cell.nNestingLevel, + !pItem->member.cell.next_cell ? ", END" : + (!pItem->member.cell.prev_cell ? ", START" :"")); + break; case diParagraph: TRACE("Paragraph(ofs=%d)\n", pItem->member.para.nCharOfs); + if (pItem->member.para.nFlags & MEPF_ROWSTART) + TRACE(" - (Table Row Start)\n"); + if (pItem->member.para.nFlags & MEPF_ROWEND) + TRACE(" - (Table Row End)\n"); break; case diStartRow: TRACE(" - StartRow\n"); diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c index 921d3fa..c93a3e5 100644 --- a/dlls/riched20/paint.c +++ b/dlls/riched20/paint.c @@ -32,13 +32,21 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT * yoffset = ME_GetYScrollPos(editor); ME_InitContext(&c, editor, hDC); SetBkMode(hDC, TRANSPARENT); - ME_MoveCaret(editor); + ME_MoveCaret(editor); /* Calls ME_WrapMarkedParagraphs */ item = editor->pBuffer->pFirst->next; c.pt.y -= yoffset; while(item != editor->pBuffer->pLast) { int ye; assert(item->type == diParagraph); - ye = c.pt.y + item->member.para.nHeight; + if (item->member.para.pCell + != item->member.para.next_para->member.para.pCell) + { + ME_Cell *cell = NULL; + cell = &ME_FindItemBack(item->member.para.next_para, diCell)->member.cell; + ye = cell->pt.y + cell->nHeight - yoffset; + } else { + ye = c.pt.y + item->member.para.nHeight; + } if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT)) { BOOL bPaint = (rcUpdate == NULL); @@ -51,7 +59,31 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT * item->member.para.nFlags &= ~MEPF_REPAINT; } } - c.pt.y = ye; + if (item->member.para.pCell) + { + ME_Cell *cell = &item->member.para.pCell->member.cell; + c.pt.x = cell->pt.x + cell->nWidth; + if (item->member.para.pCell == item->member.para.next_para->member.para.pCell) + { + c.pt.y = ye; + } else { + if (item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART) + { + cell = &ME_FindItemFwd(item->member.para.next_para, diCell)->member.cell; + } + else if (item->member.para.next_para->member.para.nFlags & MEPF_ROWEND) + { + cell = &cell->next_cell->member.cell; + } + else + { + cell = &item->member.para.next_para->member.para.pCell->member.cell; + } + c.pt.y = cell->pt.y - yoffset; + } + } else if (!(item->member.para.nFlags & MEPF_ROWSTART)) { + c.pt.y = ye; + } item = item->member.para.next_para; } if (c.pt.y= nSelFrom && runofs < nSelTo) { ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1, - c->pt.y + start->member.row.nYPos, + c->pt.y + start->member.row.pt.y, start->member.row.nHeight); } return; } - if (run->nFlags & MERF_TAB) + if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { /* wszSpace is used instead of the tab character because otherwise * an unwanted symbol can be inserted instead. */ ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, run->nWidth, nSelFrom-runofs,nSelTo-runofs, - c->pt.y + start->member.row.nYPos, + c->pt.y + start->member.row.pt.y, start->member.row.nHeight); return; } @@ -420,13 +452,13 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,ME_StrVLen(run->strText)); ME_DrawTextWithStyle(c, x, y, szMasked->szData, ME_StrVLen(szMasked), run->style, run->nWidth, - nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight); + nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.pt.y, start->member.row.nHeight); ME_DestroyString(szMasked); } else ME_DrawTextWithStyle(c, x, y, run->strText->szData, ME_StrVLen(run->strText), run->style, run->nWidth, - nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight); + nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.pt.y, start->member.row.nHeight); } } @@ -635,23 +667,38 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { ME_DisplayItem *p; ME_Run *run; ME_Paragraph *para = NULL; - RECT rc, rcPara, bounds; + RECT rc, rcText, bounds; int y = c->pt.y; int height = 0, baseline = 0, no=0, pno = 0; - int xs = 0, xe = 0; BOOL visible = FALSE; c->pt.x = c->rcView.left; - rcPara.left = c->rcView.left; - rcPara.right = c->rcView.right; + rc.left = c->rcView.left; + rc.right = c->rcView.right; for (p = paragraph; p!=paragraph->member.para.next_para; p = p->next) { switch(p->type) { case diParagraph: para = &p->member.para; assert(para); pno = 0; - xs = c->rcView.left + ME_twips2pointsX(c, para->pFmt->dxStartIndent); - xe = c->rcView.right - ME_twips2pointsX(c, para->pFmt->dxRightIndent); + if (para->pCell) + { + ME_Cell *cell = ¶->pCell->member.cell; + rc.left = cell->pt.x; + rc.right = rc.left + cell->nWidth; + rcText.left = cell->pt.x + ME_twips2pointsX(c, para->pFmt->dxStartIndent); + rcText.right = cell->pt.x + cell->nWidth + - ME_twips2pointsX(c, para->pFmt->dxRightIndent); + } + if (para->nFlags & MEPF_ROWSTART) { + ME_Cell *cell = ¶->next_para->member.para.pCell->member.cell; + rc.right = cell->pt.x; + } else if (para->nFlags & MEPF_ROWEND) { + ME_Cell *cell = ¶->prev_para->member.para.pCell->member.cell; + rc.left = cell->pt.x + cell->nWidth; + } + rcText.left = rc.left + ME_twips2pointsX(c, para->pFmt->dxStartIndent); + rcText.right = rc.right - ME_twips2pointsX(c, para->pFmt->dxRightIndent); ME_DrawParaDecoration(c, para, y, &bounds); y += bounds.top; break; @@ -659,22 +706,18 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { /* we should have seen a diParagraph before */ assert(para); y += height; - rcPara.top = y; - rcPara.bottom = y+p->member.row.nHeight; - visible = RectVisible(c->hDC, &rcPara); - if (visible) { - /* left margin */ - rc.left = c->rcView.left + bounds.left; - rc.right = xs; - rc.top = y; + rc.top = y; + if (para->nFlags & MEPF_ROWSTART) { + ME_Cell *cell = ¶->next_para->member.para.pCell->member.cell; + rc.bottom = y + cell->nHeight; + } else if (para->nFlags & MEPF_ROWEND) { + ME_Cell *cell = ¶->prev_para->member.para.pCell->member.cell; + rc.bottom = y + cell->nHeight; + } else { rc.bottom = y+p->member.row.nHeight; - FillRect(c->hDC, &rc, c->editor->hbrBackground); - /* right margin */ - rc.left = xe; - rc.right = c->rcView.right - bounds.right; - FillRect(c->hDC, &rc, c->editor->hbrBackground); - rc.left = xs; - rc.right = xe; + } + visible = RectVisible(c->hDC, &rc); + if (visible) { FillRect(c->hDC, &rc, c->editor->hbrBackground); } if (me_debug) @@ -690,7 +733,7 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { height = p->member.row.nHeight; baseline = p->member.row.nBaseline; if (!pno++) - xe += ME_twips2pointsX(c, para->pFmt->dxOffset); + rcText.right += ME_twips2pointsX(c, para->pFmt->dxOffset); break; case diRun: assert(para); @@ -721,6 +764,18 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { } /* c->pt.x += p->member.run.nWidth; */ break; + case diCell: + /* Clear any space at the bottom of the cell after the text. */ + if (para->nFlags & MEPF_ROWSTART) + break; + y += height; + rc.top = y; + rc.bottom = p->member.cell.pt.y + p->member.cell.nHeight + - ME_GetYScrollPos(c->editor); + if (RectVisible(c->hDC, &rc)) + { + FillRect(c->hDC, &rc, c->editor->hbrBackground); + } default: break; } @@ -867,7 +922,7 @@ void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun) assert(pRow); assert(pPara); - y = pPara->member.para.nYPos+pRow->member.row.nYPos; + y = pPara->member.para.pt.y+pRow->member.row.pt.y; yheight = pRow->member.row.nHeight; yold = ME_GetYScrollPos(editor); yrel = y - yold; diff --git a/dlls/riched20/para.c b/dlls/riched20/para.c index 5289fdd..3c20ee1 100644 --- a/dlls/riched20/para.c +++ b/dlls/riched20/para.c @@ -104,17 +104,51 @@ void ME_MarkForPainting(ME_TextEditor *editor, ME_DisplayItem *first, const ME_D } } +static void ME_UpdateTableFlags(ME_DisplayItem *para) +{ + para->member.para.pFmt->dwMask |= PFM_TABLE|PFM_TABLEROWDELIMITER; + if (para->member.para.pCell) { + para->member.para.nFlags |= MEPF_CELL; + } else { + para->member.para.nFlags &= ~MEPF_CELL; + } + if (para->member.para.nFlags & MEPF_ROWEND) { + para->member.para.pFmt->wEffects |= PFE_TABLEROWDELIMITER; + } else { + para->member.para.pFmt->wEffects &= ~PFE_TABLEROWDELIMITER; + } + if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_CELL|MEPF_ROWEND)) + para->member.para.pFmt->wEffects |= PFE_TABLE; + else + para->member.para.pFmt->wEffects &= ~PFE_TABLE; +} + /* split paragraph at the beginning of the run */ -ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style, int numCR, int numLF) +ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, + ME_Style *style, int numCR, int numLF, + int paraFlags) { ME_DisplayItem *next_para = NULL; ME_DisplayItem *run_para = NULL; ME_DisplayItem *new_para = ME_MakeDI(diParagraph); - ME_DisplayItem *end_run = ME_MakeRun(style,ME_MakeString(wszParagraphSign), MERF_ENDPARA); + ME_DisplayItem *end_run; ME_UndoItem *undo = NULL; int ofs; ME_DisplayItem *pp; int end_len = numCR + numLF; + 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,ME_MakeString(wszParagraphSign), run_flags); assert(run->type == diRun); @@ -139,11 +173,11 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME } new_para->member.para.nCharOfs = ME_GetParagraph(run)->member.para.nCharOfs+ofs; new_para->member.para.nCharOfs += end_len; - - new_para->member.para.nFlags = MEPF_REWRAP; /* FIXME copy flags (if applicable) */ + 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; - + /* insert paragraph into paragraph double linked list */ new_para->member.para.prev_para = run_para; new_para->member.para.next_para = next_para; @@ -154,6 +188,44 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME 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)); + } 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; @@ -208,6 +280,42 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp, ME_AddUndoItem(editor, diUndoSetParagraphFormat, tp); *tp->member.para.pFmt = *pNext->member.para.pFmt; } + + 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; diff --git a/dlls/riched20/rtf.h b/dlls/riched20/rtf.h index 87bab16..0425bf6 100644 --- a/dlls/riched20/rtf.h +++ b/dlls/riched20/rtf.h @@ -1012,7 +1012,15 @@ struct RTFTable { RTFCell cells[MAX_TABLE_CELLS]; int numCellsDefined; + + /* Used in v1.0 - v3.0 */ int numCellsInserted; + + /* v4.1 */ + /* tableRowStart may be the start row paragraph of the table row, + * or it may store the end of the previous row if it may still be + * continued, otherwise NULL is stored. */ + ME_DisplayItem *tableRowStart; }; /* diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c index e7ce035..fd37155 100644 --- a/dlls/riched20/run.c +++ b/dlls/riched20/run.c @@ -134,6 +134,9 @@ void ME_CheckCharOffsets(ME_TextEditor *editor) else ofs += ME_StrLen(p->member.run.strText); break; + case diCell: + TRACE_(richedit_check)("cell\n"); + break; default: assert(0); } @@ -436,7 +439,7 @@ void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run) { assert(run->nCharOfs != -1); - if (RUN_IS_HIDDEN(run)) + if (RUN_IS_HIDDEN(run) || run->nFlags & MERF_TABLESTART) run->nFlags |= MERF_HIDDEN; else run->nFlags &= ~MERF_HIDDEN; @@ -483,7 +486,8 @@ int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run) if (!run->strText->nLen) return 0; - if (run->nFlags & MERF_TAB) + if (run->nFlags & MERF_TAB || + (run->nFlags & (MERF_ENDCELL|MERF_ENDPARA)) == MERF_ENDCELL) { if (cx < run->nWidth/2) return 0; @@ -540,7 +544,7 @@ int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run) if (!run->strText->nLen) return 0; - if (run->nFlags & MERF_TAB) + if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { if (cx < run->nWidth/2) return 0; diff --git a/dlls/riched20/table.c b/dlls/riched20/table.c index 04c4980..a621825 100644 --- a/dlls/riched20/table.c +++ b/dlls/riched20/table.c @@ -21,18 +21,236 @@ /* * The implementation of tables differs greatly between version 3.0 * (in riched20.dll) and version 4.1 (in msftedit.dll) of richedit controls. - * Currently Wine is not implementing table support as done in version 4.1. + * Currently Wine is not distinguishing between version 3.0 and version 4.1, + * so v4.1 is assumed unless v1.0 is being emulated (i.e. riched32.dll is used). + * If this lack of distinction causes a bug in a Windows application, then Wine + * will need to start making this distinction. * * Richedit version 1.0 - 3.0: * Tables are implemented in these versions using tabs at the end of cells, * and tab stops to position the cells. The paragraph format flag PFE_TABLE * will indicate the the paragraph is a table row. Note that in this * implementation there is one paragraph per table row. + * + * Richedit version 4.1: + * Tables are implemented such that cells can contain multiple paragraphs, + * each with it's own paragraph format, and cells may even contain tables + * nested within the cell. + * + * There are is also a paragraph at the start of each table row that contains + * the rows paragraph format (e.g. to change the row alignment to row), and a + * paragraph at the end of the table row with the PFE_TABLEROWDELIMITER flag + * set. The paragraphs at the start and end of the table row should always be + * empty, but should have a length of 2. + * + * Wine implements this using display items (ME_DisplayItem) with a type of + * diCell. These cell display items store the cell properties, and are + * inserted into the editors linked list before each cell, and at the end of + * the last cell. The cell display item for a cell comes before the paragraphs + * for the cell, but the last cell display item refers to no cell, so it is + * just a delimiter. */ #include "editor.h" WINE_DEFAULT_DEBUG_CHANNEL(richedit); +WINE_DECLARE_DEBUG_CHANNEL(richedit_lists); + +static ME_DisplayItem* ME_InsertEndParaFromCursor(ME_TextEditor *editor, + int nCursor, + int numCR, + int numLF, + int paraFlags) +{ + ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor); + ME_DisplayItem *tp; + ME_Cursor* cursor = &editor->pCursors[nCursor]; + if (cursor->nOffset) { + ME_SplitRunSimple(editor, cursor->pRun, cursor->nOffset); + cursor = &editor->pCursors[nCursor]; + } + + tp = ME_SplitParagraph(editor, cursor->pRun, pStyle, numCR, numLF, paraFlags); + cursor->pRun = ME_FindItemFwd(tp, diRun); + return tp; +} + +ME_DisplayItem* ME_InsertTableRowStartFromCursor(ME_TextEditor *editor) +{ + ME_DisplayItem *para; + para = ME_InsertEndParaFromCursor(editor, 0, 1, 1, MEPF_ROWSTART); + return para->member.para.prev_para; +} + +ME_DisplayItem* ME_InsertTableRowStartAtParagraph(ME_TextEditor *editor, + ME_DisplayItem *para) +{ + ME_DisplayItem *prev_para, *end_para; + ME_Cursor savedCursor = editor->pCursors[0]; + ME_DisplayItem *startRowPara; + editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun); + editor->pCursors[0].nOffset = 0; + editor->pCursors[1] = editor->pCursors[0]; + startRowPara = ME_InsertTableRowStartFromCursor(editor); + editor->pCursors[0] = savedCursor; + editor->pCursors[1] = editor->pCursors[0]; + + end_para = ME_GetParagraph(editor->pCursors[0].pRun)->member.para.next_para; + prev_para = startRowPara->member.para.next_para; + para = prev_para->member.para.next_para; + while (para != end_para) + { + para->member.para.pCell = prev_para->member.para.pCell; + para->member.para.nFlags |= MEPF_CELL; + para->member.para.nFlags &= ~(MEPF_ROWSTART|MEPF_ROWEND); + para->member.para.pFmt->dwMask |= PFM_TABLE|PFM_TABLEROWDELIMITER; + para->member.para.pFmt->wEffects |= PFE_TABLE; + para->member.para.pFmt->wEffects &= ~PFE_TABLEROWDELIMITER; + prev_para = para; + para = para->member.para.next_para; + } + return startRowPara; +} + +/* Inserts a diCell and starts a new paragraph for the next cell. + * + * Returns the first paragraph of the new cell. */ +ME_DisplayItem* ME_InsertTableCellFromCursor(ME_TextEditor *editor) +{ + ME_DisplayItem *para; + para = ME_InsertEndParaFromCursor(editor, 0, 1, 0, MEPF_CELL); + return para; +} + +ME_DisplayItem* ME_InsertTableRowEndFromCursor(ME_TextEditor *editor) +{ + ME_DisplayItem *para; + para = ME_InsertEndParaFromCursor(editor, 0, 1, 1, MEPF_ROWEND); + return para->member.para.prev_para; +} + +ME_DisplayItem* ME_GetTableRowEnd(ME_DisplayItem *para) +{ + ME_DisplayItem *cell; + assert(para); + if (para->member.para.nFlags & MEPF_ROWEND) + return para; + if (para->member.para.nFlags & MEPF_ROWSTART) + para = para->member.para.next_para; + cell = para->member.para.pCell; + assert(cell && cell->type == diCell); + while (cell->member.cell.next_cell) + cell = cell->member.cell.next_cell; + + para = ME_FindItemFwd(cell, diParagraph); + assert(para && para->member.para.nFlags & MEPF_ROWEND); + return para; +} + +ME_DisplayItem* ME_GetTableRowStart(ME_DisplayItem *para) +{ + ME_DisplayItem *cell; + assert(para); + if (para->member.para.nFlags & MEPF_ROWSTART) + return para; + if (para->member.para.nFlags & MEPF_ROWEND) + para = para->member.para.prev_para; + cell = para->member.para.pCell; + assert(cell && cell->type == diCell); + while (cell->member.cell.prev_cell) + cell = cell->member.cell.prev_cell; + + para = ME_FindItemBack(cell, diParagraph); + assert(para && para->member.para.nFlags & MEPF_ROWSTART); + return para; +} + +/* Make a bunch of assertions to make sure tables haven't been corrupted. + * + * These invariants may not hold true in the middle of streaming in rich text + * or during an undo and redo of streaming in rich text. It should be safe to + * call this method after an event is processed. + */ +void ME_CheckTablesForCorruption(ME_TextEditor *editor) +{ + if(TRACE_ON(richedit_lists)) + { + TRACE_(richedit_lists)("---\n"); + ME_DumpDocument(editor->pBuffer); + } +#ifndef NDEBUG + { + ME_DisplayItem *p, *pPrev; + pPrev = editor->pBuffer->pFirst; + p = pPrev->next; + if (!editor->bEmulateVersion10) /* v4.1 */ + { + while (p->type == diParagraph) + { + assert(p->member.para.pFmt->dwMask & PFM_TABLE); + assert(p->member.para.pFmt->dwMask & PFM_TABLEROWDELIMITER); + if (p->member.para.pCell) + { + assert(p->member.para.nFlags & MEPF_CELL); + assert(p->member.para.pFmt->wEffects & PFE_TABLE); + } + if (p->member.para.pCell != pPrev->member.para.pCell) + { + /* There must be a diCell in between the paragraphs if pCell changes. */ + ME_DisplayItem *pCell = ME_FindItemBack(p, diCell); + assert(pCell); + assert(ME_FindItemBack(p, diRun) == ME_FindItemBack(pCell, diRun)); + } + if (p->member.para.nFlags & MEPF_ROWEND) + { + /* ROWEND must come after a cell. */ + assert(pPrev->member.para.pCell); + assert(p->member.para.pCell + == pPrev->member.para.pCell->member.cell.parent_cell); + assert(p->member.para.pFmt->wEffects & PFE_TABLEROWDELIMITER); + } + else if (p->member.para.pCell) + { + assert(!(p->member.para.pFmt->wEffects & PFE_TABLEROWDELIMITER)); + assert(pPrev->member.para.pCell || + pPrev->member.para.nFlags & MEPF_ROWSTART); + if (pPrev->member.para.pCell && + !(pPrev->member.para.nFlags & MEPF_ROWSTART)) + { + assert(p->member.para.pCell->member.cell.parent_cell + == pPrev->member.para.pCell->member.cell.parent_cell); + if (pPrev->member.para.pCell != p->member.para.pCell) + assert(pPrev->member.para.pCell + == p->member.para.pCell->member.cell.prev_cell); + } + } + else if (!(p->member.para.nFlags & MEPF_ROWSTART)) + { + assert(!(p->member.para.pFmt->wEffects & (PFE_TABLE|PFE_TABLEROWDELIMITER))); + /* ROWSTART must be followed by a cell. */ + assert(!(p->member.para.nFlags & MEPF_CELL)); + /* ROWSTART must be followed by a cell. */ + assert(!(pPrev->member.para.nFlags & MEPF_ROWSTART)); + } + pPrev = p; + p = p->member.para.next_para; + } + } else { /* v1.0 - 3.0 */ + while (p->type == diParagraph) + { + assert(!(p->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL))); + assert(p->member.para.pFmt->dwMask & PFM_TABLE); + assert(!(p->member.para.pFmt->wEffects & PFM_TABLEROWDELIMITER)); + assert(!p->member.para.pCell); + p = p->member.para.next_para; + } + return; + } + assert(p->type == diTextEnd); + assert(!pPrev->member.para.pCell); + } +#endif +} BOOL ME_IsInTable(ME_DisplayItem *pItem) { @@ -52,9 +270,6 @@ void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, int nOfs,int *nChars) { ME_Cursor c, c2; ME_DisplayItem *this_para, *end_para; - ME_DisplayItem *pRun; - int nCharsToBoundary; - ME_CursorFromCharOfs(editor, nOfs, &c); this_para = ME_GetParagraph(c.pRun); ME_CursorFromCharOfs(editor, nOfs + *nChars, &c2); @@ -72,54 +287,111 @@ void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, int nOfs,int *nChars) end_para = end_para->member.para.next_para; } } - - if (this_para->member.para.nCharOfs != nOfs && - 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) - { - if (this_para == end_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))) { - pRun = c2.pRun; - /* Find the previous tab or end paragraph to use as a delete boundary */ - while (pRun && !(pRun->member.run.nFlags & (MERF_TAB|MERF_ENDPARA))) - pRun = ME_FindItemBack(pRun, diRun); - if (pRun && pRun->member.run.nFlags & MERF_ENDPARA) + while (this_para != end_para) { - /* We are in the first cell, and have gone back to the previous - * paragraph, so nothing needs to be protected. */ - pRun = NULL; + 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 { - /* 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) + } else { /* v1.0 - 3.0 */ + ME_DisplayItem *pRun; + int nCharsToBoundary; + + if (this_para->member.para.nCharOfs != nOfs && + 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) { - nCharsToBoundary = ME_GetParagraph(pRun)->member.para.nCharOfs - + pRun->member.run.nCharOfs - - nOfs; - if (nCharsToBoundary >= 0) - *nChars = min(*nChars, nCharsToBoundary); + if (this_para == end_para) + { + pRun = c2.pRun; + /* Find the previous tab or end paragraph to use as a delete boundary */ + while (pRun && !(pRun->member.run.nFlags & (MERF_TAB|MERF_ENDPARA))) + pRun = ME_FindItemBack(pRun, diRun); + if (pRun && pRun->member.run.nFlags & MERF_ENDPARA) + { + /* We are in the first cell, and have gone back to the previous + * paragraph, so nothing needs to be protected. */ + pRun = NULL; + } + } else { + /* 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; } - if (*nChars < 0) - nChars = 0; } static ME_DisplayItem* ME_AppendTableRow(ME_TextEditor *editor, @@ -131,18 +403,42 @@ static ME_DisplayItem* ME_AppendTableRow(ME_TextEditor *editor, int i; assert(table_row); - 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].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); + if (!editor->bEmulateVersion10) { /* v4.1 */ + ME_DisplayItem *insertedCell, *para, *cell; + cell = ME_FindItemFwd(table_row, diCell); + run = ME_GetTableRowEnd(table_row)->member.para.next_para; + run = ME_FindItemFwd(run, diRun); + 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; + 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; + }; + ME_InsertTableRowEndFromCursor(editor); + /* 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].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; } - return table_row->member.para.next_para; } /* Selects the next table cell or appends a new table row if at end of table */ @@ -154,43 +450,88 @@ static void ME_SelectOrInsertNextCell(ME_TextEditor *editor, assert(run && run->type == diRun); assert(ME_IsInTable(run)); - 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)) + 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, diRunOrParagraphOrEnd); - if (run->type != diRun) + run = ME_FindItemFwd(run, diRun); + assert(run); + } + for (i = 0; i < 2; i++) + { + while (!(run->member.run.nFlags & MERF_TAB)) { - para = run; - if (ME_IsInTable(para)) + run = ME_FindItemFwd(run, diRunOrParagraphOrEnd); + if (run->type != diRun) { - 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; + 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; } - if (i == 0) - run = ME_FindItemFwd(run, diRun); - editor->pCursors[i].pRun = run; - editor->pCursors[i].nOffset = 0; } } @@ -213,18 +554,25 @@ void ME_TabPressedInTable(ME_TextEditor *editor, BOOL bSelectedRow) toCursor = editor->pCursors[0]; } } - if (!ME_IsInTable(fromCursor.pRun)) + if (!editor->bEmulateVersion10) /* v4.1 */ { - editor->pCursors[0] = fromCursor; - editor->pCursors[1] = fromCursor; - /* FIXME: For some reason the caret is shown at the start of the - * previous paragraph in v1.0 to v3.0, and bCaretAtEnd only works - * within the paragraph for wrapped lines. */ - if (ME_FindItemBack(fromCursor.pRun, diRun)) - editor->bCaretAtEnd = TRUE; - } else { - if (bSelectedRow || !ME_IsInTable(toCursor.pRun)) + if (!ME_IsInTable(toCursor.pRun)) { + editor->pCursors[0] = toCursor; + editor->pCursors[1] = toCursor; + } else { + ME_SelectOrInsertNextCell(editor, toCursor.pRun); + } + } else { /* v1.0 - 3.0 */ + if (!ME_IsInTable(fromCursor.pRun)) { + editor->pCursors[0] = fromCursor; + editor->pCursors[1] = fromCursor; + /* FIXME: For some reason the caret is shown at the start of the + * previous paragraph in v1.0 to v3.0, and bCaretAtEnd only works + * within the paragraph for wrapped lines. */ + if (ME_FindItemBack(fromCursor.pRun, diRun)) + editor->bCaretAtEnd = TRUE; + } else if ((bSelectedRow || !ME_IsInTable(toCursor.pRun))) { ME_SelectOrInsertNextCell(editor, fromCursor.pRun); } else { if (ME_IsSelection(editor) && !toCursor.nOffset) diff --git a/dlls/riched20/undo.c b/dlls/riched20/undo.c index d5e071c..3ab39d0 100644 --- a/dlls/riched20/undo.c +++ b/dlls/riched20/undo.c @@ -89,10 +89,17 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_Disp case diUndoJoinParagraphs: break; case diUndoSplitParagraph: + { + ME_DisplayItem *prev_para = pdi->member.para.prev_para; assert(pdi->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); + pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2); + pItem->member.para.pFmt->dwMask = 0; *pItem->member.para.pFmt = *pdi->member.para.pFmt; + pItem->member.para.nFlags = prev_para->member.para.nFlags & ~MEPF_CELL; + pItem->member.para.pCell = NULL; break; + } default: assert(0 == "AddUndoItem, unsupported item type"); return NULL; @@ -314,16 +321,35 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem) case diUndoSplitParagraph: { ME_Cursor tmp; - ME_DisplayItem *new_para; + 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); + 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; + 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; + } break; } default: @@ -361,6 +387,7 @@ BOOL ME_Undo(ME_TextEditor *editor) { if (p) p->prev = NULL; ME_AddUndoItem(editor, diUndoEndTransaction, NULL); + ME_CheckTablesForCorruption(editor); editor->nUndoStackSize--; editor->nUndoMode = nMode; ME_UpdateRepaint(editor); @@ -396,6 +423,7 @@ BOOL ME_Redo(ME_TextEditor *editor) { if (p) p->prev = NULL; ME_AddUndoItem(editor, diUndoEndTransaction, NULL); + ME_CheckTablesForCorruption(editor); editor->nUndoMode = nMode; ME_UpdateRepaint(editor); return TRUE; diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 5fe2209..27aec2d 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -42,17 +42,37 @@ static ME_DisplayItem *ME_MakeRow(int height, int baseline, int width) return item; } -static void ME_BeginRow(ME_WrapContext *wc) +static void ME_BeginRow(ME_WrapContext *wc, ME_DisplayItem *para) { + assert(para && para->type == diParagraph); wc->pRowStart = NULL; wc->bOverflown = FALSE; wc->pLastSplittableRun = NULL; - if (wc->context->editor->bWordWrap) - wc->nAvailWidth = wc->context->rcView.right - wc->context->rcView.left - - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; - else + if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) { + wc->nAvailWidth = 0; + 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; + cell->nWidth = max(ME_twips2pointsX(wc->context, width), 0); + + wc->nAvailWidth = cell->nWidth + - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; + } else if (wc->context->editor->bWordWrap) { + wc->nAvailWidth = wc->context->rcView.right - wc->context->rcView.left + - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; + } else { wc->nAvailWidth = ~0u >> 1; - wc->pt.x = 0; + } + wc->pt.x = wc->context->pt.x; } static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd) @@ -100,7 +120,7 @@ static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd) } row = ME_MakeRow(ascent+descent, ascent, width); - row->member.row.nYPos = wc->pt.y; + row->member.row.pt = wc->pt; row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin); row->member.row.nRMargin = wc->nRightMargin; assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT); @@ -118,7 +138,7 @@ static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd) ME_InsertBefore(wc->pRowStart, row); wc->nRow++; wc->pt.y += ascent+descent; - ME_BeginRow(wc); + ME_BeginRow(wc, para); } static void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p) @@ -326,9 +346,9 @@ static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p) } /* will current run fit? */ - if (wc->pt.x + run->nWidth > wc->nAvailWidth) + if (wc->pt.x + run->nWidth > wc->context->pt.x + wc->nAvailWidth) { - int loc = wc->nAvailWidth - wc->pt.x; + int loc = wc->context->pt.x + wc->nAvailWidth - wc->pt.x; /* total white run ? */ if (run->nFlags & MERF_WHITESPACE) { /* let the overflow logic handle it */ @@ -421,7 +441,6 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp, DWORD begino wc.nLeftMargin = wc.nFirstMargin + ME_twips2pointsX(c, tp->member.para.pFmt->dxOffset); wc.nRightMargin = ME_twips2pointsX(c, tp->member.para.pFmt->dxRightIndent); wc.nRow = 0; - wc.pt.x = 0; wc.pt.y = 0; if (tp->member.para.pFmt->dwMask & PFM_SPACEBEFORE) wc.pt.y += ME_twips2pointsY(c, tp->member.para.pFmt->dySpaceBefore); @@ -440,7 +459,7 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp, DWORD begino linespace = ME_GetParaLineSpace(c, &tp->member.para); - ME_BeginRow(&wc); + ME_BeginRow(&wc, tp); for (p = tp->next; p!=tp->member.para.next_para; ) { assert(p->type != diStartRow); if (p->type == diRun) { @@ -515,11 +534,11 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { BOOL bRedraw = FALSE; assert(item->type == diParagraph); - editor->nHeight = max(editor->nHeight, item->member.para.nYPos); + editor->nHeight = max(editor->nHeight, item->member.para.pt.y); if ((item->member.para.nFlags & MEPF_REWRAP) - || (item->member.para.nYPos != c.pt.y)) + || (item->member.para.pt.y != c.pt.y)) bRedraw = TRUE; - item->member.para.nYPos = c.pt.y; + item->member.para.pt = c.pt; ME_WrapTextParagraph(&c, item, editor->selofs); @@ -532,24 +551,90 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { bModified = bModified | bRedraw; - yLastPos = c.pt.y; - c.pt.y += item->member.para.nHeight; + yLastPos = max(yLastPos, c.pt.y); + + if (item->member.para.nFlags & MEPF_ROWSTART) + { + ME_DisplayItem *cell = ME_FindItemFwd(item, diCell); + cell->member.cell.pt = c.pt; + } + else if (item->member.para.nFlags & MEPF_ROWEND) + { + /* Set all the cells to the height of the largest cell */ + ME_DisplayItem *startRowPara; + int prevHeight, nHeight; + ME_DisplayItem *cell = ME_FindItemBack(item, diCell); + prevHeight = cell->member.cell.nHeight; + nHeight = cell->member.cell.prev_cell->member.cell.nHeight; + cell->member.cell.nHeight = nHeight; + item->member.para.nHeight = nHeight; + cell = cell->member.cell.prev_cell; + while (cell->member.cell.prev_cell) + { + cell = cell->member.cell.prev_cell; + cell->member.cell.nHeight = nHeight; + } + /* Also set the height of the start row paragraph */ + startRowPara = ME_FindItemBack(cell, diParagraph); + startRowPara->member.para.nHeight = nHeight; + c.pt.x = startRowPara->member.para.pt.x; + c.pt.y = cell->member.cell.pt.y + nHeight; + if (prevHeight < nHeight) + { + /* The height of the cells has grown, so invalidate the bottom of + * the cells. */ + item->member.para.nFlags |= MEPF_REPAINT; + cell = ME_FindItemBack(item, diCell); + while (cell) { + ME_FindItemBack(cell, diParagraph)->member.para.nFlags |= MEPF_REPAINT; + cell = cell->member.cell.prev_cell; + } + } + } + else if (item->member.para.pCell && + item->member.para.pCell != item->member.para.next_para->member.para.pCell) + { + /* The next paragraph is in the next cell in the table row. */ + ME_Cell *cell = &item->member.para.pCell->member.cell; + cell->nHeight = c.pt.y + item->member.para.nHeight - cell->pt.y; + + /* Propagate the largest height to the end so that it can be easily + * sent back to all the cells at the end of the row. */ + if (cell->prev_cell) + cell->nHeight = max(cell->nHeight, cell->prev_cell->member.cell.nHeight); + + c.pt.x = cell->pt.x + cell->nWidth; + c.pt.y = cell->pt.y; + cell->next_cell->member.cell.pt = c.pt; + } + else + { + if (item->member.para.pCell) { + /* Next paragraph in the same cell. */ + c.pt.x = item->member.para.pCell->member.cell.pt.x; + } else { + /* Normal paragraph */ + c.pt.x = 0; + } + c.pt.y += item->member.para.nHeight; + } item = item->member.para.next_para; } editor->sizeWindow.cx = c.rcView.right-c.rcView.left; editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top; editor->nTotalLength = c.pt.y; - editor->pBuffer->pLast->member.para.nYPos = yLastPos; + editor->pBuffer->pLast->member.para.pt.x = 0; + editor->pBuffer->pLast->member.para.pt.y = yLastPos; ME_DestroyContext(&c, editor->hWnd); /* Each paragraph may contain multiple rows, which should be scrollable, even - if the containing paragraph has nYPos == 0 */ + if the containing paragraph has pt.y == 0 */ item = editor->pBuffer->pFirst; while ((item = ME_FindItemFwd(item, diStartRow)) != NULL) { assert(item->type == diStartRow); - editor->nHeight = max(editor->nHeight, item->member.row.nYPos); + editor->nHeight = max(editor->nHeight, item->member.row.pt.y); } if (bModified || editor->nTotalLength < editor->nLastTotalLength) @@ -569,8 +654,8 @@ void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor) { ME_DisplayItem *item = editor->pBuffer->pFirst; while(item != editor->pBuffer->pLast) { if (item->member.para.nFlags & MEPF_REPAINT) { - rc.top = item->member.para.nYPos - ofs; - rc.bottom = item->member.para.nYPos + item->member.para.nHeight - ofs; + rc.top = item->member.para.pt.y - ofs; + rc.bottom = item->member.para.pt.y + item->member.para.nHeight - ofs; InvalidateRect(editor->hWnd, &rc, TRUE); } item = item->member.para.next_para; diff --git a/dlls/riched20/writer.c b/dlls/riched20/writer.c index 13afb3c..512dbce 100644 --- a/dlls/riched20/writer.c +++ b/dlls/riched20/writer.c @@ -41,6 +41,7 @@ ME_StreamOutInit(ME_TextEditor *editor, EDITSTREAM *stream) pStream->written = 0; pStream->nFontTblLen = 0; pStream->nColorTblLen = 1; + pStream->nNestingLevel = 0; return pStream; } @@ -284,28 +285,43 @@ ME_StreamOutRTFFontAndColorTbl(ME_OutStream *pStream, ME_DisplayItem *pFirstRun, } static BOOL -ME_StreamOutRTFTableProps(ME_OutStream *pStream, const ME_DisplayItem *para) +ME_StreamOutRTFTableProps(ME_TextEditor *editor, ME_OutStream *pStream, + const ME_DisplayItem *para) { - PARAFORMAT2 *pFmt; + ME_DisplayItem *cell; char props[STREAMOUT_BUFFER_SIZE] = ""; - int i; if (!ME_StreamOutPrint(pStream, "\\trowd")) return FALSE; - pFmt = para->member.para.pFmt; - - for (i = 0; i < pFmt->cTabCount; i++) - { - sprintf(props, "\\cellx%d", pFmt->rgxTabs[i] & 0x00FFFFFF); - if (!ME_StreamOutPrint(pStream, props)) - return FALSE; + if (!editor->bEmulateVersion10) { /* v4.1 */ + assert(para->member.para.nFlags & MEPF_ROWSTART); + cell = para->member.para.next_para->member.para.pCell; + assert(cell); + do { + sprintf(props, "\\cellx%d", cell->member.cell.nRightBoundary); + if (!ME_StreamOutPrint(pStream, props)) + return FALSE; + cell = cell->member.cell.next_cell; + } while (cell->member.cell.next_cell); + } else { /* v1.0 - 3.0 */ + PARAFORMAT2 *pFmt = para->member.para.pFmt; + int i; + + assert(!(para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL))); + for (i = 0; i < pFmt->cTabCount; i++) + { + sprintf(props, "\\cellx%d", pFmt->rgxTabs[i] & 0x00FFFFFF); + if (!ME_StreamOutPrint(pStream, props)) + return FALSE; + } } props[0] = '\0'; return TRUE; } static BOOL -ME_StreamOutRTFParaProps(ME_OutStream *pStream, const ME_DisplayItem *para) +ME_StreamOutRTFParaProps(ME_TextEditor *editor, ME_OutStream *pStream, + const ME_DisplayItem *para) { PARAFORMAT2 *fmt = para->member.para.pFmt; char props[STREAMOUT_BUFFER_SIZE] = ""; @@ -315,8 +331,13 @@ ME_StreamOutRTFParaProps(ME_OutStream *pStream, const ME_DisplayItem *para) if (!ME_StreamOutPrint(pStream, "\\pard")) return FALSE; - if (fmt->dwMask & PFM_TABLE && fmt->wEffects & PFE_TABLE) - strcat(props, "\\intbl"); + if (!editor->bEmulateVersion10) { /* v4.1 */ + if (pStream->nNestingLevel > 0) + strcat(props, "\\intbl"); + } else { /* v1.0 - 3.0 */ + if (fmt->dwMask & PFM_TABLE && fmt->wEffects & PFE_TABLE) + strcat(props, "\\intbl"); + } /* TODO: PFM_BORDER. M$ does not emit any keywords for these properties, and * when streaming border keywords in, PFM_BORDER is set, but wBorder field is @@ -683,7 +704,7 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC /* TODO: section formatting properties */ - if (!ME_StreamOutRTFParaProps(pStream, ME_GetParagraph(p))) + if (!ME_StreamOutRTFParaProps(editor, pStream, ME_GetParagraph(p))) return FALSE; while(1) @@ -691,14 +712,28 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC switch(p->type) { case diParagraph: - if (p->member.para.pFmt->dwMask & PFM_TABLE && - p->member.para.pFmt->wEffects & PFE_TABLE) - { - if (!ME_StreamOutRTFTableProps(pStream, p)) + if (!editor->bEmulateVersion10) { /* v4.1 */ + if (p->member.para.nFlags & MEPF_ROWSTART) { + pStream->nNestingLevel++; + if (!ME_StreamOutRTFTableProps(editor, pStream, p)) + return FALSE; + } else if (p->member.para.nFlags & MEPF_ROWEND) { + pStream->nNestingLevel--; + if (!ME_StreamOutPrint(pStream, "\\row \r\n")) + return FALSE; + } else if (!ME_StreamOutRTFParaProps(editor, pStream, p)) { + return FALSE; + } + } else { /* v1.0 - 3.0 */ + if (p->member.para.pFmt->dwMask & PFM_TABLE && + p->member.para.pFmt->wEffects & PFE_TABLE) + { + if (!ME_StreamOutRTFTableProps(editor, pStream, p)) + return FALSE; + } + if (!ME_StreamOutRTFParaProps(editor, pStream, p)) return FALSE; } - if (!ME_StreamOutRTFParaProps(pStream, p)) - return FALSE; pPara = p; break; case diRun: @@ -706,10 +741,13 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC break; TRACE("flags %xh\n", p->member.run.nFlags); /* TODO: emit embedded objects */ + if (pPara->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) + break; if (p->member.run.nFlags & MERF_GRAPHICS) { FIXME("embedded objects are not handled\n"); } else if (p->member.run.nFlags & MERF_TAB) { - if (pPara->member.para.pFmt->dwMask & PFM_TABLE && + if (editor->bEmulateVersion10 && /* v1.0 - 3.0 */ + pPara->member.para.pFmt->dwMask & PFM_TABLE && pPara->member.para.pFmt->wEffects & PFE_TABLE) { if (!ME_StreamOutPrint(pStream, "\\cell ")) @@ -718,9 +756,14 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC if (!ME_StreamOutPrint(pStream, "\\tab ")) return FALSE; } + } else if (p->member.run.nFlags & MERF_ENDCELL) { + if (!ME_StreamOutPrint(pStream, "\\cell ")) + return FALSE; + nChars--; } else if (p->member.run.nFlags & MERF_ENDPARA) { - if (pPara->member.para.pFmt->dwMask & PFM_TABLE - && pPara->member.para.pFmt->wEffects & PFE_TABLE) + if (pPara->member.para.pFmt->dwMask & PFM_TABLE && + pPara->member.para.pFmt->wEffects & PFE_TABLE && + !(pPara->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL))) { if (!ME_StreamOutPrint(pStream, "\\row \r\n")) return FALSE;