diff --git a/dlls/comctl32/tab.c b/dlls/comctl32/tab.c index 0ecbb637d72..fe82d59e90f 100644 --- a/dlls/comctl32/tab.c +++ b/dlls/comctl32/tab.c @@ -83,8 +83,10 @@ typedef struct BYTE extra[1]; /* Space for caller supplied info, variable size */ } TAB_ITEM; -/* The size of a tab item depends on how much extra data is requested */ -#define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo])) +/* The size of a tab item depends on how much extra data is requested. + TCM_INSERTITEM always stores at least LPARAM sized data. */ +#define EXTRA_ITEM_SIZE(infoPtr) (max((infoPtr)->cbInfo, sizeof(LPARAM))) +#define TAB_ITEM_SIZE(infoPtr) FIELD_OFFSET(TAB_ITEM, extra[EXTRA_ITEM_SIZE(infoPtr)]) typedef struct { @@ -1728,7 +1730,7 @@ TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect /* * if owner draw, tell the owner to draw */ - if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd)) + if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && IsWindow(infoPtr->hwndNotify)) { DRAWITEMSTRUCT dis; UINT id; @@ -1741,14 +1743,9 @@ TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect drawRect->left += 1; } - /* - * get the control id - */ id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID ); - /* - * put together the DRAWITEMSTRUCT - */ + /* fill DRAWITEMSTRUCT */ dis.CtlType = ODT_TAB; dis.CtlID = id; dis.itemID = iItem; @@ -1761,11 +1758,18 @@ TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect dis.hwndItem = infoPtr->hwnd; dis.hDC = hdc; CopyRect(&dis.rcItem,drawRect); - dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra; - /* - * send the draw message - */ + /* when extra data fits ULONG_PTR, store it directly */ + if (infoPtr->cbInfo > sizeof(LPARAM)) + dis.itemData = (ULONG_PTR) TAB_GetItem(infoPtr, iItem)->extra; + else + { + /* this could be considered broken on 64 bit, but that's how it works - + only first 4 bytes are copied */ + memcpy(&dis.itemData, (ULONG_PTR*)TAB_GetItem(infoPtr, iItem)->extra, 4); + } + + /* draw notification */ SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, id, (LPARAM)&dis ); } else @@ -2686,10 +2690,10 @@ TAB_InsertItemT (TAB_INFO *infoPtr, INT iItem, const TCITEMW *pti, BOOL bUnicode item->iImage = -1; if (pti->mask & TCIF_PARAM) - memcpy(item->extra, &pti->lParam, infoPtr->cbInfo); + memcpy(item->extra, &pti->lParam, EXTRA_ITEM_SIZE(infoPtr)); else - memset(item->extra, 0, infoPtr->cbInfo); - + memset(item->extra, 0, EXTRA_ITEM_SIZE(infoPtr)); + TAB_SetItemBounds(infoPtr); if (infoPtr->uNumItem > 1) TAB_InvalidateTabArea(infoPtr); diff --git a/dlls/comctl32/tests/tab.c b/dlls/comctl32/tests/tab.c index d74155d461c..03c43908bce 100644 --- a/dlls/comctl32/tests/tab.c +++ b/dlls/comctl32/tests/tab.c @@ -61,7 +61,8 @@ "%s: Expected [%d,%d] got [%d,%d]\n", msg, (int)width, (int)height,\ rTab.right - rTab.left, rTab.bottom - rTab.top); -static HFONT hFont = 0; +static HFONT hFont; +static DRAWITEMSTRUCT g_drawitem; static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; @@ -337,6 +338,10 @@ static LRESULT WINAPI parentWindowProcess(HWND hwnd, UINT message, WPARAM wParam add_message(sequences, PARENT_SEQ_INDEX, &msg); } + /* dump sent structure data */ + if (message == WM_DRAWITEM) + g_drawitem = *(DRAWITEMSTRUCT*)lParam; + defwndproc_counter++; ret = DefWindowProcA(hwnd, message, wParam, lParam); defwndproc_counter--; @@ -790,11 +795,43 @@ static void test_unicodeformat(HWND parent_wnd, INT nTabs) static void test_getset_item(HWND parent_wnd, INT nTabs) { - TCITEM tcItem; - DWORD ret; char szText[32] = "New Label"; + TCITEM tcItem; + LPARAM lparam; + DWORD ret; HWND hTab; + hTab = CreateWindowA( + WC_TABCONTROLA, + "TestTab", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | TCS_FIXEDWIDTH | TCS_OWNERDRAWFIXED, + 10, 10, 300, 100, + parent_wnd, NULL, NULL, 0); + + ok(GetParent(hTab) == NULL, "got %p, expected null parent\n", GetParent(hTab)); + + ret = SendMessageA(hTab, TCM_SETITEMEXTRA, sizeof(LPARAM)-1, 0); + ok(ret == TRUE, "got %d\n", ret); + + /* set some item data */ + tcItem.lParam = ~0; + tcItem.mask = TCIF_PARAM; + + ret = SendMessageA(hTab, TCM_INSERTITEMA, 0, (LPARAM)&tcItem); + ok(ret == 0, "got %d\n", ret); + + /* all sizeof(LPARAM) returned anyway when using sizeof(LPARAM)-1 size */ + memset(&lparam, 0xaa, sizeof(lparam)); + tcItem.lParam = lparam; + tcItem.mask = TCIF_PARAM; + ret = SendMessage(hTab, TCM_GETITEM, 0, (LPARAM)&tcItem); + expect(TRUE, ret); + /* everything higher specified size is preserved */ + memset(&lparam, 0xff, sizeof(lparam)-1); + ok(tcItem.lParam == lparam, "Expected 0x%lx, got 0x%lx\n", lparam, tcItem.lParam); + + DestroyWindow(hTab); + hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs); ok(hTab != NULL, "Failed to create tab control\n"); @@ -1233,6 +1270,101 @@ static void test_TCM_SETITEMEXTRA(HWND parent_wnd) DestroyWindow(hTab); } +static void test_TCS_OWNERDRAWFIXED(HWND parent_wnd) +{ + LPARAM lparam, lparam2; + TCITEMA item; + HWND hTab; + BOOL ret; + + hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH|TCS_OWNERDRAWFIXED, TCIF_TEXT|TCIF_IMAGE, 4); + ok(hTab != NULL, "Failed to create tab control\n"); + + ok(GetParent(hTab) == NULL, "got %p, expected null parent\n", GetParent(hTab)); + + /* set some item data */ + memset(&lparam, 0xde, sizeof(LPARAM)); + + item.mask = TCIF_PARAM; + item.lParam = lparam; + ret = SendMessageA(hTab, TCM_SETITEMA, 0, (LPARAM)&item); + ok(ret == TRUE, "got %d\n", ret); + + memset(&g_drawitem, 0, sizeof(g_drawitem)); + + ShowWindow(hTab, SW_SHOW); + RedrawWindow(hTab, NULL, 0, RDW_UPDATENOW); + + lparam = 0; + memset(&lparam, 0xde, 4); + ok(g_drawitem.itemData == lparam, "got %lx, expected %lx\n", g_drawitem.itemData, lparam); + + DestroyWindow(hTab); + + /* now with custom extra data length */ + hTab = CreateWindowA( + WC_TABCONTROLA, + "TestTab", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | TCS_FIXEDWIDTH | TCS_OWNERDRAWFIXED, + 10, 10, 300, 100, + parent_wnd, NULL, NULL, 0); + + ok(GetParent(hTab) == NULL, "got %p, expected null parent\n", GetParent(hTab)); + + ret = SendMessageA(hTab, TCM_SETITEMEXTRA, sizeof(LPARAM)+1, 0); + ok(ret == TRUE, "got %d\n", ret); + + /* set some item data */ + memset(&lparam, 0xde, sizeof(LPARAM)); + item.mask = TCIF_PARAM; + item.lParam = lparam; + + ret = SendMessageA(hTab, TCM_INSERTITEMA, 0, (LPARAM)&item); + ok(ret == 0, "got %d\n", ret); + + memset(&g_drawitem, 0, sizeof(g_drawitem)); + + ShowWindow(hTab, SW_SHOW); + RedrawWindow(hTab, NULL, 0, RDW_UPDATENOW); + + ok(*(ULONG_PTR*)g_drawitem.itemData == lparam, "got %lx, expected %lx\n", g_drawitem.itemData, lparam); + + DestroyWindow(hTab); + + /* same thing, but size smaller than default */ + hTab = CreateWindowA( + WC_TABCONTROLA, + "TestTab", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | TCS_FIXEDWIDTH | TCS_OWNERDRAWFIXED, + 10, 10, 300, 100, + parent_wnd, NULL, NULL, 0); + + ok(GetParent(hTab) == NULL, "got %p, expected null parent\n", GetParent(hTab)); + + ret = SendMessageA(hTab, TCM_SETITEMEXTRA, sizeof(LPARAM)-1, 0); + ok(ret == TRUE, "got %d\n", ret); + + memset(&lparam, 0xde, sizeof(lparam)); + item.mask = TCIF_PARAM; + item.lParam = lparam; + + ret = SendMessageA(hTab, TCM_INSERTITEMA, 0, (LPARAM)&item); + ok(ret == 0, "got %d\n", ret); + + memset(&g_drawitem, 0, sizeof(g_drawitem)); + + ShowWindow(hTab, SW_SHOW); + RedrawWindow(hTab, NULL, 0, RDW_UPDATENOW); + + lparam = 0; + memset(&lparam, 0xde, 4); + memset(&lparam2, 0xde, sizeof(LPARAM)-1); + ok(g_drawitem.itemData == lparam || broken(g_drawitem.itemData == lparam2) /* win98 */, + "got 0x%lx, expected 0x%lx\n", g_drawitem.itemData, lparam); + + DestroyWindow(hTab); +} + START_TEST(tab) { HWND parent_wnd; @@ -1278,6 +1410,7 @@ START_TEST(tab) test_delete_selection(parent_wnd); test_removeimage(parent_wnd); test_TCM_SETITEMEXTRA(parent_wnd); + test_TCS_OWNERDRAWFIXED(parent_wnd); DestroyWindow(parent_wnd); }