Synced riched20 with Wine HEAD

svn path=/trunk/; revision=35728
This commit is contained in:
Pierre Schweitzer 2008-08-28 10:43:26 +00:00
parent dea8ec1229
commit f381a715d2
17 changed files with 3050 additions and 586 deletions

View file

@ -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;
}
@ -234,7 +234,7 @@ ME_MoveCaret(ME_TextEditor *editor)
if (ME_WrapMarkedParagraphs(editor))
ME_UpdateScrollBar(editor);
ME_GetCursorCoordinates(editor, &editor->pCursors[0], &x, &y, &height);
if(editor->bHaveFocus)
if(editor->bHaveFocus && !ME_IsSelection(editor))
{
RECT rect;
@ -249,38 +249,96 @@ ME_MoveCaret(ME_TextEditor *editor)
void ME_ShowCaret(ME_TextEditor *ed)
{
ME_MoveCaret(ed);
if(ed->bHaveFocus)
if(ed->bHaveFocus && !ME_IsSelection(ed))
ShowCaret(ed->hWnd);
}
void ME_HideCaret(ME_TextEditor *ed)
{
if(ed->bHaveFocus)
if(!ed->bHaveFocus || ME_IsSelection(ed))
{
HideCaret(ed->hWnd);
DestroyCaret();
}
}
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
int nChars)
BOOL ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars,
BOOL bForce)
{
ME_Cursor c;
int shift = 0;
int totalChars = nChars;
ME_DisplayItem *start_para;
{
/* Prevent deletion past last end of paragraph run. */
ME_DisplayItem *pTextEnd = editor->pBuffer->pLast;
int nMaxChars = pTextEnd->member.para.prev_para->member.para.nCharOfs;
nMaxChars += ME_FindItemBack(pTextEnd, diRun)->member.run.nCharOfs;
nMaxChars -= nOfs;
nChars = min(nChars, nMaxChars);
}
ME_CursorFromCharOfs(editor, nOfs, &c);
start_para = ME_GetParagraph(c.pRun);
if (!bForce)
{
ME_ProtectPartialTableDeletion(editor, nOfs, &nChars);
if (nChars == 0)
return FALSE;
}
while(nChars > 0)
{
ME_Run *run;
ME_CursorFromCharOfs(editor, nOfs, &c);
ME_CursorFromCharOfs(editor, nOfs+nChars, &c);
if (!c.nOffset &&
nOfs+nChars == (c.pRun->member.run.nCharOfs
+ ME_GetParagraph(c.pRun)->member.para.nCharOfs))
{
/* We aren't deleting anything in this run, so we will go back to the
* last run we are deleting text in. */
c.pRun = ME_FindItemBack(c.pRun, diRun);
if (c.pRun->member.run.nFlags & MERF_ENDPARA)
c.nOffset = c.pRun->member.run.nCR + c.pRun->member.run.nLF;
else
c.nOffset = c.pRun->member.run.strText->nLen;
}
run = &c.pRun->member.run;
if (run->nFlags & MERF_ENDPARA) {
int eollen = run->nCR + run->nLF;
BOOL keepFirstParaFormat;
if (!ME_FindItemFwd(c.pRun, diParagraph))
{
return;
return TRUE;
}
ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun));
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);
nChars -= (eollen < nChars) ? eollen : nChars;
@ -289,22 +347,21 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
else
{
ME_Cursor cursor;
int nIntendedChars = nChars;
int nCharsToDelete = nChars;
int nCharsToDelete = min(nChars, c.nOffset);
int i;
int loc = c.nOffset;
c.nOffset -= nCharsToDelete;
ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP;
cursor = c;
ME_StrRelPos(run->strText, loc, &nChars);
/* nChars is the number of characters that should be deleted from the
FOLLOWING runs (these AFTER cursor.pRun)
PRECEDING runs (these BEFORE cursor.pRun)
nCharsToDelete is a number of chars to delete from THIS run */
nCharsToDelete -= nChars;
nChars -= nCharsToDelete;
shift -= nCharsToDelete;
TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n",
nCharsToDelete, nIntendedChars, nChars, c.nOffset,
TRACE("Deleting %d (remaning %d) chars at %d in '%s' (%d)\n",
nCharsToDelete, nChars, c.nOffset,
debugstr_w(run->strText->szData), run->strText->nLen);
if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete)
@ -314,7 +371,7 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
to the current (deleted) run */
ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
if (pUndo)
pUndo->di.member.run.nCharOfs = nOfs;
pUndo->di.member.run.nCharOfs = nOfs+nChars;
}
else
{
@ -322,7 +379,7 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
if (pUndo) {
ME_DestroyString(pUndo->di.member.run.strText);
pUndo->di.member.run.nCharOfs = nOfs;
pUndo->di.member.run.nCharOfs = nOfs+nChars;
pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete);
}
}
@ -373,15 +430,16 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
continue;
}
}
return TRUE;
}
void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor,
int nChars)
BOOL ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars)
{
assert(nCursor>=0 && nCursor<editor->nCursors);
/* text operations set modified state */
editor->nModifyStep = 1;
ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars);
return ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars,
FALSE);
}
static ME_DisplayItem *
@ -432,31 +490,6 @@ void ME_InsertEndRowFromCursor(ME_TextEditor *editor, int nCursor)
ME_SendSelChange(editor);
}
void
ME_InsertTableCellFromCursor(ME_TextEditor *editor, int nCursor)
{
WCHAR tab = '\t';
ME_DisplayItem *p, *run;
ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor);
p = ME_InternalInsertTextFromCursor(editor, nCursor, &tab, 1, pStyle,
MERF_CELL);
run = p;
while ((run = ME_FindItemBack(run, diRunOrParagraph))->type == diRun)
{
if (run->member.run.nFlags & MERF_CELL)
{
assert(run->member.run.pCell->next);
p->member.run.pCell = run->member.run.pCell->next;
return;
}
}
assert(run->type == diParagraph);
assert(run->member.para.bTable);
assert(run->member.para.pCells);
p->member.run.pCell = run->member.para.pCells;
}
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
const WCHAR *str, int len, ME_Style *style)
@ -572,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);
@ -619,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;
@ -645,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;
@ -690,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;
@ -723,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;
@ -744,13 +785,63 @@ ME_MoveCursorWords(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs)
void
ME_SelectWord(ME_TextEditor *editor)
ME_SelectByType(ME_TextEditor *editor, ME_SelectionType selectionType)
{
ME_MoveCursorWords(editor, &editor->pCursors[1], +1);
editor->pCursors[0] = editor->pCursors[1];
ME_MoveCursorWords(editor, &editor->pCursors[0], -1);
}
/* pCursor[0] is the end of the selection
* pCursor[1] is the start of the selection (or the position selection anchor)
* pCursor[2] and [3] are the selection anchors that are backed up
* so they are kept when the selection changes for drag selection.
*/
editor->nSelectionType = selectionType;
switch(selectionType)
{
case stPosition:
break;
case stWord:
ME_MoveCursorWords(editor, &editor->pCursors[0], +1);
editor->pCursors[1] = editor->pCursors[0];
ME_MoveCursorWords(editor, &editor->pCursors[1], -1);
break;
case stLine:
case stParagraph:
{
ME_DisplayItem *pItem;
ME_DIType fwdSearchType, backSearchType;
if (selectionType == stParagraph) {
backSearchType = diParagraph;
fwdSearchType = diParagraphOrEnd;
} else {
backSearchType = diStartRow;
fwdSearchType = diStartRowOrParagraphOrEnd;
}
pItem = ME_FindItemFwd(editor->pCursors[0].pRun, fwdSearchType);
assert(pItem);
if (pItem->type == diTextEnd)
editor->pCursors[0].pRun = ME_FindItemBack(pItem, diRun);
else
editor->pCursors[0].pRun = ME_FindItemFwd(pItem, diRun);
editor->pCursors[0].nOffset = 0;
pItem = ME_FindItemBack(pItem, backSearchType);
editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun);
editor->pCursors[1].nOffset = 0;
break;
}
case stDocument:
/* Select everything with cursor anchored from the start of the text */
editor->nSelectionType = stDocument;
editor->pCursors[1].pRun = ME_FindItemFwd(editor->pBuffer->pFirst, diRun);
editor->pCursors[1].nOffset = 0;
editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
editor->pCursors[0].nOffset = 0;
break;
default: assert(0);
}
/* Store the anchor positions for extending the selection. */
editor->pCursors[2] = editor->pCursors[0];
editor->pCursors[3] = editor->pCursors[1];
}
int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor)
{
@ -759,11 +850,60 @@ int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor)
+ pCursor->pRun->member.run.nCharOfs + pCursor->nOffset;
}
static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol)
/* 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,
* so client coordinates must first be adjusted by the scroll position.
*
* returns TRUE if the result was exactly under the cursor, otherwise returns
* FALSE, and result is set to the closest position to the coordinates.
*/
static BOOL ME_FindPixelPos(ME_TextEditor *editor, int x, int y,
ME_Cursor *result, BOOL *is_eol)
{
ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para;
ME_DisplayItem *last = NULL;
int rx = 0;
BOOL isExact = TRUE;
if (is_eol)
*is_eol = 0;
@ -772,11 +912,15 @@ static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *resu
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 */
@ -784,7 +928,7 @@ static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *resu
{
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;
@ -802,6 +946,7 @@ static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *resu
/* The position is below the last paragraph, so the last row will be used
* rather than the end of the text, so the x position will be used to
* determine the offset closest to the pixel position. */
isExact = FALSE;
p = ME_FindItemBack(p, diStartRow);
if (p != NULL){
p = ME_FindItemFwd(p, diRun);
@ -830,16 +975,19 @@ static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *resu
result->pRun = ME_FindItemFwd(editor->pCursors[0].pRun, diRun);
result->nOffset = 0;
}
return;
return isExact;
}
break;
case diStartRow:
isExact = FALSE;
p = ME_FindItemFwd(p, diRun);
if (is_eol) *is_eol = 1;
rx = 0; /* FIXME not sure */
goto found_here;
case diCell:
case diParagraph:
case diTextEnd:
isExact = FALSE;
rx = 0; /* FIXME not sure */
p = last;
goto found_here;
@ -850,29 +998,102 @@ static void ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *resu
result->pRun = ME_FindItemBack(p, diRun);
result->nOffset = 0;
assert(result->pRun->member.run.nFlags & MERF_ENDPARA);
return FALSE;
}
int
ME_CharFromPos(ME_TextEditor *editor, int x, int y)
/* Returns the character offset closest to the pixel position
*
* x & y are pixel positions in client coordinates.
*
* isExact will be set to TRUE if the run is directly under the pixel
* position, FALSE if it not, unless isExact is set to NULL.
*/
int ME_CharFromPos(ME_TextEditor *editor, int x, int y, BOOL *isExact)
{
ME_Cursor cursor;
RECT rc;
BOOL bResult;
GetClientRect(editor->hWnd, &rc);
if (x < 0 || y < 0 || x >= rc.right || y >= rc.bottom)
if (x < 0 || y < 0 || x >= rc.right || y >= rc.bottom) {
if (isExact) *isExact = FALSE;
return -1;
}
y += ME_GetYScrollPos(editor);
ME_FindPixelPos(editor, x, y, &cursor, NULL);
bResult = ME_FindPixelPos(editor, x, y, &cursor, NULL);
if (isExact) *isExact = bResult;
return (ME_GetParagraph(cursor.pRun)->member.para.nCharOfs
+ cursor.pRun->member.run.nCharOfs + cursor.nOffset);
}
/* Extends the selection with a word, line, or paragraph selection type.
*
* The selection is anchored by editor->pCursors[2-3] such that the text
* between the anchors will remain selected, and one end will be extended.
*
* editor->pCursors[0] should have the position to extend the selection to
* before this function is called.
*
* Nothing will be done if editor->nSelectionType equals stPosition.
*/
static void ME_ExtendAnchorSelection(ME_TextEditor *editor)
{
ME_Cursor tmp_cursor;
int curOfs, anchorStartOfs, anchorEndOfs;
if (editor->nSelectionType == stPosition || editor->nSelectionType == stDocument)
return;
curOfs = ME_GetCursorOfs(editor, 0);
anchorStartOfs = ME_GetCursorOfs(editor, 3);
anchorEndOfs = ME_GetCursorOfs(editor, 2);
tmp_cursor = editor->pCursors[0];
editor->pCursors[0] = editor->pCursors[2];
editor->pCursors[1] = editor->pCursors[3];
if (curOfs < anchorStartOfs)
{
/* Extend the left side of selection */
editor->pCursors[1] = tmp_cursor;
if (editor->nSelectionType == stWord)
ME_MoveCursorWords(editor, &editor->pCursors[1], -1);
else
{
ME_DisplayItem *pItem;
ME_DIType searchType = ((editor->nSelectionType == stLine) ?
diStartRowOrParagraph:diParagraph);
pItem = ME_FindItemBack(editor->pCursors[1].pRun, searchType);
editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun);
editor->pCursors[1].nOffset = 0;
}
}
else if (curOfs >= anchorEndOfs)
{
/* Extend the right side of selection */
editor->pCursors[0] = tmp_cursor;
if (editor->nSelectionType == stWord)
ME_MoveCursorWords(editor, &editor->pCursors[0], +1);
else
{
ME_DisplayItem *pItem;
ME_DIType searchType = ((editor->nSelectionType == stLine) ?
diStartRowOrParagraphOrEnd:diParagraphOrEnd);
pItem = ME_FindItemFwd(editor->pCursors[0].pRun, searchType);
if (pItem->type == diTextEnd)
editor->pCursors[0].pRun = ME_FindItemBack(pItem, diRun);
else
editor->pCursors[0].pRun = ME_FindItemFwd(pItem, diRun);
editor->pCursors[0].nOffset = 0;
}
}
}
void ME_LButtonDown(ME_TextEditor *editor, int x, int y, int clickNum)
{
ME_Cursor tmp_cursor;
int is_selection = 0;
BOOL is_shift;
editor->nUDArrowX = -1;
@ -880,59 +1101,54 @@ void ME_LButtonDown(ME_TextEditor *editor, int x, int y, int clickNum)
tmp_cursor = editor->pCursors[0];
is_selection = ME_IsSelection(editor);
is_shift = GetKeyState(VK_SHIFT) < 0;
if (x >= editor->selofs)
ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd);
if (x >= editor->selofs || is_shift)
{
ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd);
if (GetKeyState(VK_SHIFT)>=0)
if (clickNum > 1)
{
/* Shift key is not down */
editor->pCursors[1] = editor->pCursors[0];
if (clickNum > 1)
ME_SelectWord(editor);
if (is_shift) {
if (x >= editor->selofs)
ME_SelectByType(editor, stWord);
else
ME_SelectByType(editor, stParagraph);
} else if (clickNum % 2 == 0) {
ME_SelectByType(editor, stWord);
} else {
ME_SelectByType(editor, stParagraph);
}
}
else if (!is_selection) {
else if (!is_shift)
{
editor->nSelectionType = stPosition;
editor->pCursors[1] = editor->pCursors[0];
}
else if (!is_selection)
{
editor->nSelectionType = stPosition;
editor->pCursors[1] = tmp_cursor;
is_selection = 1;
}
else if (editor->nSelectionType != stPosition)
{
ME_ExtendAnchorSelection(editor);
}
}
else
{
ME_DisplayItem *pRow;
editor->linesel = 1;
editor->sely = y;
/* Set pCursors[0] to beginning of line */
ME_FindPixelPos(editor, x, y, &editor->pCursors[1], &editor->bCaretAtEnd);
/* Set pCursors[1] to end of line */
pRow = ME_FindItemFwd(editor->pCursors[1].pRun, diStartRowOrParagraphOrEnd);
assert(pRow);
/* pCursor[0] is the position where the cursor will be drawn,
* pCursor[1] is the other end of the selection range
* pCursor[2] and [3] are backups of [0] and [1] so I
* don't have to look them up again
*/
if (pRow->type == diStartRow) {
/* FIXME WTF was I thinking about here ? */
ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun);
assert(pRun);
editor->pCursors[0].pRun = pRun;
editor->pCursors[0].nOffset = 0;
editor->bCaretAtEnd = 1;
if (clickNum < 2) {
ME_SelectByType(editor, stLine);
} else if (clickNum % 2 == 0 || is_shift) {
ME_SelectByType(editor, stParagraph);
} else {
editor->pCursors[0].pRun = ME_FindItemBack(pRow, diRun);
assert(editor->pCursors[0].pRun && editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA);
editor->pCursors[0].nOffset = 0;
editor->bCaretAtEnd = 0;
ME_SelectByType(editor, stDocument);
}
editor->pCursors[2] = editor->pCursors[0];
editor->pCursors[3] = editor->pCursors[1];
}
ME_InvalidateSelection(editor);
HideCaret(editor->hWnd);
ME_MoveCaret(editor);
ShowCaret(editor->hWnd);
ME_ShowCaret(editor);
ME_ClearTempStyle(editor);
ME_SendSelChange(editor);
}
@ -941,43 +1157,38 @@ void ME_MouseMove(ME_TextEditor *editor, int x, int y)
{
ME_Cursor tmp_cursor;
if (editor->nSelectionType == stDocument)
return;
y += ME_GetYScrollPos(editor);
tmp_cursor = editor->pCursors[0];
/* FIXME: do something with the return value of ME_FindPixelPos */
if (!editor->linesel)
ME_FindPixelPos(editor, x, y, &tmp_cursor, &editor->bCaretAtEnd);
else ME_FindPixelPos(editor, (y > editor->sely) * editor->rcFormat.right, y, &tmp_cursor, &editor->bCaretAtEnd);
if (!memcmp(&tmp_cursor, editor->pCursors, sizeof(tmp_cursor)))
return;
ME_FindPixelPos(editor, x, y, &tmp_cursor, &editor->bCaretAtEnd);
ME_InvalidateSelection(editor);
if (!editor->linesel)
editor->pCursors[0] = tmp_cursor;
else if (!memcmp(&tmp_cursor, editor->pCursors+2, sizeof(tmp_cursor)) ||
!memcmp(&tmp_cursor, editor->pCursors+3, sizeof(tmp_cursor)))
editor->pCursors[0] = tmp_cursor;
ME_ExtendAnchorSelection(editor);
if (editor->nSelectionType != stPosition &&
memcmp(&editor->pCursors[1], &editor->pCursors[3], sizeof(ME_Cursor)))
{
editor->pCursors[0] = editor->pCursors[2];
editor->pCursors[1] = editor->pCursors[3];
}
else if (y < editor->sely)
{
editor->pCursors[0] = tmp_cursor;
editor->pCursors[1] = editor->pCursors[2];
}
else
{
editor->pCursors[0] = tmp_cursor;
editor->pCursors[1] = editor->pCursors[3];
/* The scroll the cursor towards the other end, since it was the one
* extended by ME_ExtendAnchorSelection
*/
ME_Cursor tmpCursor = editor->pCursors[0];
editor->pCursors[0] = editor->pCursors[1];
editor->pCursors[1] = tmpCursor;
SendMessageW(editor->hWnd, EM_SCROLLCARET, 0, 0);
editor->pCursors[1] = editor->pCursors[0];
editor->pCursors[0] = tmpCursor;
} else {
SendMessageW(editor->hWnd, EM_SCROLLCARET, 0, 0);
}
ME_InvalidateSelection(editor);
HideCaret(editor->hWnd);
ME_MoveCaret(editor);
ME_InvalidateSelection(editor);
ShowCaret(editor->hWnd);
ME_ShowCaret(editor);
ME_SendSelChange(editor);
SendMessageW(editor->hWnd, EM_SCROLLCARET, 0, 0);
}
static ME_DisplayItem *ME_FindRunInRow(ME_TextEditor *editor, ME_DisplayItem *pRow,
@ -1048,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 */
@ -1062,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)
{
@ -1094,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;
@ -1106,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;
@ -1149,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;
@ -1159,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;

File diff suppressed because it is too large Load diff

View file

@ -84,7 +84,6 @@ ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass);
ME_DisplayItem *ME_MakeDI(ME_DIType type);
void ME_DestroyDisplayItem(ME_DisplayItem *item);
void ME_DestroyTableCellList(ME_DisplayItem *item);
void ME_DumpDocument(ME_TextBuffer *buffer);
const char *ME_GetDITypeName(ME_DIType type);
@ -176,14 +175,14 @@ void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod);
/* caret.c */
int ME_SetSelection(ME_TextEditor *editor, int from, int to);
void ME_SelectWord(ME_TextEditor *editor);
void ME_SelectByType(ME_TextEditor *editor, ME_SelectionType selectionType);
void ME_HideCaret(ME_TextEditor *ed);
void ME_ShowCaret(ME_TextEditor *ed);
void ME_MoveCaret(ME_TextEditor *ed);
int ME_CharFromPos(ME_TextEditor *editor, int x, int y);
int ME_CharFromPos(ME_TextEditor *editor, int x, int y, BOOL *isExact);
void ME_LButtonDown(ME_TextEditor *editor, int x, int y, int clickNum);
void ME_MouseMove(ME_TextEditor *editor, int x, int y);
void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars);
BOOL ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars);
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
const WCHAR *str, int len, ME_Style *style);
void ME_InsertEndRowFromCursor(ME_TextEditor *editor, int nCursor);
@ -199,8 +198,7 @@ BOOL ME_IsSelection(ME_TextEditor *editor);
void ME_DeleteSelection(ME_TextEditor *editor);
void ME_SendSelChange(ME_TextEditor *editor);
void ME_InsertOLEFromCursor(ME_TextEditor *editor, const REOBJECT* reo, int nCursor);
void ME_InsertTableCellFromCursor(ME_TextEditor *editor, int nCursor);
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars);
BOOL ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars, BOOL bForce);
int ME_GetTextLength(ME_TextEditor *editor);
int ME_GetTextLengthEx(ME_TextEditor *editor, const GETTEXTLENGTHEX *how);
ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor);
@ -219,18 +217,20 @@ 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_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp);
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);
void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048]);
void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFORMAT2 *pFmt);
void ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt);
BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFORMAT2 *pFmt);
BOOL ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt);
void ME_GetParaFormat(ME_TextEditor *editor, const ME_DisplayItem *para, PARAFORMAT2 *pFmt);
void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
/* marks from first up to (but not including) last */
void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, const ME_DisplayItem *last);
void ME_MarkForPainting(ME_TextEditor *editor, ME_DisplayItem *first, const ME_DisplayItem *last);
void ME_MarkAllForWrapping(ME_TextEditor *editor);
void ME_SetDefaultParaFormat(PARAFORMAT2 *pFmt);
/* paint.c */
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate);
@ -285,6 +285,21 @@ extern BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, int sel_min, int sel_max)
BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, int sel_min, int sel_max);
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);
struct RTFTable *ME_MakeTableDef(ME_TextEditor *editor);
void ME_InitTableDef(ME_TextEditor *editor, struct RTFTable *tableDef);
/* undo.c */
ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi);
void ME_CommitUndo(ME_TextEditor *editor);

