[2/2] richedit: Implemented the horizontal scrollbar.

Dylan Smith dylan.ah.smith at gmail.com
Tue Jan 13 12:33:02 CST 2009


The implementation for the horizontal scrollbar is mostly similar to how
the vertical scrollbar is implemented.

First the width needed to be determined while performing the wrapping of
the text.  With word wrap on this width will usually be less than the
width of the formatting rectangle, however, it may still wrap with
tables longer than this width, or with large fonts causing caracters to
be greater than the available width.

Painting mostly involved modifying the context so that everything was
shifted by the scroll position.  There was also additional changes
needed to the paragraph border. Notably it is possible to scroll past
the total width in native by pressing the right arrow on the scroll bar
after scrolling to the right by sliding the scroll thumb.  This meant
that the background to the right of the paragraph border needs to be
erased if visible.
---
 dlls/riched20/caret.c        |   67 +++++----
 dlls/riched20/editor.c       |  113 +++++++++++----
 dlls/riched20/editor.h       |   11 +-
 dlls/riched20/editstr.h      |    7 +-
 dlls/riched20/paint.c        |  315 ++++++++++++++++++++++++------------------
 dlls/riched20/tests/editor.c |    3 -
 dlls/riched20/wrap.c         |   12 ++-
 7 files changed, 322 insertions(+), 206 deletions(-)
-------------- next part --------------
diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c
index 1f53fc1..3df8566 100644
--- a/dlls/riched20/caret.c
+++ b/dlls/riched20/caret.c
@@ -168,7 +168,7 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor,
   assert(!(ME_GetParagraph(pCursorRun)->member.para.nFlags & MEPF_REWRAP));
   assert(pCursor->pRun);
   assert(pCursor->pRun->type == diRun);
-  
+
   if (pCursorRun->type == diRun) {
     ME_DisplayItem *row = ME_FindItemBack(pCursorRun, diStartRowOrParagraph);
 
@@ -178,9 +178,9 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor,
       ME_DisplayItem *run = pCursorRun;
       ME_DisplayItem *para = NULL;
       SIZE sz = {0, 0};
-    
+
       ME_InitContext(&c, editor, hDC);
-      
+
       if (!pCursor->nOffset)
       {
         ME_DisplayItem *prev = ME_FindItemBack(pCursorRun, diRunOrParagraph);
@@ -192,7 +192,7 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor,
       para = ME_FindItemBack(row, diParagraph);
       assert(para);
       assert(para->type == diParagraph);
-      if (editor->bCaretAtEnd && !pCursor->nOffset && 
+      if (editor->bCaretAtEnd && !pCursor->nOffset &&
           run == ME_FindItemFwd(row, diRun))
       {
         ME_DisplayItem *tmp = ME_FindItemBack(row, diRunOrParagraph);
@@ -214,9 +214,9 @@ ME_GetCursorCoordinates(ME_TextEditor *editor, ME_Cursor *pCursor,
       }
 
       *height = pSizeRun->member.run.nAscent + pSizeRun->member.run.nDescent;
-      *x = c.rcView.left + run->member.run.pt.x + sz.cx;
+      *x = c.rcView.left + run->member.run.pt.x + sz.cx - editor->horz_si.nPos;
       *y = c.rcView.top + para->member.para.pt.y + row->member.row.nBaseline
-           + run->member.run.pt.y - pSizeRun->member.run.nAscent - ME_GetYScrollPos(editor);
+           + run->member.run.pt.y - pSizeRun->member.run.nAscent - editor->vert_si.nPos;
       ME_DestroyContext(&c, editor->hWnd);
       return;
     }
@@ -1025,7 +1025,8 @@ int ME_CharFromPos(ME_TextEditor *editor, int x, int y, BOOL *isExact)
     if (isExact) *isExact = FALSE;
     return -1;
   }
-  y += ME_GetYScrollPos(editor);
+  x += editor->horz_si.nPos;
+  y += editor->vert_si.nPos;
   bResult = ME_FindPixelPos(editor, x, y, &cursor, NULL);
   if (isExact) *isExact = bResult;
   return (ME_GetParagraph(cursor.pRun)->member.para.nCharOfs
@@ -1099,10 +1100,11 @@ void ME_LButtonDown(ME_TextEditor *editor, int x, int y, int clickNum)
   ME_Cursor tmp_cursor;
   int is_selection = 0;
   BOOL is_shift;
-  
+
   editor->nUDArrowX = -1;
-  
-  y += ME_GetYScrollPos(editor);
+
+  x += editor->horz_si.nPos;
+  y += editor->vert_si.nPos;
 
   tmp_cursor = editor->pCursors[0];
   is_selection = ME_IsSelection(editor);
@@ -1161,10 +1163,11 @@ void ME_LButtonDown(ME_TextEditor *editor, int x, int y, int clickNum)
 void ME_MouseMove(ME_TextEditor *editor, int x, int y)
 {
   ME_Cursor tmp_cursor;
-  
+
   if (editor->nSelectionType == stDocument)
       return;
-  y += ME_GetYScrollPos(editor);
+  x += editor->horz_si.nPos;
+  y += editor->vert_si.nPos;
 
   tmp_cursor = editor->pCursors[0];
   /* FIXME: do something with the return value of ME_FindPixelPos */
@@ -1179,9 +1182,9 @@ void ME_MouseMove(ME_TextEditor *editor, int x, int y)
   {
       /* The scroll the cursor towards the other end, since it was the one
        * extended by ME_ExtendAnchorSelection */
-      ME_EnsureVisible(editor, editor->pCursors[1].pRun);
+      ME_EnsureVisible(editor, &editor->pCursors[1]);
   } else {
-      ME_EnsureVisible(editor, editor->pCursors[0].pRun);
+      ME_EnsureVisible(editor, &editor->pCursors[0]);
   }
 
   ME_InvalidateSelection(editor);
@@ -1367,18 +1370,18 @@ static void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor)
   ME_DisplayItem *pLast, *p;
   int x, y, ys, yd, yp, yprev;
   ME_Cursor tmp_curs = *pCursor;
-  
+
   x = ME_GetXForArrow(editor, pCursor);
   if (!pCursor->nOffset && editor->bCaretAtEnd)
     pRun = ME_FindItemBack(pRun, diRun);
-  
+
   p = ME_FindItemBack(pRun, diStartRowOrParagraph);
   assert(p->type == diStartRow);
   yp = ME_FindItemBack(p, diParagraph)->member.para.pt.y;
   yprev = ys = y = yp + p->member.row.pt.y;
   yd = y - editor->sizeWindow.cy;
   pLast = p;
-  
+
   do {
     p = ME_FindItemBack(p, diStartRowOrParagraph);
     if (!p)
@@ -1395,15 +1398,16 @@ static void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor)
     pLast = p;
     yprev = y;
   } while(1);
-  
+
   pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
   ME_UpdateSelection(editor, &tmp_curs);
   if (yprev < editor->sizeWindow.cy)
   {
-    ME_EnsureVisible(editor, ME_FindItemFwd(editor->pBuffer->pFirst, diRun));
+    ME_Cursor startCursor = {ME_FindItemFwd(editor->pBuffer->pFirst, diRun), 0};
+    ME_EnsureVisible(editor, &startCursor);
     ME_Repaint(editor);
   }
-  else 
+  else
   {
     ME_ScrollUp(editor, ys-yprev);
   }
@@ -1422,18 +1426,18 @@ static void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor)
   ME_DisplayItem *pLast, *p;
   int x, y, ys, yd, yp, yprev;
   ME_Cursor tmp_curs = *pCursor;
-  
+
   x = ME_GetXForArrow(editor, pCursor);
   if (!pCursor->nOffset && editor->bCaretAtEnd)
     pRun = ME_FindItemBack(pRun, diRun);
-  
+
   p = ME_FindItemBack(pRun, diStartRowOrParagraph);
   assert(p->type == diStartRow);
   yp = ME_FindItemBack(p, diParagraph)->member.para.pt.y;
   yprev = ys = y = yp + p->member.row.pt.y;
   yd = y + editor->sizeWindow.cy;
   pLast = p;
-  
+
   do {
     p = ME_FindItemFwd(p, diStartRowOrParagraph);
     if (!p)
@@ -1448,15 +1452,16 @@ static void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor)
     pLast = p;
     yprev = y;
   } while(1);
