propsheet.c 106 KB
Newer Older
1 2 3 4
/*
 * Property Sheets
 *
 * Copyright 1998 Francis Beaudet
5
 * Copyright 1999 Thuy Nguyen
6
 * Copyright 2004 Maxime Bellenge
Filip Navara's avatar
Filip Navara committed
7
 * Copyright 2004 Filip Navara
8
 *
9 10 11 12 13 14 15 16 17 18 19 20
 * 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
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22
 *
Filip Navara's avatar
Filip Navara committed
23 24 25 26 27 28 29
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Sep. 12, 2004, by Filip Navara.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
 *
30
 * TODO:
31
 *   - Tab order
Filip Navara's avatar
Filip Navara committed
32 33 34 35
 *   - Wizard 97 header resizing
 *   - Enforcing of minimal wizard size
 *   - Messages:
 *     o PSM_RECALCPAGESIZES
36 37
 *     o WM_HELP
 *     o WM_CONTEXTMENU
Filip Navara's avatar
Filip Navara committed
38 39 40 41 42 43 44 45 46 47 48 49
 *   - Notifications:
 *     o PSN_GETOBJECT
 *     o PSN_QUERYINITIALFOCUS
 *     o PSN_TRANSLATEACCELERATOR
 *   - Styles:
 *     o PSH_RTLREADING
 *     o PSH_STRETCHWATERMARK
 *     o PSH_USEPAGELANG
 *     o PSH_USEPSTARTPAGE
 *   - Page styles:
 *     o PSP_USEFUSIONCONTEXT
 *     o PSP_USEREFPARENT
50 51
 */

52
#include <stdarg.h>
53
#include <string.h>
54

55
#define NONAMELESSUNION
56

57
#include "windef.h"
58
#include "winbase.h"
59 60 61
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
62
#include "commctrl.h"
63
#include "prsht.h"
64
#include "comctl32.h"
65
#include "uxtheme.h"
66

67
#include "wine/debug.h"
68 69 70 71

/******************************************************************************
 * Data structures
 */
72 73
#include "pshpack2.h"

74 75 76 77 78 79 80 81 82
typedef struct
{
  WORD dlgVer;
  WORD signature;
  DWORD helpID;
  DWORD exStyle;
  DWORD style;
} MyDLGTEMPLATEEX;

83 84 85 86 87 88 89
typedef struct
{
  DWORD helpid;
  DWORD exStyle;
  DWORD style;
  short x;
  short y;
90
  short cx;
91 92 93 94 95
  short cy;
  DWORD id;
} MyDLGITEMTEMPLATEEX;
#include "poppack.h"

96 97
typedef struct tagPropPageInfo
{
98
  HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
99 100 101 102 103
  HWND hwndPage;
  BOOL isDirty;
  LPCWSTR pszText;
  BOOL hasHelp;
  BOOL useCallback;
Alexandre Julliard's avatar
Alexandre Julliard committed
104
  BOOL hasIcon;
105 106 107 108
} PropPageInfo;

typedef struct tagPropSheetInfo
{
109
  HWND hwnd;
110
  PROPSHEETHEADERW ppshheader;
111
  BOOL unicode;
112
  LPWSTR strPropertiesFor;
113
  int nPages;
114 115 116 117
  int active_page;
  BOOL isModeless;
  BOOL hasHelp;
  BOOL hasApply;
118
  BOOL hasFinish;
119
  BOOL usePropPage;
120
  BOOL useCallback;
Alexandre Julliard's avatar
Alexandre Julliard committed
121
  BOOL activeValid;
122
  PropPageInfo* proppage;
123 124
  HFONT hFont;
  HFONT hFontBold;
125 126
  int width;
  int height;
127
  HIMAGELIST hImageList;
128
  BOOL ended;
129
  INT result;
130 131 132 133 134 135 136 137 138 139 140
} PropSheetInfo;

typedef struct
{
  int x;
  int y;
} PADDING_INFO;

/******************************************************************************
 * Defines and global variables
 */
141

142
static const WCHAR PropSheetInfoStr[] =
143
    {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
144

145 146
#define PSP_INTERNAL_UNICODE 0x80000000

147
#define MAX_CAPTION_LENGTH 255
148
#define MAX_TABTEXT_LENGTH 255
Alexandre Julliard's avatar
Alexandre Julliard committed
149
#define MAX_BUTTONTEXT_LENGTH 64
150

151 152
#define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)

Filip Navara's avatar
Filip Navara committed
153 154 155 156
/* Wizard metrics specified in DLUs */
#define WIZARD_PADDING 7
#define WIZARD_HEADER_HEIGHT 36
                         	
157 158 159 160
/******************************************************************************
 * Prototypes
 */
static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
161
static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
162
static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
163 164
static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
                                int index,
165
                                int skipdir,
166
                                HPROPSHEETPAGE hpage);
167
static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, const PropSheetInfo* psInfo, int original_index);
168
static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
169
static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
170
static BOOL PROPSHEET_RemovePage(HWND hwndDlg, int index, HPROPSHEETPAGE hpage);
171

172
static INT_PTR CALLBACK
173 174
PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

175
WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
176

177 178
static WCHAR *heap_strdupW(const WCHAR *str)
{
179
    int len = lstrlenW(str) + 1;
180
    WCHAR *ret = Alloc(len * sizeof(WCHAR));
181
    lstrcpyW(ret, str);
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    return ret;
}

static WCHAR *heap_strdupAtoW(const char *str)
{
    WCHAR *ret;
    INT len;

    len = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
    ret = Alloc(len * sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);

    return ret;
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
#define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
/******************************************************************************
 *            PROPSHEET_UnImplementedFlags
 *
 * Document use of flags we don't implement yet.
 */
static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
{
    CHAR string[256];

    string[0] = '\0';

  /*
   * unhandled header flags:
   *  PSH_RTLREADING         0x00000800
   *  PSH_STRETCHWATERMARK   0x00040000
   *  PSH_USEPAGELANG        0x00200000
   */

    add_flag(PSH_RTLREADING);
    add_flag(PSH_STRETCHWATERMARK);
    add_flag(PSH_USEPAGELANG);
    if (string[0] != '\0')
	FIXME("%s\n", string);
}
222
#undef add_flag
223

224 225 226 227 228
/******************************************************************************
 *            PROPSHEET_GetPageRect
 *
 * Retrieve rect from tab control and map into the dialog for SetWindowPos
 */
Filip Navara's avatar
Filip Navara committed
229 230
static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg,
                                  RECT *rc, LPCPROPSHEETPAGEW ppshpage)
231
{
Filip Navara's avatar
Filip Navara committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {     
        HWND hwndChild;
        RECT r;

        if (((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
             (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
             !(ppshpage->dwFlags & PSP_HIDEHEADER)) ||
            (psInfo->ppshheader.dwFlags & PSH_WIZARD))
        {
            rc->left = rc->top = WIZARD_PADDING;
        }
        else
        {
            rc->left = rc->top = 0;
        }
        rc->right = psInfo->width - rc->left;
        rc->bottom = psInfo->height - rc->top;
        MapDialogRect(hwndDlg, rc);
250

Filip Navara's avatar
Filip Navara committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
        if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
            (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
            !(ppshpage->dwFlags & PSP_HIDEHEADER))
        {
            hwndChild = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
            GetClientRect(hwndChild, &r);
            MapWindowPoints(hwndChild, hwndDlg, (LPPOINT) &r, 2);
            rc->top += r.bottom + 1;
        }
    } else {
        HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
        GetClientRect(hwndTabCtrl, rc);
        SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
        MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
    }
266 267
}

268 269 270 271 272
/******************************************************************************
 *            PROPSHEET_FindPageByResId
 *
 * Find page index corresponding to page resource id.
 */
273
static INT PROPSHEET_FindPageByResId(const PropSheetInfo * psInfo, LRESULT resId)
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
{
   INT i;

   for (i = 0; i < psInfo->nPages; i++)
   {
      LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;

      /* Fixme: if resource ID is a string shall we use strcmp ??? */
      if (lppsp->u.pszTemplate == (LPVOID)resId)
         break;
   }

   return i;
}

289 290 291 292 293 294 295 296 297 298 299 300 301
/******************************************************************************
 *            PROPSHEET_CollectSheetInfoCommon
 *
 * Common code for PROPSHEET_CollectSheetInfoA/W
 */
static void PROPSHEET_CollectSheetInfoCommon(PropSheetInfo * psInfo, DWORD dwFlags)
{
  PROPSHEET_UnImplementedFlags(dwFlags);

  psInfo->hasHelp = dwFlags & PSH_HASHELP;
  psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
  psInfo->hasFinish = dwFlags & PSH_WIZARDHASFINISH;
  psInfo->isModeless = dwFlags & PSH_MODELESS;
302
  psInfo->usePropPage = dwFlags & PSH_PROPSHEETPAGE;
303 304 305 306 307 308 309 310
  if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
     psInfo->active_page = 0;

  psInfo->result = 0;
  psInfo->hImageList = 0;
  psInfo->activeValid = FALSE;
}

311
/******************************************************************************
312
 *            PROPSHEET_CollectSheetInfoA
313 314 315
 *
 * Collect relevant data.
 */
316
static void PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
317 318
                                       PropSheetInfo * psInfo)
{
319
  DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
320 321
  DWORD dwFlags = lppsh->dwFlags;

322
  psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
Alexandre Julliard's avatar
Alexandre Julliard committed
323

324
  memcpy(&psInfo->ppshheader,lppsh,dwSize);
325
  TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
326 327 328
	lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
	debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);

329 330 331
  if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
     psInfo->ppshheader.pszCaption = NULL;
  else
332
  {
333
     if (!IS_INTRESOURCE(lppsh->pszCaption))
334
     {
335
        int len = MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, NULL, 0);
336 337 338 339
        WCHAR *caption = Alloc( len*sizeof (WCHAR) );

        MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, caption, len);
        psInfo->ppshheader.pszCaption = caption;
340
     }
341
  }
342
  psInfo->nPages = lppsh->nPages;
343 344 345

  if (dwFlags & PSH_USEPSTARTPAGE)
  {
346
    TRACE("PSH_USEPSTARTPAGE is on\n");
347 348 349 350 351
    psInfo->active_page = 0;
  }
  else
    psInfo->active_page = lppsh->u2.nStartPage;

352
  PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
353 354 355
}

/******************************************************************************
356 357 358 359
 *            PROPSHEET_CollectSheetInfoW
 *
 * Collect relevant data.
 */
360
static void PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
361 362 363 364 365
                                       PropSheetInfo * psInfo)
{
  DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
  DWORD dwFlags = lppsh->dwFlags;

366
  psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
367 368

  memcpy(&psInfo->ppshheader,lppsh,dwSize);
369
  TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t%s\nnPages\t\t%d\npfnCallback\t%p\n",
370 371
      lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);

372 373 374
  if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
     psInfo->ppshheader.pszCaption = NULL;
  else
375
  {
376
     if (!IS_INTRESOURCE(lppsh->pszCaption))
377
       psInfo->ppshheader.pszCaption = heap_strdupW( lppsh->pszCaption );
378 379 380 381 382
  }
  psInfo->nPages = lppsh->nPages;

  if (dwFlags & PSH_USEPSTARTPAGE)
  {
383
    TRACE("PSH_USEPSTARTPAGE is on\n");
384 385 386 387 388
    psInfo->active_page = 0;
  }
  else
    psInfo->active_page = lppsh->u2.nStartPage;

389
  PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
390 391 392
}

/******************************************************************************
393
 *            PROPSHEET_CollectPageInfo
394 395 396 397
 *
 * Collect property sheet data.
 * With code taken from DIALOG_ParseTemplate32.
 */
398
static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
399
                               PropSheetInfo * psInfo,
400
                               int index, BOOL resize)
401
{
402
  const DLGTEMPLATE* pTemplate;
403 404 405 406
  const WORD*  p;
  DWORD dwFlags;
  int width, height;

407 408 409
  if (!lppsp)
    return FALSE;

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
  TRACE("\n");
  psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
  psInfo->proppage[index].hwndPage = 0;
  psInfo->proppage[index].isDirty = FALSE;

  /*
   * Process property page flags.
   */
  dwFlags = lppsp->dwFlags;
  psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
  psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
  psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);

  /* as soon as we have a page with the help flag, set the sheet flag on */
  if (psInfo->proppage[index].hasHelp)
    psInfo->hasHelp = TRUE;

  /*
   * Process page template.
   */
  if (dwFlags & PSP_DLGINDIRECT)
431
    pTemplate = lppsp->u.pResource;
432
  else if(dwFlags & PSP_INTERNAL_UNICODE )
433 434 435
  {
    HRSRC hResource = FindResourceW(lppsp->hInstance,
                                    lppsp->u.pszTemplate,
436
                                    (LPWSTR)RT_DIALOG);
437 438
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
439
    pTemplate = LockResource(hTemplate);
440
  }
441 442 443
  else
  {
    HRSRC hResource = FindResourceA(lppsp->hInstance,
444
                                    (LPCSTR)lppsp->u.pszTemplate,
445
                                    (LPSTR)RT_DIALOG);
446 447
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
448
    pTemplate = LockResource(hTemplate);
449
  }
450 451 452 453 454 455 456 457 458

  /*
   * Extract the size of the page and the caption.
   */
  if (!pTemplate)
      return FALSE;

  p = (const WORD *)pTemplate;

459
  if (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
460
  {
461
    /* DLGTEMPLATEEX (not defined in any std. header file) */
462 463 464 465 466 467 468 469 470

    p++;       /* dlgVer    */
    p++;       /* signature */
    p += 2;    /* help ID   */
    p += 2;    /* ext style */
    p += 2;    /* style     */
  }
  else
  {
471
    /* DLGTEMPLATE */
472 473 474 475 476 477 478 479 480 481 482

    p += 2;    /* style     */
    p += 2;    /* ext style */
  }

  p++;    /* nb items */
  p++;    /*   x      */
  p++;    /*   y      */
  width  = (WORD)*p; p++;
  height = (WORD)*p; p++;

483 484 485
  if (lppsp->dwFlags & (PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE))
    psInfo->ppshheader.dwFlags |= PSH_HEADER;

Filip Navara's avatar
Filip Navara committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
  /* Special calculation for interior wizard pages so the largest page is
   * calculated correctly. We need to add all the padding and space occupied
   * by the header so the width and height sums up to the whole wizard client
   * area. */
  if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
      (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
      !(dwFlags & PSP_HIDEHEADER))
  {
      height += 2 * WIZARD_PADDING + WIZARD_HEADER_HEIGHT;
      width += 2 * WIZARD_PADDING;
  }
  if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
  {
      height += 2 * WIZARD_PADDING;
      width += 2 * WIZARD_PADDING;
  }

503
  /* remember the largest width and height */
504 505 506 507
  if (resize)
  {
      if (width > psInfo->width)
        psInfo->width = width;
508

509 510 511
      if (height > psInfo->height)
        psInfo->height = height;
  }
512 513 514 515 516 517 518 519 520 521 522

  /* menu */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
      p += 2;
      break;
    default:
523
      p += lstrlenW( p ) + 1;
524
      break;
525
  }
526 527 528 529 530 531 532 533 534 535 536

  /* class */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
      p += 2;
      break;
    default:
537
      p += lstrlenW( p ) + 1;
538 539 540 541
      break;
  }

  /* Extract the caption */
542 543
  psInfo->proppage[index].pszText = p;
  TRACE("Tab %d %s\n",index,debugstr_w( p ));
544 545 546 547 548

  if (dwFlags & PSP_USETITLE)
  {
    WCHAR szTitle[256];
    const WCHAR *pTitle;
549
    static const WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
550

551
    if (IS_INTRESOURCE( lppsp->pszTitle ))
552
    {
553
      if (LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle, szTitle, ARRAY_SIZE(szTitle)))
554
        pTitle = szTitle;
555 556 557 558
      else if (*p)
        pTitle = p;
      else
        pTitle = pszNull;
559 560 561 562
    }
    else
      pTitle = lppsp->pszTitle;

563
    psInfo->proppage[index].pszText = heap_strdupW( pTitle );
564 565 566 567 568
  }

  /*
   * Build the image list for icons
   */
569
  if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
570 571 572 573 574 575
  {
    HICON hIcon;
    int icon_cx = GetSystemMetrics(SM_CXSMICON);
    int icon_cy = GetSystemMetrics(SM_CYSMICON);

    if (dwFlags & PSP_USEICONID)
576
      hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
                         icon_cx, icon_cy, LR_DEFAULTCOLOR);
    else
      hIcon = lppsp->u2.hIcon;

    if ( hIcon )
    {
      if (psInfo->hImageList == 0 )
	psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);

      ImageList_AddIcon(psInfo->hImageList, hIcon);
    }

  }

  return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
593

594 595 596 597 598
/******************************************************************************
 *            PROPSHEET_CreateDialog
 *
 * Creates the actual property sheet.
 */
599
static INT_PTR PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
600 601 602
{
  LRESULT ret;
  LPCVOID template;
603
  LPVOID temp = 0;
604
  HRSRC hRes;
605
  DWORD resSize;
606 607
  WORD resID = IDD_PROPSHEET;

608
  TRACE("(%p)\n", psInfo);
609
  if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
610
    resID = IDD_WIZARD;
611

612 613 614
  if( psInfo->unicode )
  {
    if(!(hRes = FindResourceW(COMCTL32_hModule,
615
                            MAKEINTRESOURCEW(resID),
616
                            (LPWSTR)RT_DIALOG)))
617 618 619 620 621 622
      return -1;
  }
  else
  {
    if(!(hRes = FindResourceA(COMCTL32_hModule,
                            MAKEINTRESOURCEA(resID),
623
                            (LPSTR)RT_DIALOG)))
624 625
      return -1;
  }
626

627
  if(!(template = LoadResource(COMCTL32_hModule, hRes)))
628
    return -1;
629

630 631 632 633 634
  /*
   * Make a copy of the dialog template.
   */
  resSize = SizeofResource(COMCTL32_hModule, hRes);

635
  temp = Alloc(2 * resSize);
636 637

  if (!temp)
638
    return -1;
639 640 641

  memcpy(temp, template, resSize);

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
  if (psInfo->ppshheader.dwFlags & PSH_NOCONTEXTHELP)
  {
    if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
      ((MyDLGTEMPLATEEX*)temp)->style &= ~DS_CONTEXTHELP;
    else
      ((DLGTEMPLATE*)temp)->style &= ~DS_CONTEXTHELP;
  }
  if ((psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) &&
      (psInfo->ppshheader.dwFlags & PSH_WIZARDCONTEXTHELP))
  {
    if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
      ((MyDLGTEMPLATEEX*)temp)->style |= DS_CONTEXTHELP;
    else
      ((DLGTEMPLATE*)temp)->style |= DS_CONTEXTHELP;
  }

658
  if (psInfo->useCallback)
659
    (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
660

661 662 663 664
  /* NOTE: MSDN states "Returns a positive value if successful, or -1
   * otherwise for modal property sheets.", but this is wrong. The
   * actual return value is either TRUE (success), FALSE (cancel) or
   * -1 (error). */
665 666
  if( psInfo->unicode )
  {
Frank Richter's avatar
Frank Richter committed
667
    ret = (INT_PTR)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
668 669
                                          temp, psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc, (LPARAM)psInfo);
670
    if ( !ret ) ret = -1;
671 672 673
  }
  else
  {
Frank Richter's avatar
Frank Richter committed
674
    ret = (INT_PTR)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
675 676
                                          temp, psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc, (LPARAM)psInfo);
677
    if ( !ret ) ret = -1;
678
  }
679

680
  Free(temp);
681

682 683 684 685
  return ret;
}

/******************************************************************************
686
 *            PROPSHEET_SizeMismatch
687
 *
688 689
 *     Verify that the tab control and the "largest" property sheet page dlg. template
 *     match in size.
690
 */
691
static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, const PropSheetInfo* psInfo)
692 693 694 695 696 697 698 699
{
  HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  RECT rcOrigTab, rcPage;

  /*
   * Original tab size.
   */
  GetClientRect(hwndTabCtrl, &rcOrigTab);
700
  TRACE("orig tab %s\n", wine_dbgstr_rect(&rcOrigTab));
701 702 703 704

  /*
   * Biggest page size.
   */
705
  SetRect(&rcPage, 0, 0, psInfo->width, psInfo->height);
706
  MapDialogRect(hwndDlg, &rcPage);
707
  TRACE("biggest page %s\n", wine_dbgstr_rect(&rcPage));
708

709
  if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
710
    return TRUE;
711
  if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
712 713 714 715 716 717 718 719 720 721 722 723 724 725
    return TRUE;

  return FALSE;
}

/******************************************************************************
 *            PROPSHEET_AdjustSize
 *
 * Resizes the property sheet and the tab control to fit the largest page.
 */
static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
{
  HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
726
  RECT rc,tabRect;
727
  int buttonHeight;
728
  PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
729
  RECT units;
730
  LONG style;
731 732 733 734 735 736 737 738

  /* Get the height of buttons */
  GetClientRect(hwndButton, &rc);
  buttonHeight = rc.bottom;

  /*
   * Biggest page size.
   */
739
  SetRect(&rc, 0, 0, psInfo->width, psInfo->height);
740 741
  MapDialogRect(hwndDlg, &rc);

742 743 744 745 746
  /* retrieve the dialog units */
  units.left = units.right = 4;
  units.top = units.bottom = 8;
  MapDialogRect(hwndDlg, &units);

747 748 749
  /*
   * Resize the tab control.
   */
750 751
  GetClientRect(hwndTabCtrl,&tabRect);

752
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
753 754 755 756

  if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
  {
      rc.bottom = rc.top + tabRect.bottom - tabRect.top;
757
      psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
758
  }
759

760 761 762
  if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
  {
      rc.right = rc.left + tabRect.right - tabRect.left;
763
      psInfo->width  = MulDiv((rc.right - rc.left),4,units.left);
764 765
  }

766
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
767 768 769

  rc.right -= rc.left;
  rc.bottom -= rc.top;
770
  TRACE("setting tab %p, rc (0,0)-(%d,%d)\n",
Frank Richter's avatar
Frank Richter committed
771
        hwndTabCtrl, rc.right, rc.bottom);
772 773 774 775 776
  SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

  GetClientRect(hwndTabCtrl, &rc);

777
  TRACE("tab client rc %s\n", wine_dbgstr_rect(&rc));
778

779 780 781
  rc.right += (padding.x * 2);
  rc.bottom += buttonHeight + (3 * padding.y);

782 783 784
  style = GetWindowLongW(hwndDlg, GWL_STYLE);
  if (!(style & WS_CHILD))
    AdjustWindowRect(&rc, style, FALSE);
785 786 787

  rc.right -= rc.left;
  rc.bottom -= rc.top;
788 789 790 791

  /*
   * Resize the property sheet.
   */
792
  TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
Frank Richter's avatar
Frank Richter committed
793
        hwndDlg, rc.right, rc.bottom);
794 795 796 797 798
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  return TRUE;
}

799 800 801 802 803
/******************************************************************************
 *            PROPSHEET_AdjustSizeWizard
 *
 * Resizes the property sheet to fit the largest page.
 */
804
static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, const PropSheetInfo* psInfo)
805 806
{
  HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
Filip Navara's avatar
Filip Navara committed
807
  RECT rc, lineRect, dialogRect;
808

Filip Navara's avatar
Filip Navara committed
809
  /* Biggest page size */
810
  SetRect(&rc, 0, 0, psInfo->width, psInfo->height);
811 812
  MapDialogRect(hwndDlg, &rc);

813
  TRACE("Biggest page %s\n", wine_dbgstr_rect(&rc));
814

Filip Navara's avatar
Filip Navara committed
815 816 817 818 819
  /* Add space for the buttons row */
  GetWindowRect(hwndLine, &lineRect);
  MapWindowPoints(NULL, hwndDlg, (LPPOINT)&lineRect, 2);
  GetClientRect(hwndDlg, &dialogRect);
  rc.bottom += dialogRect.bottom - lineRect.top - 1;
820

Filip Navara's avatar
Filip Navara committed
821 822 823 824
  /* Convert the client coordinates to window coordinates */
  AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);

  /* Resize the property sheet */
825
  TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
Frank Richter's avatar
Frank Richter committed
826
        hwndDlg, rc.right, rc.bottom);
Filip Navara's avatar
Filip Navara committed
827
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
828
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
Filip Navara's avatar
Filip Navara committed
829

830 831 832
  return TRUE;
}

833 834 835 836 837
/******************************************************************************
 *            PROPSHEET_AdjustButtons
 *
 * Adjusts the buttons' positions.
 */
838
static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, const PropSheetInfo* psInfo)
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
{
  HWND hwndButton = GetDlgItem(hwndParent, IDOK);
  RECT rcSheet;
  int x, y;
  int num_buttons = 2;
  int buttonWidth, buttonHeight;
  PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);

  if (psInfo->hasApply)
    num_buttons++;

  if (psInfo->hasHelp)
    num_buttons++;

  /*
   * Obtain the size of the buttons.
   */
  GetClientRect(hwndButton, &rcSheet);
  buttonWidth = rcSheet.right;
  buttonHeight = rcSheet.bottom;

  /*
   * Get the size of the property sheet.
862
   */
863 864
  GetClientRect(hwndParent, &rcSheet);

865
  /*
866 867 868 869 870
   * All buttons will be at this y coordinate.
   */
  y = rcSheet.bottom - (padding.y + buttonHeight);

  /*
871
   * Position OK button and make it default.
872 873 874 875 876 877 878 879
   */
  hwndButton = GetDlgItem(hwndParent, IDOK);

  x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);

  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

880
  SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
881 882


883 884 885 886 887
  /*
   * Position Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

888
  x += padding.x + buttonWidth;
889 890 891 892 893 894 895 896 897

  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

  /*
   * Position Apply button.
   */
  hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);

898 899
  if(psInfo->hasApply)
    x += padding.x + buttonWidth;
900 901 902
  else
    ShowWindow(hwndButton, SW_HIDE);

903 904 905 906
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
              SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  EnableWindow(hwndButton, FALSE);

907 908 909 910 911
  /*
   * Position Help button.
   */
  hwndButton = GetDlgItem(hwndParent, IDHELP);

912 913 914
  x += padding.x + buttonWidth;
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
              SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
915

916
  if(!psInfo->hasHelp)
917 918 919 920 921
    ShowWindow(hwndButton, SW_HIDE);

  return TRUE;
}

922 923 924 925 926 927
/******************************************************************************
 *            PROPSHEET_AdjustButtonsWizard
 *
 * Adjusts the buttons' positions.
 */
static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
928
                                          const PropSheetInfo* psInfo)
929 930 931
{
  HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
  HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
932
  HWND hwndLineHeader = GetDlgItem(hwndParent, IDC_SUNKEN_LINEHEADER);
933 934 935 936
  RECT rcSheet;
  int x, y;
  int num_buttons = 3;
  int buttonWidth, buttonHeight, lineHeight, lineWidth;
937
  PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
938 939 940

  if (psInfo->hasHelp)
    num_buttons++;
941 942
  if (psInfo->hasFinish)
    num_buttons++;
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962

  /*
   * Obtain the size of the buttons.
   */
  GetClientRect(hwndButton, &rcSheet);
  buttonWidth = rcSheet.right;
  buttonHeight = rcSheet.bottom;

  GetClientRect(hwndLine, &rcSheet);
  lineHeight = rcSheet.bottom;

  /*
   * Get the size of the property sheet.
   */
  GetClientRect(hwndParent, &rcSheet);

  /*
   * All buttons will be at this y coordinate.
   */
  y = rcSheet.bottom - (padding.y + buttonHeight);
963
  
964
  /*
965
   * Position the Back button.
966
   */
967
  hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
968

969
  x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)) - buttonWidth;
970 971 972 973

  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

974 975 976 977 978 979 980
  /*
   * Position the Next button.
   */
  hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
  
  x += buttonWidth;
  
981 982 983 984
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

  /*
985
   * Position the Finish button.
986
   */
987 988 989 990
  hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
  
  if (psInfo->hasFinish)
    x += padding.x + buttonWidth;
991 992 993 994

  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

995 996 997
  if (!psInfo->hasFinish)
    ShowWindow(hwndButton, SW_HIDE);

998 999 1000 1001 1002
  /*
   * Position the Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

1003
  x += padding.x + buttonWidth;
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014

  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

  /*
   * Position Help button.
   */
  hwndButton = GetDlgItem(hwndParent, IDHELP);

  if (psInfo->hasHelp)
  {
1015
    x += padding.x + buttonWidth;
1016 1017 1018 1019 1020 1021 1022

    SetWindowPos(hwndButton, 0, x, y, 0, 0,
                 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  }
  else
    ShowWindow(hwndButton, SW_HIDE);

Filip Navara's avatar
Filip Navara committed
1023 1024
  if (psInfo->ppshheader.dwFlags &
      (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)) 
1025 1026
      padding.x = 0;

1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
  /*
   * Position and resize the sunken line.
   */
  x = padding.x;
  y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);

  lineWidth = rcSheet.right - (padding.x * 2);
  SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
               SWP_NOZORDER | SWP_NOACTIVATE);

1037 1038 1039 1040 1041 1042 1043 1044 1045
  /*
   * Position and resize the header sunken line.
   */
  
  SetWindowPos(hwndLineHeader, 0, 0, 0, rcSheet.right, 2,
	       SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  if (!(psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)))
      ShowWindow(hwndLineHeader, SW_HIDE);

1046 1047 1048
  return TRUE;
}

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
/******************************************************************************
 *            PROPSHEET_GetPaddingInfo
 *
 * Returns the layout information.
 */
static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
{
  HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  RECT rcTab;
  PADDING_INFO padding;

  GetWindowRect(hwndTab, &rcTab);
1061
  MapWindowPoints( 0, hwndDlg, (POINT *)&rcTab, 2 );
1062

1063 1064
  padding.x = rcTab.left;
  padding.y = rcTab.top;
1065 1066 1067 1068

  return padding;
}

1069 1070 1071 1072 1073
/******************************************************************************
 *            PROPSHEET_GetPaddingInfoWizard
 *
 * Returns the layout information.
 * Vertical spacing is the distance between the line and the buttons.
1074 1075 1076 1077 1078 1079
 * Do NOT use the Help button to gather padding information when it isn't mapped
 * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
 * for it in this case !
 * FIXME: I'm not sure about any other coordinate problems with these evil
 * buttons. Fix it in case additional problems appear or maybe calculate
 * a padding in a completely different way, as this is somewhat messy.
1080
 */
1081 1082
static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
 psInfo)
1083 1084 1085 1086
{
  PADDING_INFO padding;
  RECT rc;
  HWND hwndControl;
1087 1088
  INT idButton;
  POINT ptButton, ptLine;
1089

1090
  TRACE("\n");
1091 1092 1093 1094 1095 1096
  if (psInfo->hasHelp)
  {
	idButton = IDHELP;
  }
  else
  {
1097
    if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1098 1099 1100 1101 1102 1103 1104 1105 1106
    {
	idButton = IDC_NEXT_BUTTON;
    }
    else
    {
	/* hopefully this is ok */
	idButton = IDCANCEL;
    }
  }
1107

1108
  hwndControl = GetDlgItem(hwndDlg, idButton);
1109
  GetWindowRect(hwndControl, &rc);
1110
  MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1111 1112
  ptButton.x = rc.left;
  ptButton.y = rc.top;
1113 1114 1115 1116

  /* Line */
  hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
  GetWindowRect(hwndControl, &rc);
1117
  MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1118
  ptLine.x = rc.left;
1119 1120
  ptLine.y = rc.bottom;

1121 1122 1123 1124
  padding.y = ptButton.y - ptLine.y;

  if (padding.y < 0)
	  ERR("padding negative ! Please report this !\n");
1125

1126 1127
  /* this is most probably not correct, but the best we have now */
  padding.x = padding.y;
1128 1129 1130
  return padding;
}

1131 1132 1133 1134 1135 1136
/******************************************************************************
 *            PROPSHEET_CreateTabControl
 *
 * Insert the tabs in the tab control.
 */
static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
1137
                                       const PropSheetInfo * psInfo)
1138 1139
{
  HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1140
  TCITEMW item;
1141
  int i, nTabs;
Alexandre Julliard's avatar
Alexandre Julliard committed
1142
  int iImage = 0;
1143

1144
  TRACE("\n");
1145
  item.mask = TCIF_TEXT;
1146
  item.cchTextMax = MAX_TABTEXT_LENGTH;
1147

1148
  nTabs = psInfo->nPages;
1149

1150 1151 1152 1153 1154
  /*
   * Set the image list for icons.
   */
  if (psInfo->hImageList)
  {
1155
    SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1156 1157
  }

1158
  SendMessageW(hwndTabCtrl, WM_SETREDRAW, 0, 0);
1159 1160
  for (i = 0; i < nTabs; i++)
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
1161 1162 1163 1164 1165 1166 1167 1168 1169
    if ( psInfo->proppage[i].hasIcon )
    {
      item.mask |= TCIF_IMAGE;
      item.iImage = iImage++;
    }
    else
    {
      item.mask &= ~TCIF_IMAGE;
    }
1170

1171
    item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1172
    SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, i, (LPARAM)&item);
1173
  }
1174
  SendMessageW(hwndTabCtrl, WM_SETREDRAW, 1, 0);
1175 1176 1177

  return TRUE;
}
1178 1179 1180 1181

/******************************************************************************
 *            PROPSHEET_WizardSubclassProc
 *
1182
 * Subclassing window procedure for wizard exterior pages to prevent drawing
1183 1184
 * background and so drawing above the watermark.
 */
1185
static LRESULT CALLBACK
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
PROPSHEET_WizardSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRef)
{
  switch (uMsg)
  {
    case WM_ERASEBKGND:
      return TRUE;

    case WM_CTLCOLORSTATIC:
      SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
      return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
  }

  return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}

1201 1202 1203 1204
/*
 * Get the size of an in-memory Template
 *
 *( Based on the code of PROPSHEET_CollectPageInfo)
1205
 * See also dialog.c/DIALOG_ParseTemplate32().
1206 1207
 */

1208
static UINT GetTemplateSize(const DLGTEMPLATE* pTemplate)
1209 1210 1211

{
  const WORD*  p = (const WORD *)pTemplate;
1212
  BOOL  istemplateex = (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
1213
  WORD nrofitems;
1214
  UINT ret;
1215 1216 1217

  if (istemplateex)
  {
1218
    /* DLGTEMPLATEEX (not defined in any std. header file) */
1219

1220
    TRACE("is DLGTEMPLATEEX\n");
1221 1222 1223 1224 1225 1226 1227 1228
    p++;       /* dlgVer    */
    p++;       /* signature */
    p += 2;    /* help ID   */
    p += 2;    /* ext style */
    p += 2;    /* style     */
  }
  else
  {
1229
    /* DLGTEMPLATE */
1230

1231
    TRACE("is DLGTEMPLATE\n");
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
    p += 2;    /* style     */
    p += 2;    /* ext style */
  }

  nrofitems =   (WORD)*p; p++;    /* nb items */
  p++;    /*   x      */
  p++;    /*   y      */
  p++;    /*   width  */
  p++;    /*   height */

  /* menu */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
      p += 2;
      break;
    default:
1252 1253
      TRACE("menu %s\n",debugstr_w( p ));
      p += lstrlenW( p ) + 1;
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
      break;
  }

  /* class */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
1264
      p += 2; /* 0xffff plus predefined window class ordinal value */
1265 1266
      break;
    default:
1267 1268
      TRACE("class %s\n",debugstr_w( p ));
      p += lstrlenW( p ) + 1;
1269 1270 1271
      break;
  }

1272
  /* title */
1273 1274
  TRACE("title %s\n",debugstr_w( p ));
  p += lstrlenW( p ) + 1;
1275

1276
  /* font, if DS_SETFONT set */
1277
  if ((DS_SETFONT & ((istemplateex)?  ((const MyDLGTEMPLATEEX*)pTemplate)->style :
1278 1279 1280
		     pTemplate->style)))
    {
      p+=(istemplateex)?3:1;
1281 1282
      TRACE("font %s\n",debugstr_w( p ));
      p += lstrlenW( p ) + 1; /* the font name */
1283 1284
    }

1285 1286
  /* now process the DLGITEMTEMPLATE(EX) structs (plus custom data)
   * that are following the DLGTEMPLATE(EX) data */
1287 1288 1289
  TRACE("%d items\n",nrofitems);
  while (nrofitems > 0)
    {
Frank Richter's avatar
Frank Richter committed
1290
      p = (WORD*)(((DWORD_PTR)p + 3) & ~3); /* DWORD align */
1291
      
1292
      /* skip header */
1293 1294
      p += (istemplateex ? sizeof(MyDLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE))/sizeof(WORD);
      
1295
      /* check class */
1296 1297 1298 1299 1300 1301
      switch ((WORD)*p)
	{
	case 0x0000:
	  p++;
	  break;
	case 0xffff:
1302
          TRACE("class ordinal 0x%08x\n",*(const DWORD*)p);
1303 1304 1305
	  p += 2;
	  break;
	default:
1306 1307
	  TRACE("class %s\n",debugstr_w( p ));
	  p += lstrlenW( p ) + 1;
1308 1309
	  break;
	}
1310 1311

      /* check title text */
1312 1313 1314 1315 1316 1317
      switch ((WORD)*p)
	{
	case 0x0000:
	  p++;
	  break;
	case 0xffff:
1318
          TRACE("text ordinal 0x%08x\n",*(const DWORD*)p);
1319 1320 1321
	  p += 2;
	  break;
	default:
1322 1323
	  TRACE("text %s\n",debugstr_w( p ));
	  p += lstrlenW( p ) + 1;
1324 1325
	  break;
	}
1326
      p += *p / sizeof(WORD) + 1;    /* Skip extra data */
1327 1328 1329
      --nrofitems;
    }
  
1330
  ret = (p - (const WORD*)pTemplate) * sizeof(WORD);
1331 1332
  TRACE("%p %p size 0x%08x\n", p, pTemplate, ret);
  return ret;
1333
}
1334 1335 1336 1337

/******************************************************************************
 *            PROPSHEET_CreatePage
 *
1338
 * Creates a page.
1339
 */
1340
static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1341
                                int index,
1342
                                const PropSheetInfo * psInfo,
1343
                                LPCPROPSHEETPAGEW ppshpage)
1344
{
1345
  const DLGTEMPLATE* pTemplate;
1346
  HWND hwndPage;
1347
  DWORD resSize;
1348
  DLGTEMPLATE* pTemplateCopy = NULL;
1349

1350
  TRACE("index %d\n", index);
1351

1352 1353 1354 1355 1356
  if (ppshpage == NULL)
  {
    return FALSE;
  }

1357
  if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1358
    {
1359
      pTemplate = ppshpage->u.pResource;
1360 1361
      resSize = GetTemplateSize(pTemplate);
    }
1362
  else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1363
  {
1364 1365 1366
    HRSRC hResource;
    HANDLE hTemplate;

1367
    hResource = FindResourceW(ppshpage->hInstance,
1368
                                    ppshpage->u.pszTemplate,
1369
                                    (LPWSTR)RT_DIALOG);
1370 1371 1372 1373 1374 1375 1376 1377 1378
    if(!hResource)
	return FALSE;

    resSize = SizeofResource(ppshpage->hInstance, hResource);

    hTemplate = LoadResource(ppshpage->hInstance, hResource);
    if(!hTemplate)
	return FALSE;

1379
    pTemplate = LockResource(hTemplate);
1380 1381 1382
    /*
     * Make a copy of the dialog template to make it writable
     */
1383
  }
1384 1385 1386 1387 1388 1389
  else
  {
    HRSRC hResource;
    HANDLE hTemplate;

    hResource = FindResourceA(ppshpage->hInstance,
1390
                                    (LPCSTR)ppshpage->u.pszTemplate,
1391
                                    (LPSTR)RT_DIALOG);
1392 1393 1394 1395 1396 1397 1398 1399 1400
    if(!hResource)
	return FALSE;

    resSize = SizeofResource(ppshpage->hInstance, hResource);

    hTemplate = LoadResource(ppshpage->hInstance, hResource);
    if(!hTemplate)
	return FALSE;

1401
    pTemplate = LockResource(hTemplate);
1402 1403 1404 1405
    /*
     * Make a copy of the dialog template to make it writable
     */
  }
1406 1407
  pTemplateCopy = Alloc(resSize);
  if (!pTemplateCopy)
1408 1409
    return FALSE;
  
1410 1411
  TRACE("copying pTemplate %p into pTemplateCopy %p (%d)\n", pTemplate, pTemplateCopy, resSize);
  memcpy(pTemplateCopy, pTemplate, resSize);
1412

1413
  if (((MyDLGTEMPLATEEX*)pTemplateCopy)->signature == 0xFFFF)
1414
  {
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~DS_MODALFRAME;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_CAPTION;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_SYSMENU;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_POPUP;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_DISABLED;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_VISIBLE;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_THICKFRAME;

    ((MyDLGTEMPLATEEX*)pTemplateCopy)->exStyle |= WS_EX_CONTROLPARENT;
1425 1426 1427
  }
  else
  {
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
    pTemplateCopy->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
    pTemplateCopy->style &= ~DS_MODALFRAME;
    pTemplateCopy->style &= ~WS_CAPTION;
    pTemplateCopy->style &= ~WS_SYSMENU;
    pTemplateCopy->style &= ~WS_POPUP;
    pTemplateCopy->style &= ~WS_DISABLED;
    pTemplateCopy->style &= ~WS_VISIBLE;
    pTemplateCopy->style &= ~WS_THICKFRAME;

    pTemplateCopy->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1438 1439 1440
  }

  if (psInfo->proppage[index].useCallback)
1441
    (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1442
                               (LPPROPSHEETPAGEW)ppshpage);
1443

1444 1445
  if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
     hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1446
					pTemplateCopy,
1447 1448 1449 1450 1451
					hwndParent,
					ppshpage->pfnDlgProc,
					(LPARAM)ppshpage);
  else
     hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1452
					pTemplateCopy,
1453 1454
					hwndParent,
					ppshpage->pfnDlgProc,
Marcus Meissner's avatar
Marcus Meissner committed
1455
					(LPARAM)ppshpage);
1456
  /* Free a no more needed copy */
1457
  Free(pTemplateCopy);
1458

1459 1460 1461
  if(!hwndPage)
      return FALSE;

1462
  psInfo->proppage[index].hwndPage = hwndPage;
1463

1464 1465 1466 1467 1468 1469 1470 1471
  /* Subclass exterior wizard pages */
  if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
     (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
     (ppshpage->dwFlags & PSP_HIDEHEADER))
  {
      SetWindowSubclass(hwndPage, PROPSHEET_WizardSubclassProc, 1,
                        (DWORD_PTR)ppshpage);
  }
1472 1473
  if (!(psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD))
      EnableThemeDialogTexture (hwndPage, ETDT_ENABLETAB);
1474

1475 1476 1477
  return TRUE;
}

1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
/******************************************************************************
 *            PROPSHEET_LoadWizardBitmaps
 *
 * Loads the watermark and header bitmaps for a wizard.
 */
static VOID PROPSHEET_LoadWizardBitmaps(PropSheetInfo *psInfo)
{
  if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD))
  {
    /* if PSH_USEHBMWATERMARK is not set, load the resource from pszbmWatermark 
       and put the HBITMAP in hbmWatermark. Thus all the rest of the code always 
       considers hbmWatermark as valid. */
    if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
        !(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK))
    {
1493
      psInfo->ppshheader.u4.hbmWatermark =
Frank Richter's avatar
Frank Richter committed
1494
        CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
1495 1496 1497 1498 1499 1500
    }

    /* Same behavior as for watermarks */
    if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
        !(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER))
    {
1501
      psInfo->ppshheader.u5.hbmHeader =
Frank Richter's avatar
Frank Richter committed
1502
        CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u5.pszbmHeader, 0, NULL, 0);
1503 1504 1505 1506 1507
    }
  }
}


1508 1509 1510 1511 1512 1513 1514
/******************************************************************************
 *            PROPSHEET_ShowPage
 *
 * Displays or creates the specified page.
 */
static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
{
1515
  HWND hwndTabCtrl;
1516
  HWND hwndLineHeader;
1517
  HWND control;
1518
  LPCPROPSHEETPAGEW ppshpage;
1519 1520

  TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1521
  if (index == psInfo->active_page)
1522
  {
1523
      if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
Alexandre Julliard's avatar
Alexandre Julliard committed
1524
          SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1525 1526
      return TRUE;
  }
1527

1528
  ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1529
  if (psInfo->proppage[index].hwndPage == 0)
1530
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
1531
     PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1532
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1533

1534
  if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1535 1536 1537
  {
     PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
                         psInfo->proppage[index].pszText);
1538 1539 1540 1541

     control = GetNextDlgTabItem(psInfo->proppage[index].hwndPage, NULL, FALSE);
     if(control != NULL)
         SetFocus(control);
1542
  }
1543

1544 1545
  if (psInfo->active_page != -1)
     ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1546

1547
  ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1548

1549 1550 1551
  /* Synchronize current selection with tab control
   * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
  hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1552
  SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1553

1554
  psInfo->active_page = index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1555
  psInfo->activeValid = TRUE;
1556

1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567
  if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW) )
  {
      hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
      ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
      
      if ((ppshpage->dwFlags & PSP_HIDEHEADER) || (!(psInfo->ppshheader.dwFlags & PSH_HEADER)) )
	  ShowWindow(hwndLineHeader, SW_HIDE);
      else
	  ShowWindow(hwndLineHeader, SW_SHOW);
  }

1568 1569 1570
  return TRUE;
}

1571 1572 1573 1574 1575
/******************************************************************************
 *            PROPSHEET_Back
 */
static BOOL PROPSHEET_Back(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1576
  PSHNOTIFY psn;
1577
  HWND hwndPage;
1578
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1579
  LRESULT result;
1580
  int idx;
1581

1582
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1583
  if (psInfo->active_page < 0)
1584 1585
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1586 1587 1588 1589
  psn.hdr.code     = PSN_WIZBACK;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1590

1591 1592
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;

1593
  result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1594
  if (result == -1)
1595
    return FALSE;
1596 1597 1598 1599
  else if (result == 0)
     idx = psInfo->active_page - 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, result);
1600

1601
  if (idx >= 0 && idx < psInfo->nPages)
1602
  {
1603
     if (PROPSHEET_CanSetCurSel(hwndDlg))
1604 1605 1606
     {
        SetFocus(GetDlgItem(hwndDlg, IDC_BACK_BUTTON));
        SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
1607
        PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1608
     }
1609
  }
1610 1611 1612 1613 1614 1615 1616 1617
  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Next
 */
static BOOL PROPSHEET_Next(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1618
  PSHNOTIFY psn;
1619 1620
  HWND hwndPage;
  LRESULT msgResult = 0;
1621
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1622
  int idx;
1623

1624
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1625
  if (psInfo->active_page < 0)
1626 1627
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1628 1629 1630 1631
  psn.hdr.code     = PSN_WIZNEXT;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1632

1633 1634
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;

1635
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1636 1637
  if (msgResult == -1)
    return FALSE;
1638 1639 1640 1641
  else if (msgResult == 0)
     idx = psInfo->active_page + 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1642

1643
  if (idx < psInfo->nPages )
1644
  {
1645
     if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1646 1647 1648
     {
        SetFocus(GetDlgItem(hwndDlg, IDC_NEXT_BUTTON));
        SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1649
        PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1650
     }
1651
  }
1652 1653 1654 1655 1656 1657 1658 1659 1660

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Finish
 */
static BOOL PROPSHEET_Finish(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1661
  PSHNOTIFY psn;
1662 1663
  HWND hwndPage;
  LRESULT msgResult = 0;
1664
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1665

1666
  TRACE("active_page %d\n", psInfo->active_page);
1667 1668 1669
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1670 1671 1672 1673
  psn.hdr.code     = PSN_WIZFINISH;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1674

1675 1676
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;

1677
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1678 1679 1680 1681 1682 1683

  TRACE("msg result %ld\n", msgResult);

  if (msgResult != 0)
    return FALSE;

1684 1685
  if (psInfo->result == 0)
      psInfo->result = IDOK;
1686
  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1687
    psInfo->activeValid = FALSE;
1688
  else
1689
    psInfo->ended = TRUE;
1690 1691 1692 1693

  return TRUE;
}

1694 1695 1696
/******************************************************************************
 *            PROPSHEET_Apply
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1697
static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1698 1699 1700
{
  int i;
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1701
  PSHNOTIFY psn;
1702
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1703

1704
  TRACE("active_page %d\n", psInfo->active_page);
1705 1706 1707
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1708 1709 1710
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1711

1712 1713 1714 1715

  /*
   * Send PSN_KILLACTIVE to the current page.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1716
  psn.hdr.code = PSN_KILLACTIVE;
1717 1718 1719

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;

1720
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1721 1722 1723 1724 1725
    return FALSE;

  /*
   * Send PSN_APPLY to all pages.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1726 1727
  psn.hdr.code = PSN_APPLY;
  psn.lParam   = lParam;
1728

1729
  for (i = 0; i < psInfo->nPages; i++)
1730 1731
  {
    hwndPage = psInfo->proppage[i].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1732 1733
    if (hwndPage)
    {
1734
       switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1735 1736 1737 1738 1739 1740 1741
       {
       case PSNRET_INVALID:
           PROPSHEET_ShowPage(hwndDlg, i, psInfo);
           /* fall through */
       case PSNRET_INVALID_NOCHANGEPAGE:
           return FALSE;
       }
Alexandre Julliard's avatar
Alexandre Julliard committed
1742 1743
    }
  }
1744

Alexandre Julliard's avatar
Alexandre Julliard committed
1745 1746 1747 1748 1749 1750 1751 1752 1753
  if(lParam)
  {
     psInfo->activeValid = FALSE;
  }
  else if(psInfo->active_page >= 0)
  {
     psn.hdr.code = PSN_SETACTIVE;
     psn.lParam   = 0;
     hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1754
     SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1755 1756 1757 1758 1759 1760 1761 1762
  }

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Cancel
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1763
static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1764
{
1765
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1766
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1767 1768
  PSHNOTIFY psn;
  int i;
1769

1770
  TRACE("active_page %d\n", psInfo->active_page);
1771 1772 1773 1774
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1775 1776 1777 1778
  psn.hdr.code     = PSN_QUERYCANCEL;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1779

1780
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1781 1782
    return;

Alexandre Julliard's avatar
Alexandre Julliard committed
1783 1784
  psn.hdr.code = PSN_RESET;
  psn.lParam   = lParam;
1785

Alexandre Julliard's avatar
Alexandre Julliard committed
1786 1787 1788
  for (i = 0; i < psInfo->nPages; i++)
  {
    hwndPage = psInfo->proppage[i].hwndPage;
1789

Alexandre Julliard's avatar
Alexandre Julliard committed
1790
    if (hwndPage)
1791
       SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
Alexandre Julliard's avatar
Alexandre Julliard committed
1792
  }
1793 1794

  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1795 1796 1797 1798
  {
     /* makes PSM_GETCURRENTPAGEHWND return NULL */
     psInfo->activeValid = FALSE;
  }
1799
  else
1800
    psInfo->ended = TRUE;
1801 1802
}

1803 1804 1805 1806 1807
/******************************************************************************
 *            PROPSHEET_Help
 */
static void PROPSHEET_Help(HWND hwndDlg)
{
1808
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1809
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1810
  PSHNOTIFY psn;
1811

1812
  TRACE("active_page %d\n", psInfo->active_page);
1813 1814 1815 1816
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1817 1818 1819 1820
  psn.hdr.code     = PSN_HELP;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1821

1822
  SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1823 1824
}

1825 1826 1827 1828 1829 1830
/******************************************************************************
 *            PROPSHEET_Changed
 */
static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
{
  int i;
1831
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1832

1833
  TRACE("\n");
1834
  if (!psInfo) return;
1835 1836 1837
  /*
   * Set the dirty flag of this page.
   */
1838
  for (i = 0; i < psInfo->nPages; i++)
1839 1840 1841 1842
  {
    if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
      psInfo->proppage[i].isDirty = TRUE;
  }
1843

1844 1845 1846 1847 1848 1849
  /*
   * Enable the Apply button.
   */
  if (psInfo->hasApply)
  {
    HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1850

1851 1852 1853
    EnableWindow(hwndApplyBtn, TRUE);
  }
}
1854

1855 1856 1857 1858 1859 1860 1861 1862
/******************************************************************************
 *            PROPSHEET_UnChanged
 */
static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
{
  int i;
  BOOL noPageDirty = TRUE;
  HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1863
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1864

1865
  TRACE("\n");
1866
  if ( !psInfo ) return;
1867
  for (i = 0; i < psInfo->nPages; i++)
1868 1869 1870 1871
  {
    /* set the specified page as clean */
    if (psInfo->proppage[i].hwndPage == hwndCleanPage)
      psInfo->proppage[i].isDirty = FALSE;
1872

1873
    /* look to see if there are any dirty pages */
1874 1875 1876
    if (psInfo->proppage[i].isDirty)
      noPageDirty = FALSE;
  }
1877

1878 1879 1880 1881 1882 1883
  /*
   * Disable Apply button.
   */
  if (noPageDirty)
    EnableWindow(hwndApplyBtn, FALSE);
}
1884

1885 1886 1887 1888 1889
/******************************************************************************
 *            PROPSHEET_PressButton
 */
static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
{
1890
  TRACE("buttonID %d\n", buttonID);
1891 1892 1893
  switch (buttonID)
  {
    case PSBTN_APPLYNOW:
1894
      PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1895 1896
      break;
    case PSBTN_BACK:
1897
      PROPSHEET_Back(hwndDlg);
1898 1899
      break;
    case PSBTN_CANCEL:
1900
      PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1901 1902
      break;
    case PSBTN_FINISH:
1903
      PROPSHEET_Finish(hwndDlg);
1904 1905
      break;
    case PSBTN_HELP:
1906
      PROPSHEET_DoCommand(hwndDlg, IDHELP);
1907 1908
      break;
    case PSBTN_NEXT:
1909
      PROPSHEET_Next(hwndDlg);
1910 1911
      break;
    case PSBTN_OK:
1912
      PROPSHEET_DoCommand(hwndDlg, IDOK);
1913 1914
      break;
    default:
1915
      FIXME("Invalid button index %d\n", buttonID);
1916 1917
  }
}
1918

1919 1920

/*************************************************************************
1921
 * BOOL PROPSHEET_CanSetCurSel [Internal]
1922
 *
1923
 * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
 *
 * PARAMS
 *     hwndDlg        [I] handle to a Dialog hWnd
 *
 * RETURNS
 *     TRUE if Current Selection can change
 *
 * NOTES
 */
static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
{
1935
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1936
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1937
  PSHNOTIFY psn;
1938
  BOOL res = FALSE;
1939

Alexandre Julliard's avatar
Alexandre Julliard committed
1940
  if (!psInfo)
1941 1942 1943 1944
  {
     res = FALSE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1945

1946
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1947
  if (psInfo->active_page < 0)
1948 1949 1950 1951
  {
     res = TRUE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1952

1953 1954 1955 1956
  /*
   * Notify the current page.
   */
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1957 1958 1959 1960
  psn.hdr.code     = PSN_KILLACTIVE;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1961

1962
  res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1963 1964 1965 1966

end:
  TRACE("<-- %d\n", res);
  return res;
1967 1968
}

1969 1970 1971 1972 1973
/******************************************************************************
 *            PROPSHEET_SetCurSel
 */
static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
                                int index,
1974 1975 1976
				int skipdir,
                                HPROPSHEETPAGE hpage
				)
1977
{
1978
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1979
  HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
1980
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1981

1982
  TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1983 1984

  index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
Thuy Nguyen's avatar
Thuy Nguyen committed
1985

1986 1987 1988 1989
  if (index < 0 || index >= psInfo->nPages)
  {
    TRACE("Could not find page to select!\n");
    return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
1990 1991
  }

1992 1993 1994 1995 1996
  /* unset active page while doing this transition. */
  if (psInfo->active_page != -1)
     ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
  psInfo->active_page = -1;

1997
  while (1) {
1998 1999
    int result;
    PSHNOTIFY psn;
2000
    RECT rc;
Filip Navara's avatar
Filip Navara committed
2001
    LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2002

2003 2004 2005
    if (hwndTabControl)
	SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);

2006 2007 2008 2009 2010
    psn.hdr.code     = PSN_SETACTIVE;
    psn.hdr.hwndFrom = hwndDlg;
    psn.hdr.idFrom   = 0;
    psn.lParam       = 0;

2011
    if (!psInfo->proppage[index].hwndPage) {
2012 2013
      if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage)) {
        PROPSHEET_RemovePage(hwndDlg, index, NULL);
2014 2015 2016 2017 2018 2019 2020

        if (!psInfo->isModeless)
        {
            DestroyWindow(hwndDlg);
            return FALSE;
        }

2021 2022 2023 2024 2025 2026
        if(index >= psInfo->nPages)
          index--;
        if(index < 0)
            return FALSE;
        continue;
      }
2027
    }
2028

Filip Navara's avatar
Filip Navara committed
2029 2030 2031 2032
    /* Resize the property sheet page to the fit in the Tab control
     * (for regular property sheets) or to fit in the client area (for
     * wizards).
     * NOTE: The resizing happens every time the page is selected and
2033
     * not only when it's created (some applications depend on it). */
Filip Navara's avatar
Filip Navara committed
2034
    PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2035 2036
    TRACE("setting page %p, rc (%s) w=%d, h=%d\n",
          psInfo->proppage[index].hwndPage, wine_dbgstr_rect(&rc),
Filip Navara's avatar
Filip Navara committed
2037 2038 2039 2040
          rc.right - rc.left, rc.bottom - rc.top);
    SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP,
                 rc.left, rc.top,
                 rc.right - rc.left, rc.bottom - rc.top, 0);
2041

2042
    result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2043 2044 2045 2046 2047 2048
    if (!result)
      break;
    if (result == -1) {
      index+=skipdir;
      if (index < 0) {
	index = 0;
2049
	WARN("Tried to skip before first property sheet page!\n");
2050 2051 2052
	break;
      }
      if (index >= psInfo->nPages) {
2053
	WARN("Tried to skip after last property sheet page!\n");
2054 2055 2056 2057
	index = psInfo->nPages-1;
	break;
      }
    }
2058 2059
    else if (result != 0)
    {
2060 2061 2062 2063
      int old_index = index;
      index = PROPSHEET_FindPageByResId(psInfo, result);
      if(index >= psInfo->nPages) {
        index = old_index;
2064
        WARN("Tried to skip to nonexistent page by res id\n");
2065 2066 2067
        break;
      }
      continue;
2068
    }
2069
  }
2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084

  /* Invalidate the header area */
  if ( (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
       (psInfo->ppshheader.dwFlags & PSH_HEADER) )
  {
    HWND hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
    RECT r;

    GetClientRect(hwndLineHeader, &r);
    MapWindowPoints(hwndLineHeader, hwndDlg, (LPPOINT) &r, 2);
    SetRect(&r, 0, 0, r.right + 1, r.top - 1);

    InvalidateRect(hwndDlg, &r, TRUE);
  }

2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097
  /*
   * Display the new page.
   */
  PROPSHEET_ShowPage(hwndDlg, index, psInfo);

  if (psInfo->proppage[index].hasHelp)
    EnableWindow(hwndHelp, TRUE);
  else
    EnableWindow(hwndHelp, FALSE);

  return TRUE;
}

2098 2099 2100 2101 2102 2103 2104 2105
/******************************************************************************
 *            PROPSHEET_SetCurSelId
 *
 * Selects the page, specified by resource id.
 */
static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
{
      int idx;
2106
      PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2107 2108 2109 2110 2111 2112 2113 2114 2115

      idx = PROPSHEET_FindPageByResId(psInfo, id);
      if (idx < psInfo->nPages )
      {
          if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
              PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
      }
}

2116 2117 2118 2119 2120
/******************************************************************************
 *            PROPSHEET_SetTitleA
 */
static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
{
2121
  if(!IS_INTRESOURCE(lpszText))
2122 2123
  {
     WCHAR szTitle[256];
2124
     MultiByteToWideChar(CP_ACP, 0, lpszText, -1, szTitle, ARRAY_SIZE(szTitle));
2125 2126 2127 2128 2129 2130 2131
     PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
  }
  else
  {
     PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
  }
}
2132

2133 2134 2135 2136 2137
/******************************************************************************
 *            PROPSHEET_SetTitleW
 */
static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
{
2138 2139
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
  WCHAR szTitle[256];
2140

2141
  TRACE("%s (style %08x)\n", debugstr_w(lpszText), dwStyle);
2142
  if (IS_INTRESOURCE(lpszText)) {
2143
    if (!LoadStringW(psInfo->ppshheader.hInstance, LOWORD(lpszText), szTitle, ARRAY_SIZE(szTitle)))
2144 2145 2146
      return;
    lpszText = szTitle;
  }
2147 2148
  if (dwStyle & PSH_PROPTITLE)
  {
2149
    WCHAR* dest;
2150 2151
    int lentitle = lstrlenW(lpszText);
    int lenprop  = lstrlenW(psInfo->strPropertiesFor);
2152

2153
    dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2154
    wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
2155

2156
    SetWindowTextW(hwndDlg, dest);
2157
    Free(dest);
2158 2159
  }
  else
2160
    SetWindowTextW(hwndDlg, lpszText);
2161 2162
}

2163 2164 2165 2166 2167
/******************************************************************************
 *            PROPSHEET_SetFinishTextA
 */
static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
{
2168
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2169 2170
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

2171
  TRACE("'%s'\n", lpszText);
2172 2173 2174 2175 2176 2177
  /* Set text, show and enable the Finish button */
  SetWindowTextA(hwndButton, lpszText);
  ShowWindow(hwndButton, SW_SHOW);
  EnableWindow(hwndButton, TRUE);

  /* Make it default pushbutton */
2178
  SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2179 2180 2181 2182 2183

  /* Hide Back button */
  hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
  ShowWindow(hwndButton, SW_HIDE);

2184 2185 2186 2187 2188 2189
  if (!psInfo->hasFinish)
  {
    /* Hide Next button */
    hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
    ShowWindow(hwndButton, SW_HIDE);
  }
2190 2191
}

2192 2193 2194 2195 2196
/******************************************************************************
 *            PROPSHEET_SetFinishTextW
 */
static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
{
2197
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2198 2199
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

2200
  TRACE("%s\n", debugstr_w(lpszText));
2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212
  /* Set text, show and enable the Finish button */
  SetWindowTextW(hwndButton, lpszText);
  ShowWindow(hwndButton, SW_SHOW);
  EnableWindow(hwndButton, TRUE);

  /* Make it default pushbutton */
  SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);

  /* Hide Back button */
  hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
  ShowWindow(hwndButton, SW_HIDE);

2213 2214 2215 2216 2217 2218
  if (!psInfo->hasFinish)
  {
    /* Hide Next button */
    hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
    ShowWindow(hwndButton, SW_HIDE);
  }
2219 2220
}

2221 2222 2223 2224 2225 2226 2227 2228 2229
/******************************************************************************
 *            PROPSHEET_QuerySiblings
 */
static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
                                       WPARAM wParam, LPARAM lParam)
{
  int i = 0;
  HWND hwndPage;
  LRESULT msgResult = 0;
2230
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2231

2232
  while ((i < psInfo->nPages) && (msgResult == 0))
2233 2234
  {
    hwndPage = psInfo->proppage[i].hwndPage;
2235
    msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2236 2237 2238 2239 2240 2241 2242
    i++;
  }

  return msgResult;
}

/******************************************************************************
2243
 *            PROPSHEET_InsertPage
2244
 */
2245
static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
2246
{
2247 2248
  PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
  PropPageInfo *ppi, *prev_ppi = psInfo->proppage;
2249
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2250
  LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2251 2252
  TCITEMW item;
  int index;
2253

2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272
  TRACE("hwndDlg %p, hpageInsertAfter %p, hpage %p\n", hwndDlg, hpageInsertAfter, hpage);

  if (IS_INTRESOURCE(hpageInsertAfter))
    index = LOWORD(hpageInsertAfter);
  else
  {
    index = PROPSHEET_GetPageIndex(hpageInsertAfter, psInfo, -1);
    if (index < 0)
    {
      TRACE("Could not find page to insert after!\n");
      return FALSE;
    }
    index++;
  }

  if (index > psInfo->nPages)
    index = psInfo->nPages;

  ppi = Alloc(sizeof(PropPageInfo) * (psInfo->nPages + 1));
2273 2274 2275
  if (!ppi)
      return FALSE;

2276 2277 2278 2279 2280 2281 2282 2283
  /*
   * Fill in a new PropPageInfo entry.
   */
  if (index > 0)
    memcpy(ppi, prev_ppi, index * sizeof(PropPageInfo));
  memset(&ppi[index], 0, sizeof(PropPageInfo));
  if (index < psInfo->nPages)
    memcpy(&ppi[index + 1], &prev_ppi[index], (psInfo->nPages - index) * sizeof(PropPageInfo));
2284
  psInfo->proppage = ppi;
2285

2286 2287 2288 2289 2290 2291 2292 2293
  if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, index, FALSE))
  {
     psInfo->proppage = prev_ppi;
     Free(ppi);
     return FALSE;
  }

  psInfo->proppage[index].hpage = hpage;
2294

Alexandre Julliard's avatar
Alexandre Julliard committed
2295 2296 2297
  if (ppsp->dwFlags & PSP_PREMATURE)
  {
     /* Create the page but don't show it */
2298 2299 2300 2301 2302 2303
     if (!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppsp))
     {
        psInfo->proppage = prev_ppi;
        Free(ppi);
        return FALSE;
     }
Alexandre Julliard's avatar
Alexandre Julliard committed
2304
  }
2305

2306 2307 2308 2309 2310
  Free(prev_ppi);
  psInfo->nPages++;
  if (index <= psInfo->active_page)
    psInfo->active_page++;

2311 2312 2313 2314
  /*
   * Add a new tab to the tab control.
   */
  item.mask = TCIF_TEXT;
2315
  item.pszText = (LPWSTR) psInfo->proppage[index].pszText;
2316 2317
  item.cchTextMax = MAX_TABTEXT_LENGTH;

2318 2319 2320
  if (psInfo->hImageList)
    SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);

2321
  if (psInfo->proppage[index].hasIcon)
2322 2323
  {
    item.mask |= TCIF_IMAGE;
2324
    item.iImage = index;
2325 2326
  }

2327
  SendMessageW(hwndTabControl, TCM_INSERTITEMW, index, (LPARAM)&item);
2328

Alexandre Julliard's avatar
Alexandre Julliard committed
2329
  /* If it is the only page - show it */
2330
  if (psInfo->nPages == 1)
2331
     PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2332

2333
  return TRUE;
2334 2335
}

2336 2337 2338 2339 2340 2341 2342 2343 2344 2345
/******************************************************************************
 *            PROPSHEET_AddPage
 */
static BOOL PROPSHEET_AddPage(HWND hwndDlg, HPROPSHEETPAGE hpage)
{
  PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
  TRACE("hwndDlg %p, hpage %p\n", hwndDlg, hpage);
  return PROPSHEET_InsertPage(hwndDlg, UlongToPtr(psInfo->nPages), hpage);
}

2346 2347 2348 2349 2350 2351 2352
/******************************************************************************
 *            PROPSHEET_RemovePage
 */
static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
                                 int index,
                                 HPROPSHEETPAGE hpage)
{
2353
  PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2354
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2355
  PropPageInfo* oldPages;
2356

2357
  TRACE("index %d, hpage %p\n", index, hpage);
2358 2359 2360
  if (!psInfo) {
    return FALSE;
  }
2361 2362

  index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
2363

2364
  /* Make sure that index is within range */
2365
  if (index < 0 || index >= psInfo->nPages)
Alexandre Julliard's avatar
Alexandre Julliard committed
2366
  {
2367
      TRACE("Could not find page to remove!\n");
2368
      return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2369
  }
2370

2371
  TRACE("total pages %d removing page %d active page %d\n",
2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
        psInfo->nPages, index, psInfo->active_page);
  /*
   * Check if we're removing the active page.
   */
  if (index == psInfo->active_page)
  {
    if (psInfo->nPages > 1)
    {
      if (index > 0)
      {
        /* activate previous page  */
2383
        PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2384 2385 2386 2387
      }
      else
      {
        /* activate the next page */
2388
        PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2389
        psInfo->active_page = index;
2390 2391 2392 2393
      }
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2394 2395 2396
      psInfo->active_page = -1;
      if (!psInfo->isModeless)
      {
2397
         psInfo->ended = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2398 2399
         return TRUE;
      }
2400 2401
    }
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2402
  else if (index < psInfo->active_page)
2403 2404
    psInfo->active_page--;

2405 2406 2407 2408 2409 2410 2411 2412 2413
  /* Unsubclass the page dialog window */
  if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD) &&
     (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
     ((PROPSHEETPAGEW*)psInfo->proppage[index].hpage)->dwFlags & PSP_HIDEHEADER))
  {
     RemoveWindowSubclass(psInfo->proppage[index].hwndPage,
                          PROPSHEET_WizardSubclassProc, 1);
  }

Alexandre Julliard's avatar
Alexandre Julliard committed
2414 2415 2416 2417 2418 2419
  /* Destroy page dialog window */
  DestroyWindow(psInfo->proppage[index].hwndPage);

  /* Free page resources */
  if(psInfo->proppage[index].hpage)
  {
2420
     PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
2421

2422
     if (psp->dwFlags & PSP_USETITLE)
2423
        Free ((LPVOID)psInfo->proppage[index].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2424 2425 2426

     DestroyPropertySheetPage(psInfo->proppage[index].hpage);
  }
2427

2428
  /* Remove the tab */
2429
  SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2430

2431
  oldPages = psInfo->proppage;
2432
  psInfo->nPages--;
2433
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2434

2435
  if (index > 0)
2436 2437 2438 2439 2440 2441
    memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));

  if (index < psInfo->nPages)
    memcpy(&psInfo->proppage[index], &oldPages[index + 1],
           (psInfo->nPages - index) * sizeof(PropPageInfo));

2442
  Free(oldPages);
2443

2444 2445 2446
  return FALSE;
}

2447 2448 2449 2450 2451 2452 2453 2454 2455 2456
/******************************************************************************
 *            PROPSHEET_SetWizButtons
 *
 * This code will work if (and assumes that) the Next button is on top of the
 * Finish button. ie. Finish comes after Next in the Z order.
 * This means make sure the dialog template reflects this.
 *
 */
static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
{
2457
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
2458 2459 2460
  HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
  HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
  HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2461
  BOOL enable_finish = ((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH);
2462

2463
  TRACE("%d\n", dwFlags);
2464

2465 2466 2467
  EnableWindow(hwndBack, dwFlags & PSWIZB_BACK);
  EnableWindow(hwndNext, dwFlags & PSWIZB_NEXT);
  EnableWindow(hwndFinish, enable_finish);
Alexandre Julliard's avatar
Alexandre Julliard committed
2468

2469
  /* set the default pushbutton to an enabled button */
2470
  if (enable_finish)
2471 2472 2473 2474 2475 2476 2477 2478
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
  else if (dwFlags & PSWIZB_NEXT)
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
  else if (dwFlags & PSWIZB_BACK)
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
  else
    SendMessageW(hwndDlg, DM_SETDEFID, IDCANCEL, 0);

2479
  if (!psInfo->hasFinish)
2480
  {
2481 2482 2483 2484 2485 2486 2487 2488
    if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
    {
      /* Hide the Next button */
      ShowWindow(hwndNext, SW_HIDE);
      
      /* Show the Finish button */
      ShowWindow(hwndFinish, SW_SHOW);
    }
2489 2490 2491 2492 2493 2494 2495
    else
    {
      /* Hide the Finish button */
      ShowWindow(hwndFinish, SW_HIDE);
      /* Show the Next button */
      ShowWindow(hwndNext, SW_SHOW);
    }
2496 2497 2498
  }
}

2499 2500 2501
/******************************************************************************
 *            PROPSHEET_SetHeaderTitleW
 */
2502
static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, UINT page_index, const WCHAR *title)
2503
{
2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518
    PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
    PROPSHEETPAGEW *page;

    TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_w(title));

    if (page_index >= psInfo->nPages)
        return;

    page = (PROPSHEETPAGEW *)psInfo->proppage[page_index].hpage;

    if (!IS_INTRESOURCE(page->pszHeaderTitle))
        Free((void *)page->pszHeaderTitle);

    page->pszHeaderTitle = heap_strdupW(title);
    page->dwFlags |= PSP_USEHEADERTITLE;
2519 2520 2521 2522 2523
}

/******************************************************************************
 *            PROPSHEET_SetHeaderTitleA
 */
2524
static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, UINT page_index, const char *title)
2525
{
2526 2527 2528 2529 2530 2531 2532
    WCHAR *titleW;

    TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_a(title));

    titleW = heap_strdupAtoW(title);
    PROPSHEET_SetHeaderTitleW(hwndDlg, page_index, titleW);
    Free(titleW);
2533 2534 2535 2536 2537
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleW
 */
2538
static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, UINT page_index, const WCHAR *subtitle)
2539
{
2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554
    PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
    PROPSHEETPAGEW *page;

    TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_w(subtitle));

    if (page_index >= psInfo->nPages)
        return;

    page = (PROPSHEETPAGEW *)psInfo->proppage[page_index].hpage;

    if (!IS_INTRESOURCE(page->pszHeaderSubTitle))
        Free((void *)page->pszHeaderSubTitle);

    page->pszHeaderSubTitle = heap_strdupW(subtitle);
    page->dwFlags |= PSP_USEHEADERSUBTITLE;
2555 2556 2557 2558 2559
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleA
 */
2560
static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, UINT page_index, const char *subtitle)
2561
{
2562 2563 2564 2565 2566 2567 2568
    WCHAR *subtitleW;

    TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_a(subtitle));

    subtitleW = heap_strdupAtoW(subtitle);
    PROPSHEET_SetHeaderSubTitleW(hwndDlg, page_index, subtitleW);
    Free(subtitleW);
2569 2570 2571 2572 2573 2574 2575
}

/******************************************************************************
 *            PROPSHEET_HwndToIndex
 */
static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
{
2576
    int index;
2577
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2578 2579 2580 2581 2582 2583 2584

    TRACE("(%p, %p)\n", hwndDlg, hPageDlg);

    for (index = 0; index < psInfo->nPages; index++)
        if (psInfo->proppage[index].hwndPage == hPageDlg)
            return index;
    WARN("%p not found\n", hPageDlg);
2585 2586 2587 2588 2589 2590 2591 2592
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToHwnd
 */
static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
{
2593
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2594
    TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2595 2596
    if (!psInfo)
        return 0;
2597 2598 2599 2600 2601
    if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
        WARN("%d out of range.\n", iPageIndex);
	return 0;
    }
    return (LRESULT)psInfo->proppage[iPageIndex].hwndPage;
2602 2603 2604 2605 2606 2607 2608
}

/******************************************************************************
 *            PROPSHEET_PageToIndex
 */
static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
{
2609
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2610 2611 2612

    TRACE("(%p, %p)\n", hwndDlg, hPage);

2613
    return PROPSHEET_GetPageIndex(hPage, psInfo, -1);
2614 2615 2616 2617 2618 2619 2620
}

/******************************************************************************
 *            PROPSHEET_IndexToPage
 */
static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
{
2621
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2622 2623 2624 2625 2626 2627
    TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
    if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
        WARN("%d out of range.\n", iPageIndex);
	return 0;
    }
    return (LRESULT)psInfo->proppage[iPageIndex].hpage;
2628 2629 2630 2631 2632 2633 2634
}

/******************************************************************************
 *            PROPSHEET_IdToIndex
 */
static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
{
2635 2636
    int index;
    LPCPROPSHEETPAGEW psp;
2637
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2638 2639 2640
    TRACE("(%p, %d)\n", hwndDlg, iPageId);
    for (index = 0; index < psInfo->nPages; index++) {
        psp = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
Frank Richter's avatar
Frank Richter committed
2641
        if (psp->u.pszTemplate == MAKEINTRESOURCEW(iPageId))
2642 2643 2644
            return index;
    }

2645 2646 2647 2648 2649 2650 2651 2652
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToId
 */
static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
{
2653
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2654 2655 2656 2657 2658 2659 2660
    LPCPROPSHEETPAGEW psp;
    TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
    if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
        WARN("%d out of range.\n", iPageIndex);
	return 0;
    }
    psp = (LPCPROPSHEETPAGEW)psInfo->proppage[iPageIndex].hpage;
2661
    if (psp->dwFlags & PSP_DLGINDIRECT || !IS_INTRESOURCE(psp->u.pszTemplate)) {
2662 2663 2664
        return 0;
    }
    return (LRESULT)psp->u.pszTemplate;
2665 2666 2667 2668 2669 2670 2671
}

/******************************************************************************
 *            PROPSHEET_GetResult
 */
static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
{
2672
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2673
    return psInfo->result;
2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684
}

/******************************************************************************
 *            PROPSHEET_RecalcPageSizes
 */
static BOOL PROPSHEET_RecalcPageSizes(HWND hwndDlg)
{
    FIXME("(%p): stub\n", hwndDlg);
    return FALSE;
}

2685 2686 2687 2688
/******************************************************************************
 *            PROPSHEET_GetPageIndex
 *
 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2689 2690
 * the array of PropPageInfo. If page is not found original index is used
 * (page takes precedence over index).
2691
 */
2692
static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE page, const PropSheetInfo* psInfo, int original_index)
2693
{
2694
    int index;
2695

2696
    TRACE("page %p index %d\n", page, original_index);
2697

2698 2699 2700
    for (index = 0; index < psInfo->nPages; index++)
        if (psInfo->proppage[index].hpage == page)
            return index;
2701

2702
    return original_index;
2703 2704
}

2705 2706 2707 2708 2709
/******************************************************************************
 *            PROPSHEET_CleanUp
 */
static void PROPSHEET_CleanUp(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2710
  int i;
2711
  PropSheetInfo* psInfo = RemovePropW(hwndDlg, PropSheetInfoStr);
2712 2713

  TRACE("\n");
2714
  if (!psInfo) return;
2715
  if (!IS_INTRESOURCE(psInfo->ppshheader.pszCaption))
2716
      Free ((LPVOID)psInfo->ppshheader.pszCaption);
Alexandre Julliard's avatar
Alexandre Julliard committed
2717 2718 2719 2720 2721

  for (i = 0; i < psInfo->nPages; i++)
  {
     PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;

2722 2723 2724 2725 2726 2727 2728 2729 2730
     /* Unsubclass the page dialog window */
     if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
        (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
        (psp->dwFlags & PSP_HIDEHEADER))
     {
        RemoveWindowSubclass(psInfo->proppage[i].hwndPage,
                             PROPSHEET_WizardSubclassProc, 1);
     }

Alexandre Julliard's avatar
Alexandre Julliard committed
2731 2732 2733 2734 2735
     if(psInfo->proppage[i].hwndPage)
        DestroyWindow(psInfo->proppage[i].hwndPage);

     if(psp)
     {
2736
        if (psp->dwFlags & PSP_USETITLE)
2737
           Free ((LPVOID)psInfo->proppage[i].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2738 2739 2740 2741 2742

        DestroyPropertySheetPage(psInfo->proppage[i].hpage);
     }
  }

2743 2744 2745 2746 2747 2748 2749 2750 2751 2752
  DeleteObject(psInfo->hFont);
  DeleteObject(psInfo->hFontBold);
  /* If we created the bitmaps, destroy them */
  if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
      (!(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK)) )
      DeleteObject(psInfo->ppshheader.u4.hbmWatermark);
  if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
      (!(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)) )
      DeleteObject(psInfo->ppshheader.u5.hbmHeader);

2753 2754
  Free(psInfo->proppage);
  Free(psInfo->strPropertiesFor);
2755
  ImageList_Destroy(psInfo->hImageList);
2756

2757
  GlobalFree(psInfo);
2758 2759
}

2760
static INT do_loop(const PropSheetInfo *psInfo)
2761
{
2762
    MSG msg = { 0 };
2763
    INT ret = 0;
2764
    HWND hwnd = psInfo->hwnd;
2765
    HWND parent = psInfo->ppshheader.hwndParent;
2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778

    while(IsWindow(hwnd) && !psInfo->ended && (ret = GetMessageW(&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            break;

        if(!IsDialogMessageW(hwnd, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

2779
    if(ret == 0 && msg.message)
2780 2781
        PostQuitMessage(msg.wParam);

2782 2783 2784
    if(ret != -1)
        ret = psInfo->result;

2785 2786 2787
    if(parent)
        EnableWindow(parent, TRUE);

2788 2789 2790 2791
    DestroyWindow(hwnd);
    return ret;
}

2792 2793 2794 2795 2796 2797 2798 2799
/******************************************************************************
 *            PROPSHEET_PropertySheet
 *
 * Common code between PropertySheetA/W
 */
static INT_PTR PROPSHEET_PropertySheet(PropSheetInfo* psInfo, BOOL unicode)
{
  INT_PTR bRet = 0;
2800
  HWND parent = NULL;
2801 2802 2803 2804 2805 2806 2807 2808
  if (psInfo->active_page >= psInfo->nPages) psInfo->active_page = 0;
  TRACE("startpage: %d of %d pages\n", psInfo->active_page, psInfo->nPages);

  psInfo->unicode = unicode;
  psInfo->ended = FALSE;

  if(!psInfo->isModeless)
  {
2809
      parent = psInfo->ppshheader.hwndParent;
2810
      if (parent) EnableWindow(parent, FALSE);
2811 2812 2813
  }
  bRet = PROPSHEET_CreateDialog(psInfo);
  if(!psInfo->isModeless)
2814 2815 2816 2817
      bRet = do_loop(psInfo);
  return bRet;
}

2818
/******************************************************************************
2819 2820
 *            PropertySheet    (COMCTL32.@)
 *            PropertySheetA   (COMCTL32.@)
2821 2822 2823 2824 2825 2826 2827 2828 2829
 *
 * Creates a property sheet in the specified property sheet header.
 *
 * RETURNS
 *     Modal property sheets: Positive if successful or -1 otherwise.
 *     Modeless property sheets: Property sheet handle.
 *     Or:
 *| ID_PSREBOOTSYSTEM - The user must reboot the computer for the changes to take effect.
 *| ID_PSRESTARTWINDOWS - The user must restart Windows for the changes to take effect.
2830
 */
Frank Richter's avatar
Frank Richter committed
2831
INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2832
{
2833
  PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2834
  UINT i, n;
2835
  const BYTE* pByte;
2836

2837 2838 2839
  TRACE("(%p)\n", lppsh);

  PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2840

2841
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2842
  pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2843

2844
  for (n = i = 0; i < lppsh->nPages; i++, n++)
2845
  {
2846
    if (!psInfo->usePropPage)
2847
      psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
2848 2849
    else
    {
2850
       psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2851
       pByte += ((LPCPROPSHEETPAGEA)pByte)->dwSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
2852 2853
    }

2854
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2855
                               psInfo, n, TRUE))
2856
    {
2857
	if (psInfo->usePropPage)
2858 2859 2860 2861
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
2862
  }
2863

2864
  return PROPSHEET_PropertySheet(psInfo, FALSE);
2865 2866
}

2867
/******************************************************************************
2868
 *            PropertySheetW   (COMCTL32.@)
2869 2870
 *
 * See PropertySheetA.
2871
 */
Frank Richter's avatar
Frank Richter committed
2872
INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2873
{
2874
  PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2875
  UINT i, n;
2876
  const BYTE* pByte;
2877 2878 2879 2880 2881

  TRACE("(%p)\n", lppsh);

  PROPSHEET_CollectSheetInfoW(lppsh, psInfo);

2882
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2883
  pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2884

2885 2886
  for (n = i = 0; i < lppsh->nPages; i++, n++)
  {
2887
    if (!psInfo->usePropPage)
2888 2889 2890 2891
      psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
    else
    {
       psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2892
       pByte += ((LPCPROPSHEETPAGEW)pByte)->dwSize;
2893 2894
    }

2895
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2896
                               psInfo, n, TRUE))
2897
    {
2898
	if (psInfo->usePropPage)
2899 2900 2901 2902 2903 2904
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
  }

2905
  return PROPSHEET_PropertySheet(psInfo, TRUE);
2906 2907
}

2908 2909 2910 2911 2912 2913 2914 2915 2916 2917
static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
{
    LPWSTR ret;

    if (IS_INTRESOURCE(str))
    {
        HRSRC hrsrc;
        HGLOBAL hmem;
        WCHAR *ptr;
        WORD i, id = LOWORD(str);
2918
        UINT len;
2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935

        if (!(hrsrc = FindResourceW( instance, MAKEINTRESOURCEW((id >> 4) + 1), (LPWSTR)RT_STRING )))
            return NULL;
        if (!(hmem = LoadResource( instance, hrsrc ))) return NULL;
        if (!(ptr = LockResource( hmem ))) return NULL;
        for (i = id & 0x0f; i > 0; i--) ptr += *ptr + 1;
        len = *ptr;
        if (!len) return NULL;
        ret = Alloc( (len + 1) * sizeof(WCHAR) );
        if (ret)
        {
            memcpy( ret, ptr + 1, len * sizeof(WCHAR) );
            ret[len] = 0;
        }
    }
    else
    {
2936
        int len = (lstrlenW(str) + 1) * sizeof(WCHAR);
2937 2938 2939 2940 2941 2942 2943
        ret = Alloc( len );
        if (ret) memcpy( ret, str, len );
    }
    return ret;
}


2944
/******************************************************************************
2945 2946
 *            CreatePropertySheetPage    (COMCTL32.@)
 *            CreatePropertySheetPageA   (COMCTL32.@)
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956
 *
 * Creates a new property sheet page.
 *
 * RETURNS
 *     Success: Handle to new property sheet page.
 *     Failure: NULL.
 *
 * NOTES
 *     An application must use the PSM_ADDPAGE message to add the new page to
 *     an existing property sheet.
2957
 */
2958 2959
HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
                          LPCPROPSHEETPAGEA lpPropSheetPage)
2960
{
2961
    PROPSHEETPAGEW *ppsp;
2962

2963 2964 2965
    if (lpPropSheetPage->dwSize < PROPSHEETPAGEA_V1_SIZE)
        return NULL;

2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977
    /* original data is used for callback notifications */
    if ((lpPropSheetPage->dwFlags & PSP_USECALLBACK) && lpPropSheetPage->pfnCallback)
    {
        ppsp = Alloc(2 * sizeof(*ppsp));
        memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
        memcpy(ppsp + 1, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
    }
    else
    {
        ppsp = Alloc(sizeof(*ppsp));
        memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
    }
2978

2979
    ppsp->dwFlags &= ~PSP_INTERNAL_UNICODE;
2980

2981 2982
    if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
    {
2983
        if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
2984 2985
        {
            int len = strlen(lpPropSheetPage->u.pszTemplate) + 1;
2986 2987 2988
            char *template = Alloc( len );

            ppsp->u.pszTemplate = (LPWSTR)strcpy( template, lpPropSheetPage->u.pszTemplate );
2989 2990
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2991

2992 2993
    if (ppsp->dwFlags & PSP_USEICONID)
    {
2994
        if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
2995
            ppsp->u2.pszIcon = heap_strdupAtoW( lpPropSheetPage->u2.pszIcon );
2996 2997 2998 2999
    }

    if (ppsp->dwFlags & PSP_USETITLE)
    {
3000
        if (IS_INTRESOURCE( ppsp->pszTitle ))
3001
            ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3002 3003
        else
            ppsp->pszTitle = heap_strdupAtoW( lpPropSheetPage->pszTitle );
3004 3005 3006
    }
    else
        ppsp->pszTitle = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3007

3008 3009 3010 3011 3012
    if (ppsp->dwFlags & PSP_HIDEHEADER)
        ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);

    if (ppsp->dwFlags & PSP_USEHEADERTITLE)
    {
3013
        if (IS_INTRESOURCE( ppsp->pszHeaderTitle ))
3014
            ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3015 3016
        else
            ppsp->pszHeaderTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderTitle );
3017 3018 3019 3020 3021 3022
    }
    else
        ppsp->pszHeaderTitle = NULL;

    if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
    {
3023
        if (IS_INTRESOURCE( ppsp->pszHeaderSubTitle ))
3024
            ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3025 3026
        else
            ppsp->pszHeaderSubTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderSubTitle );
3027 3028 3029 3030
    }
    else
        ppsp->pszHeaderSubTitle = NULL;

3031 3032 3033
    if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEA_V1_SIZE && ppsp->pfnCallback)
        ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);

3034
    return (HPROPSHEETPAGE)ppsp;
3035 3036
}

3037
/******************************************************************************
3038
 *            CreatePropertySheetPageW   (COMCTL32.@)
3039 3040
 *
 * See CreatePropertySheetA.
3041
 */
3042
HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
3043
{
3044 3045 3046 3047
    PROPSHEETPAGEW *ppsp;

    if (lpPropSheetPage->dwSize < PROPSHEETPAGEW_V1_SIZE)
        return NULL;
3048

3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060
    /* original data is used for callback notifications */
    if ((lpPropSheetPage->dwFlags & PSP_USECALLBACK) && lpPropSheetPage->pfnCallback)
    {
        ppsp = Alloc(2 * sizeof(*ppsp));
        memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
        memcpy(ppsp + 1, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
    }
    else
    {
        ppsp = Alloc(sizeof(*ppsp));
        memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
    }
3061

3062
    ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
3063

3064 3065
    if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
    {
3066
        if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
3067
            ppsp->u.pszTemplate = heap_strdupW( lpPropSheetPage->u.pszTemplate );
3068
    }
3069

3070 3071
    if ( ppsp->dwFlags & PSP_USEICONID )
    {
3072
        if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
3073
            ppsp->u2.pszIcon = heap_strdupW( lpPropSheetPage->u2.pszIcon );
3074
    }
3075

3076
    if (ppsp->dwFlags & PSP_USETITLE)
3077
        ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3078 3079 3080 3081 3082 3083 3084
    else
        ppsp->pszTitle = NULL;

    if (ppsp->dwFlags & PSP_HIDEHEADER)
        ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);

    if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3085
        ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3086 3087 3088 3089
    else
        ppsp->pszHeaderTitle = NULL;

    if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3090
        ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3091 3092
    else
        ppsp->pszHeaderSubTitle = NULL;
3093

3094 3095 3096
    if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEW_V1_SIZE && ppsp->pfnCallback)
        ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);

3097
    return (HPROPSHEETPAGE)ppsp;
3098 3099
}

3100
/******************************************************************************
3101
 *            DestroyPropertySheetPage   (COMCTL32.@)
3102 3103 3104 3105 3106 3107 3108 3109
 *
 * Destroys a property sheet page previously created with
 * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
 * memory.
 *
 * RETURNS
 *     Success: TRUE
 *     Failure: FALSE
3110
 */
3111
BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
3112
{
3113
  PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
3114 3115 3116 3117

  if (!psp)
     return FALSE;

3118 3119 3120
  if ((psp->dwFlags & PSP_USECALLBACK) && psp->pfnCallback)
     psp->pfnCallback(0, PSPCB_RELEASE, psp + 1);

3121
  if (!(psp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE( psp->u.pszTemplate ))
3122
     Free ((LPVOID)psp->u.pszTemplate);
Alexandre Julliard's avatar
Alexandre Julliard committed
3123

3124
  if ((psp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE( psp->u2.pszIcon ))
3125
     Free ((LPVOID)psp->u2.pszIcon);
Alexandre Julliard's avatar
Alexandre Julliard committed
3126

3127
  if ((psp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE( psp->pszTitle ))
3128
     Free ((LPVOID)psp->pszTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
3129

3130 3131 3132 3133 3134 3135
  if ((psp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE( psp->pszHeaderTitle ))
     Free ((LPVOID)psp->pszHeaderTitle);

  if ((psp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE( psp->pszHeaderSubTitle ))
     Free ((LPVOID)psp->pszHeaderSubTitle);

3136
  Free(hPropPage);
3137 3138

  return TRUE;
3139 3140
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3141 3142 3143 3144 3145
/******************************************************************************
 *            PROPSHEET_IsDialogMessage
 */
static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
{
3146
   PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
3147

3148
   TRACE("\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
3149 3150 3151 3152 3153 3154
   if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
      return FALSE;

   if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
   {
      int new_page = 0;
3155
      INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
Alexandre Julliard's avatar
Alexandre Julliard committed
3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183

      if (!(dlgCode & DLGC_WANTMESSAGE))
      {
         switch (lpMsg->wParam)
         {
            case VK_TAB:
               if (GetKeyState(VK_SHIFT) & 0x8000)
                   new_page = -1;
                else
                   new_page = 1;
               break;

            case VK_NEXT:   new_page = 1;  break;
            case VK_PRIOR:  new_page = -1; break;
         }
      }

      if (new_page)
      {
         if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
         {
            new_page += psInfo->active_page;

            if (new_page < 0)
               new_page = psInfo->nPages - 1;
            else if (new_page >= psInfo->nPages)
               new_page = 0;

3184
            PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
3185 3186 3187 3188 3189 3190
         }

         return TRUE;
      }
   }

3191
   return IsDialogMessageW(hwnd, lpMsg);
Alexandre Julliard's avatar
Alexandre Julliard committed
3192 3193
}

3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211
/******************************************************************************
 *            PROPSHEET_DoCommand
 */
static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
{

    switch (wID) {

    case IDOK:
    case IDC_APPLY_BUTTON:
	{
	    HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);

	    if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
		break;

	    if (wID == IDOK)
		{
3212
                    PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3213

3214 3215 3216
                    /* don't overwrite ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM */
                    if (psInfo->result == 0)
                        psInfo->result = IDOK;
3217 3218 3219 3220

		    if (psInfo->isModeless)
			psInfo->activeValid = FALSE;
		    else
3221
                        psInfo->ended = TRUE;
3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247
		}
	    else
		EnableWindow(hwndApplyBtn, FALSE);

	    break;
	}

    case IDC_BACK_BUTTON:
	PROPSHEET_Back(hwnd);
	break;

    case IDC_NEXT_BUTTON:
	PROPSHEET_Next(hwnd);
	break;

    case IDC_FINISH_BUTTON:
	PROPSHEET_Finish(hwnd);
	break;

    case IDCANCEL:
	PROPSHEET_Cancel(hwnd, 0);
	break;

    case IDHELP:
	PROPSHEET_Help(hwnd);
	break;
3248 3249 3250

    default:
        return FALSE;
3251 3252 3253 3254 3255
    }

    return TRUE;
}

3256 3257 3258
/******************************************************************************
 *            PROPSHEET_Paint
 */
3259
static LRESULT PROPSHEET_Paint(HWND hwnd, HDC hdcParam)
3260
{
3261
    PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3262 3263 3264 3265 3266 3267 3268
    PAINTSTRUCT ps;
    HDC hdc, hdcSrc;
    BITMAP bm;
    HBITMAP hbmp;
    HPALETTE hOldPal = 0;
    int offsety = 0;
    HBRUSH hbr;
3269
    RECT r, rzone;
3270
    LPCPROPSHEETPAGEW ppshpage;
3271 3272
    WCHAR szBuffer[256];
    int nLength;
3273

3274
    hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3275 3276 3277 3278 3279 3280 3281
    if (!hdc) return 1;

    hdcSrc = CreateCompatibleDC(0);

    if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
	hOldPal = SelectPalette(hdc, psInfo->ppshheader.hplWatermark, FALSE);

3282 3283 3284 3285 3286 3287
    if (psInfo->active_page < 0)
        ppshpage = NULL;
    else
        ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;

    if ( (ppshpage && !(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3288 3289
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_HEADER) ) 
3290 3291 3292 3293 3294 3295
    {
	HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
	HFONT hOldFont;
	COLORREF clrOld = 0;
	int oldBkMode = 0;

3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354
        GetClientRect(hwndLineHeader, &r);
        MapWindowPoints(hwndLineHeader, hwnd, (LPPOINT) &r, 2);
        SetRect(&rzone, 0, 0, r.right + 1, r.top - 1);

        hOldFont = SelectObject(hdc, psInfo->hFontBold);

        if (psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)
        {
            hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u5.hbmHeader);

            GetObjectW(psInfo->ppshheader.u5.hbmHeader, sizeof(BITMAP), &bm);
            if (psInfo->ppshheader.dwFlags & PSH_WIZARD97_OLD)
            {
                /* Fill the unoccupied part of the header with color of the
                 * left-top pixel, but do it only when needed.
                 */
                if (bm.bmWidth < r.right || bm.bmHeight < r.bottom)
                {
                    hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
                    r = rzone;
                    if (bm.bmWidth < r.right)
                    {
                        r.left = bm.bmWidth;
                        FillRect(hdc, &r, hbr);
                    }
                    if (bm.bmHeight < r.bottom)
                    {
                        r.left = 0;
                        r.top = bm.bmHeight;
                        FillRect(hdc, &r, hbr);
                    }
                    DeleteObject(hbr);
                }

                /* Draw the header itself. */
                BitBlt(hdc, 0, 0, bm.bmWidth, min(bm.bmHeight, rzone.bottom),
                        hdcSrc, 0, 0, SRCCOPY);
            }
            else
            {
                int margin;
                hbr = GetSysColorBrush(COLOR_WINDOW);
                FillRect(hdc, &rzone, hbr);

                /* Draw the header bitmap. It's always centered like a
                 * common 49 x 49 bitmap. */
                margin = (rzone.bottom - 49) / 2;
                BitBlt(hdc, rzone.right - 49 - margin, margin,
                        min(bm.bmWidth, 49), min(bm.bmHeight, 49),
                        hdcSrc, 0, 0, SRCCOPY);

                /* NOTE: Native COMCTL32 draws a white stripe over the bitmap
                 * if its height is smaller than 49 pixels. Because the reason
                 * for this bug is unknown the current code doesn't try to
                 * replicate it. */
            }

            SelectObject(hdcSrc, hbmp);
        }
3355

3356 3357 3358
	clrOld = SetTextColor (hdc, 0x00000000);
	oldBkMode = SetBkMode (hdc, TRANSPARENT); 

3359
	if (ppshpage->dwFlags & PSP_USEHEADERTITLE) {
3360
	    SetRect(&r, 20, 10, 0, 0);
3361
            if (!IS_INTRESOURCE(ppshpage->pszHeaderTitle))
3362
                DrawTextW(hdc, ppshpage->pszHeaderTitle, -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3363
	    else
3364
	    {
Frank Richter's avatar
Frank Richter committed
3365
		nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderTitle,
3366 3367 3368
				      szBuffer, 256);
		if (nLength != 0)
		{
3369
		    DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3370 3371
		}
	    }
3372 3373 3374 3375
	}

	if (ppshpage->dwFlags & PSP_USEHEADERSUBTITLE) {
	    SelectObject(hdc, psInfo->hFont);
3376
	    SetRect(&r, 40, 25, rzone.right - 69, rzone.bottom);
3377
            if (!IS_INTRESOURCE(ppshpage->pszHeaderTitle))
3378
                DrawTextW(hdc, ppshpage->pszHeaderSubTitle, -1, &r, DT_LEFT | DT_WORDBREAK);
3379
	    else
3380
	    {
Frank Richter's avatar
Frank Richter committed
3381
		nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderSubTitle,
3382 3383 3384
				      szBuffer, 256);
		if (nLength != 0)
		{
3385
		    DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_WORDBREAK);
3386 3387
		}
	    }
3388 3389 3390 3391 3392 3393 3394 3395 3396
	}

	offsety = rzone.bottom + 2;

	SetTextColor(hdc, clrOld);
	SetBkMode(hdc, oldBkMode);
	SelectObject(hdc, hOldFont);
    }

3397
    if ( (ppshpage && (ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3398 3399
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) ) 
3400
    {
3401 3402 3403 3404
	HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);	    

	GetClientRect(hwndLine, &r);
	MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);
3405
        SetRect(&rzone, 0, 0, r.right, r.top - 1);
3406 3407 3408 3409

	hbr = GetSysColorBrush(COLOR_WINDOW);
	FillRect(hdc, &rzone, hbr);

3410
	GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), &bm);
3411 3412
	hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);

3413 3414
        /* The watermark is truncated to a width of 164 pixels */
        r.right = min(r.right, 164);
3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427
	BitBlt(hdc, 0, offsety, min(bm.bmWidth, r.right),
	       min(bm.bmHeight, r.bottom), hdcSrc, 0, 0, SRCCOPY);

	/* If the bitmap is not big enough, fill the remaining area
	   with the color of pixel (0,0) of bitmap - see MSDN */
	if (r.top > bm.bmHeight) {
	    r.bottom = r.top - 1;
	    r.top = bm.bmHeight;
	    r.left = 0;
	    r.right = bm.bmWidth;
	    hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
	    FillRect(hdc, &r, hbr);
	    DeleteObject(hbr);
3428
	}
3429 3430

	SelectObject(hdcSrc, hbmp);	    
3431 3432 3433 3434 3435 3436 3437
    }

    if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
	SelectPalette(hdc, hOldPal, FALSE);

    DeleteDC(hdcSrc);

3438
    if (!hdcParam) EndPaint(hwnd, &ps);
3439 3440 3441 3442

    return 0;
}

3443 3444 3445
/******************************************************************************
 *            PROPSHEET_DialogProc
 */
3446
static INT_PTR CALLBACK
3447 3448
PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
3449
  TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n",
3450 3451
	hwnd, uMsg, wParam, lParam);

3452 3453 3454 3455 3456
  switch (uMsg)
  {
    case WM_INITDIALOG:
    {
      PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3457
      WCHAR* strCaption = Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3458
      HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
Alexandre Julliard's avatar
Alexandre Julliard committed
3459
      int idx;
3460
      LOGFONTW logFont;
3461

3462 3463 3464
      /* Using PropSheetInfoStr to store extra data doesn't match the native
       * common control: native uses TCM_[GS]ETITEM
       */
3465
      SetPropW(hwnd, PropSheetInfoStr, psInfo);
3466

3467
      /*
3468 3469
       * psInfo->hwnd is not being used by WINE code - it exists
       * for compatibility with "real" Windoze. The same about
3470
       * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3471 3472 3473
       * property.
       */
      psInfo->hwnd = hwnd;
3474
      SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3475

3476 3477 3478 3479 3480
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
      {
        /* set up the Next and Back buttons by default */
        PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);
      }
3481

3482
      /* Set up fonts */
3483 3484
      SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
      psInfo->hFont = CreateFontIndirectW (&logFont);
3485
      logFont.lfWeight = FW_BOLD;
3486
      psInfo->hFontBold = CreateFontIndirectW (&logFont);
3487
      
3488 3489 3490
      /*
       * Small icon in the title bar.
       */
3491 3492
      if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
          (psInfo->ppshheader.dwFlags & PSH_USEHICON))
3493 3494 3495 3496 3497
      {
        HICON hIcon;
        int icon_cx = GetSystemMetrics(SM_CXSMICON);
        int icon_cy = GetSystemMetrics(SM_CYSMICON);

3498
        if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3499
          hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3500
                             psInfo->ppshheader.u.pszIcon,
3501 3502 3503 3504
                             IMAGE_ICON,
                             icon_cx, icon_cy,
                             LR_DEFAULTCOLOR);
        else
3505
          hIcon = psInfo->ppshheader.u.hIcon;
3506

3507
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3508
      }
3509

3510
      if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3511
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3512

3513 3514
      psInfo->strPropertiesFor = strCaption;

3515
      GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3516

3517 3518
      PROPSHEET_CreateTabControl(hwnd, psInfo);

3519 3520
      PROPSHEET_LoadWizardBitmaps(psInfo);

3521
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3522
      {
3523
        ShowWindow(hwndTabCtrl, SW_HIDE);
Filip Navara's avatar
Filip Navara committed
3524 3525
        PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
        PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3526
        SetFocus(GetDlgItem(hwnd, IDC_NEXT_BUTTON));
3527 3528
      }
      else
3529
      {
3530
        if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3531 3532 3533 3534
        {
          PROPSHEET_AdjustSize(hwnd, psInfo);
          PROPSHEET_AdjustButtons(hwnd, psInfo);
        }
3535
        SetFocus(GetDlgItem(hwnd, IDOK));
3536 3537
      }

3538
      if (IS_INTRESOURCE(psInfo->ppshheader.pszCaption) &&
3539
              psInfo->ppshheader.hInstance)
3540
      {
3541
         WCHAR szText[256];
3542

3543
         if (LoadStringW(psInfo->ppshheader.hInstance,
Frank Richter's avatar
Frank Richter committed
3544
                         (UINT_PTR)psInfo->ppshheader.pszCaption, szText, 255))
3545
            PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3546 3547 3548
      }
      else
      {
3549
         PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3550
                         psInfo->ppshheader.pszCaption);
3551
      }
3552

3553 3554

      if (psInfo->useCallback)
3555
             (*(psInfo->ppshheader.pfnCallback))(hwnd, PSCB_INITIALIZED, 0);
3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566

      idx = psInfo->active_page;
      psInfo->active_page = -1;

      PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);

      /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
       * as some programs call TCM_GETCURSEL to get the current selection
       * from which to switch to the next page */
      SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);

3567
      PROPSHEET_UnChanged(hwnd, NULL);
3568

3569 3570 3571 3572
      /* wizards set their focus during init */
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
          return FALSE;

3573 3574 3575
      return TRUE;
    }

3576
    case WM_PRINTCLIENT:
3577
    case WM_PAINT:
3578
      PROPSHEET_Paint(hwnd, (HDC)wParam);
3579 3580
      return TRUE;

3581 3582 3583 3584 3585
    case WM_DESTROY:
      PROPSHEET_CleanUp(hwnd);
      return TRUE;

    case WM_CLOSE:
Alexandre Julliard's avatar
Alexandre Julliard committed
3586
      PROPSHEET_Cancel(hwnd, 1);
3587
      return FALSE; /* let DefDlgProc post us WM_COMMAND/IDCANCEL */
3588 3589

    case WM_COMMAND:
3590 3591
      if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
      {
3592
          PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3593

3594 3595 3596
          if (!psInfo)
              return FALSE;

3597 3598 3599 3600 3601 3602 3603 3604
          /* No default handler, forward notification to active page */
          if (psInfo->activeValid && psInfo->active_page != -1)
          {
             HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
             SendMessageW(hwndPage, WM_COMMAND, wParam, lParam);
          }
      }
      return TRUE;
3605 3606 3607 3608 3609 3610 3611

    case WM_NOTIFY:
    {
      NMHDR* pnmh = (LPNMHDR) lParam;

      if (pnmh->code == TCN_SELCHANGE)
      {
3612
        int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3613
        PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3614 3615
      }

3616 3617 3618
      if(pnmh->code == TCN_SELCHANGING)
      {
        BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3619
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3620 3621 3622
        return TRUE;
      }

3623
      return FALSE;
3624
    }
3625 3626 3627 3628
  
    case WM_SYSCOLORCHANGE:
      COMCTL32_RefreshSysColors();
      return FALSE;
3629 3630 3631

    case PSM_GETCURRENTPAGEHWND:
    {
3632
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3633 3634
      HWND hwndPage = 0;

3635 3636 3637
      if (!psInfo)
        return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
3638
      if (psInfo->activeValid && psInfo->active_page != -1)
3639 3640
        hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;

3641
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657

      return TRUE;
    }

    case PSM_CHANGED:
      PROPSHEET_Changed(hwnd, (HWND)wParam);
      return TRUE;

    case PSM_UNCHANGED:
      PROPSHEET_UnChanged(hwnd, (HWND)wParam);
      return TRUE;

    case PSM_GETTABCONTROL:
    {
      HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);

3658
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3659 3660 3661 3662 3663 3664 3665 3666

      return TRUE;
    }

    case PSM_SETCURSEL:
    {
      BOOL msgResult;

3667 3668 3669 3670 3671
      msgResult = PROPSHEET_CanSetCurSel(hwnd);
      if(msgResult != FALSE)
      {
        msgResult = PROPSHEET_SetCurSel(hwnd,
                                       (int)wParam,
3672
				       1,
3673 3674
                                       (HPROPSHEETPAGE)lParam);
      }
3675

3676
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3677 3678 3679 3680 3681 3682

      return TRUE;
    }

    case PSM_CANCELTOCLOSE:
    {
3683
      WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3684 3685 3686 3687
      HWND hwndOK = GetDlgItem(hwnd, IDOK);
      HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);

      EnableWindow(hwndCancel, FALSE);
3688
      if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, ARRAY_SIZE(buf)))
3689
         SetWindowTextW(hwndOK, buf);
3690

Alexandre Julliard's avatar
Alexandre Julliard committed
3691
      return FALSE;
3692
    }
3693

3694 3695
    case PSM_RESTARTWINDOWS:
    {
3696
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3697

3698 3699 3700
      if (!psInfo)
        return FALSE;

3701 3702 3703 3704
      /* reboot system takes precedence over restart windows */
      if (psInfo->result != ID_PSREBOOTSYSTEM)
          psInfo->result = ID_PSRESTARTWINDOWS;

3705 3706 3707 3708 3709
      return TRUE;
    }

    case PSM_REBOOTSYSTEM:
    {
3710
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3711

3712 3713 3714
      if (!psInfo)
        return FALSE;

3715 3716
      psInfo->result = ID_PSREBOOTSYSTEM;

3717 3718 3719 3720 3721 3722
      return TRUE;
    }

    case PSM_SETTITLEA:
      PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
      return TRUE;
3723

3724 3725 3726
    case PSM_SETTITLEW:
      PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
      return TRUE;
3727 3728 3729

    case PSM_APPLY:
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3730
      BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
3731

3732
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3733 3734 3735 3736 3737 3738 3739 3740

      return TRUE;
    }

    case PSM_QUERYSIBLINGS:
    {
      LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);

3741
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3742 3743 3744 3745

      return TRUE;
    }

3746
    case PSM_ADDPAGE:
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757
    {
      /*
       * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
       *       a return value. This is not true. PSM_ADDPAGE returns TRUE
       *       on success or FALSE otherwise, as specified on MSDN Online.
       *       Also see the MFC code for
       *       CPropertySheet::AddPage(CPropertyPage* pPage).
       */

      BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);

3758
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3759

3760
      return TRUE;
3761
    }
3762 3763 3764 3765 3766

    case PSM_REMOVEPAGE:
      PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
      return TRUE;

3767 3768
    case PSM_ISDIALOGMESSAGE:
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3769
       BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3770
       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
Alexandre Julliard's avatar
Alexandre Julliard committed
3771
       return TRUE;
3772 3773 3774 3775 3776 3777
    }

    case PSM_PRESSBUTTON:
      PROPSHEET_PressButton(hwnd, (int)wParam);
      return TRUE;

3778
    case PSM_SETFINISHTEXTA:
3779
      PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3780 3781 3782 3783 3784 3785
      return TRUE;

    case PSM_SETWIZBUTTONS:
      PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
      return TRUE;

3786
    case PSM_SETCURSELID:
3787 3788
        PROPSHEET_SetCurSelId(hwnd, (int)lParam);
        return TRUE;
3789

3790
    case PSM_SETFINISHTEXTW:
3791
        PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3792
        return FALSE;
3793

3794 3795 3796
    case PSM_INSERTPAGE:
    {
        BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3797
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3798 3799 3800 3801
        return TRUE;
    }

    case PSM_SETHEADERTITLEW:
3802
        PROPSHEET_SetHeaderTitleW(hwnd, wParam, (LPCWSTR)lParam);
3803 3804 3805
        return TRUE;

    case PSM_SETHEADERTITLEA:
3806
        PROPSHEET_SetHeaderTitleA(hwnd, wParam, (LPCSTR)lParam);
3807 3808 3809
        return TRUE;

    case PSM_SETHEADERSUBTITLEW:
3810
        PROPSHEET_SetHeaderSubTitleW(hwnd, wParam, (LPCWSTR)lParam);
3811 3812 3813
        return TRUE;

    case PSM_SETHEADERSUBTITLEA:
3814
        PROPSHEET_SetHeaderSubTitleA(hwnd, wParam, (LPCSTR)lParam);
3815 3816 3817 3818 3819
        return TRUE;

    case PSM_HWNDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3820
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3821 3822 3823 3824 3825 3826
        return TRUE;
    }

    case PSM_INDEXTOHWND:
    {
        LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3827
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3828 3829 3830 3831 3832 3833
        return TRUE;
    }

    case PSM_PAGETOINDEX:
    {
        LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3834
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3835 3836 3837 3838 3839 3840
        return TRUE;
    }

    case PSM_INDEXTOPAGE:
    {
        LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3841
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3842 3843 3844 3845 3846 3847
        return TRUE;
    }

    case PSM_IDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3848
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3849 3850 3851 3852 3853 3854
        return TRUE;
    }

    case PSM_INDEXTOID:
    {
        LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3855
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3856 3857 3858 3859 3860 3861
        return TRUE;
    }

    case PSM_GETRESULT:
    {
        LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3862
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3863 3864 3865 3866 3867 3868
        return TRUE;
    }

    case PSM_RECALCPAGESIZES:
    {
        LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3869
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3870 3871 3872
        return TRUE;
    }

3873 3874 3875
    default:
      return FALSE;
  }
3876
}