Commit aa636740 authored by C. Scott Ananian's avatar C. Scott Ananian Committed by Alexandre Julliard

EDIT_EM_SetSel: Old/new selection range ordering code would break when

old_end < start < end < old_start. Reset EF_UPDATE flag after we've sent the update.
parent 8cf13894
...@@ -3192,7 +3192,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replac ...@@ -3192,7 +3192,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replac
EDIT_UpdateScrollInfo(es); EDIT_UpdateScrollInfo(es);
if(es->flags & EF_UPDATE) if(send_update || (es->flags & EF_UPDATE))
{ {
es->flags &= ~EF_UPDATE; es->flags &= ~EF_UPDATE;
EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE"); EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE");
...@@ -3603,11 +3603,23 @@ static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap) ...@@ -3603,11 +3603,23 @@ static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
es->flags |= EF_AFTER_WRAP; es->flags |= EF_AFTER_WRAP;
else else
es->flags &= ~EF_AFTER_WRAP; es->flags &= ~EF_AFTER_WRAP;
/* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */ /* Compute the necessary invalidation region. */
ORDER_UINT(start, end); /* Note that we don't need to invalidate regions which have
* "never" been selected, or those which are "still" selected.
* In fact, every time we hit a selection boundary, we can
* *toggle* whether we need to invalidate. Thus we can optimize by
* *sorting* the interval endpoints. Let's assume that we sort them
* in this order:
* start <= end <= old_start <= old_end
* Knuth 5.3.1 (p 183) asssures us that this can be done optimally
* in 5 comparisons; ie it's impossible to do better than the
* following: */
ORDER_UINT(end, old_end); ORDER_UINT(end, old_end);
ORDER_UINT(start, old_start); ORDER_UINT(start, old_start);
ORDER_UINT(old_start, old_end); ORDER_UINT(old_start, old_end);
ORDER_UINT(start, end);
/* Note that at this point 'end' and 'old_start' are not in order, but
* start is definitely the min. and old_end is definitely the max. */
if (end != old_start) if (end != old_start)
{ {
/* /*
...@@ -3616,6 +3628,7 @@ static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap) ...@@ -3616,6 +3628,7 @@ static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
* EDIT_InvalidateText(es, start, end); * EDIT_InvalidateText(es, start, end);
* EDIT_InvalidateText(es, old_start, old_end); * EDIT_InvalidateText(es, old_start, old_end);
* in place of the following if statement. * in place of the following if statement.
* (That would complete the optimal five-comparison four-element sort.)
*/ */
if (old_start > end ) if (old_start > end )
{ {
...@@ -4781,6 +4794,11 @@ static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text, BOOL unicode) ...@@ -4781,6 +4794,11 @@ static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text, BOOL unicode)
text = textW; text = textW;
} }
if (es->flags & EF_UPDATE)
/* fixed this bug once; complain if we see it about to happen again. */
ERR("SetSel may generate UPDATE message whose handler may reset "
"selection.\n");
EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE); EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE);
if (text) if (text)
{ {
...@@ -5047,7 +5065,10 @@ static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos) ...@@ -5047,7 +5065,10 @@ static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos)
*/ */
static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase) static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
{ {
if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE"); if (es->flags & EF_UPDATE) {
es->flags &= ~EF_UPDATE;
EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE");
}
InvalidateRgn(es->hwndSelf, hrgn, bErase); InvalidateRgn(es->hwndSelf, hrgn, bErase);
} }
...@@ -5059,6 +5080,9 @@ static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase) ...@@ -5059,6 +5080,9 @@ static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
*/ */
static void EDIT_UpdateText(EDITSTATE *es, LPRECT rc, BOOL bErase) static void EDIT_UpdateText(EDITSTATE *es, LPRECT rc, BOOL bErase)
{ {
if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE"); if (es->flags & EF_UPDATE) {
es->flags &= ~EF_UPDATE;
EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE");
}
InvalidateRect(es->hwndSelf, rc, bErase); InvalidateRect(es->hwndSelf, rc, bErase);
} }
/* Unit test suite for edit control. /* Unit test suite for edit control.
* *
* Copyright 2004 Vitaliy Margolen * Copyright 2004 Vitaliy Margolen
* Copyright 2005 C. Scott Ananian
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
#include <assert.h> #include <assert.h>
#include <windows.h> #include <windows.h>
#include <windowsx.h>
#include <commctrl.h> #include <commctrl.h>
#include "wine/test.h" #include "wine/test.h"
...@@ -27,6 +29,12 @@ ...@@ -27,6 +29,12 @@
#define ES_COMBO 0x200 #define ES_COMBO 0x200
#endif #endif
#define ID_EDITTEST2 99
#define MAXLEN 200
static char szEditTest2Name[] = "Edit Test 2 window class";
static HINSTANCE hinst;
static HWND hwndET2;
HWND create_editcontrol (DWORD style, DWORD exstyle) HWND create_editcontrol (DWORD style, DWORD exstyle)
{ {
...@@ -79,7 +87,7 @@ static void set_client_height(HWND Wnd, unsigned Height) ...@@ -79,7 +87,7 @@ static void set_client_height(HWND Wnd, unsigned Height)
SWP_NOMOVE | SWP_NOZORDER); SWP_NOMOVE | SWP_NOZORDER);
} }
static void test_edit_control(void) static void test_edit_control_1(void)
{ {
HWND hwEdit; HWND hwEdit;
MSG msMessage; MSG msMessage;
...@@ -268,7 +276,95 @@ static void test_edit_control(void) ...@@ -268,7 +276,95 @@ static void test_edit_control(void)
DestroyWindow(hwEdit); DestroyWindow(hwEdit);
} }
/* WM_SETTEXT is implemented by selecting all text, and then replacing the
* selection. This test checks that the first 'select all' doesn't generate
* an UPDATE message which can escape and (via a handler) change the
* selection, which would cause WM_SETTEXT to break. This old bug
* was fixed 18-Mar-2005; we check here to ensure it doesn't regress.
*/
static void test_edit_control_2(void)
{
HWND hwndMain;
char szLocalString[MAXLEN];
/* Create main and edit windows. */
hwndMain = CreateWindow(szEditTest2Name, "ET2", WS_OVERLAPPEDWINDOW,
0, 0, 200, 200, NULL, NULL, hinst, NULL);
assert(hwndMain);
if (winetest_interactive)
ShowWindow (hwndMain, SW_SHOW);
hwndET2 = CreateWindow("EDIT", NULL,
WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,
0, 0, 150, 50, /* important this not be 0 size. */
hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL);
assert(hwndET2);
if (winetest_interactive)
ShowWindow (hwndET2, SW_SHOW);
trace("EDIT: SETTEXT atomicity\n");
/* Send messages to "type" in the word 'foo'. */
SendMessage(hwndET2, WM_CHAR, 'f', 1);
SendMessage(hwndET2, WM_CHAR, 'o', 1);
SendMessage(hwndET2, WM_CHAR, 'o', 1);
/* 'foo' should have been changed to 'bar' by the UPDATE handler. */
GetWindowText(hwndET2, szLocalString, MAXLEN);
ok(lstrcmp(szLocalString, "bar")==0,
"Wrong contents of edit: %s\n", szLocalString);
/* OK, done! */
DestroyWindow (hwndET2);
DestroyWindow (hwndMain);
}
static void ET2_check_change() {
char szLocalString[MAXLEN];
/* This EN_UPDATE handler changes any 'foo' to 'bar'. */
GetWindowText(hwndET2, szLocalString, MAXLEN);
if (lstrcmp(szLocalString, "foo")==0) {
lstrcpy(szLocalString, "bar");
SendMessage(hwndET2, WM_SETTEXT, 0, (LPARAM) szLocalString);
}
/* always leave the cursor at the end. */
SendMessage(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1);
}
static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
if (id==ID_EDITTEST2 && codeNotify == EN_UPDATE)
ET2_check_change();
}
static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
switch (iMsg) {
HANDLE_MSG(hwnd, WM_COMMAND, ET2_OnCommand);
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
static BOOL RegisterWindowClasses (void)
{
WNDCLASSA cls;
cls.style = 0;
cls.lpfnWndProc = ET2_WndProc;
cls.cbClsExtra = 0;
cls.cbWndExtra = 0;
cls.hInstance = hinst;
cls.hIcon = NULL;
cls.hCursor = LoadCursorA (NULL, IDC_ARROW);
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
cls.lpszMenuName = NULL;
cls.lpszClassName = szEditTest2Name;
if (!RegisterClassA (&cls)) return FALSE;
return TRUE;
}
START_TEST(edit) START_TEST(edit)
{ {
test_edit_control(); hinst = GetModuleHandleA (NULL);
if (!RegisterWindowClasses())
assert(0);
test_edit_control_1();
test_edit_control_2();
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment