Sync riched20 to Wine 1.0-rc1
authorColin Finck <colin@reactos.org>
Sat, 10 May 2008 19:50:28 +0000 (19:50 +0000)
committerColin Finck <colin@reactos.org>
Sat, 10 May 2008 19:50:28 +0000 (19:50 +0000)
Samuel SerapiĆ³n got some apps like 7-Zip and PowerISO installed with the new version.

svn path=/trunk/; revision=33419

13 files changed:
reactos/dll/win32/riched20/caret.c
reactos/dll/win32/riched20/editor.c
reactos/dll/win32/riched20/editor.h
reactos/dll/win32/riched20/editstr.h
reactos/dll/win32/riched20/list.c
reactos/dll/win32/riched20/paint.c
reactos/dll/win32/riched20/para.c
reactos/dll/win32/riched20/reader.c
reactos/dll/win32/riched20/run.c
reactos/dll/win32/riched20/style.c
reactos/dll/win32/riched20/undo.c
reactos/dll/win32/riched20/wrap.c
reactos/dll/win32/riched20/writer.c

index a6a6217..b2172ac 100644 (file)
@@ -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
index 39827bc..1ae0e23 100644 (file)
   + 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;
index 0c0d324..aa28060 100644 (file)
@@ -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);
index 964f356..1e2c770 100644 (file)
@@ -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
index 14f9b17..951e7f3 100644 (file)
@@ -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);
index d5cb3c7..91b71dc 100644 (file)
@@ -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.y<rcUpdate->bottom && 
-          c.pt.y+item->member.para.nHeight>rcUpdate->top;
+        bPaint = c.pt.y<rcUpdate->bottom && 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. */
   
index b2f8e6c..8380a79 100644 (file)
@@ -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
index 1ecca08..5ee9fa2 100644 (file)
@@ -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);
index e193bd2..c49eb16 100644 (file)
@@ -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;
       }
index 43b2a96..b483be0 100644 (file)
@@ -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;
index 426daf4..a26f0e0 100644 (file)
@@ -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;
index a657b03..da5fef7 100644 (file)
@@ -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;
index 30e9c93..ce9e853 100644 (file)
@@ -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);
   }