View file

@ -67,27 +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 */
diUndoSetDefaultCharFormat, /* 18 */
diUndoPotentialEndTransaction, /* 19 - 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
@ -99,9 +99,9 @@ typedef enum {
/* 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_CELL 0x004
#define MERF_ENDCELL 0x004 /* v4.1 */
#define MERF_NONTEXT (MERF_GRAPHICS | MERF_TAB | MERF_CELL)
#define MERF_NONTEXT (MERF_GRAPHICS | MERF_TAB | MERF_ENDCELL)
/* run is splittable (contains white spaces in the middle or end) */
#define MERF_SPLITTABLE 0x001000
@ -121,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)
@ -133,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 *************************/
@ -149,7 +155,6 @@ typedef struct tagME_Run
int nFlags;
int nAscent, nDescent; /* pixels above/below baseline */
POINT pt; /* relative to para's position */
struct tagME_TableCell *pCell; /* for MERF_CELL: points to respective cell in ME_Paragraph */
REOBJECT *ole_obj; /* FIXME: should be a union with strText (at least) */
int nCR; int nLF; /* for MERF_ENDPARA: number of \r and \n characters encoded by run */
} ME_Run;
@ -160,28 +165,47 @@ typedef struct tagME_Document {
int last_wrapped_line;
} ME_Document;
typedef struct tagME_TableCell
typedef struct tagME_Border
{
int nRightBoundary;
struct tagME_TableCell *next;
} ME_TableCell;
int width;
COLORREF colorRef;
} ME_Border;
typedef struct tagME_BorderRect
{
ME_Border top;
ME_Border left;
ME_Border bottom;
ME_Border right;
} ME_BorderRect;
typedef struct tagME_Paragraph
{
PARAFORMAT2 *pFmt;
BOOL bTable; /* this paragraph is a table row */
struct tagME_TableCell *pCells; /* list of cells and their properties */
struct tagME_TableCell *pLastCell; /* points to the last cell in the list */
struct tagME_DisplayItem *pCell; /* v4.1 */
ME_BorderRect border;
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;
ME_BorderRect border;
POINT pt;
int nHeight, nWidth;
int yTextOffset; /* The text offset is caused by the largest top border. */
struct tagME_DisplayItem *prev_cell, *next_cell, *parent_cell;
} ME_Cell;
typedef struct tagME_Row
{
int nHeight;
@ -189,7 +213,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:
@ -211,6 +235,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 */
@ -244,6 +269,14 @@ typedef enum {
umAddBackToUndo
} ME_UndoMode;
typedef enum {
stPosition = 0,
stWord,
stLine,
stParagraph,
stDocument
} ME_SelectionType;
typedef struct tagME_FontTableItem {
BYTE bCharSet;
WCHAR *szFaceName;
@ -276,6 +309,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
@ -292,7 +328,6 @@ typedef struct tagME_TextEditor
{
HWND hWnd;
BOOL bEmulateVersion10;
BOOL bCaretShown;
ME_TextBuffer *pBuffer;
ME_Cursor *pCursors;
int nCursors;
@ -333,10 +368,14 @@ typedef struct tagME_TextEditor
BOOL bHaveFocus;
/*for IME */
int imeStartIndex;
DWORD selofs, linesel, sely;
DWORD selofs; /* The size of the selection bar on the left side of control */
ME_SelectionType nSelectionType;
/* Track previous notified selection */
CHARRANGE notified_cr;
/* Cache previously set vertical scrollbar info */
SCROLLINFO vert_si;
} ME_TextEditor;
typedef struct tagME_Context

View file

@ -112,36 +112,20 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item) {
/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */
if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) {
FREE_OBJ(item->member.para.pFmt);
ME_DestroyTableCellList(item);
}
if (item->type==diRun || item->type == diUndoInsertRun) {
if (item->member.run.ole_obj) ME_DeleteReObject(item->member.run.ole_obj);
ME_ReleaseStyle(item->member.run.style);
ME_DestroyString(item->member.run.strText);
}
if (item->type==diUndoSetCharFormat || item->type==diUndoSetDefaultCharFormat) {
if (item->type==diUndoSetCharFormat) {
ME_ReleaseStyle(item->member.ustyle);
}
if (item->type==diUndoSplitParagraph)
if (item->type==diUndoSplitParagraph) {
FREE_OBJ(item->member.para.pFmt);
FREE_OBJ(item);
}
void
ME_DestroyTableCellList(ME_DisplayItem *item)
{
if (item->member.para.pCells)
{
ME_TableCell *pCell = item->member.para.pCells;
ME_TableCell *pNext;
while (pCell) {
pNext = pCell->next;
FREE_OBJ(pCell);
pCell = pNext;
}
item->member.para.pCells = NULL;
FREE_OBJ(item->member.para.pCell);
}
FREE_OBJ(item);
}
ME_DisplayItem *ME_MakeDI(ME_DIType type) {
@ -151,8 +135,7 @@ ME_DisplayItem *ME_MakeDI(ME_DIType type) {
item->prev = item->next = NULL;
if (type == diParagraph || type == diUndoSplitParagraph) {
item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
item->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
item->member.para.pFmt->dwMask = 0;
ME_SetDefaultParaFormat(item->member.para.pFmt);
item->member.para.nFlags = MEPF_REWRAP;
}
@ -165,6 +148,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";
@ -176,7 +160,6 @@ const char *ME_GetDITypeName(ME_DIType type)
case diUndoDeleteRun: return "diUndoDeleteRun";
case diUndoJoinParagraphs: return "diJoinParagraphs";
case diUndoSplitParagraph: return "diSplitParagraph";
case diUndoSetDefaultCharFormat: return "diUndoSetDefaultCharFormat";
default: return "?";
}
}
@ -192,8 +175,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");

View file

@ -32,13 +32,35 @@ 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 yTextOffset = 0;
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 (!(item->member.para.nFlags & MEPF_ROWEND) &&
item->member.para.pCell != item->member.para.prev_para->member.para.pCell)
{
ME_DisplayItem *cell;
if (item->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART)
cell = item->member.para.pCell;
else
cell = item->member.para.prev_para->member.para.pCell;
assert(cell);
/* the border shifts the text down */
yTextOffset = cell->member.cell.yTextOffset;
ye += yTextOffset;
}
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
{
BOOL bPaint = (rcUpdate == NULL);
@ -46,12 +68,39 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
bPaint = c.pt.y<rcUpdate->bottom && ye>rcUpdate->top;
if (bPaint)
{
c.pt.y += yTextOffset;
ME_DrawParagraph(&c, item);
if (!rcUpdate || (rcUpdate->top<=c.pt.y && rcUpdate->bottom>=ye))
if (!rcUpdate || (rcUpdate->top<=c.pt.y-yTextOffset && rcUpdate->bottom>=ye))
item->member.para.nFlags &= ~MEPF_REPAINT;
}
}
c.pt.y = ye;
if (item->member.para.pCell)
{
ME_Cell *cell = &item->member.para.pCell->member.cell;
ME_DisplayItem *next_para = item->member.para.next_para;
c.pt.x = cell->pt.x + cell->nWidth;
if (item->member.para.pCell == next_para->member.para.pCell &&
!(next_para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)))
{
c.pt.y = ye;
} else {
if (next_para->member.para.nFlags & MEPF_ROWSTART)
{
cell = &ME_FindItemFwd(next_para, diCell)->member.cell;
}
else if (next_para->member.para.nFlags & MEPF_ROWEND)
{
cell = &cell->next_cell->member.cell;
}
else
{
cell = &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) {
@ -160,23 +209,77 @@ int ME_twips2pointsY(ME_Context *c, int y)
return y * c->dpi.cy * c->editor->nZoomNumerator / 1440 / c->editor->nZoomDenominator;
}
static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, int nChars,
ME_Style *s, int *width, int nSelFrom, int nSelTo, int ymin, int cy) {
static void ME_HighlightSpace(ME_Context *c, int x, int y, LPCWSTR szText,
int nChars, ME_Style *s, int width,
int nSelFrom, int nSelTo, int ymin, int cy)
{
HDC hDC = c->hDC;
HGDIOBJ hOldFont = NULL;
SIZE sz;
int selWidth;
/* Only highlight if there is a selection in the run and when
* EM_HIDESELECTION is not being used to hide the selection. */
if (nSelFrom >= nChars || nSelTo < 0 || nSelFrom >= nSelTo
|| c->editor->bHideSelection)
return;
hOldFont = ME_SelectStyleFont(c, s);
if (width <= 0)
{
GetTextExtentPoint32W(hDC, szText, nChars, &sz);
width = sz.cx;
}
if (nSelFrom < 0) nSelFrom = 0;
if (nSelTo > nChars) nSelTo = nChars;
GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
x += sz.cx;
if (nSelTo != nChars)
{
GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
selWidth = sz.cx;
} else {
selWidth = width - sz.cx;
}
ME_UnselectStyleFont(c, s, hOldFont);
if (c->editor->bEmulateVersion10)
PatBlt(hDC, x, ymin, selWidth, cy, DSTINVERT);
else
{
RECT rect;
HBRUSH hBrush;
rect.left = x;
rect.top = ymin;
rect.right = x + selWidth;
rect.bottom = ymin + cy;
hBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
FillRect(hDC, &rect, hBrush);
DeleteObject(hBrush);
}
}
static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText,
int nChars, ME_Style *s, int width,
int nSelFrom, int nSelTo, int ymin, int cy)
{
HDC hDC = c->hDC;
HGDIOBJ hOldFont;
COLORREF rgbOld;
int yOffset = 0, yTwipsOffset = 0;
SIZE sz;
COLORREF rgb;
HPEN hPen = NULL, hOldPen = NULL;
BOOL bHighlightedText = (nSelFrom < nChars && nSelTo >= 0
&& nSelFrom < nSelTo && !c->editor->bHideSelection);
int xSelStart = x, xSelEnd = x;
int *lpDx = NULL;
/* lpDx is only needed for tabs to make sure the underline done automatically
* by the font extends to the end of the tab. Tabs are always stored as
* a single character run, so we can handle this case separately, since
* otherwise lpDx would need to specify the lengths of each character. */
if (width && nChars == 1)
lpDx = &width; /* Make sure underline for tab extends across tab space */
hOldFont = ME_SelectStyleFont(c, s);
if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK))
rgb = RGB(0,0,255);
else if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
rgb = GetSysColor(COLOR_WINDOWTEXT);
else
rgb = s->fmt.crTextColor;
rgbOld = SetTextColor(hDC, rgb);
if ((s->fmt.dwMask & s->fmt.dwEffects) & CFM_OFFSET) {
yTwipsOffset = s->fmt.yOffset;
}
@ -186,12 +289,45 @@ static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, in
}
if (yTwipsOffset)
yOffset = ME_twips2pointsY(c, yTwipsOffset);
ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, NULL);
if ((s->fmt.dwMask & CFM_LINK) && (s->fmt.dwEffects & CFE_LINK))
rgb = RGB(0,0,255);
else if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
rgb = GetSysColor(COLOR_WINDOWTEXT);
else
rgb = s->fmt.crTextColor;
/* Determine the area that is selected in the run. */
GetTextExtentPoint32W(hDC, szText, nChars, &sz);
if (width) *width = sz.cx;
/* Treat width as an optional parameter. We can get the width from the
* text extent of the string if it isn't specified. */
if (!width) width = sz.cx;
if (bHighlightedText)
{
if (nSelFrom <= 0)
{
nSelFrom = 0;
}
else
{
GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
xSelStart = x + sz.cx;
}
if (nSelTo >= nChars)
{
nSelTo = nChars;
xSelEnd = x + width;
}
else
{
GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
xSelEnd = xSelStart + sz.cx;
}
}
/* Choose the pen type for underlining the text. */
if (s->fmt.dwMask & CFM_UNDERLINETYPE)
{
HPEN hPen;
switch (s->fmt.bUnderlineType)
{
case CFU_UNDERLINE:
@ -210,27 +346,67 @@ static void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, in
hPen = NULL;
break;
}
if (hPen != NULL)
if (hPen)
{
HPEN hOldPen = SelectObject(hDC, hPen);
/* FIXME: should use textmetrics info for Descent info */
MoveToEx(hDC, x, y - yOffset + 1, NULL);
LineTo(hDC, x + sz.cx, y - yOffset + 1);
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
hOldPen = SelectObject(hDC, hPen);
}
}
if (nSelFrom < nChars && nSelTo >= 0 && nSelFrom<nSelTo)
rgbOld = SetTextColor(hDC, rgb);
if (bHighlightedText && !c->editor->bEmulateVersion10)
{
if (nSelFrom < 0) nSelFrom = 0;
if (nSelTo > nChars) nSelTo = nChars;
GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
x += sz.cx;
GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
/* Invert selection if not hidden by EM_HIDESELECTION */
if (c->editor->bHideSelection == FALSE)
PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT);
COLORREF rgbBackOld;
RECT dim;
/* FIXME: should use textmetrics info for Descent info */
if (hPen)
MoveToEx(hDC, x, y - yOffset + 1, NULL);
if (xSelStart > x)
{
ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nSelFrom, NULL);
if (hPen)
LineTo(hDC, xSelStart, y - yOffset + 1);
}
dim.top = ymin;
dim.bottom = ymin + cy;
dim.left = xSelStart;
dim.right = xSelEnd;
SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
rgbBackOld = SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT));
ExtTextOutW(hDC, xSelStart, y-yOffset, ETO_OPAQUE, &dim,
szText+nSelFrom, nSelTo-nSelFrom, lpDx);
if (hPen)
LineTo(hDC, xSelEnd, y - yOffset + 1);
SetBkColor(hDC, rgbBackOld);
if (xSelEnd < x + width)
{
SetTextColor(hDC, rgb);
ExtTextOutW(hDC, xSelEnd, y-yOffset, 0, NULL, szText+nSelTo,
nChars-nSelTo, NULL);
if (hPen)
LineTo(hDC, x + width, y - yOffset + 1);
}
}
else
{
ExtTextOutW(hDC, x, y-yOffset, 0, NULL, szText, nChars, lpDx);
/* FIXME: should use textmetrics info for Descent info */
if (hPen)
{
MoveToEx(hDC, x, y - yOffset + 1, NULL);
LineTo(hDC, x + width, y - yOffset + 1);
}
if (bHighlightedText) /* v1.0 inverts the selection */
{
PatBlt(hDC, xSelStart, ymin, xSelEnd-xSelStart, cy, DSTINVERT);
}
}
if (hPen)
{
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
}
SetTextColor(hDC, rgbOld);
ME_UnselectStyleFont(c, s, hOldFont);
@ -261,14 +437,28 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
ME_GetSelection(c->editor, &nSelFrom, &nSelTo);
/* Draw selected end-of-paragraph mark */
if (run->nFlags & MERF_ENDPARA && runofs >= nSelFrom && runofs < nSelTo)
ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, NULL, 0, 1,
c->pt.y + start->member.row.nYPos,
start->member.row.nHeight);
/* you can always comment it out if you need visible paragraph marks */
if (run->nFlags & (MERF_ENDPARA | MERF_TAB | MERF_CELL))
if (run->nFlags & MERF_ENDPARA)
{
if (runofs >= nSelFrom && runofs < nSelTo)
{
ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1,
c->pt.y + start->member.row.pt.y,
start->member.row.nHeight);
}
return;
}
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.pt.y,
start->member.row.nHeight);
return;
}
if (run->nFlags & MERF_GRAPHICS)
ME_DrawOLE(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo));
@ -277,16 +467,16 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
if (c->editor->cPasswordMask)
{
ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,ME_StrVLen(run->strText));
ME_DrawTextWithStyle(c, x, y,
szMasked->szData, ME_StrVLen(szMasked), run->style, NULL,
nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight);
ME_DrawTextWithStyle(c, x, y,
szMasked->szData, ME_StrVLen(szMasked), run->style, run->nWidth,
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, NULL,
nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight);
}
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.pt.y, start->member.row.nHeight);
}
}
static const struct {unsigned width_num : 4, width_den : 4, pen_style : 4, dble : 1;} border_details[] = {
@ -375,14 +565,45 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
{
int idx, border_width, top_border, bottom_border;
RECT rc;
BOOL hasParaBorder;
SetRectEmpty(bounds);
if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return;
border_width = top_border = bottom_border = 0;
idx = (para->pFmt->wBorders >> 8) & 0xF;
if ((para->pFmt->dwMask & PFM_BORDER) && idx != 0 && (para->pFmt->wBorders & 0xF))
hasParaBorder = (!(c->editor->bEmulateVersion10 &&
para->pFmt->dwMask & PFM_TABLE &&
para->pFmt->wEffects & PFE_TABLE) &&
(para->pFmt->dwMask & PFM_BORDER) &&
idx != 0 &&
(para->pFmt->wBorders & 0xF));
if (hasParaBorder)
{
/* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit
* controls. It actually stores the paragraph or row border style. Although
* the value isn't used for drawing, it is used for streaming out rich text.
*
* wBorders stores the border style for each side (top, left, bottom, right)
* using nibble (4 bits) to store each border style. The rich text format
* control words, and their associated value are the following:
* \brdrdash 0
* \brdrdashsm 1
* \brdrdb 2
* \brdrdot 3
* \brdrhair 4
* \brdrs 5
* \brdrth 6
* \brdrtriple 7
*
* The order of the sides stored actually differs from v1.0 to 3.0 and v4.1.
* The mask corresponding to each side for the version are the following:
* mask v1.0-3.0 v4.1
* 0x000F top left
* 0x00F0 left top
* 0x0F00 bottom right
* 0xF000 right bottom
*/
if (para->pFmt->wBorders & 0x00B0)
FIXME("Unsupported border flags %x\n", para->pFmt->wBorders);
border_width = ME_GetParaBorderWidth(c->editor, para->pFmt->wBorders);
@ -410,7 +631,9 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
FillRect(c->hDC, &rc, c->editor->hbrBackground);
}
if ((para->pFmt->dwMask & PFM_BORDER) && idx != 0 && (para->pFmt->wBorders & 0xF)) {
/* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
* but might support it in later versions. */
if (hasParaBorder) {
int pen_width;
COLORREF pencr;
HPEN pen = NULL, oldpen = NULL;
@ -489,49 +712,253 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
}
}
static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
{
ME_Paragraph *para = &paragraph->member.para;
if (!c->editor->bEmulateVersion10) /* v4.1 */
{
if (para->pCell)
{
RECT rc;
ME_Cell *cell = &para->pCell->member.cell;
ME_DisplayItem *paraAfterRow;
HPEN pen, oldPen;
LOGBRUSH logBrush;
HBRUSH brush;
COLORREF color;
POINT oldPt;
int width;
BOOL atTop = (para->pCell != para->prev_para->member.para.pCell);
BOOL atBottom = (para->pCell != para->next_para->member.para.pCell);
int top = (atTop ? cell->pt.y : para->pt.y) - ME_GetYScrollPos(c->editor);
int bottom = (atBottom ?
cell->pt.y + cell->nHeight - ME_GetYScrollPos(c->editor):
top + para->nHeight + (atTop ? cell->yTextOffset : 0));
rc.left = cell->pt.x;
rc.right = rc.left + cell->nWidth;
if (atTop) {
/* Erase gap before text if not all borders are the same height. */
width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
rc.top = top + width;
width = cell->yTextOffset - width;
rc.bottom = rc.top + width;
if (width) {
FillRect(c->hDC, &rc, c->editor->hbrBackground);
}
}
/* Draw cell borders.
* The borders borders are draw in is left, top, bottom, right in order
* to be consistent with native richedit. This is noticeable from the
* overlap of borders of different colours. */
if (!(para->nFlags & MEPF_ROWEND)) {
rc.top = top;
rc.bottom = bottom;
if (cell->border.left.width > 0)
{
color = cell->border.left.colorRef;
width = max(ME_twips2pointsX(c, cell->border.left.width), 1);
} else {
color = RGB(192,192,192);
width = 1;
}
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = color;
logBrush.lbHatch = 0;
pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
width, &logBrush, 0, NULL);
oldPen = SelectObject(c->hDC, pen);
MoveToEx(c->hDC, rc.left, rc.top, &oldPt);
LineTo(c->hDC, rc.left, rc.bottom);
SelectObject(c->hDC, oldPen);
DeleteObject(pen);
MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
}
if (atTop) {
if (cell->border.top.width > 0)
{
brush = CreateSolidBrush(cell->border.top.colorRef);
width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
} else {
brush = GetStockObject(LTGRAY_BRUSH);
width = 1;
}
rc.top = top;
rc.bottom = rc.top + width;
FillRect(c->hDC, &rc, brush);
if (cell->border.top.width > 0)
DeleteObject(brush);
}
/* Draw the bottom border if at the last paragraph in the cell, and when
* in the last row of the table. */
if (atBottom) {
int oldLeft = rc.left;
width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1);
paraAfterRow = ME_GetTableRowEnd(paragraph)->member.para.next_para;
if (paraAfterRow->member.para.nFlags & MEPF_ROWSTART) {
ME_DisplayItem *nextEndCell;
nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell);
assert(nextEndCell && !nextEndCell->member.cell.next_cell);
rc.left = nextEndCell->member.cell.pt.x;
/* FIXME: Native draws FROM the bottom of the table rather than
* TO the bottom of the table in this case, but just doing so here
* will case the next row to erase the border. */
/*
rc.top = bottom;
rc.bottom = rc.top + width;
*/
}
if (rc.left < rc.right) {
if (cell->border.bottom.width > 0) {
brush = CreateSolidBrush(cell->border.bottom.colorRef);
} else {
brush = GetStockObject(LTGRAY_BRUSH);
}
rc.bottom = bottom;
rc.top = rc.bottom - width;
FillRect(c->hDC, &rc, brush);
if (cell->border.bottom.width > 0)
DeleteObject(brush);
}
rc.left = oldLeft;
}
/* Right border only drawn if at the end of the table row. */
if (!cell->next_cell->member.cell.next_cell &&
!(para->nFlags & MEPF_ROWSTART))
{
rc.top = top;
rc.bottom = bottom;
if (cell->border.right.width > 0) {
color = cell->border.right.colorRef;
width = max(ME_twips2pointsX(c, cell->border.right.width), 1);
} else {
color = RGB(192,192,192);
width = 1;
}
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = color;
logBrush.lbHatch = 0;
pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
width, &logBrush, 0, NULL);
oldPen = SelectObject(c->hDC, pen);
MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt);
LineTo(c->hDC, rc.right - 1, rc.bottom);
SelectObject(c->hDC, oldPen);
DeleteObject(pen);
MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
}
}
} else { /* v1.0 - 3.0 */
/* Draw simple table border */
if (para->pFmt->dwMask & PFM_TABLE && para->pFmt->wEffects & PFE_TABLE) {
HPEN pen = NULL, oldpen = NULL;
int i, firstX, startX, endX, rowY, rowBottom, nHeight;
POINT oldPt;
PARAFORMAT2 *pNextFmt;
pen = CreatePen(PS_SOLID, 0, para->border.top.colorRef);
oldpen = SelectObject(c->hDC, pen);
/* Find the start relative to the text */
firstX = ME_FindItemFwd(paragraph, diRun)->member.run.pt.x;
/* Go back by the horizontal gap, which is stored in dxOffset */
firstX -= ME_twips2pointsX(c, para->pFmt->dxOffset);
/* The left edge, stored in dxStartIndent affected just the first edge */
startX = firstX - ME_twips2pointsX(c, para->pFmt->dxStartIndent);
rowY = c->pt.y;
if (para->pFmt->dwMask & PFM_SPACEBEFORE)
rowY += ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
nHeight = ME_FindItemFwd(paragraph, diStartRow)->member.row.nHeight;
rowBottom = rowY + nHeight;
/* Draw horizontal lines */
MoveToEx(c->hDC, firstX, rowY, &oldPt);
i = para->pFmt->cTabCount - 1;
endX = startX + ME_twips2pointsX(c, para->pFmt->rgxTabs[i] & 0x00ffffff) + 1;
LineTo(c->hDC, endX, rowY);
pNextFmt = para->next_para->member.para.pFmt;
/* The bottom of the row only needs to be drawn if the next row is
* not a table. */
if (!(pNextFmt && pNextFmt->dwMask & PFM_TABLE && pNextFmt->wEffects &&
para->nRows == 1))
{
/* Decrement rowBottom to draw the bottom line within the row, and
* to not draw over this line when drawing the vertical lines. */
rowBottom--;
MoveToEx(c->hDC, firstX, rowBottom, NULL);
LineTo(c->hDC, endX, rowBottom);
}
/* Draw vertical lines */
MoveToEx(c->hDC, firstX, rowY, NULL);
LineTo(c->hDC, firstX, rowBottom);
for (i = 0; i < para->pFmt->cTabCount; i++)
{
int rightBoundary = para->pFmt->rgxTabs[i] & 0x00ffffff;
endX = startX + ME_twips2pointsX(c, rightBoundary);
MoveToEx(c->hDC, endX, rowY, NULL);
LineTo(c->hDC, endX, rowBottom);
}
MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
SelectObject(c->hDC, oldpen);
DeleteObject(pen);
}
}
}
void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
int align = SetTextAlign(c->hDC, TA_BASELINE);
ME_DisplayItem *p;
ME_Run *run;
ME_Paragraph *para = NULL;
RECT rc, rcPara, bounds;
RECT rc, bounds;
int y = c->pt.y;
int height = 0, baseline = 0, no=0, pno = 0;
int xs = 0, xe = 0;
int height = 0, baseline = 0, no=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;
}
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;
}
ME_DrawParaDecoration(c, para, y, &bounds);
y += bounds.top;
break;
case diStartRow:
/* 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)
@ -543,11 +970,9 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
pt.y = 12+y;
ME_DebugWrite(c->hDC, &pt, buf);
}
height = p->member.row.nHeight;
baseline = p->member.row.nBaseline;
if (!pno++)
xe += ME_twips2pointsX(c, para->pFmt->dxOffset);
break;
case diRun:
assert(para);
@ -578,11 +1003,27 @@ 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);
}
break;
default:
break;
}
no++;
}
ME_DrawTableBorders(c, paragraph);
SetTextAlign(c->hDC, align);
}
@ -641,6 +1082,7 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
ME_Repaint(editor);
}
editor->vert_si.nMax = 0;
ME_UpdateScrollBar(editor);
}
@ -663,6 +1105,14 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
bScrollBarWasVisible = ME_GetYScrollVisible(editor);
bScrollBarWillBeVisible = editor->nHeight > editor->sizeWindow.cy;
si.fMask = SIF_PAGE | SIF_RANGE;
if (GetWindowLongW(hWnd, GWL_STYLE) & ES_DISABLENOSCROLL)
si.fMask |= SIF_DISABLENOSCROLL;
if ((si.fMask & SIF_DISABLENOSCROLL))
{
bScrollBarWillBeVisible = TRUE;
}
if (bScrollBarWasVisible != bScrollBarWillBeVisible)
{
ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
@ -670,17 +1120,27 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
ME_WrapMarkedParagraphs(editor);
}
si.fMask = SIF_PAGE | SIF_RANGE;
if (GetWindowLongW(hWnd, GWL_STYLE) & ES_DISABLENOSCROLL)
si.fMask |= SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = editor->nTotalLength;
si.nPage = editor->sizeWindow.cy;
TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
if (!(si.nMin == editor->vert_si.nMin && si.nMax == editor->vert_si.nMax && si.nPage == editor->vert_si.nPage))
{
TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
editor->vert_si.nMin = si.nMin;
editor->vert_si.nMax = si.nMax;
editor->vert_si.nPage = si.nPage;
if (bScrollBarWillBeVisible)
{
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
else
{
if (bScrollBarWasVisible && !(si.fMask & SIF_DISABLENOSCROLL))
ShowScrollBar(hWnd, SB_VERT, FALSE);
}
}
}
int ME_GetYScrollPos(ME_TextEditor *editor)
@ -693,10 +1153,7 @@ int ME_GetYScrollPos(ME_TextEditor *editor)
BOOL ME_GetYScrollVisible(ME_TextEditor *editor)
{ /* Returns true if the scrollbar is visible */
SCROLLBARINFO sbi;
sbi.cbSize = sizeof(sbi);
GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
return ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0);
return (editor->vert_si.nMax - editor->vert_si.nMin >= max(editor->vert_si.nPage - 1, 0));
}
void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
@ -708,7 +1165,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;

View file

@ -28,7 +28,6 @@ static const WCHAR wszParagraphSign[] = {0xB6, 0};
void ME_MakeFirstParagraph(ME_TextEditor *editor)
{
ME_Context c;
PARAFORMAT2 fmt;
CHARFORMAT2W cf;
LOGFONTW lf;
HFONT hf;
@ -62,13 +61,6 @@ void ME_MakeFirstParagraph(ME_TextEditor *editor)
cf.bPitchAndFamily = lf.lfPitchAndFamily;
cf.bCharSet = lf.lfCharSet;
ZeroMemory(&fmt, sizeof(fmt));
fmt.cbSize = sizeof(fmt);
fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_TABSTOPS;
fmt.wAlignment = PFA_LEFT;
*para->member.para.pFmt = fmt;
style = ME_MakeStyle(&cf);
text->pDefaultStyle = style;
@ -112,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);
@ -147,40 +173,12 @@ 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;
new_para->member.para.border = run_para->member.para.border;
new_para->member.para.bTable = run_para->member.para.bTable;
/* Inherit previous cell definitions if any */
new_para->member.para.pCells = NULL;
if (run_para->member.para.pCells)
{
ME_TableCell *pCell, *pNewCell;
for (pCell = run_para->member.para.pCells; pCell; pCell = pCell->next)
{
pNewCell = ALLOC_OBJ(ME_TableCell);
pNewCell->nRightBoundary = pCell->nRightBoundary;
pNewCell->next = NULL;
if (new_para->member.para.pCells)
new_para->member.para.pLastCell->next = pNewCell;
else
new_para->member.para.pCells = pNewCell;
new_para->member.para.pLastCell = pNewCell;
}
}
/* fix paragraph properties. FIXME only needed when called from RTF reader */
if (run_para->member.para.pCells && !run_para->member.para.bTable)
{
/* Paragraph does not have an \intbl keyword, so any table definition
* stored is invalid */
ME_DestroyTableCellList(run_para);
}
/* insert paragraph into paragraph double linked list */
new_para->member.para.prev_para = run_para;
new_para->member.para.next_para = next_para;
@ -191,6 +189,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;
@ -204,7 +240,8 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME
/* join tp with tp->member.para.next_para, keeping tp's style; this
* is consistent with the original */
ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp)
ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp,
BOOL keepFirstParaFormat)
{
ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp;
int i, shift;
@ -232,14 +269,53 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp)
ME_InitCharFormat2W(&fmt);
ME_SetCharFormat(editor, pNext->member.para.nCharOfs - end_len, end_len, &fmt);
}
undo = ME_AddUndoItem(editor, diUndoSplitParagraph, NULL);
undo = ME_AddUndoItem(editor, diUndoSplitParagraph, pNext);
if (undo)
{
undo->nStart = pNext->member.para.nCharOfs - end_len;
undo->nCR = pRun->member.run.nCR;
undo->nLF = pRun->member.run.nLF;
assert(pNext->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
*undo->di.member.para.pFmt = *pNext->member.para.pFmt;
}
if (!keepFirstParaFormat)
{
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;
@ -355,7 +431,7 @@ void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
#undef DUMP_EFFECT
}
void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFORMAT2 *pFmt)
BOOL ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFORMAT2 *pFmt)
{
PARAFORMAT2 copy;
assert(sizeof(*para->member.para.pFmt) == sizeof(PARAFORMAT2));
@ -375,7 +451,8 @@ void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFOR
PFM_TABLE)
/* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
if (pFmt->dwMask & EFFECTS_MASK) {
para->member.para.pFmt->dwMask &= ~(pFmt->dwMask & EFFECTS_MASK);
para->member.para.pFmt->dwMask |= pFmt->dwMask & EFFECTS_MASK;
para->member.para.pFmt->wEffects &= ~HIWORD(pFmt->dwMask);
para->member.para.pFmt->wEffects |= pFmt->wEffects & HIWORD(pFmt->dwMask);
}
#undef EFFECTS_MASK
@ -411,6 +488,8 @@ void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, const PARAFOR
if (memcmp(&copy, para->member.para.pFmt, sizeof(PARAFORMAT2)))
para->member.para.nFlags |= MEPF_REWRAP;
return TRUE;
}
@ -438,7 +517,7 @@ ME_GetSelectionParas(ME_TextEditor *editor, ME_DisplayItem **para, ME_DisplayIte
}
void ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt)
BOOL ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt)
{
ME_DisplayItem *para, *para_end;
@ -450,6 +529,8 @@ void ME_SetSelectionParaFormat(ME_TextEditor *editor, const PARAFORMAT2 *pFmt)
break;
para = para->member.para.next_para;
} while(1);
return TRUE;
}
void ME_GetParaFormat(ME_TextEditor *editor, const ME_DisplayItem *para, PARAFORMAT2 *pFmt)
@ -472,6 +553,7 @@ void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
ME_GetParaFormat(editor, para, pFmt);
if (para == para_end) return;
/* Invalidate values that change across the selected paragraphs. */
do {
ZeroMemory(&tmp, sizeof(tmp));
tmp.cbSize = sizeof(tmp);
@ -480,8 +562,8 @@ void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
#define CHECK_FIELD(m, f) \
if (pFmt->f != tmp.f) pFmt->dwMask &= ~(m);
pFmt->dwMask &= ~((pFmt->wEffects ^ tmp.wEffects) << 16);
CHECK_FIELD(PFM_NUMBERING, wNumbering);
/* para->member.para.pFmt->wEffects = pFmt->wEffects; */
assert(tmp.dwMask & PFM_ALIGNMENT);
CHECK_FIELD(PFM_NUMBERING, wNumbering);
assert(tmp.dwMask & PFM_STARTINDENT);
@ -520,3 +602,13 @@ void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt)
para = para->member.para.next_para;
} while(1);
}
void ME_SetDefaultParaFormat(PARAFORMAT2 *pFmt)
{
ZeroMemory(pFmt, sizeof(PARAFORMAT2));
pFmt->cbSize = sizeof(PARAFORMAT2);
pFmt->dwMask = PFM_ALL2;
pFmt->wAlignment = PFA_LEFT;
pFmt->sStyle = -1;
pFmt->bOutlineLevel = TRUE;
}

View file

@ -169,6 +169,12 @@ RTFDestroy(RTF_Info *info)
}
RTFDestroyAttrs(info);
heap_free(info->cpOutputBuffer);
while (info->tableDef)
{
RTFTable *tableDef = info->tableDef;
info->tableDef = tableDef->parent;
heap_free(tableDef);
}
}
@ -235,6 +241,13 @@ void RTFInit(RTF_Info *info)
info->dwMaxCPOutputCount = 0x1000;
info->cpOutputBuffer = heap_alloc(info->dwMaxCPOutputCount);
}
if (info->tableDef)
ZeroMemory(info->tableDef, sizeof(info->tableDef));
info->tableDef = NULL;
info->nestingLevel = 0;
info->canInheritInTbl = FALSE;
info->borderType = 0;
}
/*
@ -1438,6 +1451,8 @@ static RTFKey rtfKey[] =
/* is this valid? */
{ rtfSpecialChar, rtfCurHeadPict, "chpict", 0 },
{ rtfSpecialChar, rtfUnicode, "u", 0 },
{ rtfSpecialChar, rtfNestCell, "nestcell", 0 },
{ rtfSpecialChar, rtfNestRow, "nestrow", 0 },
/*
* Character formatting attributes
@ -1601,6 +1616,7 @@ static RTFKey rtfKey[] =
{ rtfParAttr, rtfDarkDiagHatchBgPat, "bgdkdcross", 0 },
{ rtfParAttr, rtfBgPatLineColor, "cfpat", 0 },
{ rtfParAttr, rtfBgPatColor, "cbpat", 0 },
{ rtfParAttr, rtfNestLevel, "itap", 0 },
/*
* Section formatting attributes
@ -1903,6 +1919,8 @@ static RTFKey rtfKey[] =
{ rtfDestination, rtfIndexRange, "rxe", 0 },
{ rtfDestination, rtfTOC, "tc", 0 },
{ rtfDestination, rtfNeXTGraphic, "NeXTGraphic", 0 },
{ rtfDestination, rtfNestTableProps, "nesttableprops", 0 },
{ rtfDestination, rtfNoNestTables, "nonesttables", 0 },
/*
* Font families
@ -2605,7 +2623,6 @@ static void SpecialChar (RTF_Info *info)
break;
case rtfPage:
case rtfSect:
case rtfRow:
case rtfPar:
RTFPutUnicodeChar (info, '\r');
if (info->editor->bEmulateVersion10) RTFPutUnicodeChar (info, '\n');

View file

@ -22,6 +22,7 @@
<file>run.c</file>
<file>string.c</file>
<file>style.c</file>
<file>table.c</file>
<file>txtsrv.c</file>
<file>undo.c</file>
<file>wrap.c</file>

View file

@ -182,7 +182,9 @@
# define rtfTOC 72
# define rtfNeXTGraphic 73
# define rtfGenerator 74
# define rtfMaxDestination 75 /* highest dest + 1 */
# define rtfNestTableProps 75
# define rtfNoNestTables 76
# define rtfMaxDestination 77 /* highest dest + 1 */
# define rtfFontFamily 4
# define rtfFFNil 0
@ -261,6 +263,8 @@
# define rtfCurHeadPict 57 /* valid? */
/*# define rtfCurAnnot 58*/ /* apparently not used */
# define rtfUnicode 58 /* no better category*/
# define rtfNestCell 59
# define rtfNestRow 60
# define rtfStyleAttr 7
# define rtfAdditive 0 /* new in 1.10 */
@ -551,6 +555,7 @@
# define rtfDarkDiagHatchBgPat 101
# define rtfBgPatLineColor 102
# define rtfBgPatColor 103
# define rtfNestLevel 104
# define rtfCharAttr 12
# define rtfPlain 0
@ -945,7 +950,9 @@ typedef struct RTFFont RTFFont;
typedef struct RTFColor RTFColor;
typedef struct RTFStyle RTFStyle;
typedef struct RTFStyleElt RTFStyleElt;
typedef struct RTFBorder RTFBorder;
typedef struct RTFCell RTFCell;
typedef struct RTFTable RTFTable;
struct RTFFont
{
@ -1000,6 +1007,74 @@ struct RTFStyleElt
RTFStyleElt *rtfNextSE; /* next element in style */
};
struct RTFBorder
{
int width;
int color;
};
struct RTFCell
{
int rightBoundary;
RTFBorder border[4];
};
struct RTFTable
{
RTFCell cells[MAX_TABLE_CELLS];
int numCellsDefined;
int gapH, leftEdge;
/* borders for the table row */
RTFBorder border[6];
/* 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;
/* Table definitions are stored as a stack to support nested tables. */
RTFTable *parent;
};
# define RTFBorderTypeNone 0x00
# define RTFBorderTypePara 0x10 /* for \brdrX control words */
# define RTFBorderTypeRow 0x20 /* for \trbrdrX control words */
# define RTFBorderTypeCell 0x30 /* for \clbrdrX control words */
# define RTFBorderTypeMask 0xf0
/* The X in the control words \brdrX \trbrdrX and \clbrdrX mentioned above
* should be one of t, l, b, r which stand for top, left, bottom, right
* respectively. */
# define RTFBorderSideTop 0x00
# define RTFBorderSideLeft 0x01
# define RTFBorderSideBottom 0x02
# define RTFBorderSideRight 0x03
# define RTFBorderSideHorizontal 0x04
# define RTFBorderSideVertical 0x05
# define RTFBorderSideMask 0x0f
/* Here are the values from the border types and sides put together. */
# define RTFBorderParaTop 0x10
# define RTFBorderParaLeft 0x11
# define RTFBorderParaBottom 0x12
# define RTFBorderParaRight 0x13
# define RTFBorderRowTop 0x20
# define RTFBorderRowLeft 0x21
# define RTFBorderRowBottom 0x22
# define RTFBorderRowRight 0x23
# define RTFBorderRowHorizontal 0x24
# define RTFBorderRowVertical 0x25
# define RTFBorderCellTop 0x30
# define RTFBorderCellLeft 0x31
# define RTFBorderCellBottom 0x32
# define RTFBorderCellRight 0x33
/*
* Return pointer to new element of type t, or NULL
@ -1105,6 +1180,11 @@ struct _RTF_Info {
int stackTop;
BOOL styleChanged;
LPRICHEDITOLE lpRichEditOle;
RTFTable *tableDef;
int nestingLevel;
BOOL canInheritInTbl;
int borderType; /* value corresponds to the RTFBorder constants. */
};

