/* Unit test suite for tab control.
 *
 * Copyright 2003 Vitaliy Margolen
 * Copyright 2007 Hagop Hagopian
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>

#include "wine/test.h"
#include "msg.h"

#define DEFAULT_MIN_TAB_WIDTH 54
#define TAB_PADDING_X 6
#define EXTRA_ICON_PADDING 3
#define MAX_TABLEN 32

#define NUM_MSG_SEQUENCES  2
#define PARENT_SEQ_INDEX   0
#define TAB_SEQ_INDEX      1

#define expect(expected, got) ok ( expected == got, "Expected %d, got %d\n", expected, got)
#define expect_str(expected, got)\
 ok ( strcmp(expected, got) == 0, "Expected '%s', got '%s'\n", expected, got)

#define TabWidthPadded(padd_x, num) (DEFAULT_MIN_TAB_WIDTH - (TAB_PADDING_X - (padd_x)) * num)

static HIMAGELIST (WINAPI *pImageList_Create)(INT,INT,UINT,INT,INT);
static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST);
static INT (WINAPI *pImageList_GetImageCount)(HIMAGELIST);
static INT (WINAPI *pImageList_ReplaceIcon)(HIMAGELIST,INT,HICON);

static void CheckSize(HWND hwnd, INT width, INT height, const char *msg, int line)
{
    RECT r;

    SendMessageA(hwnd, TCM_GETITEMRECT, 0, (LPARAM)&r);
    if (width >= 0 && height < 0)
        ok_(__FILE__,line) (width == r.right - r.left, "%s: Expected width [%d] got [%d]\n",
            msg, width, r.right - r.left);
    else if (height >= 0 && width < 0)
        ok_(__FILE__,line) (height == r.bottom - r.top,  "%s: Expected height [%d] got [%d]\n",
            msg, height, r.bottom - r.top);
    else
        ok_(__FILE__,line) ((width  == r.right  - r.left) && (height == r.bottom - r.top ),
	    "%s: Expected [%d,%d] got [%d,%d]\n", msg, width, height,
            r.right - r.left, r.bottom - r.top);
}

#define CHECKSIZE(hwnd,width,height,msg) CheckSize(hwnd,width,height,msg,__LINE__)

static void TabCheckSetSize(HWND hwnd, INT set_width, INT set_height, INT exp_width,
    INT exp_height, const char *msg, int line)
{
    SendMessageA(hwnd, TCM_SETITEMSIZE, 0,
            MAKELPARAM((set_width >= 0) ? set_width : 0, (set_height >= 0) ? set_height : 0));
    if (winetest_interactive) RedrawWindow (hwnd, NULL, 0, RDW_UPDATENOW);
    CheckSize(hwnd, exp_width, exp_height, msg, line);
}

#define TABCHECKSETSIZE(hwnd,set_width,set_height,exp_width,exp_height,msg) \
    TabCheckSetSize(hwnd,set_width,set_height,exp_width,exp_height,msg,__LINE__)

static HFONT hFont;
static DRAWITEMSTRUCT g_drawitem;
static HWND parent_wnd;
static LRESULT tcn_selchanging_result;

static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];

static const struct message empty_sequence[] = {
    { 0 }
};

static const struct message get_row_count_seq[] = {
    { TCM_GETROWCOUNT, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message getset_cur_focus_seq[] = {
    { TCM_SETCURFOCUS, sent|lparam, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURFOCUS, sent|lparam, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURSEL, sent|lparam, 0 },
    { TCM_SETCURFOCUS, sent|lparam, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message getset_cur_sel_seq[] = {
    { TCM_SETCURSEL, sent|lparam, 0 },
    { TCM_GETCURSEL, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURSEL, sent|lparam, 0 },
    { TCM_GETCURSEL, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURSEL, sent|lparam, 0 },
    { TCM_SETCURSEL, sent|lparam, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message getset_extended_style_seq[] = {
    { TCM_GETEXTENDEDSTYLE, sent|wparam|lparam, 0, 0 },
    { TCM_SETEXTENDEDSTYLE, sent },
    { TCM_GETEXTENDEDSTYLE, sent|wparam|lparam, 0, 0 },
    { TCM_SETEXTENDEDSTYLE, sent },
    { TCM_GETEXTENDEDSTYLE, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message getset_unicode_format_seq[] = {
    { CCM_SETUNICODEFORMAT, sent|lparam, 0 },
    { CCM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
    { CCM_SETUNICODEFORMAT, sent|lparam, 0 },
    { CCM_GETUNICODEFORMAT, sent|wparam|lparam, 0, 0 },
    { CCM_SETUNICODEFORMAT, sent|lparam, 0 },
    { 0 }
};

static const struct message getset_item_seq[] = {
    { TCM_SETITEMA, sent },
    { TCM_GETITEMA, sent },
    { TCM_GETITEMA, sent },
    { 0 }
};

static const struct message getset_tooltip_seq[] = {
    { WM_NOTIFYFORMAT, sent|optional },
    { WM_QUERYUISTATE, sent|wparam|lparam|optional, 0, 0 },
    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
    { WM_NOTIFYFORMAT, sent|optional },
    { TCM_SETTOOLTIPS, sent|lparam, 0 },
    { TCM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
    { TCM_SETTOOLTIPS, sent|lparam, 0 },
    { TCM_GETTOOLTIPS, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message getset_tooltip_parent_seq[] = {
    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
    { 0 }
};

static const struct message insert_focus_seq[] = {
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_INSERTITEMA, sent|wparam, 1 },
    { WM_NOTIFYFORMAT, sent|defwinproc|optional },
    { WM_QUERYUISTATE, sent|defwinproc|optional },
    { WM_PARENTNOTIFY, sent|defwinproc|optional },
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_INSERTITEMA, sent|wparam, 2 },
    { WM_NOTIFYFORMAT, sent|defwinproc|optional },
    { WM_QUERYUISTATE, sent|defwinproc|optional, },
    { WM_PARENTNOTIFY, sent|defwinproc|optional },
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURFOCUS, sent|wparam|lparam, -1, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_INSERTITEMA, sent|wparam, 3 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message delete_focus_seq[] = {
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_DELETEITEM, sent|wparam|lparam, 1, 0 },
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_SETCURFOCUS, sent|wparam|lparam, -1, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { TCM_DELETEITEM, sent|wparam|lparam, 0, 0 },
    { TCM_GETITEMCOUNT, sent|wparam|lparam, 0, 0 },
    { TCM_GETCURFOCUS, sent|wparam|lparam, 0, 0 },
    { 0 }
};

static const struct message rbuttonup_seq[] = {
    { WM_RBUTTONUP, sent|wparam|lparam, 0, 0 },
    { WM_CONTEXTMENU, sent|defwinproc },
    { 0 }
};

static const struct message full_selchange_parent_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TCN_SELCHANGING },
    { WM_NOTIFY, sent|id, 0, 0, TCN_SELCHANGE },
    { 0 }
};

static const struct message selchanging_parent_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TCN_SELCHANGING },
    { 0 }
};

static const struct message selchange_parent_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TCN_SELCHANGE },
    { 0 }
};

static const struct message setfocus_parent_seq[] = {
    { WM_NOTIFY, sent|id, 0, 0, TCN_FOCUSCHANGE },
    { 0 }
};

static HWND
create_tabcontrol (DWORD style, DWORD mask)
{
    HWND handle;
    TCITEMA tcNewTab;
    static char text1[] = "Tab 1",
    text2[] = "Wide Tab 2",
    text3[] = "T 3";

    handle = CreateWindowA(WC_TABCONTROLA, "TestTab",
            WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | style, 10, 10, 300, 100, NULL,
            NULL, NULL, 0);
    ok(handle != NULL, "failed to create tab wnd\n");

    SetWindowLongA(handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | style);
    SendMessageA(handle, WM_SETFONT, 0, (LPARAM)hFont);

    tcNewTab.mask = mask;
    tcNewTab.pszText = text1;
    tcNewTab.iImage = 0;
    SendMessageA(handle, TCM_INSERTITEMA, 0, (LPARAM)&tcNewTab);
    tcNewTab.pszText = text2;
    tcNewTab.iImage = 1;
    SendMessageA(handle, TCM_INSERTITEMA, 1, (LPARAM)&tcNewTab);
    tcNewTab.pszText = text3;
    tcNewTab.iImage = 2;
    SendMessageA(handle, TCM_INSERTITEMA, 2, (LPARAM)&tcNewTab);

    if (winetest_interactive)
    {
        ShowWindow (handle, SW_SHOW);
        RedrawWindow (handle, NULL, 0, RDW_UPDATENOW);
        Sleep (1000);
    }

    return handle;
}

static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static LONG defwndproc_counter = 0;
    struct message msg = { 0 };
    LRESULT ret;

    /* do not log painting messages */
    if (message != WM_PAINT &&
        message != WM_ERASEBKGND &&
        message != WM_NCPAINT &&
        message != WM_NCHITTEST &&
        message != WM_GETTEXT &&
        message != WM_GETICON &&
        message != WM_DEVICECHANGE)
    {
        msg.message = message;
        msg.flags = sent|wparam|lparam;
        if (defwndproc_counter) msg.flags |= defwinproc;
        msg.wParam = wParam;
        msg.lParam = lParam;
        if (message == WM_NOTIFY && lParam)
            msg.id = ((NMHDR*)lParam)->code;
        add_message(sequences, PARENT_SEQ_INDEX, &msg);
    }

    /* dump sent structure data */
    if (message == WM_DRAWITEM)
        g_drawitem = *(DRAWITEMSTRUCT*)lParam;

    if (message == WM_NOTIFY)
    {
        NMHDR *nmhdr = (NMHDR *)lParam;
        if (nmhdr && nmhdr->code == TCN_SELCHANGING)
            return tcn_selchanging_result;
    }

    defwndproc_counter++;
    ret = DefWindowProcA(hwnd, message, wParam, lParam);
    defwndproc_counter--;

    return ret;
}

static BOOL registerParentWindowClass(void)
{
    WNDCLASSA cls;

    cls.style = 0;
    cls.lpfnWndProc = parent_wnd_proc;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = GetModuleHandleA(NULL);
    cls.hIcon = 0;
    cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
    cls.lpszMenuName = NULL;
    cls.lpszClassName = "Tab test parent class";
    return RegisterClassA(&cls);
}

static HWND createParentWindow(void)
{
    if (!registerParentWindowClass())
        return NULL;

    return CreateWindowExA(0, "Tab test parent class", "Tab test parent window",
            WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE, 0, 0, 100, 100,
            GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
}

static LRESULT WINAPI tab_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
    static LONG defwndproc_counter = 0;
    struct message msg = { 0 };
    LRESULT ret;

    /* do not log painting messages */
    if (message != WM_PAINT &&
        message != WM_ERASEBKGND &&
        message != WM_NCPAINT &&
        message != WM_NCHITTEST &&
        message != WM_GETTEXT &&
        message != WM_GETICON &&
        message != WM_DEVICECHANGE)
    {
        msg.message = message;
        msg.flags = sent|wparam|lparam;
        if (defwndproc_counter) msg.flags |= defwinproc;
        msg.wParam = wParam;
        msg.lParam = lParam;
        add_message(sequences, TAB_SEQ_INDEX, &msg);
    }

    defwndproc_counter++;
    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
    defwndproc_counter--;

    return ret;
}

static HWND createFilledTabControl(HWND parent_wnd, DWORD style, DWORD mask, INT nTabs)
{
    HWND tabHandle;
    TCITEMA tcNewTab;
    WNDPROC oldproc;
    RECT rect;
    INT i;

    GetClientRect(parent_wnd, &rect);

    tabHandle = CreateWindowA(WC_TABCONTROLA, "TestTab",
            WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | style, 0, 0, rect.right,
            rect.bottom, parent_wnd, NULL, NULL, 0);
    ok(tabHandle != NULL, "failed to create tab wnd\n");

    oldproc = (WNDPROC)SetWindowLongPtrA(tabHandle, GWLP_WNDPROC, (LONG_PTR)tab_subclass_proc);
    SetWindowLongPtrA(tabHandle, GWLP_USERDATA, (LONG_PTR)oldproc);

    tcNewTab.mask = mask;

    for (i = 0; i < nTabs; i++)
    {
        char tabName[MAX_TABLEN];

        sprintf(tabName, "Tab %d", i+1);
        tcNewTab.pszText = tabName;
        tcNewTab.iImage = i;
        SendMessageA(tabHandle, TCM_INSERTITEMA, i, (LPARAM)&tcNewTab);
    }

    if (winetest_interactive)
    {
        ShowWindow (tabHandle, SW_SHOW);
        RedrawWindow (tabHandle, NULL, 0, RDW_UPDATENOW);
        Sleep (1000);
    }

    return tabHandle;
}

static HWND create_tooltip (HWND hTab, char toolTipText[])
{
    HWND hwndTT;

    TTTOOLINFOA ti;
    LPSTR lptstr = toolTipText;
    RECT rect;

    /* Creating a tooltip window*/
    hwndTT = CreateWindowExA(WS_EX_TOPMOST, TOOLTIPS_CLASSA, NULL,
            WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            CW_USEDEFAULT, hTab, NULL, 0, NULL);

    SetWindowPos(
        hwndTT,
        HWND_TOPMOST,
        0, 0, 0, 0,
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

    GetClientRect (hTab, &rect);

    /* Initialize members of toolinfo*/
    ti.cbSize = sizeof(TTTOOLINFOA);
    ti.uFlags = TTF_SUBCLASS;
    ti.hwnd = hTab;
    ti.hinst = 0;
    ti.uId = 0;
    ti.lpszText = lptstr;

    ti.rect = rect;

    /* Add toolinfo structure to the tooltip control */
    SendMessageA(hwndTT, TTM_ADDTOOLA, 0, (LPARAM)&ti);

    return hwndTT;
}

static void test_tab(INT nMinTabWidth)
{
    HWND hwTab;
    RECT rTab;
    HIMAGELIST himl = pImageList_Create(21, 21, ILC_COLOR, 3, 4);
    SIZE size;
    HDC hdc;
    HFONT hOldFont;
    INT i, dpi, exp;

    hwTab = create_tabcontrol(TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
    /* Get System default MinTabWidth */
    if (nMinTabWidth < 0)
        nMinTabWidth = SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);

    hdc = GetDC(hwTab);
    dpi = GetDeviceCaps(hdc, LOGPIXELSX);
    hOldFont = SelectObject(hdc, (HFONT)SendMessageA(hwTab, WM_GETFONT, 0, 0));
    GetTextExtentPoint32A(hdc, "Tab 1", strlen("Tab 1"), &size);
    trace("Tab1 text size: size.cx=%d size.cy=%d\n", size.cx, size.cy);
    SelectObject(hdc, hOldFont);
    ReleaseDC(hwTab, hdc);

    trace ("  TCS_FIXEDWIDTH tabs no icon...\n");
    CHECKSIZE(hwTab, dpi, -1, "default width");
    TABCHECKSETSIZE(hwTab, 50, 20, 50, 20, "set size");
    TABCHECKSETSIZE(hwTab, 0, 1, 0, 1, "min size");

    SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);

    trace ("  TCS_FIXEDWIDTH tabs with icon...\n");
    TABCHECKSETSIZE(hwTab, 50, 30, 50, 30, "set size > icon");
    TABCHECKSETSIZE(hwTab, 20, 20, 25, 20, "set size < icon");
    TABCHECKSETSIZE(hwTab, 0, 1, 25, 1, "min size");

    DestroyWindow (hwTab);

    hwTab = create_tabcontrol(TCS_FIXEDWIDTH | TCS_BUTTONS, TCIF_TEXT|TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);

    hdc = GetDC(hwTab);
    dpi = GetDeviceCaps(hdc, LOGPIXELSX);
    ReleaseDC(hwTab, hdc);
    trace ("  TCS_FIXEDWIDTH buttons no icon...\n");
    CHECKSIZE(hwTab, dpi, -1, "default width");
    TABCHECKSETSIZE(hwTab, 20, 20, 20, 20, "set size 1");
    TABCHECKSETSIZE(hwTab, 10, 50, 10, 50, "set size 2");
    TABCHECKSETSIZE(hwTab, 0, 1, 0, 1, "min size");

    SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);

    trace ("  TCS_FIXEDWIDTH buttons with icon...\n");
    TABCHECKSETSIZE(hwTab, 50, 30, 50, 30, "set size > icon");
    TABCHECKSETSIZE(hwTab, 20, 20, 25, 20, "set size < icon");
    TABCHECKSETSIZE(hwTab, 0, 1, 25, 1, "min size");
    SendMessageA(hwTab, TCM_SETPADDING, 0, MAKELPARAM(4, 4));
    TABCHECKSETSIZE(hwTab, 0, 1, 25, 1, "set padding, min size");

    DestroyWindow (hwTab);

    hwTab = create_tabcontrol(TCS_FIXEDWIDTH | TCS_BOTTOM, TCIF_TEXT|TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);

    hdc = GetDC(hwTab);
    dpi = GetDeviceCaps(hdc, LOGPIXELSX);
    ReleaseDC(hwTab, hdc);
    trace ("  TCS_FIXEDWIDTH | TCS_BOTTOM tabs...\n");
    CHECKSIZE(hwTab, dpi, -1, "no icon, default width");

    TABCHECKSETSIZE(hwTab, 20, 20, 20, 20, "no icon, set size 1");
    TABCHECKSETSIZE(hwTab, 10, 50, 10, 50, "no icon, set size 2");
    TABCHECKSETSIZE(hwTab, 0, 1, 0, 1, "no icon, min size");

    SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);

    TABCHECKSETSIZE(hwTab, 50, 30, 50, 30, "with icon, set size > icon");
    TABCHECKSETSIZE(hwTab, 20, 20, 25, 20, "with icon, set size < icon");
    TABCHECKSETSIZE(hwTab, 0, 1, 25, 1, "with icon, min size");
    SendMessageA(hwTab, TCM_SETPADDING, 0, MAKELPARAM(4, 4));
    TABCHECKSETSIZE(hwTab, 0, 1, 25, 1, "set padding, min size");

    DestroyWindow (hwTab);

    hwTab = create_tabcontrol(0, TCIF_TEXT|TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);

    trace ("  non fixed width, with text...\n");
    exp = max(size.cx +TAB_PADDING_X*2, (nMinTabWidth < 0) ? DEFAULT_MIN_TAB_WIDTH : nMinTabWidth);
    SendMessageA( hwTab, TCM_GETITEMRECT, 0, (LPARAM)&rTab );
    ok( rTab.right  - rTab.left == exp || broken(rTab.right  - rTab.left == DEFAULT_MIN_TAB_WIDTH),
        "no icon, default width: Expected width [%d] got [%d]\n", exp, rTab.right - rTab.left );

    for (i=0; i<8; i++)
    {
        INT nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 2) : nMinTabWidth;

        SendMessageA(hwTab, TCM_SETIMAGELIST, 0, 0);
        SendMessageA(hwTab, TCM_SETPADDING, 0, MAKELPARAM(i, i));

        TABCHECKSETSIZE(hwTab, 50, 20, max(size.cx + i*2, nTabWidth), 20, "no icon, set size");
        TABCHECKSETSIZE(hwTab, 0, 1, max(size.cx + i*2, nTabWidth), 1, "no icon, min size");

        SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
        nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 3) : nMinTabWidth;

        TABCHECKSETSIZE(hwTab, 50, 30, max(size.cx + 21 + i*3, nTabWidth), 30, "with icon, set size > icon");
        TABCHECKSETSIZE(hwTab, 20, 20, max(size.cx + 21 + i*3, nTabWidth), 20, "with icon, set size < icon");
        TABCHECKSETSIZE(hwTab, 0, 1, max(size.cx + 21 + i*3, nTabWidth), 1, "with icon, min size");
    }
    DestroyWindow (hwTab);

    hwTab = create_tabcontrol(0, TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);

    trace ("  non fixed width, no text...\n");
    exp = (nMinTabWidth < 0) ? DEFAULT_MIN_TAB_WIDTH : nMinTabWidth;
    SendMessageA( hwTab, TCM_GETITEMRECT, 0, (LPARAM)&rTab );
    ok( rTab.right  - rTab.left == exp || broken(rTab.right  - rTab.left == DEFAULT_MIN_TAB_WIDTH),
        "no icon, default width: Expected width [%d] got [%d]\n", exp, rTab.right - rTab.left );

    for (i=0; i<8; i++)
    {
        INT nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 2) : nMinTabWidth;

        SendMessageA(hwTab, TCM_SETIMAGELIST, 0, 0);
        SendMessageA(hwTab, TCM_SETPADDING, 0, MAKELPARAM(i, i));

        TABCHECKSETSIZE(hwTab, 50, 20, nTabWidth, 20, "no icon, set size");
        TABCHECKSETSIZE(hwTab, 0, 1, nTabWidth, 1, "no icon, min size");

        SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
        if (i > 1 && nMinTabWidth > 0 && nMinTabWidth < DEFAULT_MIN_TAB_WIDTH)
            nTabWidth += EXTRA_ICON_PADDING *(i-1);

        TABCHECKSETSIZE(hwTab, 50, 30, nTabWidth, 30, "with icon, set size > icon");
        TABCHECKSETSIZE(hwTab, 20, 20, nTabWidth, 20, "with icon, set size < icon");
        TABCHECKSETSIZE(hwTab, 0, 1, nTabWidth, 1, "with icon, min size");
    }

    DestroyWindow (hwTab);

    pImageList_Destroy(himl);
}

static void test_width(void)
{
    trace ("Testing with default MinWidth\n");
    test_tab(-1);
    trace ("Testing with MinWidth set to -3\n");
    test_tab(-3);
    trace ("Testing with MinWidth set to 24\n");
    test_tab(24);
    trace ("Testing with MinWidth set to 54\n");
    test_tab(54);
    trace ("Testing with MinWidth set to 94\n");
    test_tab(94);
}

static void test_curfocus(void)
{
    const INT nTabs = 5;
    INT ret;
    HWND hTab;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    /* Testing CurFocus with largest appropriate value */
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, nTabs - 1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == nTabs - 1, "Unexpected focus index %d.\n", ret);

    /* Testing CurFocus with negative value */
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, -10, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == -1, "Unexpected focus index %d.\n", ret);

    /* Testing CurFocus with value larger than number of tabs */
    ret = SendMessageA(hTab, TCM_SETCURSEL, 1, 0);
    ok(ret == -1, "Unexpected focus index %d.\n", ret);

    ret = SendMessageA(hTab, TCM_SETCURFOCUS, nTabs + 1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == 1, "Unexpected focus index %d.\n", ret);

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_cur_focus_seq, "Set focused tab sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Set focused tab parent sequence", TRUE);

    DestroyWindow(hTab);

    /* TCS_BUTTONS */
    hTab = createFilledTabControl(parent_wnd, TCS_BUTTONS, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    /* Testing CurFocus with largest appropriate value */
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, nTabs - 1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == nTabs - 1, "Unexpected focus index %d.\n", ret);

    /* Testing CurFocus with negative value */
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, -10, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
todo_wine
    ok(ret == nTabs - 1, "Unexpected focus index %d.\n", ret);

    /* Testing CurFocus with value larger than number of tabs */
    ret = SendMessageA(hTab, TCM_SETCURSEL, 1, 0);
todo_wine
    ok(ret == 0, "Unexpected focus index %d.\n", ret);

    ret = SendMessageA(hTab, TCM_SETCURFOCUS, nTabs + 1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
todo_wine
    ok(ret == nTabs - 1, "Unexpected focus index %d.\n", ret);

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_cur_focus_seq, "TCS_BUTTONS: set focused tab sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, setfocus_parent_seq, "TCS_BUTTONS: set focused tab parent sequence", TRUE);

    DestroyWindow(hTab);
}

static void test_cursel(void)
{
    const INT nTabs = 5;
    INT selectionIndex;
    INT focusIndex;
    TCITEMA tcItem;
    HWND hTab;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    /* Testing CurSel with largest appropriate value */
    selectionIndex = SendMessageA(hTab, TCM_SETCURSEL, nTabs - 1, 0);
    expect(0, selectionIndex);
    selectionIndex = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    expect(nTabs-1, selectionIndex);

    /* Focus should switch with selection */
    focusIndex = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(nTabs-1, focusIndex);

    /* Testing CurSel with negative value */
    SendMessageA(hTab, TCM_SETCURSEL, -10, 0);
    selectionIndex = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    expect(-1, selectionIndex);

    /* Testing CurSel with value larger than number of tabs */
    selectionIndex = SendMessageA(hTab, TCM_SETCURSEL, 1, 0);
    expect(-1, selectionIndex);

    selectionIndex = SendMessageA(hTab, TCM_SETCURSEL, nTabs + 1, 0);
    expect(-1, selectionIndex);
    selectionIndex = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(1, selectionIndex);

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_cur_sel_seq, "Getset curSel test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Getset curSel test parent sequence", FALSE);

    /* selected item should have TCIS_BUTTONPRESSED state
       It doesn't depend on button state */
    memset(&tcItem, 0, sizeof(TCITEMA));
    tcItem.mask = TCIF_STATE;
    tcItem.dwStateMask = TCIS_BUTTONPRESSED;
    selectionIndex = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    SendMessageA(hTab, TCM_GETITEMA, selectionIndex, (LPARAM)&tcItem);
    ok (tcItem.dwState & TCIS_BUTTONPRESSED || broken(tcItem.dwState == 0), /* older comctl32 */
        "Selected item should have TCIS_BUTTONPRESSED\n");

    /* now deselect all and check previously selected item state */
    focusIndex = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(focusIndex == 1, "got %d\n", focusIndex);

    selectionIndex = SendMessageA(hTab, TCM_SETCURSEL, -1, 0);
    ok(selectionIndex == 1, "got %d\n", selectionIndex);

    memset(&tcItem, 0, sizeof(TCITEMA));

    /* focus is reset too */
    focusIndex = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(focusIndex == -1, "got %d\n", focusIndex);

    tcItem.mask = TCIF_STATE;
    tcItem.dwStateMask = TCIS_BUTTONPRESSED;
    SendMessageA(hTab, TCM_GETITEMA, selectionIndex, (LPARAM)&tcItem);
    ok(tcItem.dwState == 0, "got state %d\n", tcItem.dwState);

    DestroyWindow(hTab);
}

static void test_extendedstyle(void)
{
    const INT nTabs = 5;
    DWORD prevExtendedStyle;
    DWORD extendedStyle;
    HWND hTab;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    /* Testing Flat Separators */
    extendedStyle = SendMessageA(hTab, TCM_GETEXTENDEDSTYLE, 0, 0);
    prevExtendedStyle = SendMessageA(hTab, TCM_SETEXTENDEDSTYLE, 0, TCS_EX_FLATSEPARATORS);
    expect(extendedStyle, prevExtendedStyle);

    extendedStyle = SendMessageA(hTab, TCM_GETEXTENDEDSTYLE, 0, 0);
    expect(TCS_EX_FLATSEPARATORS, extendedStyle);

    /* Testing Register Drop */
    prevExtendedStyle = SendMessageA(hTab, TCM_SETEXTENDEDSTYLE, 0, TCS_EX_REGISTERDROP);
    expect(extendedStyle, prevExtendedStyle);

    extendedStyle = SendMessageA(hTab, TCM_GETEXTENDEDSTYLE, 0, 0);
    todo_wine{
        expect(TCS_EX_REGISTERDROP, extendedStyle);
    }

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_extended_style_seq, "Getset extendedStyle test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Getset extendedStyle test parent sequence", FALSE);

    DestroyWindow(hTab);
}

static void test_unicodeformat(void)
{
    const INT nTabs = 5;
    INT unicodeFormat;
    HWND hTab;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    unicodeFormat = SendMessageA(hTab, TCM_SETUNICODEFORMAT, TRUE, 0);
    todo_wine{
        expect(0, unicodeFormat);
    }
    unicodeFormat = SendMessageA(hTab, TCM_GETUNICODEFORMAT, 0, 0);
    expect(1, unicodeFormat);

    unicodeFormat = SendMessageA(hTab, TCM_SETUNICODEFORMAT, FALSE, 0);
    expect(1, unicodeFormat);
    unicodeFormat = SendMessageA(hTab, TCM_GETUNICODEFORMAT, 0, 0);
    expect(0, unicodeFormat);

    unicodeFormat = SendMessageA(hTab, TCM_SETUNICODEFORMAT, TRUE, 0);
    expect(0, unicodeFormat);

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_unicode_format_seq, "Getset unicodeFormat test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Getset unicodeFormat test parent sequence", FALSE);

    DestroyWindow(hTab);
}

