From 32a25796945511ab3ce1d51b5ea68d8be02c1b2a Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Sat, 10 May 2008 19:50:28 +0000 Subject: [PATCH] =?UTF-8?q?Sync=20riched20=20to=20Wine=201.0-rc1=20Samuel?= =?UTF-8?q?=20Serapi=C3=B3n=20got=20some=20apps=20like=207-Zip=20and=20Pow?= =?UTF-8?q?erISO=20installed=20with=20the=20new=20version.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit svn path=/trunk/; revision=33419 --- reactos/dll/win32/riched20/caret.c | 64 +++++-- reactos/dll/win32/riched20/editor.c | 251 ++++++++++++++++++++------- reactos/dll/win32/riched20/editor.h | 21 ++- reactos/dll/win32/riched20/editstr.h | 5 + reactos/dll/win32/riched20/list.c | 2 + reactos/dll/win32/riched20/paint.c | 126 ++++++++------ reactos/dll/win32/riched20/para.c | 27 ++- reactos/dll/win32/riched20/reader.c | 3 +- reactos/dll/win32/riched20/run.c | 12 +- reactos/dll/win32/riched20/style.c | 5 +- reactos/dll/win32/riched20/undo.c | 10 +- reactos/dll/win32/riched20/wrap.c | 14 +- reactos/dll/win32/riched20/writer.c | 45 ++++- 13 files changed, 417 insertions(+), 168 deletions(-) diff --git a/reactos/dll/win32/riched20/caret.c b/reactos/dll/win32/riched20/caret.c index a6a62178b54..b2172ace9ca 100644 --- a/reactos/dll/win32/riched20/caret.c +++ b/reactos/dll/win32/riched20/caret.c @@ -57,7 +57,9 @@ int ME_GetTextLengthEx(ME_TextEditor *editor, const GETTEXTLENGTHEX *how) length = ME_GetTextLength(editor); - if ((GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_MULTILINE) && (how->flags & GTL_USECRLF)) + if ((GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_MULTILINE) + && (how->flags & GTL_USECRLF) + && !editor->bEmulateVersion10) /* Ignore GTL_USECRLF flag in 1.0 emulation */ length += editor->nParagraphs - 1; if (how->flags & GTL_NUMBYTES) @@ -270,6 +272,8 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, ME_CursorFromCharOfs(editor, nOfs, &c); run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { + int eollen = run->nCR + run->nLF; + if (!ME_FindItemFwd(c.pRun, diParagraph)) { return; @@ -277,9 +281,7 @@ void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun)); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); - nChars--; - if (editor->bEmulateVersion10 && nChars) - nChars--; + nChars -= (eollen < nChars) ? eollen : nChars; continue; } else @@ -522,6 +524,8 @@ void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, if (pos-str < len) { /* handle EOLs */ ME_DisplayItem *tp, *end_run; ME_Style *tmp_style; + int numCR, numLF; + if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); p = &editor->pCursors[nCursor]; @@ -531,16 +535,48 @@ void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, } tmp_style = ME_GetInsertStyle(editor, nCursor); /* ME_SplitParagraph increases style refcount */ - tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style); + + /* Encode and fill number of CR and LF according to emulation mode */ + if (editor->bEmulateVersion10) { + const WCHAR * tpos; + + /* We have to find out how many consecutive \r are there, and if there + is a \n terminating the run of \r's. */ + numCR = 0; numLF = 0; + tpos = pos; + while (tpos-str < len && *tpos == '\r') { + tpos++; + numCR++; + } + if (tpos-str >= len) { + /* Reached end of text without finding anything but '\r' */ + if (tpos != pos) { + pos++; + } + numCR = 1; numLF = 0; + } else if (*tpos == '\n') { + /* The entire run of \r's plus the one \n is one single line break */ + pos = tpos + 1; + numLF = 1; + } else { + /* Found some other content past the run of \r's */ + pos++; + numCR = 1; numLF = 0; + } + } else { + if(pos-str < len && *pos =='\r') + pos++; + if(pos-str < len && *pos =='\n') + pos++; + numCR = 1; numLF = 0; + } + tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style, numCR, numLF); p->pRun = ME_FindItemFwd(tp, diRun); end_run = ME_FindItemBack(tp, diRun); ME_ReleaseStyle(end_run->member.run.style); end_run->member.run.style = tmp_style; p->nOffset = 0; - if(pos-str < len && *pos =='\r') - pos++; - if(pos-str < len && *pos =='\n') - pos++; + if(pos-str <= len) { len -= pos - str; str = pos; @@ -1294,7 +1330,15 @@ void ME_SendSelChange(ME_TextEditor *editor) sc.seltyp |= SEL_TEXT; if (sc.chrg.cpMin < sc.chrg.cpMax+1) /* wth were RICHEDIT authors thinking ? */ sc.seltyp |= SEL_MULTICHAR; - SendMessageW(GetParent(editor->hWnd), WM_NOTIFY, sc.nmhdr.idFrom, (LPARAM)&sc); + TRACE("cpMin=%d cpMax=%d seltyp=%d (%s %s)\n", + sc.chrg.cpMin, sc.chrg.cpMax, sc.seltyp, + (sc.seltyp & SEL_TEXT) ? "SEL_TEXT" : "", + (sc.seltyp & SEL_MULTICHAR) ? "SEL_MULTICHAR" : ""); + if (sc.chrg.cpMin != editor->notified_cr.cpMin || sc.chrg.cpMax != editor->notified_cr.cpMax) + { + editor->notified_cr = sc.chrg; + SendMessageW(GetParent(editor->hWnd), WM_NOTIFY, sc.nmhdr.idFrom, (LPARAM)&sc); + } } BOOL diff --git a/reactos/dll/win32/riched20/editor.c b/reactos/dll/win32/riched20/editor.c index 39827bcfcd8..1ae0e23e234 100644 --- a/reactos/dll/win32/riched20/editor.c +++ b/reactos/dll/win32/riched20/editor.c @@ -115,7 +115,7 @@ + EM_SETSCROLLPOS 3.0 - EM_SETTABSTOPS 3.0 - EM_SETTARGETDEVICE (partial) - + EM_SETTEXTEX 3.0 (no rich text insertion handling, proper style?) + + EM_SETTEXTEX 3.0 (proper style?) - EM_SETTEXTMODE 2.0 - EM_SETTYPOGRAPHYOPTIONS 3.0 + EM_SETUNDOLIMIT 2.0 @@ -620,31 +620,31 @@ static void ME_RTFParAttrHook(RTF_Info *info) case rtfBorderSingle: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ - fmt.wBorders &= ~0x70; + fmt.wBorders &= ~0x700; fmt.wBorders |= 1 << 8; break; case rtfBorderThick: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ - fmt.wBorders &= ~0x70; + fmt.wBorders &= ~0x700; fmt.wBorders |= 2 << 8; break; case rtfBorderShadow: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ - fmt.wBorders &= ~0x70; + fmt.wBorders &= ~0x700; fmt.wBorders |= 10 << 8; break; case rtfBorderDouble: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ - fmt.wBorders &= ~0x70; + fmt.wBorders &= ~0x700; fmt.wBorders |= 7 << 8; break; case rtfBorderDot: ME_GetSelectionParaFormat(info->editor, &fmt); /* we assume that borders have been created before (RTF spec) */ - fmt.wBorders &= ~0x70; + fmt.wBorders &= ~0x700; fmt.wBorders |= 11 << 8; break; case rtfBorderWidth: @@ -1022,13 +1022,14 @@ ME_StreamInFill(ME_InStream *stream) stream->dwUsed = 0; } -static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream) +static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR) { RTF_Info parser; ME_Style *style; int from, to, to2, nUndoMode; int nEventMask = editor->nEventMask; ME_InStream inStream; + BOOL invalidRTF = FALSE; TRACE("stream==%p hWnd==%p format==0x%X\n", stream, editor->hWnd, format); editor->nEventMask = 0; @@ -1068,15 +1069,16 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre ME_StreamInFill(&inStream); if (!inStream.editstream->dwError) { - if (strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6)) + if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6)) + || (editor->bEmulateVersion10 && *inStream.buffer != '{')) { - format &= ~SF_RTF; - format |= SF_TEXT; + invalidRTF = TRUE; + inStream.editstream->dwError = -16; } } } - if (!inStream.editstream->dwError) + if (!invalidRTF && !inStream.editstream->dwError) { if (format & SF_RTF) { /* setup the RTF parser */ @@ -1100,6 +1102,24 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre if (parser.lpRichEditOle) IRichEditOle_Release(parser.lpRichEditOle); + /* Remove last line break, as mandated by tests. This is not affected by + CR/LF counters, since RTF streaming presents only \para tokens, which + are converted according to the standard rules: \r for 2.0, \r\n for 1.0 + */ + if (stripLastCR) { + int newfrom, newto; + ME_GetSelection(editor, &newfrom, &newto); + if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) { + WCHAR lastchar[3] = {'\0', '\0'}; + int linebreakSize = editor->bEmulateVersion10 ? 2 : 1; + + ME_GetTextW(editor, lastchar, newto - linebreakSize, linebreakSize, 0); + if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) { + ME_InternalDeleteText(editor, newto - linebreakSize, linebreakSize); + } + } + } + style = parser.style; } else if (format & SF_TEXT) @@ -1124,10 +1144,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre ME_ReleaseStyle(style); editor->nEventMask = nEventMask; - if (editor->bRedraw) - { - ME_UpdateRepaint(editor); - } + ME_UpdateRepaint(editor); if (!(format & SFF_SELECTION)) { ME_ClearTempStyle(editor); } @@ -1169,7 +1186,7 @@ ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string) data.pos = 0; es.dwCookie = (DWORD)&data; es.pfnCallback = ME_ReadFromRTFString; - ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es); + ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, FALSE); } @@ -1177,6 +1194,7 @@ ME_DisplayItem * ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset) { ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); + int runLength; while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset) item = ME_FindItemFwd(item, diParagraph); @@ -1193,9 +1211,27 @@ ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int do { item = ME_FindItemFwd(item, diRun); - } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset)); + runLength = ME_StrLen(item->member.run.strText); + if (item->member.run.nFlags & MERF_ENDPARA) + runLength = item->member.run.nCR + item->member.run.nLF; + } while (item && (item->member.run.nCharOfs + runLength <= nOffset)); if (item) { nOffset -= item->member.run.nCharOfs; + + /* Special case: nOffset may not point exactly at the division between the + \r and the \n in 1.0 emulation. If such a case happens, it is sent + into the next run, if one exists + */ + if ( item->member.run.nFlags & MERF_ENDPARA + && nOffset == item->member.run.nCR + && item->member.run.nLF > 0) { + ME_DisplayItem *nextItem; + nextItem = ME_FindItemFwd(item, diRun); + if (nextItem) { + nOffset = 0; + item = nextItem; + } + } if (nItemOffset) *nItemOffset = nOffset; } @@ -1227,12 +1263,29 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCH else nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax; + /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */ + if (editor->bEmulateVersion10 && nMax == nTextLen) + { + flags |= FR_DOWN; + } + + /* In 1.0 emulation, cpMin must always be no greater than cpMax */ + if (editor->bEmulateVersion10 && nMax < nMin) + { + if (chrgText) + { + chrgText->cpMin = -1; + chrgText->cpMax = -1; + } + return -1; + } + /* when searching up, if cpMin < cpMax, then instead of searching * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on * [cpMax, cpMin]. The exception is when cpMax is -1, in which * case, it is always bigger than cpMin. */ - if (!(flags & FR_DOWN)) + if (!editor->bEmulateVersion10 && !(flags & FR_DOWN)) { int nSwap = nMax; @@ -1630,6 +1683,8 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { else ed->cPasswordMask = 0; + ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0; + return ed; } @@ -1922,7 +1977,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, /* Messages specific to Richedit controls */ case EM_STREAMIN: - return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam); + return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE); case EM_STREAMOUT: return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam); case WM_GETDLGCODE: @@ -2117,17 +2172,20 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, wszText = lParam ? ME_ToUnicode(pStruct->codepage == 1200, (void *)lParam) : NULL; len = wszText ? lstrlenW(wszText) : 0; - /* FIXME: this should support RTF strings too, according to MSDN */ if (pStruct->flags & ST_SELECTION) { ME_GetSelection(editor, &from, &to); style = ME_GetSelectionInsertStyle(editor); ME_InternalDeleteText(editor, from, to - from); - ME_InsertTextFromCursor(editor, 0, wszText, len, style); + if (pStruct->codepage != 1200 && lParam && !strncmp((char *)lParam, "{\\rtf", 5)) + ME_StreamInRTFString(editor, 0, (char *)lParam); + else ME_InsertTextFromCursor(editor, 0, wszText, len, style); ME_ReleaseStyle(style); } else { ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); - ME_InsertTextFromCursor(editor, 0, wszText, len, editor->pBuffer->pDefaultStyle); + 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); len = 1; } ME_CommitUndo(editor); @@ -2166,7 +2224,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, return lColor; } case EM_GETMODIFY: - return editor->nModifyStep == 0 ? 0 : 1; + return editor->nModifyStep == 0 ? 0 : -1; case EM_SETMODIFY: { if (wParam) @@ -2209,8 +2267,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, } else if (wParam == SCF_ALL) { if (editor->mode & TM_PLAINTEXT) ME_SetDefaultCharFormat(editor, p); - else + else { ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p); + editor->nModifyStep = 1; + } } else if (editor->mode & TM_PLAINTEXT) { return 0; } else { @@ -2218,8 +2278,8 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_GetSelection(editor, &from, &to); bRepaint = (from != to); ME_SetSelectionCharFormat(editor, p); + if (from != to) editor->nModifyStep = 1; } - editor->nModifyStep = 1; ME_CommitUndo(editor); if (bRepaint) ME_RewrapRepaint(editor); @@ -2394,10 +2454,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, } else TRACE("WM_SETTEXT - NULL\n"); - ME_CommitUndo(editor); - ME_EmptyUndoStack(editor); ME_SetSelection(editor, 0, 0); editor->nModifyStep = 0; + ME_CommitUndo(editor); + ME_EmptyUndoStack(editor); ME_UpdateRepaint(editor); return 1; } @@ -2431,7 +2491,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, gds.nLength = 0; es.dwCookie = (DWORD)&gds; es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode; - ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es); + ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es, FALSE); CloseClipboard(); return 0; @@ -2467,6 +2527,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, { GETTEXTLENGTHEX how; + /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */ how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS; how.codepage = unicode ? 1200 : CP_ACP; return ME_GetTextLengthEx(editor, &how); @@ -2532,7 +2593,10 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, else { /* potentially each char may be a CR, why calculate the exact value with O(N) when - we can just take a bigger buffer? :) */ + we can just take a bigger buffer? :) + The above assumption still holds with CR/LF counters, since CR->CRLF expansion + occurs only in richedit 2.0 mode, in which line breaks have only one CR + */ int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1; LPWSTR buffer; DWORD buflen = ex->cb; @@ -2574,12 +2638,12 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, rng->chrg.cpMin, rng->chrg.cpMax, unicode, editor->bEmulateVersion10, ME_GetTextLength(editor)); if (unicode) - return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, editor->bEmulateVersion10); + return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, 0); else { int nLen = rng->chrg.cpMax-rng->chrg.cpMin; WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1); - int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, editor->bEmulateVersion10); + int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, 0); /* FIXME this is a potential security hole (buffer overrun) if you know more about wchar->mbyte conversion please explain */ @@ -2592,11 +2656,9 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, { ME_DisplayItem *run; const unsigned int nMaxChars = *(WORD *) lParam; - unsigned int nEndChars, nCharsLeft = nMaxChars; + unsigned int nCharsLeft = nMaxChars; char *dest = (char *) lParam; - /* rich text editor 1.0 uses \r\n for line end, 2.0 uses just \r; - we need to know how if we have the extra \n or not */ - int nLF = editor->bEmulateVersion10; + BOOL wroteNull = FALSE; TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars, unicode ? "Unicode" : "Ansi"); @@ -2624,36 +2686,68 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, nCharsLeft -= nCopy; } - /* append \r\0 (or \r\n\0 in 1.0), space allowing */ - nEndChars = min(nCharsLeft, 2 + nLF); - nCharsLeft -= nEndChars; - if (unicode) + /* append line termination, space allowing */ + if (nCharsLeft > 0) { - const WCHAR src[] = {'\r', '\0'}; - const WCHAR src10[] = {'\r', '\n', '\0'}; - lstrcpynW((LPWSTR) dest, nLF ? src10 : src, nEndChars); + if (run && (run->member.run.nFlags & MERF_ENDPARA)) + { + unsigned int i; + /* Write as many \r as encoded in end-of-paragraph, space allowing */ + for (i = 0; i < run->member.run.nCR && nCharsLeft > 0; i++, nCharsLeft--) + { + *((WCHAR *)dest) = '\r'; + dest += unicode ? sizeof(WCHAR) : 1; + } + /* Write as many \n as encoded in end-of-paragraph, space allowing */ + for (i = 0; i < run->member.run.nLF && nCharsLeft > 0; i++, nCharsLeft--) + { + *((WCHAR *)dest) = '\n'; + dest += unicode ? sizeof(WCHAR) : 1; + } + } + if (nCharsLeft > 0) + { + if (unicode) + *((WCHAR *)dest) = '\0'; + else + *dest = '\0'; + nCharsLeft--; + wroteNull = TRUE; + } } - else - lstrcpynA(dest, nLF ? "\r\n" : "\r", nEndChars); - TRACE("EM_GETLINE: got %u bytes\n", nMaxChars - nCharsLeft); - - if (nEndChars == 2 + nLF) - return nMaxChars - nCharsLeft - 1; /* don't count \0 */ - else - return nMaxChars - nCharsLeft; + TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft); + return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0); } case EM_GETLINECOUNT: { ME_DisplayItem *item = editor->pBuffer->pFirst->next; int nRows = 0; + ME_DisplayItem *prev_para = NULL, *last_para = NULL; + while (item != editor->pBuffer->pLast) { assert(item->type == diParagraph); + prev_para = ME_FindItemBack(item, diRun); + if (prev_para) { + assert(prev_para->member.run.nFlags & MERF_ENDPARA); + } nRows += item->member.para.nRows; item = item->member.para.next_para; } + last_para = ME_FindItemBack(item, diRun); + assert(last_para); + assert(last_para->member.run.nFlags & MERF_ENDPARA); + if (editor->bEmulateVersion10 && prev_para && last_para->member.run.nCharOfs == 0 + && prev_para->member.run.nCR == 1 && prev_para->member.run.nLF == 0) + { + /* In 1.0 emulation, the last solitary \r at the very end of the text + (if one exists) is NOT a line break. + FIXME: this is an ugly hack. This should have a more regular model. */ + nRows--; + } + TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows); return max(1, nRows); } @@ -2707,8 +2801,17 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, if (item_end->type == diStartRow) nNextLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item_end, diRun), 0); else - nNextLineOfs = ME_FindItemFwd(item, diParagraphOrEnd)->member.para.nCharOfs - - (editor->bEmulateVersion10?2:1); + { + ME_DisplayItem *endPara; + + nNextLineOfs = ME_FindItemFwd(item, diParagraphOrEnd)->member.para.nCharOfs; + endPara = ME_FindItemFwd(item, diParagraphOrEnd); + endPara = ME_FindItemBack(endPara, diRun); + assert(endPara); + assert(endPara->type == diRun); + assert(endPara->member.run.nFlags & MERF_ENDPARA); + nNextLineOfs -= endPara->member.run.nCR + endPara->member.run.nLF; + } nChars = nNextLineOfs - nThisLineOfs; TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars); return nChars; @@ -3207,7 +3310,12 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, case EM_SETTARGETDEVICE: if (wParam == 0) { - editor->bWordWrap = (lParam == 0); + BOOL new = (lParam == 0); + if (editor->bWordWrap != new) + { + editor->bWordWrap = new; + ME_RewrapRepaint(editor); + } } else FIXME("Unsupported yet non NULL device in EM_SETTARGETDEVICE\n"); break; @@ -3257,6 +3365,9 @@ LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM editor->bEmulateVersion10 = TRUE; editor->pBuffer->pLast->member.para.nCharOfs = 2; + assert(editor->pBuffer->pLast->prev->type == diRun); + assert(editor->pBuffer->pLast->prev->member.run.nFlags & MERF_ENDPARA); + editor->pBuffer->pLast->prev->member.run.nLF = 1; } return result; } @@ -3327,6 +3438,9 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in return 0; } + /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */ + if (editor->bEmulateVersion10) bCRLF = 0; + if (nStart) { int nLen = ME_StrLen(item->member.run.strText) - nStart; @@ -3345,6 +3459,8 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in while(nChars && item) { int nLen = ME_StrLen(item->member.run.strText); + if (item->member.run.nFlags & MERF_ENDPARA) + nLen = item->member.run.nCR + item->member.run.nLF; if (nLen > nChars) nLen = nChars; @@ -3357,16 +3473,30 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in nLen = 0; nChars = 0; } else { - *buffer = '\r'; if (bCRLF) { - *(++buffer) = '\n'; + /* richedit 2.0 case - actual line-break is \r but should report \r\n */ + assert(nLen == 1); + *buffer++ = '\r'; + *buffer = '\n'; /* Later updated by nLen==1 at the end of the loop */ nWritten++; } - assert(nLen == 1); - /* our end paragraph consists of 2 characters now */ - if (editor->bEmulateVersion10) - nChars--; + else + { + int i, j; + + /* richedit 2.0 verbatim has only \r. richedit 1.0 should honor encodings */ + i = 0; + while (nChars - i > 0 && i < item->member.run.nCR) + { + buffer[i] = '\r'; i++; + } + j = 0; + while (nChars - i - j > 0 && j < item->member.run.nLF) + { + buffer[i+j] = '\n'; j++; + } + } } } else @@ -3522,7 +3652,6 @@ int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar) int car_pos = 0; int text_pos=-1; int URLmin, URLmax = 0; - CHARRANGE url; FINDTEXTA ft; CHARFORMAT2W cur_format; CHARFORMAT2W default_format; @@ -3576,8 +3705,6 @@ int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar) } if (text_pos != -1) { - url.cpMin=text_pos; - url.cpMax=car_pos-1; ME_SetCharFormat(editor, text_pos, (URLmax-text_pos), &link); ME_RewrapRepaint(editor); break; diff --git a/reactos/dll/win32/riched20/editor.h b/reactos/dll/win32/riched20/editor.h index 0c0d324ab49..aa28060f643 100644 --- a/reactos/dll/win32/riched20/editor.h +++ b/reactos/dll/win32/riched20/editor.h @@ -189,9 +189,6 @@ void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, void ME_InsertEndRowFromCursor(ME_TextEditor *editor, int nCursor); BOOL ME_ArrowKey(ME_TextEditor *ed, int nVKey, BOOL extend, BOOL ctrl); -void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC); -void ME_DestroyContext(ME_Context *c, HWND release); -ME_Style *GetInsertStyle(ME_TextEditor *editor, int nCursor); void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para); void ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor, int *x, int *y, int *height); @@ -209,6 +206,10 @@ int ME_GetTextLengthEx(ME_TextEditor *editor, const GETTEXTLENGTHEX *how); ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor); BOOL ME_UpdateSelection(ME_TextEditor *editor, const ME_Cursor *pTempCursor); +/* context.c */ +void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC); +void ME_DestroyContext(ME_Context *c, HWND release); + /* wrap.c */ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor); void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor); @@ -220,7 +221,7 @@ int ME_twips2pointsY(ME_Context *c, int y); 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); +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); void ME_DumpParaStyle(ME_Paragraph *s); void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048]); @@ -272,11 +273,6 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd); void ME_DestroyEditor(ME_TextEditor *editor); void ME_SendOldNotify(ME_TextEditor *editor, int nCode); void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam); -ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi); -void ME_CommitUndo(ME_TextEditor *editor); -void ME_Undo(ME_TextEditor *editor); -void ME_Redo(ME_TextEditor *editor); -void ME_EmptyUndoStack(ME_TextEditor *editor); int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, BOOL bCRLF); ME_DisplayItem *ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset); void ME_StreamInFill(ME_InStream *stream); @@ -284,6 +280,13 @@ int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar); extern int me_debug; extern void DoWrap(ME_TextEditor *editor); +/* undo.c */ +ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi); +void ME_CommitUndo(ME_TextEditor *editor); +void ME_Undo(ME_TextEditor *editor); +void ME_Redo(ME_TextEditor *editor); +void ME_EmptyUndoStack(ME_TextEditor *editor); + /* writer.c */ LRESULT ME_StreamOutRange(ME_TextEditor *editor, DWORD dwFormat, int nStart, int nTo, EDITSTREAM *stream); LRESULT ME_StreamOut(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream); diff --git a/reactos/dll/win32/riched20/editstr.h b/reactos/dll/win32/riched20/editstr.h index 964f356dcf6..1e2c770fbbe 100644 --- a/reactos/dll/win32/riched20/editstr.h +++ b/reactos/dll/win32/riched20/editstr.h @@ -148,6 +148,7 @@ typedef struct tagME_Run 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; typedef struct tagME_Document { @@ -217,6 +218,7 @@ typedef struct tagME_UndoItem { ME_DisplayItem di; int nStart, nLen; + int nCR, nLF; /* used by diUndoSplitParagraph */ } ME_UndoItem; typedef struct tagME_TextBuffer @@ -329,6 +331,9 @@ typedef struct tagME_TextEditor /*for IME */ int imeStartIndex; DWORD selofs, linesel, sely; + + /* Track previous notified selection */ + CHARRANGE notified_cr; } ME_TextEditor; typedef struct tagME_Context diff --git a/reactos/dll/win32/riched20/list.c b/reactos/dll/win32/riched20/list.c index 14f9b178a40..951e7f36e2b 100644 --- a/reactos/dll/win32/riched20/list.c +++ b/reactos/dll/win32/riched20/list.c @@ -200,6 +200,8 @@ void ME_DumpDocument(ME_TextBuffer *buffer) case diRun: TRACE(" - Run(\"%s\", %d)\n", debugstr_w(pItem->member.run.strText->szData), pItem->member.run.nCharOfs); + if (pItem->member.run.nFlags & MERF_ENDPARA) + TRACE(" - Paragraph end: %d CR, %d LF\n", pItem->member.run.nCR, pItem->member.run.nLF); break; case diTextEnd: TRACE("End(ofs=%d)\n", pItem->member.para.nCharOfs); diff --git a/reactos/dll/win32/riched20/paint.c b/reactos/dll/win32/riched20/paint.c index d5cb3c7d504..91b71dc2aac 100644 --- a/reactos/dll/win32/riched20/paint.c +++ b/reactos/dll/win32/riched20/paint.c @@ -43,8 +43,7 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT * { BOOL bPaint = (rcUpdate == NULL); if (rcUpdate) - bPaint = c.pt.ybottom && - c.pt.y+item->member.para.nHeight>rcUpdate->top; + bPaint = c.pt.ybottom && ye>rcUpdate->top; if (bPaint) { ME_DrawParagraph(&c, item); @@ -85,8 +84,6 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT * rc.bottom = ye; FillRect(hDC, &rc, c.editor->hbrBackground); } - if (ys == c.pt.y) /* don't overwrite the top bar */ - ys++; } if (editor->nTotalLength != editor->nLastTotalLength) ME_SendRequestResize(editor, FALSE); @@ -109,9 +106,11 @@ void ME_UpdateRepaint(ME_TextEditor *editor) { /* Should be called whenever the contents of the control have changed */ ME_Cursor *pCursor; + BOOL wrappedParagraphs; + wrappedParagraphs = ME_WrapMarkedParagraphs(editor); if (!editor->bRedraw) return; - if (ME_WrapMarkedParagraphs(editor)) + if (wrappedParagraphs) ME_UpdateScrollBar(editor); /* Ensure that the cursor is visible */ @@ -371,47 +370,51 @@ int ME_GetParaLineSpace(ME_Context* c, ME_Paragraph* para) return sp * c->editor->nZoomNumerator / c->editor->nZoomDenominator; } -static int ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y) +static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT* bounds) { - int idx, border_width; - int ybefore, yafter; + int idx, border_width, top_border, bottom_border; RECT rc; - if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return 0; + 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)) + { + if (para->pFmt->wBorders & 0x00B0) + FIXME("Unsupported border flags %x\n", para->pFmt->wBorders); + border_width = ME_GetParaBorderWidth(c->editor, para->pFmt->wBorders); + if (para->pFmt->wBorders & 4) top_border = border_width; + if (para->pFmt->wBorders & 8) bottom_border = border_width; + } if (para->pFmt->dwMask & PFM_SPACEBEFORE) { rc.left = c->rcView.left; rc.right = c->rcView.right; rc.top = y; - ybefore = ME_twips2pointsY(c, para->pFmt->dySpaceBefore); - rc.bottom = y + ybefore; + bounds->top = ME_twips2pointsY(c, para->pFmt->dySpaceBefore); + rc.bottom = y + bounds->top + top_border; FillRect(c->hDC, &rc, c->editor->hbrBackground); } - else ybefore = 0; + if (para->pFmt->dwMask & PFM_SPACEAFTER) { rc.left = c->rcView.left; rc.right = c->rcView.right; rc.bottom = y + para->nHeight; - yafter = ME_twips2pointsY(c, para->pFmt->dySpaceAfter); - rc.top = rc.bottom - yafter; + bounds->bottom = ME_twips2pointsY(c, para->pFmt->dySpaceAfter); + rc.top = rc.bottom - bounds->bottom - bottom_border; FillRect(c->hDC, &rc, c->editor->hbrBackground); } - else yafter = 0; - border_width = 0; - idx = (para->pFmt->wBorders >> 8) & 0xF; if ((para->pFmt->dwMask & PFM_BORDER) && idx != 0 && (para->pFmt->wBorders & 0xF)) { int pen_width; COLORREF pencr; HPEN pen = NULL, oldpen = NULL; POINT pt; - if (para->pFmt->wBorders & 0x00B0) - FIXME("Unsupported border flags %x\n", para->pFmt->wBorders); - border_width = ME_GetParaBorderWidth(c->editor, para->pFmt->wBorders); - if (para->pFmt->wBorders & 64) /* autocolor */ pencr = GetSysColor(COLOR_WINDOWTEXT); else @@ -423,50 +426,66 @@ static int ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y) MoveToEx(c->hDC, 0, 0, &pt); /* before & after spaces are not included in border */ + + /* helper to draw the double lines in case of corner */ +#define DD(x) ((para->pFmt->wBorders & (x)) ? (pen_width + 1) : 0) + if (para->pFmt->wBorders & 1) { - MoveToEx(c->hDC, c->rcView.left, y + ybefore, NULL); - LineTo(c->hDC, c->rcView.left, y + para->nHeight - yafter); + MoveToEx(c->hDC, c->rcView.left, y + bounds->top, NULL); + LineTo(c->hDC, c->rcView.left, y + para->nHeight - bounds->bottom); if (border_details[idx].dble) { - MoveToEx(c->hDC, c->rcView.left + pen_width + 1, y + ybefore + pen_width + 1, NULL); - LineTo(c->hDC, c->rcView.left + pen_width + 1, y + para->nHeight - yafter - pen_width - 1); + rc.left = c->rcView.left + 1; + rc.right = rc.left + border_width; + rc.top = y + bounds->top; + rc.bottom = y + para->nHeight - bounds->bottom; + FillRect(c->hDC, &rc, c->editor->hbrBackground); + MoveToEx(c->hDC, c->rcView.left + pen_width + 1, y + bounds->top + DD(4), NULL); + LineTo(c->hDC, c->rcView.left + pen_width + 1, y + para->nHeight - bounds->bottom - DD(8)); } + bounds->left += border_width; } if (para->pFmt->wBorders & 2) { - MoveToEx(c->hDC, c->rcView.right, y + ybefore, NULL); - LineTo(c->hDC, c->rcView.right, y + para->nHeight - yafter); + MoveToEx(c->hDC, c->rcView.right - 1, y + bounds->top, NULL); + LineTo(c->hDC, c->rcView.right - 1, y + para->nHeight - bounds->bottom); if (border_details[idx].dble) { - MoveToEx(c->hDC, c->rcView.right - pen_width - 1, y + ybefore + pen_width + 1, NULL); - LineTo(c->hDC, c->rcView.right - pen_width - 1, y + para->nHeight - yafter - pen_width - 1); + rc.left = c->rcView.right - pen_width - 1; + rc.right = c->rcView.right - 1; + rc.top = y + bounds->top; + rc.bottom = y + para->nHeight - bounds->bottom; + FillRect(c->hDC, &rc, c->editor->hbrBackground); + MoveToEx(c->hDC, c->rcView.right - 1 - pen_width - 1, y + bounds->top + DD(4), NULL); + LineTo(c->hDC, c->rcView.right - 1 - pen_width - 1, y + para->nHeight - bounds->bottom - DD(8)); } + bounds->right += border_width; } if (para->pFmt->wBorders & 4) { - MoveToEx(c->hDC, c->rcView.left, y + ybefore, NULL); - LineTo(c->hDC, c->rcView.right, y + ybefore); + MoveToEx(c->hDC, c->rcView.left, y + bounds->top, NULL); + LineTo(c->hDC, c->rcView.right, y + bounds->top); if (border_details[idx].dble) { - MoveToEx(c->hDC, c->rcView.left + pen_width + 1, y + ybefore + pen_width + 1, NULL); - LineTo(c->hDC, c->rcView.right - pen_width - 1, y + ybefore + pen_width + 1); + MoveToEx(c->hDC, c->rcView.left + DD(1), y + bounds->top + pen_width + 1, NULL); + LineTo(c->hDC, c->rcView.right - DD(2), y + bounds->top + pen_width + 1); } + bounds->top += border_width; } if (para->pFmt->wBorders & 8) { - MoveToEx(c->hDC, c->rcView.left, y + para->nHeight - yafter - 1, NULL); - LineTo(c->hDC, c->rcView.right, y + para->nHeight - yafter - 1); + MoveToEx(c->hDC, c->rcView.left, y + para->nHeight - bounds->bottom - 1, NULL); + LineTo(c->hDC, c->rcView.right, y + para->nHeight - bounds->bottom - 1); if (border_details[idx].dble) { - MoveToEx(c->hDC, c->rcView.left + pen_width + 1, y + para->nHeight - yafter - 1 - pen_width - 1, NULL); - LineTo(c->hDC, c->rcView.right - pen_width - 1, y + para->nHeight - yafter - 1 - pen_width - 1); + MoveToEx(c->hDC, c->rcView.left + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL); + LineTo(c->hDC, c->rcView.right - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1); } + bounds->bottom += border_width; } +#undef DD MoveToEx(c->hDC, pt.x, pt.y, NULL); SelectObject(c->hDC, oldpen); DeleteObject(pen); } - return ybefore + - ((para->pFmt->dwMask & PFM_BORDER) && (para->pFmt->wBorders & 4) ? - border_width : 0); } void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { @@ -474,12 +493,11 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { ME_DisplayItem *p; ME_Run *run; ME_Paragraph *para = NULL; - RECT rc, rcPara; + RECT rc, rcPara, bounds; int y = c->pt.y; int height = 0, baseline = 0, no=0, pno = 0; int xs = 0, xe = 0; BOOL visible = FALSE; - int nMargWidth = 0; c->pt.x = c->rcView.left; rcPara.left = c->rcView.left; @@ -489,12 +507,11 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { case diParagraph: para = &p->member.para; assert(para); - nMargWidth = ME_twips2pointsX(c, para->pFmt->dxStartIndent); - if (pno != 0) - nMargWidth += ME_twips2pointsX(c, para->pFmt->dxOffset); - xs = c->rcView.left+nMargWidth; + pno = 0; + xs = c->rcView.left + ME_twips2pointsX(c, para->pFmt->dxStartIndent); xe = c->rcView.right - ME_twips2pointsX(c, para->pFmt->dxRightIndent); - y += ME_DrawParaDecoration(c, para, y); + ME_DrawParaDecoration(c, para, y, &bounds); + y += bounds.top; break; case diStartRow: y += height; @@ -503,16 +520,16 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { visible = RectVisible(c->hDC, &rcPara); if (visible) { /* left margin */ - rc.left = c->rcView.left; - rc.right = c->rcView.left+nMargWidth; + rc.left = c->rcView.left + bounds.left; + rc.right = xs; rc.top = y; rc.bottom = y+p->member.row.nHeight; FillRect(c->hDC, &rc, c->editor->hbrBackground); /* right margin */ rc.left = xe; - rc.right = c->rcView.right; + rc.right = c->rcView.right - bounds.right; FillRect(c->hDC, &rc, c->editor->hbrBackground); - rc.left = c->rcView.left+nMargWidth; + rc.left = xs; rc.right = xe; FillRect(c->hDC, &rc, c->editor->hbrBackground); } @@ -528,7 +545,8 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { height = p->member.row.nHeight; baseline = p->member.row.nBaseline; - pno++; + if (!pno++) + xe += ME_twips2pointsX(c, para->pFmt->dxOffset); break; case diRun: assert(para); @@ -595,7 +613,7 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type) switch (type) { case 1: - /*Scroll absolutly*/ + /*Scroll absolutely*/ si.nPos = value; break; case 2: @@ -628,7 +646,7 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type) void ME_UpdateScrollBar(ME_TextEditor *editor) { - /* Note that this is the only funciton that should ever call SetScrolLInfo + /* Note that this is the only function that should ever call SetScrolLInfo * with SIF_PAGE or SIF_RANGE. SetScrollPos and SetScrollRange should never * be used at all. */ diff --git a/reactos/dll/win32/riched20/para.c b/reactos/dll/win32/riched20/para.c index b2f8e6c117b..8380a79b425 100644 --- a/reactos/dll/win32/riched20/para.c +++ b/reactos/dll/win32/riched20/para.c @@ -73,6 +73,8 @@ void ME_MakeFirstParagraph(ME_TextEditor *editor) run = ME_MakeRun(style, ME_MakeString(wszParagraphSign), MERF_ENDPARA); run->member.run.nCharOfs = 0; + run->member.run.nCR = 1; + run->member.run.nLF = (editor->bEmulateVersion10) ? 1 : 0; ME_InsertBefore(text->pLast, para); ME_InsertBefore(text->pLast, run); @@ -110,7 +112,7 @@ void ME_MarkForPainting(ME_TextEditor *editor, ME_DisplayItem *first, const ME_D } /* split paragraph at the beginning of the run */ -ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style) +ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style, int numCR, int numLF) { ME_DisplayItem *next_para = NULL; ME_DisplayItem *run_para = NULL; @@ -119,10 +121,12 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME ME_UndoItem *undo = NULL; int ofs; ME_DisplayItem *pp; - int end_len = (editor->bEmulateVersion10 ? 2 : 1); + int end_len = numCR + numLF; assert(run->type == diRun); + end_run->member.run.nCR = numCR; + end_run->member.run.nLF = numLF; run_para = ME_GetParagraph(run); assert(run_para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); @@ -204,7 +208,7 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp) ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp; int i, shift; ME_UndoItem *undo = NULL; - int end_len = (editor->bEmulateVersion10 ? 2 : 1); + int end_len; assert(tp->type == diParagraph); assert(tp->member.para.next_para); @@ -212,6 +216,15 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp) pNext = tp->member.para.next_para; + /* Need to locate end-of-paragraph run here, in order to know end_len */ + pRun = ME_FindItemBack(pNext, diRunOrParagraph); + + assert(pRun); + assert(pRun->type == diRun); + assert(pRun->member.run.nFlags & MERF_ENDPARA); + + end_len = pRun->member.run.nCR + pRun->member.run.nLF; + { /* null char format operation to store the original char format for the ENDPARA run */ CHARFORMAT2W fmt; @@ -222,18 +235,16 @@ ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp) 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; } shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - end_len; - pRun = ME_FindItemBack(pNext, diRunOrParagraph); pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); - - assert(pRun); - assert(pRun->type == diRun); - assert(pRun->member.run.nFlags & MERF_ENDPARA); + assert(pFirstRunInNext->type == diRun); /* if some cursor points at end of paragraph, make it point to the first diff --git a/reactos/dll/win32/riched20/reader.c b/reactos/dll/win32/riched20/reader.c index 1ecca08f6e2..5ee9fa27f9b 100644 --- a/reactos/dll/win32/riched20/reader.c +++ b/reactos/dll/win32/riched20/reader.c @@ -2605,7 +2605,8 @@ static void SpecialChar (RTF_Info *info) case rtfSect: case rtfRow: case rtfPar: - RTFPutUnicodeChar (info, '\n'); + RTFPutUnicodeChar (info, '\r'); + if (info->editor->bEmulateVersion10) RTFPutUnicodeChar (info, '\n'); break; case rtfNoBrkSpace: RTFPutUnicodeChar (info, 0x00A0); diff --git a/reactos/dll/win32/riched20/run.c b/reactos/dll/win32/riched20/run.c index e193bd22bc8..c49eb16e670 100644 --- a/reactos/dll/win32/riched20/run.c +++ b/reactos/dll/win32/riched20/run.c @@ -127,8 +127,10 @@ void ME_CheckCharOffsets(ME_TextEditor *editor) p->member.run.nFlags, p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects); assert(ofs == p->member.run.nCharOfs); - if (p->member.run.nFlags & MERF_ENDPARA) - ofs += (editor->bEmulateVersion10 ? 2 : 1); + if (p->member.run.nFlags & MERF_ENDPARA) { + assert(p->member.run.nCR + p->member.run.nLF > 0); + ofs += p->member.run.nCR + p->member.run.nLF; + } else ofs += ME_StrLen(p->member.run.strText); break; @@ -209,10 +211,12 @@ void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem ** } *ppRun = pNext; } - /* the handling of bEmulateVersion10 may be a source of many bugs, I'm afraid */ - eollen = (editor->bEmulateVersion10 ? 2 : 1); + /* Recover proper character length of this line break */ + eollen = (*ppRun)->member.run.nCR + (*ppRun)->member.run.nLF; if (nCharOfs >= nParaOfs + (*ppRun)->member.run.nCharOfs && nCharOfs < nParaOfs + (*ppRun)->member.run.nCharOfs + eollen) { + /* FIXME: Might cause problems when actually requiring an offset in the + middle of a run that is considered a single line break */ *pOfs = 0; return; } diff --git a/reactos/dll/win32/riched20/style.c b/reactos/dll/win32/riched20/style.c index 43b2a96ad46..b483be04863 100644 --- a/reactos/dll/win32/riched20/style.c +++ b/reactos/dll/win32/riched20/style.c @@ -327,10 +327,9 @@ ME_LogFontFromStyle(ME_Context* c, LOGFONTW *lf, const ME_Style *s) void ME_CharFormatFromLogFont(HDC hDC, const LOGFONTW *lf, CHARFORMAT2W *fmt) { - int rx, ry; + int ry; ME_InitCharFormat2W(fmt); - rx = GetDeviceCaps(hDC, LOGPIXELSX); ry = GetDeviceCaps(hDC, LOGPIXELSY); lstrcpyW(fmt->szFaceName, lf->lfFaceName); fmt->dwEffects = 0; @@ -341,7 +340,7 @@ void ME_CharFormatFromLogFont(HDC hDC, const LOGFONTW *lf, CHARFORMAT2W *fmt) if (lf->lfItalic) fmt->dwEffects |= CFM_ITALIC; if (lf->lfUnderline) fmt->dwEffects |= CFM_UNDERLINE; /* notice that if a logfont was created with underline due to CFM_LINK, this - would add an erronious CFM_UNDERLINE. This isn't currently ever a problem */ + would add an erroneous CFM_UNDERLINE. This isn't currently ever a problem. */ if (lf->lfStrikeOut) fmt->dwEffects |= CFM_STRIKEOUT; fmt->bPitchAndFamily = lf->lfPitchAndFamily; fmt->bCharSet = lf->lfCharSet; diff --git a/reactos/dll/win32/riched20/undo.c b/reactos/dll/win32/riched20/undo.c index 426daf4ba1f..a26f0e07eb1 100644 --- a/reactos/dll/win32/riched20/undo.c +++ b/reactos/dll/win32/riched20/undo.c @@ -56,6 +56,7 @@ ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_Disp else { ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem); + ((ME_UndoItem *)pItem)->nCR = ((ME_UndoItem *)pItem)->nLF = -1; switch(type) { case diUndoEndTransaction: @@ -225,7 +226,10 @@ static void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem) ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); if (tmp.nOffset) tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); - new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style); + assert(pUItem->nCR >= 0); + assert(pUItem->nLF >= 0); + new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style, + pUItem->nCR, pUItem->nLF); assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); *new_para->member.para.pFmt = *pItem->member.para.pFmt; break; @@ -247,7 +251,7 @@ void ME_Undo(ME_TextEditor *editor) { if (!editor->pUndoStack) return; - /* watch out for uncommited transactions ! */ + /* watch out for uncommitted transactions ! */ assert(editor->pUndoStack->type == diUndoEndTransaction); editor->nUndoMode = umAddToRedo; @@ -280,7 +284,7 @@ void ME_Redo(ME_TextEditor *editor) { if (!editor->pRedoStack) return; - /* watch out for uncommited transactions ! */ + /* watch out for uncommitted transactions ! */ assert(editor->pRedoStack->type == diUndoEndTransaction); editor->nUndoMode = umAddBackToUndo; diff --git a/reactos/dll/win32/riched20/wrap.c b/reactos/dll/win32/riched20/wrap.c index a657b037346..da5fef7359f 100644 --- a/reactos/dll/win32/riched20/wrap.c +++ b/reactos/dll/win32/riched20/wrap.c @@ -470,11 +470,9 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { ME_DisplayItem *item; ME_Context c; BOOL bModified = FALSE; - int yStart = -1, yEnd = -1; + int yStart = -1; ME_InitContext(&c, editor, GetDC(editor->hWnd)); - c.pt.x = 0; - c.pt.y = 0; editor->nHeight = 0; item = editor->pBuffer->pFirst->next; while(item != editor->pBuffer->pLast) { @@ -499,8 +497,6 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { bModified = bModified | bRedraw; c.pt.y += item->member.para.nHeight; - if (bRedraw) - yEnd = c.pt.y; item = item->member.para.next_para; } editor->sizeWindow.cx = c.rcView.right-c.rcView.left; @@ -510,6 +506,14 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) { ME_DestroyContext(&c, editor->hWnd); + /* Each paragraph may contain multiple rows, which should be scrollable, even + if the containing paragraph has nYPos == 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); + } + if (bModified || editor->nTotalLength < editor->nLastTotalLength) ME_InvalidateMarkedParagraphs(editor); return bModified; diff --git a/reactos/dll/win32/riched20/writer.c b/reactos/dll/win32/riched20/writer.c index 30e9c936e1b..ce9e8537d7f 100644 --- a/reactos/dll/win32/riched20/writer.c +++ b/reactos/dll/win32/riched20/writer.c @@ -711,9 +711,9 @@ ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int nC if (!ME_StreamOutPrint(pStream, "\r\n\\par")) return FALSE; } - nChars--; - if (editor->bEmulateVersion10 && nChars) - nChars--; + /* Skip as many characters as required by current line break */ + nChars -= (p->member.run.nCR <= nChars) ? p->member.run.nCR : nChars; + nChars -= (p->member.run.nLF <= nChars) ? p->member.run.nLF : nChars; } else if (p->member.run.nFlags & MERF_ENDROW) { if (!ME_StreamOutPrint(pStream, "\\line \r\n")) return FALSE; @@ -775,10 +775,39 @@ ME_StreamOutText(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int n if (item->member.run.nFlags & MERF_ENDPARA) { static const WCHAR szEOL[2] = { '\r', '\n' }; - if (dwFormat & SF_UNICODE) - success = ME_StreamOutMove(pStream, (const char *)szEOL, sizeof(szEOL)); - else - success = ME_StreamOutMove(pStream, "\r\n", 2); + if (!editor->bEmulateVersion10) { + /* richedit 2.0 - all line breaks are \r\n */ + if (dwFormat & SF_UNICODE) + success = ME_StreamOutMove(pStream, (const char *)szEOL, sizeof(szEOL)); + else + success = ME_StreamOutMove(pStream, "\r\n", 2); + assert(nLen == 1); + } else { + int i; int tnLen; + + /* richedit 1.0 - need to honor actual \r and \n amounts */ + nLen = item->member.run.nCR + item->member.run.nLF; + if (nLen > nChars) + nLen = nChars; + tnLen = nLen; + + i = 0; + while (tnLen > 0 && i < item->member.run.nCR) { + if (dwFormat & SF_UNICODE) + success = ME_StreamOutMove(pStream, (const char *)(&szEOL[0]), sizeof(WCHAR)); + else + success = ME_StreamOutMove(pStream, "\r", 1); + tnLen--; i++; + } + i = 0; + while (tnLen > 0 && i < item->member.run.nLF) { + if (dwFormat & SF_UNICODE) + success = ME_StreamOutMove(pStream, (const char *)(&szEOL[1]), sizeof(WCHAR)); + else + success = ME_StreamOutMove(pStream, "\n", 1); + tnLen--; i++; + } + } } else { if (dwFormat & SF_UNICODE) success = ME_StreamOutMove(pStream, (const char *)(item->member.run.strText->szData + nStart), @@ -800,8 +829,6 @@ ME_StreamOutText(ME_TextEditor *editor, ME_OutStream *pStream, int nStart, int n } nChars -= nLen; - if (editor->bEmulateVersion10 && nChars && item->member.run.nFlags & MERF_ENDPARA) - nChars--; nStart = 0; item = ME_FindItemFwd(item, diRun); }