View file

@ -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 | MERF_CELL))
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 | MERF_CELL))
if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
{
if (cx < run->nWidth/2)
return 0;
@ -667,13 +671,21 @@ static SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run
if (run->nFlags & MERF_TAB)
{
int pos = 0, i = 0, ppos;
int pos = 0, i = 0, ppos, shift = 0;
PARAFORMAT2 *pFmt = para->pFmt;
if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
/* The horizontal gap shifts the tab positions to leave the gap. */
shift = pFmt->dxOffset * 2;
do {
if (i < pFmt->cTabCount)
{
pos = pFmt->rgxTabs[i]&0x00FFFFFF;
/* Only one side of the horizontal gap is needed at the end of
* the table row. */
if (i == pFmt->cTabCount -1)
shift = shift >> 1;
pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF);
i++;
}
else
@ -697,11 +709,6 @@ static SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run
/* descent is unchanged */
return size;
}
if (run->nFlags & MERF_CELL)
{
size.cx = ME_twips2pointsX(c, run->pCell->nRightBoundary) - run->pt.x;
return size;
}
return size;
}
@ -832,17 +839,8 @@ void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W
void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
{
ME_Style *style;
ME_UndoItem *undo;
/* FIXME: Should this be removed? It breaks a test. */
assert(mod->cbSize == sizeof(CHARFORMAT2W));
undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL);
if (undo) {
undo->nStart = -1;
undo->nLen = -1;
undo->di.member.ustyle = editor->pBuffer->pDefaultStyle;
ME_AddRefStyle(undo->di.member.ustyle);
}
style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod);
editor->pBuffer->pDefaultStyle->fmt = style->fmt;
editor->pBuffer->pDefaultStyle->tm = style->tm;

