[COMCTL32] Sync with Wine Staging 1.7.55. CORE-10536
[reactos.git] / reactos / dll / win32 / comctl32 / propsheet.c
1 /*
2 * Property Sheets
3 *
4 * Copyright 1998 Francis Beaudet
5 * Copyright 1999 Thuy Nguyen
6 * Copyright 2004 Maxime Bellenge
7 * Copyright 2004 Filip Navara
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * 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 *
23 * This code was audited for completeness against the documented features
24 * of Comctl32.dll version 6.0 on Sep. 12, 2004, by Filip Navara.
25 *
26 * Unless otherwise noted, we believe this code to be complete, as per
27 * the specification mentioned above.
28 * If you discover missing features, or bugs, please note them below.
29 *
30 * TODO:
31 * - Tab order
32 * - Wizard 97 header resizing
33 * - Enforcing of minimal wizard size
34 * - Messages:
35 * o PSM_INSERTPAGE
36 * o PSM_RECALCPAGESIZES
37 * o PSM_SETHEADERSUBTITLE
38 * o PSM_SETHEADERTITLE
39 * o WM_HELP
40 * o WM_CONTEXTMENU
41 * - Notifications:
42 * o PSN_GETOBJECT
43 * o PSN_QUERYINITIALFOCUS
44 * o PSN_TRANSLATEACCELERATOR
45 * - Styles:
46 * o PSH_RTLREADING
47 * o PSH_STRETCHWATERMARK
48 * o PSH_USEPAGELANG
49 * o PSH_USEPSTARTPAGE
50 * - Page styles:
51 * o PSP_USEFUSIONCONTEXT
52 * o PSP_USEREFPARENT
53 */
54
55 #include "comctl32.h"
56
57 /******************************************************************************
58 * Data structures
59 */
60 #include "pshpack2.h"
61
62 typedef struct
63 {
64 WORD dlgVer;
65 WORD signature;
66 DWORD helpID;
67 DWORD exStyle;
68 DWORD style;
69 } MyDLGTEMPLATEEX;
70
71 typedef struct
72 {
73 DWORD helpid;
74 DWORD exStyle;
75 DWORD style;
76 short x;
77 short y;
78 short cx;
79 short cy;
80 DWORD id;
81 } MyDLGITEMTEMPLATEEX;
82 #include "poppack.h"
83
84 typedef struct tagPropPageInfo
85 {
86 HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
87 HWND hwndPage;
88 BOOL isDirty;
89 LPCWSTR pszText;
90 BOOL hasHelp;
91 BOOL useCallback;
92 BOOL hasIcon;
93 } PropPageInfo;
94
95 typedef struct tagPropSheetInfo
96 {
97 HWND hwnd;
98 PROPSHEETHEADERW ppshheader;
99 BOOL unicode;
100 LPWSTR strPropertiesFor;
101 int nPages;
102 int active_page;
103 BOOL isModeless;
104 BOOL hasHelp;
105 BOOL hasApply;
106 BOOL hasFinish;
107 BOOL usePropPage;
108 BOOL useCallback;
109 BOOL activeValid;
110 PropPageInfo* proppage;
111 HFONT hFont;
112 HFONT hFontBold;
113 int width;
114 int height;
115 HIMAGELIST hImageList;
116 BOOL ended;
117 INT result;
118 } PropSheetInfo;
119
120 typedef struct
121 {
122 int x;
123 int y;
124 } PADDING_INFO;
125
126 /******************************************************************************
127 * Defines and global variables
128 */
129
130 static const WCHAR PropSheetInfoStr[] =
131 {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
132
133 #define PSP_INTERNAL_UNICODE 0x80000000
134
135 #define MAX_CAPTION_LENGTH 255
136 #define MAX_TABTEXT_LENGTH 255
137 #define MAX_BUTTONTEXT_LENGTH 64
138
139 #define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)
140
141 /* Wizard metrics specified in DLUs */
142 #define WIZARD_PADDING 7
143 #define WIZARD_HEADER_HEIGHT 36
144
145 /******************************************************************************
146 * Prototypes
147 */
148 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
149 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
150 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
151 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
152 int index,
153 int skipdir,
154 HPROPSHEETPAGE hpage);
155 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, const PropSheetInfo* psInfo, int original_index);
156 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
157 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
158 static BOOL PROPSHEET_RemovePage(HWND hwndDlg, int index, HPROPSHEETPAGE hpage);
159 static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage);
160
161 static INT_PTR CALLBACK
162 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
163
164 WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
165
166 #define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
167 /******************************************************************************
168 * PROPSHEET_UnImplementedFlags
169 *
170 * Document use of flags we don't implement yet.
171 */
172 static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
173 {
174 CHAR string[256];
175
176 string[0] = '\0';
177
178 /*
179 * unhandled header flags:
180 * PSH_RTLREADING 0x00000800
181 * PSH_STRETCHWATERMARK 0x00040000
182 * PSH_USEPAGELANG 0x00200000
183 */
184
185 add_flag(PSH_RTLREADING);
186 add_flag(PSH_STRETCHWATERMARK);
187 add_flag(PSH_USEPAGELANG);
188 if (string[0] != '\0')
189 FIXME("%s\n", string);
190 }
191 #undef add_flag
192
193 /******************************************************************************
194 * PROPSHEET_GetPageRect
195 *
196 * Retrieve rect from tab control and map into the dialog for SetWindowPos
197 */
198 static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg,
199 RECT *rc, LPCPROPSHEETPAGEW ppshpage)
200 {
201 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {
202 HWND hwndChild;
203 RECT r;
204
205 if (((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
206 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
207 !(ppshpage->dwFlags & PSP_HIDEHEADER)) ||
208 (psInfo->ppshheader.dwFlags & PSH_WIZARD))
209 {
210 rc->left = rc->top = WIZARD_PADDING;
211 }
212 else
213 {
214 rc->left = rc->top = 0;
215 }
216 rc->right = psInfo->width - rc->left;
217 rc->bottom = psInfo->height - rc->top;
218 MapDialogRect(hwndDlg, rc);
219
220 if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
221 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
222 !(ppshpage->dwFlags & PSP_HIDEHEADER))
223 {
224 hwndChild = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
225 GetClientRect(hwndChild, &r);
226 MapWindowPoints(hwndChild, hwndDlg, (LPPOINT) &r, 2);
227 rc->top += r.bottom + 1;
228 }
229 } else {
230 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
231 GetClientRect(hwndTabCtrl, rc);
232 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
233 MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
234 }
235 }
236
237 /******************************************************************************
238 * PROPSHEET_FindPageByResId
239 *
240 * Find page index corresponding to page resource id.
241 */
242 static INT PROPSHEET_FindPageByResId(const PropSheetInfo * psInfo, LRESULT resId)
243 {
244 INT i;
245
246 for (i = 0; i < psInfo->nPages; i++)
247 {
248 LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;
249
250 /* Fixme: if resource ID is a string shall we use strcmp ??? */
251 if (lppsp->u.pszTemplate == (LPVOID)resId)
252 break;
253 }
254
255 return i;
256 }
257
258 /******************************************************************************
259 * PROPSHEET_AtoW
260 *
261 * Convert ASCII to Unicode since all data is saved as Unicode.
262 */
263 static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
264 {
265 INT len;
266 WCHAR *to;
267
268 TRACE("<%s>\n", frstr);
269 len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
270 to = Alloc(len * sizeof(WCHAR));
271 MultiByteToWideChar(CP_ACP, 0, frstr, -1, to, len);
272 *tostr = to;
273 }
274
275 /******************************************************************************
276 * PROPSHEET_CollectSheetInfoCommon
277 *
278 * Common code for PROPSHEET_CollectSheetInfoA/W
279 */
280 static void PROPSHEET_CollectSheetInfoCommon(PropSheetInfo * psInfo, DWORD dwFlags)
281 {
282 PROPSHEET_UnImplementedFlags(dwFlags);
283
284 psInfo->hasHelp = dwFlags & PSH_HASHELP;
285 psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
286 psInfo->hasFinish = dwFlags & PSH_WIZARDHASFINISH;
287 psInfo->isModeless = dwFlags & PSH_MODELESS;
288 psInfo->usePropPage = dwFlags & PSH_PROPSHEETPAGE;
289 if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
290 psInfo->active_page = 0;
291
292 psInfo->result = 0;
293 psInfo->hImageList = 0;
294 psInfo->activeValid = FALSE;
295 }
296
297 /******************************************************************************
298 * PROPSHEET_CollectSheetInfoA
299 *
300 * Collect relevant data.
301 */
302 static void PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
303 PropSheetInfo * psInfo)
304 {
305 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
306 DWORD dwFlags = lppsh->dwFlags;
307
308 psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
309
310 memcpy(&psInfo->ppshheader,lppsh,dwSize);
311 TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
312 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
313 debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
314
315 if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
316 psInfo->ppshheader.pszCaption = NULL;
317 else
318 {
319 if (!IS_INTRESOURCE(lppsh->pszCaption))
320 {
321 int len = MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, NULL, 0);
322 WCHAR *caption = Alloc( len*sizeof (WCHAR) );
323
324 MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, caption, len);
325 psInfo->ppshheader.pszCaption = caption;
326 }
327 }
328 psInfo->nPages = lppsh->nPages;
329
330 if (dwFlags & PSH_USEPSTARTPAGE)
331 {
332 TRACE("PSH_USEPSTARTPAGE is on\n");
333 psInfo->active_page = 0;
334 }
335 else
336 psInfo->active_page = lppsh->u2.nStartPage;
337
338 PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
339 }
340
341 /******************************************************************************
342 * PROPSHEET_CollectSheetInfoW
343 *
344 * Collect relevant data.
345 */
346 static void PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
347 PropSheetInfo * psInfo)
348 {
349 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
350 DWORD dwFlags = lppsh->dwFlags;
351
352 psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
353
354 memcpy(&psInfo->ppshheader,lppsh,dwSize);
355 TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t%s\nnPages\t\t%d\npfnCallback\t%p\n",
356 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
357
358 if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
359 psInfo->ppshheader.pszCaption = NULL;
360 else
361 {
362 if (!IS_INTRESOURCE(lppsh->pszCaption))
363 {
364 int len = strlenW(lppsh->pszCaption);
365 WCHAR *caption = Alloc( (len+1)*sizeof(WCHAR) );
366
367 psInfo->ppshheader.pszCaption = strcpyW( caption, lppsh->pszCaption );
368 }
369 }
370 psInfo->nPages = lppsh->nPages;
371
372 if (dwFlags & PSH_USEPSTARTPAGE)
373 {
374 TRACE("PSH_USEPSTARTPAGE is on\n");
375 psInfo->active_page = 0;
376 }
377 else
378 psInfo->active_page = lppsh->u2.nStartPage;
379
380 PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
381 }
382
383 /******************************************************************************
384 * PROPSHEET_CollectPageInfo
385 *
386 * Collect property sheet data.
387 * With code taken from DIALOG_ParseTemplate32.
388 */
389 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
390 PropSheetInfo * psInfo,
391 int index, BOOL resize)
392 {
393 const DLGTEMPLATE* pTemplate;
394 const WORD* p;
395 DWORD dwFlags;
396 int width, height;
397
398 if (!lppsp)
399 return FALSE;
400
401 TRACE("\n");
402 psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
403 psInfo->proppage[index].hwndPage = 0;
404 psInfo->proppage[index].isDirty = FALSE;
405
406 /*
407 * Process property page flags.
408 */
409 dwFlags = lppsp->dwFlags;
410 psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
411 psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
412 psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
413
414 /* as soon as we have a page with the help flag, set the sheet flag on */
415 if (psInfo->proppage[index].hasHelp)
416 psInfo->hasHelp = TRUE;
417
418 /*
419 * Process page template.
420 */
421 if (dwFlags & PSP_DLGINDIRECT)
422 pTemplate = lppsp->u.pResource;
423 else if(dwFlags & PSP_INTERNAL_UNICODE )
424 {
425 HRSRC hResource = FindResourceW(lppsp->hInstance,
426 lppsp->u.pszTemplate,
427 (LPWSTR)RT_DIALOG);
428 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
429 hResource);
430 pTemplate = LockResource(hTemplate);
431 }
432 else
433 {
434 HRSRC hResource = FindResourceA(lppsp->hInstance,
435 (LPCSTR)lppsp->u.pszTemplate,
436 (LPSTR)RT_DIALOG);
437 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
438 hResource);
439 pTemplate = LockResource(hTemplate);
440 }
441
442 /*
443 * Extract the size of the page and the caption.
444 */
445 if (!pTemplate)
446 return FALSE;
447
448 p = (const WORD *)pTemplate;
449
450 if (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
451 {
452 /* DLGTEMPLATEEX (not defined in any std. header file) */
453
454 p++; /* dlgVer */
455 p++; /* signature */
456 p += 2; /* help ID */
457 p += 2; /* ext style */
458 p += 2; /* style */
459 }
460 else
461 {
462 /* DLGTEMPLATE */
463
464 p += 2; /* style */
465 p += 2; /* ext style */
466 }
467
468 p++; /* nb items */
469 p++; /* x */
470 p++; /* y */
471 width = (WORD)*p; p++;
472 height = (WORD)*p; p++;
473
474 /* Special calculation for interior wizard pages so the largest page is
475 * calculated correctly. We need to add all the padding and space occupied
476 * by the header so the width and height sums up to the whole wizard client
477 * area. */
478 if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
479 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
480 !(dwFlags & PSP_HIDEHEADER))
481 {
482 height += 2 * WIZARD_PADDING + WIZARD_HEADER_HEIGHT;
483 width += 2 * WIZARD_PADDING;
484 }
485 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
486 {
487 height += 2 * WIZARD_PADDING;
488 width += 2 * WIZARD_PADDING;
489 }
490
491 /* remember the largest width and height */
492 if (resize)
493 {
494 if (width > psInfo->width)
495 psInfo->width = width;
496
497 if (height > psInfo->height)
498 psInfo->height = height;
499 }
500
501 /* menu */
502 switch ((WORD)*p)
503 {
504 case 0x0000:
505 p++;
506 break;
507 case 0xffff:
508 p += 2;
509 break;
510 default:
511 p += lstrlenW( p ) + 1;
512 break;
513 }
514
515 /* class */
516 switch ((WORD)*p)
517 {
518 case 0x0000:
519 p++;
520 break;
521 case 0xffff:
522 p += 2;
523 break;
524 default:
525 p += lstrlenW( p ) + 1;
526 break;
527 }
528
529 /* Extract the caption */
530 psInfo->proppage[index].pszText = p;
531 TRACE("Tab %d %s\n",index,debugstr_w( p ));
532
533 if (dwFlags & PSP_USETITLE)
534 {
535 WCHAR szTitle[256];
536 const WCHAR *pTitle;
537 static const WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
538 WCHAR *text;
539 int len;
540
541 if (IS_INTRESOURCE( lppsp->pszTitle ))
542 {
543 if (LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle, szTitle, sizeof(szTitle)/sizeof(szTitle[0]) ))
544 pTitle = szTitle;
545 else if (*p)
546 pTitle = p;
547 else
548 pTitle = pszNull;
549 }
550 else
551 pTitle = lppsp->pszTitle;
552
553 len = strlenW(pTitle);
554 text = Alloc( (len+1)*sizeof (WCHAR) );
555 psInfo->proppage[index].pszText = strcpyW( text, pTitle);
556 }
557
558 /*
559 * Build the image list for icons
560 */
561 if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
562 {
563 HICON hIcon;
564 int icon_cx = GetSystemMetrics(SM_CXSMICON);
565 int icon_cy = GetSystemMetrics(SM_CYSMICON);
566
567 if (dwFlags & PSP_USEICONID)
568 hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
569 icon_cx, icon_cy, LR_DEFAULTCOLOR);
570 else
571 hIcon = lppsp->u2.hIcon;
572
573 if ( hIcon )
574 {
575 if (psInfo->hImageList == 0 )
576 psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
577
578 ImageList_AddIcon(psInfo->hImageList, hIcon);
579 }
580
581 }
582
583 return TRUE;
584 }
585
586 /******************************************************************************
587 * PROPSHEET_CreateDialog
588 *
589 * Creates the actual property sheet.
590 */
591 static INT_PTR PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
592 {
593 LRESULT ret;
594 LPCVOID template;
595 LPVOID temp = 0;
596 HRSRC hRes;
597 DWORD resSize;
598 WORD resID = IDD_PROPSHEET;
599
600 TRACE("(%p)\n", psInfo);
601 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
602 resID = IDD_WIZARD;
603
604 if( psInfo->unicode )
605 {
606 if(!(hRes = FindResourceW(COMCTL32_hModule,
607 MAKEINTRESOURCEW(resID),
608 (LPWSTR)RT_DIALOG)))
609 return -1;
610 }
611 else
612 {
613 if(!(hRes = FindResourceA(COMCTL32_hModule,
614 MAKEINTRESOURCEA(resID),
615 (LPSTR)RT_DIALOG)))
616 return -1;
617 }
618
619 if(!(template = LoadResource(COMCTL32_hModule, hRes)))
620 return -1;
621
622 /*
623 * Make a copy of the dialog template.
624 */
625 resSize = SizeofResource(COMCTL32_hModule, hRes);
626
627 temp = Alloc(resSize);
628
629 if (!temp)
630 return -1;
631
632 memcpy(temp, template, resSize);
633
634 if (psInfo->ppshheader.dwFlags & PSH_NOCONTEXTHELP)
635 {
636 if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
637 ((MyDLGTEMPLATEEX*)temp)->style &= ~DS_CONTEXTHELP;
638 else
639 ((DLGTEMPLATE*)temp)->style &= ~DS_CONTEXTHELP;
640 }
641 if ((psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) &&
642 (psInfo->ppshheader.dwFlags & PSH_WIZARDCONTEXTHELP))
643 {
644 if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
645 ((MyDLGTEMPLATEEX*)temp)->style |= DS_CONTEXTHELP;
646 else
647 ((DLGTEMPLATE*)temp)->style |= DS_CONTEXTHELP;
648 }
649
650 if (psInfo->useCallback)
651 (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
652
653 /* NOTE: MSDN states "Returns a positive value if successful, or -1
654 * otherwise for modal property sheets.", but this is wrong. The
655 * actual return value is either TRUE (success), FALSE (cancel) or
656 * -1 (error). */
657 if( psInfo->unicode )
658 {
659 ret = (INT_PTR)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
660 temp, psInfo->ppshheader.hwndParent,
661 PROPSHEET_DialogProc, (LPARAM)psInfo);
662 if ( !ret ) ret = -1;
663 }
664 else
665 {
666 ret = (INT_PTR)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
667 temp, psInfo->ppshheader.hwndParent,
668 PROPSHEET_DialogProc, (LPARAM)psInfo);
669 if ( !ret ) ret = -1;
670 }
671
672 Free(temp);
673
674 return ret;
675 }
676
677 /******************************************************************************
678 * PROPSHEET_SizeMismatch
679 *
680 * Verify that the tab control and the "largest" property sheet page dlg. template
681 * match in size.
682 */
683 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, const PropSheetInfo* psInfo)
684 {
685 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
686 RECT rcOrigTab, rcPage;
687
688 /*
689 * Original tab size.
690 */
691 GetClientRect(hwndTabCtrl, &rcOrigTab);
692 TRACE("orig tab %s\n", wine_dbgstr_rect(&rcOrigTab));
693
694 /*
695 * Biggest page size.
696 */
697 rcPage.left = 0;
698 rcPage.top = 0;
699 rcPage.right = psInfo->width;
700 rcPage.bottom = psInfo->height;
701
702 MapDialogRect(hwndDlg, &rcPage);
703 TRACE("biggest page %s\n", wine_dbgstr_rect(&rcPage));
704
705 if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
706 return TRUE;
707 if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
708 return TRUE;
709
710 return FALSE;
711 }
712
713 /******************************************************************************
714 * PROPSHEET_AdjustSize
715 *
716 * Resizes the property sheet and the tab control to fit the largest page.
717 */
718 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
719 {
720 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
721 HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
722 RECT rc,tabRect;
723 int buttonHeight;
724 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
725 RECT units;
726 LONG style;
727
728 /* Get the height of buttons */
729 GetClientRect(hwndButton, &rc);
730 buttonHeight = rc.bottom;
731
732 /*
733 * Biggest page size.
734 */
735 rc.left = 0;
736 rc.top = 0;
737 rc.right = psInfo->width;
738 rc.bottom = psInfo->height;
739
740 MapDialogRect(hwndDlg, &rc);
741
742 /* retrieve the dialog units */
743 units.left = units.right = 4;
744 units.top = units.bottom = 8;
745 MapDialogRect(hwndDlg, &units);
746
747 /*
748 * Resize the tab control.
749 */
750 GetClientRect(hwndTabCtrl,&tabRect);
751
752 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
753
754 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
755 {
756 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
757 psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
758 }
759
760 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
761 {
762 rc.right = rc.left + tabRect.right - tabRect.left;
763 psInfo->width = MulDiv((rc.right - rc.left),4,units.left);
764 }
765
766 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
767
768 rc.right -= rc.left;
769 rc.bottom -= rc.top;
770 TRACE("setting tab %p, rc (0,0)-(%d,%d)\n",
771 hwndTabCtrl, rc.right, rc.bottom);
772 SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
773 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
774
775 GetClientRect(hwndTabCtrl, &rc);
776
777 TRACE("tab client rc %s\n", wine_dbgstr_rect(&rc));
778
779 rc.right += (padding.x * 2);
780 rc.bottom += buttonHeight + (3 * padding.y);
781
782 style = GetWindowLongW(hwndDlg, GWL_STYLE);
783 if (!(style & WS_CHILD))
784 AdjustWindowRect(&rc, style, FALSE);
785
786 rc.right -= rc.left;
787 rc.bottom -= rc.top;
788
789 /*
790 * Resize the property sheet.
791 */
792 TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
793 hwndDlg, rc.right, rc.bottom);
794 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
795 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
796 return TRUE;
797 }
798
799 /******************************************************************************
800 * PROPSHEET_AdjustSizeWizard
801 *
802 * Resizes the property sheet to fit the largest page.
803 */
804 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, const PropSheetInfo* psInfo)
805 {
806 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
807 RECT rc, lineRect, dialogRect;
808
809 /* Biggest page size */
810 rc.left = 0;
811 rc.top = 0;
812 rc.right = psInfo->width;
813 rc.bottom = psInfo->height;
814 MapDialogRect(hwndDlg, &rc);
815
816 TRACE("Biggest page %s\n", wine_dbgstr_rect(&rc));
817
818 /* Add space for the buttons row */
819 GetWindowRect(hwndLine, &lineRect);
820 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&lineRect, 2);
821 GetClientRect(hwndDlg, &dialogRect);
822 rc.bottom += dialogRect.bottom - lineRect.top - 1;
823
824 /* Convert the client coordinates to window coordinates */
825 AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);
826
827 /* Resize the property sheet */
828 TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
829 hwndDlg, rc.right, rc.bottom);
830 SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
831 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
832
833 return TRUE;
834 }
835
836 /******************************************************************************
837 * PROPSHEET_AdjustButtons
838 *
839 * Adjusts the buttons' positions.
840 */
841 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, const PropSheetInfo* psInfo)
842 {
843 HWND hwndButton = GetDlgItem(hwndParent, IDOK);
844 RECT rcSheet;
845 int x, y;
846 int num_buttons = 2;
847 int buttonWidth, buttonHeight;
848 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
849
850 if (psInfo->hasApply)
851 num_buttons++;
852
853 if (psInfo->hasHelp)
854 num_buttons++;
855
856 /*
857 * Obtain the size of the buttons.
858 */
859 GetClientRect(hwndButton, &rcSheet);
860 buttonWidth = rcSheet.right;
861 buttonHeight = rcSheet.bottom;
862
863 /*
864 * Get the size of the property sheet.
865 */
866 GetClientRect(hwndParent, &rcSheet);
867
868 /*
869 * All buttons will be at this y coordinate.
870 */
871 y = rcSheet.bottom - (padding.y + buttonHeight);
872
873 /*
874 * Position OK button and make it default.
875 */
876 hwndButton = GetDlgItem(hwndParent, IDOK);
877
878 x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
879
880 SetWindowPos(hwndButton, 0, x, y, 0, 0,
881 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
882
883 SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
884
885
886 /*
887 * Position Cancel button.
888 */
889 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
890
891 x += padding.x + buttonWidth;
892
893 SetWindowPos(hwndButton, 0, x, y, 0, 0,
894 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
895
896 /*
897 * Position Apply button.
898 */
899 hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
900
901 if(psInfo->hasApply)
902 x += padding.x + buttonWidth;
903 else
904 ShowWindow(hwndButton, SW_HIDE);
905
906 SetWindowPos(hwndButton, 0, x, y, 0, 0,
907 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
908 EnableWindow(hwndButton, FALSE);
909
910 /*
911 * Position Help button.
912 */
913 hwndButton = GetDlgItem(hwndParent, IDHELP);
914
915 x += padding.x + buttonWidth;
916 SetWindowPos(hwndButton, 0, x, y, 0, 0,
917 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
918
919 if(!psInfo->hasHelp)
920 ShowWindow(hwndButton, SW_HIDE);
921
922 return TRUE;
923 }
924
925 /******************************************************************************
926 * PROPSHEET_AdjustButtonsWizard
927 *
928 * Adjusts the buttons' positions.
929 */
930 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
931 const PropSheetInfo* psInfo)
932 {
933 HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
934 HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
935 HWND hwndLineHeader = GetDlgItem(hwndParent, IDC_SUNKEN_LINEHEADER);
936 RECT rcSheet;
937 int x, y;
938 int num_buttons = 3;
939 int buttonWidth, buttonHeight, lineHeight, lineWidth;
940 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
941
942 if (psInfo->hasHelp)
943 num_buttons++;
944 if (psInfo->hasFinish)
945 num_buttons++;
946
947 /*
948 * Obtain the size of the buttons.
949 */
950 GetClientRect(hwndButton, &rcSheet);
951 buttonWidth = rcSheet.right;
952 buttonHeight = rcSheet.bottom;
953
954 GetClientRect(hwndLine, &rcSheet);
955 lineHeight = rcSheet.bottom;
956
957 /*
958 * Get the size of the property sheet.
959 */
960 GetClientRect(hwndParent, &rcSheet);
961
962 /*
963 * All buttons will be at this y coordinate.
964 */
965 y = rcSheet.bottom - (padding.y + buttonHeight);
966
967 /*
968 * Position the Back button.
969 */
970 hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
971
972 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)) - buttonWidth;
973
974 SetWindowPos(hwndButton, 0, x, y, 0, 0,
975 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
976
977 /*
978 * Position the Next button.
979 */
980 hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
981
982 x += buttonWidth;
983
984 SetWindowPos(hwndButton, 0, x, y, 0, 0,
985 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
986
987 /*
988 * Position the Finish button.
989 */
990 hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
991
992 if (psInfo->hasFinish)
993 x += padding.x + buttonWidth;
994
995 SetWindowPos(hwndButton, 0, x, y, 0, 0,
996 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
997
998 if (!psInfo->hasFinish)
999 ShowWindow(hwndButton, SW_HIDE);
1000
1001 /*
1002 * Position the Cancel button.
1003 */
1004 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
1005
1006 x += padding.x + buttonWidth;
1007
1008 SetWindowPos(hwndButton, 0, x, y, 0, 0,
1009 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1010
1011 /*
1012 * Position Help button.
1013 */
1014 hwndButton = GetDlgItem(hwndParent, IDHELP);
1015
1016 if (psInfo->hasHelp)
1017 {
1018 x += padding.x + buttonWidth;
1019
1020 SetWindowPos(hwndButton, 0, x, y, 0, 0,
1021 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1022 }
1023 else
1024 ShowWindow(hwndButton, SW_HIDE);
1025
1026 if (psInfo->ppshheader.dwFlags &
1027 (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE))
1028 padding.x = 0;
1029
1030 /*
1031 * Position and resize the sunken line.
1032 */
1033 x = padding.x;
1034 y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
1035
1036 lineWidth = rcSheet.right - (padding.x * 2);
1037 SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
1038 SWP_NOZORDER | SWP_NOACTIVATE);
1039
1040 /*
1041 * Position and resize the header sunken line.
1042 */
1043
1044 SetWindowPos(hwndLineHeader, 0, 0, 0, rcSheet.right, 2,
1045 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1046 if (!(psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)))
1047 ShowWindow(hwndLineHeader, SW_HIDE);
1048
1049 return TRUE;
1050 }
1051
1052 /******************************************************************************
1053 * PROPSHEET_GetPaddingInfo
1054 *
1055 * Returns the layout information.
1056 */
1057 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
1058 {
1059 HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1060 RECT rcTab;
1061 PADDING_INFO padding;
1062
1063 GetWindowRect(hwndTab, &rcTab);
1064 MapWindowPoints( 0, hwndDlg, (POINT *)&rcTab, 2 );
1065
1066 padding.x = rcTab.left;
1067 padding.y = rcTab.top;
1068
1069 return padding;
1070 }
1071
1072 /******************************************************************************
1073 * PROPSHEET_GetPaddingInfoWizard
1074 *
1075 * Returns the layout information.
1076 * Vertical spacing is the distance between the line and the buttons.
1077 * Do NOT use the Help button to gather padding information when it isn't mapped
1078 * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
1079 * for it in this case !
1080 * FIXME: I'm not sure about any other coordinate problems with these evil
1081 * buttons. Fix it in case additional problems appear or maybe calculate
1082 * a padding in a completely different way, as this is somewhat messy.
1083 */
1084 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
1085 psInfo)
1086 {
1087 PADDING_INFO padding;
1088 RECT rc;
1089 HWND hwndControl;
1090 INT idButton;
1091 POINT ptButton, ptLine;
1092
1093 TRACE("\n");
1094 if (psInfo->hasHelp)
1095 {
1096 idButton = IDHELP;
1097 }
1098 else
1099 {
1100 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1101 {
1102 idButton = IDC_NEXT_BUTTON;
1103 }
1104 else
1105 {
1106 /* hopefully this is ok */
1107 idButton = IDCANCEL;
1108 }
1109 }
1110
1111 hwndControl = GetDlgItem(hwndDlg, idButton);
1112 GetWindowRect(hwndControl, &rc);
1113 MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1114 ptButton.x = rc.left;
1115 ptButton.y = rc.top;
1116
1117 /* Line */
1118 hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
1119 GetWindowRect(hwndControl, &rc);
1120 MapWindowPoints( 0, hwndDlg, (POINT *)&rc, 2 );
1121 ptLine.x = rc.left;
1122 ptLine.y = rc.bottom;
1123
1124 padding.y = ptButton.y - ptLine.y;
1125
1126 if (padding.y < 0)
1127 ERR("padding negative ! Please report this !\n");
1128
1129 /* this is most probably not correct, but the best we have now */
1130 padding.x = padding.y;
1131 return padding;
1132 }
1133
1134 /******************************************************************************
1135 * PROPSHEET_CreateTabControl
1136 *
1137 * Insert the tabs in the tab control.
1138 */
1139 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
1140 const PropSheetInfo * psInfo)
1141 {
1142 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1143 TCITEMW item;
1144 int i, nTabs;
1145 int iImage = 0;
1146
1147 TRACE("\n");
1148 item.mask = TCIF_TEXT;
1149 item.cchTextMax = MAX_TABTEXT_LENGTH;
1150
1151 nTabs = psInfo->nPages;
1152
1153 /*
1154 * Set the image list for icons.
1155 */
1156 if (psInfo->hImageList)
1157 {
1158 SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1159 }
1160
1161 SendMessageW(hwndTabCtrl, WM_SETREDRAW, 0, 0);
1162 for (i = 0; i < nTabs; i++)
1163 {
1164 if ( psInfo->proppage[i].hasIcon )
1165 {
1166 item.mask |= TCIF_IMAGE;
1167 item.iImage = iImage++;
1168 }
1169 else
1170 {
1171 item.mask &= ~TCIF_IMAGE;
1172 }
1173
1174 item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1175 SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, i, (LPARAM)&item);
1176 }
1177 SendMessageW(hwndTabCtrl, WM_SETREDRAW, 1, 0);
1178
1179 return TRUE;
1180 }
1181
1182 /******************************************************************************
1183 * PROPSHEET_WizardSubclassProc
1184 *
1185 * Subclassing window procedure for wizard exterior pages to prevent drawing
1186 * background and so drawing above the watermark.
1187 */
1188 static LRESULT CALLBACK
1189 PROPSHEET_WizardSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRef)
1190 {
1191 switch (uMsg)
1192 {
1193 case WM_ERASEBKGND:
1194 return TRUE;
1195
1196 case WM_CTLCOLORSTATIC:
1197 SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
1198 return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
1199 }
1200
1201 return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1202 }
1203
1204 /*
1205 * Get the size of an in-memory Template
1206 *
1207 *( Based on the code of PROPSHEET_CollectPageInfo)
1208 * See also dialog.c/DIALOG_ParseTemplate32().
1209 */
1210
1211 static UINT GetTemplateSize(const DLGTEMPLATE* pTemplate)
1212
1213 {
1214 const WORD* p = (const WORD *)pTemplate;
1215 BOOL istemplateex = (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
1216 WORD nrofitems;
1217 UINT ret;
1218
1219 if (istemplateex)
1220 {
1221 /* DLGTEMPLATEEX (not defined in any std. header file) */
1222
1223 TRACE("is DLGTEMPLATEEX\n");
1224 p++; /* dlgVer */
1225 p++; /* signature */
1226 p += 2; /* help ID */
1227 p += 2; /* ext style */
1228 p += 2; /* style */
1229 }
1230 else
1231 {
1232 /* DLGTEMPLATE */
1233
1234 TRACE("is DLGTEMPLATE\n");
1235 p += 2; /* style */
1236 p += 2; /* ext style */
1237 }
1238
1239 nrofitems = (WORD)*p; p++; /* nb items */
1240 p++; /* x */
1241 p++; /* y */
1242 p++; /* width */
1243 p++; /* height */
1244
1245 /* menu */
1246 switch ((WORD)*p)
1247 {
1248 case 0x0000:
1249 p++;
1250 break;
1251 case 0xffff:
1252 p += 2;
1253 break;
1254 default:
1255 TRACE("menu %s\n",debugstr_w( p ));
1256 p += lstrlenW( p ) + 1;
1257 break;
1258 }
1259
1260 /* class */
1261 switch ((WORD)*p)
1262 {
1263 case 0x0000:
1264 p++;
1265 break;
1266 case 0xffff:
1267 p += 2; /* 0xffff plus predefined window class ordinal value */
1268 break;
1269 default:
1270 TRACE("class %s\n",debugstr_w( p ));
1271 p += lstrlenW( p ) + 1;
1272 break;
1273 }
1274
1275 /* title */
1276 TRACE("title %s\n",debugstr_w( p ));
1277 p += lstrlenW( p ) + 1;
1278
1279 /* font, if DS_SETFONT set */
1280 if ((DS_SETFONT & ((istemplateex)? ((const MyDLGTEMPLATEEX*)pTemplate)->style :
1281 pTemplate->style)))
1282 {
1283 p+=(istemplateex)?3:1;
1284 TRACE("font %s\n",debugstr_w( p ));
1285 p += lstrlenW( p ) + 1; /* the font name */
1286 }
1287
1288 /* now process the DLGITEMTEMPLATE(EX) structs (plus custom data)
1289 * that are following the DLGTEMPLATE(EX) data */
1290 TRACE("%d items\n",nrofitems);
1291 while (nrofitems > 0)
1292 {
1293 p = (WORD*)(((DWORD_PTR)p + 3) & ~3); /* DWORD align */
1294
1295 /* skip header */
1296 p += (istemplateex ? sizeof(MyDLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE))/sizeof(WORD);
1297
1298 /* check class */
1299 switch ((WORD)*p)
1300 {
1301 case 0x0000:
1302 p++;
1303 break;
1304 case 0xffff:
1305 TRACE("class ordinal 0x%08x\n",*(const DWORD*)p);
1306 p += 2;
1307 break;
1308 default:
1309 TRACE("class %s\n",debugstr_w( p ));
1310 p += lstrlenW( p ) + 1;
1311 break;
1312 }
1313
1314 /* check title text */
1315 switch ((WORD)*p)
1316 {
1317 case 0x0000:
1318 p++;
1319 break;
1320 case 0xffff:
1321 TRACE("text ordinal 0x%08x\n",*(const DWORD*)p);
1322 p += 2;
1323 break;
1324 default:
1325 TRACE("text %s\n",debugstr_w( p ));
1326 p += lstrlenW( p ) + 1;
1327 break;
1328 }
1329 p += *p / sizeof(WORD) + 1; /* Skip extra data */
1330 --nrofitems;
1331 }
1332
1333 ret = (p - (const WORD*)pTemplate) * sizeof(WORD);
1334 TRACE("%p %p size 0x%08x\n", p, pTemplate, ret);
1335 return ret;
1336 }
1337
1338 /******************************************************************************
1339 * PROPSHEET_CreatePage
1340 *
1341 * Creates a page.
1342 */
1343 static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1344 int index,
1345 const PropSheetInfo * psInfo,
1346 LPCPROPSHEETPAGEW ppshpage)
1347 {
1348 const DLGTEMPLATE* pTemplate;
1349 HWND hwndPage;
1350 DWORD resSize;
1351 DLGTEMPLATE* pTemplateCopy = NULL;
1352
1353 TRACE("index %d\n", index);
1354
1355 if (ppshpage == NULL)
1356 {
1357 return FALSE;
1358 }
1359
1360 if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1361 {
1362 pTemplate = ppshpage->u.pResource;
1363 resSize = GetTemplateSize(pTemplate);
1364 }
1365 else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1366 {
1367 HRSRC hResource;
1368 HANDLE hTemplate;
1369
1370 hResource = FindResourceW(ppshpage->hInstance,
1371 ppshpage->u.pszTemplate,
1372 (LPWSTR)RT_DIALOG);
1373 if(!hResource)
1374 return FALSE;
1375
1376 resSize = SizeofResource(ppshpage->hInstance, hResource);
1377
1378 hTemplate = LoadResource(ppshpage->hInstance, hResource);
1379 if(!hTemplate)
1380 return FALSE;
1381
1382 pTemplate = LockResource(hTemplate);
1383 /*
1384 * Make a copy of the dialog template to make it writable
1385 */
1386 }
1387 else
1388 {
1389 HRSRC hResource;
1390 HANDLE hTemplate;
1391
1392 hResource = FindResourceA(ppshpage->hInstance,
1393 (LPCSTR)ppshpage->u.pszTemplate,
1394 (LPSTR)RT_DIALOG);
1395 if(!hResource)
1396 return FALSE;
1397
1398 resSize = SizeofResource(ppshpage->hInstance, hResource);
1399
1400 hTemplate = LoadResource(ppshpage->hInstance, hResource);
1401 if(!hTemplate)
1402 return FALSE;
1403
1404 pTemplate = LockResource(hTemplate);
1405 /*
1406 * Make a copy of the dialog template to make it writable
1407 */
1408 }
1409 pTemplateCopy = Alloc(resSize);
1410 if (!pTemplateCopy)
1411 return FALSE;
1412
1413 TRACE("copying pTemplate %p into pTemplateCopy %p (%d)\n", pTemplate, pTemplateCopy, resSize);
1414 memcpy(pTemplateCopy, pTemplate, resSize);
1415
1416 if (((MyDLGTEMPLATEEX*)pTemplateCopy)->signature == 0xFFFF)
1417 {
1418 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1419 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~DS_MODALFRAME;
1420 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_CAPTION;
1421 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_SYSMENU;
1422 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_POPUP;
1423 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_DISABLED;
1424 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_VISIBLE;
1425 ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_THICKFRAME;
1426
1427 ((MyDLGTEMPLATEEX*)pTemplateCopy)->exStyle |= WS_EX_CONTROLPARENT;
1428 }
1429 else
1430 {
1431 pTemplateCopy->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1432 pTemplateCopy->style &= ~DS_MODALFRAME;
1433 pTemplateCopy->style &= ~WS_CAPTION;
1434 pTemplateCopy->style &= ~WS_SYSMENU;
1435 pTemplateCopy->style &= ~WS_POPUP;
1436 pTemplateCopy->style &= ~WS_DISABLED;
1437 pTemplateCopy->style &= ~WS_VISIBLE;
1438 pTemplateCopy->style &= ~WS_THICKFRAME;
1439
1440 pTemplateCopy->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1441 }
1442
1443 if (psInfo->proppage[index].useCallback)
1444 (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1445 (LPPROPSHEETPAGEW)ppshpage);
1446
1447 if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1448 hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1449 pTemplateCopy,
1450 hwndParent,
1451 ppshpage->pfnDlgProc,
1452 (LPARAM)ppshpage);
1453 else
1454 hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1455 pTemplateCopy,
1456 hwndParent,
1457 ppshpage->pfnDlgProc,
1458 (LPARAM)ppshpage);
1459 /* Free a no more needed copy */
1460 Free(pTemplateCopy);
1461
1462 if(!hwndPage)
1463 return FALSE;
1464
1465 psInfo->proppage[index].hwndPage = hwndPage;
1466
1467 /* Subclass exterior wizard pages */
1468 if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
1469 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1470 (ppshpage->dwFlags & PSP_HIDEHEADER))
1471 {
1472 SetWindowSubclass(hwndPage, PROPSHEET_WizardSubclassProc, 1,
1473 (DWORD_PTR)ppshpage);
1474 }
1475 if (!(psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD))
1476 EnableThemeDialogTexture (hwndPage, ETDT_ENABLETAB);
1477
1478 return TRUE;
1479 }
1480
1481 /******************************************************************************
1482 * PROPSHEET_LoadWizardBitmaps
1483 *
1484 * Loads the watermark and header bitmaps for a wizard.
1485 */
1486 static VOID PROPSHEET_LoadWizardBitmaps(PropSheetInfo *psInfo)
1487 {
1488 if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD))
1489 {
1490 /* if PSH_USEHBMWATERMARK is not set, load the resource from pszbmWatermark
1491 and put the HBITMAP in hbmWatermark. Thus all the rest of the code always
1492 considers hbmWatermark as valid. */
1493 if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1494 !(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK))
1495 {
1496 psInfo->ppshheader.u4.hbmWatermark =
1497 CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
1498 }
1499
1500 /* Same behavior as for watermarks */
1501 if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
1502 !(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER))
1503 {
1504 psInfo->ppshheader.u5.hbmHeader =
1505 CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u5.pszbmHeader, 0, NULL, 0);
1506 }
1507 }
1508 }
1509
1510
1511 /******************************************************************************
1512 * PROPSHEET_ShowPage
1513 *
1514 * Displays or creates the specified page.
1515 */
1516 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1517 {
1518 HWND hwndTabCtrl;
1519 HWND hwndLineHeader;
1520 HWND control;
1521 LPCPROPSHEETPAGEW ppshpage;
1522
1523 TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1524 if (index == psInfo->active_page)
1525 {
1526 if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1527 SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1528 return TRUE;
1529 }
1530
1531 ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1532 if (psInfo->proppage[index].hwndPage == 0)
1533 {
1534 PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1535 }
1536
1537 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1538 {
1539 PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
1540 psInfo->proppage[index].pszText);
1541
1542 control = GetNextDlgTabItem(psInfo->proppage[index].hwndPage, NULL, FALSE);
1543 if(control != NULL)
1544 SetFocus(control);
1545 }
1546
1547 if (psInfo->active_page != -1)
1548 ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1549
1550 ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1551
1552 /* Synchronize current selection with tab control
1553 * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1554 hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1555 SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1556
1557 psInfo->active_page = index;
1558 psInfo->activeValid = TRUE;
1559
1560 if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW) )
1561 {
1562 hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
1563 ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1564
1565 if ((ppshpage->dwFlags & PSP_HIDEHEADER) || (!(psInfo->ppshheader.dwFlags & PSH_HEADER)) )
1566 ShowWindow(hwndLineHeader, SW_HIDE);
1567 else
1568 ShowWindow(hwndLineHeader, SW_SHOW);
1569 }
1570
1571 return TRUE;
1572 }
1573
1574 /******************************************************************************
1575 * PROPSHEET_Back
1576 */
1577 static BOOL PROPSHEET_Back(HWND hwndDlg)
1578 {
1579 PSHNOTIFY psn;
1580 HWND hwndPage;
1581 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1582 LRESULT result;
1583 int idx;
1584
1585 TRACE("active_page %d\n", psInfo->active_page);
1586 if (psInfo->active_page < 0)
1587 return FALSE;
1588
1589 psn.hdr.code = PSN_WIZBACK;
1590 psn.hdr.hwndFrom = hwndDlg;
1591 psn.hdr.idFrom = 0;
1592 psn.lParam = 0;
1593
1594 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1595
1596 result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1597 if (result == -1)
1598 return FALSE;
1599 else if (result == 0)
1600 idx = psInfo->active_page - 1;
1601 else
1602 idx = PROPSHEET_FindPageByResId(psInfo, result);
1603
1604 if (idx >= 0 && idx < psInfo->nPages)
1605 {
1606 if (PROPSHEET_CanSetCurSel(hwndDlg))
1607 {
1608 SetFocus(GetDlgItem(hwndDlg, IDC_BACK_BUTTON));
1609 SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
1610 PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1611 }
1612 }
1613 return TRUE;
1614 }
1615
1616 /******************************************************************************
1617 * PROPSHEET_Next
1618 */
1619 static BOOL PROPSHEET_Next(HWND hwndDlg)
1620 {
1621 PSHNOTIFY psn;
1622 HWND hwndPage;
1623 LRESULT msgResult = 0;
1624 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1625 int idx;
1626
1627 TRACE("active_page %d\n", psInfo->active_page);
1628 if (psInfo->active_page < 0)
1629 return FALSE;
1630
1631 psn.hdr.code = PSN_WIZNEXT;
1632 psn.hdr.hwndFrom = hwndDlg;
1633 psn.hdr.idFrom = 0;
1634 psn.lParam = 0;
1635
1636 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1637
1638 msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1639 if (msgResult == -1)
1640 return FALSE;
1641 else if (msgResult == 0)
1642 idx = psInfo->active_page + 1;
1643 else
1644 idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1645
1646 if (idx < psInfo->nPages )
1647 {
1648 if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1649 {
1650 SetFocus(GetDlgItem(hwndDlg, IDC_NEXT_BUTTON));
1651 SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1652 PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1653 }
1654 }
1655
1656 return TRUE;
1657 }
1658
1659 /******************************************************************************
1660 * PROPSHEET_Finish
1661 */
1662 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1663 {
1664 PSHNOTIFY psn;
1665 HWND hwndPage;
1666 LRESULT msgResult = 0;
1667 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1668
1669 TRACE("active_page %d\n", psInfo->active_page);
1670 if (psInfo->active_page < 0)
1671 return FALSE;
1672
1673 psn.hdr.code = PSN_WIZFINISH;
1674 psn.hdr.hwndFrom = hwndDlg;
1675 psn.hdr.idFrom = 0;
1676 psn.lParam = 0;
1677
1678 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1679
1680 msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1681
1682 TRACE("msg result %ld\n", msgResult);
1683
1684 if (msgResult != 0)
1685 return FALSE;
1686
1687 if (psInfo->result == 0)
1688 psInfo->result = IDOK;
1689 if (psInfo->isModeless)
1690 psInfo->activeValid = FALSE;
1691 else
1692 psInfo->ended = TRUE;
1693
1694 return TRUE;
1695 }
1696
1697 /******************************************************************************
1698 * PROPSHEET_Apply
1699 */
1700 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1701 {
1702 int i;
1703 HWND hwndPage;
1704 PSHNOTIFY psn;
1705 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1706
1707 TRACE("active_page %d\n", psInfo->active_page);
1708 if (psInfo->active_page < 0)
1709 return FALSE;
1710
1711 psn.hdr.hwndFrom = hwndDlg;
1712 psn.hdr.idFrom = 0;
1713 psn.lParam = 0;
1714
1715
1716 /*
1717 * Send PSN_KILLACTIVE to the current page.
1718 */
1719 psn.hdr.code = PSN_KILLACTIVE;
1720
1721 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1722
1723 if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1724 return FALSE;
1725
1726 /*
1727 * Send PSN_APPLY to all pages.
1728 */
1729 psn.hdr.code = PSN_APPLY;
1730 psn.lParam = lParam;
1731
1732 for (i = 0; i < psInfo->nPages; i++)
1733 {
1734 hwndPage = psInfo->proppage[i].hwndPage;
1735 if (hwndPage)
1736 {
1737 switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1738 {
1739 case PSNRET_INVALID:
1740 PROPSHEET_ShowPage(hwndDlg, i, psInfo);
1741 /* fall through */
1742 case PSNRET_INVALID_NOCHANGEPAGE:
1743 return FALSE;
1744 }
1745 }
1746 }
1747
1748 if(lParam)
1749 {
1750 psInfo->activeValid = FALSE;
1751 }
1752 else if(psInfo->active_page >= 0)
1753 {
1754 psn.hdr.code = PSN_SETACTIVE;
1755 psn.lParam = 0;
1756 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1757 SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1758 }
1759
1760 return TRUE;
1761 }
1762
1763 /******************************************************************************
1764 * PROPSHEET_Cancel
1765 */
1766 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1767 {
1768 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1769 HWND hwndPage;
1770 PSHNOTIFY psn;
1771 int i;
1772
1773 TRACE("active_page %d\n", psInfo->active_page);
1774 if (psInfo->active_page < 0)
1775 return;
1776
1777 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1778 psn.hdr.code = PSN_QUERYCANCEL;
1779 psn.hdr.hwndFrom = hwndDlg;
1780 psn.hdr.idFrom = 0;
1781 psn.lParam = 0;
1782
1783 if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1784 return;
1785
1786 psn.hdr.code = PSN_RESET;
1787 psn.lParam = lParam;
1788
1789 for (i = 0; i < psInfo->nPages; i++)
1790 {
1791 hwndPage = psInfo->proppage[i].hwndPage;
1792
1793 if (hwndPage)
1794 SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1795 }
1796
1797 if (psInfo->isModeless)
1798 {
1799 /* makes PSM_GETCURRENTPAGEHWND return NULL */
1800 psInfo->activeValid = FALSE;
1801 }
1802 else
1803 psInfo->ended = TRUE;
1804 }
1805
1806 /******************************************************************************
1807 * PROPSHEET_Help
1808 */
1809 static void PROPSHEET_Help(HWND hwndDlg)
1810 {
1811 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1812 HWND hwndPage;
1813 PSHNOTIFY psn;
1814
1815 TRACE("active_page %d\n", psInfo->active_page);
1816 if (psInfo->active_page < 0)
1817 return;
1818
1819 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1820 psn.hdr.code = PSN_HELP;
1821 psn.hdr.hwndFrom = hwndDlg;
1822 psn.hdr.idFrom = 0;
1823 psn.lParam = 0;
1824
1825 SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1826 }
1827
1828 /******************************************************************************
1829 * PROPSHEET_Changed
1830 */
1831 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1832 {
1833 int i;
1834 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1835
1836 TRACE("\n");
1837 if (!psInfo) return;
1838 /*
1839 * Set the dirty flag of this page.
1840 */
1841 for (i = 0; i < psInfo->nPages; i++)
1842 {
1843 if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1844 psInfo->proppage[i].isDirty = TRUE;
1845 }
1846
1847 /*
1848 * Enable the Apply button.
1849 */
1850 if (psInfo->hasApply)
1851 {
1852 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1853
1854 EnableWindow(hwndApplyBtn, TRUE);
1855 }
1856 }
1857
1858 /******************************************************************************
1859 * PROPSHEET_UnChanged
1860 */
1861 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1862 {
1863 int i;
1864 BOOL noPageDirty = TRUE;
1865 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1866 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1867
1868 TRACE("\n");
1869 if ( !psInfo ) return;
1870 for (i = 0; i < psInfo->nPages; i++)
1871 {
1872 /* set the specified page as clean */
1873 if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1874 psInfo->proppage[i].isDirty = FALSE;
1875
1876 /* look to see if there are any dirty pages */
1877 if (psInfo->proppage[i].isDirty)
1878 noPageDirty = FALSE;
1879 }
1880
1881 /*
1882 * Disable Apply button.
1883 */
1884 if (noPageDirty)
1885 EnableWindow(hwndApplyBtn, FALSE);
1886 }
1887
1888 /******************************************************************************
1889 * PROPSHEET_PressButton
1890 */
1891 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1892 {
1893 TRACE("buttonID %d\n", buttonID);
1894 switch (buttonID)
1895 {
1896 case PSBTN_APPLYNOW:
1897 PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1898 break;
1899 case PSBTN_BACK:
1900 PROPSHEET_Back(hwndDlg);
1901 break;
1902 case PSBTN_CANCEL:
1903 PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1904 break;
1905 case PSBTN_FINISH:
1906 PROPSHEET_Finish(hwndDlg);
1907 break;
1908 case PSBTN_HELP:
1909 PROPSHEET_DoCommand(hwndDlg, IDHELP);
1910 break;
1911 case PSBTN_NEXT:
1912 PROPSHEET_Next(hwndDlg);
1913 break;
1914 case PSBTN_OK:
1915 PROPSHEET_DoCommand(hwndDlg, IDOK);
1916 break;
1917 default:
1918 FIXME("Invalid button index %d\n", buttonID);
1919 }
1920 }
1921
1922
1923 /*************************************************************************
1924 * BOOL PROPSHEET_CanSetCurSel [Internal]
1925 *
1926 * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1927 *
1928 * PARAMS
1929 * hwndDlg [I] handle to a Dialog hWnd
1930 *
1931 * RETURNS
1932 * TRUE if Current Selection can change
1933 *
1934 * NOTES
1935 */
1936 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1937 {
1938 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1939 HWND hwndPage;
1940 PSHNOTIFY psn;
1941 BOOL res = FALSE;
1942
1943 if (!psInfo)
1944 {
1945 res = FALSE;
1946 goto end;
1947 }
1948
1949 TRACE("active_page %d\n", psInfo->active_page);
1950 if (psInfo->active_page < 0)
1951 {
1952 res = TRUE;
1953 goto end;
1954 }
1955
1956 /*
1957 * Notify the current page.
1958 */
1959 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1960 psn.hdr.code = PSN_KILLACTIVE;
1961 psn.hdr.hwndFrom = hwndDlg;
1962 psn.hdr.idFrom = 0;
1963 psn.lParam = 0;
1964
1965 res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1966
1967 end:
1968 TRACE("<-- %d\n", res);
1969 return res;
1970 }
1971
1972 /******************************************************************************
1973 * PROPSHEET_SetCurSel
1974 */
1975 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1976 int index,
1977 int skipdir,
1978 HPROPSHEETPAGE hpage
1979 )
1980 {
1981 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1982 HWND hwndHelp = GetDlgItem(hwndDlg, IDHELP);
1983 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1984
1985 TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1986
1987 index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
1988
1989 if (index < 0 || index >= psInfo->nPages)
1990 {
1991 TRACE("Could not find page to select!\n");
1992 return FALSE;
1993 }
1994
1995 /* unset active page while doing this transition. */
1996 if (psInfo->active_page != -1)
1997 ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1998 psInfo->active_page = -1;
1999
2000 while (1) {
2001 int result;
2002 PSHNOTIFY psn;
2003 RECT rc;
2004 LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2005
2006 if (hwndTabControl)
2007 SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);
2008
2009 psn.hdr.code = PSN_SETACTIVE;
2010 psn.hdr.hwndFrom = hwndDlg;
2011 psn.hdr.idFrom = 0;
2012 psn.lParam = 0;
2013
2014 if (!psInfo->proppage[index].hwndPage) {
2015 if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage)) {
2016 PROPSHEET_RemovePage(hwndDlg, index, NULL);
2017 if(index >= psInfo->nPages)
2018 index--;
2019 if(index < 0)
2020 return FALSE;
2021 continue;
2022 }
2023 }
2024
2025 /* Resize the property sheet page to the fit in the Tab control
2026 * (for regular property sheets) or to fit in the client area (for
2027 * wizards).
2028 * NOTE: The resizing happens every time the page is selected and
2029 * not only when it's created (some applications depend on it). */
2030 PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2031 TRACE("setting page %p, rc (%s) w=%d, h=%d\n",
2032 psInfo->proppage[index].hwndPage, wine_dbgstr_rect(&rc),
2033 rc.right - rc.left, rc.bottom - rc.top);
2034 SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP,
2035 rc.left, rc.top,
2036 rc.right - rc.left, rc.bottom - rc.top, 0);
2037
2038 result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2039 if (!result)
2040 break;
2041 if (result == -1) {
2042 index+=skipdir;
2043 if (index < 0) {
2044 index = 0;
2045 WARN("Tried to skip before first property sheet page!\n");
2046 break;
2047 }
2048 if (index >= psInfo->nPages) {
2049 WARN("Tried to skip after last property sheet page!\n");
2050 index = psInfo->nPages-1;
2051 break;
2052 }
2053 }
2054 else if (result != 0)
2055 {
2056 int old_index = index;
2057 index = PROPSHEET_FindPageByResId(psInfo, result);
2058 if(index >= psInfo->nPages) {
2059 index = old_index;
2060 WARN("Tried to skip to nonexistent page by res id\n");
2061 break;
2062 }
2063 continue;
2064 }
2065 }
2066
2067 /* Invalidate the header area */
2068 if ( (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
2069 (psInfo->ppshheader.dwFlags & PSH_HEADER) )
2070 {
2071 HWND hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
2072 RECT r;
2073
2074 GetClientRect(hwndLineHeader, &r);
2075 MapWindowPoints(hwndLineHeader, hwndDlg, (LPPOINT) &r, 2);
2076 SetRect(&r, 0, 0, r.right + 1, r.top - 1);
2077
2078 InvalidateRect(hwndDlg, &r, TRUE);
2079 }
2080
2081 /*
2082 * Display the new page.
2083 */
2084 PROPSHEET_ShowPage(hwndDlg, index, psInfo);
2085
2086 if (psInfo->proppage[index].hasHelp)
2087 EnableWindow(hwndHelp, TRUE);
2088 else
2089 EnableWindow(hwndHelp, FALSE);
2090
2091 return TRUE;
2092 }
2093
2094 /******************************************************************************
2095 * PROPSHEET_SetCurSelId
2096 *
2097 * Selects the page, specified by resource id.
2098 */
2099 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
2100 {
2101 int idx;
2102 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2103
2104 idx = PROPSHEET_FindPageByResId(psInfo, id);
2105 if (idx < psInfo->nPages )
2106 {
2107 if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
2108 PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
2109 }
2110 }
2111
2112 /******************************************************************************
2113 * PROPSHEET_SetTitleA
2114 */
2115 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
2116 {
2117 if(!IS_INTRESOURCE(lpszText))
2118 {
2119 WCHAR szTitle[256];
2120 MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
2121 szTitle, sizeof(szTitle)/sizeof(WCHAR));
2122 PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
2123 }
2124 else
2125 {
2126 PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
2127 }
2128 }
2129
2130 /******************************************************************************
2131 * PROPSHEET_SetTitleW
2132 */
2133 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
2134 {
2135 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2136 WCHAR szTitle[256];
2137
2138 TRACE("%s (style %08x)\n", debugstr_w(lpszText), dwStyle);
2139 if (IS_INTRESOURCE(lpszText)) {
2140 if (!LoadStringW(psInfo->ppshheader.hInstance,
2141 LOWORD(lpszText), szTitle, sizeof(szTitle)/sizeof(szTitle[0])))
2142 return;
2143 lpszText = szTitle;
2144 }
2145 if (dwStyle & PSH_PROPTITLE)
2146 {
2147 WCHAR* dest;
2148 int lentitle = strlenW(lpszText);
2149 int lenprop = strlenW(psInfo->strPropertiesFor);
2150
2151 dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2152 wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
2153
2154 SetWindowTextW(hwndDlg, dest);
2155 Free(dest);
2156 }
2157 else
2158 SetWindowTextW(hwndDlg, lpszText);
2159 }
2160
2161 /******************************************************************************
2162 * PROPSHEET_SetFinishTextA
2163 */
2164 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
2165 {
2166 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2167 HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2168
2169 TRACE("'%s'\n", lpszText);
2170 /* Set text, show and enable the Finish button */
2171 SetWindowTextA(hwndButton, lpszText);
2172 ShowWindow(hwndButton, SW_SHOW);
2173 EnableWindow(hwndButton, TRUE);
2174
2175 /* Make it default pushbutton */
2176 SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2177
2178 /* Hide Back button */
2179 hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2180 ShowWindow(hwndButton, SW_HIDE);
2181
2182 if (!psInfo->hasFinish)
2183 {
2184 /* Hide Next button */
2185 hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2186 ShowWindow(hwndButton, SW_HIDE);
2187 }
2188 }
2189
2190 /******************************************************************************
2191 * PROPSHEET_SetFinishTextW
2192 */
2193 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
2194 {
2195 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2196 HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2197
2198 TRACE("%s\n", debugstr_w(lpszText));
2199 /* Set text, show and enable the Finish button */
2200 SetWindowTextW(hwndButton, lpszText);
2201 ShowWindow(hwndButton, SW_SHOW);
2202 EnableWindow(hwndButton, TRUE);
2203
2204 /* Make it default pushbutton */
2205 SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2206
2207 /* Hide Back button */
2208 hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2209 ShowWindow(hwndButton, SW_HIDE);
2210
2211 if (!psInfo->hasFinish)
2212 {
2213 /* Hide Next button */
2214 hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2215 ShowWindow(hwndButton, SW_HIDE);
2216 }
2217 }
2218
2219 /******************************************************************************
2220 * PROPSHEET_QuerySiblings
2221 */
2222 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
2223 WPARAM wParam, LPARAM lParam)
2224 {
2225 int i = 0;
2226 HWND hwndPage;
2227 LRESULT msgResult = 0;
2228 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2229
2230 while ((i < psInfo->nPages) && (msgResult == 0))
2231 {
2232 hwndPage = psInfo->proppage[i].hwndPage;
2233 msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2234 i++;
2235 }
2236
2237 return msgResult;
2238 }
2239
2240
2241 /******************************************************************************
2242 * PROPSHEET_AddPage
2243 */
2244 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
2245 HPROPSHEETPAGE hpage)
2246 {
2247 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2248 TRACE("hpage %p\n", hpage);
2249 return PROPSHEET_InsertPage(hwndDlg, (HPROPSHEETPAGE)(ULONG_PTR)psInfo->nPages, hpage);
2250 }
2251
2252 /******************************************************************************
2253 * PROPSHEET_RemovePage
2254 */
2255 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
2256 int index,
2257 HPROPSHEETPAGE hpage)
2258 {
2259 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2260 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2261 PropPageInfo* oldPages;
2262
2263 TRACE("index %d, hpage %p\n", index, hpage);
2264 if (!psInfo) {
2265 return FALSE;
2266 }
2267
2268 index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
2269
2270 /* Make sure that index is within range */
2271 if (index < 0 || index >= psInfo->nPages)
2272 {
2273 TRACE("Could not find page to remove!\n");
2274 return FALSE;
2275 }
2276
2277 TRACE("total pages %d removing page %d active page %d\n",
2278 psInfo->nPages, index, psInfo->active_page);
2279 /*
2280 * Check if we're removing the active page.
2281 */
2282 if (index == psInfo->active_page)
2283 {
2284 if (psInfo->nPages > 1)
2285 {
2286 if (index > 0)
2287 {
2288 /* activate previous page */
2289 PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2290 }
2291 else
2292 {
2293 /* activate the next page */
2294 PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2295 psInfo->active_page = index;
2296 }
2297 }
2298 else
2299 {
2300 psInfo->active_page = -1;
2301 if (!psInfo->isModeless)
2302 {
2303 psInfo->ended = TRUE;
2304 return TRUE;
2305 }
2306 }
2307 }
2308 else if (index < psInfo->active_page)
2309 psInfo->active_page--;
2310
2311 /* Unsubclass the page dialog window */
2312 if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD) &&
2313 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2314 ((PROPSHEETPAGEW*)psInfo->proppage[index].hpage)->dwFlags & PSP_HIDEHEADER))
2315 {
2316 RemoveWindowSubclass(psInfo->proppage[index].hwndPage,
2317 PROPSHEET_WizardSubclassProc, 1);
2318 }
2319
2320 /* Destroy page dialog window */
2321 DestroyWindow(psInfo->proppage[index].hwndPage);
2322
2323 /* Free page resources */
2324 if(psInfo->proppage[index].hpage)
2325 {
2326 PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2327
2328 if (psp->dwFlags & PSP_USETITLE)
2329 Free ((LPVOID)psInfo->proppage[index].pszText);
2330
2331 DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2332 }
2333
2334 /* Remove the tab */
2335 SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2336
2337 oldPages = psInfo->proppage;
2338 psInfo->nPages--;
2339 psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2340
2341 if (index > 0)
2342 memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2343
2344 if (index < psInfo->nPages)
2345 memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2346 (psInfo->nPages - index) * sizeof(PropPageInfo));
2347
2348 Free(oldPages);
2349
2350 return FALSE;
2351 }
2352
2353 /******************************************************************************
2354 * PROPSHEET_SetWizButtons
2355 *
2356 * This code will work if (and assumes that) the Next button is on top of the
2357 * Finish button. ie. Finish comes after Next in the Z order.
2358 * This means make sure the dialog template reflects this.
2359 *
2360 */
2361 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2362 {
2363 PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2364 HWND hwndBack = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2365 HWND hwndNext = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2366 HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2367 BOOL enable_finish = ((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH);
2368
2369 #ifdef __REACTOS__
2370 HWND hwndCancel = GetDlgItem(hwndDlg, IDCANCEL);
2371 INT iDefItem = 0;
2372 HWND hwndFocus;
2373 #endif
2374
2375 TRACE("%d\n", dwFlags);
2376
2377 EnableWindow(hwndBack, dwFlags & PSWIZB_BACK);
2378 EnableWindow(hwndNext, dwFlags & PSWIZB_NEXT);
2379 EnableWindow(hwndFinish, enable_finish);
2380
2381 #ifndef __REACTOS__
2382 /* set the default pushbutton to an enabled button */
2383 if (enable_finish)
2384 SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2385 else if (dwFlags & PSWIZB_NEXT)
2386 SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2387 else if (dwFlags & PSWIZB_BACK)
2388 SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
2389 else
2390 SendMessageW(hwndDlg, DM_SETDEFID, IDCANCEL, 0);
2391 #endif
2392
2393 if (!psInfo->hasFinish)
2394 {
2395 if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2396 {
2397 /* Hide the Next button */
2398 ShowWindow(hwndNext, SW_HIDE);
2399
2400 /* Show the Finish button */
2401 ShowWindow(hwndFinish, SW_SHOW);
2402 }
2403 else
2404 {
2405 /* Hide the Finish button */
2406 ShowWindow(hwndFinish, SW_HIDE);
2407 /* Show the Next button */
2408 ShowWindow(hwndNext, SW_SHOW);
2409 }
2410 }
2411
2412 #ifdef __REACTOS__
2413 /* set the default pushbutton to an enabled button */
2414 if (((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH))
2415 iDefItem = IDC_FINISH_BUTTON;
2416 else if (dwFlags & PSWIZB_NEXT)
2417 iDefItem = IDC_NEXT_BUTTON;
2418 else if (dwFlags & PSWIZB_BACK)
2419 iDefItem = IDC_BACK_BUTTON;
2420 else
2421 iDefItem = IDCANCEL;
2422 SendMessageW(hwndDlg, DM_SETDEFID, iDefItem, 0);
2423
2424 /* Set focus if no control has it */
2425 hwndFocus = GetFocus();
2426 if (!hwndFocus || hwndFocus == hwndCancel)
2427 SetFocus(GetDlgItem(hwndDlg, iDefItem));
2428 #endif
2429
2430 }
2431
2432 /******************************************************************************
2433 * PROPSHEET_InsertPage
2434 */
2435 static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
2436 {
2437 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2438 PropPageInfo * ppi, * prev_ppi = psInfo->proppage;
2439 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2440 LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2441 TCITEMW item;
2442 int index;
2443
2444 TRACE("hwndDlg %p, hpageInsertAfter %p, hpage %p\n", hwndDlg, hpageInsertAfter, hpage);
2445
2446 if (IS_INTRESOURCE(hpageInsertAfter))
2447 index = LOWORD(hpageInsertAfter);
2448 else
2449 {
2450 index = PROPSHEET_GetPageIndex(hpageInsertAfter, psInfo, -1);
2451 if (index < 0)
2452 {
2453 TRACE("Could not find page to insert after!\n");
2454 return FALSE;
2455 }
2456 index++;
2457 }
2458
2459 if (index > psInfo->nPages)
2460 index = psInfo->nPages;
2461
2462 /*
2463 * Allocate a new PropPageInfo entry.
2464 */
2465 ppi = Alloc(sizeof(PropPageInfo) * (psInfo->nPages + 1));
2466 if (!ppi)
2467 return FALSE;
2468
2469 /*
2470 * Fill in a new PropPageInfo entry.
2471 */
2472 if (index > 0)
2473 memcpy(ppi, prev_ppi, index * sizeof(PropPageInfo));
2474 memset(&ppi[index], 0, sizeof(PropPageInfo));
2475 if (index < psInfo->nPages)
2476 memcpy(&ppi[index + 1], &prev_ppi[index], (psInfo->nPages - index) * sizeof(PropPageInfo));
2477 psInfo->proppage = ppi;
2478
2479 if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, index, FALSE))
2480 {
2481 psInfo->proppage = prev_ppi;
2482 Free(ppi);
2483 return FALSE;
2484 }
2485
2486 psInfo->proppage[index].hpage = hpage;
2487
2488 if (ppsp->dwFlags & PSP_PREMATURE)
2489 {
2490 /* Create the page but don't show it */
2491 if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppsp))
2492 {
2493 psInfo->proppage = prev_ppi;
2494 Free(ppi);
2495 return FALSE;
2496 }
2497 }
2498
2499 Free(prev_ppi);
2500 psInfo->nPages++;
2501 if (index <= psInfo->active_page)
2502 psInfo->active_page++;
2503
2504 /*
2505 * Add a new tab to the tab control.
2506 */
2507 item.mask = TCIF_TEXT;
2508 item.pszText = (LPWSTR) psInfo->proppage[index].pszText;
2509 item.cchTextMax = MAX_TABTEXT_LENGTH;
2510
2511 if (psInfo->hImageList)
2512 {
2513 SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
2514 }
2515
2516 if (psInfo->proppage[index].hasIcon)
2517 {
2518 item.mask |= TCIF_IMAGE;
2519 item.iImage = index;
2520 }
2521
2522 SendMessageW(hwndTabControl, TCM_INSERTITEMW, index,
2523 (LPARAM)&item);
2524
2525 /* If it is the only page - show it */
2526 if (psInfo->nPages == 1)
2527 PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2528
2529 return TRUE;
2530 }
2531
2532 /******************************************************************************
2533 * PROPSHEET_SetHeaderTitleW
2534 */
2535 static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderTitle)
2536 {
2537 FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderTitle));
2538 }
2539
2540 /******************************************************************************
2541 * PROPSHEET_SetHeaderTitleA
2542 */
2543 static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderTitle)
2544 {
2545 FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderTitle));
2546 }
2547
2548 /******************************************************************************
2549 * PROPSHEET_SetHeaderSubTitleW
2550 */
2551 static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderSubTitle)
2552 {
2553 FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderSubTitle));
2554 }
2555
2556 /******************************************************************************
2557 * PROPSHEET_SetHeaderSubTitleA
2558 */
2559 static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderSubTitle)
2560 {
2561 FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderSubTitle));
2562 }
2563
2564 /******************************************************************************
2565 * PROPSHEET_HwndToIndex
2566 */
2567 static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
2568 {
2569 int index;
2570 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2571
2572 TRACE("(%p, %p)\n", hwndDlg, hPageDlg);
2573
2574 for (index = 0; index < psInfo->nPages; index++)
2575 if (psInfo->proppage[index].hwndPage == hPageDlg)
2576 return index;
2577 WARN("%p not found\n", hPageDlg);
2578 return -1;
2579 }
2580
2581 /******************************************************************************
2582 * PROPSHEET_IndexToHwnd
2583 */
2584 static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
2585 {
2586 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2587 TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2588 if (!psInfo)
2589 return 0;
2590 if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2591 WARN("%d out of range.\n", iPageIndex);
2592 return 0;
2593 }
2594 return (LRESULT)psInfo->proppage[iPageIndex].hwndPage;
2595 }
2596
2597 /******************************************************************************
2598 * PROPSHEET_PageToIndex
2599 */
2600 static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
2601 {
2602 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2603
2604 TRACE("(%p, %p)\n", hwndDlg, hPage);
2605
2606 return PROPSHEET_GetPageIndex(hPage, psInfo, -1);
2607 }
2608
2609 /******************************************************************************
2610 * PROPSHEET_IndexToPage
2611 */
2612 static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
2613 {
2614 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2615 TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2616 if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2617 WARN("%d out of range.\n", iPageIndex);
2618 return 0;
2619 }
2620 return (LRESULT)psInfo->proppage[iPageIndex].hpage;
2621 }
2622
2623 /******************************************************************************
2624 * PROPSHEET_IdToIndex
2625 */
2626 static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
2627 {
2628 int index;
2629 LPCPROPSHEETPAGEW psp;
2630 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2631 TRACE("(%p, %d)\n", hwndDlg, iPageId);
2632 for (index = 0; index < psInfo->nPages; index++) {
2633 psp = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2634 if (psp->u.pszTemplate == MAKEINTRESOURCEW(iPageId))
2635 return index;
2636 }
2637
2638 return -1;
2639 }
2640
2641 /******************************************************************************
2642 * PROPSHEET_IndexToId
2643 */
2644 static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
2645 {
2646 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2647 LPCPROPSHEETPAGEW psp;
2648 TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2649 if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2650 WARN("%d out of range.\n", iPageIndex);
2651 return 0;
2652 }
2653 psp = (LPCPROPSHEETPAGEW)psInfo->proppage[iPageIndex].hpage;
2654 if (psp->dwFlags & PSP_DLGINDIRECT || !IS_INTRESOURCE(psp->u.pszTemplate)) {
2655 return 0;
2656 }
2657 return (LRESULT)psp->u.pszTemplate;
2658 }
2659
2660 /******************************************************************************
2661 * PROPSHEET_GetResult
2662 */
2663 static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
2664 {
2665 PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2666 return psInfo->result;
2667 }
2668
2669 /******************************************************************************
2670 * PROPSHEET_RecalcPageSizes
2671 */
2672 static BOOL PROPSHEET_RecalcPageSizes(HWND hwndDlg)
2673 {
2674 FIXME("(%p): stub\n", hwndDlg);
2675 return FALSE;
2676 }
2677
2678 /******************************************************************************
2679 * PROPSHEET_GetPageIndex
2680 *
2681 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2682 * the array of PropPageInfo. If page is not found original index is used
2683 * (page takes precedence over index).
2684 */
2685 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE page, const PropSheetInfo* psInfo, int original_index)
2686 {
2687 int index;
2688
2689 TRACE("page %p index %d\n", page, original_index);
2690
2691 for (index = 0; index < psInfo->nPages; index++)
2692 if (psInfo->proppage[index].hpage == page)
2693 return index;
2694
2695 return original_index;
2696 }
2697
2698 /******************************************************************************
2699 * PROPSHEET_CleanUp
2700 */
2701 static void PROPSHEET_CleanUp(HWND hwndDlg)
2702 {
2703 int i;
2704 PropSheetInfo* psInfo = RemovePropW(hwndDlg, PropSheetInfoStr);
2705
2706 TRACE("\n");
2707 if (!psInfo) return;
2708 if (!IS_INTRESOURCE(psInfo->ppshheader.pszCaption))
2709 Free ((LPVOID)psInfo->ppshheader.pszCaption);
2710
2711 for (i = 0; i < psInfo->nPages; i++)
2712 {
2713 PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2714
2715 /* Unsubclass the page dialog window */
2716 if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
2717 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2718 (psp->dwFlags & PSP_HIDEHEADER))
2719 {
2720 RemoveWindowSubclass(psInfo->proppage[i].hwndPage,
2721 PROPSHEET_WizardSubclassProc, 1);
2722 }
2723
2724 if(psInfo->proppage[i].hwndPage)
2725 DestroyWindow(psInfo->proppage[i].hwndPage);
2726
2727 if(psp)
2728 {
2729 if (psp->dwFlags & PSP_USETITLE)
2730 Free ((LPVOID)psInfo->proppage[i].pszText);
2731
2732 DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2733 }
2734 }
2735
2736 DeleteObject(psInfo->hFont);
2737 DeleteObject(psInfo->hFontBold);
2738 /* If we created the bitmaps, destroy them */
2739 if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2740 (!(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK)) )
2741 DeleteObject(psInfo->ppshheader.u4.hbmWatermark);
2742 if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
2743 (!(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)) )
2744 DeleteObject(psInfo->ppshheader.u5.hbmHeader);
2745
2746 Free(psInfo->proppage);
2747 Free(psInfo->strPropertiesFor);
2748 ImageList_Destroy(psInfo->hImageList);
2749
2750 GlobalFree(psInfo);
2751 }
2752
2753 static INT do_loop(const PropSheetInfo *psInfo)
2754 {
2755 MSG msg;
2756 INT ret = -1;
2757 HWND hwnd = psInfo->hwnd;
2758 HWND parent = psInfo->ppshheader.hwndParent;
2759
2760 while(IsWindow(hwnd) && !psInfo->ended && (ret = GetMessageW(&msg, NULL, 0, 0)))
2761 {
2762 if(ret == -1)
2763 break;
2764
2765 if(!IsDialogMessageW(hwnd, &msg))
2766 {
2767 TranslateMessage(&msg);
2768 DispatchMessageW(&msg);
2769 }
2770 }
2771
2772 if(ret == 0)
2773 {
2774 PostQuitMessage(msg.wParam);
2775 ret = -1;
2776 }
2777
2778 if(ret != -1)
2779 ret = psInfo->result;
2780
2781 if(parent)
2782 EnableWindow(parent, TRUE);
2783
2784 DestroyWindow(hwnd);
2785 return ret;
2786 }
2787
2788 /******************************************************************************
2789 * PROPSHEET_PropertySheet
2790 *
2791 * Common code between PropertySheetA/W
2792 */
2793 static INT_PTR PROPSHEET_PropertySheet(PropSheetInfo* psInfo, BOOL unicode)
2794 {
2795 INT_PTR bRet = 0;
2796 HWND parent = NULL;
2797 if (psInfo->active_page >= psInfo->nPages) psInfo->active_page = 0;
2798 TRACE("startpage: %d of %d pages\n", psInfo->active_page, psInfo->nPages);
2799
2800 psInfo->unicode = unicode;
2801 psInfo->ended = FALSE;
2802
2803 if(!psInfo->isModeless)
2804 {
2805 parent = psInfo->ppshheader.hwndParent;
2806 if (parent) EnableWindow(parent, FALSE);
2807 }
2808 bRet = PROPSHEET_CreateDialog(psInfo);
2809 if(!psInfo->isModeless)
2810 bRet = do_loop(psInfo);
2811 return bRet;
2812 }
2813
2814 /******************************************************************************
2815 * PropertySheet (COMCTL32.@)
2816 * PropertySheetA (COMCTL32.@)
2817 *
2818 * Creates a property sheet in the specified property sheet header.
2819 *
2820 * RETURNS
2821 * Modal property sheets: Positive if successful or -1 otherwise.
2822 * Modeless property sheets: Property sheet handle.
2823 * Or:
2824 *| ID_PSREBOOTSYSTEM - The user must reboot the computer for the changes to take effect.
2825 *| ID_PSRESTARTWINDOWS - The user must restart Windows for the changes to take effect.
2826 */
2827 INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2828 {
2829 PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2830 UINT i, n;
2831 const BYTE* pByte;
2832
2833 TRACE("(%p)\n", lppsh);
2834
2835 PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2836
2837 psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2838 pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2839
2840 for (n = i = 0; i < lppsh->nPages; i++, n++)
2841 {
2842 if (!psInfo->usePropPage)
2843 psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2844 else
2845 {
2846 psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2847 pByte += ((LPCPROPSHEETPAGEA)pByte)->dwSize;
2848 }
2849
2850 if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2851 psInfo, n, TRUE))
2852 {
2853 if (psInfo->usePropPage)
2854 DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2855 n--;
2856 psInfo->nPages--;
2857 }
2858 }
2859
2860 return PROPSHEET_PropertySheet(psInfo, FALSE);
2861 }
2862
2863 /******************************************************************************
2864 * PropertySheetW (COMCTL32.@)
2865 *
2866 * See PropertySheetA.
2867 */
2868 INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2869 {
2870 PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2871 UINT i, n;
2872 const BYTE* pByte;
2873
2874 TRACE("(%p)\n", lppsh);
2875
2876 PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2877
2878 psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2879 pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2880
2881 for (n = i = 0; i < lppsh->nPages; i++, n++)
2882 {
2883 if (!psInfo->usePropPage)
2884 psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2885 else
2886 {
2887 psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2888 pByte += ((LPCPROPSHEETPAGEW)pByte)->dwSize;
2889 }
2890
2891 if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2892 psInfo, n, TRUE))
2893 {
2894 if (psInfo->usePropPage)
2895 DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2896 n--;
2897 psInfo->nPages--;
2898 }
2899 }
2900
2901 return PROPSHEET_PropertySheet(psInfo, TRUE);
2902 }
2903
2904 static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
2905 {
2906 LPWSTR ret;
2907
2908 if (IS_INTRESOURCE(str))
2909 {
2910 HRSRC hrsrc;
2911 HGLOBAL hmem;
2912 WCHAR *ptr;
2913 WORD i, id = LOWORD(str);
2914 UINT len;
2915
2916 if (!(hrsrc = FindResourceW( instance, MAKEINTRESOURCEW((id >> 4) + 1), (LPWSTR)RT_STRING )))
2917 return NULL;
2918 if (!(hmem = LoadResource( instance, hrsrc ))) return NULL;
2919 if (!(ptr = LockResource( hmem ))) return NULL;
2920 for (i = id & 0x0f; i > 0; i--) ptr += *ptr + 1;
2921 len = *ptr;
2922 if (!len) return NULL;
2923 ret = Alloc( (len + 1) * sizeof(WCHAR) );
2924 if (ret)
2925 {
2926 memcpy( ret, ptr + 1, len * sizeof(WCHAR) );
2927 ret[len] = 0;
2928 }
2929 }
2930 else
2931 {
2932 int len = (strlenW(str) + 1) * sizeof(WCHAR);
2933 ret = Alloc( len );
2934 if (ret) memcpy( ret, str, len );
2935 }
2936 return ret;
2937 }
2938
2939
2940 /******************************************************************************
2941 * CreatePropertySheetPage (COMCTL32.@)
2942 * CreatePropertySheetPageA (COMCTL32.@)
2943 *
2944 * Creates a new property sheet page.
2945 *
2946 * RETURNS
2947 * Success: Handle to new property sheet page.
2948 * Failure: NULL.
2949 *
2950 * NOTES
2951 * An application must use the PSM_ADDPAGE message to add the new page to
2952 * an existing property sheet.
2953 */
2954 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2955 LPCPROPSHEETPAGEA lpPropSheetPage)
2956 {
2957 PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2958
2959 memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2960
2961 ppsp->dwFlags &= ~ PSP_INTERNAL_UNICODE;
2962
2963 if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
2964 {
2965 if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
2966 {
2967 int len = strlen(lpPropSheetPage->u.pszTemplate) + 1;
2968 char *template = Alloc( len );
2969
2970 ppsp->u.pszTemplate = (LPWSTR)strcpy( template, lpPropSheetPage->u.pszTemplate );
2971 }
2972 }
2973
2974 if (ppsp->dwFlags & PSP_USEICONID)
2975 {
2976 if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
2977 PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2978 }
2979
2980 if (ppsp->dwFlags & PSP_USETITLE)
2981 {
2982 if (!IS_INTRESOURCE( ppsp->pszTitle ))
2983 PROPSHEET_AtoW( &ppsp->pszTitle, lpPropSheetPage->pszTitle );
2984 else
2985 ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
2986 }
2987 else
2988 ppsp->pszTitle = NULL;
2989
2990 if (ppsp->dwFlags & PSP_HIDEHEADER)
2991 ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
2992
2993 if (ppsp->dwFlags & PSP_USEHEADERTITLE)
2994 {
2995 if (!IS_INTRESOURCE( ppsp->pszHeaderTitle ))
2996 PROPSHEET_AtoW(&ppsp->pszHeaderTitle, lpPropSheetPage->pszHeaderTitle);
2997 else
2998 ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
2999 }
3000 else
3001 ppsp->pszHeaderTitle = NULL;
3002
3003 if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3004 {
3005 if (!IS_INTRESOURCE( ppsp->pszHeaderSubTitle ))
3006 PROPSHEET_AtoW(&ppsp->pszHeaderSubTitle, lpPropSheetPage->pszHeaderSubTitle);
3007 else
3008 ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3009 }
3010 else
3011 ppsp->pszHeaderSubTitle = NULL;
3012
3013 return (HPROPSHEETPAGE)ppsp;
3014 }
3015
3016 /******************************************************************************
3017 * CreatePropertySheetPageW (COMCTL32.@)
3018 *
3019 * See CreatePropertySheetA.
3020 */
3021 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
3022 {
3023 PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
3024
3025 memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));
3026
3027 ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
3028
3029 if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
3030 {
3031 if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
3032 {
3033 int len = strlenW(lpPropSheetPage->u.pszTemplate) + 1;
3034 WCHAR *template = Alloc( len * sizeof (WCHAR) );
3035
3036 ppsp->u.pszTemplate = strcpyW( template, lpPropSheetPage->u.pszTemplate );
3037 }
3038 }
3039
3040 if ( ppsp->dwFlags & PSP_USEICONID )
3041 {
3042 if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
3043 {
3044 int len = strlenW(lpPropSheetPage->u2.pszIcon) + 1;
3045 WCHAR *icon = Alloc( len * sizeof (WCHAR) );
3046
3047 ppsp->u2.pszIcon = strcpyW( icon, lpPropSheetPage->u2.pszIcon );
3048 }
3049 }
3050
3051 if (ppsp->dwFlags & PSP_USETITLE)
3052 ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3053 else
3054 ppsp->pszTitle = NULL;
3055
3056 if (ppsp->dwFlags & PSP_HIDEHEADER)
3057 ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
3058
3059 if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3060 ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3061 else
3062 ppsp->pszHeaderTitle = NULL;
3063
3064 if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3065 ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3066 else
3067 ppsp->pszHeaderSubTitle = NULL;
3068
3069 return (HPROPSHEETPAGE)ppsp;
3070 }
3071
3072 /******************************************************************************
3073 * DestroyPropertySheetPage (COMCTL32.@)
3074 *
3075 * Destroys a property sheet page previously created with
3076 * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
3077 * memory.
3078 *
3079 * RETURNS
3080 * Success: TRUE
3081 * Failure: FALSE
3082 */
3083 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
3084 {
3085 PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
3086
3087 if (!psp)
3088 return FALSE;
3089
3090 if (!(psp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE( psp->u.pszTemplate ))
3091 Free ((LPVOID)psp->u.pszTemplate);
3092
3093 if ((psp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE( psp->u2.pszIcon ))
3094 Free ((LPVOID)psp->u2.pszIcon);
3095
3096 if ((psp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE( psp->pszTitle ))
3097 Free ((LPVOID)psp->pszTitle);
3098
3099 if ((psp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE( psp->pszHeaderTitle ))
3100 Free ((LPVOID)psp->pszHeaderTitle);
3101
3102 if ((psp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE( psp->pszHeaderSubTitle ))
3103 Free ((LPVOID)psp->pszHeaderSubTitle);
3104
3105 Free(hPropPage);
3106
3107 return TRUE;
3108 }
3109
3110 /******************************************************************************
3111 * PROPSHEET_IsDialogMessage
3112 */
3113 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
3114 {
3115 PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3116
3117 TRACE("\n");
3118 if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
3119 return FALSE;
3120
3121 if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
3122 {
3123 int new_page = 0;
3124 INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
3125
3126 if (!(dlgCode & DLGC_WANTMESSAGE))
3127 {
3128 switch (lpMsg->wParam)
3129 {
3130 case VK_TAB:
3131 if (GetKeyState(VK_SHIFT) & 0x8000)
3132 new_page = -1;
3133 else
3134 new_page = 1;
3135 break;
3136
3137 case VK_NEXT: new_page = 1; break;
3138 case VK_PRIOR: new_page = -1; break;
3139 }
3140 }
3141
3142 if (new_page)
3143 {
3144 if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
3145 {
3146 new_page += psInfo->active_page;
3147
3148 if (new_page < 0)
3149 new_page = psInfo->nPages - 1;
3150 else if (new_page >= psInfo->nPages)
3151 new_page = 0;
3152
3153 PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
3154 }
3155