static void test_getset_item(void)
{
    char szText[32] = "New Label";
    const INT nTabs = 5;
    TCITEMA 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 = SendMessageA(hTab, TCM_GETITEMA, 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");

    /* passing invalid index should result in initialization to zero
       for members mentioned in mask requested */

    /* valid range here is [0,4] */
    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_PARAM;
    ret = SendMessageA(hTab, TCM_GETITEMA, 5, (LPARAM)&tcItem);
    expect(FALSE, ret);
    ok(tcItem.lParam == 0, "Expected zero lParam, got %lu\n", tcItem.lParam);

    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_IMAGE;
    ret = SendMessageA(hTab, TCM_GETITEMA, 5, (LPARAM)&tcItem);
    expect(FALSE, ret);
    expect(0, tcItem.iImage);

    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_TEXT;
    tcItem.pszText = szText;
    szText[0] = 'a';
    ret = SendMessageA(hTab, TCM_GETITEMA, 5, (LPARAM)&tcItem);
    expect(FALSE, ret);
    expect('a', szText[0]);

    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_STATE;
    tcItem.dwStateMask = 0;
    tcItem.dwState = TCIS_BUTTONPRESSED;
    ret = SendMessageA(hTab, TCM_GETITEMA, 5, (LPARAM)&tcItem);
    expect(FALSE, ret);
    ok(tcItem.dwState == 0, "Expected zero dwState, got %u\n", tcItem.dwState);

    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_STATE;
    tcItem.dwStateMask = TCIS_BUTTONPRESSED;
    tcItem.dwState = TCIS_BUTTONPRESSED;
    ret = SendMessageA(hTab, TCM_GETITEMA, 5, (LPARAM)&tcItem);
    expect(FALSE, ret);
    ok(tcItem.dwState == 0, "Expected zero dwState\n");

    /* check with negative index to be sure */
    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_PARAM;
    ret = SendMessageA(hTab, TCM_GETITEMA, -1, (LPARAM)&tcItem);
    expect(FALSE, ret);
    ok(tcItem.lParam == 0, "Expected zero lParam, got %lu\n", tcItem.lParam);

    memset(&tcItem, 0xcc, sizeof(tcItem));
    tcItem.mask = TCIF_PARAM;
    ret = SendMessageA(hTab, TCM_GETITEMA, -2, (LPARAM)&tcItem);
    expect(FALSE, ret);
    ok(tcItem.lParam == 0, "Expected zero lParam, got %lu\n", tcItem.lParam);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    tcItem.mask = TCIF_TEXT;
    tcItem.pszText = &szText[0];
    tcItem.cchTextMax = sizeof(szText);

    strcpy(szText, "New Label");
    ok(SendMessageA(hTab, TCM_SETITEMA, 0, (LPARAM)&tcItem), "Setting new item failed.\n");
    ok(SendMessageA(hTab, TCM_GETITEMA, 0, (LPARAM)&tcItem), "Getting item failed.\n");
    expect_str("New Label", tcItem.pszText);

    ok(SendMessageA(hTab, TCM_GETITEMA, 1, (LPARAM)&tcItem), "Getting item failed.\n");
    expect_str("Tab 2", tcItem.pszText);

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_item_seq, "Getset item test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Getset item test parent sequence", FALSE);

    /* TCIS_BUTTONPRESSED doesn't depend on tab style */
    memset(&tcItem, 0, sizeof(tcItem));
    tcItem.mask = TCIF_STATE;
    tcItem.dwStateMask = TCIS_BUTTONPRESSED;
    tcItem.dwState = TCIS_BUTTONPRESSED;
    ok(SendMessageA(hTab, TCM_SETITEMA, 0, (LPARAM)&tcItem), "Setting new item failed.\n");
    tcItem.dwState = 0;
    ok(SendMessageA(hTab, TCM_GETITEMA, 0, (LPARAM)&tcItem), "Getting item failed.\n");
    if (tcItem.dwState)
    {
        ok (tcItem.dwState == TCIS_BUTTONPRESSED, "TCIS_BUTTONPRESSED should be set.\n");
        /* next highlight item, test that dwStateMask actually masks */
        tcItem.mask = TCIF_STATE;
        tcItem.dwStateMask = TCIS_HIGHLIGHTED;
        tcItem.dwState = TCIS_HIGHLIGHTED;
        ok(SendMessageA(hTab, TCM_SETITEMA, 0, (LPARAM)&tcItem), "Setting new item failed.\n");
        tcItem.dwState = 0;
        ok(SendMessageA(hTab, TCM_GETITEMA, 0, (LPARAM)&tcItem), "Getting item failed.\n");
        ok (tcItem.dwState == TCIS_HIGHLIGHTED, "TCIS_HIGHLIGHTED should be set.\n");
        tcItem.mask = TCIF_STATE;
        tcItem.dwStateMask = TCIS_BUTTONPRESSED;
        tcItem.dwState = 0;
        ok(SendMessageA(hTab, TCM_GETITEMA, 0, (LPARAM)&tcItem), "Getting item failed.\n");
        ok (tcItem.dwState == TCIS_BUTTONPRESSED, "TCIS_BUTTONPRESSED should be set.\n");
    }
    else win_skip( "Item state mask not supported\n" );

    DestroyWindow(hTab);
}

static void test_getset_tooltips(void)
{
    char toolTipText[32] = "ToolTip Text Test";
    HWND hTab, toolTip, hwnd;
    const INT nTabs = 5;
    int ret;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    toolTip = create_tooltip(hTab, toolTipText);
    ret = SendMessageA(hTab, TCM_SETTOOLTIPS, (WPARAM)toolTip, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    hwnd = (HWND)SendMessageA(hTab, TCM_GETTOOLTIPS, 0, 0);
    ok(toolTip == hwnd, "Unexpected tooltip window.\n");

    ret = SendMessageA(hTab, TCM_SETTOOLTIPS, 0, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);
    hwnd = (HWND)SendMessageA(hTab, TCM_GETTOOLTIPS, 0, 0);
    ok(hwnd == NULL, "Unexpected tooltip window.\n");
    ok(IsWindow(toolTip), "Expected tooltip window to be alive.\n");

    ok_sequence(sequences, TAB_SEQ_INDEX, getset_tooltip_seq, "Getset tooltip test sequence", TRUE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, getset_tooltip_parent_seq, "Getset tooltip test parent sequence", TRUE);

    DestroyWindow(hTab);
    DestroyWindow(toolTip);
}

static void test_adjustrect(void)
{
    HWND hTab;
    INT r;

    ok(parent_wnd != NULL, "no parent window!\n");

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, 0, 0);
    ok(hTab != NULL, "Failed to create tab control\n");

    r = SendMessageA(hTab, TCM_ADJUSTRECT, FALSE, 0);
    expect(-1, r);

    r = SendMessageA(hTab, TCM_ADJUSTRECT, TRUE, 0);
    expect(-1, r);
}

