[PATCH] richedit: do not hide or show vertical scrollbar if app hides or shows it behind our back

Alex Villacís Lasso alex at karlalex.palosanto.com
Tue May 13 00:32:07 CDT 2008


Some applications have never heard of ES_DISABLENOSCROLL and attempt to force scrollbars to be always shown (with ShowScrollBar() or similar) when otherwise richedit would hide them. If richedit attempts to wrestle control back, a recursive loop of requests can result if app overrides WM_SIZE behavior. So let the app have its way. Richedit should reclaim control when forced visibility matches default automatic behavior.
Tests to verify this behavior.
---
 dlls/riched20/editor.c       |    3 +-
 dlls/riched20/editstr.h      |    1 +
 dlls/riched20/paint.c        |   44 +++++++++--
 dlls/riched20/tests/editor.c |  173 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 214 insertions(+), 7 deletions(-)

diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 81382b3..9f18d5f 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -1685,7 +1685,7 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) {
     ed->cPasswordMask = 0;
   
   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
-
+  ed->bVScrollEnabled = FALSE;
   return ed;
 }
 
@@ -2923,6 +2923,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
     return MAKELONG( pt.x, pt.y );
   }
   case WM_CREATE:
+    editor->bVScrollEnabled = ((GetWindowLongW(hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
     if (GetWindowLongW(hWnd, GWL_STYLE) & WS_HSCROLL)
     { /* Squelch the default horizontal scrollbar it would make */
       ShowScrollBar(editor->hWnd, SB_HORZ, FALSE);
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index 1e2c770..20aac0d 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -334,6 +334,7 @@ typedef struct tagME_TextEditor
 
   /* Track previous notified selection */
   CHARRANGE notified_cr;
+  BOOL bVScrollEnabled;
 } ME_TextEditor;
 
 typedef struct tagME_Context
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index df2eda8..7bb1a01 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -651,20 +651,34 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
    * be used at all. */
   
   HWND hWnd;
-  SCROLLINFO si;
+  SCROLLINFO si, si_prev;
+  BOOL bCurrentEnabled;
   BOOL bScrollBarWasVisible,bScrollBarWillBeVisible;
+  BOOL bForcedVisible = FALSE, bForcedInvisible = FALSE;
   
   if (ME_WrapMarkedParagraphs(editor))
     FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
   
+  bCurrentEnabled = ((GetWindowLongW(editor->hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
+  if (editor->bVScrollEnabled != bCurrentEnabled)
+  {
+    bForcedInvisible = !(GetWindowLongW(editor->hWnd, GWL_STYLE) & WS_VSCROLL);
+    bForcedVisible = !bForcedInvisible;
+  }
+
   hWnd = editor->hWnd;
   si.cbSize = sizeof(si);
   bScrollBarWasVisible = ME_GetYScrollVisible(editor);
   bScrollBarWillBeVisible = editor->nHeight > editor->sizeWindow.cy;
-  
+  editor->bVScrollEnabled = bScrollBarWillBeVisible;
   if (bScrollBarWasVisible != bScrollBarWillBeVisible)
   {
-    ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
+    if ((bForcedVisible || bForcedInvisible) && (bForcedVisible != bScrollBarWillBeVisible))
+      ShowScrollBar(hWnd, SB_VERT, bForcedVisible);
+    else if (!(bForcedVisible || bForcedInvisible))
+      ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
+    if (!bForcedVisible && !bForcedInvisible)
+      editor->bVScrollEnabled = ((GetWindowLongW(hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
     ME_MarkAllForWrapping(editor);
     ME_WrapMarkedParagraphs(editor);
   }
@@ -679,12 +693,30 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
   {
     si.nMax = editor->nTotalLength;
     si.nPage = editor->sizeWindow.cy;
+    if (bForcedVisible)
+    {
+      TRACE("Setting page to max to preserve scrollbar...\n");
+      si.nPage = si.nMax;
+    }
   } else {
     si.nMax = si.nPage = 0;
+    if (bForcedVisible)
+    {
+      TRACE("Setting page = max = 1 to preserve scrollbar...\n");
+      si.nPage = si.nMax = 1;
+    }
+  }
+
+  si_prev.cbSize = sizeof(si_prev);
+  si_prev.fMask = SIF_PAGE | SIF_RANGE;
+  GetScrollInfo(hWnd, SB_VERT, &si_prev);
+  if (!(si_prev.nMax == si.nMax && si_prev.nMin == si.nMin && si_prev.nMax == si.nMax))
+  {
+    TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
+    if (!bForcedVisible && !bForcedInvisible)
+      editor->bVScrollEnabled = (si.nMax > si.nPage);
+    SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
   }
-     
-  TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
-  SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
 }
 
 int ME_GetYScrollPos(ME_TextEditor *editor)
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 5dabe62..c17cc40 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -2055,6 +2055,178 @@ static void test_EM_SCROLL(void)
   DestroyWindow(hwndRichEdit);
 }
 
+static void test_scrollbar_visibility(void)
+{
+  HWND hwndRichEdit = new_richedit(NULL);
+  const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
+  SCROLLBARINFO sbi;
+
+  /* These tests show that richedit should temporarily refrain from automatically
+     hiding or showing its scrollbars (vertical at least) when an explicit request
+     is made via ShowScrollBar() or similar, outside of standard richedit logic.
+     Some applications depend on forced showing (when otherwise richedit would
+     hide the vertical scrollbar) and are thrown on an endless recursive loop
+     if richedit auto-hides the scrollbar again. Apparently they never heard of
+     the ES_DISABLENOSCROLL style... */
+
+  /* Test default scrollbar visibility behavior */
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  DestroyWindow(hwndRichEdit);
+
+  /* Test behavior with explicit visibility request */
+  hwndRichEdit = new_richedit(NULL);
+
+  /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
+  trace("explicitly requesting visible scrollbar\n");
+  ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  /* Ditto, see above */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  /* Ditto, see above */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  /* Ditto, see above */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  /* Ditto, see above */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  DestroyWindow(hwndRichEdit);
+
+  hwndRichEdit = new_richedit(NULL);
+
+  trace("explicitly requesting invisible scrollbar\n");
+  ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
+  trace("explicitly requesting invisible scrollbar\n");
+  ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0), 
+    "Vertical scrollbar is visible, should be invisible.\n");
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+  memset(&sbi, 0, sizeof(sbi));
+  sbi.cbSize = sizeof(sbi);
+  GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+  trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+  ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0), 
+    "Vertical scrollbar is invisible, should be visible.\n");
+
+  DestroyWindow(hwndRichEdit);
+}
+
 static void test_EM_SETUNDOLIMIT(void)
 {
   /* cases we test for:
@@ -4168,6 +4340,7 @@ START_TEST( editor )
   test_EM_GETLINE();
   test_EM_SCROLLCARET();
   test_EM_SCROLL();
+  test_scrollbar_visibility();
   test_WM_SETTEXT();
   test_EM_LINELENGTH();
   test_EM_SETCHARFORMAT();
-- 
1.5.4.1


--------------010303010100050309090304--



More information about the wine-patches mailing list