-  
+
   pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
   ME_UpdateSelection(editor, &tmp_curs);
   if (yprev >= editor->nTotalLength-editor->sizeWindow.cy)
   {
-    ME_EnsureVisible(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun));
+    ME_Cursor endCursor = {ME_FindItemBack(editor->pBuffer->pLast, diRun), 0};
+    ME_EnsureVisible(editor, &endCursor);
     ME_Repaint(editor);
   }
-  else 
+  else
   {
     ME_ScrollUp(editor,ys-yprev);
   }
@@ -1593,7 +1598,7 @@ ME_ArrowKey(ME_TextEditor *editor, int nVKey, BOOL extend, BOOL ctrl)
   ME_Cursor *p = &editor->pCursors[nCursor];
   ME_Cursor tmp_curs = *p;
   BOOL success = FALSE;
-  
+
   ME_CheckCharOffsets(editor);
   switch(nVKey) {
     case VK_LEFT:
@@ -1630,22 +1635,22 @@ ME_ArrowKey(ME_TextEditor *editor, int nVKey, BOOL extend, BOOL ctrl)
       editor->bCaretAtEnd = 0;
       break;
     }
-    case VK_END: 
+    case VK_END:
       if (ctrl)
         ME_ArrowCtrlEnd(editor, &tmp_curs);
       else
         ME_ArrowEnd(editor, &tmp_curs);
       break;
   }
-  
+
   if (!extend)
     editor->pCursors[1] = tmp_curs;
   *p = tmp_curs;
-  
+
   ME_InvalidateSelection(editor);
   ME_Repaint(editor);
   HideCaret(editor->hWnd);
-  ME_EnsureVisible(editor, tmp_curs.pRun); 
+  ME_EnsureVisible(editor, &tmp_curs);
   ME_ShowCaret(editor);
   ME_SendSelChange(editor);
   return success;
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index d022e17..f34bf3c 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -65,7 +65,7 @@
   - EM_GETREDONAME 2.0
   + EM_GETSEL
   + EM_GETSELTEXT (ANSI&Unicode)
-  + EM_GETSCROLLPOS 3.0 (only Y value valid)
+  + EM_GETSCROLLPOS 3.0
 ! - EM_GETTHUMB
   + EM_GETTEXTEX 2.0
   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
@@ -137,12 +137,14 @@
   + WM_GETDLGCODE (the current implementation is incomplete)
   + WM_GETTEXT (ANSI&Unicode)
   + WM_GETTEXTLENGTH (ANSI version sucks)
+  + WM_HSCROLL
   + WM_PASTE
   + WM_SETFONT
   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
   - WM_STYLECHANGING
   - WM_STYLECHANGED (things like read-only flag)
   + WM_UNICHAR
+  + WM_VSCROLL
 
   Notifications
 
@@ -184,7 +186,7 @@
   - ES_VERTICAL
   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
   - WS_SETFONT