static void test_insert_focus(void)
{
    HWND hTab;
    INT nTabsRetrieved;
    INT r;
    TCITEMA tcNewTab;
    DWORD mask = TCIF_TEXT|TCIF_IMAGE;
    static char tabName[] = "TAB";
    tcNewTab.mask = mask;
    tcNewTab.pszText = tabName;

    ok(parent_wnd != NULL, "no parent window!\n");

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, mask, 0);
    ok(hTab != NULL, "Failed to create tab control\n");

    r = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    expect(-1, r);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(0, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(-1, r);

    tcNewTab.iImage = 1;
    r = SendMessageA(hTab, TCM_INSERTITEMA, 1, (LPARAM)&tcNewTab);
    expect(0, r);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(1, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(0, r);

    tcNewTab.iImage = 2;
    r = SendMessageA(hTab, TCM_INSERTITEMA, 2, (LPARAM)&tcNewTab);
    expect(1, r);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(2, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(0, r);

    r = SendMessageA(hTab, TCM_SETCURFOCUS, -1, 0);
    expect(0, r);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(-1, r);

    tcNewTab.iImage = 3;
    r = SendMessageA(hTab, TCM_INSERTITEMA, 3, (LPARAM)&tcNewTab);
    expect(2, r);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(2, r);

    ok_sequence(sequences, TAB_SEQ_INDEX, insert_focus_seq, "insert_focus test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "insert_focus parent test sequence", TRUE);

    DestroyWindow(hTab);
}

static void test_delete_focus(void)
{
    HWND hTab;
    INT nTabsRetrieved;
    INT r;

    ok(parent_wnd != NULL, "no parent window!\n");

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, 2);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(2, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(0, r);

    r = SendMessageA(hTab, TCM_DELETEITEM, 1, 0);
    expect(1, r);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(1, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(0, r);

    r = SendMessageA(hTab, TCM_SETCURFOCUS, -1, 0);
    expect(0, r);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(-1, r);

    r = SendMessageA(hTab, TCM_DELETEITEM, 0, 0);
    expect(1, r);

    nTabsRetrieved = SendMessageA(hTab, TCM_GETITEMCOUNT, 0, 0);
    expect(0, nTabsRetrieved);

    r = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    expect(-1, r);

    ok_sequence(sequences, TAB_SEQ_INDEX, delete_focus_seq, "delete_focus test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "delete_focus parent test sequence", TRUE);

    DestroyWindow(hTab);
}

static void test_removeimage(void)
{
    static const BYTE bits[32];
    HWND hwTab;
    INT i;
    TCITEMA item;
    HICON hicon;
    HIMAGELIST himl = pImageList_Create(16, 16, ILC_COLOR, 3, 4);

    hicon = CreateIcon(NULL, 16, 16, 1, 1, bits, bits);
    pImageList_ReplaceIcon(himl, -1, hicon);
    pImageList_ReplaceIcon(himl, -1, hicon);
    pImageList_ReplaceIcon(himl, -1, hicon);

    hwTab = create_tabcontrol(TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE);
    SendMessageA(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);

    memset(&item, 0, sizeof(TCITEMA));
    item.mask = TCIF_IMAGE;

    for(i = 0; i < 3; i++) {
        SendMessageA(hwTab, TCM_GETITEMA, i, (LPARAM)&item);
        expect(i, item.iImage);
    }

    /* remove image middle image */
    SendMessageA(hwTab, TCM_REMOVEIMAGE, 1, 0);
    i = pImageList_GetImageCount(himl);
    ok(i == 2, "Unexpected image count %d.\n", i);
    item.iImage = -1;
    SendMessageA(hwTab, TCM_GETITEMA, 0, (LPARAM)&item);
    expect(0, item.iImage);
    item.iImage = 0;
    SendMessageA(hwTab, TCM_GETITEMA, 1, (LPARAM)&item);
    expect(-1, item.iImage);
    item.iImage = 0;
    SendMessageA(hwTab, TCM_GETITEMA, 2, (LPARAM)&item);
    expect(1, item.iImage);
    /* remove first image */
    SendMessageA(hwTab, TCM_REMOVEIMAGE, 0, 0);
    i = pImageList_GetImageCount(himl);
    ok(i == 1, "Unexpected image count %d.\n", i);
    item.iImage = 0;
    SendMessageA(hwTab, TCM_GETITEMA, 0, (LPARAM)&item);
    expect(-1, item.iImage);
    item.iImage = 0;
    SendMessageA(hwTab, TCM_GETITEMA, 1, (LPARAM)&item);
    expect(-1, item.iImage);
    item.iImage = -1;
    SendMessageA(hwTab, TCM_GETITEMA, 2, (LPARAM)&item);
    expect(0, item.iImage);
    /* remove the last one */
    SendMessageA(hwTab, TCM_REMOVEIMAGE, 0, 0);
    i = pImageList_GetImageCount(himl);
    ok(i == 0, "Unexpected image count %d.\n", i);
    for(i = 0; i < 3; i++) {
        item.iImage = 0;
        SendMessageA(hwTab, TCM_GETITEMA, i, (LPARAM)&item);
        expect(-1, item.iImage);
    }

    DestroyWindow(hwTab);
    pImageList_Destroy(himl);
    DestroyIcon(hicon);
}

static void test_delete_selection(void)
{
    HWND hTab;
    INT ret;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, 4);
    ok(hTab != NULL, "Failed to create tab control\n");

    ret = SendMessageA(hTab, TCM_SETCURSEL, 3, 0);
    expect(0, ret);
    ret = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    expect(3, ret);
    /* delete selected item - selection goes to -1 */
    ret = SendMessageA(hTab, TCM_DELETEITEM, 3, 0);
    expect(TRUE, ret);

    ret = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    expect(-1, ret);

    DestroyWindow(hTab);
}

static void test_TCM_SETITEMEXTRA(void)
{
    HWND hTab;
    DWORD ret;

    hTab = CreateWindowA(
	WC_TABCONTROLA,
	"TestTab",
	WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | TCS_FIXEDWIDTH,
        10, 10, 300, 100,
        parent_wnd, NULL, NULL, 0);

    /* zero is valid size too */
    ret = SendMessageA(hTab, TCM_SETITEMEXTRA, 0, 0);
    if (ret == FALSE)
    {
        win_skip("TCM_SETITEMEXTRA not supported\n");
        DestroyWindow(hTab);
        return;
    }

    ret = SendMessageA(hTab, TCM_SETITEMEXTRA, -1, 0);
    ok(ret == FALSE, "got %d\n", ret);

    ret = SendMessageA(hTab, TCM_SETITEMEXTRA, 2, 0);
    ok(ret == TRUE, "got %d\n", ret);
    DestroyWindow(hTab);

    /* it's not possible to change extra data size for control with tabs */
    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, 4);
    ok(hTab != NULL, "Failed to create tab control\n");

    ret = SendMessageA(hTab, TCM_SETITEMEXTRA, 2, 0);
    ok(ret == FALSE, "got %d\n", ret);
    DestroyWindow(hTab);
}

static void test_TCS_OWNERDRAWFIXED(void)
{
    LPARAM lparam;
    ULONG_PTR itemdata, itemdata2;
    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);

    itemdata = 0;
    memset(&itemdata, 0xde, 4);
    ok(g_drawitem.itemData == itemdata, "got 0x%lx, expected 0x%lx\n", g_drawitem.itemData, itemdata);

    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);

    memset(&itemdata, 0xde, sizeof(ULONG_PTR));
    ok(*(ULONG_PTR*)g_drawitem.itemData == itemdata, "got 0x%lx, expected 0x%lx\n", g_drawitem.itemData, itemdata);

    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);

    itemdata = itemdata2 = 0;
    memset(&itemdata, 0xde, 4);
    memset(&itemdata2, 0xde, sizeof(LPARAM)-1);
    ok(g_drawitem.itemData == itemdata || broken(g_drawitem.itemData == itemdata2) /* win98 */,
        "got 0x%lx, expected 0x%lx\n", g_drawitem.itemData, itemdata);

    DestroyWindow(hTab);
}