View file

@ -197,7 +197,10 @@ ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style)
COPY_STYLE_ITEM(CFM_LCID, lcid);
COPY_STYLE_ITEM(CFM_OFFSET, yOffset);
COPY_STYLE_ITEM(CFM_REVAUTHOR, bRevAuthor);
COPY_STYLE_ITEM(CFM_SIZE, yHeight);
if (style->dwMask & CFM_SIZE) {
s->fmt.dwMask |= CFM_SIZE;
s->fmt.yHeight = min(style->yHeight, yHeightCharPtsMost * 20);
}
COPY_STYLE_ITEM(CFM_SPACING, sSpacing);
COPY_STYLE_ITEM(CFM_STYLE, sStyle);
COPY_STYLE_ITEM(CFM_UNDERLINETYPE, bUnderlineType);

View file

@ -0,0 +1,618 @@
/*
* RichEdit functions dealing with on tables
*
* Copyright 2008 by Dylan Smith
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* 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 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"
#include "rtf.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)
{
PARAFORMAT2 *pFmt;
if (!pItem)
return FALSE;
if (pItem->type == diRun)
pItem = ME_GetParagraph(pItem);
if (pItem->type != diParagraph)
return FALSE;
pFmt = pItem->member.para.pFmt;
return pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE;
}
/* Table rows should either be deleted completely or not at all. */
void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, int nOfs,int *nChars)
{
ME_Cursor c, c2;
ME_DisplayItem *this_para, *end_para;
ME_CursorFromCharOfs(editor, nOfs, &c);
this_para = ME_GetParagraph(c.pRun);
ME_CursorFromCharOfs(editor, nOfs + *nChars, &c2);
end_para = ME_GetParagraph(c2.pRun);
if (c2.pRun->member.run.nFlags & MERF_ENDPARA) {
/* End offset might be in the middle of the end paragraph run.
* If this is the case, then we need to use the next paragraph as the last
* paragraphs.
*/
int remaining = nOfs + *nChars - c2.pRun->member.run.nCharOfs
- end_para->member.para.nCharOfs;
if (remaining)
{
assert(remaining < c2.pRun->member.run.nCR + c2.pRun->member.run.nLF);
end_para = end_para->member.para.next_para;
}
}
if (!editor->bEmulateVersion10) { /* v4.1 */
if (this_para->member.para.pCell != end_para->member.para.pCell ||
((this_para->member.para.nFlags|end_para->member.para.nFlags)
& (MEPF_ROWSTART|MEPF_ROWEND)))
{
while (this_para != end_para)
{
ME_DisplayItem *next_para = this_para->member.para.next_para;
BOOL bTruancateDeletion = FALSE;
if (this_para->member.para.nFlags & MEPF_ROWSTART) {
/* The following while loop assumes that next_para is MEPF_ROWSTART,
* so moving back one paragraph let's it be processed as the start
* of the row. */
next_para = this_para;
this_para = this_para->member.para.prev_para;
} else if (next_para->member.para.pCell != this_para->member.para.pCell
|| this_para->member.para.nFlags & MEPF_ROWEND)
{
/* Start of the deletion from after the start of the table row. */
bTruancateDeletion = TRUE;
}
while (!bTruancateDeletion &&
next_para->member.para.nFlags & MEPF_ROWSTART)
{
next_para = ME_GetTableRowEnd(next_para)->member.para.next_para;
if (next_para->member.para.nCharOfs > nOfs + *nChars)
{
/* End of deletion is not past the end of the table row. */
next_para = this_para->member.para.next_para;
/* Delete the end paragraph preceding the table row if the
* preceding table row will be empty. */
if (this_para->member.para.nCharOfs >= nOfs)
{
next_para = next_para->member.para.next_para;
}
bTruancateDeletion = TRUE;
} else {
this_para = next_para->member.para.prev_para;
}
}
if (bTruancateDeletion)
{
ME_Run *end_run = &ME_FindItemBack(next_para, diRun)->member.run;
int nCharsNew = (next_para->member.para.nCharOfs - nOfs
- end_run->nCR - end_run->nLF);
nCharsNew = max(nCharsNew, 0);
assert(nCharsNew <= *nChars);
*nChars = nCharsNew;
break;
}
this_para = next_para;
}
}
} else { /* v1.0 - 3.0 */
ME_DisplayItem *pRun;
int nCharsToBoundary;
if (this_para->member.para.nCharOfs != nOfs &&
this_para->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)
{
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;
}
}
static ME_DisplayItem* ME_AppendTableRow(ME_TextEditor *editor,
ME_DisplayItem *table_row)
{
WCHAR endl = '\r', tab = '\t';
ME_DisplayItem *run;
PARAFORMAT2 *pFmt;
int i;
assert(table_row);
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;
}
}
/* Selects the next table cell or appends a new table row if at end of table */
static void ME_SelectOrInsertNextCell(ME_TextEditor *editor,
ME_DisplayItem *run)
{
ME_DisplayItem *para = ME_GetParagraph(run);
int i;
assert(run && run->type == diRun);
assert(ME_IsInTable(run));
if (!editor->bEmulateVersion10) { /* v4.1 */
ME_DisplayItem *cell;
/* Get the initial cell */
if (para->member.para.nFlags & MEPF_ROWSTART) {
cell = para->member.para.next_para->member.para.pCell;
} else if (para->member.para.nFlags & MEPF_ROWEND) {
cell = para->member.para.prev_para->member.para.pCell;
} else {
cell = para->member.para.pCell;
}
assert(cell);
/* Get the next cell. */
if (cell->member.cell.next_cell &&
cell->member.cell.next_cell->member.cell.next_cell)
{
cell = cell->member.cell.next_cell;
} else {
para = ME_GetTableRowEnd(ME_FindItemFwd(cell, diParagraph));
para = para->member.para.next_para;
assert(para);
if (para->member.para.nFlags & MEPF_ROWSTART) {
cell = para->member.para.next_para->member.para.pCell;
} else {
/* Insert row */
para = para->member.para.prev_para;
para = ME_AppendTableRow(editor, ME_GetTableRowStart(para));
/* Put cursor at the start of the new table row */
para = para->member.para.next_para;
editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
editor->pCursors[0].nOffset = 0;
editor->pCursors[1] = editor->pCursors[0];
ME_WrapMarkedParagraphs(editor);
return;
}
}
/* Select cell */
editor->pCursors[1].pRun = ME_FindItemFwd(cell, diRun);
editor->pCursors[1].nOffset = 0;
assert(editor->pCursors[0].pRun);
cell = cell->member.cell.next_cell;
editor->pCursors[0].pRun = ME_FindItemBack(cell, diRun);
editor->pCursors[0].nOffset = 0;
assert(editor->pCursors[1].pRun);
} else { /* v1.0 - 3.0 */
if (run->member.run.nFlags & MERF_ENDPARA &&
ME_IsInTable(ME_FindItemFwd(run, diParagraphOrEnd)))
{
run = ME_FindItemFwd(run, diRun);
assert(run);
}
for (i = 0; i < 2; i++)
{
while (!(run->member.run.nFlags & MERF_TAB))
{
run = ME_FindItemFwd(run, diRunOrParagraphOrEnd);
if (run->type != diRun)
{
para = run;
if (ME_IsInTable(para))
{
run = ME_FindItemFwd(para, diRun);
assert(run);
editor->pCursors[0].pRun = run;
editor->pCursors[0].nOffset = 0;
i = 1;
} else {
/* Insert table row */
para = ME_AppendTableRow(editor, para->member.para.prev_para);
/* Put cursor at the start of the new table row */
editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
editor->pCursors[0].nOffset = 0;
editor->pCursors[1] = editor->pCursors[0];
ME_WrapMarkedParagraphs(editor);
return;
}
}
}
if (i == 0)
run = ME_FindItemFwd(run, diRun);
editor->pCursors[i].pRun = run;
editor->pCursors[i].nOffset = 0;
}
}
}
void ME_TabPressedInTable(ME_TextEditor *editor, BOOL bSelectedRow)
{
/* FIXME: Shift tab should move to the previous cell. */
ME_Cursor fromCursor, toCursor;
ME_InvalidateSelection(editor);
{
int from, to;
from = ME_GetCursorOfs(editor, 0);
to = ME_GetCursorOfs(editor, 1);
if (from <= to)
{
fromCursor = editor->pCursors[0];
toCursor = editor->pCursors[1];
} else {
fromCursor = editor->pCursors[1];
toCursor = editor->pCursors[0];
}
}
if (!editor->bEmulateVersion10) /* v4.1 */
{
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)
{
ME_DisplayItem *run;
run = ME_FindItemBack(toCursor.pRun, diRunOrParagraphOrEnd);
if (run->type == diRun && run->member.run.nFlags & MERF_TAB)
ME_SelectOrInsertNextCell(editor, run);
else
ME_SelectOrInsertNextCell(editor, toCursor.pRun);
} else {
ME_SelectOrInsertNextCell(editor, toCursor.pRun);
}
}
}
ME_InvalidateSelection(editor);
ME_Repaint(editor);
HideCaret(editor->hWnd);
ME_ShowCaret(editor);
ME_SendSelChange(editor);
}
struct RTFTable *ME_MakeTableDef(ME_TextEditor *editor)
{
RTFTable *tableDef = ALLOC_OBJ(RTFTable);
ZeroMemory(tableDef, sizeof(RTFTable));
if (!editor->bEmulateVersion10) /* v4.1 */
tableDef->gapH = 10;
return tableDef;
}
void ME_InitTableDef(ME_TextEditor *editor, struct RTFTable *tableDef)
{
ZeroMemory(tableDef->cells, sizeof(tableDef->cells));
ZeroMemory(tableDef->border, sizeof(tableDef->border));
tableDef->numCellsDefined = 0;
tableDef->leftEdge = 0;
if (!editor->bEmulateVersion10) /* v4.1 */
tableDef->gapH = 10;
else /* v1.0 - 3.0 */
tableDef->gapH = 0;
}

