propsheet.c 106 KB
Newer Older
1 2 3 4
/*
 * Property Sheets
 *
 * Copyright 1998 Francis Beaudet
5
 * Copyright 1999 Thuy Nguyen
6
 * Copyright 2004 Maxime Bellenge
Filip Navara's avatar
Filip Navara committed
7
 * Copyright 2004 Filip Navara
8
 *
9 10 11 12 13 14 15 16 17 18 19 20
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22
 *
Filip Navara's avatar
Filip Navara committed
23 24 25 26 27 28 29
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Sep. 12, 2004, by Filip Navara.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
 *
30
 * TODO:
31
 *   - Tab order
Filip Navara's avatar
Filip Navara committed
32 33 34 35
 *   - Wizard 97 header resizing
 *   - Enforcing of minimal wizard size
 *   - Messages:
 *     o PSM_RECALCPAGESIZES
36 37
 *     o WM_HELP
 *     o WM_CONTEXTMENU
Filip Navara's avatar
Filip Navara committed
38 39 40 41 42 43 44 45 46 47 48 49
 *   - Notifications:
 *     o PSN_GETOBJECT
 *     o PSN_QUERYINITIALFOCUS
 *     o PSN_TRANSLATEACCELERATOR
 *   - Styles:
 *     o PSH_RTLREADING
 *     o PSH_STRETCHWATERMARK
 *     o PSH_USEPAGELANG
 *     o PSH_USEPSTARTPAGE
 *   - Page styles:
 *     o PSP_USEFUSIONCONTEXT
 *     o PSP_USEREFPARENT
50 51
 */

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

55
#define NONAMELESSUNION
56

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

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

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

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

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

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

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

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

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

142
static const WCHAR PropSheetInfoStr[] = L"PropertySheetInfo";
143

144 145
#define PSP_INTERNAL_UNICODE 0x80000000

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

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

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

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

174
WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
175

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

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

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

    return ret;
}

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

    string[0] = '\0';

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

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

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

Filip Navara's avatar
Filip Navara committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
        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);
    }
265 266
}

267 268 269 270 271
/******************************************************************************
 *            PROPSHEET_FindPageByResId
 *
 * Find page index corresponding to page resource id.
 */
272
static INT PROPSHEET_FindPageByResId(const PropSheetInfo * psInfo, LRESULT resId)
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
{
   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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

406 407 408
  if (!lppsp)
    return FALSE;

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  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)
430
    pTemplate = lppsp->u.pResource;
431
  else if(dwFlags & PSP_INTERNAL_UNICODE )
432 433 434
  {
    HRSRC hResource = FindResourceW(lppsp->hInstance,
                                    lppsp->u.pszTemplate,
435
                                    (LPWSTR)RT_DIALOG);
436 437
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
438
    pTemplate = LockResource(hTemplate);
439
  }
440 441 442
  else
  {
    HRSRC hResource = FindResourceA(lppsp->hInstance,
443
                                    (LPCSTR)lppsp->u.pszTemplate,
444
                                    (LPSTR)RT_DIALOG);
445 446
    HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
                                     hResource);
447
    pTemplate = LockResource(hTemplate);
448
  }
449 450 451 452 453 454 455 456 457

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

  p = (const WORD *)pTemplate;

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

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

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

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

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

Filip Navara's avatar
Filip Navara committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
  /* 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;
  }

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

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

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

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

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

  if (dwFlags & PSP_USETITLE)
  {
    WCHAR szTitle[256];
    const WCHAR *pTitle;

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

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

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

    if (dwFlags & PSP_USEICONID)
574
      hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
                         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
591

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

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

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

625
  if(!(template = LoadResource(COMCTL32_hModule, hRes)))
626
    return -1;
627

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

633
  temp = Alloc(2 * resSize);
634 635

  if (!temp)
636
    return -1;
637 638 639

  memcpy(temp, template, resSize);

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
  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;
  }

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

659 660 661 662
  /* 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). */
663 664
  if( psInfo->unicode )
  {
Frank Richter's avatar
Frank Richter committed
665
    ret = (INT_PTR)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
666 667
                                          temp, psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc, (LPARAM)psInfo);
668
    if ( !ret ) ret = -1;
669 670 671
  }
  else
  {
Frank Richter's avatar
Frank Richter committed
672
    ret = (INT_PTR)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
673 674
                                          temp, psInfo->ppshheader.hwndParent,
                                          PROPSHEET_DialogProc, (LPARAM)psInfo);
675
    if ( !ret ) ret = -1;
676
  }
677

678
  Free(temp);
679

680 681 682 683
  return ret;
}

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

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

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

707
  if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
708
    return TRUE;
709
  if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
710 711 712 713 714 715 716 717 718 719 720 721 722 723
    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);
724
  RECT rc,tabRect;
725
  int buttonHeight;
726
  PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
727
  RECT units;
728
  LONG style;
729 730 731 732 733 734 735 736

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

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

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

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

750
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
751 752 753 754

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

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

764
  SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
765 766 767

  rc.right -= rc.left;
  rc.bottom -= rc.top;
768
  TRACE("setting tab %p, rc (0,0)-(%ld,%ld)\n", hwndTabCtrl, rc.right, rc.bottom);
769 770 771 772 773
  SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

  GetClientRect(hwndTabCtrl, &rc);

774
  TRACE("tab client rc %s\n", wine_dbgstr_rect(&rc));
775

776 777 778
  rc.right += (padding.x * 2);
  rc.bottom += buttonHeight + (3 * padding.y);

779 780 781
  style = GetWindowLongW(hwndDlg, GWL_STYLE);
  if (!(style & WS_CHILD))
    AdjustWindowRect(&rc, style, FALSE);
782 783 784

  rc.right -= rc.left;
  rc.bottom -= rc.top;
785 786 787 788

  /*
   * Resize the property sheet.
   */
789
  TRACE("setting dialog %p, rc (0,0)-(%ld,%ld)\n", hwndDlg, rc.right, rc.bottom);
790 791 792 793 794
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  return TRUE;
}

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

Filip Navara's avatar
Filip Navara committed
805
  /* Biggest page size */
806
  SetRect(&rc, 0, 0, psInfo->width, psInfo->height);
807 808
  MapDialogRect(hwndDlg, &rc);

809
  TRACE("Biggest page %s\n", wine_dbgstr_rect(&rc));
810

Filip Navara's avatar
Filip Navara committed
811 812 813 814 815
  /* 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;
816

Filip Navara's avatar
Filip Navara committed
817 818 819 820
  /* Convert the client coordinates to window coordinates */
  AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);

  /* Resize the property sheet */
821
  TRACE("setting dialog %p, rc (0,0)-(%ld,%ld)\n", hwndDlg, rc.right, rc.bottom);
Filip Navara's avatar
Filip Navara committed
822
  SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
823
               SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
Filip Navara's avatar
Filip Navara committed
824

825 826 827
  return TRUE;
}

828 829 830 831 832
/******************************************************************************
 *            PROPSHEET_AdjustButtons
 *
 * Adjusts the buttons' positions.
 */
833
static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, const PropSheetInfo* psInfo)
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
{
  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.
857
   */
858 859
  GetClientRect(hwndParent, &rcSheet);

860
  /*
861 862 863 864 865
   * All buttons will be at this y coordinate.
   */
  y = rcSheet.bottom - (padding.y + buttonHeight);

  /*
866
   * Position OK button and make it default.
867 868 869 870 871 872 873 874
   */
  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);

875
  SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
876 877


878 879 880 881 882
  /*
   * Position Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

883
  x += padding.x + buttonWidth;
884 885 886 887 888 889 890 891 892

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

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

893 894
  if(psInfo->hasApply)
    x += padding.x + buttonWidth;
895 896 897
  else
    ShowWindow(hwndButton, SW_HIDE);

898 899 900 901
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
              SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  EnableWindow(hwndButton, FALSE);

902 903 904 905 906
  /*
   * Position Help button.
   */
  hwndButton = GetDlgItem(hwndParent, IDHELP);