-  - WS_HSCROLL
+  + WS_HSCROLL
   + WS_VSCROLL
 */
 
@@ -197,7 +199,6 @@
  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
  * - COM interface (looks like a major pain in the TODO list)
  * - calculate heights of pictures (half-done)
- * - horizontal scrolling (not even started)
  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
  * - find/replace
  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
@@ -2681,6 +2682,7 @@ static ME_TextEditor *ME_MakeEditor(HWND hWnd, BOOL bEmulateVersion10)
   ed->pCursors[2] = ed->pCursors[0];
   ed->pCursors[3] = ed->pCursors[1];
   ed->nLastTotalLength = ed->nTotalLength = 0;
+  ed->nLastTotalWidth = ed->nTotalWidth = 0;
   ed->nUDArrowX = -1;
   ed->nSequence = 0;
   ed->rgbBackColor = -1;
@@ -2730,13 +2732,19 @@ static ME_TextEditor *ME_MakeEditor(HWND hWnd, BOOL bEmulateVersion10)
   
   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
 
-  /* Default vertical scrollbar information */
+  /* Default scrollbar information */
   ed->vert_si.cbSize = sizeof(SCROLLINFO);
   ed->vert_si.nMin = 0;
   ed->vert_si.nMax = 0;
   ed->vert_si.nPage = 0;
   ed->vert_si.nPos = 0;
 
+  ed->horz_si.cbSize = sizeof(SCROLLINFO);
+  ed->horz_si.nMin = 0;
+  ed->horz_si.nMax = 0;
+  ed->horz_si.nPage = 0;
+  ed->horz_si.nPos = 0;
+
   OleInitialize(NULL);
 
   return ed;
@@ -3156,12 +3164,12 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
   case EM_SETSCROLLPOS:
   {
     POINT *point = (POINT *)lParam;
-    ME_ScrollAbs(editor, point->y);
+    ME_ScrollAbs(editor, point->x, point->y);
     return 0;
   }
   case EM_AUTOURLDETECT:
   {
-    if (wParam==1 || wParam ==0) 
+    if (wParam==1 || wParam ==0)
     {
         editor->AutoURLDetect_bEnable = (BOOL)wParam;
         return 0;
@@ -3363,7 +3371,7 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
   case EM_GETFIRSTVISIBLELINE:
   {
     ME_DisplayItem *p = editor->pBuffer->pFirst;
-    int y = ME_GetYScrollPos(editor);
+    int y = editor->vert_si.nPos;
     int ypara = 0;
     int count = 0;
     int ystart, yend;
@@ -3433,7 +3441,7 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     return len;
   }
   case EM_SCROLLCARET:
-    ME_EnsureVisible(editor, editor->pCursors[0].pRun);
+    ME_EnsureVisible(editor, &editor->pCursors[0]);
     return 0;
   case WM_SETFONT:
   {
@@ -3587,10 +3595,10 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
   }
   case EM_GETSCROLLPOS:
   {
-      POINT *point = (POINT *)lParam;
-      point->x = 0; /* FIXME side scrolling not implemented */
-      point->y = ME_GetYScrollPos(editor);
-      return 1;
+    POINT *point = (POINT *)lParam;
+    point->x = editor->horz_si.nPos;
+    point->y = editor->vert_si.nPos;
+    return 1;
   }
   case EM_GETTEXTRANGE:
   {
@@ -3837,9 +3845,8 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     ME_DisplayItem *pRun;
     int nCharOfs, nOffset, nLength;
     POINTL pt = {0,0};
-    SCROLLINFO si;
-    
-    nCharOfs = wParam; 
+
+    nCharOfs = wParam;
     /* detect which API version we're dealing with */
     if (wParam >= 0x40000)
         nCharOfs = lParam;
@@ -3854,10 +3861,8 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     pt.y += ME_GetParagraph(pRun)->member.para.pt.y + editor->rcFormat.top;
     pt.x += editor->rcFormat.left;
 
+    pt.x -= editor->horz_si.nPos;
     pt.y -= editor->vert_si.nPos;
-    si.cbSize = sizeof(si);
-    si.fMask = SIF_POS;
-    if (GetScrollInfo(editor->hWnd, SB_HORZ, &si)) pt.x -= si.nPos;
 
     if (wParam >= 0x40000) {
         *(POINTL *)wParam = pt;
@@ -3869,10 +3874,6 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     SCROLLINFO si;
 
     ME_SetDefaultFormatRect(editor);
-    if (GetWindowLongW(editor->hWnd, GWL_STYLE) & WS_HSCROLL)
-    { /* Squelch the default horizontal scrollbar it would make */
-      ShowScrollBar(editor->hWnd, SB_HORZ, FALSE);
-    }
 
     si.cbSize = sizeof(si);
     si.fMask = SIF_PAGE | SIF_RANGE;
@@ -3882,6 +3883,7 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
     si.nMin = 0;
     si.nPage = 0;
     SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE);
+    SetScrollInfo(editor->hWnd, SB_HORZ, &si, TRUE);
 
     ME_CommitUndo(editor);
     ME_WrapMarkedParagraphs(editor);
@@ -4046,21 +4048,72 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
   case EM_STOPGROUPTYPING:
     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
     return 0;
+  case WM_HSCROLL:
+  {
+    const int scrollUnit = 7;
+
+    switch(LOWORD(wParam))
+    {
+      case SB_LEFT:
+        ME_ScrollAbs(editor, 0, 0);
+        break;
+      case SB_RIGHT:
+        ME_ScrollAbs(editor,
+                     editor->horz_si.nMax - (int)editor->horz_si.nPage,
+                     editor->vert_si.nMax - (int)editor->vert_si.nPage);
+        break;
+      case SB_LINELEFT:
+        ME_ScrollLeft(editor, scrollUnit);
+        break;
+      case SB_LINERIGHT:
+        ME_ScrollRight(editor, scrollUnit);
+        break;
+      case SB_PAGELEFT:
+        ME_ScrollLeft(editor, editor->sizeWindow.cx);
+        break;
+      case SB_PAGERIGHT:
+        ME_ScrollRight(editor, editor->sizeWindow.cx);
+        break;
+      case SB_THUMBTRACK:
+      case SB_THUMBPOSITION:
+      {
+        SCROLLINFO sbi;
+        sbi.cbSize = sizeof(sbi);
+        sbi.fMask = SIF_TRACKPOS;
+        /* Try to get 32-bit track position value. */
+        if (!GetScrollInfo(editor->hWnd, SB_HORZ, &sbi))
+          /* GetScrollInfo failed, settle for 16-bit value in wParam. */
+          sbi.nTrackPos = HIWORD(wParam);
+
+        ME_HScrollAbs(editor, sbi.nTrackPos);
+        break;
+      }
+    }
+    break;
+  }
   case EM_SCROLL: /* fall through */
-  case WM_VSCROLL: 
+  case WM_VSCROLL:
   {
     int origNPos;
     int lineHeight;
-    
-    origNPos = ME_GetYScrollPos(editor);
+
+    origNPos = editor->vert_si.nPos;
     lineHeight = 24;
-    
+
     if (editor && editor->pBuffer && editor->pBuffer->pDefaultStyle)
       lineHeight = editor->pBuffer->pDefaultStyle->tm.tmHeight;
     if (lineHeight <= 0) lineHeight = 24;
-    
-    switch(LOWORD(wParam)) 
+
+    switch(LOWORD(wParam))
     {
+      case SB_TOP:
+        ME_ScrollAbs(editor, 0, 0);
+        break;
+      case SB_BOTTOM:
+        ME_ScrollAbs(editor,
+                     editor->horz_si.nMax - (int)editor->horz_si.nPage,
+                     editor->vert_si.nMax - (int)editor->vert_si.nPage);
+        break;
       case SB_LINEUP:
         ME_ScrollUp(editor,lineHeight);
         break;
@@ -4084,12 +4137,12 @@ static LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
           /* GetScrollInfo failed, settle for 16-bit value in wParam. */
           sbi.nTrackPos = HIWORD(wParam);
 
-        ME_ScrollAbs(editor, sbi.nTrackPos);
+        ME_VScrollAbs(editor, sbi.nTrackPos);
         break;
       }
     }
     if (msg == EM_SCROLL)
-      return 0x00010000 | (((ME_GetYScrollPos(editor) - origNPos)/lineHeight) & 0xffff);
+      return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
     break;
   }
   case WM_MOUSEWHEEL:
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index ca820ce..72af88d 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -238,7 +238,7 @@ void ME_Repaint(ME_TextEditor *editor);
 void ME_RewrapRepaint(ME_TextEditor *editor);
 void ME_UpdateRepaint(ME_TextEditor *editor);
 void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph);
-void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun);
+void ME_EnsureVisible(ME_TextEditor *editor, ME_Cursor *pCursor);
 void ME_InvalidateSelection(ME_TextEditor *editor);
 void ME_QueueInvalidateFromCursor(ME_TextEditor *editor, int nCursor);
 BOOL ME_SetZoom(ME_TextEditor *editor, int numerator, int denominator);