View file

@ -84,17 +84,22 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_Disp
else pItem->member.run.ole_obj = NULL;
break;
case diUndoSetCharFormat:
case diUndoSetDefaultCharFormat:
break;
case diUndoDeleteRun:
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;
@ -283,8 +288,11 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
case diUndoSetParagraphFormat:
{
ME_Cursor tmp;
ME_DisplayItem *para;
ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt);
para = ME_FindItemBack(tmp.pRun, diParagraph);
ME_AddUndoItem(editor, diUndoSetParagraphFormat, para);
*para->member.para.pFmt = *pItem->member.para.pFmt;
break;
}
case diUndoSetCharFormat:
@ -292,11 +300,6 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt);
break;
}
case diUndoSetDefaultCharFormat:
{
ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt);
break;
}
case diUndoInsertRun:
{
ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem);
@ -304,7 +307,7 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
}
case diUndoDeleteRun:
{
ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen);
ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen, TRUE);
break;
}
case diUndoJoinParagraphs:
@ -312,22 +315,41 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
ME_Cursor tmp;
ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
/* the only thing that's needed is paragraph offset, so no need to split runs */
ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun));
ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun), TRUE);
break;
}
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:
@ -365,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);
@ -400,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;

View file

@ -42,17 +42,48 @@ 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)
{
PARAFORMAT2 *pFmt;
assert(para && para->type == diParagraph);
pFmt = para->member.para.pFmt;
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;
if (!cell->prev_cell)
{
int rowIndent = ME_GetTableRowEnd(para)->member.para.pFmt->dxStartIndent;
width -= rowIndent;
}
cell->nWidth = max(ME_twips2pointsX(wc->context, width), 0);
wc->nAvailWidth = cell->nWidth
- (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
} 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;
if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
/* Shift the text down because of the border. */
wc->pt.y++;
}
static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
@ -60,8 +91,10 @@ static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
ME_DisplayItem *p, *row, *para;
BOOL bSkippingSpaces = TRUE;
int ascent = 0, descent = 0, width=0, shift = 0, align = 0;
PARAFORMAT2 *pFmt;
/* wrap text */
para = ME_GetParagraph(wc->pRowStart);
pFmt = para->member.para.pFmt;
for (p = pEnd->prev; p!=wc->pRowStart->prev; p = p->prev)
{
@ -100,7 +133,16 @@ 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;
if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
{
/* The text was shifted down in ME_BeginRow so move the wrap context
* back to where it should be. */
wc->pt.y--;
/* The height of the row is increased by the borders. */
row->member.row.nHeight += 2;
}
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);
@ -117,14 +159,23 @@ 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);
wc->pt.y += row->member.row.nHeight;
ME_BeginRow(wc, para);
}
static void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p)
{
ME_DisplayItem *para = p->member.para.prev_para;
PARAFORMAT2 *pFmt = para->member.para.pFmt;
if (wc->pRowStart)
ME_InsertRowStart(wc, p);
if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
{
/* ME_BeginRow was called an extra time for the paragraph, and it shifts the
* text down by one pixel for the border, so fix up the wrap context. */
wc->pt.y--;
}
/*
p = p->member.para.prev_para->next;
@ -290,9 +341,13 @@ static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
if (wc->bOverflown) /* just skipping final whitespaces */
{
if (run->nFlags & (MERF_WHITESPACE|MERF_TAB)) {
/* End paragraph run can't overflow to the next line by itself. */
if (run->nFlags & MERF_ENDPARA)
return p->next;
if (run->nFlags & MERF_WHITESPACE) {
p->member.run.nFlags |= MERF_SKIPPED;
/* wc->pt.x += run->nWidth; */
wc->pt.x += run->nWidth;
/* skip runs consisting of only whitespaces */
return p->next;
}
@ -320,17 +375,11 @@ static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
ME_InsertRowStart(wc, p);
return p;
}
/* we're not at the end of the row */
if (run->nFlags & MERF_TAB) {
/* force recomputation of tabs' size as it depends on position */
ME_CalcRunExtent(wc->context, &ME_GetParagraph(p)->member.para,
wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run);
}
/* 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 */
@ -340,7 +389,12 @@ static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
/* TAB: we can split before */
if (run->nFlags & MERF_TAB) {
wc->bOverflown = TRUE;
return p;
if (wc->pRowStart == p)
/* Don't split before the start of the run, or we will get an
* endless loop. */
return p->next;
else
return p;
}
/* graphics: we can split before, if run's width is smaller than row's width */
if ((run->nFlags & MERF_GRAPHICS) && run->nWidth <= wc->nAvailWidth) {
@ -360,8 +414,20 @@ static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
pp = ME_SplitByBacktracking(wc, p, loc);
if (pp == wc->pRowStart)
{
/* we had only spaces so far, entire content can be omitted */
wc->pt.x = 0;
if (run->nFlags & MERF_STARTWHITE)
{
/* we had only spaces so far, so we must be on the first line of the
* paragraph, since no other lines of the paragraph start with spaces. */
assert(!wc->nRow);
/* The lines will only contain spaces, and the rest of the run will
* overflow onto the next line. */
wc->bOverflown = TRUE;
return p;
}
/* Couldn't split the first run, possible because we have a large font
* with a single character that caused an overflow.
*/
wc->pt.x += run->nWidth;
return p->next;
}
if (p != pp) /* found a suitable split point */
@ -392,46 +458,55 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp, DWORD begino
ME_WrapContext wc;
int border = 0;
int linespace = 0;
PARAFORMAT2 *pFmt;
assert(tp->type == diParagraph);
if (!(tp->member.para.nFlags & MEPF_REWRAP)) {
return;
}
ME_PrepareParagraphForWrapping(c, tp);
pFmt = tp->member.para.pFmt;
wc.context = c;
/* wc.para_style = tp->member.para.style; */
wc.style = NULL;
wc.nFirstMargin = ME_twips2pointsX(c, tp->member.para.pFmt->dxStartIndent) + beginofs;
wc.nLeftMargin = wc.nFirstMargin + ME_twips2pointsX(c, tp->member.para.pFmt->dxOffset);
wc.nRightMargin = ME_twips2pointsX(c, tp->member.para.pFmt->dxRightIndent);
if (tp->member.para.nFlags & MEPF_ROWEND) {
wc.nFirstMargin = wc.nLeftMargin = wc.nRightMargin = 0;
} else {
int dxStartIndent = pFmt->dxStartIndent;
if (tp->member.para.pCell) {
dxStartIndent += ME_GetTableRowEnd(tp)->member.para.pFmt->dxOffset;
}
wc.nFirstMargin = ME_twips2pointsX(c, dxStartIndent);
wc.nLeftMargin = wc.nFirstMargin + ME_twips2pointsX(c, pFmt->dxOffset);
wc.nRightMargin = ME_twips2pointsX(c, pFmt->dxRightIndent);
}
if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
{
wc.nFirstMargin += ME_twips2pointsX(c, pFmt->dxOffset * 2);
}
wc.nRow = 0;
wc.pt.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);
if (tp->member.para.pFmt->dwMask & PFM_BORDER)
if (pFmt->dwMask & PFM_SPACEBEFORE)
wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceBefore);
if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) &&
pFmt->dwMask & PFM_BORDER)
{
border = ME_GetParaBorderWidth(c->editor, tp->member.para.pFmt->wBorders);
if (tp->member.para.pFmt->wBorders & 1) {
if (pFmt->wBorders & 1) {
wc.nFirstMargin += border;
wc.nLeftMargin += border;
}
if (tp->member.para.pFmt->wBorders & 2)
if (pFmt->wBorders & 2)
wc.nRightMargin -= border;
if (tp->member.para.pFmt->wBorders & 4)
if (pFmt->wBorders & 4)
wc.pt.y += border;
}
if (c->editor->bWordWrap)
wc.nAvailWidth = c->rcView.right - c->rcView.left - wc.nFirstMargin - wc.nRightMargin;
else
wc.nAvailWidth = ~0u >> 1;
wc.pRowStart = NULL;
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) {
@ -442,10 +517,11 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp, DWORD begino
wc.pt.y += linespace;
}
ME_WrapEndParagraph(&wc, p);
if ((tp->member.para.pFmt->dwMask & PFM_BORDER) && (tp->member.para.pFmt->wBorders & 8))
if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) &&
(pFmt->dwMask & PFM_BORDER) && (pFmt->wBorders & 8))
wc.pt.y += border;
if (tp->member.para.pFmt->dwMask & PFM_SPACEAFTER)
wc.pt.y += ME_twips2pointsY(c, tp->member.para.pFmt->dySpaceAfter);
wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceAfter);
tp->member.para.nFlags &= ~MEPF_REWRAP;
tp->member.para.nHeight = wc.pt.y;
@ -500,17 +576,18 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
int yLastPos = 0;
ME_InitContext(&c, editor, GetDC(editor->hWnd));
c.pt.x = editor->selofs;
editor->nHeight = 0;
item = editor->pBuffer->pFirst->next;
while(item != editor->pBuffer->pLast) {
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);
@ -523,24 +600,129 @@ 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);
ME_DisplayItem *endRowPara;
int borderWidth = 0;
cell->member.cell.pt = c.pt;
/* Offset the text by the largest top border width. */
while (cell->member.cell.next_cell) {
borderWidth = max(borderWidth, cell->member.cell.border.top.width);
cell = cell->member.cell.next_cell;
}
endRowPara = ME_FindItemFwd(cell, diParagraph);
assert(endRowPara->member.para.nFlags & MEPF_ROWEND);
if (borderWidth > 0)
{
borderWidth = max(ME_twips2pointsY(&c, borderWidth), 1);
while (cell) {
cell->member.cell.yTextOffset = borderWidth;
cell = cell->member.cell.prev_cell;
}
c.pt.y += borderWidth;
}
if (endRowPara->member.para.pFmt->dxStartIndent > 0)
{
int dxStartIndent = endRowPara->member.para.pFmt->dxStartIndent;
cell = ME_FindItemFwd(item, diCell);
cell->member.cell.pt.x += ME_twips2pointsX(&c, dxStartIndent);
c.pt.x = cell->member.cell.pt.x;
}
}
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, bottomBorder = 0;
ME_DisplayItem *cell = ME_FindItemBack(item, diCell);
if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART))
{
/* Last row, the bottom border is added to the height. */
cell = cell->member.cell.prev_cell;
while (cell)
{
bottomBorder = max(bottomBorder, cell->member.cell.border.bottom.width);
cell = cell->member.cell.prev_cell;
}
bottomBorder = ME_twips2pointsY(&c, bottomBorder);
cell = ME_FindItemBack(item, diCell);
}
prevHeight = cell->member.cell.nHeight;
nHeight = cell->member.cell.prev_cell->member.cell.nHeight + bottomBorder;
cell->member.cell.nHeight = nHeight;
item->member.para.nHeight = nHeight;
cell = cell->member.cell.prev_cell;
cell->member.cell.nHeight = nHeight;
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;
c.pt.y += cell->yTextOffset;
}
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 = editor->selofs;
}
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)
@ -560,8 +742,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;

View file

@ -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;
}
@ -283,35 +284,64 @@ ME_StreamOutRTFFontAndColorTbl(ME_OutStream *pStream, ME_DisplayItem *pFirstRun,
return TRUE;
}
static BOOL
ME_StreamOutRTFTableProps(ME_TextEditor *editor, ME_OutStream *pStream,
const ME_DisplayItem *para)
{
ME_DisplayItem *cell;
char props[STREAMOUT_BUFFER_SIZE] = "";
if (!ME_StreamOutPrint(pStream, "\\trowd"))
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 + strlen(props), "\\cellx%d", cell->member.cell.nRightBoundary);
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)));
if (pFmt->dxOffset)
sprintf(props + strlen(props), "\\trgaph%d", pFmt->dxOffset);
if (pFmt->dxStartIndent)
sprintf(props + strlen(props), "\\trleft%d", pFmt->dxStartIndent);
for (i = 0; i < pFmt->cTabCount; i++)
{
sprintf(props + strlen(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] = "";
int i;
if (para->member.para.pCells)
{
ME_TableCell *cell = para->member.para.pCells;
if (!ME_StreamOutPrint(pStream, "\\trowd"))
return FALSE;
do {
sprintf(props, "\\cellx%d", cell->nRightBoundary);
if (!ME_StreamOutPrint(pStream, props))
return FALSE;
cell = cell->next;
} while (cell);
props[0] = '\0';
}
/* TODO: Don't emit anything if the last PARAFORMAT2 is inherited */
if (!ME_StreamOutPrint(pStream, "\\pard"))
return FALSE;
if (para->member.para.bTable)
strcat(props, "\\intbl");
if (!editor->bEmulateVersion10) { /* v4.1 */
if (pStream->nNestingLevel > 0)
strcat(props, "\\intbl");
if (pStream->nNestingLevel > 1)
sprintf(props + strlen(props), "\\itap%d", pStream->nNestingLevel);
} 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
@ -376,46 +406,46 @@ ME_StreamOutRTFParaProps(ME_OutStream *pStream, const ME_DisplayItem *para)
strcat(props, "\\rtlpar");
if (fmt->dwMask & PFM_SIDEBYSIDE && fmt->wEffects & PFE_SIDEBYSIDE)
strcat(props, "\\sbys");
if (fmt->dwMask & PFM_TABLE && fmt->dwMask & PFE_TABLE)
strcat(props, "\\intbl");
if (fmt->dwMask & PFM_OFFSET)
sprintf(props + strlen(props), "\\li%d", fmt->dxOffset);
if (fmt->dwMask & PFM_OFFSETINDENT || fmt->dwMask & PFM_STARTINDENT)
sprintf(props + strlen(props), "\\fi%d", fmt->dxStartIndent);
if (fmt->dwMask & PFM_RIGHTINDENT)
sprintf(props + strlen(props), "\\ri%d", fmt->dxRightIndent);
if (!(editor->bEmulateVersion10 && /* v1.0 - 3.0 */
fmt->dwMask & PFM_TABLE && fmt->wEffects & PFE_TABLE))
{
if (fmt->dwMask & PFM_OFFSET)
sprintf(props + strlen(props), "\\li%d", fmt->dxOffset);
if (fmt->dwMask & PFM_OFFSETINDENT || fmt->dwMask & PFM_STARTINDENT)
sprintf(props + strlen(props), "\\fi%d", fmt->dxStartIndent);
if (fmt->dwMask & PFM_RIGHTINDENT)
sprintf(props + strlen(props), "\\ri%d", fmt->dxRightIndent);
if (fmt->dwMask & PFM_TABSTOPS) {
static const char * const leader[6] = { "", "\\tldot", "\\tlhyph", "\\tlul", "\\tlth", "\\tleq" };
for (i = 0; i < fmt->cTabCount; i++) {
switch ((fmt->rgxTabs[i] >> 24) & 0xF) {
case 1:
strcat(props, "\\tqc");
break;
case 2:
strcat(props, "\\tqr");
break;
case 3:
strcat(props, "\\tqdec");
break;
case 4:
/* Word bar tab (vertical bar). Handled below */
break;
}
if (fmt->rgxTabs[i] >> 28 <= 5)
strcat(props, leader[fmt->rgxTabs[i] >> 28]);
sprintf(props+strlen(props), "\\tx%d", fmt->rgxTabs[i]&0x00FFFFFF);
}
}
}
if (fmt->dwMask & PFM_SPACEAFTER)
sprintf(props + strlen(props), "\\sa%d", fmt->dySpaceAfter);
if (fmt->dwMask & PFM_SPACEBEFORE)
sprintf(props + strlen(props), "\\sb%d", fmt->dySpaceBefore);
if (fmt->dwMask & PFM_STYLE)
sprintf(props + strlen(props), "\\s%d", fmt->sStyle);
if (fmt->dwMask & PFM_TABSTOPS) {
static const char * const leader[6] = { "", "\\tldot", "\\tlhyph", "\\tlul", "\\tlth", "\\tleq" };
for (i = 0; i < fmt->cTabCount; i++) {
switch ((fmt->rgxTabs[i] >> 24) & 0xF) {
case 1:
strcat(props, "\\tqc");
break;
case 2:
strcat(props, "\\tqr");
break;
case 3:
strcat(props, "\\tqdec");
break;
case 4:
/* Word bar tab (vertical bar). Handled below */
break;
}
if (fmt->rgxTabs[i] >> 28 <= 5)
strcat(props, leader[fmt->rgxTabs[i] >> 28]);
sprintf(props+strlen(props), "\\tx%d", fmt->rgxTabs[i]&0x00FFFFFF);
}
}
if (fmt->dwMask & PFM_SHADING) {
static const char * const style[16] = { "", "\\bgdkhoriz", "\\bgdkvert", "\\bgdkfdiag",
@ -680,7 +710,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)
@ -688,8 +718,39 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC
switch(p->type)
{
case diParagraph:
if (!ME_StreamOutRTFParaProps(pStream, p))
return FALSE;
if (!editor->bEmulateVersion10) { /* v4.1 */
if (p->member.para.nFlags & MEPF_ROWSTART) {
pStream->nNestingLevel++;
if (pStream->nNestingLevel == 1) {
if (!ME_StreamOutRTFTableProps(editor, pStream, p))
return FALSE;
}
} else if (p->member.para.nFlags & MEPF_ROWEND) {
pStream->nNestingLevel--;
if (pStream->nNestingLevel > 1) {
if (!ME_StreamOutPrint(pStream, "{\\*\\nesttableprops"))
return FALSE;
if (!ME_StreamOutRTFTableProps(editor, pStream, p))
return FALSE;
if (!ME_StreamOutPrint(pStream, "\\nestrow}{\\nonesttables\\par}\r\n"))
return FALSE;
} else {
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;
}
pPara = p;
break;
case diRun:
@ -697,14 +758,35 @@ 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 (p->member.run.nFlags & MERF_GRAPHICS)
if (pPara->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND))
break;
if (p->member.run.nFlags & MERF_GRAPHICS) {
FIXME("embedded objects are not handled\n");
if (p->member.run.nFlags & MERF_CELL) {
if (!ME_StreamOutPrint(pStream, "\\cell "))
return FALSE;
} else if (p->member.run.nFlags & MERF_TAB) {
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 "))
return FALSE;
} else {
if (!ME_StreamOutPrint(pStream, "\\tab "))
return FALSE;
}
} else if (p->member.run.nFlags & MERF_ENDCELL) {
if (pStream->nNestingLevel > 1) {
if (!ME_StreamOutPrint(pStream, "\\nestcell "))
return FALSE;
} else {
if (!ME_StreamOutPrint(pStream, "\\cell "))
return FALSE;
}
nChars--;
} else if (p->member.run.nFlags & MERF_ENDPARA) {
if (pPara->member.para.bTable) {
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;
} else {

View file

@ -23,6 +23,8 @@ typedef struct tagCWPSTRUCT *LPCWPSTRUCT;
#define WM_ALTTABACTIVE 0x0029
#define UNICODE_NOCHAR 0xFFFF
#ifndef E_PROP_ID_UNSUPPORTED
#define E_PROP_ID_UNSUPPORTED ((HRESULT)0x80070490)
#endif