907 908 909
  x += padding.x + buttonWidth;
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
              SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
910

911
  if(!psInfo->hasHelp)
912 913 914 915 916
    ShowWindow(hwndButton, SW_HIDE);

  return TRUE;
}

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

  if (psInfo->hasHelp)
    num_buttons++;
936 937
  if (psInfo->hasFinish)
    num_buttons++;
938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957

  /*
   * 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);
958
  
959
  /*
960
   * Position the Back button.
961
   */
962
  hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
963

964
  x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)) - buttonWidth;
965 966 967 968

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

969 970 971 972 973 974 975
  /*
   * Position the Next button.
   */
  hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
  
  x += buttonWidth;
  
976 977 978 979
  SetWindowPos(hwndButton, 0, x, y, 0, 0,
               SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

  /*
980
   * Position the Finish button.
981
   */
982 983 984 985
  hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
  
  if (psInfo->hasFinish)
    x += padding.x + buttonWidth;
986 987 988 989

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

990 991 992
  if (!psInfo->hasFinish)
    ShowWindow(hwndButton, SW_HIDE);

993 994 995 996 997
  /*
   * Position the Cancel button.
   */
  hwndButton = GetDlgItem(hwndParent, IDCANCEL);

998
  x += padding.x + buttonWidth;
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

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

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

  if (psInfo->hasHelp)
  {
1010
    x += padding.x + buttonWidth;
1011 1012 1013 1014 1015 1016 1017

    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
1018 1019
  if (psInfo->ppshheader.dwFlags &
      (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)) 
1020 1021
      padding.x = 0;

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
  /*
   * 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);

1032 1033 1034 1035 1036 1037 1038 1039 1040
  /*
   * 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);

1041 1042 1043
  return TRUE;
}

1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
/******************************************************************************
 *            PROPSHEET_GetPaddingInfo
 *
 * Returns the layout information.
 */
static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
{
  HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
  RECT rcTab;
  PADDING_INFO padding;

  GetWindowRect(hwndTab, &rcTab);
1056
  MapWindowPoints( 0, hwndDlg, (POINT *)&rcTab, 2 );
1057

1058 1059
  padding.x = rcTab.left;
  padding.y = rcTab.top;
1060 1061 1062 1063

  return padding;
}

1064 1065 1066 1067 1068
/******************************************************************************
 *            PROPSHEET_GetPaddingInfoWizard
 *
 * Returns the layout information.
 * Vertical spacing is the distance between the line and the buttons.
1069 1070 1071 1072 1073 1074
 * 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.
1075
 */
1076 1077
static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
 psInfo)
1078 1079 1080 1081
{
  PADDING_INFO padding;
  RECT rc;
  HWND hwndControl;
1082 1083
  INT idButton;
  POINT ptButton, ptLine;
1084

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

1103
  hwndControl = GetDlgItem(hwndDlg, idButton);
1104
  GetWindowRect(hwndControl, &rc);
1105
  MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1106 1107
  ptButton.x = rc.left;
  ptButton.y = rc.top;
1108 1109 1110 1111

  /* Line */
  hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
  GetWindowRect(hwndControl, &rc);
1112
  MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1113
  ptLine.x = rc.left;
1114 1115
  ptLine.y = rc.bottom;

1116 1117 1118 1119
  padding.y = ptButton.y - ptLine.y;

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

1121 1122
  /* this is most probably not correct, but the best we have now */
  padding.x = padding.y;
1123 1124 1125
  return padding;
}

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

1139
  TRACE("\n");
1140
  item.mask = TCIF_TEXT;
1141
  item.cchTextMax = MAX_TABTEXT_LENGTH;
1142

1143
  nTabs = psInfo->nPages;
1144

1145 1146 1147 1148 1149
  /*
   * Set the image list for icons.
   */
  if (psInfo->hImageList)
  {
1150
    SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1151 1152
  }

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

1166
    item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1167
    SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, i, (LPARAM)&item);
1168
  }
1169
  SendMessageW(hwndTabCtrl, WM_SETREDRAW, 1, 0);
1170 1171 1172

  return TRUE;
}
1173 1174 1175 1176

/******************************************************************************
 *            PROPSHEET_WizardSubclassProc
 *
1177
 * Subclassing window procedure for wizard exterior pages to prevent drawing
1178 1179
 * background and so drawing above the watermark.
 */
1180
static LRESULT CALLBACK
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
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);
}

1196 1197 1198 1199
/*
 * Get the size of an in-memory Template
 *
 *( Based on the code of PROPSHEET_CollectPageInfo)
1200
 * See also dialog.c/DIALOG_ParseTemplate32().
1201 1202
 */

1203
static UINT GetTemplateSize(const DLGTEMPLATE* pTemplate)
1204 1205 1206

{
  const WORD*  p = (const WORD *)pTemplate;
1207
  BOOL  istemplateex = (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
1208
  WORD nrofitems;
1209
  UINT ret;
1210 1211 1212

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

1215
    TRACE("is DLGTEMPLATEEX\n");
1216 1217 1218 1219 1220 1221 1222 1223
    p++;       /* dlgVer    */
    p++;       /* signature */
    p += 2;    /* help ID   */
    p += 2;    /* ext style */
    p += 2;    /* style     */
  }
  else
  {
1224
    /* DLGTEMPLATE */
1225

1226
    TRACE("is DLGTEMPLATE\n");
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
    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:
1247 1248
      TRACE("menu %s\n",debugstr_w( p ));
      p += lstrlenW( p ) + 1;
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
      break;
  }

  /* class */
  switch ((WORD)*p)
  {
    case 0x0000:
      p++;
      break;
    case 0xffff:
1259
      p += 2; /* 0xffff plus predefined window class ordinal value */
1260 1261
      break;
    default:
1262 1263
      TRACE("class %s\n",debugstr_w( p ));
      p += lstrlenW( p ) + 1;
1264 1265 1266
      break;
  }

1267
  /* title */
1268 1269
  TRACE("title %s\n",debugstr_w( p ));
  p += lstrlenW( p ) + 1;
1270

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

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

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

/******************************************************************************
 *            PROPSHEET_CreatePage
 *
1333
 * Creates a page.
1334
 */
1335
static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1336
                                int index,
1337
                                const PropSheetInfo * psInfo,
1338
                                LPCPROPSHEETPAGEW ppshpage)
1339
{
1340
  const DLGTEMPLATE* pTemplate;
1341
  HWND hwndPage;
1342
  DWORD resSize;
1343
  DLGTEMPLATE* pTemplateCopy = NULL;
1344

1345
  TRACE("index %d\n", index);
1346

1347 1348 1349 1350 1351
  if (ppshpage == NULL)
  {
    return FALSE;
  }

1352
  if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1353
    {
1354
      pTemplate = ppshpage->u.pResource;
1355 1356
      resSize = GetTemplateSize(pTemplate);
    }
1357
  else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1358
  {
1359 1360 1361
    HRSRC hResource;
    HANDLE hTemplate;

1362
    hResource = FindResourceW(ppshpage->hInstance,
1363
                                    ppshpage->u.pszTemplate,
1364
                                    (LPWSTR)RT_DIALOG);
1365 1366 1367 1368 1369 1370 1371 1372 1373
    if(!hResource)
	return FALSE;

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

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

1374
    pTemplate = LockResource(hTemplate);
1375 1376 1377
    /*
     * Make a copy of the dialog template to make it writable
     */
1378
  }
1379 1380 1381 1382 1383 1384
  else
  {
    HRSRC hResource;
    HANDLE hTemplate;

    hResource = FindResourceA(ppshpage->hInstance,
1385
                                    (LPCSTR)ppshpage->u.pszTemplate,
1386
                                    (LPSTR)RT_DIALOG);
1387 1388 1389 1390 1391 1392 1393 1394 1395
    if(!hResource)
	return FALSE;

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

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

1396
    pTemplate = LockResource(hTemplate);
1397 1398 1399 1400
    /*
     * Make a copy of the dialog template to make it writable
     */
  }
1401 1402
  pTemplateCopy = Alloc(resSize);
  if (!pTemplateCopy)
1403 1404
    return FALSE;
  
1405
  TRACE("copying pTemplate %p into pTemplateCopy %p (%ld)\n", pTemplate, pTemplateCopy, resSize);
1406
  memcpy(pTemplateCopy, pTemplate, resSize);
1407

1408
  if (((MyDLGTEMPLATEEX*)pTemplateCopy)->signature == 0xFFFF)
1409
  {
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~DS_MODALFRAME;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_CAPTION;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_SYSMENU;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_POPUP;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_DISABLED;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_VISIBLE;
    ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_THICKFRAME;

    ((MyDLGTEMPLATEEX*)pTemplateCopy)->exStyle |= WS_EX_CONTROLPARENT;
1420 1421 1422
  }
  else
  {
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
    pTemplateCopy->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
    pTemplateCopy->style &= ~DS_MODALFRAME;
    pTemplateCopy->style &= ~WS_CAPTION;
    pTemplateCopy->style &= ~WS_SYSMENU;
    pTemplateCopy->style &= ~WS_POPUP;
    pTemplateCopy->style &= ~WS_DISABLED;
    pTemplateCopy->style &= ~WS_VISIBLE;
    pTemplateCopy->style &= ~WS_THICKFRAME;

    pTemplateCopy->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1433 1434 1435
  }

  if (psInfo->proppage[index].useCallback)
1436
    (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1437
                               (LPPROPSHEETPAGEW)ppshpage);
1438

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

1454 1455 1456
  if(!hwndPage)
      return FALSE;

1457
  psInfo->proppage[index].hwndPage = hwndPage;
1458

1459 1460 1461 1462 1463 1464 1465 1466
  /* 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);
  }
1467 1468
  if (!(psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD))
      EnableThemeDialogTexture (hwndPage, ETDT_ENABLETAB);
1469

1470 1471 1472
  return TRUE;
}

1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
/******************************************************************************
 *            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))
    {
1488
      psInfo->ppshheader.u4.hbmWatermark =
Frank Richter's avatar
Frank Richter committed
1489
        CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
1490 1491 1492 1493 1494 1495
    }

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


1503 1504 1505 1506 1507 1508 1509
/******************************************************************************
 *            PROPSHEET_ShowPage
 *
 * Displays or creates the specified page.
 */
static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
{
1510
  HWND hwndTabCtrl;
1511
  HWND hwndLineHeader;
1512
  HWND control;
1513
  LPCPROPSHEETPAGEW ppshpage;
1514 1515

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

1523
  ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1524
  if (psInfo->proppage[index].hwndPage == 0)
1525
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
1526
     PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1527
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1528

1529
  if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1530 1531 1532
  {
     PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
                         psInfo->proppage[index].pszText);
1533 1534 1535 1536

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

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

1542
  ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1543

1544 1545 1546
  /* 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);
1547
  SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1548

1549
  psInfo->active_page = index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1550
  psInfo->activeValid = TRUE;
1551

1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
  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);
  }

1563 1564 1565
  return TRUE;
}

1566 1567 1568 1569 1570
/******************************************************************************
 *            PROPSHEET_Back
 */
static BOOL PROPSHEET_Back(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1571
  PSHNOTIFY psn;
1572
  HWND hwndPage;
1573
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1574
  LRESULT result;
1575
  int idx;
1576

1577
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1578
  if (psInfo->active_page < 0)
1579 1580
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1581 1582 1583 1584
  psn.hdr.code     = PSN_WIZBACK;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1585

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

1588
  result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1589
  if (result == -1)
1590
    return FALSE;
1591 1592 1593 1594
  else if (result == 0)
     idx = psInfo->active_page - 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, result);
1595

1596
  if (idx >= 0 && idx < psInfo->nPages)
1597
  {
1598
     if (PROPSHEET_CanSetCurSel(hwndDlg))
1599 1600 1601
     {
        SetFocus(GetDlgItem(hwndDlg, IDC_BACK_BUTTON));
        SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
1602
        PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1603
     }
1604
  }
1605 1606 1607 1608 1609 1610 1611 1612
  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Next
 */
static BOOL PROPSHEET_Next(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1613
  PSHNOTIFY psn;
1614 1615
  HWND hwndPage;
  LRESULT msgResult = 0;
1616
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1617
  int idx;
1618

1619
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1620
  if (psInfo->active_page < 0)
1621 1622
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1623 1624 1625 1626
  psn.hdr.code     = PSN_WIZNEXT;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1627

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

1630
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1631 1632
  if (msgResult == -1)
    return FALSE;
1633 1634 1635 1636
  else if (msgResult == 0)
     idx = psInfo->active_page + 1;
  else
     idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1637

1638
  if (idx < psInfo->nPages )
1639
  {
1640
     if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1641 1642 1643
     {
        SetFocus(GetDlgItem(hwndDlg, IDC_NEXT_BUTTON));
        SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1644
        PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1645
     }
1646
  }
1647 1648 1649 1650 1651 1652 1653 1654 1655

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Finish
 */
static BOOL PROPSHEET_Finish(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1656
  PSHNOTIFY psn;
1657 1658
  HWND hwndPage;
  LRESULT msgResult = 0;
1659
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1660

1661
  TRACE("active_page %d\n", psInfo->active_page);
1662 1663 1664
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1665 1666 1667 1668
  psn.hdr.code     = PSN_WIZFINISH;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1669

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

1672
  msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1673

1674
  TRACE("msg result %Id\n", msgResult);
1675 1676 1677 1678

  if (msgResult != 0)
    return FALSE;

1679 1680
  if (psInfo->result == 0)
      psInfo->result = IDOK;
1681
  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1682
    psInfo->activeValid = FALSE;
1683
  else
1684
    psInfo->ended = TRUE;
1685 1686 1687 1688

  return TRUE;
}

1689 1690 1691
/******************************************************************************
 *            PROPSHEET_Apply
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1692
static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1693 1694 1695
{
  int i;
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1696
  PSHNOTIFY psn;
1697
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1698

1699
  TRACE("active_page %d\n", psInfo->active_page);
1700 1701 1702
  if (psInfo->active_page < 0)
     return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1703 1704 1705
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1706

1707 1708 1709 1710

  /*
   * Send PSN_KILLACTIVE to the current page.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1711
  psn.hdr.code = PSN_KILLACTIVE;
1712 1713 1714

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

1715
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1716 1717 1718 1719 1720
    return FALSE;

  /*
   * Send PSN_APPLY to all pages.
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
1721 1722
  psn.hdr.code = PSN_APPLY;
  psn.lParam   = lParam;
1723

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1740 1741 1742 1743 1744 1745 1746 1747 1748
  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;
1749
     SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1750 1751 1752 1753 1754 1755 1756 1757
  }

  return TRUE;
}

/******************************************************************************
 *            PROPSHEET_Cancel
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1758
static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1759
{
1760
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1761
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1762 1763
  PSHNOTIFY psn;
  int i;
1764

1765
  TRACE("active_page %d\n", psInfo->active_page);
1766 1767 1768 1769
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1770 1771 1772 1773
  psn.hdr.code     = PSN_QUERYCANCEL;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1774

1775
  if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1776 1777
    return;

Alexandre Julliard's avatar
Alexandre Julliard committed
1778 1779
  psn.hdr.code = PSN_RESET;
  psn.lParam   = lParam;
1780

Alexandre Julliard's avatar
Alexandre Julliard committed
1781 1782 1783
  for (i = 0; i < psInfo->nPages; i++)
  {
    hwndPage = psInfo->proppage[i].hwndPage;
1784

Alexandre Julliard's avatar
Alexandre Julliard committed
1785
    if (hwndPage)
1786
       SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
Alexandre Julliard's avatar
Alexandre Julliard committed
1787
  }
1788 1789

  if (psInfo->isModeless)
Alexandre Julliard's avatar
Alexandre Julliard committed
1790 1791 1792 1793
  {
     /* makes PSM_GETCURRENTPAGEHWND return NULL */
     psInfo->activeValid = FALSE;
  }
1794
  else
1795
    psInfo->ended = TRUE;
1796 1797
}

1798 1799 1800 1801 1802
/******************************************************************************
 *            PROPSHEET_Help
 */
static void PROPSHEET_Help(HWND hwndDlg)
{
1803
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1804
  HWND hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1805
  PSHNOTIFY psn;
1806

1807
  TRACE("active_page %d\n", psInfo->active_page);
1808 1809 1810 1811
  if (psInfo->active_page < 0)
     return;

  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1812 1813 1814 1815
  psn.hdr.code     = PSN_HELP;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1816

1817
  SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1818 1819
}

1820 1821 1822 1823 1824 1825
/******************************************************************************
 *            PROPSHEET_Changed
 */
static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
{
  int i;
1826
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1827

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

1839 1840 1841 1842 1843 1844
  /*
   * Enable the Apply button.
   */
  if (psInfo->hasApply)
  {
    HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1845

1846 1847 1848
    EnableWindow(hwndApplyBtn, TRUE);
  }
}
1849

1850 1851 1852 1853 1854 1855 1856 1857
/******************************************************************************
 *            PROPSHEET_UnChanged
 */
static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
{
  int i;
  BOOL noPageDirty = TRUE;
  HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1858
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1859

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

1868
    /* look to see if there are any dirty pages */
1869 1870 1871
    if (psInfo->proppage[i].isDirty)
      noPageDirty = FALSE;
  }
1872

1873 1874 1875 1876 1877 1878
  /*
   * Disable Apply button.
   */
  if (noPageDirty)
    EnableWindow(hwndApplyBtn, FALSE);
}
1879

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

1914 1915

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1935
  if (!psInfo)
1936 1937 1938 1939
  {
     res = FALSE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1940

1941
  TRACE("active_page %d\n", psInfo->active_page);
Alexandre Julliard's avatar
Alexandre Julliard committed
1942
  if (psInfo->active_page < 0)
1943 1944 1945 1946
  {
     res = TRUE;
     goto end;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
1947

1948 1949 1950 1951
  /*
   * Notify the current page.
   */
  hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
Alexandre Julliard's avatar
Alexandre Julliard committed
1952 1953 1954 1955
  psn.hdr.code     = PSN_KILLACTIVE;
  psn.hdr.hwndFrom = hwndDlg;
  psn.hdr.idFrom   = 0;
  psn.lParam       = 0;
1956

1957
  res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1958 1959 1960 1961

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

1964 1965 1966 1967 1968
/******************************************************************************
 *            PROPSHEET_SetCurSel
 */
static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
                                int index,
1969 1970 1971
				int skipdir,
                                HPROPSHEETPAGE hpage
				)
1972
{
1973
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1974
  HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
1975
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1976

1977
  TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1978 1979

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

1981 1982 1983 1984
  if (index < 0 || index >= psInfo->nPages)
  {
    TRACE("Could not find page to select!\n");
    return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
1985 1986
  }

1987 1988 1989 1990 1991
  /* unset active page while doing this transition. */
  if (psInfo->active_page != -1)
     ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
  psInfo->active_page = -1;

1992
  while (1) {
1993 1994
    int result;
    PSHNOTIFY psn;
1995
    RECT rc;
Filip Navara's avatar
Filip Navara committed
1996
    LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1997

1998 1999 2000
    if (hwndTabControl)
	SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);

2001 2002 2003 2004 2005
    psn.hdr.code     = PSN_SETACTIVE;
    psn.hdr.hwndFrom = hwndDlg;
    psn.hdr.idFrom   = 0;
    psn.lParam       = 0;

2006
    if (!psInfo->proppage[index].hwndPage) {
2007 2008
      if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage)) {
        PROPSHEET_RemovePage(hwndDlg, index, NULL);
2009 2010 2011 2012 2013 2014 2015

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

2016 2017 2018 2019 2020 2021
        if(index >= psInfo->nPages)
          index--;
        if(index < 0)
            return FALSE;
        continue;
      }
2022
    }
2023

Filip Navara's avatar
Filip Navara committed
2024 2025 2026 2027
    /* 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
2028
     * not only when it's created (some applications depend on it). */
Filip Navara's avatar
Filip Navara committed
2029
    PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2030
    TRACE("setting page %p, rc (%s) w=%ld, h=%ld\n",
2031
          psInfo->proppage[index].hwndPage, wine_dbgstr_rect(&rc),
Filip Navara's avatar
Filip Navara committed
2032 2033 2034 2035
          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);
2036

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

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

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

    InvalidateRect(hwndDlg, &r, TRUE);
  }

2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
  /*
   * Display the new page.
   */
  PROPSHEET_ShowPage(hwndDlg, index, psInfo);

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

  return TRUE;
}

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

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

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

2128 2129 2130 2131 2132
/******************************************************************************
 *            PROPSHEET_SetTitleW
 */
static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
{
2133 2134
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
  WCHAR szTitle[256];
2135

2136
  TRACE("%s (style %#lx)\n", debugstr_w(lpszText), dwStyle);
2137
  if (IS_INTRESOURCE(lpszText)) {
2138
    if (!LoadStringW(psInfo->ppshheader.hInstance, LOWORD(lpszText), szTitle, ARRAY_SIZE(szTitle)))
2139 2140 2141
      return;
    lpszText = szTitle;
  }
2142 2143
  if (dwStyle & PSH_PROPTITLE)
  {
2144
    WCHAR* dest;
2145 2146
    int lentitle = lstrlenW(lpszText);
    int lenprop  = lstrlenW(psInfo->strPropertiesFor);
2147

2148
    dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2149
    wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
2150

2151
    SetWindowTextW(hwndDlg, dest);
2152
    Free(dest);
2153 2154
  }
  else
2155
    SetWindowTextW(hwndDlg, lpszText);
2156 2157
}

2158 2159 2160 2161 2162
/******************************************************************************
 *            PROPSHEET_SetFinishTextA
 */
static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
{
2163
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2164 2165
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

2166
  TRACE("'%s'\n", lpszText);
2167 2168 2169 2170 2171 2172
  /* Set text, show and enable the Finish button */
  SetWindowTextA(hwndButton, lpszText);
  ShowWindow(hwndButton, SW_SHOW);
  EnableWindow(hwndButton, TRUE);

  /* Make it default pushbutton */
2173
  SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2174 2175 2176 2177 2178

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

2179 2180 2181 2182 2183 2184
  if (!psInfo->hasFinish)
  {
    /* Hide Next button */
    hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
    ShowWindow(hwndButton, SW_HIDE);
  }
2185 2186
}

2187 2188 2189 2190 2191
/******************************************************************************
 *            PROPSHEET_SetFinishTextW
 */
static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
{
2192
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2193 2194
  HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);

2195
  TRACE("%s\n", debugstr_w(lpszText));
2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
  /* 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);

2208 2209 2210 2211 2212 2213
  if (!psInfo->hasFinish)
  {
    /* Hide Next button */
    hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
    ShowWindow(hwndButton, SW_HIDE);
  }
2214 2215
}

2216 2217 2218 2219 2220 2221 2222 2223 2224
/******************************************************************************
 *            PROPSHEET_QuerySiblings
 */
static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
                                       WPARAM wParam, LPARAM lParam)
{
  int i = 0;
  HWND hwndPage;
  LRESULT msgResult = 0;
2225
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2226

2227
  while ((i < psInfo->nPages) && (msgResult == 0))
2228 2229
  {
    hwndPage = psInfo->proppage[i].hwndPage;
2230
    msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2231 2232 2233 2234 2235 2236 2237
    i++;
  }

  return msgResult;
}

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

2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267
  TRACE("hwndDlg %p, hpageInsertAfter %p, hpage %p\n", hwndDlg, hpageInsertAfter, hpage);

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

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

  ppi = Alloc(sizeof(PropPageInfo) * (psInfo->nPages + 1));
2268 2269 2270
  if (!ppi)
      return FALSE;

2271 2272 2273 2274 2275 2276 2277 2278
  /*
   * Fill in a new PropPageInfo entry.
   */
  if (index > 0)
    memcpy(ppi, prev_ppi, index * sizeof(PropPageInfo));
  memset(&ppi[index], 0, sizeof(PropPageInfo));
  if (index < psInfo->nPages)
    memcpy(&ppi[index + 1], &prev_ppi[index], (psInfo->nPages - index) * sizeof(PropPageInfo));
2279
  psInfo->proppage = ppi;
2280

2281 2282 2283 2284 2285 2286 2287 2288
  if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, index, FALSE))
  {
     psInfo->proppage = prev_ppi;
     Free(ppi);
     return FALSE;
  }

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

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

2301 2302 2303 2304 2305
  Free(prev_ppi);
  psInfo->nPages++;
  if (index <= psInfo->active_page)
    psInfo->active_page++;

2306 2307 2308 2309
  /*
   * Add a new tab to the tab control.
   */
  item.mask = TCIF_TEXT;
2310
  item.pszText = (LPWSTR) psInfo->proppage[index].pszText;
2311 2312
  item.cchTextMax = MAX_TABTEXT_LENGTH;

2313 2314 2315
  if (psInfo->hImageList)
    SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);

2316
  if (psInfo->proppage[index].hasIcon)
2317 2318
  {
    item.mask |= TCIF_IMAGE;
2319
    item.iImage = index;
2320 2321
  }

2322
  SendMessageW(hwndTabControl, TCM_INSERTITEMW, index, (LPARAM)&item);
2323

Alexandre Julliard's avatar
Alexandre Julliard committed
2324
  /* If it is the only page - show it */
2325
  if (psInfo->nPages == 1)
2326
     PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2327

2328
  return TRUE;
2329 2330
}

2331 2332 2333 2334 2335 2336 2337 2338 2339 2340
/******************************************************************************
 *            PROPSHEET_AddPage
 */
static BOOL PROPSHEET_AddPage(HWND hwndDlg, HPROPSHEETPAGE hpage)
{
  PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
  TRACE("hwndDlg %p, hpage %p\n", hwndDlg, hpage);
  return PROPSHEET_InsertPage(hwndDlg, UlongToPtr(psInfo->nPages), hpage);
}

2341 2342 2343 2344 2345 2346 2347
/******************************************************************************
 *            PROPSHEET_RemovePage
 */
static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
                                 int index,
                                 HPROPSHEETPAGE hpage)
{
2348
  PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2349
  HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2350
  PropPageInfo* oldPages;
2351

2352
  TRACE("index %d, hpage %p\n", index, hpage);
2353 2354 2355
  if (!psInfo) {
    return FALSE;
  }
2356 2357

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

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

2366
  TRACE("total pages %d removing page %d active page %d\n",
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377
        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  */
2378
        PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2379 2380 2381 2382
      }
      else
      {
        /* activate the next page */
2383
        PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2384
        psInfo->active_page = index;
2385 2386 2387 2388
      }
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2389 2390 2391
      psInfo->active_page = -1;
      if (!psInfo->isModeless)
      {
2392
         psInfo->ended = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2393 2394
         return TRUE;
      }
2395 2396
    }
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
2397
  else if (index < psInfo->active_page)
2398 2399
    psInfo->active_page--;

2400 2401 2402 2403 2404 2405 2406 2407 2408
  /* 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
2409 2410 2411 2412 2413 2414
  /* Destroy page dialog window */
  DestroyWindow(psInfo->proppage[index].hwndPage);

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

2417
     if (psp->dwFlags & PSP_USETITLE)
2418
        Free ((LPVOID)psInfo->proppage[index].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2419 2420 2421

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

2423
  /* Remove the tab */
2424
  SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2425

2426
  oldPages = psInfo->proppage;
2427
  psInfo->nPages--;
2428
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2429

2430
  if (index > 0)
2431 2432 2433 2434 2435 2436
    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));

2437
  Free(oldPages);
2438

2439 2440 2441
  return FALSE;
}

2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
/******************************************************************************
 *            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)
{
2452
  PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
2453 2454 2455
  HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
  HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
  HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2456
  BOOL enable_finish = ((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH);
2457

2458
  TRACE("%lx\n", dwFlags);
2459

2460 2461 2462
  EnableWindow(hwndBack, dwFlags & PSWIZB_BACK);
  EnableWindow(hwndNext, dwFlags & PSWIZB_NEXT);
  EnableWindow(hwndFinish, enable_finish);
Alexandre Julliard's avatar
Alexandre Julliard committed
2463

2464
  /* set the default pushbutton to an enabled button */
2465
  if (enable_finish)
2466 2467 2468 2469 2470 2471 2472 2473
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
  else if (dwFlags & PSWIZB_NEXT)
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
  else if (dwFlags & PSWIZB_BACK)
    SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
  else
    SendMessageW(hwndDlg, DM_SETDEFID, IDCANCEL, 0);

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

2494 2495 2496
/******************************************************************************
 *            PROPSHEET_SetHeaderTitleW
 */
2497
static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, UINT page_index, const WCHAR *title)
2498
{
2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513
    PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
    PROPSHEETPAGEW *page;

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

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

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

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

    page->pszHeaderTitle = heap_strdupW(title);
    page->dwFlags |= PSP_USEHEADERTITLE;
2514 2515 2516 2517 2518
}

/******************************************************************************
 *            PROPSHEET_SetHeaderTitleA
 */
2519
static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, UINT page_index, const char *title)
2520
{
2521 2522 2523 2524 2525 2526 2527
    WCHAR *titleW;

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

    titleW = heap_strdupAtoW(title);
    PROPSHEET_SetHeaderTitleW(hwndDlg, page_index, titleW);
    Free(titleW);
2528 2529 2530 2531 2532
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleW
 */
2533
static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, UINT page_index, const WCHAR *subtitle)
2534
{
2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549
    PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
    PROPSHEETPAGEW *page;

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

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

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

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

    page->pszHeaderSubTitle = heap_strdupW(subtitle);
    page->dwFlags |= PSP_USEHEADERSUBTITLE;
2550 2551 2552 2553 2554
}

/******************************************************************************
 *            PROPSHEET_SetHeaderSubTitleA
 */
2555
static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, UINT page_index, const char *subtitle)
2556
{
2557 2558 2559 2560 2561 2562 2563
    WCHAR *subtitleW;

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

    subtitleW = heap_strdupAtoW(subtitle);
    PROPSHEET_SetHeaderSubTitleW(hwndDlg, page_index, subtitleW);
    Free(subtitleW);
2564 2565 2566 2567 2568 2569 2570
}

/******************************************************************************
 *            PROPSHEET_HwndToIndex
 */
static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
{
2571
    int index;
2572
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2573 2574 2575 2576 2577 2578 2579

    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);
2580 2581 2582 2583 2584 2585 2586 2587
    return -1;
}

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

/******************************************************************************
 *            PROPSHEET_PageToIndex
 */
static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
{
2604
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2605 2606 2607

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

2608
    return PROPSHEET_GetPageIndex(hPage, psInfo, -1);
2609 2610 2611 2612 2613 2614 2615
}

/******************************************************************************
 *            PROPSHEET_IndexToPage
 */
static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
{
2616
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2617 2618 2619 2620 2621 2622
    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;
2623 2624 2625 2626 2627 2628 2629
}

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

2640 2641 2642 2643 2644 2645 2646 2647
    return -1;
}

/******************************************************************************
 *            PROPSHEET_IndexToId
 */
static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
{
2648
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2649 2650 2651 2652 2653 2654 2655
    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;
2656
    if (psp->dwFlags & PSP_DLGINDIRECT || !IS_INTRESOURCE(psp->u.pszTemplate)) {
2657 2658 2659
        return 0;
    }
    return (LRESULT)psp->u.pszTemplate;
2660 2661 2662 2663 2664 2665 2666
}

/******************************************************************************
 *            PROPSHEET_GetResult
 */
static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
{
2667
    PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2668
    return psInfo->result;
2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679
}

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

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

2691
    TRACE("page %p index %d\n", page, original_index);
2692

2693 2694 2695
    for (index = 0; index < psInfo->nPages; index++)
        if (psInfo->proppage[index].hpage == page)
            return index;
2696

2697
    return original_index;
2698 2699
}

2700 2701 2702 2703 2704
/******************************************************************************
 *            PROPSHEET_CleanUp
 */
static void PROPSHEET_CleanUp(HWND hwndDlg)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2705
  int i;
2706
  PropSheetInfo* psInfo = RemovePropW(hwndDlg, PropSheetInfoStr);
2707 2708

  TRACE("\n");
2709
  if (!psInfo) return;
2710
  if (!IS_INTRESOURCE(psInfo->ppshheader.pszCaption))
2711
      Free ((LPVOID)psInfo->ppshheader.pszCaption);
Alexandre Julliard's avatar
Alexandre Julliard committed
2712 2713 2714 2715 2716

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

2717 2718 2719 2720 2721 2722 2723 2724 2725
     /* 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
2726 2727 2728 2729 2730
     if(psInfo->proppage[i].hwndPage)
        DestroyWindow(psInfo->proppage[i].hwndPage);

     if(psp)
     {
2731
        if (psp->dwFlags & PSP_USETITLE)
2732
           Free ((LPVOID)psInfo->proppage[i].pszText);
Alexandre Julliard's avatar
Alexandre Julliard committed
2733 2734 2735 2736 2737

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

2738 2739 2740 2741 2742 2743 2744 2745 2746 2747
  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);

2748 2749
  Free(psInfo->proppage);
  Free(psInfo->strPropertiesFor);
2750
  ImageList_Destroy(psInfo->hImageList);
2751

2752
  GlobalFree(psInfo);
2753 2754
}

2755
static INT do_loop(const PropSheetInfo *psInfo)
2756
{
2757
    MSG msg = { 0 };
2758
    INT ret = 0;
2759
    HWND hwnd = psInfo->hwnd;
2760
    HWND parent = psInfo->ppshheader.hwndParent;
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773

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

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

2774
    if(ret == 0 && msg.message)
2775 2776
        PostQuitMessage(msg.wParam);

2777 2778 2779
    if(ret != -1)
        ret = psInfo->result;

2780 2781 2782
    if(parent)
        EnableWindow(parent, TRUE);

2783 2784 2785 2786
    DestroyWindow(hwnd);
    return ret;
}

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

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

  if(!psInfo->isModeless)
  {
2804
      parent = psInfo->ppshheader.hwndParent;
2805
      if (parent) EnableWindow(parent, FALSE);
2806 2807 2808
  }
  bRet = PROPSHEET_CreateDialog(psInfo);
  if(!psInfo->isModeless)
2809 2810 2811 2812
      bRet = do_loop(psInfo);
  return bRet;
}

2813
/******************************************************************************
2814 2815
 *            PropertySheet    (COMCTL32.@)
 *            PropertySheetA   (COMCTL32.@)
2816 2817 2818 2819 2820 2821 2822 2823 2824
 *
 * 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.
2825
 */
Frank Richter's avatar
Frank Richter committed
2826
INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2827
{
2828
  PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2829
  UINT i, n;
2830
  const BYTE* pByte;
2831

2832 2833 2834
  TRACE("(%p)\n", lppsh);

  PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2835

2836
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2837
  pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2838

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

2849
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2850
                               psInfo, n, TRUE))
2851
    {
2852
	if (psInfo->usePropPage)
2853 2854 2855 2856
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
2857
  }
2858

2859
  return PROPSHEET_PropertySheet(psInfo, FALSE);
2860 2861
}

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

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

  PROPSHEET_CollectSheetInfoW(lppsh, psInfo);

2877
  psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2878
  pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2879

2880 2881
  for (n = i = 0; i < lppsh->nPages; i++, n++)
  {
2882
    if (!psInfo->usePropPage)
2883 2884 2885 2886
      psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
    else
    {
       psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2887
       pByte += ((LPCPROPSHEETPAGEW)pByte)->dwSize;
2888 2889
    }

2890
    if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2891
                               psInfo, n, TRUE))
2892
    {
2893
	if (psInfo->usePropPage)
2894 2895 2896 2897 2898 2899
	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
	n--;
	psInfo->nPages--;
    }
  }

2900
  return PROPSHEET_PropertySheet(psInfo, TRUE);
2901 2902
}

2903 2904 2905 2906 2907 2908 2909 2910 2911 2912
static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
{
    LPWSTR ret;

    if (IS_INTRESOURCE(str))
    {
        HRSRC hrsrc;
        HGLOBAL hmem;
        WCHAR *ptr;
        WORD i, id = LOWORD(str);
2913
        UINT len;
2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930

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


2939
/******************************************************************************
2940 2941
 *            CreatePropertySheetPage    (COMCTL32.@)
 *            CreatePropertySheetPageA   (COMCTL32.@)
2942 2943 2944 2945 2946 2947 2948 2949 2950 2951
 *
 * 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.
2952
 */
2953 2954
HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
                          LPCPROPSHEETPAGEA lpPropSheetPage)
2955
{
2956
    PROPSHEETPAGEW *ppsp;
2957

2958 2959 2960
    if (lpPropSheetPage->dwSize < PROPSHEETPAGEA_V1_SIZE)
        return NULL;

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

2974
    ppsp->dwFlags &= ~PSP_INTERNAL_UNICODE;
2975

2976 2977
    if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
    {
2978
        if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
2979 2980
        {
            int len = strlen(lpPropSheetPage->u.pszTemplate) + 1;
2981 2982 2983
            char *template = Alloc( len );

            ppsp->u.pszTemplate = (LPWSTR)strcpy( template, lpPropSheetPage->u.pszTemplate );
2984 2985
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2986

2987 2988
    if (ppsp->dwFlags & PSP_USEICONID)
    {
2989
        if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
2990
            ppsp->u2.pszIcon = heap_strdupAtoW( lpPropSheetPage->u2.pszIcon );
2991 2992 2993 2994
    }

    if (ppsp->dwFlags & PSP_USETITLE)
    {
2995
        if (IS_INTRESOURCE( ppsp->pszTitle ))
2996
            ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
2997 2998
        else
            ppsp->pszTitle = heap_strdupAtoW( lpPropSheetPage->pszTitle );
2999 3000 3001
    }
    else
        ppsp->pszTitle = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3002

3003 3004 3005 3006 3007
    if (ppsp->dwFlags & PSP_HIDEHEADER)
        ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);

    if (ppsp->dwFlags & PSP_USEHEADERTITLE)
    {
3008
        if (IS_INTRESOURCE( ppsp->pszHeaderTitle ))
3009
            ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3010 3011
        else
            ppsp->pszHeaderTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderTitle );
3012 3013 3014 3015 3016 3017
    }
    else
        ppsp->pszHeaderTitle = NULL;

    if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
    {
3018
        if (IS_INTRESOURCE( ppsp->pszHeaderSubTitle ))
3019
            ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3020 3021
        else
            ppsp->pszHeaderSubTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderSubTitle );
3022 3023 3024 3025
    }
    else
        ppsp->pszHeaderSubTitle = NULL;

3026 3027 3028
    if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEA_V1_SIZE && ppsp->pfnCallback)
        ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);

3029
    return (HPROPSHEETPAGE)ppsp;
3030 3031
}

3032
/******************************************************************************
3033
 *            CreatePropertySheetPageW   (COMCTL32.@)
3034 3035
 *
 * See CreatePropertySheetA.
3036
 */
3037
HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
3038
{
3039 3040 3041 3042
    PROPSHEETPAGEW *ppsp;

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

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

3057
    ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
3058

3059 3060
    if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
    {
3061
        if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
3062
            ppsp->u.pszTemplate = heap_strdupW( lpPropSheetPage->u.pszTemplate );
3063
    }
3064

3065 3066
    if ( ppsp->dwFlags & PSP_USEICONID )
    {
3067
        if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
3068
            ppsp->u2.pszIcon = heap_strdupW( lpPropSheetPage->u2.pszIcon );
3069
    }
3070

3071
    if (ppsp->dwFlags & PSP_USETITLE)
3072
        ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3073 3074 3075 3076 3077 3078 3079
    else
        ppsp->pszTitle = NULL;

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

    if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3080
        ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3081 3082 3083 3084
    else
        ppsp->pszHeaderTitle = NULL;

    if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3085
        ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3086 3087
    else
        ppsp->pszHeaderSubTitle = NULL;
3088

3089 3090 3091
    if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEW_V1_SIZE && ppsp->pfnCallback)
        ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);

3092
    return (HPROPSHEETPAGE)ppsp;
3093 3094
}

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

  if (!psp)
     return FALSE;

3113 3114 3115
  if ((psp->dwFlags & PSP_USECALLBACK) && psp->pfnCallback)
     psp->pfnCallback(0, PSPCB_RELEASE, psp + 1);

3116
  if (!(psp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE( psp->u.pszTemplate ))
3117
     Free ((LPVOID)psp->u.pszTemplate);
Alexandre Julliard's avatar
Alexandre Julliard committed
3118

3119
  if ((psp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE( psp->u2.pszIcon ))
3120
     Free ((LPVOID)psp->u2.pszIcon);
Alexandre Julliard's avatar
Alexandre Julliard committed
3121

3122
  if ((psp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE( psp->pszTitle ))
3123
     Free ((LPVOID)psp->pszTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
3124

3125 3126 3127 3128 3129 3130
  if ((psp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE( psp->pszHeaderTitle ))
     Free ((LPVOID)psp->pszHeaderTitle);

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

3131
  Free(hPropPage);
3132 3133

  return TRUE;
3134 3135
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3136 3137 3138 3139 3140
/******************************************************************************
 *            PROPSHEET_IsDialogMessage
 */
static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
{
3141
   PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
3142

3143
   TRACE("\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
3144 3145 3146 3147 3148 3149
   if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
      return FALSE;

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

      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;

3179
            PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
3180 3181 3182 3183 3184 3185
         }

         return TRUE;
      }
   }

3186
   return IsDialogMessageW(hwnd, lpMsg);
Alexandre Julliard's avatar
Alexandre Julliard committed
3187 3188
}

3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206
/******************************************************************************
 *            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)
		{
3207
                    PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3208

3209 3210 3211
                    /* don't overwrite ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM */
                    if (psInfo->result == 0)
                        psInfo->result = IDOK;
3212 3213 3214 3215

		    if (psInfo->isModeless)
			psInfo->activeValid = FALSE;
		    else
3216
                        psInfo->ended = TRUE;
3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242
		}
	    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;
3243 3244 3245

    default:
        return FALSE;
3246 3247 3248 3249 3250
    }

    return TRUE;
}

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

3269
    hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3270 3271 3272 3273 3274 3275 3276
    if (!hdc) return 1;

    hdcSrc = CreateCompatibleDC(0);

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

3277 3278 3279 3280 3281 3282
    if (psInfo->active_page < 0)
        ppshpage = NULL;
    else
        ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;

    if ( (ppshpage && !(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3283 3284
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_HEADER) ) 
3285 3286 3287 3288 3289 3290
    {
	HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
	HFONT hOldFont;
	COLORREF clrOld = 0;
	int oldBkMode = 0;

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

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

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

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

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

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

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

            SelectObject(hdcSrc, hbmp);
        }
3350

3351 3352 3353
	clrOld = SetTextColor (hdc, 0x00000000);
	oldBkMode = SetBkMode (hdc, TRANSPARENT); 

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

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

	offsety = rzone.bottom + 2;

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

3392
    if ( (ppshpage && (ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3393 3394
	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
	 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) ) 
3395
    {
3396 3397 3398 3399
	HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);	    

	GetClientRect(hwndLine, &r);
	MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);
3400
        SetRect(&rzone, 0, 0, r.right, r.top - 1);
3401 3402 3403 3404

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

3405
	GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), &bm);
3406 3407
	hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);

3408 3409
        /* The watermark is truncated to a width of 164 pixels */
        r.right = min(r.right, 164);
3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422
	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);
3423
	}
3424 3425

	SelectObject(hdcSrc, hbmp);	    
3426 3427 3428 3429 3430 3431 3432
    }

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

    DeleteDC(hdcSrc);

3433
    if (!hdcParam) EndPaint(hwnd, &ps);
3434 3435 3436 3437

    return 0;
}

3438 3439 3440
/******************************************************************************
 *            PROPSHEET_DialogProc
 */
3441
static INT_PTR CALLBACK
3442 3443
PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
3444
  TRACE("hwnd %p, msg=0x%04x, wparam %Ix, lparam %Ix\n", hwnd, uMsg, wParam, lParam);
3445

3446 3447 3448 3449 3450
  switch (uMsg)
  {
    case WM_INITDIALOG:
    {
      PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3451
      WCHAR* strCaption = Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3452
      HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
Alexandre Julliard's avatar
Alexandre Julliard committed
3453
      int idx;
3454
      LOGFONTW logFont;
3455

3456 3457 3458
      /* Using PropSheetInfoStr to store extra data doesn't match the native
       * common control: native uses TCM_[GS]ETITEM
       */
3459
      SetPropW(hwnd, PropSheetInfoStr, psInfo);
3460

3461
      /*
3462 3463
       * psInfo->hwnd is not being used by WINE code - it exists
       * for compatibility with "real" Windoze. The same about
3464
       * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3465 3466 3467
       * property.
       */
      psInfo->hwnd = hwnd;
3468
      SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3469

3470 3471 3472 3473 3474
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
      {
        /* set up the Next and Back buttons by default */
        PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);
      }
3475

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

3492
        if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3493
          hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3494
                             psInfo->ppshheader.u.pszIcon,
3495 3496 3497 3498
                             IMAGE_ICON,
                             icon_cx, icon_cy,
                             LR_DEFAULTCOLOR);
        else
3499
          hIcon = psInfo->ppshheader.u.hIcon;
3500

3501
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3502
      }
3503

3504
      if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3505
        SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3506

3507 3508
      psInfo->strPropertiesFor = strCaption;

3509
      GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3510

3511 3512
      PROPSHEET_CreateTabControl(hwnd, psInfo);

3513 3514
      PROPSHEET_LoadWizardBitmaps(psInfo);

3515
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3516
      {
3517
        ShowWindow(hwndTabCtrl, SW_HIDE);
Filip Navara's avatar
Filip Navara committed
3518 3519
        PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
        PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3520
        SetFocus(GetDlgItem(hwnd, IDC_NEXT_BUTTON));
3521 3522
      }
      else
3523
      {
3524
        if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3525 3526 3527 3528
        {
          PROPSHEET_AdjustSize(hwnd, psInfo);
          PROPSHEET_AdjustButtons(hwnd, psInfo);
        }
3529
        SetFocus(GetDlgItem(hwnd, IDOK));
3530 3531
      }

3532
      if (IS_INTRESOURCE(psInfo->ppshheader.pszCaption) &&
3533
              psInfo->ppshheader.hInstance)
3534
      {
3535
         WCHAR szText[256];
3536

3537
         if (LoadStringW(psInfo->ppshheader.hInstance,
Frank Richter's avatar
Frank Richter committed
3538
                         (UINT_PTR)psInfo->ppshheader.pszCaption, szText, 255))
3539
            PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3540 3541 3542
      }
      else
      {
3543
         PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3544
                         psInfo->ppshheader.pszCaption);
3545
      }
3546

3547 3548

      if (psInfo->useCallback)
3549
             (*(psInfo->ppshheader.pfnCallback))(hwnd, PSCB_INITIALIZED, 0);
3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560

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

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

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

3561
      PROPSHEET_UnChanged(hwnd, NULL);
3562

3563 3564 3565 3566
      /* wizards set their focus during init */
      if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
          return FALSE;

3567 3568 3569
      return TRUE;
    }

3570
    case WM_PRINTCLIENT:
3571
    case WM_PAINT:
3572
      PROPSHEET_Paint(hwnd, (HDC)wParam);
3573 3574
      return TRUE;

3575 3576 3577 3578 3579
    case WM_DESTROY:
      PROPSHEET_CleanUp(hwnd);
      return TRUE;

    case WM_CLOSE:
Alexandre Julliard's avatar
Alexandre Julliard committed
3580
      PROPSHEET_Cancel(hwnd, 1);
3581
      return FALSE; /* let DefDlgProc post us WM_COMMAND/IDCANCEL */
3582 3583

    case WM_COMMAND:
3584 3585
      if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
      {
3586
          PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3587

3588 3589 3590
          if (!psInfo)
              return FALSE;

3591 3592 3593 3594 3595 3596 3597 3598
          /* 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;
3599 3600 3601 3602 3603 3604 3605

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

      if (pnmh->code == TCN_SELCHANGE)
      {
3606
        int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3607
        PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3608 3609
      }

3610 3611 3612
      if(pnmh->code == TCN_SELCHANGING)
      {
        BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3613
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3614 3615 3616
        return TRUE;
      }

3617
      return FALSE;
3618
    }
3619 3620 3621 3622
  
    case WM_SYSCOLORCHANGE:
      COMCTL32_RefreshSysColors();
      return FALSE;
3623 3624 3625

    case PSM_GETCURRENTPAGEHWND:
    {
3626
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3627 3628
      HWND hwndPage = 0;

3629 3630 3631
      if (!psInfo)
        return FALSE;

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

3635
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651

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

3652
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3653 3654 3655 3656 3657 3658 3659 3660

      return TRUE;
    }

    case PSM_SETCURSEL:
    {
      BOOL msgResult;

3661 3662 3663 3664 3665
      msgResult = PROPSHEET_CanSetCurSel(hwnd);
      if(msgResult != FALSE)
      {
        msgResult = PROPSHEET_SetCurSel(hwnd,
                                       (int)wParam,
3666
				       1,
3667 3668
                                       (HPROPSHEETPAGE)lParam);
      }
3669

3670
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3671 3672 3673 3674 3675 3676

      return TRUE;
    }

    case PSM_CANCELTOCLOSE:
    {
3677
      WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3678 3679 3680 3681
      HWND hwndOK = GetDlgItem(hwnd, IDOK);
      HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);

      EnableWindow(hwndCancel, FALSE);
3682
      if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, ARRAY_SIZE(buf)))
3683
         SetWindowTextW(hwndOK, buf);
3684

Alexandre Julliard's avatar
Alexandre Julliard committed
3685
      return FALSE;
3686
    }
3687

3688 3689
    case PSM_RESTARTWINDOWS:
    {
3690
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3691

3692 3693 3694
      if (!psInfo)
        return FALSE;

3695 3696 3697 3698
      /* reboot system takes precedence over restart windows */
      if (psInfo->result != ID_PSREBOOTSYSTEM)
          psInfo->result = ID_PSRESTARTWINDOWS;

3699 3700 3701 3702 3703
      return TRUE;
    }

    case PSM_REBOOTSYSTEM:
    {
3704
      PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3705

3706 3707 3708
      if (!psInfo)
        return FALSE;

3709 3710
      psInfo->result = ID_PSREBOOTSYSTEM;

3711 3712 3713 3714 3715 3716
      return TRUE;
    }

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

3718 3719 3720
    case PSM_SETTITLEW:
      PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
      return TRUE;
3721 3722 3723

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

3726
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3727 3728 3729 3730 3731 3732 3733 3734

      return TRUE;
    }

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

3735
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3736 3737 3738 3739

      return TRUE;
    }

3740
    case PSM_ADDPAGE:
3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751
    {
      /*
       * 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);

3752
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3753

3754
      return TRUE;
3755
    }
3756 3757 3758 3759 3760

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

3761 3762
    case PSM_ISDIALOGMESSAGE:
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3763
       BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3764
       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
Alexandre Julliard's avatar
Alexandre Julliard committed
3765
       return TRUE;
3766 3767 3768 3769 3770 3771
    }

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

3772
    case PSM_SETFINISHTEXTA:
3773
      PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3774 3775 3776 3777 3778 3779
      return TRUE;

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

3780
    case PSM_SETCURSELID:
3781 3782
        PROPSHEET_SetCurSelId(hwnd, (int)lParam);
        return TRUE;
3783

3784
    case PSM_SETFINISHTEXTW:
3785
        PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3786
        return FALSE;
3787

3788 3789 3790
    case PSM_INSERTPAGE:
    {
        BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3791
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3792 3793 3794 3795
        return TRUE;
    }

    case PSM_SETHEADERTITLEW:
3796
        PROPSHEET_SetHeaderTitleW(hwnd, wParam, (LPCWSTR)lParam);
3797 3798 3799
        return TRUE;

    case PSM_SETHEADERTITLEA:
3800
        PROPSHEET_SetHeaderTitleA(hwnd, wParam, (LPCSTR)lParam);
3801 3802 3803
        return TRUE;

    case PSM_SETHEADERSUBTITLEW:
3804
        PROPSHEET_SetHeaderSubTitleW(hwnd, wParam, (LPCWSTR)lParam);
3805 3806 3807
        return TRUE;

    case PSM_SETHEADERSUBTITLEA:
3808
        PROPSHEET_SetHeaderSubTitleA(hwnd, wParam, (LPCSTR)lParam);
3809 3810 3811 3812 3813
        return TRUE;

    case PSM_HWNDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3814
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3815 3816 3817 3818 3819 3820
        return TRUE;
    }

    case PSM_INDEXTOHWND:
    {
        LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3821
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3822 3823 3824 3825 3826 3827
        return TRUE;
    }

    case PSM_PAGETOINDEX:
    {
        LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3828
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3829 3830 3831 3832 3833 3834
        return TRUE;
    }

    case PSM_INDEXTOPAGE:
    {
        LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3835
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3836 3837 3838 3839 3840 3841
        return TRUE;
    }

    case PSM_IDTOINDEX:
    {
        LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3842
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3843 3844 3845 3846 3847 3848
        return TRUE;
    }

    case PSM_INDEXTOID:
    {
        LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3849
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3850 3851 3852 3853 3854 3855
        return TRUE;
    }

    case PSM_GETRESULT:
    {
        LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3856
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3857 3858 3859 3860 3861 3862
        return TRUE;
    }

    case PSM_RECALCPAGESIZES:
    {
        LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3863
        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3864 3865 3866
        return TRUE;
    }

3867 3868 3869
    default:
      return FALSE;
  }
3870
}