[PATCH 1/2] riched20: Implement ITextSelection::GetText. (try 3)

Jactry Zeng jactry92 at gmail.com
Mon Jul 14 20:18:37 CDT 2014


Hi Huw,

diTextEnd is useful for append a '\r' to the last paragraph.
But because of bug of EM_SETSEL or EM_GETSEL, this feature
still cannot be implemented completely.
For example:
When the text in Rich Edit control is: "TestSomeText"
                                                       01234567890123
We cannot distinguish between SendMessageA(w, EM_SETSEL, 0, 12) and
SendMessageA(w, EM_SETSEL, 0, 13).
Attachment is the patch, I just added wine_todo for failed tests cause by
this bug.
But I still have no idea about fixing this bug. Could you give me some
suggestion?
Or just mark them as todo now is OK?

Thank you!


2014-07-07 16:38 GMT+08:00 Huw Davies <huw at codeweavers.com>:

> On Fri, Jul 04, 2014 at 07:00:16PM +0800, Jactry Zeng wrote:
> > A few weeks ago, I have made some tests about this problem. And it seem
> is an
> > unimplement feature of Rich edit control and should not be fixed in
> ME_GetTextW
> > or ITextSeletction::GetText simply:
> >
> > 1. end-of-story is different from end-of-paragraph
> > From document of ITextDocument::GetStoryCount: Rich edit controls have
> only one
> > story.[1]
> > So ‘story’ may is an unit like 'paragraph' in Rich edit control. We may
> need
> > add a MERF_ENDSTORY to implement it.[2]
> >
> > 2. What does the end-of-story looks like?
> > When we run the wordpad with native riched20, we can use ‘ctrl + shitf +
> →’ to
> > select the ‘space’ at the end of the Rich edit control. But we cannot
> set the
> > cursor behind it.(a picture here[3])
> > And wine’s riched20 didn’t implement this ‘space’. This 'space' may is
> the end-
> > of-story.
> >
> > A crash will happen when we copy the ‘space’ from a native Rich edit
> control to
> > a builtin one.(backtrace.txt[4])
> >
> > BTW, there is a know bug of EM_SETSEL: cannot select the last character
> of Rich
> > edit control.[5]
> >
> > 3.
> > From the last paragraph of this blog:
> http://blogs.msdn.com/b/murrays/archive/
> > 2008/11/22/paragraphs-and-paragraph-formatting.aspx
> > It is sure there is a final EOP in a Rich edit control.
> >
> > There seems to be every indication that we should implement the final
> EOP of
> > Rich edit control rather than just append a ‘\r’ in
> ITextSelection::GetText.
> > So I just let a FIXME comment there. I am not sure if I am right.
> > Do you have any suggestion?
>
> Well first of all, if there are bugs with the underlying richedit,
> these need fixing first.  Note, that every richedit consists of at
> least one paragraph (and hence one final EOP).
>
> I was suggesting adding to ME_GetTextW the ability to append a '\r' if it
> were passed an appropriate flag, of course this wouldn't be the behaviour
> used for its current callers.  There's already a diTextEnd item in the
> display list to mark the end of the document, so there should be no
> need to add a new end-of-story marker.  Most of the iterator functions
> stop when this is reached, so you tend not to actually see it, but
> that shouldn't be a problem.
>
> Huw.
>



-- 
Regards,
Jactry Zeng
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20140715/405523e0/attachment-0001.html>
-------------- next part --------------
diff --git a/dlls/riched20/clipboard.c b/dlls/riched20/clipboard.c
index f7514a2..250a233 100644
--- a/dlls/riched20/clipboard.c
+++ b/dlls/riched20/clipboard.c
@@ -352,7 +352,7 @@ static HGLOBAL get_unicode_text(ME_TextEditor *editor, const ME_Cursor *start, i
 
     ret = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR) * (nChars + pars + 1));
     data = GlobalLock(ret);
-    ME_GetTextW(editor, data, nChars + pars, start, nChars, TRUE);
+    ME_GetTextW(editor, data, nChars + pars, start, nChars, TRUE, FALSE);
     GlobalUnlock(ret);
     return ret;
 }
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 43d74ec..5d124c9 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -1630,7 +1630,7 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre
           ME_Cursor linebreakCursor = *selEnd;
 
           ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize);
-          ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE);
+          ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, FALSE, FALSE);
           if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
             ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
           }