@@ -247,13 +247,14 @@ int  ME_twips2pointsY(ME_Context *c, int y);
 
 /* scroll functions in paint.c */
 
-void ME_ScrollAbs(ME_TextEditor *editor, int absY);
+void ME_ScrollAbs(ME_TextEditor *editor, int x, int y);
+void ME_HScrollAbs(ME_TextEditor *editor, int x);
+void ME_VScrollAbs(ME_TextEditor *editor, int y);
 void ME_ScrollUp(ME_TextEditor *editor, int cy);
 void ME_ScrollDown(ME_TextEditor *editor, int cy);
-void ME_Scroll(ME_TextEditor *editor, int value, int type);
+void ME_ScrollLeft(ME_TextEditor *editor, int cx);
+void ME_ScrollRight(ME_TextEditor *editor, int cx);
 void ME_UpdateScrollBar(ME_TextEditor *editor);
-int ME_GetYScrollPos(ME_TextEditor *editor);
-BOOL ME_GetYScrollVisible(ME_TextEditor *editor);
 
 /* other functions in paint.c */
 int  ME_GetParaBorderWidth(ME_TextEditor *editor, int);
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index 6850ca7..68a316c 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -189,7 +189,7 @@ typedef struct tagME_Paragraph
   int nCharOfs;
   int nFlags;
   POINT pt;
-  int nHeight;
+  int nHeight, nWidth;
   int nLastPaintYPos, nLastPaintHeight;
   int nRows;
   struct tagME_DisplayItem *prev_para, *next_para, *document;
@@ -333,6 +333,7 @@ typedef struct tagME_TextEditor
   int nCursors;
   SIZE sizeWindow;
   int nTotalLength, nLastTotalLength;
+  int nTotalWidth, nLastTotalWidth;
   int nUDArrowX;
   int nSequence;
   COLORREF rgbBackColor;
@@ -374,8 +375,8 @@ typedef struct tagME_TextEditor
   /* Track previous notified selection */
   CHARRANGE notified_cr;
 
