dialog.c 33.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* Unit test suite for the dialog functions.
 *
 * Copyright 2004 Bill Medland
 *
 * 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
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 19 20 21 22 23 24 25 26 27
 *
 *
 *
 * This test suite currently works by building a quite complex hierarchy of
 * objects in a variety of styles and then performs a limited number of tests
 * for the previous and next dialog group or tab items.
 *
 * The test specifically does not test all possibilities at this time since
 * there are several cases where the Windows behaviour is rather strange and
 * significant work would be required to get the Wine code to duplicate the
28
 * strangeness, especially since most are in situations that would not
29 30 31 32 33 34 35 36 37 38 39 40 41 42
 * normally be met.
 */

#include <assert.h>
#include <stdio.h>
#include <stdarg.h>

#include "wine/test.h"
#include "windef.h"
#include "winbase.h"
#include "winuser.h"

#define MAXHWNDS 1024
static HWND hwnd [MAXHWNDS];
43
static unsigned int numwnds=1; /* 0 is reserved for null */
44 45 46 47

/* Global handles */
static HINSTANCE g_hinst;                          /* This application's HINSTANCE */
static HWND g_hwndMain, g_hwndButton1, g_hwndButton2, g_hwndButtonCancel;
48
static HWND g_hwndTestDlg, g_hwndTestDlgBut1, g_hwndTestDlgBut2, g_hwndTestDlgEdit;
49 50 51 52
static HWND g_hwndInitialFocusT1, g_hwndInitialFocusT2, g_hwndInitialFocusGroupBox;

static LONG g_styleInitialFocusT1, g_styleInitialFocusT2;
static BOOL g_bInitialFocusInitDlgResult;
53

54 55 56
static int g_terminated;

typedef struct {
57
    unsigned int id;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
    int parent;
    DWORD style;
    DWORD exstyle;
} h_entry;

