Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/comctl32/pager.c | 72 +++++++++++++++++++ dlls/comctl32/tests/pager.c | 137 ++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+)
diff --git a/dlls/comctl32/pager.c b/dlls/comctl32/pager.c index 0367108db2..9ab492e333 100644 --- a/dlls/comctl32/pager.c +++ b/dlls/comctl32/pager.c @@ -84,6 +84,8 @@ typedef struct INT TLbtnState; /* state of top or left btn */ INT BRbtnState; /* state of bottom or right btn */ INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */ + WCHAR *pwszBuffer;/* text buffer for converted notifications */ + INT nBufferSize;/* size of the above buffer */ } PAGER_INFO;
#define TIMERID1 1 @@ -605,6 +607,7 @@ static LRESULT PAGER_Destroy (PAGER_INFO *infoPtr) { SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); + heap_free (infoPtr->pwszBuffer); heap_free (infoPtr); return 0; } @@ -1038,10 +1041,25 @@ static UINT PAGER_GetAnsiNtfCode(UINT code) /* Toolbar */ case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA; case TBN_GETINFOTIPW: return TBN_GETINFOTIPA; + /* Tooltip */ + case TTN_GETDISPINFOW: return TTN_GETDISPINFOA; } return code; }
+static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size) +{ + if (!infoPtr->pwszBuffer) + infoPtr->pwszBuffer = heap_alloc(size); + else if (infoPtr->nBufferSize < size) + infoPtr->pwszBuffer = heap_realloc(infoPtr->pwszBuffer, size); + + if (!infoPtr->pwszBuffer) return FALSE; + if (infoPtr->nBufferSize < size) infoPtr->nBufferSize = size; + + return TRUE; +} + static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, WCHAR **text, INT *textMax, ConversionFlag flags) { WCHAR emptyW[] = {0}; @@ -1093,6 +1111,8 @@ done:
static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr) { + LRESULT ret; + if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
switch (hdr->code) @@ -1109,6 +1129,58 @@ static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr) NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr; return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtbgit->pszText, &nmtbgit->cchTextMax, FROM_ANSI); } + /* Tooltip */ + case TTN_GETDISPINFOW: + { + NMTTDISPINFOW *nmttdiW = (NMTTDISPINFOW *)hdr; + NMTTDISPINFOA nmttdiA = {0}; + INT size; + + nmttdiA.hdr.code = PAGER_GetAnsiNtfCode(nmttdiW->hdr.code); + nmttdiA.hdr.hwndFrom = nmttdiW->hdr.hwndFrom; + nmttdiA.hdr.idFrom = nmttdiW->hdr.idFrom; + nmttdiA.hinst = nmttdiW->hinst; + nmttdiA.uFlags = nmttdiW->uFlags; + nmttdiA.lParam = nmttdiW->lParam; + nmttdiA.lpszText = nmttdiA.szText; + WideCharToMultiByte(CP_ACP, 0, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText), nmttdiA.szText, + ARRAY_SIZE(nmttdiA.szText), NULL, FALSE); + + ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmttdiA); + + nmttdiW->hinst = nmttdiA.hinst; + nmttdiW->uFlags = nmttdiA.uFlags; + nmttdiW->lParam = nmttdiA.lParam; + + MultiByteToWideChar(CP_ACP, 0, nmttdiA.szText, ARRAY_SIZE(nmttdiA.szText), nmttdiW->szText, + ARRAY_SIZE(nmttdiW->szText)); + if (!nmttdiA.lpszText) + nmttdiW->lpszText = nmttdiW->szText; + else if (!IS_INTRESOURCE(nmttdiA.lpszText)) + { + size = MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, 0, 0); + if (size > ARRAY_SIZE(nmttdiW->szText)) + { + if (!PAGER_AdjustBuffer(infoPtr, size * sizeof(WCHAR))) return ret; + MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, infoPtr->pwszBuffer, size); + nmttdiW->lpszText = infoPtr->pwszBuffer; + /* Override content in szText */ + memcpy(nmttdiW->szText, nmttdiW->lpszText, min(sizeof(nmttdiW->szText), size * sizeof(WCHAR))); + } + else + { + MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText)); + nmttdiW->lpszText = nmttdiW->szText; + } + } + else + { + nmttdiW->szText[0] = 0; + nmttdiW->lpszText = (WCHAR *)nmttdiA.lpszText; + } + + return ret; + } } /* Other notifications or notifications above that don't have text, thus no need to convert */ return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr); diff --git a/dlls/comctl32/tests/pager.c b/dlls/comctl32/tests/pager.c index 46f2eb0acb..bf79f34c5b 100644 --- a/dlls/comctl32/tests/pager.c +++ b/dlls/comctl32/tests/pager.c @@ -630,6 +630,53 @@ static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, L wm_notify_text_handler(&nmtbgit->pszText, &nmtbgit->cchTextMax); break; } + /* Tooltip */ + case TTN_GETDISPINFOA: + { + NMTTDISPINFOA *nmttdi = (NMTTDISPINFOA *)hdr; + switch (notify_test_data.sub_test_id) + { + case 0: + break; + case 1: + memcpy(nmttdi->szText, test_a, sizeof(test_a)); + break; + case 2: + memcpy(nmttdi->szText, test_a, sizeof(test_a)); + nmttdi->lpszText = test_a; + break; + case 3: + memcpy(nmttdi->szText, test_a, sizeof(test_a)); + nmttdi->lpszText = (CHAR *)1; + nmttdi->hinst = GetModuleHandleA(0); + break; + case 4: + memcpy(nmttdi->szText, test_a, sizeof(test_a)); + nmttdi->lpszText = test_a; + nmttdi->hinst = GetModuleHandleA(0); + break; + case 5: + nmttdi->lpszText = test_a; + break; + case 6: + memcpy(nmttdi->szText, test_a, 2); + nmttdi->szText[2] = 0; + nmttdi->lpszText = test_a; + break; + case 7: + expect_eq_text_a(nmttdi->szText, test_a); + expect_eq_pointer(nmttdi->lpszText, nmttdi->szText); + break; + case 8: + expect_text_empty(nmttdi->szText); + expect_eq_pointer(nmttdi->lpszText, nmttdi->szText); + break; + case 9: + nmttdi->lpszText = large_a; + break; + } + break; + } default: ok(0, "Unexpected message 0x%08x\n", hdr->code); } @@ -828,6 +875,95 @@ static void test_wm_notify_toolbar(HWND pager) TBN_GETINFOTIPA, FROM_ANSI); }
+static void test_wm_notify_tooltip(HWND pager) +{ + NMTTDISPINFOW nmttdi; + WCHAR *buffer_w; + INT size; + + /* if lpszText is not set by parent, will be set to the address of szText, even if szText is empty */ + notify_test_data.sub_test_id = 0; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_text_empty(nmttdi.szText); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Parent write szText. If lpszText is not set by parent, will be set to the address of szText */ + notify_test_data.sub_test_id = 1; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Parent write szText. Parent replace nmttdi.lpszText with pointer */ + notify_test_data.sub_test_id = 2; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Parent write szText. Parent set int resource. szText end up empty */ + notify_test_data.sub_test_id = 3; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_text_empty(nmttdi.szText); + ok(IS_INTRESOURCE(nmttdi.lpszText), "Expect int resource\n"); + ok(nmttdi.hinst != NULL, "Expect instance gets set\n"); + + /* Parent set szText, lpszText and instance */ + notify_test_data.sub_test_id = 4; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + ok(nmttdi.hinst != NULL, "Expect instance gets set\n"); + + /* Empty szText will be filled with lpszText if lpszText is set */ + notify_test_data.sub_test_id = 5; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Not empty szText will be overwritten with lpszText if lpszText is set after conversion */ + notify_test_data.sub_test_id = 6; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Set szText before send_notify. szText got converted for parent */ + notify_test_data.sub_test_id = 7; + memset(&nmttdi, 0, sizeof(nmttdi)); + memcpy(nmttdi.szText, test_w, sizeof(test_w)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_eq_text_w(nmttdi.szText, test_w); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Set lpszText before send_notify. lpszText doesn't get converted for parent */ + notify_test_data.sub_test_id = 8; + memset(&nmttdi, 0, sizeof(nmttdi)); + nmttdi.lpszText = test_w; + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + expect_text_empty(nmttdi.szText); + expect_eq_pointer(nmttdi.lpszText, nmttdi.szText); + + /* Parent set lpszText to a string larger than the size of szText array */ + notify_test_data.sub_test_id = 9; + memset(&nmttdi, 0, sizeof(nmttdi)); + send_notify(TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE, TRUE); + size = MultiByteToWideChar(CP_ACP, 0, large_a, -1, 0, 0); + buffer_w = heap_alloc(size * sizeof(WCHAR)); + if (!buffer_w) return; + MultiByteToWideChar(CP_ACP, 0, large_a, -1, buffer_w, size); + expect_eq_text_w(nmttdi.lpszText, buffer_w); + buffer_w[ARRAY_SIZE(nmttdi.szText)] = 0; + expect_eq_text_w(nmttdi.szText, buffer_w); + /* nmttdi.szText is not null terminated */ + expect_eq_int(lstrlenW(nmttdi.szText), ARRAY_SIZE(nmttdi.szText)); + heap_free(buffer_w); +} + static void test_wm_notify(void) { static const CHAR *class = "Pager notify class"; @@ -844,6 +980,7 @@ static void test_wm_notify(void) SendMessageW(pager, PGM_SETCHILD, 0, (LPARAM)child1_wnd);
test_wm_notify_toolbar(pager); + test_wm_notify_tooltip(pager);
DestroyWindow(parent); UnregisterClassA(class, GetModuleHandleA(NULL));