Commit 300db376 authored by Dylan Smith's avatar Dylan Smith Committed by Alexandre Julliard

richedit: Each cell can contain multiple paragraphs in msftedit.

parent bc61a637
......@@ -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;
......
......@@ -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:
......@@ -669,10 +725,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)
{
......@@ -683,7 +745,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;
......@@ -704,7 +767,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 &&
......@@ -718,11 +793,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;
......@@ -1128,6 +1262,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);
......@@ -1145,6 +1281,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);
......@@ -1624,10 +1790,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;
......@@ -2503,10 +2676,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;
......@@ -2572,7 +2745,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 */
......@@ -3089,10 +3262,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;
......@@ -3287,8 +3460,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;
......@@ -3726,7 +3904,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. */
......@@ -3738,10 +3921,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
{
......
......@@ -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);
......
......@@ -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
......
......@@ -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");
......
......@@ -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<c.rcView.bottom) {
......@@ -394,19 +426,19 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
if (runofs >= 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 = &para->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 = &para->next_para->member.para.pCell->member.cell;
rc.right = cell->pt.x;
} else if (para->nFlags & MEPF_ROWEND) {
ME_Cell *cell = &para->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 = &para->next_para->member.para.pCell->member.cell;
rc.bottom = y + cell->nHeight;
} else if (para->nFlags & MEPF_ROWEND) {
ME_Cell *cell = &para->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;
......
......@@ -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;
......
......@@ -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;
};
/*
......
......@@ -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;
......
......@@ -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)
......
......@@ -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;
......
......@@ -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 = &para->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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment