propsheet.c 102 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 21 22
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
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 36 37 38 39 40
 *   - Wizard 97 header resizing
 *   - Enforcing of minimal wizard size
 *   - Messages:
 *     o PSM_GETRESULT
 *     o PSM_IDTOINDEX
 *     o PSM_INSERTPAGE
 *     o PSM_RECALCPAGESIZES
 *     o PSM_SETHEADERSUBTITLE
 *     o PSM_SETHEADERTITLE
41 42
 *     o WM_HELP
 *     o WM_CONTEXTMENU
Filip Navara's avatar
Filip Navara committed
43 44 45 46 47 48 49 50 51 52 53 54 55
 *   - Notifications:
 *     o PSN_GETOBJECT
 *     o PSN_QUERYINITIALFOCUS
 *     o PSN_TRANSLATEACCELERATOR
 *   - Styles:
 *     o PSH_WIZARDHASFINISH
 *     o PSH_RTLREADING
 *     o PSH_STRETCHWATERMARK
 *     o PSH_USEPAGELANG
 *     o PSH_USEPSTARTPAGE
 *   - Page styles:
 *     o PSP_USEFUSIONCONTEXT
 *     o PSP_USEREFPARENT
56 57
 */

58
#include <stdarg.h>
59
#include <string.h>
60

61 62
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
63
#include "windef.h"
64
#include "winbase.h"
65 66 67
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
68
#include "commctrl.h"
69
#include "prsht.h"
70
#include "comctl32.h"
71

72 73
#include "wine/debug.h"
#include "wine/unicode.h"
74 75 76 77

/******************************************************************************
 * Data structures
 */
78 79
#include "pshpack2.h"

80 81 82 83 84 85 86 87 88
typedef struct
{
  WORD dlgVer;
  WORD signature;
  DWORD helpID;
  DWORD exStyle;
  DWORD style;
} MyDLGTEMPLATEEX;

89 90 91 92 93 94 95
typedef struct
{
  DWORD helpid;
  DWORD exStyle;
  DWORD style;
  short x;
  short y;
96
  short cx;
97 98 99 100 101
  short cy;
  DWORD id;
} MyDLGITEMTEMPLATEEX;
#include "poppack.h"

102 103
typedef struct tagPropPageInfo
{
104
  HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
105 106 107 108 109
  HWND hwndPage;
  BOOL isDirty;
  LPCWSTR pszText;
  BOOL hasHelp;
  BOOL useCallback;
Alexandre Julliard's avatar
Alexandre Julliard committed
110
  BOOL hasIcon;
111 112 113 114
} PropPageInfo;

typedef struct tagPropSheetInfo
{
115
  HWND hwnd;
116
  PROPSHEETHEADERW ppshheader;
117
  BOOL unicode;
118
  LPWSTR strPropertiesFor;
119
  int nPages;
120 121 122 123 124 125 126
  int active_page;
  BOOL isModeless;
  BOOL hasHelp;
  BOOL hasApply;
  BOOL useCallback;
  BOOL restartWindows;
  BOOL rebootSystem;
Alexandre Julliard's avatar
Alexandre Julliard committed
127
  BOOL activeValid;
128
  PropPageInfo* proppage;
129 130
  HFONT hFont;
  HFONT hFontBold;
131 132
  int width;
  int height;
133
  HIMAGELIST hImageList;
134
  BOOL ended;
135 136 137 138 139 140 141 142 143 144 145
} PropSheetInfo;

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

/******************************************************************************
 * Defines and global variables
 */
146

147
const WCHAR PropSheetInfoStr[] =
148
    {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
149

150 151
#define PSP_INTERNAL_UNICODE 0x80000000

152
#define MAX_CAPTION_LENGTH 255
153
#define MAX_TABTEXT_LENGTH 255
Alexandre Julliard's avatar
Alexandre Julliard committed
154
#define MAX_BUTTONTEXT_LENGTH 64
155

156 157
#define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)

Filip Navara's avatar
Filip Navara committed
158 159 160 161
/* Wizard metrics specified in DLUs */
#define WIZARD_PADDING 7
#define WIZARD_HEADER_HEIGHT 36
                         	
162 163 164
/******************************************************************************
 * Prototypes
 */
165
static int PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
166
static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo);
167 168
static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
169 170 171
static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
                                       PropSheetInfo * psInfo);
static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
172
                                       PropSheetInfo * psInfo);
173
static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
174 175
                                      PropSheetInfo * psInfo,
                                      int index);
176 177
static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
                                       PropSheetInfo * psInfo);
178
static BOOL PROPSHEET_CreatePage(HWND hwndParent, int index,
179
                                const PropSheetInfo * psInfo,
180
                                LPCPROPSHEETPAGEW ppshpage);
181 182
static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
183 184 185
static BOOL PROPSHEET_Back(HWND hwndDlg);
static BOOL PROPSHEET_Next(HWND hwndDlg);
static BOOL PROPSHEET_Finish(HWND hwndDlg);
Alexandre Julliard's avatar
Alexandre Julliard committed
186 187
static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam);
static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam);
188
static void PROPSHEET_Help(HWND hwndDlg);
189 190 191
static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
192
static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText);
193
static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText);
194
static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
195
static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
196
static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
197 198
static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
                                int index,
199
                                int skipdir,
200
                                HPROPSHEETPAGE hpage);
201
static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id);
202 203 204 205 206 207 208 209
static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
                                       WPARAM wParam, LPARAM lParam);
static BOOL PROPSHEET_AddPage(HWND hwndDlg,
                              HPROPSHEETPAGE hpage);

static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
                                 int index,
                                 HPROPSHEETPAGE hpage);
210
static void PROPSHEET_CleanUp(HWND hwndDlg);
211
static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
212
static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags);
213
static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
Alexandre Julliard's avatar
Alexandre Julliard committed
214
static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg);
215
static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
216

217
INT_PTR CALLBACK
218 219
PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

220
WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
221

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
#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_WIZARDHASFINISH    0x00000010
   *  PSH_RTLREADING         0x00000800
   *  PSH_STRETCHWATERMARK   0x00040000
   *  PSH_USEPAGELANG        0x00200000
   */

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

251 252 253 254 255
/******************************************************************************
 *            PROPSHEET_GetPageRect
 *
 * Retrieve rect from tab control and map into the dialog for SetWindowPos
 */
Filip Navara's avatar
Filip Navara committed
256 257
static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg,
                                  RECT *rc, LPCPROPSHEETPAGEW ppshpage)
258
{
Filip Navara's avatar
Filip Navara committed
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    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);
277

Filip Navara's avatar
Filip Navara committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        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);
    }
293 294
}

295 296 297 298 299
/******************************************************************************
 *            PROPSHEET_FindPageByResId
 *
 * Find page index corresponding to page resource id.
 */
300
static INT PROPSHEET_FindPageByResId(PropSheetInfo * psInfo, LRESULT resId)
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
{
   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;
}

316 317 318 319 320 321 322 323 324 325 326
/******************************************************************************
 *            PROPSHEET_AtoW
 *
 * Convert ASCII to Unicode since all data is saved as Unicode.
 */
static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
{
    INT len;

    TRACE("<%s>\n", frstr);
    len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
327
    *tostr = Alloc(len * sizeof(WCHAR));
328 329 330
    MultiByteToWideChar(CP_ACP, 0, frstr, -1, (LPWSTR)*tostr, len);
}

331
/******************************************************************************
332
 *            PROPSHEET_CollectSheetInfoA
333 334 335
 *
 * Collect relevant data.
 */
336
static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
337 338
                                       PropSheetInfo * psInfo)
{
339
  DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
340 341 342 343
  DWORD dwFlags = lppsh->dwFlags;

  psInfo->hasHelp = dwFlags & PSH_HASHELP;
  psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
344
  psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
345
  psInfo->isModeless = dwFlags & PSH_MODELESS;
Alexandre Julliard's avatar
Alexandre Julliard committed
346

347
  memcpy(&psInfo->ppshheader,lppsh,dwSize);
348
  TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
349 350 351 352
	lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
	debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);

  PROPSHEET_UnImplementedFlags(lppsh->dwFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
353

354 355 356
  if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
     psInfo->ppshheader.pszCaption = NULL;
  else
357
  {
358 359
     if (HIWORD(lppsh->pszCaption))
     {
360
        int len = MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, NULL, 0);
361
        psInfo->ppshheader.pszCaption = Alloc( len*sizeof (WCHAR) );
362
        MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, (LPWSTR) psInfo->ppshheader.pszCaption, len);
363
     }
364
  }
365
  psInfo->nPages = lppsh->nPages;
366 367 368

  if (dwFlags & PSH_USEPSTARTPAGE)
  {
369
    TRACE("PSH_USEPSTARTPAGE is on\n");
370 371 372 373 374
    psInfo->active_page = 0;
  }
  else
    psInfo->active_page = lppsh->u2.nStartPage;

Alexandre Julliard's avatar
Alexandre Julliard committed
375 376 377
  if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
     psInfo->active_page = 0;

378 379
  psInfo->restartWindows = FALSE;
  psInfo->rebootSystem = FALSE;
380
  psInfo->hImageList = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
381
  psInfo->activeValid = FALSE;
382 383 384 385 386

  return TRUE;
}

/******************************************************************************
387 388 389 390 391 392 393 394 395 396 397 398
 *            PROPSHEET_CollectSheetInfoW
 *
 * Collect relevant data.
 */
static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
                                       PropSheetInfo * psInfo)
{
  DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
  DWORD dwFlags = lppsh->dwFlags;

  psInfo->hasHelp = dwFlags & PSH_HASHELP;
  psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
399
  psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
400 401 402
  psInfo->isModeless = dwFlags & PSH_MODELESS;

  memcpy(&psInfo->ppshheader,lppsh,dwSize);
403
  TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
404 405 406 407
      lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);

  PROPSHEET_UnImplementedFlags(lppsh->dwFlags);

408 409 410
  if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
     psInfo->ppshheader.pszCaption = NULL;
  else
411
  {
412
     if (HIWORD(lppsh->pszCaption))
413 414
     {
        int len = strlenW(lppsh->pszCaption);
415
        psInfo->ppshheader.pszCaption = Alloc( (len+1)*sizeof(WCHAR) );
416 417
        strcpyW( (WCHAR *)psInfo->ppshheader.pszCaption, lppsh->pszCaption );
     }
418 419 420 421 422
  }
  psInfo->nPages = lppsh->nPages;

  if (dwFlags & PSH_USEPSTARTPAGE)
  {
423
    TRACE("PSH_USEPSTARTPAGE is on\n");
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    psInfo->active_page = 0;
  }
  else
    psInfo->active_page = lppsh->u2.nStartPage;

  if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
     psInfo->active_page = 0;

  psInfo->restartWindows = FALSE;
  psInfo->rebootSystem = FALSE;
  psInfo->hImageList = 0;
  psInfo->activeValid = FALSE;

  return TRUE;
}

/******************************************************************************
441
 *            PROPSHEET_CollectPageInfo
442 443 444 445
 *
 * Collect property sheet data.
 * With code taken from DIALOG_ParseTemplate32.
 */
446
BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
                               PropSheetInfo * psInfo,
                               int index)
{
  DLGTEMPLATE* pTemplate;
  const WORD*  p;
  DWORD dwFlags;
  int width, height;

  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)
    pTemplate = (DLGTEMPLATE*)lppsp->u.pResource;
477
  else if(dwFlags & PSP_INTERNAL_UNICODE )
478 479 480
  {
    HRSRC hResource = FindResourceW(lppsp->hInstance,
                                    lppsp->u.pszTemplate,
481
                                    (LPWSTR)RT_DIALOG);
482 483 484 485
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
    pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
  }
486 487 488 489
  else
  {
    HRSRC hResource = FindResourceA(lppsp->hInstance,
                                    (LPSTR)lppsp->u.pszTemplate,
490
                                    (LPSTR)RT_DIALOG);
491 492 493 494
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
    pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
  }
495 496 497 498 499 500 501 502 503 504 505

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

  p = (const WORD *)pTemplate;

  if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
  {
506
    /* DLGTEMPLATEEX (not defined in any std. header file) */
507 508 509 510 511 512 513 514 515

    p++;       /* dlgVer    */
    p++;       /* signature */
    p += 2;    /* help ID   */
    p += 2;    /* ext style */
    p += 2;    /* style     */
  }
  else
  {
516
    /* DLGTEMPLATE */
517 518 519 520 521 522 523 524 525 526 527

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

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

Filip Navara's avatar
Filip Navara committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
  /* 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;
  }

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
  /* remember the largest width and height */
  if (width > psInfo->width)
    psInfo->width = width;

  if (height > psInfo->height)
    psInfo->height = height;

  /* menu */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
      p += 2;
      break;
    default:
      p += lstrlenW( (LPCWSTR)p ) + 1;
      break;
564
  }
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588

  /* class */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
      p += 2;
      break;
    default:
      p += lstrlenW( (LPCWSTR)p ) + 1;
      break;
  }

  /* Extract the caption */
  psInfo->proppage[index].pszText = (LPCWSTR)p;
  TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
  p += lstrlenW((LPCWSTR)p) + 1;

  if (dwFlags & PSP_USETITLE)
  {
    WCHAR szTitle[256];
    const WCHAR *pTitle;
589
    static const WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
590 591 592 593
    int len;

    if ( !HIWORD( lppsp->pszTitle ) )
    {
594
      if (!LoadStringW( lppsp->hInstance, (UINT)lppsp->pszTitle,szTitle,sizeof(szTitle) ))
595 596 597 598 599 600 601 602 603 604
      {
        pTitle = pszNull;
	FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
      }
      else
        pTitle = szTitle;
    }
    else
      pTitle = lppsp->pszTitle;

605
    len = strlenW(pTitle);
606
    psInfo->proppage[index].pszText = Alloc( (len+1)*sizeof (WCHAR) );
607 608 609 610 611 612
    strcpyW( (LPWSTR)psInfo->proppage[index].pszText,pTitle);
  }

  /*
   * Build the image list for icons
   */
613
  if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
614 615 616 617 618 619
  {
    HICON hIcon;
    int icon_cx = GetSystemMetrics(SM_CXSMICON);
    int icon_cy = GetSystemMetrics(SM_CYSMICON);

    if (dwFlags & PSP_USEICONID)
620
      hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
                         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
637

638 639 640 641 642
/******************************************************************************
 *            PROPSHEET_CreateDialog
 *
 * Creates the actual property sheet.
 */
643
int PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
644 645 646
{
  LRESULT ret;
  LPCVOID template;
647
  LPVOID temp = 0;
648
  HRSRC hRes;
649
  DWORD resSize;
650 651
  WORD resID = IDD_PROPSHEET;

652
  TRACE("\n");
653
  if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
654
    resID = IDD_WIZARD;
655

656 657 658
  if( psInfo->unicode )
  {
    if(!(hRes = FindResourceW(COMCTL32_hModule,
659
                            MAKEINTRESOURCEW(resID),
660
                            (LPWSTR)RT_DIALOG)))
661 662 663 664 665 666
      return -1;
  }
  else
  {
    if(!(hRes = FindResourceA(COMCTL32_hModule,
                            MAKEINTRESOURCEA(resID),
667
                            (LPSTR)RT_DIALOG)))
668 669
      return -1;
  }
670 671

  if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
672
    return -1;
673

674 675 676 677 678
  /*
   * Make a copy of the dialog template.
   */
  resSize = SizeofResource(COMCTL32_hModule, hRes);

679
  temp = Alloc(resSize);
680 681

  if (!temp)
682
    return -1;
683 684 685

  memcpy(temp, template, resSize);

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
  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;
  }

702
  if (psInfo->useCallback)
703
    (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
704

705 706 707 708
  /* 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). */
709 710
  if( psInfo->unicode )
  {
711 712 713 714 715 716
    ret = (int)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
                                          (LPDLGTEMPLATEW) temp,
                                          psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc,
                                          (LPARAM)psInfo);
    if ( !ret ) ret = -1;
717 718 719
  }
  else
  {
720 721 722 723 724 725
    ret = (int)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
                                          (LPDLGTEMPLATEA) temp,
                                          psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc,
                                          (LPARAM)psInfo);
    if ( !ret ) ret = -1;
726
  }
727

728
  Free(temp);
729

730 731 732 733
  return ret;
}

/******************************************************************************
734
 *            PROPSHEET_SizeMismatch
735
 *
736 737
 *     Verify that the tab control and the "largest" property sheet page dlg. template
 *     match in size.
738
 */
739
static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo)
740 741 742 743 744 745 746 747
{
  HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  RECT rcOrigTab, rcPage;

  /*
   * Original tab size.
   */
  GetClientRect(hwndTabCtrl, &rcOrigTab);
748
  TRACE("orig tab %ld %ld %ld %ld\n", rcOrigTab.left, rcOrigTab.top,
749 750 751 752 753
        rcOrigTab.right, rcOrigTab.bottom);

  /*
   * Biggest page size.
   */
Filip Navara's avatar
Filip Navara committed
754 755
  rcPage.left   = 0;
  rcPage.top    = 0;
756 757 758 759
  rcPage.right  = psInfo->width;
  rcPage.bottom = psInfo->height;

  MapDialogRect(hwndDlg, &rcPage);
760
  TRACE("biggest page %ld %ld %ld %ld\n", rcPage.left, rcPage.top,
761 762
        rcPage.right, rcPage.bottom);

763
  if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
764
    return TRUE;
765
  if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
766 767 768 769 770 771 772 773 774 775 776 777 778 779
    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);
780
  RECT rc,tabRect;
781 782
  int tabOffsetX, tabOffsetY, buttonHeight;
  PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
783
  RECT units;
784 785 786 787 788 789 790 791

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

  /*
   * Biggest page size.
   */
Filip Navara's avatar
Filip Navara committed
792 793
  rc.left   = 0;
  rc.top    = 0;
794 795 796 797 798
  rc.right  = psInfo->width;
  rc.bottom = psInfo->height;

  MapDialogRect(hwndDlg, &rc);

799 800 801 802 803
  /* retrieve the dialog units */
  units.left = units.right = 4;
  units.top = units.bottom = 8;
  MapDialogRect(hwndDlg, &units);

804 805 806
  /*
   * Resize the tab control.
   */
807 808
  GetClientRect(hwndTabCtrl,&tabRect);

809
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
810 811 812 813

  if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
  {
      rc.bottom = rc.top + tabRect.bottom - tabRect.top;
814
      psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
815
  }
816

817 818 819
  if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
  {
      rc.right = rc.left + tabRect.right - tabRect.left;
820
      psInfo->width  = MulDiv((rc.right - rc.left),4,units.left);
821 822
  }

823
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
824 825 826 827 828 829

  tabOffsetX = -(rc.left);
  tabOffsetY = -(rc.top);

  rc.right -= rc.left;
  rc.bottom -= rc.top;
830
  TRACE("setting tab %08lx, rc (0,0)-(%ld,%ld)\n",
831
        (DWORD)hwndTabCtrl, rc.right, rc.bottom);
832 833 834 835 836
  SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

  GetClientRect(hwndTabCtrl, &rc);

837
  TRACE("tab client rc %ld %ld %ld %ld\n",
838 839 840 841 842 843 844 845
        rc.left, rc.top, rc.right, rc.bottom);

  rc.right += ((padding.x * 2) + tabOffsetX);
  rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);

  /*
   * Resize the property sheet.
   */
846
  TRACE("setting dialog %08lx, rc (0,0)-(%ld,%ld)\n",
847
        (DWORD)hwndDlg, rc.right, rc.bottom);
848 849 850 851 852
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  return TRUE;
}

853 854 855 856 857 858 859 860
/******************************************************************************
 *            PROPSHEET_AdjustSizeWizard
 *
 * Resizes the property sheet to fit the largest page.
 */
static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo)
{
  HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
Filip Navara's avatar
Filip Navara committed
861
  RECT rc, lineRect, dialogRect;
862

Filip Navara's avatar
Filip Navara committed
863 864 865
  /* Biggest page size */
  rc.left   = 0;
  rc.top    = 0;
866 867 868 869
  rc.right  = psInfo->width;
  rc.bottom = psInfo->height;
  MapDialogRect(hwndDlg, &rc);

870
  TRACE("Biggest page %ld %ld %ld %ld\n", rc.left, rc.top, rc.right, rc.bottom);
871

Filip Navara's avatar
Filip Navara committed
872 873 874 875 876
  /* 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;
877

Filip Navara's avatar
Filip Navara committed
878 879 880 881
  /* Convert the client coordinates to window coordinates */
  AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);

  /* Resize the property sheet */
882
  TRACE("setting dialog %08lx, rc (0,0)-(%ld,%ld)\n",
883
        (DWORD)hwndDlg, rc.right, rc.bottom);
Filip Navara's avatar
Filip Navara committed
884
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
885
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
Filip Navara's avatar
Filip Navara committed
886

887 888 889
  return TRUE;
}

890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
/******************************************************************************
 *            PROPSHEET_AdjustButtons
 *
 * Adjusts the buttons' positions.
 */
static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
{
  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.
919
   */
920 921
  GetClientRect(hwndParent, &rcSheet);

922
  /*
923 924 925 926 927
   * All buttons will be at this y coordinate.
   */
  y = rcSheet.bottom - (padding.y + buttonHeight);

  /*
928
   * Position OK button and make it default.
929 930 931 932 933 934 935 936
   */
  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);

937
  SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
938 939


940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
  /*
   * Position Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

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

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

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

  if (psInfo->hasApply)
  {
    if (psInfo->hasHelp)
      x = rcSheet.right - ((padding.x + buttonWidth) * 2);
    else
      x = rcSheet.right - (padding.x + buttonWidth);
961

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
    SetWindowPos(hwndButton, 0, x, y, 0, 0,
                 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

    EnableWindow(hwndButton, FALSE);
  }
  else
    ShowWindow(hwndButton, SW_HIDE);

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

  if (psInfo->hasHelp)
  {
    x = rcSheet.right - (padding.x + buttonWidth);
978

979 980 981 982 983 984 985 986 987
    SetWindowPos(hwndButton, 0, x, y, 0, 0,
                 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  }
  else
    ShowWindow(hwndButton, SW_HIDE);

  return TRUE;
}

988 989 990 991 992 993 994 995 996 997
/******************************************************************************
 *            PROPSHEET_AdjustButtonsWizard
 *
 * Adjusts the buttons' positions.
 */
static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
                                          PropSheetInfo* psInfo)
{
  HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
  HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
998
  HWND hwndLineHeader = GetDlgItem(hwndParent, IDC_SUNKEN_LINEHEADER);
999 1000 1001 1002
  RECT rcSheet;
  int x, y;
  int num_buttons = 3;
  int buttonWidth, buttonHeight, lineHeight, lineWidth;
1003
  PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079

  if (psInfo->hasHelp)
    num_buttons++;

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

  /*
   * Position the Next and the Finish buttons.
   */
  hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);

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

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

  hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);

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

  ShowWindow(hwndButton, SW_HIDE);

  /*
   * Position the Back button.
   */
  hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);

  x -= buttonWidth;

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

  /*
   * Position the Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

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

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

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

  if (psInfo->hasHelp)
  {
    x = rcSheet.right - (padding.x + buttonWidth);

    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
1080 1081
  if (psInfo->ppshheader.dwFlags &
      (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)) 
1082 1083
      padding.x = 0;

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
  /*
   * 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);

1094 1095 1096 1097 1098 1099 1100 1101 1102
  /*
   * 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);

1103 1104 1105
  return TRUE;
}

1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
/******************************************************************************
 *            PROPSHEET_GetPaddingInfo
 *
 * Returns the layout information.
 */
static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
{
  HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  RECT rcTab;
  POINT tl;
  PADDING_INFO padding;

  GetWindowRect(hwndTab, &rcTab);

  tl.x = rcTab.left;
  tl.y = rcTab.top;

  ScreenToClient(hwndDlg, &tl);

  padding.x = tl.x;
  padding.y = tl.y;

  return padding;
}

1131 1132 1133 1134 1135
/******************************************************************************
 *            PROPSHEET_GetPaddingInfoWizard
 *
 * Returns the layout information.
 * Vertical spacing is the distance between the line and the buttons.
1136 1137 1138 1139 1140 1141
 * 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.
1142
 */
1143 1144
static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
 psInfo)
1145 1146 1147 1148
{
  PADDING_INFO padding;
  RECT rc;
  HWND hwndControl;
1149 1150
  INT idButton;
  POINT ptButton, ptLine;
1151

1152
  TRACE("\n");
1153 1154 1155 1156 1157 1158
  if (psInfo->hasHelp)
  {
	idButton = IDHELP;
  }
  else
  {
1159
    if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1160 1161 1162 1163 1164 1165 1166 1167 1168
    {
	idButton = IDC_NEXT_BUTTON;
    }
    else
    {
	/* hopefully this is ok */
	idButton = IDCANCEL;
    }
  }
1169

1170
  hwndControl = GetDlgItem(hwndDlg, idButton);
1171 1172
  GetWindowRect(hwndControl, &rc);

1173 1174
  ptButton.x = rc.left;
  ptButton.y = rc.top;
1175

1176
  ScreenToClient(hwndDlg, &ptButton);
1177 1178 1179 1180 1181

  /* Line */
  hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
  GetWindowRect(hwndControl, &rc);

1182
  ptLine.x = rc.left;
1183 1184 1185 1186
  ptLine.y = rc.bottom;

  ScreenToClient(hwndDlg, &ptLine);

1187 1188 1189 1190
  padding.y = ptButton.y - ptLine.y;

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

1192 1193
  /* this is most probably not correct, but the best we have now */
  padding.x = padding.y;
1194 1195 1196
  return padding;
}

1197 1198 1199 1200 1201 1202 1203 1204 1205
/******************************************************************************
 *            PROPSHEET_CreateTabControl
 *
 * Insert the tabs in the tab control.
 */
static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
                                       PropSheetInfo * psInfo)
{
  HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1206
  TCITEMW item;
1207
  int i, nTabs;
Alexandre Julliard's avatar
Alexandre Julliard committed
1208
  int iImage = 0;
1209

1210
  TRACE("\n");
1211
  item.mask = TCIF_TEXT;
1212
  item.cchTextMax = MAX_TABTEXT_LENGTH;
1213

1214
  nTabs = psInfo->nPages;
1215

1216 1217 1218 1219 1220
  /*
   * Set the image list for icons.
   */
  if (psInfo->hImageList)
  {
1221
    SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1222 1223
  }

1224
  SendMessageW(GetDlgItem(hwndTabCtrl, IDC_TABCONTROL), WM_SETREDRAW, 0, 0);
1225 1226
  for (i = 0; i < nTabs; i++)
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
1227 1228 1229 1230 1231 1232 1233 1234 1235
    if ( psInfo->proppage[i].hasIcon )
    {
      item.mask |= TCIF_IMAGE;
      item.iImage = iImage++;
    }
    else
    {
      item.mask &= ~TCIF_IMAGE;
    }
1236

1237 1238
    item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
    SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, (WPARAM)i, (LPARAM)&item);
1239
  }
1240
  SendMessageW(GetDlgItem(hwndTabCtrl, IDC_TABCONTROL), WM_SETREDRAW, 1, 0);
1241 1242 1243

  return TRUE;
}
1244 1245 1246 1247 1248 1249 1250

/******************************************************************************
 *            PROPSHEET_WizardSubclassProc
 *
 * Subclassing window procedure for wizard extrior pages to prevent drawing
 * background and so drawing above the watermark.
 */
1251
static LRESULT CALLBACK
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
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);
}

1267 1268 1269 1270
/*
 * Get the size of an in-memory Template
 *
 *( Based on the code of PROPSHEET_CollectPageInfo)
1271
 * See also dialog.c/DIALOG_ParseTemplate32().
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
 */

static UINT GetTemplateSize(DLGTEMPLATE* pTemplate)

{
  const WORD*  p = (const WORD *)pTemplate;
  BOOL  istemplateex = (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
  WORD nrofitems;

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

1285
    TRACE("is DLGTEMPLATEEX\n");
1286 1287 1288 1289 1290 1291 1292 1293
    p++;       /* dlgVer    */
    p++;       /* signature */
    p += 2;    /* help ID   */
    p += 2;    /* ext style */
    p += 2;    /* style     */
  }
  else
  {
1294
    /* DLGTEMPLATE */
1295

1296
    TRACE("is DLGTEMPLATE\n");
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
    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:
      TRACE("menu %s\n",debugstr_w((LPCWSTR)p));
      p += lstrlenW( (LPCWSTR)p ) + 1;
      break;
  }

  /* class */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
1329
      p += 2; /* 0xffff plus predefined window class ordinal value */
1330 1331 1332 1333 1334 1335 1336
      break;
    default:
      TRACE("class %s\n",debugstr_w((LPCWSTR)p));
      p += lstrlenW( (LPCWSTR)p ) + 1;
      break;
  }

1337
  /* title */
1338 1339 1340
  TRACE("title %s\n",debugstr_w((LPCWSTR)p));
  p += lstrlenW((LPCWSTR)p) + 1;

1341
  /* font, if DS_SETFONT set */
1342 1343 1344 1345 1346
  if ((DS_SETFONT & ((istemplateex)?  ((MyDLGTEMPLATEEX*)pTemplate)->style :
		     pTemplate->style)))
    {
      p+=(istemplateex)?3:1;
      TRACE("font %s\n",debugstr_w((LPCWSTR)p));
1347
      p += lstrlenW( (LPCWSTR)p ) + 1; /* the font name */
1348 1349
    }

1350 1351
  /* now process the DLGITEMTEMPLATE(EX) structs (plus custom data)
   * that are following the DLGTEMPLATE(EX) data */
1352 1353 1354 1355 1356
  TRACE("%d items\n",nrofitems);
  while (nrofitems > 0)
    {
      p = (WORD*)(((DWORD)p + 3) & ~3); /* DWORD align */
      
1357
      /* skip header */
1358 1359
      p += (istemplateex ? sizeof(MyDLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE))/sizeof(WORD);
      
1360
      /* check class */
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
      switch ((WORD)*p)
	{
	case 0x0000:
	  p++;
	  break;
	case 0xffff:
	  TRACE("class ordinal 0x%08lx\n",*(DWORD*)p);
	  p += 2;
	  break;
	default:
	  TRACE("class %s\n",debugstr_w((LPCWSTR)p));
	  p += lstrlenW( (LPCWSTR)p ) + 1;
	  break;
	}
1375 1376

      /* check title text */
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
      switch ((WORD)*p)
	{
	case 0x0000:
	  p++;
	  break;
	case 0xffff:
	  TRACE("text ordinal 0x%08lx\n",*(DWORD*)p);
	  p += 2;
	  break;
	default:
	  TRACE("text %s\n",debugstr_w((LPCWSTR)p));
	  p += lstrlenW( (LPCWSTR)p ) + 1;
	  break;
	}
1391
      p += *p / sizeof(WORD) + 1;    /* Skip extra data */
1392 1393 1394 1395 1396 1397 1398
      --nrofitems;
    }
  
  TRACE("%p %p size 0x%08x\n",p, (WORD*)pTemplate,sizeof(WORD)*(p - (WORD*)pTemplate));
  return (p - (WORD*)pTemplate)*sizeof(WORD);
  
}
1399 1400 1401 1402

/******************************************************************************
 *            PROPSHEET_CreatePage
 *
1403
 * Creates a page.
1404
 */
1405
static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1406
                                int index,
1407
                                const PropSheetInfo * psInfo,
1408
                                LPCPROPSHEETPAGEW ppshpage)
1409 1410 1411
{
  DLGTEMPLATE* pTemplate;
  HWND hwndPage;
1412 1413
  DWORD resSize;
  LPVOID temp = NULL;
1414

1415
  TRACE("index %d\n", index);
1416

1417 1418 1419 1420 1421
  if (ppshpage == NULL)
  {
    return FALSE;
  }

1422
  if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1423 1424 1425 1426
    {
      pTemplate = (DLGTEMPLATE*)ppshpage->u.pResource;
      resSize = GetTemplateSize(pTemplate);
    }
1427
  else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1428
  {
1429 1430 1431
    HRSRC hResource;
    HANDLE hTemplate;

1432
    hResource = FindResourceW(ppshpage->hInstance,
1433
                                    ppshpage->u.pszTemplate,
1434
                                    (LPWSTR)RT_DIALOG);
1435 1436 1437 1438 1439 1440 1441 1442 1443
    if(!hResource)
	return FALSE;

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

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

1444
    pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
1445 1446 1447
    /*
     * Make a copy of the dialog template to make it writable
     */
1448
  }
1449 1450 1451 1452 1453 1454 1455
  else
  {
    HRSRC hResource;
    HANDLE hTemplate;

    hResource = FindResourceA(ppshpage->hInstance,
                                    (LPSTR)ppshpage->u.pszTemplate,
1456
                                    (LPSTR)RT_DIALOG);
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470
    if(!hResource)
	return FALSE;

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

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

    pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
    /*
     * Make a copy of the dialog template to make it writable
     */
  }
1471
  temp = Alloc(resSize);
1472 1473 1474
  if (!temp)
    return FALSE;
  
1475
  TRACE("copying pTemplate %p into temp %p (%ld)\n", pTemplate, temp, resSize);
1476 1477
  memcpy(temp, pTemplate, resSize);
  pTemplate = temp;
1478 1479 1480

  if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
  {
Filip Navara's avatar
Filip Navara committed
1481
    ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1482 1483 1484 1485
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
1486
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1487
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE;
1488
    ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_THICKFRAME;
1489 1490

    ((MyDLGTEMPLATEEX*)pTemplate)->exStyle |= WS_EX_CONTROLPARENT;
1491 1492 1493
  }
  else
  {
Filip Navara's avatar
Filip Navara committed
1494
    pTemplate->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1495 1496 1497 1498
    pTemplate->style &= ~DS_MODALFRAME;
    pTemplate->style &= ~WS_CAPTION;
    pTemplate->style &= ~WS_SYSMENU;
    pTemplate->style &= ~WS_POPUP;
1499
    pTemplate->style &= ~WS_DISABLED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1500
    pTemplate->style &= ~WS_VISIBLE;
1501
    pTemplate->style &= ~WS_THICKFRAME;
1502 1503

    pTemplate->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1504 1505 1506
  }

  if (psInfo->proppage[index].useCallback)
1507
    (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1508
                               (LPPROPSHEETPAGEW)ppshpage);
1509

1510 1511 1512 1513 1514 1515 1516 1517
  if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
     hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
					pTemplate,
					hwndParent,
					ppshpage->pfnDlgProc,
					(LPARAM)ppshpage);
  else
     hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1518 1519 1520
					pTemplate,
					hwndParent,
					ppshpage->pfnDlgProc,
Marcus Meissner's avatar
Marcus Meissner committed
1521
					(LPARAM)ppshpage);
1522 1523
  /* Free a no more needed copy */
  if(temp)
1524
      Free(temp);
1525

1526
  psInfo->proppage[index].hwndPage = hwndPage;
1527

1528 1529 1530 1531 1532 1533 1534 1535 1536
  /* 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);
  }

1537 1538 1539
  return TRUE;
}

1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
/******************************************************************************
 *            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))
    {
      ((PropSheetInfo *)psInfo)->ppshheader.u4.hbmWatermark = 
        CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
    }

    /* Same behavior as for watermarks */
    if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
        !(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER))
    {
      ((PropSheetInfo *)psInfo)->ppshheader.u5.hbmHeader = 
        CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT)psInfo->ppshheader.u5.pszbmHeader, 0, NULL, 0);
    }
  }
}


1570 1571 1572 1573 1574 1575 1576
/******************************************************************************
 *            PROPSHEET_ShowPage
 *
 * Displays or creates the specified page.
 */
static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
{
1577
  HWND hwndTabCtrl;
1578 1579
  HWND hwndLineHeader;
  LPCPROPSHEETPAGEW ppshpage;
1580 1581

  TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1582
  if (index == psInfo->active_page)
1583
  {
1584
      if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
Alexandre Julliard's avatar
Alexandre Julliard committed
1585
          SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1586 1587
      return TRUE;
  }
1588

1589
  ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1590
  if (psInfo->proppage[index].hwndPage == 0)
1591
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
1592
     PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1593
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1594

1595 1596
  if ((psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) &&
      (ppshpage->dwFlags & PSP_USETITLE))
1597 1598 1599 1600
  {
     PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
                         psInfo->proppage[index].pszText);
  }
1601

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

1605
  ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1606

1607 1608 1609
  /* 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);
1610
  SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1611

1612
  psInfo->active_page = index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1613
  psInfo->activeValid = TRUE;
1614

1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625
  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);
  }

1626 1627 1628
  InvalidateRgn(hwndDlg, NULL, TRUE);
  UpdateWindow(hwndDlg);

1629 1630 1631
  return TRUE;
}

1632 1633 1634 1635 1636
/******************************************************************************
 *            PROPSHEET_Back
 */
static BOOL PROPSHEET_Back(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1637
  PSHNOTIFY psn;
1638
  HWND hwndPage;
1639
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1640
                                                    PropSheetInfoStr);
1641
  LRESULT result;
1642
  int idx;
1643

1644
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1645
  if (psInfo->active_page < 0)
1646 1647
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1648 1649 1650 1651
  psn.hdr.code     = PSN_WIZBACK;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1652

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

1655
  result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1656
  if (result == -1)
1657
    return FALSE;
1658 1659 1660 1661
  else if (result == 0)
     idx = psInfo->active_page - 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, result);
1662

1663
  if (idx >= 0 && idx < psInfo->nPages)
1664
  {
1665 1666
     if (PROPSHEET_CanSetCurSel(hwndDlg))
        PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1667
  }
1668 1669 1670 1671 1672 1673 1674 1675
  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Next
 */
static BOOL PROPSHEET_Next(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1676
  PSHNOTIFY psn;
1677 1678
  HWND hwndPage;
  LRESULT msgResult = 0;
1679
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1680
                                                    PropSheetInfoStr);
1681
  int idx;
1682

1683
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1684
  if (psInfo->active_page < 0)
1685 1686
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1687 1688 1689 1690
  psn.hdr.code     = PSN_WIZNEXT;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1691

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

1694
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1695 1696
  if (msgResult == -1)
    return FALSE;
1697 1698 1699 1700
  else if (msgResult == 0)
     idx = psInfo->active_page + 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1701

1702
  if (idx < psInfo->nPages )
1703
  {
1704 1705
     if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
        PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1706
  }
1707 1708 1709 1710 1711 1712 1713 1714 1715

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Finish
 */
static BOOL PROPSHEET_Finish(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1716
  PSHNOTIFY psn;
1717 1718
  HWND hwndPage;
  LRESULT msgResult = 0;
1719
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1720 1721
                                                    PropSheetInfoStr);

1722
  TRACE("active_page %d\n", psInfo->active_page);
1723 1724 1725
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1726 1727 1728 1729
  psn.hdr.code     = PSN_WIZFINISH;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1730

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

1733
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1734 1735 1736 1737 1738 1739 1740

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

  if (msgResult != 0)
    return FALSE;

  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1741
    psInfo->activeValid = FALSE;
1742
  else
1743
    psInfo->ended = TRUE;
1744 1745 1746 1747

  return TRUE;
}

1748 1749 1750
/******************************************************************************
 *            PROPSHEET_Apply
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1751
static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1752 1753 1754
{
  int i;
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1755
  PSHNOTIFY psn;
1756
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1757 1758
                                                    PropSheetInfoStr);

1759
  TRACE("active_page %d\n", psInfo->active_page);
1760 1761 1762
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1763 1764 1765
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1766

1767 1768 1769 1770

  /*
   * Send PSN_KILLACTIVE to the current page.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1771
  psn.hdr.code = PSN_KILLACTIVE;
1772 1773 1774

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

1775
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1776 1777 1778 1779 1780
    return FALSE;

  /*
   * Send PSN_APPLY to all pages.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1781 1782
  psn.hdr.code = PSN_APPLY;
  psn.lParam   = lParam;
1783

1784
  for (i = 0; i < psInfo->nPages; i++)
1785 1786
  {
    hwndPage = psInfo->proppage[i].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1787 1788
    if (hwndPage)
    {
1789
       switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1790 1791 1792 1793 1794 1795 1796
       {
       case PSNRET_INVALID:
           PROPSHEET_ShowPage(hwndDlg, i, psInfo);
           /* fall through */
       case PSNRET_INVALID_NOCHANGEPAGE:
           return FALSE;
       }
Alexandre Julliard's avatar
Alexandre Julliard committed
1797 1798
    }
  }
1799

Alexandre Julliard's avatar
Alexandre Julliard committed
1800 1801 1802 1803 1804 1805 1806 1807 1808
  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;
1809
     SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1810 1811 1812 1813 1814 1815 1816 1817
  }

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Cancel
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1818
static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1819
{
1820
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1821
                                                    PropSheetInfoStr);
1822
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1823 1824
  PSHNOTIFY psn;
  int i;
1825

1826
  TRACE("active_page %d\n", psInfo->active_page);
1827 1828 1829 1830
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1831 1832 1833 1834
  psn.hdr.code     = PSN_QUERYCANCEL;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1835

1836
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1837 1838
    return;

Alexandre Julliard's avatar
Alexandre Julliard committed
1839 1840
  psn.hdr.code = PSN_RESET;
  psn.lParam   = lParam;
1841

Alexandre Julliard's avatar
Alexandre Julliard committed
1842 1843 1844
  for (i = 0; i < psInfo->nPages; i++)
  {
    hwndPage = psInfo->proppage[i].hwndPage;
1845

Alexandre Julliard's avatar
Alexandre Julliard committed
1846
    if (hwndPage)
1847
       SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
Alexandre Julliard's avatar
Alexandre Julliard committed
1848
  }
1849 1850

  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1851 1852 1853 1854
  {
     /* makes PSM_GETCURRENTPAGEHWND return NULL */
     psInfo->activeValid = FALSE;
  }
1855
  else
1856
    psInfo->ended = TRUE;
1857 1858
}

1859 1860 1861 1862 1863
/******************************************************************************
 *            PROPSHEET_Help
 */
static void PROPSHEET_Help(HWND hwndDlg)
{
1864
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1865
                                                    PropSheetInfoStr);
1866
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1867
  PSHNOTIFY psn;
1868

1869
  TRACE("active_page %d\n", psInfo->active_page);
1870 1871 1872 1873
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1874 1875 1876 1877
  psn.hdr.code     = PSN_HELP;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1878

1879
  SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1880 1881
}

1882 1883 1884 1885 1886 1887
/******************************************************************************
 *            PROPSHEET_Changed
 */
static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
{
  int i;
1888
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1889 1890
                                                    PropSheetInfoStr);

1891
  TRACE("\n");
1892
  if (!psInfo) return;
1893 1894 1895
  /*
   * Set the dirty flag of this page.
   */
1896
  for (i = 0; i < psInfo->nPages; i++)
1897 1898 1899 1900
  {
    if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
      psInfo->proppage[i].isDirty = TRUE;
  }
1901

1902 1903 1904 1905 1906 1907
  /*
   * Enable the Apply button.
   */
  if (psInfo->hasApply)
  {
    HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1908

1909 1910 1911
    EnableWindow(hwndApplyBtn, TRUE);
  }
}
1912

1913 1914 1915 1916 1917 1918 1919 1920
/******************************************************************************
 *            PROPSHEET_UnChanged
 */
static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
{
  int i;
  BOOL noPageDirty = TRUE;
  HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1921
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1922
                                                    PropSheetInfoStr);
1923

1924
  TRACE("\n");
1925
  if ( !psInfo ) return;
1926
  for (i = 0; i < psInfo->nPages; i++)
1927 1928 1929 1930
  {
    /* set the specified page as clean */
    if (psInfo->proppage[i].hwndPage == hwndCleanPage)
      psInfo->proppage[i].isDirty = FALSE;
1931

1932 1933 1934 1935
    /* look to see if there's any dirty pages */
    if (psInfo->proppage[i].isDirty)
      noPageDirty = FALSE;
  }
1936

1937 1938 1939 1940 1941 1942
  /*
   * Disable Apply button.
   */
  if (noPageDirty)
    EnableWindow(hwndApplyBtn, FALSE);
}
1943

1944 1945 1946 1947 1948
/******************************************************************************
 *            PROPSHEET_PressButton
 */
static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
{
1949
  TRACE("buttonID %d\n", buttonID);
1950 1951 1952
  switch (buttonID)
  {
    case PSBTN_APPLYNOW:
1953
      PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1954 1955
      break;
    case PSBTN_BACK:
1956
      PROPSHEET_Back(hwndDlg);
1957 1958
      break;
    case PSBTN_CANCEL:
1959
      PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1960 1961
      break;
    case PSBTN_FINISH:
1962
      PROPSHEET_Finish(hwndDlg);
1963 1964
      break;
    case PSBTN_HELP:
1965
      PROPSHEET_DoCommand(hwndDlg, IDHELP);
1966 1967
      break;
    case PSBTN_NEXT:
1968
      PROPSHEET_Next(hwndDlg);
1969 1970
      break;
    case PSBTN_OK:
1971
      PROPSHEET_DoCommand(hwndDlg, IDOK);
1972 1973
      break;
    default:
1974
      FIXME("Invalid button index %d\n", buttonID);
1975 1976
  }
}
1977

1978 1979

/*************************************************************************
1980
 * BOOL PROPSHEET_CanSetCurSel [Internal]
1981
 *
1982
 * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993
 *
 * PARAMS
 *     hwndDlg        [I] handle to a Dialog hWnd
 *
 * RETURNS
 *     TRUE if Current Selection can change
 *
 * NOTES
 */
static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
{
1994
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1995 1996
                                                    PropSheetInfoStr);
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1997
  PSHNOTIFY psn;
1998
  BOOL res = FALSE;
1999

2000
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
2001
  if (!psInfo)
2002 2003 2004 2005
  {
     res = FALSE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2006 2007

  if (psInfo->active_page < 0)
2008 2009 2010 2011
  {
     res = TRUE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2012

2013 2014 2015 2016
  /*
   * Notify the current page.
   */
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
2017 2018 2019 2020
  psn.hdr.code     = PSN_KILLACTIVE;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
2021

2022
  res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2023 2024 2025 2026

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

2029 2030 2031 2032 2033
/******************************************************************************
 *            PROPSHEET_SetCurSel
 */
static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
                                int index,
2034 2035 2036
				int skipdir,
                                HPROPSHEETPAGE hpage
				)
2037
{
2038
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2039
  HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
2040
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2041

2042
  TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
Alexandre Julliard's avatar
Alexandre Julliard committed
2043
  /* hpage takes precedence over index */
2044
  if (hpage != NULL)
Thuy Nguyen's avatar
Thuy Nguyen committed
2045 2046
    index = PROPSHEET_GetPageIndex(hpage, psInfo);

2047 2048 2049 2050
  if (index < 0 || index >= psInfo->nPages)
  {
    TRACE("Could not find page to select!\n");
    return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
2051 2052
  }

2053
  while (1) {
2054 2055
    int result;
    PSHNOTIFY psn;
2056
    RECT rc;
Filip Navara's avatar
Filip Navara committed
2057
    LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2058

2059 2060 2061
    if (hwndTabControl)
	SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);

2062 2063 2064 2065 2066
    psn.hdr.code     = PSN_SETACTIVE;
    psn.hdr.hwndFrom = hwndDlg;
    psn.hdr.idFrom   = 0;
    psn.lParam       = 0;

2067 2068 2069
    if (!psInfo->proppage[index].hwndPage) {
      PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
    }
2070

Filip Navara's avatar
Filip Navara committed
2071 2072 2073 2074
    /* 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
2075
     * not only when it's created (some applications depend on it). */
Filip Navara's avatar
Filip Navara committed
2076 2077 2078 2079 2080 2081 2082
    PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
    TRACE("setting page %p, rc (%ld,%ld)-(%ld,%ld) w=%ld, h=%ld\n",
          psInfo->proppage[index].hwndPage, rc.left, rc.top, rc.right, rc.bottom,
          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);
2083

2084
    result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2085 2086 2087 2088 2089 2090
    if (!result)
      break;
    if (result == -1) {
      index+=skipdir;
      if (index < 0) {
	index = 0;
2091
	WARN("Tried to skip before first property sheet page!\n");
2092 2093 2094
	break;
      }
      if (index >= psInfo->nPages) {
2095
	WARN("Tried to skip after last property sheet page!\n");
2096 2097 2098 2099
	index = psInfo->nPages-1;
	break;
      }
    }
2100 2101
    else if (result != 0)
    {
2102 2103 2104 2105 2106 2107 2108 2109
      int old_index = index;
      index = PROPSHEET_FindPageByResId(psInfo, result);
      if(index >= psInfo->nPages) {
        index = old_index;
        WARN("Tried to skip to nonexistant page by res id\n");
        break;
      }
      continue;
2110
    }
2111
  }
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124
  /*
   * Display the new page.
   */
  PROPSHEET_ShowPage(hwndDlg, index, psInfo);

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

  return TRUE;
}

2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143
/******************************************************************************
 *            PROPSHEET_SetCurSelId
 *
 * Selects the page, specified by resource id.
 */
static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
{
      int idx;
      PropSheetInfo* psInfo =
          (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);

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

2144 2145 2146 2147 2148
/******************************************************************************
 *            PROPSHEET_SetTitleA
 */
static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
{
2149 2150 2151
  if(HIWORD(lpszText))
  {
     WCHAR szTitle[256];
2152
     MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
2153
			     szTitle, sizeof(szTitle));
2154 2155 2156 2157 2158 2159 2160
     PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
  }
  else
  {
     PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
  }
}
2161

2162 2163 2164 2165 2166 2167 2168 2169 2170
/******************************************************************************
 *            PROPSHEET_SetTitleW
 */
static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
{
  PropSheetInfo*	psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
  WCHAR			szTitle[256];

  TRACE("'%s' (style %08lx)\n", debugstr_w(lpszText), dwStyle);
2171
  if (HIWORD(lpszText) == 0) {
2172
    if (!LoadStringW(psInfo->ppshheader.hInstance,
2173
                     LOWORD(lpszText), szTitle, sizeof(szTitle)-sizeof(WCHAR)))
2174 2175 2176
      return;
    lpszText = szTitle;
  }
2177 2178
  if (dwStyle & PSH_PROPTITLE)
  {
2179 2180 2181
    WCHAR* dest;
    int lentitle = strlenW(lpszText);
    int lenprop  = strlenW(psInfo->strPropertiesFor);
2182

2183
    dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2184 2185
    strcpyW(dest, psInfo->strPropertiesFor);
    strcatW(dest, lpszText);
2186

2187
    SetWindowTextW(hwndDlg, dest);
2188
    Free(dest);
2189 2190
  }
  else
2191
    SetWindowTextW(hwndDlg, lpszText);
2192 2193
}

2194 2195 2196 2197 2198 2199 2200
/******************************************************************************
 *            PROPSHEET_SetFinishTextA
 */
static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
{
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

2201
  TRACE("'%s'\n", lpszText);
2202 2203 2204 2205 2206 2207
  /* Set text, show and enable the Finish button */
  SetWindowTextA(hwndButton, lpszText);
  ShowWindow(hwndButton, SW_SHOW);
  EnableWindow(hwndButton, TRUE);

  /* Make it default pushbutton */
2208
  SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2209 2210 2211 2212 2213 2214 2215 2216 2217 2218

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

  /* Hide Next button */
  hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
  ShowWindow(hwndButton, SW_HIDE);
}

2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243
/******************************************************************************
 *            PROPSHEET_SetFinishTextW
 */
static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
{
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

  TRACE("'%s'\n", debugstr_w(lpszText));
  /* 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);

  /* Hide Next button */
  hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
  ShowWindow(hwndButton, SW_HIDE);
}

2244 2245 2246 2247 2248 2249 2250 2251 2252
/******************************************************************************
 *            PROPSHEET_QuerySiblings
 */
static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
                                       WPARAM wParam, LPARAM lParam)
{
  int i = 0;
  HWND hwndPage;
  LRESULT msgResult = 0;
2253
  PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2254

2255
  while ((i < psInfo->nPages) && (msgResult == 0))
2256 2257
  {
    hwndPage = psInfo->proppage[i].hwndPage;
2258
    msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271
    i++;
  }

  return msgResult;
}


/******************************************************************************
 *            PROPSHEET_AddPage
 */
static BOOL PROPSHEET_AddPage(HWND hwndDlg,
                              HPROPSHEETPAGE hpage)
{
2272
  PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2273 2274
                                                     PropSheetInfoStr);
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2275 2276
  TCITEMW item;
  LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2277

2278
  TRACE("hpage %p\n", hpage);
2279 2280 2281
  /*
   * Allocate and fill in a new PropPageInfo entry.
   */
2282
  psInfo->proppage = (PropPageInfo*) ReAlloc(psInfo->proppage,
2283 2284
                                                      sizeof(PropPageInfo) *
                                                      (psInfo->nPages + 1));
2285
  if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
2286
      return FALSE;
2287

2288 2289
  psInfo->proppage[psInfo->nPages].hpage = hpage;

Alexandre Julliard's avatar
Alexandre Julliard committed
2290 2291 2292 2293 2294
  if (ppsp->dwFlags & PSP_PREMATURE)
  {
     /* Create the page but don't show it */
     PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
  }
2295 2296 2297 2298 2299

  /*
   * Add a new tab to the tab control.
   */
  item.mask = TCIF_TEXT;
2300
  item.pszText = (LPWSTR) psInfo->proppage[psInfo->nPages].pszText;
2301 2302
  item.cchTextMax = MAX_TABTEXT_LENGTH;

2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313
  if (psInfo->hImageList)
  {
    SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
  }

  if ( psInfo->proppage[psInfo->nPages].hasIcon )
  {
    item.mask |= TCIF_IMAGE;
    item.iImage = psInfo->nPages;
  }

2314
  SendMessageW(hwndTabControl, TCM_INSERTITEMW, psInfo->nPages + 1,
2315 2316 2317 2318
               (LPARAM)&item);

  psInfo->nPages++;

Alexandre Julliard's avatar
Alexandre Julliard committed
2319 2320
  /* If it is the only page - show it */
  if(psInfo->nPages == 1)
2321
     PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2322
  return TRUE;
2323 2324 2325 2326 2327 2328 2329 2330 2331
}

/******************************************************************************
 *            PROPSHEET_RemovePage
 */
static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
                                 int index,
                                 HPROPSHEETPAGE hpage)
{
2332
  PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2333 2334
                                                     PropSheetInfoStr);
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2335
  PropPageInfo* oldPages;
2336

2337
  TRACE("index %d, hpage %p\n", index, hpage);
2338 2339 2340
  if (!psInfo) {
    return FALSE;
  }
2341 2342 2343 2344 2345 2346
  /*
   * hpage takes precedence over index.
   */
  if (hpage != 0)
  {
    index = PROPSHEET_GetPageIndex(hpage, psInfo);
2347
  }
2348

2349
  /* Make sure that index is within range */
2350
  if (index < 0 || index >= psInfo->nPages)
Alexandre Julliard's avatar
Alexandre Julliard committed
2351
  {
2352
      TRACE("Could not find page to remove!\n");
2353
      return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2354
  }
2355

2356
  TRACE("total pages %d removing page %d active page %d\n",
2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367
        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  */
2368
        PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2369 2370 2371 2372
      }
      else
      {
        /* activate the next page */
2373
        PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2374
        psInfo->active_page = index;
2375 2376 2377 2378
      }
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2379 2380 2381
      psInfo->active_page = -1;
      if (!psInfo->isModeless)
      {
2382
         psInfo->ended = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2383 2384
         return TRUE;
      }
2385 2386
    }
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2387
  else if (index < psInfo->active_page)
2388 2389
    psInfo->active_page--;

2390 2391 2392 2393 2394 2395 2396 2397 2398
  /* 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
2399 2400 2401 2402 2403 2404
  /* Destroy page dialog window */
  DestroyWindow(psInfo->proppage[index].hwndPage);

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

     if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
2408
        Free ((LPVOID)psInfo->proppage[index].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2409 2410 2411

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

2413
  /* Remove the tab */
2414
  SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2415

2416
  oldPages = psInfo->proppage;
2417
  psInfo->nPages--;
2418
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2419

2420
  if (index > 0)
2421 2422 2423 2424 2425 2426
    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));

2427
  Free(oldPages);
2428

2429 2430 2431
  return FALSE;
}

2432 2433 2434 2435 2436 2437 2438 2439 2440 2441
/******************************************************************************
 *            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)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2442 2443 2444
  HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
  HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
  HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2445 2446 2447

  TRACE("%ld\n", dwFlags);

Alexandre Julliard's avatar
Alexandre Julliard committed
2448 2449 2450 2451
  EnableWindow(hwndBack, FALSE);
  EnableWindow(hwndNext, FALSE);
  EnableWindow(hwndFinish, FALSE);

2452
  if (dwFlags & PSWIZB_BACK)
Alexandre Julliard's avatar
Alexandre Julliard committed
2453
    EnableWindow(hwndBack, TRUE);
2454 2455 2456 2457

  if (dwFlags & PSWIZB_NEXT)
  {
    /* Hide the Finish button */
Alexandre Julliard's avatar
Alexandre Julliard committed
2458
    ShowWindow(hwndFinish, SW_HIDE);
2459 2460

    /* Show and enable the Next button */
Alexandre Julliard's avatar
Alexandre Julliard committed
2461 2462
    ShowWindow(hwndNext, SW_SHOW);
    EnableWindow(hwndNext, TRUE);
2463 2464

    /* Set the Next button as the default pushbutton  */
2465
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2466 2467 2468 2469 2470
  }

  if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
  {
    /* Hide the Next button */
Alexandre Julliard's avatar
Alexandre Julliard committed
2471
    ShowWindow(hwndNext, SW_HIDE);
2472 2473

    /* Show the Finish button */
Alexandre Julliard's avatar
Alexandre Julliard committed
2474
    ShowWindow(hwndFinish, SW_SHOW);
2475 2476

    if (dwFlags & PSWIZB_FINISH)
Alexandre Julliard's avatar
Alexandre Julliard committed
2477
      EnableWindow(hwndFinish, TRUE);
2478 2479

    /* Set the Finish button as the default pushbutton  */
2480
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2481 2482 2483
  }
}

2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
/******************************************************************************
 *            PROPSHEET_InsertPage
 */
static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
{
    if (!HIWORD(hpageInsertAfter))
        FIXME("(%p, %d, %p): stub\n", hwndDlg, LOWORD(hpageInsertAfter), hpage);
    else
        FIXME("(%p, %p, %p): stub\n", hwndDlg, hpageInsertAfter, hpage);
    return FALSE;
}

/******************************************************************************
 *            PROPSHEET_SetHeaderTitleW
 */
static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderTitle)
{
    FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderTitle));
}

/******************************************************************************
 *            PROPSHEET_SetHeaderTitleA
 */
static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderTitle)
{
    FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderTitle));
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleW
 */
static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderSubTitle)
{
    FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderSubTitle));
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleA
 */
static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderSubTitle)
{
    FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderSubTitle));
}

/******************************************************************************
 *            PROPSHEET_HwndToIndex
 */
static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
{
2533 2534 2535 2536 2537 2538 2539 2540 2541 2542
    int index;
    PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
                                                       PropSheetInfoStr);

    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);
2543 2544 2545 2546 2547 2548 2549 2550
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToHwnd
 */
static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
{
2551 2552 2553 2554 2555 2556 2557 2558
    PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
                                                       PropSheetInfoStr);
    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].hwndPage;
2559 2560 2561 2562 2563 2564 2565
}

/******************************************************************************
 *            PROPSHEET_PageToIndex
 */
static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
{
2566 2567 2568 2569 2570 2571 2572 2573 2574 2575
    int index;
    PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
                                                       PropSheetInfoStr);

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

    for (index = 0; index < psInfo->nPages; index++)
        if (psInfo->proppage[index].hpage == hPage)
            return index;
    WARN("%p not found\n", hPage);
2576 2577 2578 2579 2580 2581 2582 2583
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToPage
 */
static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
{
2584 2585 2586 2587 2588 2589 2590 2591
    PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
                                                       PropSheetInfoStr);
    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;
2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607
}

/******************************************************************************
 *            PROPSHEET_IdToIndex
 */
static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
{
    FIXME("(%p, %d): stub\n", hwndDlg, iPageId);
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToId
 */
static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
{
2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620
    PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
                                                       PropSheetInfoStr);
    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;
    if (psp->dwFlags & PSP_DLGINDIRECT || HIWORD(psp->u.pszTemplate)) {
        return 0;
    }
    return (LRESULT)psp->u.pszTemplate;
2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640
}

/******************************************************************************
 *            PROPSHEET_GetResult
 */
static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
{
    FIXME("(%p): stub\n", hwndDlg);
    return -1;
}

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

2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651
/******************************************************************************
 *            PROPSHEET_GetPageIndex
 *
 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
 * the array of PropPageInfo.
 */
static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
{
  BOOL found = FALSE;
  int index = 0;

2652
  TRACE("hpage %p\n", hpage);
2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666
  while ((index < psInfo->nPages) && (found == FALSE))
  {
    if (psInfo->proppage[index].hpage == hpage)
      found = TRUE;
    else
      index++;
  }

  if (found == FALSE)
    index = -1;

  return index;
}

2667 2668 2669 2670 2671
/******************************************************************************
 *            PROPSHEET_CleanUp
 */
static void PROPSHEET_CleanUp(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2672
  int i;
2673
  PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropW(hwndDlg,
2674
                                                       PropSheetInfoStr);
2675 2676

  TRACE("\n");
2677
  if (!psInfo) return;
2678
  if (HIWORD(psInfo->ppshheader.pszCaption))
2679
      Free ((LPVOID)psInfo->ppshheader.pszCaption);
Alexandre Julliard's avatar
Alexandre Julliard committed
2680 2681 2682 2683 2684

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

2685 2686 2687 2688 2689 2690 2691 2692 2693
     /* 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
2694 2695 2696 2697 2698 2699
     if(psInfo->proppage[i].hwndPage)
        DestroyWindow(psInfo->proppage[i].hwndPage);

     if(psp)
     {
        if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
2700
           Free ((LPVOID)psInfo->proppage[i].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2701 2702 2703 2704 2705

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

2706 2707 2708 2709 2710 2711 2712 2713 2714 2715
  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);

2716 2717
  Free(psInfo->proppage);
  Free(psInfo->strPropertiesFor);
2718
  ImageList_Destroy(psInfo->hImageList);
2719 2720 2721 2722

  GlobalFree((HGLOBAL)psInfo);
}

2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750
static INT do_loop(PropSheetInfo *psInfo)
{
    MSG msg;
    INT ret = -1;
    HWND hwnd = psInfo->hwnd;

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

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

    if(ret == 0)
    {
        PostQuitMessage(msg.wParam);
        ret = -1;
    }

    DestroyWindow(hwnd);
    return ret;
}

2751
/******************************************************************************
2752 2753
 *            PropertySheet    (COMCTL32.@)
 *            PropertySheetA   (COMCTL32.@)
2754 2755 2756 2757 2758 2759 2760 2761 2762
 *
 * 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.
2763
 */
2764
INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2765
{
2766 2767 2768
  int bRet = 0;
  PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
                                                       sizeof(PropSheetInfo));
2769
  UINT i, n;
Alexandre Julliard's avatar
Alexandre Julliard committed
2770
  BYTE* pByte;
2771

2772 2773 2774
  TRACE("(%p)\n", lppsh);

  PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2775

2776
  psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2777
                                                    lppsh->nPages);
2778
  pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2779

2780
  for (n = i = 0; i < lppsh->nPages; i++, n++)
2781 2782
  {
    if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2783
      psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
2784 2785
    else
    {
2786
       psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
Alexandre Julliard's avatar
Alexandre Julliard committed
2787 2788 2789
       pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
    }

2790
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2791 2792 2793 2794 2795 2796 2797
                               psInfo, n))
    {
	if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
2798
  }
2799

2800
  psInfo->unicode = FALSE;
2801 2802
  psInfo->ended = FALSE;

2803
  bRet = PROPSHEET_CreateDialog(psInfo);
2804 2805
  if(!psInfo->isModeless)
      bRet = do_loop(psInfo);
2806

2807
  return bRet;
2808 2809
}

2810
/******************************************************************************
2811
 *            PropertySheetW   (COMCTL32.@)
2812 2813
 *
 * See PropertySheetA.
2814
 */
2815
INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2816
{
2817 2818 2819
  int bRet = 0;
  PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
                                                       sizeof(PropSheetInfo));
2820
  UINT i, n;
2821 2822 2823 2824 2825 2826
  BYTE* pByte;

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

  PROPSHEET_CollectSheetInfoW(lppsh, psInfo);

2827
  psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2828 2829
                                                    lppsh->nPages);
  pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2830

2831 2832 2833 2834 2835 2836 2837 2838 2839 2840
  for (n = i = 0; i < lppsh->nPages; i++, n++)
  {
    if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
      psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
    else
    {
       psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
       pByte += ((LPPROPSHEETPAGEW)pByte)->dwSize;
    }

2841
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2842 2843 2844 2845 2846 2847 2848 2849 2850
                               psInfo, n))
    {
	if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
  }

2851
  psInfo->unicode = TRUE;
2852 2853
  psInfo->ended = FALSE;

2854
  bRet = PROPSHEET_CreateDialog(psInfo);
2855 2856
  if(!psInfo->isModeless)
      bRet = do_loop(psInfo);
2857 2858

  return bRet;
2859 2860
}

2861
/******************************************************************************
2862 2863
 *            CreatePropertySheetPage    (COMCTL32.@)
 *            CreatePropertySheetPageA   (COMCTL32.@)
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873
 *
 * 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.
2874
 */
2875 2876
HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
                          LPCPROPSHEETPAGEA lpPropSheetPage)
2877
{
2878
  PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2879

2880
  memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2881

2882
  ppsp->dwFlags &= ~ PSP_INTERNAL_UNICODE;
2883
  if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2884
  {
2885 2886
     int len = strlen(lpPropSheetPage->u.pszTemplate);

2887
     ppsp->u.pszTemplate = Alloc( len+1 );
2888
     strcpy( (LPSTR)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
2889
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2890
  if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2891
  {
2892
      PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2893
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2894 2895

  if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2896
  {
2897
      PROPSHEET_AtoW(&ppsp->pszTitle, lpPropSheetPage->pszTitle);
2898
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2899 2900 2901
  else if ( !(ppsp->dwFlags & PSP_USETITLE) )
      ppsp->pszTitle = NULL;

2902
  return (HPROPSHEETPAGE)ppsp;
2903 2904
}

2905
/******************************************************************************
2906
 *            CreatePropertySheetPageW   (COMCTL32.@)
2907 2908
 *
 * See CreatePropertySheetA.
2909
 */
2910
HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
2911
{
2912
  PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2913 2914 2915

  memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));

2916 2917
  ppsp->dwFlags |= PSP_INTERNAL_UNICODE;

2918 2919 2920
  if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
  {
    int len = strlenW(lpPropSheetPage->u.pszTemplate);
2921

2922
    ppsp->u.pszTemplate = Alloc( (len+1)*sizeof (WCHAR) );
2923 2924 2925 2926 2927
    strcpyW( (WCHAR *)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
  }
  if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
  {
      int len = strlenW(lpPropSheetPage->u2.pszIcon);
2928
      ppsp->u2.pszIcon = Alloc( (len+1)*sizeof (WCHAR) );
2929 2930 2931 2932 2933 2934
      strcpyW( (WCHAR *)ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon );
  }

  if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
  {
      int len = strlenW(lpPropSheetPage->pszTitle);
2935
      ppsp->pszTitle = Alloc( (len+1)*sizeof (WCHAR) );
2936 2937 2938 2939 2940 2941
      strcpyW( (WCHAR *)ppsp->pszTitle, lpPropSheetPage->pszTitle );
  }
  else if ( !(ppsp->dwFlags & PSP_USETITLE) )
      ppsp->pszTitle = NULL;

  return (HPROPSHEETPAGE)ppsp;
2942 2943
}

2944
/******************************************************************************
2945
 *            DestroyPropertySheetPage   (COMCTL32.@)
2946 2947 2948 2949 2950 2951 2952 2953
 *
 * Destroys a property sheet page previously created with
 * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
 * memory.
 *
 * RETURNS
 *     Success: TRUE
 *     Failure: FALSE
2954
 */
2955
BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2956
{
2957
  PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
2958 2959 2960 2961

  if (!psp)
     return FALSE;

2962
  if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
2963
     Free ((LPVOID)psp->u.pszTemplate);
Alexandre Julliard's avatar
Alexandre Julliard committed
2964 2965

  if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2966
     Free ((LPVOID)psp->u2.pszIcon);
Alexandre Julliard's avatar
Alexandre Julliard committed
2967 2968

  if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2969
     Free ((LPVOID)psp->pszTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
2970

2971
  Free(hPropPage);
2972 2973

  return TRUE;
2974 2975
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2976 2977 2978 2979 2980
/******************************************************************************
 *            PROPSHEET_IsDialogMessage
 */
static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
{
2981
   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
2982

2983
   TRACE("\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2984 2985 2986 2987 2988 2989
   if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
      return FALSE;

   if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
   {
      int new_page = 0;
2990
      INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
Alexandre Julliard's avatar
Alexandre Julliard committed
2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018

      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;

3019
            PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
3020 3021 3022 3023 3024 3025
         }

         return TRUE;
      }
   }

3026
   return IsDialogMessageW(hwnd, lpMsg);
Alexandre Julliard's avatar
Alexandre Julliard committed
3027 3028
}

3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060
/******************************************************************************
 *            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)
		{
		    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
								      PropSheetInfoStr);
		    int result = TRUE;

		    if (psInfo->restartWindows)
			result = ID_PSRESTARTWINDOWS;

		    /* reboot system takes precedence over restart windows */
		    if (psInfo->rebootSystem)
			result = ID_PSREBOOTSYSTEM;

		    if (psInfo->isModeless)
			psInfo->activeValid = FALSE;
		    else
3061
                        psInfo->ended = TRUE;
3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087
		}
	    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;
3088 3089 3090

    default:
        return FALSE;
3091 3092 3093 3094 3095
    }

    return TRUE;
}

3096 3097 3098
/******************************************************************************
 *            PROPSHEET_Paint
 */
3099
static LRESULT PROPSHEET_Paint(HWND hwnd, HDC hdcParam)
3100 3101 3102 3103 3104 3105 3106 3107 3108
{
    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
    PAINTSTRUCT ps;
    HDC hdc, hdcSrc;
    BITMAP bm;
    HBITMAP hbmp;
    HPALETTE hOldPal = 0;
    int offsety = 0;
    HBRUSH hbr;
3109
    RECT r, rzone;
3110
    LPCPROPSHEETPAGEW ppshpage;
3111 3112
    WCHAR szBuffer[256];
    int nLength;
3113

3114
    hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3115 3116 3117 3118 3119 3120 3121 3122 3123
    if (!hdc) return 1;

    hdcSrc = CreateCompatibleDC(0);
    ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;

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

    if ( (!(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3124 3125
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_HEADER) ) 
3126 3127 3128 3129 3130 3131 3132
    {
	HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
	HFONT hOldFont;
	COLORREF clrOld = 0;
	int oldBkMode = 0;

	hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u5.hbmHeader);
3133
	hOldFont = SelectObject(hdc, psInfo->hFontBold);
3134 3135 3136

	GetClientRect(hwndLineHeader, &r);
	MapWindowPoints(hwndLineHeader, hwnd, (LPPOINT) &r, 2);
3137
	SetRect(&rzone, 0, 0, r.right + 1, r.top - 1);
3138

3139
	GetObjectW(psInfo->ppshheader.u5.hbmHeader, sizeof(BITMAP), (LPVOID)&bm);		
3140

3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162
 	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));
 	        CopyRect(&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);
 	    }
3163 3164 3165 3166 3167

 	    /* Draw the header itself. */
 	    BitBlt(hdc, 0, 0,
 	           bm.bmWidth, min(bm.bmHeight, rzone.bottom),
 	           hdcSrc, 0, 0, SRCCOPY);
3168 3169 3170
 	}
 	else
 	{
3171
 	    hbr = GetSysColorBrush(COLOR_WINDOW);
3172
 	    FillRect(hdc, &rzone, hbr);
3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184

 	    /* Draw the header bitmap. It's always centered like a
 	     * common 49 x 49 bitmap. */
 	    BitBlt(hdc, rzone.right - 49 - ((rzone.bottom - 49) / 2),
 	           (rzone.bottom - 49) / 2,
 	           bm.bmWidth, bm.bmHeight,
 	           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. */
3185
 	}
3186

3187 3188 3189
	clrOld = SetTextColor (hdc, 0x00000000);
	oldBkMode = SetBkMode (hdc, TRANSPARENT); 

3190
	if (ppshpage->dwFlags & PSP_USEHEADERTITLE) {
3191
	    SetRect(&r, 20, 10, 0, 0);
3192 3193 3194 3195 3196 3197 3198 3199 3200
	    if (HIWORD(ppshpage->pszHeaderTitle))
	    {
		if (psInfo->unicode)
		    DrawTextW(hdc, (LPWSTR)ppshpage->pszHeaderTitle,
			      -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
		else
		    DrawTextA(hdc, (LPCSTR)ppshpage->pszHeaderTitle,
			      -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
	    }
3201
	    else
3202 3203 3204 3205 3206 3207 3208 3209 3210
	    {
		nLength = LoadStringW(ppshpage->hInstance, (UINT)ppshpage->pszHeaderTitle,
				      szBuffer, 256);
		if (nLength != 0)
		{
		    DrawTextW(hdc, szBuffer, nLength,
			      &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
		}
	    }
3211 3212 3213 3214
	}

	if (ppshpage->dwFlags & PSP_USEHEADERSUBTITLE) {
	    SelectObject(hdc, psInfo->hFont);
3215
	    SetRect(&r, 40, 25, rzone.right - 69, rzone.bottom);
3216 3217 3218 3219 3220 3221 3222 3223 3224
	    if (HIWORD(ppshpage->pszHeaderTitle))
	    {
		if (psInfo->unicode)
		    DrawTextW(hdc, (LPWSTR)ppshpage->pszHeaderSubTitle,
			      -1, &r, DT_LEFT | DT_SINGLELINE);
		else
		    DrawTextA(hdc, (LPCSTR)ppshpage->pszHeaderSubTitle,
			      -1, &r, DT_LEFT | DT_SINGLELINE);
	    }
3225
	    else
3226 3227 3228 3229 3230 3231
	    {
		nLength = LoadStringW(ppshpage->hInstance, (UINT)ppshpage->pszHeaderSubTitle,
				      szBuffer, 256);
		if (nLength != 0)
		{
		    DrawTextW(hdc, szBuffer, nLength,
3232
			      &r, DT_LEFT | DT_SINGLELINE);
3233 3234
		}
	    }
3235 3236 3237 3238 3239 3240 3241 3242 3243 3244
	}

	offsety = rzone.bottom + 2;

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

3245
    if ( (ppshpage->dwFlags & PSP_HIDEHEADER) &&
3246 3247
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) ) 
3248
    {
3249 3250 3251 3252 3253
	HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);	    

	GetClientRect(hwndLine, &r);
	MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);

3254 3255 3256 3257 3258 3259 3260 3261
	rzone.left = 0;
	rzone.top = 0;
	rzone.right = r.right;
	rzone.bottom = r.top - 1;

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

3262
	GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), (LPVOID)&bm);
3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277
	hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);

	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);
3278
	}
3279 3280

	SelectObject(hdcSrc, hbmp);	    
3281 3282 3283 3284 3285 3286 3287
    }

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

    DeleteDC(hdcSrc);

3288
    if (!hdcParam) EndPaint(hwnd, &ps);
3289 3290 3291 3292

    return 0;
}

3293 3294 3295
/******************************************************************************
 *            PROPSHEET_DialogProc
 */
3296
INT_PTR CALLBACK
3297 3298
PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
3299
  TRACE("hwnd=%p msg=0x%04x wparam=%x lparam=%lx\n",
3300 3301
	hwnd, uMsg, wParam, lParam);

3302 3303 3304 3305 3306
  switch (uMsg)
  {
    case WM_INITDIALOG:
    {
      PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3307
      WCHAR* strCaption = (WCHAR*)Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3308
      HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3309
      LPCPROPSHEETPAGEW ppshpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
3310
      int idx;
3311
      LOGFONTW logFont;
3312

3313 3314 3315
      /* Using PropSheetInfoStr to store extra data doesn't match the native
       * common control: native uses TCM_[GS]ETITEM
       */
3316
      SetPropW(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
3317

3318
      /*
3319 3320
       * psInfo->hwnd is not being used by WINE code - it exists
       * for compatibility with "real" Windoze. The same about
3321
       * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3322 3323 3324
       * property.
       */
      psInfo->hwnd = hwnd;
3325
      SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3326

3327 3328 3329
      /* set up the Next and Back buttons by default */
      PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);

3330
      /* Set up fonts */
3331 3332
      SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
      psInfo->hFont = CreateFontIndirectW (&logFont);
3333
      logFont.lfWeight = FW_BOLD;
3334
      psInfo->hFontBold = CreateFontIndirectW (&logFont);
3335
      
3336 3337 3338
      /*
       * Small icon in the title bar.
       */
3339 3340
      if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
          (psInfo->ppshheader.dwFlags & PSH_USEHICON))
3341 3342 3343 3344 3345
      {
        HICON hIcon;
        int icon_cx = GetSystemMetrics(SM_CXSMICON);
        int icon_cy = GetSystemMetrics(SM_CYSMICON);

3346
        if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3347
          hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3348
                             psInfo->ppshheader.u.pszIcon,
3349 3350 3351 3352
                             IMAGE_ICON,
                             icon_cx, icon_cy,
                             LR_DEFAULTCOLOR);
        else
3353
          hIcon = psInfo->ppshheader.u.hIcon;
3354

3355
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3356
      }
3357

3358
      if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3359
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3360

3361 3362
      psInfo->strPropertiesFor = strCaption;

3363
      GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3364

3365 3366
      PROPSHEET_CreateTabControl(hwnd, psInfo);

3367 3368
      PROPSHEET_LoadWizardBitmaps(psInfo);

3369
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3370
      {
3371
        ShowWindow(hwndTabCtrl, SW_HIDE);
Filip Navara's avatar
Filip Navara committed
3372 3373
        PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
        PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3374 3375
      }
      else
3376
      {
3377
        if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3378 3379 3380 3381
        {
          PROPSHEET_AdjustSize(hwnd, psInfo);
          PROPSHEET_AdjustButtons(hwnd, psInfo);
        }
3382 3383
      }

3384 3385 3386
      if (psInfo->useCallback)
             (*(psInfo->ppshheader.pfnCallback))(hwnd,
					      PSCB_INITIALIZED, (LPARAM)0);
3387

Alexandre Julliard's avatar
Alexandre Julliard committed
3388
      idx = psInfo->active_page;
3389
      ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[idx].hpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
3390
      psInfo->active_page = -1;
3391

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

3394 3395 3396
      /* 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 */
3397
      SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
3398

3399 3400
      if (!HIWORD(psInfo->ppshheader.pszCaption) &&
              psInfo->ppshheader.hInstance)
3401
      {
3402
         WCHAR szText[256];
3403

3404
         if (LoadStringW(psInfo->ppshheader.hInstance,
3405
                 (UINT)psInfo->ppshheader.pszCaption, szText, 255))
3406
            PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3407 3408 3409
      }
      else
      {
3410
         PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3411
                         psInfo->ppshheader.pszCaption);
3412
      }
3413 3414 3415 3416

      return TRUE;
    }

3417
    case WM_PAINT:
3418
      PROPSHEET_Paint(hwnd, (HDC)wParam);
3419 3420
      return TRUE;

3421 3422 3423 3424 3425
    case WM_DESTROY:
      PROPSHEET_CleanUp(hwnd);
      return TRUE;

    case WM_CLOSE:
Alexandre Julliard's avatar
Alexandre Julliard committed
3426
      PROPSHEET_Cancel(hwnd, 1);
3427 3428 3429
      return TRUE;

    case WM_COMMAND:
3430 3431 3432 3433
      if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
      {
          PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);

3434 3435 3436
          if (!psInfo)
              return FALSE;

3437 3438 3439 3440 3441 3442 3443 3444
          /* 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;
3445

3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456
    case WM_SYSCOMMAND:
      switch(wParam & 0xfff0)
      {
        case SC_CLOSE:
          PROPSHEET_Cancel(hwnd, 1);
          return TRUE;

        default:
          return FALSE;
      }

3457 3458 3459 3460 3461 3462
    case WM_NOTIFY:
    {
      NMHDR* pnmh = (LPNMHDR) lParam;

      if (pnmh->code == TCN_SELCHANGE)
      {
3463
        int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3464
        PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3465 3466
      }

3467 3468 3469
      if(pnmh->code == TCN_SELCHANGING)
      {
        BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3470
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3471 3472 3473
        return TRUE;
      }

3474
      return FALSE;
3475 3476 3477 3478
    }

    case PSM_GETCURRENTPAGEHWND:
    {
3479
      PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3480 3481 3482
                                                        PropSheetInfoStr);
      HWND hwndPage = 0;

3483 3484 3485
      if (!psInfo)
        return FALSE;

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

3489
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505

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

3506
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3507 3508 3509 3510 3511 3512 3513 3514

      return TRUE;
    }

    case PSM_SETCURSEL:
    {
      BOOL msgResult;

3515 3516 3517 3518 3519
      msgResult = PROPSHEET_CanSetCurSel(hwnd);
      if(msgResult != FALSE)
      {
        msgResult = PROPSHEET_SetCurSel(hwnd,
                                       (int)wParam,
3520
				       1,
3521 3522
                                       (HPROPSHEETPAGE)lParam);
      }
3523

3524
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3525 3526 3527 3528 3529 3530

      return TRUE;
    }

    case PSM_CANCELTOCLOSE:
    {
3531
      WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3532 3533 3534 3535
      HWND hwndOK = GetDlgItem(hwnd, IDOK);
      HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);

      EnableWindow(hwndCancel, FALSE);
3536 3537
      if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
         SetWindowTextW(hwndOK, buf);
3538

Alexandre Julliard's avatar
Alexandre Julliard committed
3539
      return FALSE;
3540
    }
3541

3542 3543
    case PSM_RESTARTWINDOWS:
    {
3544
      PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3545
                                                        PropSheetInfoStr);
3546

3547 3548 3549
      if (!psInfo)
        return FALSE;

3550 3551 3552 3553 3554 3555
      psInfo->restartWindows = TRUE;
      return TRUE;
    }

    case PSM_REBOOTSYSTEM:
    {
3556
      PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3557 3558
                                                        PropSheetInfoStr);

3559 3560 3561
      if (!psInfo)
        return FALSE;

3562 3563 3564 3565 3566 3567 3568
      psInfo->rebootSystem = TRUE;
      return TRUE;
    }

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

3570 3571 3572
    case PSM_SETTITLEW:
      PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
      return TRUE;
3573 3574 3575

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

3578
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3579 3580 3581 3582 3583 3584 3585 3586

      return TRUE;
    }

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

3587
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3588 3589 3590 3591

      return TRUE;
    }

3592
    case PSM_ADDPAGE:
3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603
    {
      /*
       * 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);

3604
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3605

3606
      return TRUE;
3607
    }
3608 3609 3610 3611 3612

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

3613 3614
    case PSM_ISDIALOGMESSAGE:
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3615
       BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3616
       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
Alexandre Julliard's avatar
Alexandre Julliard committed
3617
       return TRUE;
3618 3619 3620 3621 3622 3623
    }

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

3624
    case PSM_SETFINISHTEXTA:
3625
      PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3626 3627 3628 3629 3630 3631
      return TRUE;

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

3632
    case PSM_SETCURSELID:
3633 3634
        PROPSHEET_SetCurSelId(hwnd, (int)lParam);
        return TRUE;
3635

3636
    case PSM_SETFINISHTEXTW:
3637
        PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3638
        return FALSE;
3639

3640 3641 3642
    case PSM_INSERTPAGE:
    {
        BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3643
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665
        return TRUE;
    }

    case PSM_SETHEADERTITLEW:
        PROPSHEET_SetHeaderTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
        return TRUE;

    case PSM_SETHEADERTITLEA:
        PROPSHEET_SetHeaderTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
        return TRUE;

    case PSM_SETHEADERSUBTITLEW:
        PROPSHEET_SetHeaderSubTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
        return TRUE;

    case PSM_SETHEADERSUBTITLEA:
        PROPSHEET_SetHeaderSubTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
        return TRUE;

    case PSM_HWNDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3666
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3667 3668 3669 3670 3671 3672
        return TRUE;
    }

    case PSM_INDEXTOHWND:
    {
        LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3673
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3674 3675 3676 3677 3678 3679
        return TRUE;
    }

    case PSM_PAGETOINDEX:
    {
        LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3680
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3681 3682 3683 3684 3685 3686
        return TRUE;
    }

    case PSM_INDEXTOPAGE:
    {
        LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3687
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3688 3689 3690 3691 3692 3693
        return TRUE;
    }

    case PSM_IDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3694
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3695 3696 3697 3698 3699 3700
        return TRUE;
    }

    case PSM_INDEXTOID:
    {
        LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3701
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3702 3703 3704 3705 3706 3707
        return TRUE;
    }

    case PSM_GETRESULT:
    {
        LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3708
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3709 3710 3711 3712 3713 3714
        return TRUE;
    }

    case PSM_RECALCPAGESIZES:
    {
        LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3715
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3716 3717 3718
        return TRUE;
    }

3719 3720 3721
    default:
      return FALSE;
  }
3722
}