riched20: corrected EM_GETLINE and wrong hWnd patches (combined into one)

Krzysztof Foltman wdev at foltman.com
Fri Jun 30 15:51:23 CDT 2006


ChangeLog:
  * EM_GETLINE support (roughly based on code by Thomas Kho, although
there are important changes, for example, it works correctly in RichEdit 
1.0 emulation mode) (bug #4305)
  * Workaround for wrong window handles (bug# 4452)
  * EM_LINELENGTH handles paragraph ends properly (bug #5375)

I'm sending it as one patch, just in case it gets rejected again - 
trying to split the patch seems to give me some mysterious git problems.

Krzysztof

-------------- next part --------------
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 9a2b966..4ae3be6 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -52,7 +52,7 @@
   - EM_GETIMESTATUS
   - EM_GETLANGOPTIONS 2.0
   - EM_GETLIMITTEXT
-  - EM_GETLINE        
+  + EM_GETLINE
   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
   + EM_GETMODIFY
   - EM_GETOLEINTERFACE
@@ -1060,6 +1060,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD n
         {
           CHARFORMAT2W chf;
           char buf[2048];
+          chf.cbSize = sizeof(chf);
           
           ME_GetSelectionCharFormat(editor, &chf);
           ME_DumpStyleToBuf(&chf, buf);
@@ -1393,11 +1394,16 @@ get_msg_name(UINT msg)
  */
 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
   SCROLLINFO si;
-  ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
-  
+  ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
+
   TRACE("hWnd %p msg %04x (%s) %08x %08lx\n",
         hWnd, msg, get_msg_name(msg), wParam, lParam);
   
+  if (!editor && msg != WM_NCCREATE && msg != WM_NCDESTROY) {
+    ERR("RichEditANSIWndProc called with invalid hWnd %p - application bug?\n", hWnd);
+    return 0; 
+  }
+    
   switch(msg) {
   
   UNSUPPORTED_MSG(EM_DISPLAYBAND)
@@ -1411,7 +1417,6 @@ LRESULT WINAPI RichEditANSIWndProc(HWND 
   /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
   UNSUPPORTED_MSG(EM_GETLIMITTEXT)
-  UNSUPPORTED_MSG(EM_GETLINE)
   /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
   UNSUPPORTED_MSG(EM_GETPASSWORDCHAR)
   UNSUPPORTED_MSG(EM_GETREDONAME)
@@ -2016,6 +2021,78 @@ LRESULT WINAPI RichEditANSIWndProc(HWND 
       return nChars;
     }
   }
+  case EM_GETLINE:
+  {
+    ME_DisplayItem *run;
+    ME_String *strText;
+    int nMaxChars = (int) *(WORD *) lParam;
+    BOOL bIsUnicode = IsWindowUnicode(hWnd);
+    int nEndChars;
+    int nWrittenChars = 0;
+    char *dest = (char *) lParam;
+
+    TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
+          bIsUnicode ? "Unicode" : "Ansi");
+
+    run = ME_FindRowWithNumber(editor, (int) wParam);
+    if (run == NULL)
+      return 0;
+
+    while ((run = ME_FindItemFwd(run, diRunOrStartRow))
+           && !(run->member.run.nFlags & MERF_ENDPARA)
+           && nWrittenChars < nMaxChars)
+    {
+      int nCopy;
+      if (run->type != diRun)
+        break;
+
+      strText = run->member.run.strText;
+      nCopy = min(nMaxChars - nWrittenChars, strText->nLen);
+
+      if (bIsUnicode) {
+        memcpy(dest, strText->szData, nCopy * sizeof(WCHAR));
+        dest += nCopy * sizeof(WCHAR);
+      } else {
+        WideCharToMultiByte(CP_ACP, 0, strText->szData, -1, dest, nCopy, NULL,
+                            NULL);
+        dest += nCopy;
+      }
+      nWrittenChars += nCopy;
+    }
+
+    /* append "\r\0", space allowing */
+    nEndChars =  min(nMaxChars - nWrittenChars, editor->bEmulateVersion10? 3 : 2);
+    if (nEndChars) {
+      if (bIsUnicode) {
+        WCHAR *wdest = (WCHAR *)dest;
+        
+        *wdest++ = '\r', nWrittenChars++, nEndChars--;
+        
+        if (nEndChars > 0 && editor->bEmulateVersion10)
+          *wdest++ = '\n', nWrittenChars++, nEndChars--;
+          
+        if (nEndChars > 0)
+          *wdest++ = '\0';
+          
+      } else {
+        *dest++ = '\r', nWrittenChars++, nEndChars--;
+
+        if (nEndChars > 0 && editor->bEmulateVersion10)
+          *dest++ = '\n', nWrittenChars++, nEndChars--;
+          
+        if (nEndChars > 0)
+          *dest++ = '\0';        
+      }
+      assert(nEndChars == 0 || nEndChars == 1);
+    }
+
+    if (nEndChars == 1)
+      TRACE("EM_GETLINE: wrote %d chars, including terminating NUL\n", nWrittenChars + 1);
+    else
+      TRACE("EM_GETLINE: wrote %d chars, no terminating NUL\n", nWrittenChars);
+
+    return nWrittenChars;
+  }
   case EM_GETLINECOUNT:
   {
     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
@@ -2073,12 +2150,11 @@ LRESULT WINAPI RichEditANSIWndProc(HWND 
     item = ME_FindItemAtOffset(editor, diRun, wParam, NULL);
     item = ME_RowStart(item);
     nThisLineOfs = ME_CharOfsFromRunOfs(editor, ME_FindItemFwd(item, diRun), 0);
-    item_end = ME_FindItemFwd(item, diStartRow);
-    if (item_end)
+    item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
+    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);
+      nNextLineOfs = item_end->member.para.nCharOfs - (editor->bEmulateVersion10?2:1);
     nChars = nNextLineOfs - nThisLineOfs;
     TRACE("EM_LINELENGTH(%d)==%d\n",wParam, nChars);
     return nChars;
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index c71ca7f..da6e5b0 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -189,6 +189,67 @@ static void test_EM_FINDTEXT(void)
   DestroyWindow(hwndRichEdit);
 }
 
+struct getline_s {
+  int line;
+  size_t buffer_len;
+  char *text;
+} gl[] = {
+  {0, 10, "foo bar\r"},
+  {1, 10, "\r"},
+  {2, 10, "bar\r"},
+  {3, 10, "\r"},
+
+  /* Buffer smaller than line length */
+  {0, 2, "foo bar\r"},
+  {0, 1, "foo bar\r"},
+  {0, 0, "foo bar\r"}
+};
+
+static void test_EM_GETLINE(void)
+{
+  int i;
+  HWND hwndRichEdit = new_richedit(NULL);
+  static const int nBuf = 1024;
+  char dest[1024], origdest[1024];
+  const char text[] = "foo bar\n"
+      "\n"
+      "bar\n";
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
+
+  memset(origdest, 0xBB, nBuf);
+  for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
+  {
+    int nCopied;
+    int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
+    int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
+    memset(dest, 0xBB, nBuf);
+    *(WORD *) dest = gl[i].buffer_len;
+
+    /* EM_GETLINE appends a "\r\0" to the end of the line
+     * nCopied counts up to and including the '\r' */
+    nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
+    ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
+       expected_nCopied);
+    /* two special cases since a parameter is passed via dest */
+    if (gl[i].buffer_len == 0)
+      ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
+         "buffer_len=0\n");
+    else if (gl[i].buffer_len == 1)
+      ok(dest[0] == gl[i].text[0] && !dest[1] &&
+         !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
+    else {
+      ok(!strncmp(dest, gl[i].text, expected_bytes_written),
+         "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
+      ok(!strncmp(dest + expected_bytes_written, origdest
+                  + expected_bytes_written, nBuf - expected_bytes_written),
+         "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
+    }
+  }
+
+  DestroyWindow(hwndRichEdit);
+}
+
 static int get_scroll_pos_y(HWND hwnd)
 {
   POINT p = {-1, -1};
@@ -822,6 +883,7 @@ START_TEST( editor )
   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
   test_EM_FINDTEXT();
+  test_EM_GETLINE();
   test_EM_SCROLLCARET();
   test_EM_SCROLL();
   test_EM_SETTEXTMODE();


More information about the wine-patches mailing list