The actual patch that brings in the EM_FORMATRANGE function. Tested with programs reported to have problems in Bug 6254.

James McKenzie jjmckenzie51 at earthlink.net
Sat Feb 20 19:47:25 CST 2010


---
 dlls/riched20/editor.c       |    7 +-
 dlls/riched20/editor.h       |    5 ++
 dlls/riched20/list.c         |   95 ++++++++++++++++++++++++++
 dlls/riched20/paint.c        |  154 +++++++++++++++++++++++++++++++++++++++++-
 dlls/riched20/style.c        |   46 +++++++++++++
 dlls/riched20/tests/editor.c |   15 ++---
 6 files changed, 308 insertions(+), 14 deletions(-)

diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 7a11647..3ad6d86 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -40,7 +40,7 @@
   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
   - EM_FINDWORDBREAK
   - EM_FMTLINES
-  - EM_FORMATRANGE
+  + EM_FORMATRANGE
   + EM_GETAUTOURLDETECT 2.0
   - EM_GETBIDIOPTIONS 3.0
   - EM_GETCHARFORMAT (partly done)
@@ -201,7 +201,7 @@
  * - calculate heights of pictures (half-done)
  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
  * - find/replace
- * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
+ * - how to implement EM_DISPLAYBAND ? (Mission Impossible)
  * - italic caret with italic fonts
  * - IME
  * - most notifications aren't sent at all (the most important ones are)
@@ -3012,7 +3012,6 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
   UNSUPPORTED_MSG(EM_DISPLAYBAND)
   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
   UNSUPPORTED_MSG(EM_FMTLINES)
-  UNSUPPORTED_MSG(EM_FORMATRANGE)
   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
@@ -3902,6 +3901,8 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     }
     return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
   }
+  case EM_FORMATRANGE:
+    return ME_FormatContent(editor, (FORMATRANGE *) lParam, (BOOL) wParam);
   case WM_CREATE:
   {
     INT max;
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index cdb5544..0843a00 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -73,6 +73,9 @@ CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from);
 void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
 void ME_CopyCharFormat(CHARFORMAT2W *pDest, const CHARFORMAT2W *pSrc); /* only works with 2W structs */
 void ME_CharFormatFromLogFont(HDC hDC, const LOGFONTW *lf, CHARFORMAT2W *fmt); /* ditto */
+ME_Style *ME_MapStyle(ME_StyleMap *m, ME_Style *s);
+ME_StyleMap *ME_MakeStyleMap(void);
+void ME_FreeStyleMap(ME_StyleMap *m);
 
 /* list.c */
 void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat);
@@ -86,6 +89,7 @@ ME_DisplayItem *ME_MakeDI(ME_DIType type);
 void ME_DestroyDisplayItem(ME_DisplayItem *item);
 void ME_DumpDocument(ME_TextBuffer *buffer);
 const char *ME_GetDITypeName(ME_DIType type);
+void ME_DuplicateText(ME_DisplayItem *pFirst, ME_DisplayItem *pLast, ME_DisplayItem **ppNew, ME_DisplayItem **ppNewLast);
 
 /* string.c */
 ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars);
@@ -208,6 +212,7 @@ void ME_MarkAllForWrapping(ME_TextEditor *editor);
 void ME_SetDefaultParaFormat(PARAFORMAT2 *pFmt);
 
 /* paint.c */
+LPARAM ME_FormatContent(ME_TextEditor *editor, FORMATRANGE *pfr, BOOL bNoOutput);
 void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate);
 void ME_Repaint(ME_TextEditor *editor);
 void ME_RewrapRepaint(ME_TextEditor *editor);
diff --git a/dlls/riched20/list.c b/dlls/riched20/list.c
index bb91bc4..8b877c8 100644
--- a/dlls/riched20/list.c
+++ b/dlls/riched20/list.c
@@ -2,6 +2,7 @@
  * RichEdit - Basic operations on double linked lists.
  *
  * Copyright 2004 by Krzysztof Foltman
+ * Copyright 2006 CorVu Corporation
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -159,6 +160,100 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item) {
   FREE_OBJ(item);
 }
 