static const h_entry hierarchy [] = {
    /* 0 is reserved for the null window */
    {  1,  0, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE},
    { 20,  1,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    {  2,  1,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
    { 60,  2,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    /* What happens with groups when the parent is disabled */
    {  8,  2,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, WS_EX_CONTROLPARENT},
    { 85,  8,  WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP, 0},
    {  9,  8,  WS_CHILD, WS_EX_CONTROLPARENT},
73
    { 86,  9,  WS_CHILD | WS_VISIBLE, 0},
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    { 87,  9,  WS_CHILD | WS_VISIBLE, 0},
    { 31,  8,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 10,  2,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
    { 88, 10,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 11, 10,  WS_CHILD, WS_EX_CONTROLPARENT},
    { 89, 11,  WS_CHILD | WS_VISIBLE, 0},
    { 32, 11,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 90, 11,  WS_CHILD | WS_VISIBLE, 0},
    { 33, 10,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 21,  2,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 61,  2,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    {  3,  1,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
    { 22,  3,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 62,  3,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    {  7,  3,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
    {  4,  7,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
    { 83,  4,  WS_CHILD | WS_VISIBLE, 0},
    {  5,  4,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
    /* A couple of controls around the main dialog */
    { 29,  5,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 81,  5,  WS_CHILD | WS_VISIBLE, 0},
    /* The main dialog with lots of controls */
    {  6,  5,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
        /* At the start of a dialog */
        /* Disabled controls are skipped */
    { 63,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
        /* Invisible controls are skipped */
    { 64,  6,  WS_CHILD | WS_TABSTOP, 0},
        /* Invisible disabled controls are skipped */
    { 65,  6,  WS_CHILD | WS_DISABLED | WS_TABSTOP, 0},
        /* Non-tabstop controls are skipped for tabs but not for groups */
    { 66,  6,  WS_CHILD | WS_VISIBLE, 0},
        /* End of first group, with no tabstops in it */
    { 23,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
        /* At last a tabstop */
    { 67,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    /* A group that is totally disabled or invisible */
    { 24,  6,  WS_CHILD | WS_DISABLED | WS_GROUP, 0},
    { 68,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
    { 69,  6,  WS_CHILD | WS_TABSTOP, 0},
    /* A valid group in the middle of the dialog (not the first nor last group*/
    { 25,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
        /* A non-tabstop item will be skipped for tabs */
    { 70,  6,  WS_CHILD | WS_VISIBLE, 0},
        /* A disabled item will be skipped for tabs and groups */
    { 71,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
        /* A valid item will be found for tabs and groups */
    { 72,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
        /* A disabled item to skip when looking for the next group item */
    { 73,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
    /* The next group begins with an enabled visible label */
    { 26,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    { 74,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    { 75,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    /* That group is terminated by a disabled label */
    { 27,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP, 0},
    { 76,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    { 77,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    /* That group is terminated by an invisible label */
    { 28,  6,  WS_CHILD | WS_GROUP, 0},
    /* The end of the dialog with item for loop and recursion testing */
    { 78,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    /* No tabstop so skipped for prev tab, but found for prev group */
    { 79,  6,  WS_CHILD | WS_VISIBLE, 0},
    { 80,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
    /* A couple of controls after the main dialog */
    { 82,  5,  WS_CHILD | WS_VISIBLE, 0},
    { 30,  5,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
    /* And around them */
    { 84,  4,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
    {0, 0, 0, 0}
};

static BOOL CreateWindows (HINSTANCE hinst)
{
    const h_entry *p = hierarchy;

    while (p->id != 0)
    {
        DWORD style, exstyle;
        char ctrlname[9];

        /* Basically assert that the hierarchy is valid and track the
         * maximum control number
         */
        if (p->id >= numwnds)
        {
            if (p->id >=  sizeof(hwnd)/sizeof(hwnd[0]))
            {
                trace ("Control %d is out of range\n", p->id);
                return FALSE;
            }
            else
                numwnds = p->id+1;
        }
        if (p->id <= 0)
        {
            trace ("Control %d is out of range\n", p->id);
            return FALSE;
        }
        if (hwnd[p->id] != 0)
        {
            trace ("Control %d is used more than once\n", p->id);
            return FALSE;
        }

        /* Create the control */
        sprintf (ctrlname, "ctrl%4.4d", p->id);
        hwnd[p->id] = CreateWindowEx (p->exstyle, TEXT(p->parent ? "static" : "GetNextDlgItemWindowClass"), TEXT(ctrlname), p->style, 10, 10, 10, 10, hwnd[p->parent], p->parent ? (HMENU) (2000 + p->id) : 0, hinst, 0);
        if (!hwnd[p->id])
        {
            trace ("Failed to create control %d\n", p->id);
            return FALSE;
        }

        /* Check that the styles are as we specified (except the main one
190
         * which is quite frequently messed up).  If this keeps breaking then
191 192 193 194 195 196
         * we could mask out the bits that don't concern us.
         */
        if (p->parent)
        {
            style = GetWindowLong (hwnd[p->id], GWL_STYLE);
            exstyle = GetWindowLong (hwnd[p->id], GWL_EXSTYLE);
197
            if (style != p->style || exstyle != p->exstyle)
198
            {
199
                trace ("Style mismatch at %d: %8.8x %8.8x cf %8.8x %8.8x\n", p->id, style, exstyle, p->style, p->exstyle);
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
            }
        }
        p++;
    }

    return TRUE;
}

/* Form the lParam of a WM_KEYDOWN message */
static DWORD KeyDownData (int repeat, int scancode, int extended, int wasdown)
{
    return ((repeat & 0x0000FFFF) | ((scancode & 0x00FF) >> 16) |
            (extended ? 0x01000000 : 0) | (wasdown ? 0x40000000 : 0));
}

/* Form a WM_KEYDOWN VK_TAB message to the specified window */
static void FormTabMsg (MSG *pMsg, HWND hwnd)
{
    pMsg->hwnd = hwnd;
    pMsg->message = WM_KEYDOWN;
    pMsg->wParam = VK_TAB;
    pMsg->lParam = KeyDownData (1, 0x0F, 0, 0);
    /* pMsg->time is not set.  It shouldn't be needed */
    /* pMsg->pt is ignored */
}

/* Form a WM_KEYDOWN VK_RETURN message to the specified window */
static void FormEnterMsg (MSG *pMsg, HWND hwnd)
{
    pMsg->hwnd = hwnd;
    pMsg->message = WM_KEYDOWN;
    pMsg->wParam = VK_RETURN;
    pMsg->lParam = KeyDownData (1, 0x1C, 0, 0);
    /* pMsg->time is not set.  It shouldn't be needed */
    /* pMsg->pt is ignored */
}

/***********************************************************************
 *
 * The actual tests
 */

typedef struct
{
    int isok; /* or is it todo */
    int test;
    int dlg;
    int ctl;
    int tab;
    int prev;
    int res;
} test_record;

static int id (HWND h)
{
255
    unsigned int i;
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
    for (i = 0; i < numwnds; i++)
        if (hwnd[i] == h)
            return i;
    return -1;
}

/* Tests
 *
 * Tests 1-8 test the hCtl argument of null or the dialog itself.
 *
 *   1. Prev Group of null is null
 *   2. Prev Tab of null is null
 *   3. Prev Group of hDlg in hDlg is null
 *   4. Prev Tab of hDlg in hDlg is null
 *   5. Next Group of null is first visible enabled child
 *      Check it skips invisible, diabled and both.
 *   6. Next Tab of null is first visible enabled tabstop
 *      Check it skips invisible, disabled, nontabstop, and in combination.
 *   7. Next Group of hDlg in hDlg is as of null
 *   8. Next Tab of hDlg in hDlg is as of null
 *
 * Tests 9-14 test descent
 *
 *   9. DS_CONTROL does not result in descending the hierarchy for Tab Next
 *  10. DS_CONTROL does not result in descending the hierarchy for Group Next
 *  11. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Next
 *  12. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Next
 *  13. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Prev
 *  14. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Prev
 *
 * Tests 15-24 are the basic Prev/Next Group tests
 *
 *  15. Next Group of a visible enabled non-group control is the next visible
 *      enabled non-group control, if there is one before the next group
 *  16. Next Group of a visible enabled non-group control wraps around to the
 *      beginning of the group on finding a control that starts another group.
 *      Note that the group is in the middle of the dialog.
 *  17. As 16 except note that the next group is started with a disabled
 *      visible control.
 *  18. As 16 except note that the next group is started with an invisible
 *      enabled control.
 *  19. Next Group wraps around the controls of the dialog
 *  20. Next Group is the same even if the initial control is disabled.
 *  21. Next Group is the same even if the initial control is invisible.
 *  22. Next Group is the same even if the initial control has the group style
 *  23. Next Group returns the initial control if there is no visible enabled
 *      control in the group. (Initial control disabled and not group style).
 *  24. Prev version of test 16.
 *      Prev Group of a visible enabled non-group control wraps around to the
 *      beginning of the group on finding a control that starts the group.
 *      Note that the group is in the middle of the dialog.
 *
 * In tests 25 to 28 the control is sitting under dialogs which do not have
 * the WS_EX_CONTROLPARENT style and so cannot be reached from the top of
 * the dialog.
 *
 *  25. Next Group of an inaccessible control is as if it were accessible
 *  26. Prev Group of an inaccessible control begins searching at the highest
 *      level ancestor that did not permit recursion down the hierarchy
 *  27. Next Tab of an inaccessible control is as if it were accessible
316
 *  28. Prev Tab of an inaccessible control begins searching at the highest
317 318 319 320
 *      level ancestor that did not permit recursion down the hierarchy.
 *
 * Tests 29- are the basic Tab tests
 *
321
 *  29. Next Tab of a control is the next visible enabled control with the
322 323 324
 *      Tabstop style (N.B. skips disabled, invisible and non-tabstop)
 *  30. Prev Tab of a control is the previous visible enabled control with the
 *      Tabstop style (N.B. skips disabled, invisible and non-tabstop)
325
 *  31. Next Tab test with at least two layers of descent and finding the
326 327 328
 *      result not at the first control.
 *  32. Next Tab test with at least two layers of descent with the descent and
 *      control at the start of each level.
329
 *  33. Prev Tab test with at least two layers of descent and finding the
330 331 332 333 334 335 336 337 338 339 340 341
 *      result not at the last control.
 *  34. Prev Tab test with at least two layers of descent with the descent and
 *      control at the end of each level.
 *
 *  35. Passing NULL may result in the first child being the one returned.
 *      (group test)
 *  36. Passing NULL may result in the first child being the one returned.
 *      (tab test)
 */

static void GetNextDlgItemTest (void)
{
342
    static test_record test [] =
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    {
        /* isok test dlg  ctl  tab  prev res  */

        {   1,   1,    6,   0,   0,   1,   0},
        {   1,   2,    6,   0,   1,   1,   0},
        {   1,   3,    6,   6,   0,   1,   0},
        {   1,   4,    6,   6,   1,   1,   0},
        {   1,   5,    6,   0,   0,   0,  66},
        {   1,   6,    6,   0,   1,   0,  67},
        {   1,   7,    6,   6,   0,   0,  66},
        {   1,   8,    6,   6,   1,   0,  67},

        {   1,   9,    4,  83,   1,   0,  84},
        {   1,  10,    4,  83,   0,   0,   5},
        {   1,  11,    5,  81,   1,   0,  67},
        {   1,  12,    5,  81,   0,   0,  66},
        {   1,  13,    5,  82,   1,   1,  78},

        {   1,  14,    5,  82,   0,   1,  79},
        {   1,  15,    6,  70,   0,   0,  72},
        {   1,  16,    6,  72,   0,   0,  25},
        {   1,  17,    6,  75,   0,   0,  26},
        {   1,  18,    6,  77,   0,   0,  76},
        {   1,  19,    6,  79,   0,   0,  66},
        {   1,  20,    6,  71,   0,   0,  72},
        {   1,  21,    6,  64,   0,   0,  66},

        {   1,  22,    6,  25,   0,   0,  70},
        {   1,  23,    6,  68,   0,   0,  68},
        {   1,  24,    6,  25,   0,   1,  72},
        {   1,  25,    1,  70,   0,   0,  72},
374
        /*{   0,  26,    1,  70,   0,   1,   3}, Crashes Win95*/
375
        {   1,  27,    1,  70,   1,   0,  72},
376
        /*{   0,  28,    1,  70,   1,   1,  61}, Crashes Win95*/
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

        {   1,  29,    6,  67,   1,   0,  72},
        {   1,  30,    6,  72,   1,   1,  67},

        {   1,  35,    2,   0,   0,   0,  60},
        {   1,  36,    2,   0,   1,   0,  60},

        {   0,   0,    0,   0,   0,   0,   0}  /* End of test */
    };
    const test_record *p = test;

    ok (CreateWindows (g_hinst), "Could not create test windows\n");

    while (p->dlg)
    {
        HWND a;
        a = (p->tab ? GetNextDlgTabItem : GetNextDlgGroupItem) (hwnd[p->dlg], hwnd[p->ctl], p->prev);
        if (p->isok)
        {
            ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was %d instead of %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
        }
        else
        {
            todo_wine
            {
                ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was actually  %d matching expected %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
            }
        }
        p++;
    }
}

/*
 *  OnMainWindowCreate
 */
static BOOL OnMainWindowCreate (HWND hwnd, LPCREATESTRUCT lpcs)
{
    g_hwndButton1 = CreateWindow (TEXT("button"), TEXT("Button &1"),
            WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | BS_TEXT,
            10, 10, 80, 80, hwnd, (HMENU)100, g_hinst, 0);
    if (!g_hwndButton1) return FALSE;

    g_hwndButton2 = CreateWindow (TEXT("button"), TEXT("Button &2"),
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_TEXT,
            110, 10, 80, 80, hwnd, (HMENU)200, g_hinst, 0);
    if (!g_hwndButton2) return FALSE;

    g_hwndButtonCancel = CreateWindow (TEXT("button"), TEXT("Cancel"),
            WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
            210, 10, 80, 80, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
    if (!g_hwndButtonCancel) return FALSE;

    return TRUE;
}


433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/*
 *  OnTestDlgCreate
 */

static BOOL OnTestDlgCreate (HWND hwnd, LPCREATESTRUCT lpcs)
{
    g_hwndTestDlgEdit = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING |
            WS_EX_RIGHTSCROLLBAR | WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE,
            TEXT("Edit"), TEXT("Edit"),
            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL,
            16,33,184,24, hwnd, (HMENU)101, g_hinst, 0);
    if (!g_hwndTestDlgEdit) return FALSE;

    g_hwndTestDlgBut1 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
            | WS_EX_NOPARENTNOTIFY,
            TEXT("button"), TEXT("Button &1"),
            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
            204,33,30,24, hwnd, (HMENU)201, g_hinst, 0);
    if (!g_hwndTestDlgBut1) return FALSE;

    g_hwndTestDlgBut2 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
            | WS_EX_NOPARENTNOTIFY, TEXT("button"),
            TEXT("Button &2"),
            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
            90,102,80,24, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
    if (!g_hwndTestDlgBut2) return FALSE;

    return TRUE;
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
static LRESULT CALLBACK main_window_procA (HWND hwnd, UINT uiMsg, WPARAM wParam,
        LPARAM lParam)
{
    LRESULT result;
    switch (uiMsg)
    {
        /* Add blank case statements for these to ensure we don't use them
         * by mistake.
         */
        case DM_GETDEFID: break;
        case DM_SETDEFID: break;

        case WM_CREATE:
            return (OnMainWindowCreate (hwnd,
                    (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
        case WM_COMMAND:
            if (wParam == IDCANCEL)
            {
                g_terminated = TRUE;
                return 0;
            }
            break;
    }

    result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
    return result;
}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
static LRESULT CALLBACK disabled_test_proc (HWND hwnd, UINT uiMsg,
        WPARAM wParam, LPARAM lParam)
{
    LRESULT result;
    DWORD dw;
    HWND hwndOk;

    switch (uiMsg)
    {
        case WM_INITDIALOG:
            dw = SendMessage(hwnd, DM_GETDEFID, 0, 0);
            assert(DC_HASDEFID == HIWORD(dw));
            hwndOk = GetDlgItem(hwnd, LOWORD(dw));
            assert(hwndOk);
            EnableWindow(hwndOk, FALSE);

            PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
            PostMessage(hwnd, WM_COMMAND, IDCANCEL, 0);
            break;
        case WM_COMMAND:
            if (wParam == IDOK)
            {
                g_terminated = TRUE;
                EndDialog(hwnd, 0);
                return 0;
            }
            else if (wParam == IDCANCEL)
            {
                EndDialog(hwnd, 0);
                return 0;
            }
            break;
    }

    result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
    return result;
}

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
static LRESULT CALLBACK testDlgWinProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
        LPARAM lParam)
{
    LRESULT result;
    switch (uiMsg)
    {
        /* Add blank case statements for these to ensure we don't use them
         * by mistake.
         */
        case DM_GETDEFID: break;
        case DM_SETDEFID: break;

        case WM_CREATE:
            return (OnTestDlgCreate (hwnd,
                    (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
    }

    result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
    return result;
}
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

static BOOL RegisterWindowClasses (void)
{
    WNDCLASSA cls;

    cls.style = 0;
    cls.lpfnWndProc = DefWindowProcA;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = g_hinst;
    cls.hIcon = NULL;
    cls.hCursor = LoadCursorA (NULL, IDC_ARROW);
    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    cls.lpszMenuName = NULL;
    cls.lpszClassName = "GetNextDlgItemWindowClass";

    if (!RegisterClassA (&cls)) return FALSE;

    cls.lpfnWndProc = main_window_procA;
    cls.lpszClassName = "IsDialogMessageWindowClass";

    if (!RegisterClassA (&cls)) return FALSE;

572 573 574 575 576
    GetClassInfoA(0, "#32770", &cls);
    cls.lpfnWndProc = testDlgWinProc;
    cls.lpszClassName = "WM_NEXTDLGCTLWndClass";
    if (!RegisterClassA (&cls)) return FALSE;

577 578 579
    return TRUE;
}

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
static void WM_NEXTDLGCTLTest(void)
{
    DWORD dwVal;

    g_hwndTestDlg = CreateWindowEx( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
              | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT | WS_EX_APPWINDOW,
              "WM_NEXTDLGCTLWndClass",
              "WM_NEXTDLGCTL Message test window",
              WS_POPUPWINDOW | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_OVERLAPPED |
              WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK | DS_SETFONT | DS_MODALFRAME,
              0, 0, 235, 135,
              NULL, NULL, g_hinst, 0);

    assert (g_hwndTestDlg);
    assert (g_hwndTestDlgBut1);
    assert (g_hwndTestDlgBut2);
    assert (g_hwndTestDlgEdit);

    /*
     * Test message DM_SETDEFID
     */

    DefDlgProcA( g_hwndTestDlg, DM_SETDEFID, IDCANCEL, 0 );
    DefDlgProcA( g_hwndTestDlgBut1, BM_SETSTYLE, BS_DEFPUSHBUTTON, FALSE );
    dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);

606
    ok ( IDCANCEL == (LOWORD(dwVal)), "Did not set default ID\n" );
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631

    /*
     * Check whether message WM_NEXTDLGCTL is changing the focus to next control and if
     * the destination control is a button, style of the button should be changed to
     * BS_DEFPUSHBUTTON with out making it default.
     */

    /*
     * Keep the focus on Edit control.
     */

    if ( SetFocus( g_hwndTestDlgEdit ) )
    {
         ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't set on Edit control\n");

        /*
         * Test message WM_NEXTDLGCTL
         */
        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
        ok ((GetFocus() == g_hwndTestDlgBut1), "Focus didn't move to first button\n");

        /*
         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
         */
        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
632
        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664

        /*
         * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
         * the style of default button changed to BS_PUSHBUTTON.
         */
        if ( IDCANCEL == (LOWORD(dwVal)) )
        {
                ok ( ((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
                        "Button1 style not set to BS_DEFPUSHBUTTON\n" );

                ok ( !((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
                        "Button2's style not chaged to BS_PUSHBUTTON\n" );
        }

        /*
         * Move focus to Button2 using "WM_NEXTDLGCTL"
         */
        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
        ok ((GetFocus() == g_hwndTestDlgBut2), "Focus didn't move to second button\n");

        /*
         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
         */
        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");

        /*
         * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
         * the style of button which lost the focus changed to BS_PUSHBUTTON.
         */
        if ( IDCANCEL == (LOWORD(dwVal)) )
        {
665
                ok ( ((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
                        "Button2 style not set to BS_DEFPUSHBUTTON\n" );

                ok ( !((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
                        "Button1's style not chaged to BS_PUSHBUTTON\n" );
        }

        /*
         * Move focus to Edit control using "WM_NEXTDLGCTL"
         */
        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
        ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't move to Edit control\n");

        /*
         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
         */
        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
682
        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
683
    }
684
    DestroyWindow(g_hwndTestDlg);
685 686
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
static void IsDialogMessageWTest (void)
{
    MSG msg;

    g_hwndMain = CreateWindow ("IsDialogMessageWindowClass", "IsDialogMessageWindowClass",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            NULL, NULL, g_hinst, 0);

    assert (g_hwndMain);
    assert (g_hwndButton1);
    assert (g_hwndButtonCancel);

    /* The focus should initially be nowhere.  The first TAB should take it
     * to the first button.  The second TAB should take it to the Cancel
     * button.
     */
    FormTabMsg (&msg, g_hwndMain);
    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle first TAB\n");
    ok ((GetFocus() == g_hwndButton1), "Focus did not move to first button\n");
    FormTabMsg (&msg, g_hwndButton1);
    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle second TAB\n");
    ok ((GetFocus() == g_hwndButtonCancel),
            "Focus did not move to cancel button\n");
    FormEnterMsg (&msg, g_hwndButtonCancel);
    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle the ENTER\n");
    ok (g_terminated, "ENTER did not terminate\n");
}

716

717
static INT_PTR CALLBACK delayFocusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
        LPARAM lParam)
{
    switch (uiMsg)
    {
    case WM_INITDIALOG:
        g_hwndMain = hDlg;
       g_hwndInitialFocusGroupBox = GetDlgItem(hDlg,100);
       g_hwndButton1 = GetDlgItem(hDlg,200);
       g_hwndButton2 = GetDlgItem(hDlg,201);
       g_hwndButtonCancel = GetDlgItem(hDlg,IDCANCEL);
       g_styleInitialFocusT1 = GetWindowLong(g_hwndInitialFocusGroupBox, GWL_STYLE);

       /* Initially check the second radio button */
       SendMessage(g_hwndButton1, BM_SETCHECK, BST_UNCHECKED, 0);
       SendMessage(g_hwndButton2, BM_SETCHECK, BST_CHECKED  , 0);
       /* Continue testing after dialog initialization */
       PostMessage(hDlg, WM_USER, 0, 0);
       return g_bInitialFocusInitDlgResult;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDCANCEL)
       {
           EndDialog(hDlg, LOWORD(wParam));
           return TRUE;
       }
       return FALSE;

    case WM_USER:
       g_styleInitialFocusT2 = GetWindowLong(hDlg, GWL_STYLE);
        g_hwndInitialFocusT1 = GetFocus();
       SetFocus(hDlg);
        g_hwndInitialFocusT2 = GetFocus();
       PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
       return TRUE;
    }

    return FALSE;
}

757
static INT_PTR CALLBACK focusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
        LPARAM lParam)
{
    switch (uiMsg)
    {
    case WM_INITDIALOG:
       return TRUE;

    case WM_COMMAND:
       if (LOWORD(wParam) == IDCANCEL)
       {
           EndDialog(hDlg, LOWORD(wParam));
           return TRUE;
       }
       else if (LOWORD(wParam) == 200)
       {
           if (HIWORD(wParam) == EN_SETFOCUS)
               g_hwndInitialFocusT1 = (HWND)lParam;
       }
       return FALSE;
    }

    return FALSE;
}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
/* Helper for InitialFocusTest */
static const char * GetHwndString(HWND hw)
{
  if (hw == NULL)
    return "a null handle";
  if (hw == g_hwndMain)
    return "the dialog handle";
  if (hw == g_hwndInitialFocusGroupBox)
    return "the group box control";
  if (hw == g_hwndButton1)
    return "the first button";
  if (hw == g_hwndButton2)
    return "the second button";
  if (hw == g_hwndButtonCancel)
    return "the cancel button";

  return "unknown handle";
}

static void InitialFocusTest (void)
{
    /* Test 1:
     * This test intentionally returns FALSE in response to WM_INITDIALOG
     * without setting focus to a control. This is not allowed according to
     * MSDN, but it is exactly what MFC's CFormView does.
     *
     * Since the WM_INITDIALOG handler returns FALSE without setting the focus,
     * the focus should initially be NULL. Later, when we manually set focus to
     * the dialog, the default handler should set focus to the first control that
     * is "visible, not disabled, and has the WS_TABSTOP style" (MSDN). Because the
     * second radio button has been checked, it should be the first control
     * that meets these criteria and should receive the focus.
     */

    g_bInitialFocusInitDlgResult = FALSE;
    g_hwndInitialFocusT1 = (HWND) -1;
    g_hwndInitialFocusT2 = (HWND) -1;
    g_styleInitialFocusT1 = -1;
    g_styleInitialFocusT2 = -1;

822
    DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, delayFocusDlgWinProc);
823 824 825 826

    ok (((g_styleInitialFocusT1 & WS_TABSTOP) == 0),
       "Error in wrc - Detected WS_TABSTOP as default style for GROUPBOX\n");

827
    ok (((g_styleInitialFocusT2 & WS_VISIBLE) == 0),
828 829
       "Modal dialogs should not be shown until the message queue first goes empty\n");

830 831 832 833
    ok ((g_hwndInitialFocusT1 == NULL),
        "Error in initial focus when WM_INITDIALOG returned FALSE: "
        "Expected NULL focus, got %s (%p).\n",
        GetHwndString(g_hwndInitialFocusT1), g_hwndInitialFocusT1);
834

835 836 837 838 839
    ok ((g_hwndInitialFocusT2 == g_hwndButton2),
        "Error after first SetFocus() when WM_INITDIALOG returned FALSE: "
        "Expected the second button (%p), got %s (%p).\n",
        g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
        g_hwndInitialFocusT2);
840 841 842 843 844 845 846 847 848 849 850 851 852

    /* Test 2:
     * This is the same as above, except WM_INITDIALOG is made to return TRUE.
     * This should cause the focus to go to the second radio button right away
     * and stay there (until the user indicates otherwise).
     */

    g_bInitialFocusInitDlgResult = TRUE;
    g_hwndInitialFocusT1 = (HWND) -1;
    g_hwndInitialFocusT2 = (HWND) -1;
    g_styleInitialFocusT1 = -1;
    g_styleInitialFocusT2 = -1;

853
    DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, delayFocusDlgWinProc);
854 855 856 857 858 859 860 861 862 863 864 865

    ok ((g_hwndInitialFocusT1 == g_hwndButton2),
       "Error in initial focus when WM_INITDIALOG returned TRUE: "
       "Expected the second button (%p), got %s (%p).\n",
       g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
       g_hwndInitialFocusT2);

    ok ((g_hwndInitialFocusT2 == g_hwndButton2),
       "Error after first SetFocus() when WM_INITDIALOG returned TRUE: "
       "Expected the second button (%p), got %s (%p).\n",
       g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
       g_hwndInitialFocusT2);
866 867 868 869 870 871 872 873 874 875 876 877 878 879

    /* Test 3:
     * If the dialog has DS_CONTROL and it's not visible then we shouldn't change focus */
    {
        HWND hDlg;
        HRSRC hResource;
        HANDLE hTemplate;
        DLGTEMPLATE* pTemplate;

        hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG", (LPSTR)RT_DIALOG);
        hTemplate = LoadResource(g_hinst, hResource);
        pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);

        g_hwndInitialFocusT1 = 0;
880
        hDlg = CreateDialogIndirectParamW(g_hinst, pTemplate, NULL, focusDlgWinProc, 0);
881 882 883 884 885 886 887
        ok (hDlg != 0, "Failed to create test dialog.\n");

        ok ((g_hwndInitialFocusT1 == 0),
            "Focus should not be set for an invisible DS_CONTROL dialog %p.\n", g_hwndInitialFocusT1);

        DestroyWindow(hDlg);
    }
888 889
}

890 891 892 893 894 895 896 897 898 899 900 901
static void test_GetDlgItemText(void)
{
    char string[64];
    BOOL ret;

    strcpy(string, "Overwrite Me");
    ret = GetDlgItemTextA(NULL, 0, string, sizeof(string)/sizeof(string[0]));
    ok(!ret, "GetDlgItemText(NULL) shouldn't have succeeded\n");

    ok(string[0] == '\0', "string retrieved using GetDlgItemText should have been NULL terminated\n");
}

902 903 904 905 906 907 908 909 910 911 912 913 914 915
static void test_DialogBoxParamA(void)
{
    int ret;
    HWND hwnd_invalid = (HWND)0x4444;

    SetLastError(0xdeadbeef);
    ret = DialogBoxParamA(GetModuleHandle(NULL), "IDD_DIALOG" , hwnd_invalid, 0 , 0);
    ok(0 == ret, "DialogBoxParamA returned %d, expected 0\n", ret);
    ok(ERROR_INVALID_WINDOW_HANDLE == GetLastError(),"got %d, expected ERROR_INVALID_WINDOW_HANDLE\n",GetLastError());
    SetLastError(0xdeadbeef);
    ret = DialogBoxParamA(GetModuleHandle(NULL), "RESOURCE_INVALID" , 0, 0, 0);
    ok(-1 == ret, "DialogBoxParamA returned %d, expected -1\n", ret);
    ok(ERROR_RESOURCE_NAME_NOT_FOUND == GetLastError(),"got %d, expected ERROR_RESOURCE_NAME_NOT_FOUND\n",GetLastError());
}
916

917 918 919 920 921 922 923
static void test_DisabledDialogTest(void)
{
    g_terminated = FALSE;
    DialogBoxParam(g_hinst, "IDD_DIALOG", NULL, (DLGPROC)disabled_test_proc, 0);
    ok(FALSE == g_terminated, "dialog with disabled ok button has been terminated\n");
}

924 925 926 927 928 929 930 931
START_TEST(dialog)
{
    g_hinst = GetModuleHandleA (0);

    if (!RegisterWindowClasses()) assert(0);

    GetNextDlgItemTest();
    IsDialogMessageWTest();
932
    WM_NEXTDLGCTLTest();
933
    InitialFocusTest();
934
    test_GetDlgItemText();
935
    test_DialogBoxParamA();
936
    test_DisabledDialogTest();
937
}