@@ -1968,7 +1968,7 @@ static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
     if (ex->codepage == CP_UNICODE)
     {
       return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
-                         &start, nChars, ex->flags & GT_USECRLF);
+                         &start, nChars, ex->flags & GT_USECRLF, FALSE);
     }
     else
     {
@@ -1985,7 +1985,7 @@ static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
       buflen = min(crlfmul * nChars, ex->cb - 1);
       buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
 
-      nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF);
+      nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF, FALSE);
       rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
                                (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
       if (rc) rc--; /* do not count 0 terminator */
@@ -2000,12 +2000,12 @@ static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
 {
     if (!strText) return 0;
     if (unicode) {
-      return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE);
+      return ME_GetTextW(editor, strText, INT_MAX, start, nLen, FALSE, FALSE);
     } else {
       int nChars;
       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
       if (!p) return 0;
-      nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE);
+      nChars = ME_GetTextW(editor, p, nLen, start, nLen, FALSE, FALSE);
       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
                           nLen+1, NULL, NULL);
       FREE_OBJ(p);
@@ -4747,7 +4747,8 @@ void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
  * The written text is always NULL terminated.
  */
 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
-                const ME_Cursor *start, int srcChars, BOOL bCRLF)
+                const ME_Cursor *start, int srcChars, BOOL bCRLF,
+                BOOL bEOP)
 {
   ME_DisplayItem *pRun, *pNextRun;
   const WCHAR *pStart = buffer;
@@ -4765,7 +4766,6 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
   nLen = pRun->member.run.len - start->nOffset;
   str = get_text( &pRun->member.run, start->nOffset );
 
-  /* No '\r' is appended to the last paragraph. */
   while (srcChars && buflen && pNextRun)
   {
     int nFlags = pRun->member.run.nFlags;
@@ -4797,7 +4797,15 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
     nLen = pRun->member.run.len;
     str = get_text( &pRun->member.run, 0 );
   }
-  *buffer = 0;
+  /* '\r' is appended to the last paragraph. */
+  if (pRun->next->type == diTextEnd && bEOP)
+  {
+    *buffer = '\r';
+    buffer ++;
+    *buffer = 0;
+  }
+  else
+    *buffer = 0;
   return buffer - pStart;
 }
 
@@ -5041,7 +5049,7 @@ static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, i
   WCHAR bufferW[MAX_PREFIX_LEN + 1];
   unsigned int i;
 
-  ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE);
+  ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, FALSE, FALSE);
   for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++)
   {
     if (nChars < prefixes[i].length) continue;
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index af7291f..edbac8e 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -252,7 +252,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
                          LPARAM lParam, BOOL unicode, HRESULT* phresult) DECLSPEC_HIDDEN;
 void ME_SendOldNotify(ME_TextEditor *editor, int nCode) DECLSPEC_HIDDEN;
 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
-                const ME_Cursor *start, int srcChars, BOOL bCRLF) DECLSPEC_HIDDEN;
+                const ME_Cursor *start, int srcChars, BOOL bCRLF, BOOL bEOP) DECLSPEC_HIDDEN;
 void ME_RTFCharAttrHook(struct _RTF_Info *info) DECLSPEC_HIDDEN;
 void ME_RTFParAttrHook(struct _RTF_Info *info) DECLSPEC_HIDDEN;
 void ME_RTFTblAttrHook(struct _RTF_Info *info) DECLSPEC_HIDDEN;
diff --git a/dlls/riched20/richole.c b/dlls/riched20/richole.c
index 6f2d579..4530384 100644
--- a/dlls/riched20/richole.c
+++ b/dlls/riched20/richole.c
@@ -783,11 +783,34 @@ static HRESULT WINAPI ITextSelection_fnInvoke(
 static HRESULT WINAPI ITextSelection_fnGetText(ITextSelection *me, BSTR *pbstr)
 {
     ITextSelectionImpl *This = impl_from_ITextSelection(me);
+    ME_Cursor *start = NULL, *end = NULL;
+    int nChars, bEOP;
+
     if (!This->reOle)
         return CO_E_RELEASED;
+    TRACE("%p\n", pbstr);
+    if (!pbstr)
+        return E_INVALIDARG;
 
-    FIXME("not implemented\n");
-    return E_NOTIMPL;
+    ME_GetSelection(This->reOle->editor, &start, &end);
+    nChars = ME_GetCursorOfs(end) - ME_GetCursorOfs(start);
+    bEOP = (end->pRun->next->type == diTextEnd);
+    if (bEOP)
+        nChars ++;
+    if (!nChars)
+    {
+        *pbstr = NULL;
+        return S_OK;
+    }
+
+    *pbstr = SysAllocStringLen(NULL, nChars);
+    if (!*pbstr)
+        return E_OUTOFMEMORY;
+
+    ME_GetTextW(This->reOle->editor, *pbstr, nChars, start, nChars, FALSE, bEOP);
+    TRACE("%s\n", wine_dbgstr_w(*pbstr));
+
+    return S_OK;
 }
 
 static HRESULT WINAPI ITextSelection_fnSetText(ITextSelection *me, BSTR bstr)
diff --git a/dlls/riched20/tests/richole.c b/dlls/riched20/tests/richole.c
index d10c063..d5cd286 100644
--- a/dlls/riched20/tests/richole.c
+++ b/dlls/riched20/tests/richole.c
@@ -402,6 +402,98 @@ static void test_ITextDocument_Open(void)
   VariantClear(&testfile);
 }
 