+static ME_DisplayItem *ME_DuplicateDisplayItem(ME_DisplayItem *item,
+                                               ME_DisplayItem *prev,
+                                               ME_StyleMap *m,
+                                               ME_DisplayItem **pPrevCell,
+                                               ME_DisplayItem **pPrevPara)
+{
+  ME_DisplayItem *mdi = ALLOC_OBJ(ME_DisplayItem);
+
+  *mdi = *item;
+  mdi->prev = prev;
+  mdi->next = NULL;
+  if (prev)
+    prev->next = mdi;
+
+  switch (item->type)
+  {
+    case diCell:
+    {
+      ME_DisplayItem *prev_cell = *pPrevCell;
+      if (mdi->member.cell.prev_cell)
+      {
+        prev_cell->member.cell.next_cell = mdi;
+        mdi->member.cell.prev_cell = prev_cell;
+        mdi->member.cell.parent_cell = prev_cell->member.cell.parent_cell;
+      } else {
+        mdi->member.cell.parent_cell = prev_cell;
+      }
+      if (mdi->member.cell.next_cell)
+        *pPrevCell = mdi;
+      else
+        *pPrevCell = prev_cell->member.cell.parent_cell;
+      break;
+    }
+
+    case diParagraph:
+      mdi->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
+      *mdi->member.para.pFmt = *item->member.para.pFmt;
+      mdi->member.para.next_para = 0;
+      if (mdi->member.para.pCell)
+        mdi->member.para.pCell = *pPrevCell;
+      /* fall-through */
+    case diTextEnd:
+      /* Make sure the paragraphs are linked up within duplicated text */
+      mdi->member.para.next_para = NULL;
+      mdi->member.para.prev_para = *pPrevPara;
+      if (*pPrevPara)
+        (*pPrevPara)->member.para.next_para = mdi;
+      *pPrevPara = mdi;
+      break;
+
+    case diRun:
+      /* We need to copy the style and text, in the case of styles
+       * we need to reduce duplication which we can do with a style
+       * map.
+       */
+      mdi->member.run.style = ME_MapStyle(m, mdi->member.run.style);
+      mdi->member.run.strText = ME_StrDup(item->member.run.strText);
+
+      if (item->member.run.ole_obj)
+      {
+        mdi->member.run.ole_obj = ALLOC_OBJ(*item->member.run.ole_obj);
+        ME_CopyReObject(mdi->member.run.ole_obj, item->member.run.ole_obj);
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  return mdi;
+}
+
+/* ME_DuplicateText makes a duplicate of all ME_DisplayItem structures starting
+ * with pFirst and ending with pLast.
+ */
+void ME_DuplicateText(ME_DisplayItem *pFirst, ME_DisplayItem *pLast, ME_DisplayItem
+                      **ppNew, ME_DisplayItem **ppNewLast)
+{
+  ME_DisplayItem *pNow;
+  ME_StyleMap *m = ME_MakeStyleMap();
+  ME_DisplayItem *prev_cell = NULL;
+  ME_DisplayItem *prev_para = NULL;
+
+  pNow = ME_DuplicateDisplayItem(pFirst, NULL, m, &prev_cell, &prev_para);
+  *ppNew = pNow;
+  while (pFirst != pLast)
+  {
+    pFirst = pFirst->next;
+    pNow = ME_DuplicateDisplayItem(pFirst, pNow, m, &prev_cell, &prev_para);
+    *ppNewLast = pNow;
+  }
+  ME_FreeStyleMap(m);
+}
+
 ME_DisplayItem *ME_MakeDI(ME_DIType type) {
   ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
   ZeroMemory(item, sizeof(ME_DisplayItem));
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index e17c2bd..6b22722 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -72,6 +72,157 @@ int ME_twips2pointsY(ME_Context *c, int y)
   return y;
 }
 
+LPARAM ME_FormatContent(ME_TextEditor *editor, FORMATRANGE *pfr, BOOL bDraw)
+{
+  ME_Context c;
+  ME_DisplayItem *pFirst;
+  ME_DisplayItem *pLast;
+  ME_DisplayItem *pNow;
+  ME_DisplayItem *pIntra;
+  int cyRow;
+  int cyOffset = -1;
+  int iEndOffset = -1;
+  int textlength = ME_GetTextLength(editor);
+  int targetBottom, numCharPrintable = 0;
+
+  if (!pfr || pfr->chrg.cpMin >= textlength)
+    return textlength;
+
+  ME_InitContext(&c, editor, pfr->hdc, pfr->hdcTarget);
+  c.bHideSelection = TRUE;
+  c.bClip = TRUE;
+  c.bWordWrap = TRUE;
+  c.nZoomNumerator = 0;
+  c.nZoomDenominator = 0;
+
+  c.rcView.left = ME_twips2targetX(&c, pfr->rc.left);
+  c.rcView.right = ME_twips2targetX(&c, pfr->rc.right);
+  c.rcView.top = ME_twips2targetY(&c, pfr->rc.top);
+  c.rcView.bottom = ME_twips2targetY(&c, pfr->rc.bottom);
+  c.nAvailWidth = c.rcView.right - c.rcView.left;
+  numCharPrintable = c.nAvailWidth / (1440/c.dpiTarget.cy);
+
+  targetBottom = c.rcView.bottom;
+
+  c.pt.x = 0;
+  c.pt.y = 0;
+
+  /* Get a copy of all the text in the control so we can perform calculations
+   * without interfering with the values cached for the on-screen data.
+   */
+
+  ME_DuplicateText(editor->pBuffer->pFirst, editor->pBuffer->pLast, &pFirst, &pLast);
+
+  /* Now perform wrapping on the resulting data, and find the vertical offset of
+   * the row at the start of the character range, and the character offset of the
+   * first non-printable character.
+   */
+
+  for (pNow = pFirst->next; pNow != pLast; pNow = pNow->member.para.next_para)
+  {
+    int local_pt_y = pNow->member.para.pt.y = c.pt.y;
+    pNow->member.para.nFlags |= MEPF_REWRAP;
+    ME_WrapTextParagraph(&c, pNow);
+    if (iEndOffset == -1)
+    {
+      cyRow = 0;
+
+      for (pIntra = pNow; pIntra != pNow->member.para.next_para; pIntra = pIntra->next)
+      {
+        if (pIntra->type == diStartRow)
+        {
+          local_pt_y += cyRow;
+          cyRow = pIntra->member.row.nHeight;
+        }
+        else if (pIntra->type == diRun)
+        {
+          int iRunOfs = pNow->member.para.nCharOfs + pIntra->member.run.nCharOfs;
+          int iRunEndOfs = iRunOfs + (pIntra->member.run.strText ?
+                                     pIntra->member.run.strText->nLen : 1);
+          if (cyOffset == -1 && iRunEndOfs > pfr->chrg.cpMin)
+            cyOffset = local_pt_y;
+          if (cyOffset != -1 && iEndOffset == -1)
+          {
+            if (local_pt_y > cyOffset &&
+                local_pt_y + cyRow > c.rcView.bottom - c.rcView.top + cyOffset)
+            {
+              iEndOffset = iRunOfs;
+              targetBottom = local_pt_y - cyOffset;
+              break;
+            }
+            if (pfr->chrg.cpMax >= 0 &&
+                iRunEndOfs > pfr->chrg.cpMax)
+            {
+              int iBottom = local_pt_y + cyRow - cyOffset + c.rcView.top;
+              targetBottom = local_pt_y + cyRow - cyOffset;
+
+              if (c.rcView.bottom > iBottom)
+                c.rcView.bottom = iBottom;
+              iEndOffset = iRunOfs;
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (iEndOffset == -1)
+  {
+    iEndOffset = numCharPrintable + 1;
+    targetBottom = c.pt.y - cyOffset;
+  }
+  pfr->rc.bottom = (LONGLONG)targetBottom * 1440 / c.dpiTarget.cy + pfr->rc.top;
+  c.rcView.bottom = targetBottom + c.rcView.top;
+
+  /* Then draw the data */
+
+  if (bDraw)
+  {
+    HRGN oldRgn;
+    int iBGMode = SetBkMode(c.hDC, TRANSPARENT);
+    int savedTotalWidth = editor->nTotalWidth;
+    editor->nTotalWidth = c.rcView.right - c.rcView.left;
+
+    oldRgn = CreateRectRgn(0, 0, 0, 0);
+    if (!GetClipRgn(c.hDC, oldRgn))
+    {
+      DeleteObject(oldRgn);
+      oldRgn = NULL;
+    }
+    IntersectClipRect(c.hDC,
+                      ME_twips2pointsX(&c, pfr->rcPage.left),
+                      ME_twips2pointsY(&c, pfr->rc.top),
+                      ME_twips2pointsX(&c, pfr->rcPage.right),
+                      ME_target2pointsY(&c, c.rcView.bottom));
+
+    c.pt.x = ME_twips2targetX(&c, pfr->rc.left);
+    c.pt.y = ME_twips2targetY(&c, pfr->rc.top) - cyOffset;
+
+    for (pNow = pFirst->next; pNow != pLast; pNow = pNow->member.para.next_para)
+    {
+      int y = c.pt.y + pNow->member.para.pt.y;
+      if (y + pNow->member.para.nHeight > c.rcView.top &&
+          y < c.rcView.bottom)
+        ME_DrawParagraph(&c, pNow);
+    }
+    SelectClipRgn(c.hDC, oldRgn);
+    if (oldRgn)
+      DeleteObject(oldRgn);
+    SetBkMode(c.hDC, iBGMode);
+    editor->nTotalWidth = savedTotalWidth;
+  }
+
+  while (pFirst)
+  {
+    pNow = pFirst;
+    pFirst = pFirst->next;
+    ME_DestroyDisplayItem(pNow);
+  }
+
+  return iEndOffset;
+}
+
 void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate)
 {
   ME_DisplayItem *item;
@@ -483,8 +634,9 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
     return;
   }
 
+  /* Add check for text and graphics to hide/unhide them */
   if (run->nFlags & MERF_GRAPHICS)
-    ME_DrawOLE(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo));
+    ME_DrawOLE(c, x, y, run, para, !c->bHideSelection && runofs >= nSelFrom && runofs < nSelTo);
   else
   {
     if (c->editor->cPasswordMask)
diff --git a/dlls/riched20/style.c b/dlls/riched20/style.c
index 7545b42..6247fc6 100644
--- a/dlls/riched20/style.c
+++ b/dlls/riched20/style.c
@@ -472,6 +472,52 @@ void ME_ReleaseStyle(ME_Style *s)
     ME_DestroyStyle(s);
 }
 
+ME_Style *ME_MapStyle(ME_StyleMap *m, ME_Style *s)
+{
+  ME_StyleMapping *tail;
+
+  if (!s)
+    return s;
+
+  for (tail = m->first; tail; tail = tail->next)
+  {
+    if (tail->from == s)
+    {
+      ME_AddRefStyle(tail->to);
+      return tail->to;
+    }
+  }
+  tail = ALLOC_OBJ(ME_StyleMapping);
+  tail->from = s;
+  tail->to = ME_MakeStyle(&s->fmt);
+  ME_AddRefStyle(tail->to);
+  // Insert new style mapping at the front of the linked list.
+  tail->next = m->first;
+  m->first = tail;
+  return tail->to;
+}
+
+ME_StyleMap *ME_MakeStyleMap(void)
+{
+  ME_StyleMap *sm = ALLOC_OBJ(ME_StyleMap);
+
+  sm->first = 0;
+  return sm;
+}
+
+void ME_FreeStyleMap(ME_StyleMap *m)
+{
+  while (m->first)
+  {
+    ME_StyleMapping *p = m->first;
+
+    m->first = p->next;
+    ME_ReleaseStyle(p->to);
+    FREE_OBJ(p);
+  }
+  FREE_OBJ(m);
+}
+
 ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor)
 {
   if (ME_IsSelection(editor))
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 8aa1c39..ffc5822 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -4859,12 +4859,11 @@ static void test_EM_FORMATRANGE(void)
     fr.hdc = fr.hdcTarget = hdc;
     fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
     fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
+    trace("%d is the value of stringsize.cx and %d is the right margin\n", stringsize.cx, fr.rc.right);
     fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
 
     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
-    todo_wine {
     ok(r == len, "Expected %d, got %d\n", len, r);
-    }
 
     /* We know that the page can't hold the full string. See how many characters
      * are on the first one
@@ -4872,13 +4871,9 @@ static void test_EM_FORMATRANGE(void)
     fr.chrg.cpMin = 0;
     fr.chrg.cpMax = -1;
     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
-    todo_wine {
     ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
-    }
     if (fmtstrings[i].first)
-      todo_wine {
       ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
-      }
     else
       ok(r < len, "Expected < %d, got %d\n", len, r);
 
@@ -4886,18 +4881,18 @@ static void test_EM_FORMATRANGE(void)
     fr.chrg.cpMin = r;
     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
     if (fmtstrings[i].second)
-      todo_wine {
       ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
-      }
     else
       ok (r < len, "Expected < %d, got %d\n", len, r);
 
     /* There is at least on more page, but we don't care */
+    /* Value returned by SendMessage should be equal to the length of the
+       original string when EM_FORMATRANGE is called with the lparam
+       variable is set to NULL or 0 even if fr.chrg.cpMin is set to the end
+       of the string */
 
     r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
-    todo_wine {
     ok(r == len, "Expected %d, got %d\n", len, r);
-    }
   }
 
   ReleaseDC(NULL, hdc);
-- 
1.6.6


--------------060706040408000303030306--



More information about the wine-patches mailing list