-  /* Cache previously set vertical scrollbar info */
-  SCROLLINFO vert_si;
+  /* Cache previously set scrollbar info */
+  SCROLLINFO vert_si, horz_si;
 
   BOOL bMouseCaptured;
 } ME_TextEditor;
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index d58ea41..ede2676 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -27,7 +27,6 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
 {
   ME_DisplayItem *item;
   ME_Context c;
-  int yoffset;
   int ys, ye;
   HRGN oldRgn, clipRgn;
 
@@ -42,15 +41,14 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
   DeleteObject(clipRgn);
 
   editor->nSequence++;
-  yoffset = ME_GetYScrollPos(editor);
   ME_InitContext(&c, editor, hDC);
   SetBkMode(hDC, TRANSPARENT);
   ME_MoveCaret(editor); /* Calls ME_WrapMarkedParagraphs */
   item = editor->pBuffer->pFirst->next;
   /* This context point is an offset for the paragraph positions stored
    * during wrapping. It shouldn't be modified during painting. */
-  c.pt.x = c.rcView.left;
-  c.pt.y = c.rcView.top - yoffset;
+  c.pt.x = c.rcView.left - editor->horz_si.nPos;
+  c.pt.y = c.rcView.top - editor->vert_si.nPos;
   while(item != editor->pBuffer->pLast)
   {
     assert(item->type == diParagraph);
@@ -112,9 +110,11 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
     if (!IsRectEmpty(&rc))
       FillRect(hDC, &rc, c.editor->hbrBackground);
   }
-  if (editor->nTotalLength != editor->nLastTotalLength)
+  if (editor->nTotalLength != editor->nLastTotalLength ||
+      editor->nTotalWidth != editor->nLastTotalWidth)
     ME_SendRequestResize(editor, FALSE);
   editor->nLastTotalLength = editor->nTotalLength;
+  editor->nLastTotalWidth = editor->nTotalWidth;
   ME_DestroyContext(&c, NULL);
 
   SelectClipRgn(hDC, oldRgn);
@@ -145,11 +145,11 @@ void ME_UpdateRepaint(ME_TextEditor *editor)
   wrappedParagraphs = ME_WrapMarkedParagraphs(editor);
   if (wrappedParagraphs)
     ME_UpdateScrollBar(editor);
-  
+
   /* Ensure that the cursor is visible */
   pCursor = &editor->pCursors[0];
-  ME_EnsureVisible(editor, pCursor->pRun);
-  
+  ME_EnsureVisible(editor, pCursor);
+
   /* send EN_CHANGE if the event mask asks for it */
   if(editor->nEventMask & ENM_CHANGE)
   {
@@ -163,7 +163,7 @@ void ME_UpdateRepaint(ME_TextEditor *editor)
 
 void
 ME_RewrapRepaint(ME_TextEditor *editor)
-{ 
+{
   /* RewrapRepaint should be called whenever the control has changed in
    * looks, but not content. Like resizing. */
   
@@ -618,7 +618,7 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
   /* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
    * but might support it in later versions. */
   if (hasParaBorder) {
-    int         pen_width;
+    int         pen_width, rightEdge;
     COLORREF    pencr;
     HPEN        pen = NULL, oldpen = NULL;
     POINT       pt;
@@ -628,6 +628,9 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
     else
       pencr = pen_colors[(para->pFmt->wBorders >> 12) & 0xF];
 
+    rightEdge = c->pt.x + max(c->editor->sizeWindow.cx,
+                              c->editor->nTotalWidth);
+
     pen_width = ME_GetBorderPenWidth(c->editor, idx);
     pen = CreatePen(border_details[idx].pen_style, pen_width, pencr);
     oldpen = SelectObject(c->hDC, pen);
@@ -640,51 +643,51 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
 
     if (para->pFmt->wBorders & 1)
     {
-      MoveToEx(c->hDC, c->rcView.left, y + bounds->top, NULL);
-      LineTo(c->hDC, c->rcView.left, y + para->nHeight - bounds->bottom);
+      MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
+      LineTo(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom);
       if (border_details[idx].dble) {
-        rc.left = c->rcView.left + 1;
+        rc.left = c->pt.x + 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));
+        MoveToEx(c->hDC, c->pt.x + pen_width + 1, y + bounds->top + DD(4), NULL);
+        LineTo(c->hDC, c->pt.x + 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 - 1, y + bounds->top, NULL);
-      LineTo(c->hDC, c->rcView.right - 1, y + para->nHeight - bounds->bottom);
+      MoveToEx(c->hDC, rightEdge - 1, y + bounds->top, NULL);
+      LineTo(c->hDC, rightEdge - 1, y + para->nHeight - bounds->bottom);
       if (border_details[idx].dble) {
-        rc.left = c->rcView.right - pen_width - 1;
-        rc.right = c->rcView.right - 1;
+        rc.left = rightEdge - pen_width - 1;
+        rc.right = rc.left + pen_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.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));
+        MoveToEx(c->hDC, rightEdge - 1 - pen_width - 1, y + bounds->top + DD(4), NULL);
+        LineTo(c->hDC, rightEdge - 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 + bounds->top, NULL);
-      LineTo(c->hDC, c->rcView.right, y + bounds->top);
+      MoveToEx(c->hDC, c->pt.x, y + bounds->top, NULL);
+      LineTo(c->hDC, rightEdge, y + bounds->top);
       if (border_details[idx].dble) {
-        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);
+        MoveToEx(c->hDC, c->pt.x + DD(1), y + bounds->top + pen_width + 1, NULL);
+        LineTo(c->hDC, rightEdge - 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 - bounds->bottom - 1, NULL);
-      LineTo(c->hDC, c->rcView.right, y + para->nHeight - bounds->bottom - 1);
+      MoveToEx(c->hDC, c->pt.x, y + para->nHeight - bounds->bottom - 1, NULL);
+      LineTo(c->hDC, rightEdge, y + para->nHeight - bounds->bottom - 1);
       if (border_details[idx].dble) {
-        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);
+        MoveToEx(c->hDC, c->pt.x + DD(1), y + para->nHeight - bounds->bottom - 1 - pen_width - 1, NULL);
+        LineTo(c->hDC, rightEdge - DD(2), y + para->nHeight - bounds->bottom - 1 - pen_width - 1);
       }
       bounds->bottom += border_width;
     }
@@ -925,8 +928,12 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
   }
   ME_DrawParaDecoration(c, para, y, &bounds);
   y += bounds.top;
-  rc.left += bounds.left;
-  rc.right -= bounds.right;
+  if (bounds.left || bounds.right) {
+    rc.left = max(rc.left, c->pt.x + bounds.left);
+    rc.right = min(rc.right, c->pt.x - bounds.right
+                             + max(c->editor->sizeWindow.cx,
+                                   c->editor->nTotalWidth));
+  }
 
   for (p = paragraph->next; p != para->next_para; p = p->next)
   {
@@ -946,6 +953,16 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
         if (visible) {
           FillRect(c->hDC, &rc, c->editor->hbrBackground);
         }
+        if (bounds.right)
+        {
+          /* If scrolled to the right past the end of the text, then
+           * there may be space to the right of the paragraph border. */
+          RECT rcAfterBrdr = rc;
+          rcAfterBrdr.left = rc.right + bounds.right;
+          rcAfterBrdr.right = c->rcView.right;
+          if (RectVisible(c->hDC, &rcAfterBrdr))
+            FillRect(c->hDC, &rcAfterBrdr, c->editor->hbrBackground);
+        }
         if (me_debug)
         {
           const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0};
@@ -1012,158 +1029,192 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
   SetTextAlign(c->hDC, align);
 }
 
-void ME_ScrollAbs(ME_TextEditor *editor, int absY)
-{
-  ME_Scroll(editor, absY, 1);
-}
-
-void ME_ScrollUp(ME_TextEditor *editor, int cy)
+void ME_ScrollAbs(ME_TextEditor *editor, int x, int y)
 {
-  ME_Scroll(editor, cy, 2);
-}
-
-void ME_ScrollDown(ME_TextEditor *editor, int cy)
-{ 
-  ME_Scroll(editor, cy, 3);
-}
-
-void ME_Scroll(ME_TextEditor *editor, int value, int type)
-{
-  SCROLLINFO si;
-  int nOrigPos, nNewPos, nActualScroll;
-  HWND hWnd;
   LONG winStyle;
   BOOL bScrollBarIsVisible, bScrollBarWillBeVisible;
+  int scrollX = 0, scrollY = 0;
+
+  if (editor->horz_si.nPos != x) {
+    x = min(x, editor->horz_si.nMax);
+    x = max(x, editor->horz_si.nMin);
+    SetScrollPos(editor->hWnd, SB_HORZ, x, TRUE);
+    scrollX = editor->horz_si.nPos - x;
+    editor->horz_si.nPos = x;
+  }
 
-  nOrigPos = ME_GetYScrollPos(editor);
-  
-  si.cbSize = sizeof(SCROLLINFO);
-  si.fMask = SIF_POS;
-  
-  switch (type)
-  {
-    case 1:
-      /*Scroll absolutely*/
-      si.nPos = value;
-      break;
-    case 2:
-      /* Scroll up - towards the beginning of the document */
-      si.nPos = nOrigPos - value;
-      break;
-    case 3:
-      /* Scroll down - towards the end of the document */
-      si.nPos = nOrigPos + value;
-      break;
-    default:
-      FIXME("ME_Scroll called incorrectly\n");
-      si.nPos = 0;
+  if (editor->vert_si.nPos != y) {
+    y = min(y, editor->vert_si.nMax - (int)editor->vert_si.nPage);
+    y = max(y, editor->vert_si.nMin);
+    SetScrollPos(editor->hWnd, SB_VERT, y, TRUE);
+    scrollY = editor->vert_si.nPos - y;
+    editor->vert_si.nPos = y;
   }
-  
-  nNewPos = SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE);
-  editor->vert_si.nPos = nNewPos;
-  nActualScroll = nOrigPos - nNewPos;
-  if (abs(nActualScroll) > editor->sizeWindow.cy)
+
+  if (abs(scrollX) > editor->sizeWindow.cx ||
+      abs(scrollY) > editor->sizeWindow.cy)
     InvalidateRect(editor->hWnd, NULL, TRUE);
   else
-    ScrollWindowEx(editor->hWnd, 0, nActualScroll, &editor->rcFormat,
+    ScrollWindowEx(editor->hWnd, scrollX, scrollY, &editor->rcFormat,
                    &editor->rcFormat, NULL, NULL, SW_INVALIDATE);
   ME_Repaint(editor);
-  
-  hWnd = editor->hWnd;
-  winStyle = GetWindowLongW(hWnd, GWL_STYLE);
+
+  winStyle = GetWindowLongW(editor->hWnd, GWL_STYLE);
+  bScrollBarIsVisible = (winStyle & WS_HSCROLL) != 0;
+  bScrollBarWillBeVisible = (editor->nTotalWidth > editor->sizeWindow.cx)
+                            || (winStyle & ES_DISABLENOSCROLL);
+  if (bScrollBarIsVisible != bScrollBarWillBeVisible)
+    ShowScrollBar(editor->hWnd, SB_HORZ, bScrollBarWillBeVisible);
+
   bScrollBarIsVisible = (winStyle & WS_VSCROLL) != 0;
   bScrollBarWillBeVisible = (editor->nTotalLength > editor->sizeWindow.cy)
                             || (winStyle & ES_DISABLENOSCROLL);
   if (bScrollBarIsVisible != bScrollBarWillBeVisible)
-  {
-    ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
-  }
+    ShowScrollBar(editor->hWnd, SB_VERT, bScrollBarWillBeVisible);
+
   ME_UpdateScrollBar(editor);
 }
 
- 
+void ME_HScrollAbs(ME_TextEditor *editor, int x)
+{
+  ME_ScrollAbs(editor, x, editor->vert_si.nPos);
+}
+
+void ME_VScrollAbs(ME_TextEditor *editor, int y)
+{
+  ME_ScrollAbs(editor, editor->horz_si.nPos, y);
+}
+
+void ME_ScrollUp(ME_TextEditor *editor, int cy)
+{
+  ME_VScrollAbs(editor, editor->vert_si.nPos - cy);
+}
+
+void ME_ScrollDown(ME_TextEditor *editor, int cy)
+{
+  ME_VScrollAbs(editor, editor->vert_si.nPos + cy);
+}
+
+void ME_ScrollLeft(ME_TextEditor *editor, int cx)
+{
+  ME_HScrollAbs(editor, editor->horz_si.nPos - cx);
+}
+
+void ME_ScrollRight(ME_TextEditor *editor, int cx)
+{
+  ME_HScrollAbs(editor, editor->horz_si.nPos + cx);
+}
+
 void ME_UpdateScrollBar(ME_TextEditor *editor)
-{ 
-  /* 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. */
-  
-  HWND hWnd;
+{
+  /* Note that this is the only function that should ever call
+   * SetScrollInfo with SIF_PAGE or SIF_RANGE. */
+
   SCROLLINFO si;
-  BOOL bScrollBarWasVisible,bScrollBarWillBeVisible;
-  
+  BOOL bScrollBarWasVisible, bScrollBarWillBeVisible;
+
   if (ME_WrapMarkedParagraphs(editor))
     FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
-  
-  hWnd = editor->hWnd;
+
   si.cbSize = sizeof(si);
-  bScrollBarWasVisible = ME_GetYScrollVisible(editor);
-  bScrollBarWillBeVisible = editor->nTotalLength > editor->sizeWindow.cy;
+  si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
+  if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_DISABLENOSCROLL)
+    si.fMask |= SIF_DISABLENOSCROLL;
 
-  if (editor->vert_si.nPos && !bScrollBarWillBeVisible)
+  /* Update horizontal scrollbar */
+  bScrollBarWasVisible = editor->horz_si.nMax > editor->horz_si.nPage;
+  bScrollBarWillBeVisible = editor->nTotalWidth > editor->sizeWindow.cx;
+  if (editor->horz_si.nPos && !bScrollBarWillBeVisible)
   {
-    ME_ScrollAbs(editor, 0);
-    /* ME_ScrollAbs will call this function,
+    ME_HScrollAbs(editor, 0);
+    /* ME_HScrollAbs will call this function,
      * so nothing else needs to be done here. */
     return;
   }
 
-  si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
-  if (GetWindowLongW(hWnd, GWL_STYLE) & ES_DISABLENOSCROLL)
+  si.nMin = 0;
+  si.nMax = editor->nTotalWidth;
+  si.nPos = editor->horz_si.nPos;
+  si.nPage = editor->sizeWindow.cx;
+
+  if (si.nMin != editor->horz_si.nMin ||
+      si.nMax != editor->horz_si.nMax ||
+      si.nPage != editor->horz_si.nPage)
   {
-    si.fMask |= SIF_DISABLENOSCROLL;
+    TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
+    editor->horz_si.nMin = si.nMin;
+    editor->horz_si.nMax = si.nMax;
+    editor->horz_si.nPage = si.nPage;
+    if (bScrollBarWillBeVisible || bScrollBarWasVisible)
+      SetScrollInfo(editor->hWnd, SB_HORZ, &si, TRUE);
+  }
+
+  if (si.fMask & SIF_DISABLENOSCROLL)
     bScrollBarWillBeVisible = TRUE;
+
+  if (bScrollBarWasVisible != bScrollBarWillBeVisible)
+    ShowScrollBar(editor->hWnd, SB_HORZ, bScrollBarWillBeVisible);
+
+  /* Update vertical scrollbar */
+  bScrollBarWasVisible = editor->vert_si.nMax > editor->vert_si.nPage;
+  bScrollBarWillBeVisible = editor->nTotalLength > editor->sizeWindow.cy;
+
+  if (editor->vert_si.nPos && !bScrollBarWillBeVisible)
+  {
+    ME_VScrollAbs(editor, 0);
+    /* ME_VScrollAbs will call this function,
+     * so nothing else needs to be done here. */
+    return;
   }
 
-  si.nMin = 0;
   si.nMax = editor->nTotalLength;
   si.nPos = editor->vert_si.nPos;
   si.nPage = editor->sizeWindow.cy;
 
-  if (!(si.nMin == editor->vert_si.nMin &&
-        si.nMax == editor->vert_si.nMax &&
-        si.nPage == editor->vert_si.nPage))
+  if (si.nMin != editor->vert_si.nMin ||
+      si.nMax != editor->vert_si.nMax ||
+      si.nPage != editor->vert_si.nPage)
   {
     TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
     editor->vert_si.nMin = si.nMin;
     editor->vert_si.nMax = si.nMax;
     editor->vert_si.nPage = si.nPage;
     if (bScrollBarWillBeVisible || bScrollBarWasVisible)
-      SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
+      SetScrollInfo(editor->hWnd, SB_VERT, &si, TRUE);
   }
 
+  if (si.fMask & SIF_DISABLENOSCROLL)
+    bScrollBarWillBeVisible = TRUE;
+
   if (bScrollBarWasVisible != bScrollBarWillBeVisible)
-    ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
+    ShowScrollBar(editor->hWnd, SB_VERT, bScrollBarWillBeVisible);
 }
 
-int ME_GetYScrollPos(ME_TextEditor *editor)
+void ME_EnsureVisible(ME_TextEditor *editor, ME_Cursor *pCursor)
 {
-  return editor->vert_si.nPos;
-}
+  ME_Run *pRun = &pCursor->pRun->member.run;
+  ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
+  ME_DisplayItem *pPara = ME_FindItemBack(pCursor->pRun, diParagraph);
+  int x, y, yheight;
 
-BOOL ME_GetYScrollVisible(ME_TextEditor *editor)
-{ /* Returns true if the scrollbar is visible */
-  return (editor->vert_si.nMax - editor->vert_si.nMin > editor->vert_si.nPage);
-}
-
-void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
-{
-  ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow);
-  ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph);
-  int y, yrel, yheight, yold;
-  
   assert(pRow);
   assert(pPara);
-  
-  y = pPara->member.para.pt.y+pRow->member.row.pt.y;
+
+  x = pRun->pt.x + ME_PointFromChar(editor, pRun, pCursor->nOffset);
+  if (x > editor->horz_si.nPos + editor->sizeWindow.cx)
+    x = x + 1 - editor->sizeWindow.cx;
+  else if (x > editor->horz_si.nPos)
+    x = editor->horz_si.nPos;
+
+  y = pPara->member.para.pt.y + pRow->member.row.pt.y;
   yheight = pRow->member.row.nHeight;
-  yold = ME_GetYScrollPos(editor);
-  yrel = y - yold;
-  
-  if (y < yold)
-    ME_ScrollAbs(editor,y);
-  else if (yrel + yheight > editor->sizeWindow.cy) 
-    ME_ScrollAbs(editor,y+yheight-editor->sizeWindow.cy);
+
+  if (y < editor->vert_si.nPos)
+    ME_ScrollAbs(editor, x, y);
+  else if (y + yheight > editor->vert_si.nPos + editor->sizeWindow.cy)
+    ME_ScrollAbs(editor, x, y + yheight - editor->sizeWindow.cy);
+  else if (x != editor->horz_si.nPos)
+    ME_ScrollAbs(editor, x, editor->vert_si.nPos);
 }
 
 
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 8c3281c..0f24ff0 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -621,12 +621,9 @@ static void test_EM_POSFROMCHAR(void)
   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