+static void test_ITextSelection_GetText(void)
+{
+  HWND w;
+  IRichEditOle *reOle = NULL;
+  ITextDocument *txtDoc = NULL;
+  ITextSelection *txtSel = NULL;
+  HRESULT hres;
+  BSTR bstr = NULL;
+  int first, lim;
+  static const CHAR test_text1[] = "TestSomeText";
+  static const WCHAR bufW1[] = {'T', 'e', 's', 't', 0};
+  static const WCHAR bufW2[] = {'T', 'e', 'x', 't', '\r', 0};
+  static const WCHAR bufW3[] = {'T', 'e', 'x', 't', 0};
+  static const WCHAR bufW4[] = {'T', 'e', 's', 't', 'S', 'o', 'm',
+                                'e', 'T', 'e', 'x', 't', '\r', 0};
+  static const WCHAR bufW5[] = {'\r', 0};
+  SYSTEM_INFO sysinfo;
+  int is64bit;
+  GetSystemInfo(&sysinfo);
+  is64bit = (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64);
+
+  create_interfaces(&w, &reOle, &txtDoc, &txtSel);
+  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
+
+  first = 0, lim = 4;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!lstrcmpW(bstr, bufW1), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  first = 4, lim = 0;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!lstrcmpW(bstr, bufW1), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  first = 1, lim = 1;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!bstr, "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  if(is64bit)
+    win_skip("native 64bit riched20 can't support a NULL parameter\n");
+  else
+    {
+      hres = ITextSelection_GetText(txtSel, NULL);
+      ok(hres == E_INVALIDARG, "ITextSelection_GetText\n");
+    }
+
+  first = 8, lim = 12;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  todo_wine ok(!lstrcmpW(bstr, bufW3), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  /* FIXME: a '\r' should be appended at the end of a story */
+  first = 8, lim = 13;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!lstrcmpW(bstr, bufW2), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  first = 12, lim = 13;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!lstrcmpW(bstr, bufW5), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  first = 0, lim = -1;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  ok(!lstrcmpW(bstr, bufW4), "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  first = -1, lim = 9;
+  SendMessageA(w, EM_SETSEL, first, lim);
+  hres = ITextSelection_GetText(txtSel, &bstr);
+  ok(hres == S_OK, "ITextSelection_GetText\n");
+  todo_wine ok(!bstr, "got wrong text: %s\n", wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+
+  release_interfaces(&w, &reOle, &txtDoc, &txtSel);
+}
+
 START_TEST(richole)
 {
   /* Must explicitly LoadLibrary(). The test has no references to functions in
@@ -411,4 +503,5 @@ START_TEST(richole)
 
   test_Interfaces();
   test_ITextDocument_Open();
+  test_ITextSelection_GetText();
 }
diff --git a/dlls/riched20/txtsrv.c b/dlls/riched20/txtsrv.c
index 1f78405..636efff 100644
--- a/dlls/riched20/txtsrv.c
+++ b/dlls/riched20/txtsrv.c
@@ -264,7 +264,7 @@ DECLSPEC_HIDDEN HRESULT WINAPI fnTextSrv_TxGetText(ITextServices *iface, BSTR *p
          return E_OUTOFMEMORY;
 
       ME_CursorFromCharOfs(This->editor, 0, &start);
-      ME_GetTextW(This->editor, bstr, length, &start, INT_MAX, FALSE);
+      ME_GetTextW(This->editor, bstr, length, &start, INT_MAX, FALSE, FALSE);
       *pbstrText = bstr;
    } else {
       *pbstrText = NULL;


More information about the wine-devel mailing list