diff --git a/reactos/dll/win32/riched20/caret.c b/reactos/dll/win32/riched20/caret.c index c23d92c01c0..376318e7309 100644 --- a/reactos/dll/win32/riched20/caret.c +++ b/reactos/dll/win32/riched20/caret.c @@ -215,7 +215,7 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, *height = pSizeRun->member.run.nAscent + pSizeRun->member.run.nDescent; *x = run->member.run.pt.x + sz.cx; - *y = para->member.para.nYPos + row->member.row.nBaseline + run->member.run.pt.y - pSizeRun->member.run.nAscent - ME_GetYScrollPos(editor); + *y = para->member.para.pt.y + row->member.row.nBaseline + run->member.run.pt.y - pSizeRun->member.run.nAscent - ME_GetYScrollPos(editor); ME_DestroyContext(&c, editor->hWnd); return; } @@ -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 && nCursornCursors); /* 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; diff --git a/reactos/dll/win32/riched20/editor.c b/reactos/dll/win32/riched20/editor.c index 2d000616e36..aa5938326a3 100644 --- a/reactos/dll/win32/riched20/editor.c +++ b/reactos/dll/win32/riched20/editor.c @@ -140,10 +140,10 @@ + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode - WM_STYLECHANGING - WM_STYLECHANGED (things like read-only flag) - - WM_UNICHAR - + + WM_UNICHAR + Notifications - + * EN_CHANGE (sent from the wrong place) - EN_CORRECTTEXT - EN_DROPFILES @@ -320,6 +320,32 @@ static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStrea return 0; } +static void ME_ApplyBorderProperties(RTF_Info *info, + ME_BorderRect *borderRect, + RTFBorder *borderDef) +{ + int i, colorNum; + ME_Border *pBorders[] = {&borderRect->top, + &borderRect->left, + &borderRect->bottom, + &borderRect->right}; + for (i = 0; i < 4; i++) + { + RTFColor *colorDef = info->colorList; + pBorders[i]->width = borderDef[i].width; + colorNum = borderDef[i].color; + while (colorDef && colorDef->rtfCNum != colorNum) + colorDef = colorDef->rtfNextColor; + if (colorDef) + pBorders[i]->colorRef = RGB( + colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0, + colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0, + colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0); + else + pBorders[i]->colorRef = RGB(0, 0, 0); + } +} + static void ME_RTFCharAttrHook(RTF_Info *info) { CHARFORMAT2W fmt; @@ -451,8 +477,13 @@ static void ME_RTFParAttrHook(RTF_Info *info) switch(info->rtfMinor) { case rtfParDef: /* restores default paragraph attributes */ - fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS | PFM_OFFSET | - PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE | PFM_STARTINDENT; + if (!info->editor->bEmulateVersion10) /* v4.1 */ + info->borderType = RTFBorderParaLeft; + else /* v1.0 - 3.0 */ + info->borderType = RTFBorderParaTop; + fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS | + PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE | + PFM_STARTINDENT; /* TODO: numbering, shading */ fmt.wAlignment = PFA_LEFT; fmt.cTabCount = 0; @@ -462,31 +493,113 @@ static void ME_RTFParAttrHook(RTF_Info *info) fmt.bLineSpacingRule = 0; fmt.dySpaceBefore = fmt.dySpaceAfter = 0; fmt.dyLineSpacing = 0; - RTFFlushOutputBuffer(info); - ME_GetParagraph(info->editor->pCursors[0].pRun)->member.para.bTable = FALSE; + if (!info->editor->bEmulateVersion10) /* v4.1 */ + { + if (info->tableDef && info->tableDef->tableRowStart && + info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_Cursor cursor; + ME_DisplayItem *para; + /* We are just after a table row. */ + RTFFlushOutputBuffer(info); + cursor = info->editor->pCursors[0]; + para = ME_GetParagraph(cursor.pRun); + if (para == info->tableDef->tableRowStart->member.para.next_para + && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs) + { + /* Since the table row end, no text has been inserted, and the \intbl + * control word has not be used. We can confirm that we are not in a + * table anymore. + */ + info->tableDef->tableRowStart = NULL; + info->canInheritInTbl = FALSE; + } + } + } else { /* v1.0 - v3.0 */ + fmt.dwMask |= PFM_TABLE; + fmt.wEffects &= ~PFE_TABLE; + } + break; + case rtfNestLevel: + if (!info->editor->bEmulateVersion10) /* v4.1 */ + { + while (info->rtfParam > info->nestingLevel) { + RTFTable *tableDef = ALLOC_OBJ(RTFTable); + ZeroMemory(tableDef, sizeof(RTFTable)); + tableDef->parent = info->tableDef; + info->tableDef = tableDef; + + RTFFlushOutputBuffer(info); + if (tableDef->tableRowStart && + tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_DisplayItem *para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + } else { + ME_Cursor cursor; + WCHAR endl = '\r'; + cursor = info->editor->pCursors[0]; + if (cursor.nOffset || cursor.pRun->member.run.nCharOfs) + ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); + tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor); + } + + info->nestingLevel++; + } + info->canInheritInTbl = FALSE; + } break; case rtfInTable: { - ME_DisplayItem *para; - - RTFFlushOutputBuffer(info); - para = ME_GetParagraph(info->editor->pCursors[0].pRun); - assert(para->member.para.pCells); - para->member.para.bTable = TRUE; - return; + if (!info->editor->bEmulateVersion10) /* v4.1 */ + { + if (info->nestingLevel < 1) + { + RTFTable *tableDef; + if (!info->tableDef) + { + info->tableDef = ALLOC_OBJ(RTFTable); + ZeroMemory(info->tableDef, sizeof(RTFTable)); + } + tableDef = info->tableDef; + RTFFlushOutputBuffer(info); + if (tableDef->tableRowStart && + tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_DisplayItem *para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + } else { + ME_Cursor cursor; + WCHAR endl = '\r'; + cursor = info->editor->pCursors[0]; + if (cursor.nOffset || cursor.pRun->member.run.nCharOfs) + ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); + tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor); + } + info->nestingLevel = 1; + info->canInheritInTbl = TRUE; + } + return; + } else { /* v1.0 - v3.0 */ + fmt.dwMask |= PFM_TABLE; + fmt.wEffects |= PFE_TABLE; + } + break; } case rtfFirstIndent: ME_GetSelectionParaFormat(info->editor, &fmt); - fmt.dwMask |= PFM_STARTINDENT | PFM_OFFSET; - fmt.dxStartIndent += info->rtfParam; + fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET; + fmt.dxStartIndent += fmt.dxOffset + info->rtfParam; fmt.dxOffset = -info->rtfParam; break; case rtfLeftIndent: - /* we assume rtfLeftIndent is always specified before rtfFirstIndent */ ME_GetSelectionParaFormat(info->editor, &fmt); - fmt.dwMask |= PFM_STARTINDENT; - fmt.dxStartIndent = info->rtfParam; - fmt.dxOffset = 0; + fmt.dwMask = PFM_STARTINDENT; + fmt.dxStartIndent = info->rtfParam - fmt.dxOffset; break; case rtfRightIndent: fmt.dwMask = PFM_RIGHTINDENT; @@ -509,11 +622,11 @@ static void ME_RTFParAttrHook(RTF_Info *info) ME_GetSelectionParaFormat(info->editor, &fmt); if (!(fmt.dwMask & PFM_TABSTOPS)) { - fmt.dwMask |= PFM_TABSTOPS; fmt.cTabCount = 0; } - if (fmt.cTabCount < MAX_TAB_STOPS) + if (fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000) fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam; + fmt.dwMask = PFM_TABSTOPS; break; case rtfKeep: fmt.dwMask = PFM_KEEP; @@ -573,90 +686,138 @@ static void ME_RTFParAttrHook(RTF_Info *info) fmt.wNumberingStart = info->rtfParam; break; case rtfBorderLeft: + info->borderType = RTFBorderParaLeft; ME_GetSelectionParaFormat(info->editor, &fmt); if (!(fmt.dwMask & PFM_BORDER)) { - fmt.dwMask |= PFM_BORDER; fmt.wBorderSpace = 0; fmt.wBorderWidth = 1; fmt.wBorders = 0; } fmt.wBorders |= 1; + fmt.dwMask = PFM_BORDER; break; case rtfBorderRight: + info->borderType = RTFBorderParaRight; ME_GetSelectionParaFormat(info->editor, &fmt); if (!(fmt.dwMask & PFM_BORDER)) { - fmt.dwMask |= PFM_BORDER; fmt.wBorderSpace = 0; fmt.wBorderWidth = 1; fmt.wBorders = 0; } fmt.wBorders |= 2; + fmt.dwMask = PFM_BORDER; break; case rtfBorderTop: + info->borderType = RTFBorderParaTop; ME_GetSelectionParaFormat(info->editor, &fmt); if (!(fmt.dwMask & PFM_BORDER)) { - fmt.dwMask |= PFM_BORDER; fmt.wBorderSpace = 0; fmt.wBorderWidth = 1; fmt.wBorders = 0; } fmt.wBorders |= 4; + fmt.dwMask = PFM_BORDER; break; case rtfBorderBottom: + info->borderType = RTFBorderParaBottom; ME_GetSelectionParaFormat(info->editor, &fmt); if (!(fmt.dwMask & PFM_BORDER)) { - fmt.dwMask |= PFM_BORDER; fmt.wBorderSpace = 0; fmt.wBorderWidth = 1; fmt.wBorders = 0; } fmt.wBorders |= 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderSingle: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorders &= ~0x700; fmt.wBorders |= 1 << 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderThick: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorders &= ~0x700; fmt.wBorders |= 2 << 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderShadow: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorders &= ~0x700; fmt.wBorders |= 10 << 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderDouble: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorders &= ~0x700; fmt.wBorders |= 7 << 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderDot: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorders &= ~0x700; fmt.wBorders |= 11 << 8; + fmt.dwMask = PFM_BORDER; break; case rtfBorderWidth: + { + int borderSide = info->borderType & RTFBorderSideMask; + RTFTable *tableDef = info->tableDef; ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorderWidth |= ((info->rtfParam / 15) & 7) << 8; + if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell) + { + RTFBorder *border; + if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS) + break; + border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide]; + border->width = info->rtfParam; + break; + } + fmt.dwMask = PFM_BORDER; break; + } case rtfBorderSpace: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ fmt.wBorderSpace = info->rtfParam; + fmt.dwMask = PFM_BORDER; break; - } + case rtfBorderColor: + { + RTFTable *tableDef = info->tableDef; + int borderSide = info->borderType & RTFBorderSideMask; + int borderType = info->borderType & RTFBorderTypeMask; + switch(borderType) + { + case RTFBorderTypePara: + if (!info->editor->bEmulateVersion10) /* v4.1 */ + break; + /* v1.0 - 3.0 treat paragraph and row borders the same. */ + case RTFBorderTypeRow: + if (tableDef) { + tableDef->border[borderSide].color = info->rtfParam; + } + break; + case RTFBorderTypeCell: + if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) { + tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam; + } + break; + } + break; + } + } if (fmt.dwMask) { RTFFlushOutputBuffer(info); /* FIXME too slow ? how come ?*/ @@ -666,35 +827,249 @@ static void ME_RTFParAttrHook(RTF_Info *info) static void ME_RTFTblAttrHook(RTF_Info *info) { - ME_DisplayItem *para; - switch (info->rtfMinor) { case rtfRowDef: - RTFFlushOutputBuffer(info); - para = ME_GetParagraph(info->editor->pCursors[0].pRun); - - /* Release possibly inherited cell definitions */ - ME_DestroyTableCellList(para); - - para->member.para.pCells = ALLOC_OBJ(ME_TableCell); - para->member.para.pCells->nRightBoundary = 0; - para->member.para.pCells->next = NULL; - para->member.para.pLastCell = para->member.para.pCells; - break; - case rtfCellPos: - RTFFlushOutputBuffer(info); - para = ME_GetParagraph(info->editor->pCursors[0].pRun); - - if (para->member.para.pLastCell->nRightBoundary) - { - ME_TableCell *pCell = ALLOC_OBJ(ME_TableCell); - - pCell->next = NULL; - para->member.para.pLastCell->next = pCell; - para->member.para.pLastCell = pCell; + { + if (!info->editor->bEmulateVersion10) /* v4.1 */ + info->borderType = 0; /* Not sure */ + else /* v1.0 - 3.0 */ + info->borderType = RTFBorderRowTop; + if (!info->tableDef) { + info->tableDef = ME_MakeTableDef(info->editor); + } else { + ME_InitTableDef(info->editor, info->tableDef); + } + break; + } + case rtfCellPos: + if (!info->tableDef) + { + info->tableDef = ME_MakeTableDef(info->editor); + } + if (info->tableDef->numCellsDefined >= MAX_TABLE_CELLS) + break; + info->tableDef->cells[info->tableDef->numCellsDefined].rightBoundary = info->rtfParam; + { + /* Tab stops were used to store cell positions before v4.1 but v4.1 + * still seems to set the tabstops without using them. */ + ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); + PARAFORMAT2 *pFmt = para->member.para.pFmt; + int cellNum = info->tableDef->numCellsDefined; + pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF; + pFmt->rgxTabs[cellNum] = 0x00FFFFFF & info->rtfParam; + } + info->tableDef->numCellsDefined++; + break; + case rtfRowBordTop: + info->borderType = RTFBorderRowTop; + break; + case rtfRowBordLeft: + info->borderType = RTFBorderRowLeft; + break; + case rtfRowBordBottom: + info->borderType = RTFBorderRowBottom; + break; + case rtfRowBordRight: + info->borderType = RTFBorderRowRight; + break; + case rtfCellBordTop: + info->borderType = RTFBorderCellTop; + break; + case rtfCellBordLeft: + info->borderType = RTFBorderCellLeft; + break; + case rtfCellBordBottom: + info->borderType = RTFBorderCellBottom; + break; + case rtfCellBordRight: + info->borderType = RTFBorderCellRight; + break; + case rtfRowGapH: + if (info->tableDef) + info->tableDef->gapH = info->rtfParam; + break; + case rtfRowLeftEdge: + if (info->tableDef) + info->tableDef->leftEdge = info->rtfParam; + break; + } +} + +static void ME_RTFSpecialCharHook(RTF_Info *info) +{ + RTFTable *tableDef = info->tableDef; + switch (info->rtfMinor) + { + case rtfNestCell: + if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */ + break; + /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */ + case rtfCell: + if (!tableDef) + break; + RTFFlushOutputBuffer(info); + if (!info->editor->bEmulateVersion10) { /* v4.1 */ + if (tableDef->tableRowStart) + { + if (!info->nestingLevel && + tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + ME_DisplayItem *para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + info->nestingLevel = 1; + } + ME_InsertTableCellFromCursor(info->editor); + } + } else { /* v1.0 - v3.0 */ + ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); + PARAFORMAT2 *pFmt = para->member.para.pFmt; + if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE && + tableDef->numCellsInserted < tableDef->numCellsDefined) + { + WCHAR tab = '\t'; + ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style); + tableDef->numCellsInserted++; + } + } + break; + case rtfNestRow: + if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */ + break; + /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */ + case rtfRow: + { + ME_DisplayItem *para, *cell, *run; + int i; + + if (!tableDef) + break; + RTFFlushOutputBuffer(info); + if (!info->editor->bEmulateVersion10) { /* v4.1 */ + if (!tableDef->tableRowStart) + break; + if (!info->nestingLevel && + tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND) + { + para = tableDef->tableRowStart; + para = para->member.para.next_para; + para = ME_InsertTableRowStartAtParagraph(info->editor, para); + tableDef->tableRowStart = para; + info->nestingLevel++; + } + para = tableDef->tableRowStart; + cell = ME_FindItemFwd(para, diCell); + assert(cell && !cell->member.cell.prev_cell); + if (tableDef->numCellsDefined < 1) + { + /* 2000 twips appears to be the cell size that native richedit uses + * when no cell sizes are specified. */ + const int defaultCellSize = 2000; + int nRightBoundary = defaultCellSize; + cell->member.cell.nRightBoundary = nRightBoundary; + while (cell->member.cell.next_cell) { + cell = cell->member.cell.next_cell; + nRightBoundary += defaultCellSize; + cell->member.cell.nRightBoundary = nRightBoundary; + } + para = ME_InsertTableCellFromCursor(info->editor); + cell = para->member.para.pCell; + cell->member.cell.nRightBoundary = nRightBoundary; + } else { + for (i = 0; i < tableDef->numCellsDefined; i++) + { + RTFCell *cellDef = &tableDef->cells[i]; + cell->member.cell.nRightBoundary = cellDef->rightBoundary; + ME_ApplyBorderProperties(info, &cell->member.cell.border, + cellDef->border); + cell = cell->member.cell.next_cell; + if (!cell) + { + para = ME_InsertTableCellFromCursor(info->editor); + cell = para->member.para.pCell; + } + } + /* Cell for table row delimiter is empty */ + cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary; + } + + run = ME_FindItemFwd(cell, diRun); + if (info->editor->pCursors[0].pRun != run || + info->editor->pCursors[0].nOffset) + { + int nOfs, nChars; + /* Delete inserted cells that aren't defined. */ + info->editor->pCursors[1].pRun = run; + info->editor->pCursors[1].nOffset = 0; + nOfs = ME_GetCursorOfs(info->editor, 1); + nChars = ME_GetCursorOfs(info->editor, 0) - nOfs; + ME_InternalDeleteText(info->editor, nOfs, nChars, TRUE); + } + + para = ME_InsertTableRowEndFromCursor(info->editor); + para->member.para.pFmt->dxOffset = abs(info->tableDef->gapH); + para->member.para.pFmt->dxStartIndent = info->tableDef->leftEdge; + ME_ApplyBorderProperties(info, ¶->member.para.border, + tableDef->border); + info->nestingLevel--; + if (!info->nestingLevel) + { + if (info->canInheritInTbl) { + tableDef->tableRowStart = para; + } else { + while (info->tableDef) { + tableDef = info->tableDef; + info->tableDef = tableDef->parent; + heap_free(tableDef); + } + } + } else { + info->tableDef = tableDef->parent; + heap_free(tableDef); + } + } else { /* v1.0 - v3.0 */ + WCHAR endl = '\r'; + ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun); + PARAFORMAT2 *pFmt = para->member.para.pFmt; + pFmt->dxOffset = info->tableDef->gapH; + pFmt->dxStartIndent = info->tableDef->leftEdge; + + para = ME_GetParagraph(info->editor->pCursors[0].pRun); + ME_ApplyBorderProperties(info, ¶->member.para.border, + tableDef->border); + while (tableDef->numCellsInserted < tableDef->numCellsDefined) + { + WCHAR tab = '\t'; + ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style); + tableDef->numCellsInserted++; + } + pFmt->cTabCount = tableDef->numCellsDefined; + if (!tableDef->numCellsDefined) + pFmt->wEffects &= ~PFE_TABLE; + ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style); + tableDef->numCellsInserted = 0; + } + break; + } + case rtfTab: + case rtfPar: + if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */ + ME_DisplayItem *para; + PARAFORMAT2 *pFmt; + RTFFlushOutputBuffer(info); + para = ME_GetParagraph(info->editor->pCursors[0].pRun); + pFmt = para->member.para.pFmt; + if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) + { + /* rtfPar is treated like a space within a table. */ + info->rtfClass = rtfText; + info->rtfMajor = ' '; + } + else if (info->rtfMinor == rtfPar && tableDef) + tableDef->numCellsInserted = 0; } - para->member.para.pLastCell->nRightBoundary = info->rtfParam; break; } } @@ -1001,11 +1376,8 @@ static void ME_RTFReadHook(RTF_Info *info) { ME_RTFTblAttrHook(info); break; case rtfSpecialChar: - if (info->rtfMinor == rtfCell) - { - RTFFlushOutputBuffer(info); - ME_InsertTableCellFromCursor(info->editor, 0); - } + ME_RTFSpecialCharHook(info); + break; } break; } @@ -1037,16 +1409,19 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre if ((format & SFF_SELECTION) && (editor->mode & TM_RICHTEXT)) { style = ME_GetSelectionInsertStyle(editor); - ME_InternalDeleteText(editor, from, to-from); + ME_InternalDeleteText(editor, from, to-from, FALSE); } else { + ME_DisplayItem *para_item; style = editor->pBuffer->pDefaultStyle; ME_AddRefStyle(style); SendMessageA(editor->hWnd, EM_SETSEL, 0, 0); - ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); + ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor), FALSE); from = to = 0; ME_ClearTempStyle(editor); - /* FIXME restore default paragraph formatting ! */ + + para_item = ME_GetParagraph(editor->pCursors[0].pRun); + ME_SetDefaultParaFormat(para_item->member.para.pFmt); } @@ -1080,6 +1455,8 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre if (!invalidRTF && !inStream.editstream->dwError) { if (format & SF_RTF) { + ME_DisplayItem *para; + /* setup the RTF parser */ memset(&parser, 0, sizeof parser); RTFSetEditStream(&parser, &inStream); @@ -1092,11 +1469,50 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre RTFSetReadHook(&parser, ME_RTFReadHook); RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup); RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup); + if (!parser.editor->bEmulateVersion10) /* v4.1 */ + RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup); BeginFile(&parser); /* do the parsing */ RTFRead(&parser); RTFFlushOutputBuffer(&parser); + if (!editor->bEmulateVersion10) { /* v4.1 */ + if (parser.tableDef && parser.tableDef->tableRowStart) + { + /* Delete any incomplete table row at the end of the rich text. */ + int nOfs, nChars; + ME_DisplayItem *pCell; + + para = parser.tableDef->tableRowStart; + + parser.rtfMinor = rtfRow; + /* Complete the table row before deleting it. + * By doing it this way we will have the current paragraph format set + * properly to reflect that is not in the complete table, and undo items + * will be added for this change to the current paragraph format. */ + if (parser.nestingLevel > 0) + { + while (parser.nestingLevel--) + ME_RTFSpecialCharHook(&parser); + } else if (parser.canInheritInTbl) { + ME_RTFSpecialCharHook(&parser); + } + if (parser.tableDef && parser.tableDef->tableRowStart && + para->member.para.nFlags & MEPF_ROWEND) + { + para = para->member.para.next_para; + } + pCell = para->member.para.pCell; + + editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun); + editor->pCursors[1].nOffset = 0; + nOfs = ME_GetCursorOfs(editor, 1); + nChars = ME_GetCursorOfs(editor, 0) - nOfs; + ME_InternalDeleteText(editor, nOfs, nChars, TRUE); + parser.tableDef->tableRowStart = NULL; + } + } + ME_CheckTablesForCorruption(editor); RTFDestroy(&parser); if (parser.lpRichEditOle) IRichEditOle_Release(parser.lpRichEditOle); @@ -1117,7 +1533,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre ME_GetTextW(editor, lastchar, newto - linebreakSize, linebreakSize, 0); if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) { - ME_InternalDeleteText(editor, newto - linebreakSize, linebreakSize); + ME_InternalDeleteText(editor, newto - linebreakSize, linebreakSize, FALSE); } } } @@ -1537,7 +1953,10 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) { BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000; - + + if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU) + editor->nSelectionType = stPosition; + switch (nKey) { case VK_LEFT: @@ -1573,10 +1992,17 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) } else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE)) { - /* Backspace can be grouped for a single undo */ - ME_ContinueCoalescingTransaction(editor); - ME_DeleteTextAtCursor(editor, 1, 1); - ME_CommitCoalescingUndo(editor); + BOOL bDeletionSucceeded; + /* Backspace can be grouped for a single undo */ + ME_ContinueCoalescingTransaction(editor); + bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1); + if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */ + /* Deletion was prevented so the cursor is moved back to where it was. + * (e.g. this happens when trying to delete cell boundaries) + */ + ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE); + } + ME_CommitCoalescingUndo(editor); } else return TRUE; @@ -1609,15 +2035,102 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) return FALSE; } -static BOOL ME_SetCursor(ME_TextEditor *editor, int x) +/* Process the message and calculate the new click count. + * + * returns: The click count if it is mouse down event, else returns 0. */ +static int ME_CalculateClickCount(HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam) { + static int clickNum = 0; + if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST) + return 0; + + if ((msg == WM_LBUTTONDBLCLK) || + (msg == WM_RBUTTONDBLCLK) || + (msg == WM_MBUTTONDBLCLK) || + (msg == WM_XBUTTONDBLCLK)) + { + msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN); + } + + if ((msg == WM_LBUTTONDOWN) || + (msg == WM_RBUTTONDOWN) || + (msg == WM_MBUTTONDOWN) || + (msg == WM_XBUTTONDOWN)) + { + static MSG prevClickMsg; + MSG clickMsg; + clickMsg.hwnd = hWnd; + clickMsg.message = msg; + clickMsg.wParam = wParam; + clickMsg.lParam = lParam; + clickMsg.time = GetMessageTime(); + clickMsg.pt.x = (short)LOWORD(lParam); + clickMsg.pt.y = (short)HIWORD(lParam); + if ((clickNum != 0) && + (clickMsg.message == prevClickMsg.message) && + (clickMsg.hwnd == prevClickMsg.hwnd) && + (clickMsg.wParam == prevClickMsg.wParam) && + (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) && + (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) && + (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2)) + { + clickNum++; + } else { + clickNum = 1; + } + prevClickMsg = clickMsg; + } else { + return 0; + } + return clickNum; +} + +static BOOL ME_SetCursor(ME_TextEditor *editor) +{ + POINT pt; + BOOL isExact; + int offset; + DWORD messagePos = GetMessagePos(); + pt.x = (short)LOWORD(messagePos); + pt.y = (short)HIWORD(messagePos); + ScreenToClient(editor->hWnd, &pt); if ((GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_SELECTIONBAR) && - (x < editor->selofs || editor->linesel)) + (pt.x < editor->selofs || + (editor->nSelectionType == stLine && GetCapture() == editor->hWnd))) { SetCursor(hLeft); return TRUE; } - return FALSE; + offset = ME_CharFromPos(editor, pt.x, pt.y, &isExact); + if (isExact) + { + if (editor->AutoURLDetect_bEnable) + { + ME_Cursor cursor; + ME_Run *run; + ME_CursorFromCharOfs(editor, offset, &cursor); + run = &cursor.pRun->member.run; + if (editor->AutoURLDetect_bEnable && + run->style->fmt.dwMask & CFM_LINK && + run->style->fmt.dwEffects & CFE_LINK) + { + SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_HAND)); + return TRUE; + } + } + if (ME_IsSelection(editor)) + { + int selStart, selEnd; + ME_GetSelection(editor, &selStart, &selEnd); + if (selStart <= offset && selEnd >= offset) { + SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_ARROW)); + return TRUE; + } + } + } + SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_IBEAM)); + return TRUE; } static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y) @@ -1653,7 +2166,12 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { ed->pBuffer = ME_MakeText(); ed->nZoomNumerator = ed->nZoomDenominator = 0; ME_MakeFirstParagraph(ed); - ed->bCaretShown = FALSE; + /* The four cursors are for: + * 0 - The position where the caret is shown + * 1 - The anchored end of the selection (for normal selection) + * 2 & 3 - The anchored start and end respectively for word, line, + * or paragraph selection. + */ ed->nCursors = 4; ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors); ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun); @@ -1700,7 +2218,7 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { ed->selofs = SELECTIONBAR_WIDTH; else ed->selofs = 0; - ed->linesel = 0; + ed->nSelectionType = stPosition; if (GetWindowLongW(hWnd, GWL_STYLE) & ES_PASSWORD) ed->cPasswordMask = '*'; @@ -1709,6 +2227,15 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0; + /* Default vertical scrollbar information */ + ed->vert_si.cbSize = sizeof(SCROLLINFO); + ed->vert_si.nMin = 0; + ed->vert_si.nMax = 0; + ed->vert_si.nPage = 0; + ed->vert_si.nPos = 0; + + OleInitialize(NULL); + return ed; } @@ -1777,6 +2304,7 @@ void ME_DestroyEditor(ME_TextEditor *editor) DeleteObject(editor->hbrBackground); if(editor->lpOleCallback) IUnknown_Release(editor->lpOleCallback); + OleUninitialize(); FREE_OBJ(editor->pBuffer); FREE_OBJ(editor->pCursors); @@ -1996,10 +2524,9 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX) UNSUPPORTED_MSG(WM_STYLECHANGING) UNSUPPORTED_MSG(WM_STYLECHANGED) -/* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */ - + /* Messages specific to Richedit controls */ - + case EM_STREAMIN: return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE); case EM_STREAMOUT: @@ -2134,6 +2661,8 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_InvalidateSelection(editor); ME_SetSelection(editor, wParam, lParam); ME_InvalidateSelection(editor); + HideCaret(editor->hWnd); + ME_ShowCaret(editor); ME_SendSelChange(editor); return 0; } @@ -2166,6 +2695,8 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_InvalidateSelection(editor); end = ME_SetSelection(editor, range.cpMin, range.cpMax); ME_InvalidateSelection(editor); + HideCaret(editor->hWnd); + ME_ShowCaret(editor); ME_SendSelChange(editor); return end; @@ -2197,16 +2728,16 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (pStruct->flags & ST_SELECTION) { ME_GetSelection(editor, &from, &to); style = ME_GetSelectionInsertStyle(editor); - ME_InternalDeleteText(editor, from, to - from); + ME_InternalDeleteText(editor, from, to - from, FALSE); if (pStruct->codepage != 1200 && lParam && !strncmp((char *)lParam, "{\\rtf", 5)) - ME_StreamInRTFString(editor, 0, (char *)lParam); + ME_StreamInRTFString(editor, 1, (char *)lParam); else ME_InsertTextFromCursor(editor, 0, wszText, len, style); ME_ReleaseStyle(style); if (editor->AutoURLDetect_bEnable) ME_UpdateSelectionLinkAttribute(editor); } else { - ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); + ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor), FALSE); if (pStruct->codepage != 1200 && lParam && !strncmp((char *)lParam, "{\\rtf", 5)) ME_StreamInRTFString(editor, 0, (char *)lParam); else ME_InsertTextFromCursor(editor, 0, wszText, len, editor->pBuffer->pDefaultStyle); @@ -2328,13 +2859,15 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, return tmp.dwMask; } case EM_SETPARAFORMAT: - ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); + { + BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); ME_RewrapRepaint(editor); ME_CommitUndo(editor); - return 0; + return result; + } case EM_GETPARAFORMAT: ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); - return 0; + return ((PARAFORMAT2 *)lParam)->dwMask; case EM_GETFIRSTVISIBLELINE: { ME_DisplayItem *p = editor->pBuffer->pFirst; @@ -2347,10 +2880,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (p->type == diTextEnd) break; if (p->type == diParagraph) { - ypara = p->member.para.nYPos; + ypara = p->member.para.pt.y; continue; } - ystart = ypara + p->member.row.nYPos; + ystart = ypara + p->member.row.pt.y; yend = ystart + p->member.row.nHeight; if (y < yend) { break; @@ -2374,7 +2907,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, { int from, to; ME_GetSelection(editor, &from, &to); - ME_InternalDeleteText(editor, from, to-from); + ME_InternalDeleteText(editor, from, to-from, FALSE); ME_CommitUndo(editor); ME_UpdateRepaint(editor); return 0; @@ -2389,7 +2922,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_GetSelection(editor, &from, &to); style = ME_GetSelectionInsertStyle(editor); - ME_InternalDeleteText(editor, from, to-from); + ME_InternalDeleteText(editor, from, to-from, FALSE); ME_InsertTextFromCursor(editor, 0, wszText, len, style); ME_ReleaseStyle(style); /* drop temporary style if line end */ @@ -2416,7 +2949,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, nPos = ME_GetYScrollPos(editor); row = ME_RowStart(editor->pCursors[0].pRun); para = ME_GetParagraph(row); - top = para->member.para.nYPos + row->member.row.nYPos; + top = para->member.para.pt.y + row->member.row.pt.y; bottom = top + row->member.row.nHeight; if (top < nPos) /* caret above window */ @@ -2448,7 +2981,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, } case WM_SETTEXT: { - ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); + ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor), FALSE); if (lParam) { TRACE("WM_SETTEXT lParam==%lx\n",lParam); @@ -2548,7 +3081,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, } if (SUCCEEDED(hr) && msg == WM_CUT) { - ME_InternalDeleteText(editor, range.cpMin, range.cpMax-range.cpMin); + ME_InternalDeleteText(editor, range.cpMin, range.cpMax-range.cpMin, FALSE); ME_CommitUndo(editor); ME_UpdateRepaint(editor); } @@ -2914,7 +3447,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, case EM_SETZOOM: return ME_SetZoom(editor, wParam, lParam); case EM_CHARFROMPOS: - return ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y); + return ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y, NULL); case EM_POSFROMCHAR: { ME_DisplayItem *pRun; @@ -2933,10 +3466,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, assert(pRun->type == diRun); pt.y = pRun->member.run.pt.y; pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset); - pt.y += ME_GetParagraph(pRun)->member.para.nYPos; + pt.y += ME_GetParagraph(pRun)->member.para.pt.y; } else { pt.x = 0; - pt.y = editor->pBuffer->pLast->member.para.nYPos; + pt.y = editor->pBuffer->pLast->member.para.pt.y; } pt.x += editor->selofs; @@ -2953,33 +3486,50 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y ); } case WM_CREATE: + { + SCROLLINFO si; + GetClientRect(hWnd, &editor->rcFormat); if (GetWindowLongW(hWnd, GWL_STYLE) & WS_HSCROLL) { /* Squelch the default horizontal scrollbar it would make */ ShowScrollBar(editor->hWnd, SB_HORZ, FALSE); } + + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE; + if (GetWindowLongW(hWnd, GWL_STYLE) & ES_DISABLENOSCROLL) + si.fMask |= SIF_DISABLENOSCROLL; + si.nMax = (si.fMask & SIF_DISABLENOSCROLL) ? 1 : 0; + si.nMin = 0; + si.nPage = 0; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + ME_CommitUndo(editor); ME_WrapMarkedParagraphs(editor); ME_MoveCaret(editor); return 0; + } case WM_DESTROY: ME_DestroyEditor(editor); SetWindowLongPtrW(hWnd, 0, 0); return 0; + case WM_SETCURSOR: + { + return ME_SetCursor(editor); + } case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: { - int clickNum = (msg == WM_LBUTTONDBLCLK) ? 2 : 1; ME_CommitUndo(editor); /* End coalesced undos for typed characters */ if ((editor->nEventMask & ENM_MOUSEEVENTS) && !ME_FilterEvent(editor, msg, &wParam, &lParam)) return 0; SetFocus(hWnd); ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam), - clickNum); + ME_CalculateClickCount(hWnd, msg, wParam, lParam)); SetCapture(hWnd); ME_LinkNotify(editor,msg,wParam,lParam); - if (!ME_SetCursor(editor, LOWORD(lParam))) goto do_default; + if (!ME_SetCursor(editor)) goto do_default; break; } case WM_MOUSEMOVE: @@ -2989,21 +3539,22 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (GetCapture() == hWnd) ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)); ME_LinkNotify(editor,msg,wParam,lParam); - if (!ME_SetCursor(editor, LOWORD(lParam))) goto do_default; + /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */ + if (GetCapture() == hWnd) + ME_SetCursor(editor); break; case WM_LBUTTONUP: if (GetCapture() == hWnd) ReleaseCapture(); + if (editor->nSelectionType == stDocument) + editor->nSelectionType = stPosition; if ((editor->nEventMask & ENM_MOUSEEVENTS) && !ME_FilterEvent(editor, msg, &wParam, &lParam)) return 0; else { - BOOL ret; - editor->linesel = 0; - ret = ME_SetCursor(editor, LOWORD(lParam)); + ME_SetCursor(editor); ME_LinkNotify(editor,msg,wParam,lParam); - if (!ret) goto do_default; } break; case WM_RBUTTONUP: @@ -3035,8 +3586,8 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, return 0; case WM_KILLFOCUS: ME_CommitUndo(editor); /* End coalesced undos for typed characters */ - ME_HideCaret(editor); editor->bHaveFocus = FALSE; + ME_HideCaret(editor); ME_SendOldNotify(editor, EN_KILLFOCUS); return 0; case WM_ERASEBKGND: @@ -3112,10 +3663,35 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (((unsigned)wstr)>=' ' || (wstr=='\r' && (GetWindowLongW(hWnd, GWL_STYLE) & ES_MULTILINE)) || wstr=='\t') { + int from, to; + BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; + ME_GetSelection(editor, &from, &to); + if (wstr=='\t' + /* v4.1 allows tabs to be inserted with ctrl key down */ + && !(ctrl_is_down && !editor->bEmulateVersion10) + ) + { + ME_Cursor cursor = editor->pCursors[0]; + ME_DisplayItem *para; + BOOL bSelectedRow = FALSE; + + para = ME_GetParagraph(cursor.pRun); + if (ME_IsSelection(editor) && + cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 && + para->member.para.prev_para->type == diParagraph) + { + para = para->member.para.prev_para; + bSelectedRow = TRUE; + } + if (ME_IsInTable(para)) + { + ME_TabPressedInTable(editor, bSelectedRow); + ME_CommitUndo(editor); + return 0; + } + } /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */ /* WM_CHAR is restricted to nTextLimit */ - int from, to; - ME_GetSelection(editor, &from, &to); if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from)) { ME_Style *style = ME_GetInsertStyle(editor, 0); @@ -3127,6 +3703,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_InsertTextFromCursor(editor, 0, &wstr, 1, style); ME_ReleaseStyle(style); ME_CommitCoalescingUndo(editor); + SetCursor(NULL); } if (editor->AutoURLDetect_bEnable) ME_UpdateSelectionLinkAttribute(editor); @@ -3135,6 +3712,23 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, } return 0; } + case WM_UNICHAR: + if (unicode) + { + if(wParam == UNICODE_NOCHAR) return TRUE; + if(wParam <= 0x000fffff) + { + if(wParam > 0xffff) /* convert to surrogates */ + { + wParam -= 0x10000; + SendMessageW(editor->hWnd, WM_CHAR, (wParam >> 10) + 0xd800, 0); + SendMessageW(editor->hWnd, WM_CHAR, (wParam & 0x03ff) + 0xdc00, 0); + } + else SendMessageW(editor->hWnd, WM_CHAR, wParam, 0); + } + return 0; + } + break; case EM_STOPGROUPTYPING: ME_CommitUndo(editor); /* End coalesced undos for typed characters */ return 0; @@ -3429,6 +4023,7 @@ void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam { int x,y; ME_Cursor tmpCursor; + BOOL isExact; int nCharOfs; /* The start of the clicked text. Absolute character offset */ ME_Run *tmpRun; @@ -3436,8 +4031,8 @@ void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam ENLINK info; x = (short)LOWORD(lParam); y = (short)HIWORD(lParam); - nCharOfs = ME_CharFromPos(editor, x, y); - if (nCharOfs < 0) return; + nCharOfs = ME_CharFromPos(editor, x, y, &isExact); + if (!isExact) return; ME_CursorFromCharOfs(editor, nCharOfs, &tmpCursor); tmpRun = &tmpCursor.pRun->member.run; @@ -3513,7 +4108,12 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in if (nLen > nChars) nLen = nChars; - if (item->member.run.nFlags & MERF_ENDPARA) + if (item->member.run.nFlags & MERF_ENDCELL && + item->member.run.nFlags & MERF_ENDPARA) + { + *buffer = '\t'; + } + else if (item->member.run.nFlags & MERF_ENDPARA) { if (!ME_FindItemFwd(item, diRun)) /* No '\r' is appended to the last paragraph. */ @@ -3525,10 +4125,16 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in if (bCRLF) { /* richedit 2.0 case - actual line-break is \r but should report \r\n */ - assert(nLen == 1); + if (ME_GetParagraph(item)->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) + assert(nLen == 2); + else + assert(nLen == 1); *buffer++ = '\r'; *buffer = '\n'; /* Later updated by nLen==1 at the end of the loop */ - nWritten++; + if (nLen == 1) + nWritten++; + else + buffer--; } else { diff --git a/reactos/dll/win32/riched20/editor.h b/reactos/dll/win32/riched20/editor.h index a23fb8b3972..5e6f4cb502c 100644 --- a/reactos/dll/win32/riched20/editor.h +++ b/reactos/dll/win32/riched20/editor.h @@ -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); diff --git a/reactos/dll/win32/riched20/editstr.h b/reactos/dll/win32/riched20/editstr.h index cab52ad58ad..edbeb131834 100644 --- a/reactos/dll/win32/riched20/editstr.h +++ b/reactos/dll/win32/riched20/editstr.h @@ -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 diff --git a/reactos/dll/win32/riched20/list.c b/reactos/dll/win32/riched20/list.c index f5c5f42fd8d..4ddc54a9bfc 100644 --- a/reactos/dll/win32/riched20/list.c +++ b/reactos/dll/win32/riched20/list.c @@ -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"); diff --git a/reactos/dll/win32/riched20/paint.c b/reactos/dll/win32/riched20/paint.c index a2bc7c52236..9cfc21ef640 100644 --- a/reactos/dll/win32/riched20/paint.c +++ b/reactos/dll/win32/riched20/paint.c @@ -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.ybottom && 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.ydpi.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 && nSelFromeditor->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; diff --git a/reactos/dll/win32/riched20/para.c b/reactos/dll/win32/riched20/para.c index de69794c33d..dd745ffc18a 100644 --- a/reactos/dll/win32/riched20/para.c +++ b/reactos/dll/win32/riched20/para.c @@ -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; +} diff --git a/reactos/dll/win32/riched20/reader.c b/reactos/dll/win32/riched20/reader.c index f456a09fdc3..8801137bc96 100644 --- a/reactos/dll/win32/riched20/reader.c +++ b/reactos/dll/win32/riched20/reader.c @@ -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'); diff --git a/reactos/dll/win32/riched20/riched20.rbuild b/reactos/dll/win32/riched20/riched20.rbuild index e01a41bb17b..49bcba6bbb3 100644 --- a/reactos/dll/win32/riched20/riched20.rbuild +++ b/reactos/dll/win32/riched20/riched20.rbuild @@ -22,6 +22,7 @@ run.c string.c style.c + table.c txtsrv.c undo.c wrap.c diff --git a/reactos/dll/win32/riched20/rtf.h b/reactos/dll/win32/riched20/rtf.h index 8b39f60f647..ec08161a94b 100644 --- a/reactos/dll/win32/riched20/rtf.h +++ b/reactos/dll/win32/riched20/rtf.h @@ -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. */ }; diff --git a/reactos/dll/win32/riched20/run.c b/reactos/dll/win32/riched20/run.c index 00285365976..bb34ad864ed 100644 --- a/reactos/dll/win32/riched20/run.c +++ b/reactos/dll/win32/riched20/run.c @@ -134,6 +134,9 @@ void ME_CheckCharOffsets(ME_TextEditor *editor) else ofs += ME_StrLen(p->member.run.strText); break; + case diCell: + TRACE_(richedit_check)("cell\n"); + break; default: assert(0); } @@ -436,7 +439,7 @@ void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run) { assert(run->nCharOfs != -1); - if (RUN_IS_HIDDEN(run)) + if (RUN_IS_HIDDEN(run) || run->nFlags & MERF_TABLESTART) run->nFlags |= MERF_HIDDEN; else run->nFlags &= ~MERF_HIDDEN; @@ -483,7 +486,8 @@ int ME_CharFromPoint(ME_Context *c, int cx, ME_Run *run) if (!run->strText->nLen) return 0; - if (run->nFlags & (MERF_TAB | 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; diff --git a/reactos/dll/win32/riched20/style.c b/reactos/dll/win32/riched20/style.c index 66e45f5a832..b7dae3e97a5 100644 --- a/reactos/dll/win32/riched20/style.c +++ b/reactos/dll/win32/riched20/style.c @@ -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); diff --git a/reactos/dll/win32/riched20/table.c b/reactos/dll/win32/riched20/table.c new file mode 100644 index 00000000000..248ea11719f --- /dev/null +++ b/reactos/dll/win32/riched20/table.c @@ -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; +} diff --git a/reactos/dll/win32/riched20/undo.c b/reactos/dll/win32/riched20/undo.c index bf992ab20f7..3ab39d01526 100644 --- a/reactos/dll/win32/riched20/undo.c +++ b/reactos/dll/win32/riched20/undo.c @@ -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; diff --git a/reactos/dll/win32/riched20/wrap.c b/reactos/dll/win32/riched20/wrap.c index adabea13803..a3c5d544ff6 100644 --- a/reactos/dll/win32/riched20/wrap.c +++ b/reactos/dll/win32/riched20/wrap.c @@ -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; diff --git a/reactos/dll/win32/riched20/writer.c b/reactos/dll/win32/riched20/writer.c index ce9e8537d7f..912cae3f48e 100644 --- a/reactos/dll/win32/riched20/writer.c +++ b/reactos/dll/win32/riched20/writer.c @@ -41,6 +41,7 @@ ME_StreamOutInit(ME_TextEditor *editor, EDITSTREAM *stream) pStream->written = 0; pStream->nFontTblLen = 0; pStream->nColorTblLen = 1; + pStream->nNestingLevel = 0; return pStream; } @@ -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 { diff --git a/reactos/include/reactos/wine/winuser.h b/reactos/include/reactos/wine/winuser.h index be58b229247..9d11b5cb047 100644 --- a/reactos/include/reactos/wine/winuser.h +++ b/reactos/include/reactos/wine/winuser.h @@ -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