mirror of
https://github.com/reactos/reactos.git
synced 2025-06-04 17:00:31 +00:00
Synced riched20 with Wine HEAD
svn path=/trunk/; revision=35728
This commit is contained in:
parent
dea8ec1229
commit
f381a715d2
17 changed files with 3050 additions and 586 deletions
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 = ¶graph->member.para;
|
||||
if (!c->editor->bEmulateVersion10) /* v4.1 */
|
||||
{
|
||||
if (para->pCell)
|
||||
{
|
||||
RECT rc;
|
||||
ME_Cell *cell = ¶->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 = ¶->pCell->member.cell;
|
||||
rc.left = cell->pt.x;
|
||||
rc.right = rc.left + cell->nWidth;
|
||||
}
|
||||
if (para->nFlags & MEPF_ROWSTART) {
|
||||
ME_Cell *cell = ¶->next_para->member.para.pCell->member.cell;
|
||||
rc.right = cell->pt.x;
|
||||
} else if (para->nFlags & MEPF_ROWEND) {
|
||||
ME_Cell *cell = ¶->prev_para->member.para.pCell->member.cell;
|
||||
rc.left = cell->pt.x + cell->nWidth;
|
||||
}
|
||||
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 = ¶->next_para->member.para.pCell->member.cell;
|
||||
rc.bottom = y + cell->nHeight;
|
||||
} else if (para->nFlags & MEPF_ROWEND) {
|
||||
ME_Cell *cell = ¶->prev_para->member.para.pCell->member.cell;
|
||||
rc.bottom = y + cell->nHeight;
|
||||
} else {
|
||||
rc.bottom = y+p->member.row.nHeight;
|
||||
FillRect(c->hDC, &rc, c->editor->hbrBackground);
|
||||
/* right margin */
|
||||
rc.left = xe;
|
||||
rc.right = c->rcView.right - bounds.right;
|
||||
FillRect(c->hDC, &rc, c->editor->hbrBackground);
|
||||
rc.left = xs;
|
||||
rc.right = xe;
|
||||
}
|
||||
visible = RectVisible(c->hDC, &rc);
|
||||
if (visible) {
|
||||
FillRect(c->hDC, &rc, c->editor->hbrBackground);
|
||||
}
|
||||
if (me_debug)
|
||||
|
@ -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;
|
||||
|
|
|
@ -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(©, 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;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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. */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
618
reactos/dll/win32/riched20/table.c
Normal file
618
reactos/dll/win32/riched20/table.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 = ¶->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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue