Fixed some problems with scrolling in the edit control.

diff --git a/controls/edit.c b/controls/edit.c
index b0ca3f3..5ccc440 100644
--- a/controls/edit.c
+++ b/controls/edit.c
@@ -90,6 +90,8 @@
 	INT left_margin;		/* in pixels */
 	INT right_margin;		/* in pixels */
 	RECT format_rect;
+	INT text_width;			/* width of the widest line in pixels for multi line controls
+					   and just line width for single line controls	*/
 	INT region_posx;		/* Position of cursor relative to region: */
 	INT region_posy;		/* -1: to left, 0: within, 1: to right */
 	EDITWORDBREAKPROC16 word_break_proc16;
@@ -105,7 +107,6 @@
 	INT lock_count;		/* amount of re-entries in the EditWndProc */
 	INT tabs_count;
 	LPINT tabs;
-	INT text_width;		/* width of the widest line in pixels */
 	LINEDEF *first_line_def;	/* linked list of (soft) linebreaks */
 	HLOCAL hloc32W;		/* our unicode local memory block */
 	HLOCAL16 hloc16;	/* alias for 16-bit control receiving EM_GETHANDLE16
@@ -167,6 +168,7 @@
  *	Helper functions only valid for one type of control
  */
 static void	EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
+static void	EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es);
 static LPWSTR	EDIT_GetPasswordPointer_SL(EDITSTATE *es);
 static void	EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
 static void	EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
@@ -194,6 +196,7 @@
 static void	EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap); 
 static void	EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
 static void	EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
+static void	EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es);
 static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
 /*
  *	EM_XXX message handlers
@@ -209,6 +212,7 @@
 static INT	EDIT_EM_LineIndex(EDITSTATE *es, INT line);
 static INT	EDIT_EM_LineLength(EDITSTATE *es, INT index);
 static BOOL	EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
+static BOOL	EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy);
 static LRESULT	EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
 static void	EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update);
 static LRESULT	EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
@@ -1221,6 +1225,15 @@
 	ReleaseDC(wnd->hwndSelf, dc);
 }
 
+/*********************************************************************
+ *
+ *	EDIT_CalcLineWidth_SL
+ *
+ */
+static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es)
+{
+    es->text_width = SLOWORD(EDIT_EM_PosFromChar(wnd, es, strlenW(es->text), FALSE));
+}
 
 /*********************************************************************
  *
@@ -1474,7 +1487,7 @@
 
 	    if(es->hloc32W)
 	    {
-		TRACE("Locking 32-bit UNICODE buffer\n");
+		/*TRACE("Locking 32-bit UNICODE buffer\n");*/
 		es->text = LocalLock(es->hloc32W);
 
 		if(es->hloc32A)
@@ -2047,19 +2060,7 @@
 			     BOOL after_wrap)
 {
 	LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
-	INT x = SLOWORD(res);
-	INT y = SHIWORD(res);
-
-	if(x < es->format_rect.left)
-		x = es->format_rect.left;
-	if(x > es->format_rect.right - 2)
-		x = es->format_rect.right - 2;
-	if(y > es->format_rect.bottom)
-		y = es->format_rect.bottom;
-	if(y < es->format_rect.top)
-		y = es->format_rect.top;
-	SetCaretPos(x, y);
-	return;
+	SetCaretPos(SLOWORD(res), SHIWORD(res));
 }
 
 
@@ -2087,10 +2088,29 @@
 	es->format_rect.right -= es->right_margin;
 	es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
 	if (es->style & ES_MULTILINE)
-		es->format_rect.bottom = es->format_rect.top +
-			max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
+	{
+	    INT fw, vlc, max_x_offset, max_y_offset;
+
+	    vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
+	    es->format_rect.bottom = es->format_rect.top + max(1, vlc) * es->line_height;
+
+	    /* correct es->x_offset */
+	    fw = es->format_rect.right - es->format_rect.left;
+	    max_x_offset = es->text_width - fw;
+	    if(max_x_offset < 0) max_x_offset = 0;
+	    if(es->x_offset > max_x_offset)
+		es->x_offset = max_x_offset;
+
+	    /* correct es->y_offset */
+	    max_y_offset = es->line_count - vlc;
+	    if(max_y_offset < 0) max_y_offset = 0;
+	    if(es->y_offset > max_y_offset)
+		es->y_offset = max_y_offset;
+	}
 	else
+	/* Windows doesn't care to fix text placement for SL controls */
 		es->format_rect.bottom = es->format_rect.top + es->line_height;
+
 	if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
 		EDIT_BuildLineDefs_ML(wnd, es);
 }
@@ -2159,6 +2179,42 @@
 
 /*********************************************************************
  *
+ *	EDIT_UpdateScrollInfo
+ *
+ */
+static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es)
+{
+    if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
+    {
+	SCROLLINFO si;
+	si.cbSize	= sizeof(SCROLLINFO);
+	si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
+	si.nMin		= 0;
+	si.nMax		= es->line_count - 1;
+	si.nPage	= (es->format_rect.bottom - es->format_rect.top) / es->line_height;
+	si.nPos		= es->y_offset;
+	TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
+		si.nMin, si.nMax, si.nPage, si.nPos);
+	SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
+    }
+
+    if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
+    {
+	SCROLLINFO si;
+	si.cbSize	= sizeof(SCROLLINFO);
+	si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
+	si.nMin		= 0;
+	si.nMax		= es->text_width - 1;
+	si.nPage	= es->format_rect.right - es->format_rect.left;
+	si.nPos		= es->x_offset;
+	TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
+		si.nMin, si.nMax, si.nPage, si.nPos);
+	SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
+    }
+}
+
+/*********************************************************************
+ *
  *	EDIT_WordBreakProc
  *
  *	Find the beginning of words.
@@ -2275,7 +2331,7 @@
  *
  *	Hopefully this won't fire back at us.
  *	We always start with a fixed buffer in the local heap.
- *	Despite of the documenation says that the local heap is used
+ *	Despite of the documentation says that the local heap is used
  *	only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
  *	buffer on the local heap.
  *
@@ -2542,22 +2598,46 @@
  *
  *	EM_LINESCROLL
  *
- *	FIXME: dx is in average character widths
- *		However, we assume it is in pixels when we use this
- *		function internally
+ *	NOTE: dx is in average character widths, dy - in lines;
  *
  */
 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
 {
-	INT nyoff;
-
 	if (!(es->style & ES_MULTILINE))
 		return FALSE;
 
-	if (-dx > es->x_offset)
-		dx = -es->x_offset;
-	if (dx > es->text_width - es->x_offset)
-		dx = es->text_width - es->x_offset;
+	dx *= es->char_width;
+	return EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
+}
+
+/*********************************************************************
+ *
+ *	EDIT_EM_LineScroll_internal
+ *
+ *	Version of EDIT_EM_LineScroll for internal use.
+ *	It doesn't refuse if ES_MULTILINE is set and assumes that
+ *	dx is in pixels, dy - in lines.
+ *
+ */
+static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy)
+{
+	INT nyoff;
+	INT x_offset_in_pixels;
+
+	if (es->style & ES_MULTILINE)
+	{
+	    x_offset_in_pixels = es->x_offset;
+	}
+	else
+	{
+	    dy = 0;
+	    x_offset_in_pixels = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->x_offset, FALSE));
+	}
+
+	if (-dx > x_offset_in_pixels)
+		dx = -x_offset_in_pixels;
+	if (dx > es->text_width - x_offset_in_pixels)
+		dx = es->text_width - x_offset_in_pixels;
 	nyoff = max(0, es->y_offset + dy);
 	if (nyoff >= es->line_count)
 		nyoff = es->line_count - 1;
@@ -2565,12 +2645,17 @@
 	if (dx || dy) {
 		RECT rc1;
 		RECT rc;
+
+		es->y_offset = nyoff;
+		if(es->style & ES_MULTILINE)
+		    es->x_offset += dx;
+		else
+		    es->x_offset += dx / es->char_width;
+
 		GetClientRect(wnd->hwndSelf, &rc1);
 		IntersectRect(&rc, &rc1, &es->format_rect);
 		ScrollWindowEx(wnd->hwndSelf, -dx, dy,
 				NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
-		es->y_offset = nyoff;
-		es->x_offset += dx;
 	}
 	if (dx && !(es->flags & EF_HSCROLL_TRACK))
 		EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
@@ -2742,6 +2827,8 @@
 	/* FIXME: really inefficient */
 	if (es->style & ES_MULTILINE)
 		EDIT_BuildLineDefs_ML(wnd, es);
+	else
+	    EDIT_CalcLineWidth_SL(wnd, es);
 
 	EDIT_EM_SetSel(wnd, es, s, s, FALSE);
 	es->flags |= EF_MODIFIED;
@@ -2788,8 +2875,14 @@
 		return (LRESULT)FALSE;
 	}
 	if (dy) {
+	    INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
+	    /* check if we are going to move too far */
+	    if(es->y_offset + dy > es->line_count - vlc)
+		dy = es->line_count - vlc - es->y_offset;
+
+	    /* Notification is done in EDIT_EM_LineScroll */
+	    if(dy)
 		EDIT_EM_LineScroll(wnd, es, 0, dy);
-		EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
 	}
 	return MAKELONG((INT16)dy, (BOOL16)TRUE);
 }
@@ -2826,7 +2919,13 @@
 		if (x > es->format_rect.right)
 			dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
 		if (dy || dx)
-			EDIT_EM_LineScroll(wnd, es, dx, dy);
+		{
+		    /* check if we are going to move too far */
+		    if(es->x_offset + dx + ww > es->text_width)
+			dx = es->text_width - ww - es->x_offset;
+		    if(dx || dy)
+			EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
+		}
 	} else {
 		INT x;
 		INT goal;
@@ -2858,6 +2957,9 @@
 			EDIT_UpdateText(wnd, NULL, TRUE);
 		}
 	}
+
+    if(es->flags & EF_FOCUSED)
+	EDIT_SetCaretPos(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
 }
 
 
@@ -3107,8 +3209,6 @@
 		es->flags |= EF_AFTER_WRAP;
 	else
 		es->flags &= ~EF_AFTER_WRAP;
-	if (es->flags & EF_FOCUSED)
-		EDIT_SetCaretPos(wnd, es, end, after_wrap);
 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
         ORDER_UINT(start, end);
         ORDER_UINT(end, old_end);
@@ -3242,6 +3342,7 @@
 	EDIT_EM_EmptyUndoBuffer(es);
 	EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
 	EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
+	EDIT_EM_ScrollCaret(wnd, es);
 	HeapFree(GetProcessHeap(), 0, utext);
 
 	TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
@@ -3636,7 +3737,14 @@
 		return 0;
 	}
 	if (dx)
-		EDIT_EM_LineScroll(wnd, es, dx, 0);
+	{
+	    INT fw = es->format_rect.right - es->format_rect.left;
+	    /* check if we are going to move too far */
+	    if(es->x_offset + dx + fw > es->text_width)
+		dx = es->text_width - fw - es->x_offset;
+	    if(dx)
+		EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
+	}
 	return ret;
 }
 
@@ -3706,7 +3814,14 @@
 		return 0;
 	}
 	if (dx)
-		EDIT_EM_LineScroll(wnd, es, dx, 0);
+	{
+	    INT fw = es->format_rect.right - es->format_rect.left;
+	    /* check if we are going to move too far */
+	    if(es->x_offset + dx + fw > es->text_width)
+		dx = es->text_width - fw - es->x_offset;
+	    if(dx)
+		EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
+	}
 	return 0;
 }
 
@@ -4180,33 +4295,11 @@
 	}
 	if (es->font)
 		SelectObject(dc, old_font);
-	if (es->flags & EF_FOCUSED)
-		EDIT_SetCaretPos(wnd, es, es->selection_end,
-				 es->flags & EF_AFTER_WRAP);
+
         if (!wParam)
             EndPaint(wnd->hwndSelf, &ps);
-	if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
-		INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
-		SCROLLINFO si;
-		si.cbSize	= sizeof(SCROLLINFO);
-		si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
-		si.nMin		= 0;
-		si.nMax		= es->line_count + vlc - 2;
-		si.nPage	= vlc;
-		si.nPos		= es->y_offset;
-		SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
-	}
-	if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
-		SCROLLINFO si;
-		INT fw = es->format_rect.right - es->format_rect.left;
-		si.cbSize	= sizeof(SCROLLINFO);
-		si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
-		si.nMin		= 0;
-		si.nMax		= es->text_width + fw - 1;
-		si.nPage	= fw;
-		si.nPos		= es->x_offset;
-		SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
-	}
+
+	EDIT_UpdateScrollInfo(wnd, es);
 }
 
 
@@ -4289,6 +4382,8 @@
 
 	if (es->style & ES_MULTILINE)
 		EDIT_BuildLineDefs_ML(wnd, es);
+	else
+	    EDIT_CalcLineWidth_SL(wnd, es);
 
 	if (redraw)
 		EDIT_UpdateText(wnd, NULL, TRUE);