-  todo_wine {
-  /* Fails on builtin because horizontal scrollbar is not being shown */
   ok((signed short)(LOWORD(result)) < xpos,
         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
         (signed short)(LOWORD(result)), xpos);
-  }
   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
 
   /* Test around end of text that doesn't end in a newline. */
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c
index 7eb844c..9012cfe 100644
--- a/dlls/riched20/wrap.c
+++ b/dlls/riched20/wrap.c
@@ -133,6 +133,7 @@ static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
       }
   }
 
+  para->member.para.nWidth = max(para->member.para.nWidth, width);
   row = ME_MakeRow(ascent+descent, ascent, width);
   if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
       pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
@@ -536,6 +537,7 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
 static void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) {
   ME_DisplayItem *p, *pRow;
 
+  tp->member.para.nWidth = 0;
   /* remove all items that will be reinserted by paragraph wrapper anyway */
   tp->member.para.nRows = 0;
   for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
@@ -578,6 +580,7 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
   ME_Context c;
   BOOL bModified = FALSE;
   int yStart = -1;
+  int totalWidth = 0;
 
   ME_InitContext(&c, editor, GetDC(editor->hWnd));
   c.pt.x = 0;
@@ -638,6 +641,7 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
       ME_DisplayItem *startRowPara;
       int prevHeight, nHeight, bottomBorder = 0;
       ME_DisplayItem *cell = ME_FindItemBack(item, diCell);
+      item->member.para.nWidth = cell->member.cell.pt.x + cell->member.cell.nWidth;
       if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART))
       {
         /* Last row, the bottom border is added to the height. */
@@ -707,12 +711,15 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
       }
       c.pt.y += item->member.para.nHeight;
     }
+
+    totalWidth = max(totalWidth, item->member.para.nWidth);
     item = item->member.para.next_para;
   }
   editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
   editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
-  
+
   editor->nTotalLength = c.pt.y;
+  editor->nTotalWidth = totalWidth;
   editor->pBuffer->pLast->member.para.pt.x = 0;
   editor->pBuffer->pLast->member.para.pt.y = c.pt.y;
 
@@ -732,7 +739,7 @@ void ME_InvalidateMarkedParagraphs(ME_TextEditor *editor)
 
   ME_InitContext(&c, editor, GetDC(editor->hWnd));
   rc = c.rcView;
-  ofs = ME_GetYScrollPos(editor);
+  ofs = editor->vert_si.nPos;
 
   item = editor->pBuffer->pFirst;
   while(item != editor->pBuffer->pLast) {
@@ -772,6 +779,7 @@ ME_SendRequestResize(ME_TextEditor *editor, BOOL force)
       info.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
       info.nmhdr.code = EN_REQUESTRESIZE;
       info.rc = rc;
+      info.rc.right = editor->nTotalWidth;
       info.rc.bottom = editor->nTotalLength;
 
       editor->nEventMask &= ~ENM_REQUESTRESIZE;


More information about the wine-patches mailing list