static void test_WM_CONTEXTMENU(void)
{
    HWND hTab;

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, 4);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    SendMessageA(hTab, WM_RBUTTONUP, 0, 0);

    ok_sequence(sequences, TAB_SEQ_INDEX, rbuttonup_seq, "WM_RBUTTONUP response sequence", FALSE);

    DestroyWindow(hTab);
}

struct tabcreate_style {
    DWORD style;
    DWORD act_style;
};

static const struct tabcreate_style create_styles[] =
{
    { WS_CHILD|TCS_BOTTOM|TCS_VERTICAL, WS_CHILD|WS_CLIPSIBLINGS|TCS_BOTTOM|TCS_VERTICAL|TCS_MULTILINE },
    { WS_CHILD|TCS_VERTICAL,            WS_CHILD|WS_CLIPSIBLINGS|TCS_VERTICAL|TCS_MULTILINE },
    { 0 }
};

static void test_create(void)
{
    const struct tabcreate_style *ptr = create_styles;
    DWORD style;
    HWND hTab;

    while (ptr->style)
    {
        hTab = CreateWindowA(WC_TABCONTROLA, "TestTab", ptr->style,
            10, 10, 300, 100, parent_wnd, NULL, NULL, 0);
        style = GetWindowLongA(hTab, GWL_STYLE);
        ok(style == ptr->act_style, "expected style 0x%08x, got style 0x%08x\n", ptr->act_style, style);

        DestroyWindow(hTab);
        ptr++;
    }
}

static void init_functions(void)
{
    HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");

#define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
    X(ImageList_Create);
    X(ImageList_Destroy);
    X(ImageList_GetImageCount);
    X(ImageList_ReplaceIcon);
#undef X
}

static void test_TCN_SELCHANGING(void)
{
    const INT nTabs = 5;
    HWND hTab;
    INT ret;

    hTab = createFilledTabControl(parent_wnd, WS_CHILD|TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    /* Initially first tab is focused. */
    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == 0, "Unexpected tab focus %d.\n", ret);

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    /* Setting focus to currently focused item should do nothing. */
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, 0, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);

    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Set focus to focused tab sequence", FALSE);

    /* Allow selection change. */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    tcn_selchanging_result = 0;
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, nTabs - 1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);

    ok_sequence(sequences, PARENT_SEQ_INDEX, full_selchange_parent_seq, "Focus change allowed sequence", FALSE);

    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == nTabs - 1, "Unexpected focused tab %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
    ok(ret == nTabs - 1, "Unexpected selected tab %d.\n", ret);

    /* Forbid selection change. */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    tcn_selchanging_result = 1;
    ret = SendMessageA(hTab, TCM_SETCURFOCUS, 0, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);

    ok_sequence(sequences, PARENT_SEQ_INDEX, selchanging_parent_seq, "Focus change disallowed sequence", FALSE);

    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
todo_wine
    ok(ret == nTabs - 1, "Unexpected focused tab %d.\n", ret);
    ret = SendMessageA(hTab, TCM_GETCURSEL, 0, 0);
todo_wine
    ok(ret == nTabs - 1, "Unexpected selected tab %d.\n", ret);

    /* Removing focus sends only TCN_SELCHANGE */
    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    ret = SendMessageA(hTab, TCM_SETCURFOCUS, -1, 0);
    ok(ret == 0, "Unexpected ret value %d.\n", ret);

    ok_sequence(sequences, PARENT_SEQ_INDEX, selchange_parent_seq, "Remove focus sequence", FALSE);

    ret = SendMessageA(hTab, TCM_GETCURFOCUS, 0, 0);
    ok(ret == -1, "Unexpected focused tab %d.\n", ret);

    tcn_selchanging_result = 0;

    DestroyWindow(hTab);
}

static void test_TCM_GETROWCOUNT(void)
{
    const INT nTabs = 5;
    HWND hTab;
    INT count;

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, nTabs);
    ok(hTab != NULL, "Failed to create tab control\n");

    flush_sequences(sequences, NUM_MSG_SEQUENCES);

    count = SendMessageA(hTab, TCM_GETROWCOUNT, 0, 0);
    ok(count == 1, "Unexpected row count %d.\n", count);

    ok_sequence(sequences, TAB_SEQ_INDEX, get_row_count_seq, "Get rowCount test sequence", FALSE);
    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_sequence, "Get rowCount test parent sequence", FALSE);

    DestroyWindow(hTab);
}

START_TEST(tab)
{
    LOGFONTA logfont;

    lstrcpyA(logfont.lfFaceName, "Arial");
    memset(&logfont, 0, sizeof(logfont));
    logfont.lfHeight = -12;
    logfont.lfWeight = FW_NORMAL;
    logfont.lfCharSet = ANSI_CHARSET;
    hFont = CreateFontIndirectA(&logfont);

    init_functions();

    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);

    parent_wnd = createParentWindow();
    ok(parent_wnd != NULL, "Failed to create parent window!\n");

    test_width();
    test_curfocus();
    test_cursel();
    test_extendedstyle();
    test_unicodeformat();
    test_getset_item();
    test_getset_tooltips();
    test_adjustrect();
    test_insert_focus();
    test_delete_focus();
    test_delete_selection();
    test_removeimage();
    test_TCM_SETITEMEXTRA();
    test_TCS_OWNERDRAWFIXED();
    test_WM_CONTEXTMENU();
    test_create();
    test_TCN_SELCHANGING();
    test_TCM_GETROWCOUNT();

    DestroyWindow(parent_wnd);
}
