diff -Naur wine-0.9.6-pristine/configure.ac wine_full/wine-0.9.6/configure.ac --- wine-0.9.6-pristine/configure.ac 2006-01-19 06:13:50.000000000 -0800 +++ wine_full/wine-0.9.6/configure.ac 2006-01-24 13:53:10.686945000 -0800 @@ -1655,6 +1655,7 @@ dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/riched20/Makefile +dlls/riched20/tests/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile diff -Naur wine-0.9.6-pristine/dlls/riched20/Makefile.in wine_full/wine-0.9.6/dlls/riched20/Makefile.in --- wine-0.9.6-pristine/dlls/riched20/Makefile.in 2006-01-19 06:14:11.000000000 -0800 +++ wine_full/wine-0.9.6/dlls/riched20/Makefile.in 2006-01-24 13:53:24.300313000 -0800 @@ -24,6 +24,8 @@ wrap.c \ writer.c +SUBDIRS = tests + @MAKE_DLL_RULES@ ### Dependencies: diff -Naur wine-0.9.6-pristine/dlls/riched20/editor.c wine_full/wine-0.9.6/dlls/riched20/editor.c --- wine-0.9.6-pristine/dlls/riched20/editor.c 2006-01-19 06:14:11.000000000 -0800 +++ wine_full/wine-0.9.6/dlls/riched20/editor.c 2006-01-31 15:44:10.870159000 -0800 @@ -64,7 +64,7 @@ - EM_GETREDONAME 2.0 + EM_GETSEL + EM_GETSELTEXT (ANSI&Unicode) - - EM_GETSCROLLPOS 3.0 + + EM_GETSCROLLPOS 3.0 (only Y value valid) ! - EM_GETTHUMB - EM_GETTEXTEX 2.0 + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented) @@ -88,7 +88,7 @@ + EM_REQUESTRESIZE + EM_REPLACESEL (proper style?) ANSI&Unicode - EM_SCROLL - - EM_SCROLLCARET + + EM_SCROLLCARET - EM_SELECTIONTYPE - EM_SETBIDIOPTIONS 3.0 + EM_SETBKGNDCOLOR @@ -1190,7 +1190,6 @@ UNSUPPORTED_MSG(EM_GETOPTIONS) UNSUPPORTED_MSG(EM_GETPASSWORDCHAR) UNSUPPORTED_MSG(EM_GETREDONAME) - UNSUPPORTED_MSG(EM_GETSCROLLPOS) UNSUPPORTED_MSG(EM_GETTEXTMODE) UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS) UNSUPPORTED_MSG(EM_GETUNDONAME) @@ -1199,7 +1198,6 @@ UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */ UNSUPPORTED_MSG(EM_PASTESPECIAL) UNSUPPORTED_MSG(EM_SCROLL) - UNSUPPORTED_MSG(EM_SCROLLCARET) UNSUPPORTED_MSG(EM_SELECTIONTYPE) UNSUPPORTED_MSG(EM_SETBIDIOPTIONS) UNSUPPORTED_MSG(EM_SETEDITSTYLE) @@ -1501,6 +1499,41 @@ ME_UpdateRepaint(editor); return 0; } + case EM_SCROLLCARET: + { + int top, bottom; /* row's Y values relative to document top */ + ME_DisplayItem *para, *row; /* paragraph & row that contain with run */ + + row = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow); + para = ME_FindItemBack(row, diParagraph); + top = para->member.para.nYPos + row->member.row.nYPos; + bottom = top + row->member.row.nHeight; + + if ((top < editor->nScrollPosY) + || (editor->nScrollPosY + editor->sizeWindow.cy < bottom)) + { + int dy; + int prevScrollPosY = editor->nScrollPosY; + + if (top < editor->nScrollPosY) /* caret above window */ + editor->nScrollPosY = top; + else /* caret below window */ + editor->nScrollPosY = bottom - editor->sizeWindow.cy; + + if (editor->nScrollPosY < 0) + editor->nScrollPosY = 0; + + dy = prevScrollPosY - editor->nScrollPosY; + SetScrollPos(hWnd, SB_VERT, editor->nScrollPosY, TRUE); + if (editor->bRedraw) + { + ScrollWindow(hWnd, 0, dy, NULL, NULL); + UpdateWindow(hWnd); + } + } + + return 0; + } case WM_SETTEXT: { ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); @@ -1661,6 +1694,13 @@ tr.lpstrText = (WCHAR *)lParam; return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); } + case EM_GETSCROLLPOS: + { + POINT *point = (POINT *)lParam; + point->x = 0; /* FIXME get this */ + point->y = editor->nScrollPosY; + return 1; + } case EM_GETTEXTRANGE: { TEXTRANGEW *rng = (TEXTRANGEW *)lParam; diff -Naur wine-0.9.6-pristine/dlls/riched20/list.c wine_full/wine-0.9.6/dlls/riched20/list.c --- wine-0.9.6-pristine/dlls/riched20/list.c 2006-01-19 06:14:11.000000000 -0800 +++ wine_full/wine-0.9.6/dlls/riched20/list.c 2006-01-31 15:10:03.205420000 -0800 @@ -171,10 +171,10 @@ TRACE("Start\n"); break; case diParagraph: - TRACE("Paragraph(ofs=%d)\n", pItem->member.para.nCharOfs); + TRACE("Paragraph(ofs=%d,y=%d,h=%d)\n", pItem->member.para.nCharOfs, pItem->member.para.nYPos, pItem->member.para.nHeight); break; case diStartRow: - TRACE(" - StartRow\n"); + TRACE(" - StartRow(y=%d,h=%d)\n", pItem->member.row.nYPos, pItem->member.row.nHeight); break; case diRun: TRACE(" - Run(\"%s\", %d)\n", debugstr_w(pItem->member.run.strText->szData), diff -Naur wine-0.9.6-pristine/dlls/riched20/tests/Makefile.in wine_full/wine-0.9.6/dlls/riched20/tests/Makefile.in --- wine-0.9.6-pristine/dlls/riched20/tests/Makefile.in 1969-12-31 16:00:00.000000000 -0800 +++ wine_full/wine-0.9.6/dlls/riched20/tests/Makefile.in 2006-01-24 13:53:24.319294000 -0800 @@ -0,0 +1,13 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +TESTDLL = riched20.dll +IMPORTS = riched20 user32 gdi32 kernel32 + +CTESTS = \ + editor.c + +@MAKE_TEST_RULES@ + +### Dependencies: diff -Naur wine-0.9.6-pristine/dlls/riched20/tests/editor.c wine_full/wine-0.9.6/dlls/riched20/tests/editor.c --- wine-0.9.6-pristine/dlls/riched20/tests/editor.c 1969-12-31 16:00:00.000000000 -0800 +++ wine_full/wine-0.9.6/dlls/riched20/tests/editor.c 2006-01-31 15:48:00.782075000 -0800 @@ -0,0 +1,844 @@ +/* +* Unit test suite for rich edit control +* +* Copyright 2006 Google (Thomas Kho) +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include + +static HMODULE hmoduleRichEdit; +static int EN_LINK_rcvd = 0; + +const char tinytext[] = "wine"; +static const char haystack[] = "Think of Wine as a compatibility layer for " + "running Windows programs. Wine does not require Microsoft Windows, as it " + "is a completely free alternative implementation of the Windows API " + "consisting of 100% non-Microsoft code, however Wine can optionally use " + "native Windows DLLs if they are available. Wine provides both a " + "development toolkit for porting Windows source code to Unix as well as a " + "program loader, allowing many unmodified Windows programs to run on " + "x86-based Unixes, including Linux, FreeBSD, and Solaris."; +static const char testrtf[] = "{\\rtf1\\ansi\\deff0\\adeflang1025\ +{\\fonttbl{\\f0\\froman\\fprq2\\fcharset0 Times New Roman;}{\\f1\\froman\\fprq2\\fcharset0 Times New Roman;}{\\f2\\fnil\\fprq2\\fcharset0 Arial;}{\\f3\\fnil\\fprq2\\fcharset0 Lucidasans;}{\\f4\\fnil\\fprq0\\fcharset0 Lucidasans;}}\ +{\\colortbl;\\red0\\green0\\blue0;\\red220\\green35\\blue0;\\red0\\green0\\blue255;\\red255\\green255\\blue0;\\red128\\green128\\blue128;}\ +{\\stylesheet{\\s1\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af3\\afs24\\lang255\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs24\\lang1033\\loch\\f0\\fs24\\lang1033\\snext1 Normal;}\ +{\\s2\\sa120\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af3\\afs24\\lang255\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs24\\lang1033\\loch\\f0\\fs24\\lang1033\\sbasedon1\\snext2 Body Text;}\ +{\\s3\\sa120\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af4\\afs24\\lang255\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs24\\lang1033\\loch\\f0\\fs24\\lang1033\\sbasedon2\\snext3 List;}\ +{\\s4\\sb120\\sa120\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af4\\afs20\\lang255\\ai\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs20\\lang1033\\i\\loch\\f0\\fs20\\lang1033\\i\\sbasedon1\\snext4 caption;}\ +{\\s5\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af4\\afs24\\lang255\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs24\\lang1033\\loch\\f0\\fs24\\lang1033\\sbasedon1\\snext5 Index;}\ +}\ +{\\info{\\creatim\\yr2006\\mo1\\dy24\\hr17\\min48}{\\revtim\\yr1601\\mo1\\dy1\\hr0\\min0}{\\printim\\yr1601\\mo1\\dy1\\hr0\\min0}{\\comment StarWriter}{\\vern6800}}\\deftab709\ +{\\*\\pgdsctbl\ +{\\pgdsc0\\pgdscuse195\\pgwsxn12240\\pghsxn15840\\marglsxn1134\\margrsxn1134\\margtsxn1134\\margbsxn1134\\pgdscnxt0 Standard;}}\ +\\paperh15840\\paperw12240\\margl1134\\margr1134\\margt1134\\margb1134\\sectd\\sbknone\\pgwsxn12240\\pghsxn15840\\marglsxn1134\\margrsxn1134\\margtsxn1134\\margbsxn1134\\ftnbj\\ftnstart1\\ftnrstcont\\ftnnar\\aenddoc\\aftnrstcont\\aftnstart1\\aftnnrlc\ +\\pard\\plain \\ltrpar\\s1\\cf0{\\*\\hyphen2\\hyphlead2\\hyphtrail2\\hyphmax0}\\rtlch\\af3\\afs24\\lang255\\ltrch\\dbch\\af2\\langfe255\\hich\\f0\\fs24\\lang1033\\loch\\f0\\fs24\\lang1033{\\rtlch \\ltrch\\loch\\f0\\fs24\\lang1033\\i0\\b0{\\cf2 This}}{\\rtlch \\ltrch\\loch\\f0\\fs24\\lang1033\\i0\\b0 {\\cf3 is} {\\chcbpat4 a} {\\ul\\ulc0{\\b test}}{\\ul\\ulc0 {\\i RTF}}.}\ +\\par }"; + +typedef struct SCREENBITMAP_S { + HBITMAP hBitmap; + BYTE *data; +} SCREENBITMAP; + +static void empty_message_queue(void) +{ + MSG msg; + + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void pause(int secs) +{ + time_t end; + + end = time(NULL) + secs; + + /* The message pump uses PeekMessage() to empty the queue and then sleeps for + * 50ms before retrying the queue. */ + while (time(NULL) < end) { + empty_message_queue(); + Sleep(50); + } +} + +/* caller must call DeleteObject() on the returned SCREENBITMAP.hBitmap */ +static SCREENBITMAP get_hwnd_bitmap(HWND hWnd) +{ + BYTE *pBits; + BITMAPINFO *bmi; + HBITMAP hBitmap, hPrevBitmap; + HDC hDCWnd, hDCMem; + SCREENBITMAP sb; + + UpdateWindow(hWnd); + + hDCWnd = GetWindowDC(hWnd); + ok(hDCWnd != NULL, "error: %d\n", (int) GetLastError()); + + bmi = calloc(1, sizeof(BITMAPINFO)); + bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi->bmiHeader.biWidth = GetDeviceCaps(hDCWnd, HORZRES); + bmi->bmiHeader.biHeight = GetDeviceCaps(hDCWnd, VERTRES); + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biBitCount = 32; + bmi->bmiHeader.biCompression = BI_RGB; + + hDCMem = CreateCompatibleDC(hDCWnd); + ok(hDCMem != NULL, "error: %d\n", (int) GetLastError()); + + hBitmap = CreateDIBSection(hDCWnd, bmi, DIB_RGB_COLORS, (void **) &pBits, + NULL, 0); + ok(hBitmap != NULL && pBits != NULL, "error: %d\n", (int) GetLastError()); + + /* must restore hDCMem to hPrevBitmap */ + hPrevBitmap = SelectObject(hDCMem, hBitmap); + ok(hPrevBitmap != HGDI_ERROR && hPrevBitmap != NULL, "hBitmap=%d\n", + (int) hPrevBitmap); + + ok(BitBlt(hDCMem, 0, 0, bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight, + hDCWnd, 0, 0, SRCCOPY) != 0, "error: %d\n", (int) GetLastError()); + + hPrevBitmap = SelectObject(hDCMem, hPrevBitmap); + ok(hBitmap == hPrevBitmap, "Expected %d, got %d.\n", (int) hBitmap, + (int) hPrevBitmap); + + DeleteDC(hDCMem); + free(bmi); + ReleaseDC(hWnd, hDCWnd); + + sb.hBitmap = hBitmap; + sb.data = pBits; + return sb; +} + +/* returns non-zero if the rectangular area defined by (x, y, width, height) + * in the two screens differ */ +static int screens_differ(SCREENBITMAP one, SCREENBITMAP two, int x, int y, + int width, int height) { + DIBSECTION ds1, ds2; + int linesize1, linesize2; + int i, j; + + ok(GetObject(one.hBitmap, sizeof(DIBSECTION), &ds1) != 0, "error: %d\n", + (int) GetLastError()); + ok(GetObject(two.hBitmap, sizeof(DIBSECTION), &ds2) != 0, "error: %d\n", + (int) GetLastError()); + + /* require 32 bits per pixel */ + ok((ds1.dsBmih.biBitCount == 32 && ds2.dsBmih.biBitCount == 32), + "%d != %d != 32\n", ds1.dsBmih.biBitCount, ds2.dsBmih.biBitCount); + + linesize1 = ds1.dsBmih.biWidth * 4; + linesize2 = ds2.dsBmih.biWidth * 4; + + for (i = y; i < y + height; i++) { + BYTE *pb1 = one.data + x*4 + (ds1.dsBmih.biHeight - i - 1) * linesize1; + BYTE *pb2 = two.data + x*4 + (ds2.dsBmih.biHeight - i - 1) * linesize2; + for (j = 0; j < width; j++, pb1+=4, pb2+=4) { + if (pb1[2] != pb2[2] || pb1[1] != pb2[1] || pb1[0] != pb2[0]) + return 1; + } + } + + return 0; +} + +static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) { + HWND hwnd; + hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL + |WS_VISIBLE, 0, 0, 200, 50, parent, NULL, + hmoduleRichEdit, NULL); + ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError()); + return hwnd; +} + +static HWND new_richedit(HWND parent) { + return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent); +} + +static HWND new_static_wnd(HWND parent) { + return new_window("Static", 0, parent); +} + +static void key_press(WORD c) +{ + INPUT input; + + memset(&input, 0, sizeof(INPUT)); + input.type = INPUT_KEYBOARD; + input.ki.wVk = c; + + SendInput(1, &input, sizeof(INPUT)); + empty_message_queue(); +} + +static int xscreen = -1; +static int yscreen = -1; + +static long normalizeX(long x) +{ + if (xscreen == -1) + xscreen = GetSystemMetrics(SM_CXSCREEN); + + return (x * 65536) / (xscreen == 0 ? 1 : xscreen); +} + +static long normalizeY(long y) +{ + if (yscreen == -1) + yscreen = GetSystemMetrics(SM_CYSCREEN); + + return (y * 65536) / (yscreen == 0 ? 1 : yscreen); +} + +static long denormalizeX(long x) +{ + if (xscreen == -1) + xscreen = GetSystemMetrics(SM_CXSCREEN); + + return (x * xscreen) / 65536; +} + +static long denormalizeY(long y) +{ + if (yscreen == -1) + yscreen = GetSystemMetrics(SM_CYSCREEN); + + return (y * yscreen) / 65536; +} + +/* click at (x1, y1), drag to (x2, y2) and release */ +static void mouse_drag(int x1, int y1, int x2, int y2) +{ + +} + +static LRESULT CALLBACK proc_EM_AUTOURLDETECT(HWND hWnd, UINT Msg, WPARAM wParam, + LPARAM lParam) +{ + if ((Msg == WM_NOTIFY) && ((ENLINK *) lParam)->nmhdr.code == EN_LINK) + EN_LINK_rcvd = 1; + + return DefWindowProc(hWnd, Msg, wParam, lParam); +} + +static void check_EN_LINK_rcvd(HWND hwnd, int is_url) +{ + /* simulate mouse movement at (2,2) relative to control */ + SendMessage(hwnd, WM_MOUSEMOVE, 0 , 2 | 2 << 16); + + if (is_url) { /* control text is url; should get EN_LINK */ + todo_wine { + ok(0 != EN_LINK_rcvd, "en_link_rcvd=%d\n", EN_LINK_rcvd); + } + } + else { + ok(0 == EN_LINK_rcvd, "en_link_rcvd=%d\n", EN_LINK_rcvd); + } + + EN_LINK_rcvd = 0; +} + +static void test_EM_AUTOURLDETECT(void) +{ + struct urls_s { + char *text; + int is_url; + } urls[3] = { + {"winehq.org\n", 0}, + {"www.winehq.org\n", 1}, + {"http://www.winehq.org\n", 1} + }; + + int i, screen_diff; + SCREENBITMAP before, after; + HWND hwndRichEdit, parent; + LONG_PTR prevWndProc; + + parent = new_static_wnd(NULL); + hwndRichEdit = new_richedit(parent); + + SendMessage(hwndRichEdit, EM_SETEVENTMASK, 0, ENM_LINK); + /* overwrite the parent WndProc to get EN_LINK notifications */ + SetLastError(0); + prevWndProc = SetWindowLongPtr(parent, GWLP_WNDPROC, (LONG_PTR) proc_EM_AUTOURLDETECT); + ok(prevWndProc != 0 || (prevWndProc == 0 && GetLastError() == 0), "error: %d\n", (int) GetLastError()); + + /* for each url, check that the screen changes with/without ENM_LINK if + * expected and that the EN_LINK message is received if expected */ + for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) { + SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text); + check_EN_LINK_rcvd(hwndRichEdit, 0); + before = get_hwnd_bitmap(hwndRichEdit); + + SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text); + check_EN_LINK_rcvd(hwndRichEdit, urls[i].is_url); + after = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(before, after, 0, 0, 200, 50); + if (urls[i].is_url) { + todo_wine { + ok(screen_diff, "no change\n"); + } + } else { + ok(!screen_diff, "change\n"); + } + + DeleteObject(before.hBitmap); + DeleteObject(after.hBitmap); + } + + /* restore the WndProc */ + SetLastError(0); + prevWndProc = SetWindowLongPtr(parent, GWLP_WNDPROC, (LONG_PTR) prevWndProc); + ok(prevWndProc != 0 || (prevWndProc == 0 && GetLastError() == 0), "error: %d\n", (int) GetLastError()); + + DestroyWindow(hwndRichEdit); + DestroyWindow(parent); +} + +static void test_EM_EXGETSEL(void) +{ + int screen_diff; + char text[] = "test selection of text"; + SCREENBITMAP before, after; + CHARRANGE cr; + HWND hwndRichEdit = new_richedit(NULL); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + /* initially there should be no text selection */ + memset(&cr, 0, sizeof(CHARRANGE)); + SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM) &cr); + ok(cr.cpMin == cr.cpMax, "(%ld,%ld)\n", cr.cpMin, cr.cpMax); + + /* select characters 3 to 10 */ + cr.cpMin = 2; + cr.cpMax = 10; + before = get_hwnd_bitmap(hwndRichEdit); + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + after = get_hwnd_bitmap(hwndRichEdit); + screen_diff = screens_differ(before, after, 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "selection not updated to the screen\n"); + } + DeleteObject(before.hBitmap); + DeleteObject(after.hBitmap); + + /* verify selection change */ + memset(&cr, 0, sizeof(CHARRANGE)); + SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM) &cr); + ok(cr.cpMin == 2 && cr.cpMax == 10, "(%ld,%ld)\n", cr.cpMin, cr.cpMax); + + /* check that range (0, -1) means everything */ + cr.cpMin = 0; + cr.cpMax = -1; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + memset(&cr, 0, sizeof(CHARRANGE)); + SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM) &cr); + /* TODO: bug in Windows? WinXP reports (0,23) range, while Wine reports + * (0, 22) */ + todo_wine { + ok(cr.cpMin == 0 && cr.cpMax == sizeof(text), "(%ld,%ld)\n", cr.cpMin, + cr.cpMax); + } + + /* TODO: simulate drag-selection */ + /* TODO: color of text under the cursor is not inverted in Wine, but it is in + * Windows */ + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_EXLIMITTEXT(void) +{ + int i, screen_diff; + char *text; + int textlimit = 0; /* multiple of 100 */ + HWND hwndRichEdit = new_richedit(NULL); + SCREENBITMAP sb[4]; + + todo_wine { + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default */ + + textlimit = 40000; + SendMessage(hwndRichEdit, EM_LIMITTEXT, textlimit, 0); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(textlimit == i, "expected: %d, actual: %d\n", textlimit, i); + + textlimit = 80000; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + /* default for WParam = 0 */ + ok(65536 == i, "expected: %d, actual: %d\n", 65536, i); + + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + /* our textlimit */ + ok(textlimit == i, "expected: %d, actual: %d\n", textlimit, i); + } + + text = malloc(textlimit+1); + memset(text, 'W', textlimit); + text[textlimit] = 0; + /* maxed out text */ + SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM) text); + + sb[0] = get_hwnd_bitmap(hwndRichEdit); /* maxed text */ + + key_press(VK_BACK); + sb[1] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[0], sb[1], 0, 0, 200, 50); + ok(screen_diff, "change expected\n"); + + key_press('A'); + sb[2] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[1], sb[2], 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "change expected\n"); + } + + key_press('A'); /* full; should be no effect */ + sb[3] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[2], sb[3], 0, 0, 200, 50); + ok(!screen_diff, "no change expected\n"); + + for (i = 0; i < sizeof(sb)/sizeof(SCREENBITMAP); i++) + DeleteObject(sb[i].hBitmap); + + DestroyWindow(hwndRichEdit); +} + +static void check_EM_FINDTEXT(HWND hwnd, int start, int end, char needle[], + int flags, int expected_start) { + int findloc; + FINDTEXT ft; + memset(&ft, 0, sizeof(ft)); + ft.chrg.cpMin = start; + ft.chrg.cpMax = end; + ft.lpstrText = needle; + findloc = SendMessage(hwnd, EM_FINDTEXT, flags, (LPARAM) &ft); + ok(findloc == expected_start, + "finding %s in range (%d,%d), got start at %d\n", needle, start, end, + findloc); +} + +static void test_EM_FINDTEXT(void) +{ + CHARRANGE cr; + GETTEXTLENGTHEX gtl; + int size; + + HWND hwndRichEdit = new_richedit(NULL); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack); + SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(cr.cpMin == cr.cpMax, "(%ld,%ld)\n", cr.cpMin, cr.cpMax); + + gtl.flags = GTL_NUMCHARS; + gtl.codepage = CP_ACP; + size = SendMessage(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0); + ok(size == sizeof(haystack) - 1, "size=%d, sizeof haystack=%d\n", + size, sizeof(haystack)); /* sizeof counts '\0' */ + + check_EM_FINDTEXT(hwndRichEdit, 0, size, "Wine", FR_DOWN | FR_MATCHCASE, 9); + check_EM_FINDTEXT(hwndRichEdit, 10, size, "Wine", FR_DOWN | FR_MATCHCASE, 69); + check_EM_FINDTEXT(hwndRichEdit, 298, size, "Wine", FR_DOWN | FR_MATCHCASE, + -1); + check_EM_FINDTEXT(hwndRichEdit, 0, size, "wine", FR_DOWN | FR_MATCHCASE, -1); + todo_wine { + check_EM_FINDTEXT(hwndRichEdit, 0, size, "wine", FR_DOWN | FR_WHOLEWORD, + 9); + check_EM_FINDTEXT(hwndRichEdit, 0, size, "win", FR_DOWN | FR_WHOLEWORD, + -1); + } + + /* Check a corner case noted in bug 4144. The Wine implementation fails to + * find the second occurrence of 'blah' in the string 'blahblah' searching + * the range (4,8) */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "blahblah"); + check_EM_FINDTEXT(hwndRichEdit, 0, 8, "blah", FR_DOWN, 0); + todo_wine { + check_EM_FINDTEXT(hwndRichEdit, 4, 8, "blah", FR_DOWN, 4); + } + /* curiously, the following works */ + check_EM_FINDTEXT(hwndRichEdit, 4, 9, "blah", FR_DOWN, 4); + + DestroyWindow(hwndRichEdit); +} + +static void check_EM_FINDTEXTEX(HWND hwnd, int start, int end, char needle[], + int flags, int expected_start, + int expected_end) { + int findloc; + FINDTEXTEX ft; + memset(&ft, 0, sizeof(ft)); + ft.chrg.cpMin = start; + ft.chrg.cpMax = end; + ft.lpstrText = needle; + findloc = SendMessage(hwnd, EM_FINDTEXTEX, flags, (LPARAM) &ft); + ok(findloc == expected_start, + "finding %s in range (%d,%d), got start at %d\n", needle, start, end, + findloc); + if(findloc != -1) { + ok(ft.chrgText.cpMin == expected_start, + "finding %s in range (%d,%d), got start at %ld\n", needle, start, end, + ft.chrgText.cpMin); + ok(ft.chrgText.cpMax == expected_end, + "finding %s in range (%d,%d), got end at %ld\n", needle, start, end, + ft.chrgText.cpMax); + } +} + +static void test_EM_FINDTEXTEX(void) +{ + CHARRANGE cr; + GETTEXTLENGTHEX gtl; + int size; + + HWND hwndRichEdit = new_richedit(NULL); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack); + SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM) &cr); + ok(cr.cpMin == cr.cpMax, "(%ld,%ld)\n", cr.cpMin, cr.cpMax); + + gtl.flags = GTL_NUMCHARS; + gtl.codepage = CP_ACP; + size = SendMessage(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0); + ok(size == sizeof(haystack) - 1, "size=%d, sizeof haystack=%d\n", size, + sizeof(haystack)); /* sizeof counts '\0' */ + + check_EM_FINDTEXTEX(hwndRichEdit, 0, size, "Wine", FR_DOWN | FR_MATCHCASE, 9, + 13); + check_EM_FINDTEXTEX(hwndRichEdit, 10, size, "Wine", FR_DOWN | FR_MATCHCASE, + 69, 73); + check_EM_FINDTEXTEX(hwndRichEdit, 298, size, "Wine", FR_DOWN | FR_MATCHCASE, + -1, -1); + check_EM_FINDTEXTEX(hwndRichEdit, 0, size, "wine", FR_DOWN | FR_MATCHCASE, + -1, -1); + todo_wine { + check_EM_FINDTEXTEX(hwndRichEdit, 0, size, "wine", FR_DOWN | FR_WHOLEWORD, + 9, 13); + check_EM_FINDTEXTEX(hwndRichEdit, 0, size, "win", FR_DOWN | FR_WHOLEWORD, + -1, -1); + } + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_SCROLL(void) +{ + int i, screen_diff; + SCREENBITMAP sb[5]; + HWND hwndRichEdit = new_richedit(NULL); + + /* initially scroll is at the top */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne"); + + sb[0] = get_hwnd_bitmap(hwndRichEdit); + + SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); + sb[1] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[0], sb[1], 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "change expected\n"); + } + + SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); + sb[2] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[1], sb[2], 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "change expected\n"); + } + + SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); + sb[3] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[2], sb[3], 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "change expected\n"); + } + + SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); + sb[4] = get_hwnd_bitmap(hwndRichEdit); + + screen_diff = screens_differ(sb[3], sb[4], 0, 0, 200, 50); + todo_wine { + ok(screen_diff, "change expected\n"); + } + + for (i = 0; i < sizeof(sb)/sizeof(SCREENBITMAP); i++) + DeleteObject(sb[i].hBitmap); + + DestroyWindow(hwndRichEdit); +} + +static int get_scroll_pos_y(HWND hwnd) +{ + POINT p; + SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p); + return p.y; +} + +static void move_cursor(HWND hwnd, long charindex) +{ + CHARRANGE cr; + cr.cpMax = charindex; + cr.cpMin = charindex; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr); +} + +static void line_scroll(HWND hwnd, int amount) +{ + SendMessage(hwnd, EM_LINESCROLL, 0, amount); +} + +static void test_EM_SCROLLCARET(void) +{ + int prevY, curY; + HWND hwndRichEdit = new_richedit(NULL); + const char text[] = "aa\n" + "this is a long line of text that should be longer than one screen-width\n" + "cc\n" + "dd\n" + "ee\n" + "ff\n" + "gg\n" + "hh\n"; + + /* can't verify this */ + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + /* caret above visible window */ + line_scroll(hwndRichEdit, 3); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY != curY, "%d == %d\n", prevY, curY); + + /* caret below visible window */ + move_cursor(hwndRichEdit, sizeof(text) - 1); + line_scroll(hwndRichEdit, -3); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY != curY, "%d == %d\n", prevY, curY); + + /* caret in visible window */ + move_cursor(hwndRichEdit, sizeof(text) - 2); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY == curY, "%d != %d\n", prevY, curY); + + /* caret still in visible window */ + line_scroll(hwndRichEdit, -1); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY == curY, "%d != %d\n", prevY, curY); + + DestroyWindow(hwndRichEdit); +} + +enum {EM_STREAMIN_TEXT_TINY, EM_STREAMIN_TEXT_SHORT, EM_STREAMIN_TEXT_LONG, EM_STREAMIN_RTF_SHORT, EM_STREAMIN_MAX}; + +struct { + const char *start; + const char *end; +} static riched_contents[EM_STREAMIN_MAX] = { + {tinytext, tinytext + sizeof(tinytext)}, + {haystack, haystack + sizeof(haystack),}, + {NULL, NULL}, + {testrtf, testrtf + sizeof(testrtf)} +}; + +static void create_test_text(void) +{ + int i; + int sizelongtext = 32768; + char *start = malloc(sizelongtext); + riched_contents[EM_STREAMIN_TEXT_LONG].start = start; + riched_contents[EM_STREAMIN_TEXT_LONG].end = start + sizelongtext; + for (i = 0; i < sizelongtext; i++) + start[i] = 'a' + i % 26; + //memset(start, 'w', sizelongtext); +} + +static void delete_test_text(void) +{ + free((char *) riched_contents[EM_STREAMIN_TEXT_LONG].start); + riched_contents[EM_STREAMIN_TEXT_LONG].start = NULL; + riched_contents[EM_STREAMIN_TEXT_LONG].end = NULL; +} + +/* streams various amounts of text depending on dwCookie */ +static DWORD CALLBACK callback_EM_STREAMIN(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + static DWORD_PTR prevCookie = -1; + static const char *text; + static const char *start; + static const char *end; + + if (dwCookie != prevCookie) { + start = riched_contents[dwCookie].start; + end = riched_contents[dwCookie].end; + text = start; + prevCookie = dwCookie; + } + + if (text == end - 1) { /* reset and end */ + text = start; + *pcb = 0; + return 0; + } + + *pcb = min(end - text - 1, cb); + memcpy(pbBuff, text, *pcb); + text += *pcb; + return 0; +} + +static void do_EM_STREAMIN(HWND hWnd, WPARAM wParam, DWORD_PTR dwCookie) { + EDITSTREAM es; + es.dwCookie = dwCookie; + es.pfnCallback = (EDITSTREAMCALLBACK) callback_EM_STREAMIN; + SendMessage(hWnd, EM_STREAMIN, wParam, (LPARAM) &es); +} + +static void test_EM_STREAMIN(void) +{ + HWND hwndRichEdit; + + create_test_text(); + + /* replace whole contents */ + hwndRichEdit = new_richedit(NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "abcd"); + do_EM_STREAMIN(hwndRichEdit, SF_TEXT, EM_STREAMIN_TEXT_TINY); + DestroyWindow(hwndRichEdit); + + /* replace selection */ + + /* set short text */ + hwndRichEdit = new_richedit(NULL); + do_EM_STREAMIN(hwndRichEdit, SF_TEXT, EM_STREAMIN_TEXT_SHORT); + DestroyWindow(hwndRichEdit); + + /* set long text */ + hwndRichEdit = new_richedit(NULL); + do_EM_STREAMIN(hwndRichEdit, SF_TEXT, EM_STREAMIN_TEXT_LONG); + DestroyWindow(hwndRichEdit); + + /* set rtf text */ + hwndRichEdit = new_richedit(NULL); + do_EM_STREAMIN(hwndRichEdit, SF_RTF, EM_STREAMIN_RTF_SHORT); + + delete_test_text(); +} + +static DWORD CALLBACK callback_EM_STREAMOUT(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + static DWORD_PTR prevCookie = -1; + static const char *text; + static const char *start; + static const char *end; + + if (dwCookie != prevCookie) { + start = riched_contents[dwCookie].start; + end = riched_contents[dwCookie].end; + text = start; + prevCookie = dwCookie; + } + + if (text == end - 1) { /* reset and end */ + text = start; + *pcb = 0; + return 0; + } + + *pcb = min(end - text - 1, cb); + ok(0 == memcmp(pbBuff, text, *pcb), "error: [%s] [%s]\n", pbBuff, text); + text += *pcb; + return 0; +} + +static void do_EM_STREAMOUT(HWND hWnd, WPARAM wParam, DWORD_PTR dwCookie) { + EDITSTREAM es; + es.dwCookie = dwCookie; + es.pfnCallback = (EDITSTREAMCALLBACK) callback_EM_STREAMOUT; + SendMessage(hWnd, EM_STREAMOUT, wParam, (LPARAM) &es); +} + +static void test_EM_STREAMOUT(void) +{ + HWND hwndRichEdit; + + create_test_text(); + + /* add text */ + hwndRichEdit = new_richedit(NULL); + do_EM_STREAMIN(hwndRichEdit, SF_TEXT, EM_STREAMIN_TEXT_LONG); + do_EM_STREAMOUT(hwndRichEdit, SF_TEXT, EM_STREAMIN_TEXT_LONG); + //DestroyWindow(hwndRichEdit); + + delete_test_text(); +} + +START_TEST( editor ) +{ + /* Must explicitly LoadLibrary(). The test has no references to functions in + * RICHED20.DLL, so the linker doesn't actually link to it. */ + hmoduleRichEdit = LoadLibrary("RICHED20.DLL"); + ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError()); + + if(0) { + test_EM_AUTOURLDETECT(); + test_EM_EXGETSEL(); + test_EM_EXLIMITTEXT(); /* too slow */ + test_EM_FINDTEXT(); + test_EM_FINDTEXTEX(); + test_EM_SCROLL(); + } + test_EM_SCROLLCARET(); + if (0) { + test_EM_STREAMIN(); + test_EM_STREAMOUT(); + } + + /* Set the environment variable WINETEST_RICHED20 to keep windows + * responsive and open for 30 seconds. This is useful for debugging. */ + if (1 || getenv( "WINETEST_RICHED20" )) + pause(30); + + ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError()); +} \ No newline at end of file