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 <stdarg.h>
56 #include <string.h>
57
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
60 #include "windef.h"
61 #include "winbase.h"
62 #include "wingdi.h"
63 #include "winuser.h"
64 #include "winnls.h"
65 #include "commctrl.h"
66 #include "prsht.h"
67 #include "comctl32.h"
68 #include "uxtheme.h"
69
70 #include "wine/debug.h"
71 #include "wine/unicode.h"
72
73 /******************************************************************************
74 * Data structures
75 */
76 #include "pshpack2.h"
77
78 typedef struct
79 {
80 WORD dlgVer;
81 WORD signature;
82 DWORD helpID;
83 DWORD exStyle;
84 DWORD style;
85 } MyDLGTEMPLATEEX;
86
87 typedef struct
88 {
89 DWORD helpid;
90 DWORD exStyle;
91 DWORD style;
92 short x;
93 short y;
94 short cx;
95 short cy;
96 DWORD id;
97 } MyDLGITEMTEMPLATEEX;
98 #include "poppack.h"
99
100 typedef struct tagPropPageInfo
101 {
102 HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
103 HWND hwndPage;
104 BOOL isDirty;
105 LPCWSTR pszText;
106 BOOL hasHelp;
107 BOOL useCallback;
108 BOOL hasIcon;
109 } PropPageInfo;
110
111 typedef struct tagPropSheetInfo
112 {
113 HWND hwnd;
114 PROPSHEETHEADERW ppshheader;
115 BOOL unicode;
116 LPWSTR strPropertiesFor;
117 int nPages;
118 int active_page;
119 BOOL isModeless;
120 BOOL hasHelp;
121 BOOL hasApply;
122 BOOL hasFinish;
123 BOOL usePropPage;
124 BOOL useCallback;
125 BOOL activeValid;
126 PropPageInfo* proppage;
127 HFONT hFont;
128 HFONT hFontBold;
129 int width;
130 int height;
131 HIMAGELIST hImageList;
132 BOOL ended;
133 INT result;
134 } PropSheetInfo;
135
136 typedef struct
137 {
138 int x;
139 int y;
140 } PADDING_INFO;
141
142 /******************************************************************************
143 * Defines and global variables
144 */
145
146 static const WCHAR PropSheetInfoStr[] =
147 {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
148
149 #define PSP_INTERNAL_UNICODE 0x80000000
150
151 #define MAX_CAPTION_LENGTH 255
152 #define MAX_TABTEXT_LENGTH 255
153 #define MAX_BUTTONTEXT_LENGTH 64
154
155 #define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)
156
157 /* Wizard metrics specified in DLUs */
158 #define WIZARD_PADDING 7
159 #define WIZARD_HEADER_HEIGHT 36
160
161 /******************************************************************************
162 * Prototypes
163 */
164 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
165 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
166 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
167 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
168 int index,
169 int skipdir,
170 HPROPSHEETPAGE hpage);
171 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, const PropSheetInfo* psInfo);
172 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
173 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
174
175 static INT_PTR CALLBACK
176 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
177
178 WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
179
180 #define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
181 /******************************************************************************
182 * PROPSHEET_UnImplementedFlags
183 *
184 * Document use of flags we don't implement yet.
185 */
186 static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
187 {
188 CHAR string[256];
189
190 string[0] = '\0';
191
192 /*
193 * unhandled header flags:
194 * PSH_RTLREADING 0x00000800
195 * PSH_STRETCHWATERMARK 0x00040000
196 * PSH_USEPAGELANG 0x00200000
197 */
198
199 add_flag(PSH_RTLREADING);
200 add_flag(PSH_STRETCHWATERMARK);
201 add_flag(PSH_USEPAGELANG);
202 if (string[0] != '\0')
203 FIXME("%s\n", string);
204 }
205 #undef add_flag
206
207 /******************************************************************************
208 * PROPSHEET_GetPageRect
209 *
210 * Retrieve rect from tab control and map into the dialog for SetWindowPos
211 */
212 static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg,
213 RECT *rc, LPCPROPSHEETPAGEW ppshpage)
214 {
215 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {
216 HWND hwndChild;
217 RECT r;
218
219 if (((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
220 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
221 !(ppshpage->dwFlags & PSP_HIDEHEADER)) ||
222 (psInfo->ppshheader.dwFlags & PSH_WIZARD))
223 {
224 rc->left = rc->top = WIZARD_PADDING;
225 }
226 else
227 {
228 rc->left = rc->top = 0;
229 }
230 rc->right = psInfo->width - rc->left;
231 rc->bottom = psInfo->height - rc->top;
232 MapDialogRect(hwndDlg, rc);
233
234 if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
235 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
236 !(ppshpage->dwFlags & PSP_HIDEHEADER))
237 {
238 hwndChild = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
239 GetClientRect(hwndChild, &r);
240 MapWindowPoints(hwndChild, hwndDlg, (LPPOINT) &r, 2);
241 rc->top += r.bottom + 1;
242 }
243 } else {
244 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
245 GetClientRect(hwndTabCtrl, rc);
246 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
247 MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
248 }
249 }
250
251 /******************************************************************************
252 * PROPSHEET_FindPageByResId
253 *
254 * Find page index corresponding to page resource id.
255 */
256 static INT PROPSHEET_FindPageByResId(const PropSheetInfo * psInfo, LRESULT resId)
257 {
258 INT i;
259
260 for (i = 0; i < psInfo->nPages; i++)
261 {
262 LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;
263
264 /* Fixme: if resource ID is a string shall we use strcmp ??? */
265 if (lppsp->u.pszTemplate == (LPVOID)resId)
266 break;
267 }
268
269 return i;
270 }
271
272 /******************************************************************************
273 * PROPSHEET_AtoW
274 *
275 * Convert ASCII to Unicode since all data is saved as Unicode.
276 */
277 static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
278 {
279 INT len;
280 WCHAR *to;
281
282 TRACE("<%s>\n", frstr);
283 len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
284 to = Alloc(len * sizeof(WCHAR));
285 MultiByteToWideChar(CP_ACP, 0, frstr, -1, to, len);
286 *tostr = to;
287 }
288
289 /******************************************************************************
290 * PROPSHEET_CollectSheetInfoCommon
291 *
292 * Common code for PROPSHEET_CollectSheetInfoA/W
293 */
294 static void PROPSHEET_CollectSheetInfoCommon(PropSheetInfo * psInfo, DWORD dwFlags)
295 {
296 PROPSHEET_UnImplementedFlags(dwFlags);
297
298 psInfo->hasHelp = dwFlags & PSH_HASHELP;
299 psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
300 psInfo->hasFinish = dwFlags & PSH_WIZARDHASFINISH;
301 psInfo->isModeless = dwFlags & PSH_MODELESS;
302 psInfo->usePropPage = dwFlags & PSH_PROPSHEETPAGE;
303 if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
304 psInfo->active_page = 0;
305
306 psInfo->result = 0;
307 psInfo->hImageList = 0;
308 psInfo->activeValid = FALSE;
309 }
310
311 /******************************************************************************
312 * PROPSHEET_CollectSheetInfoA
313 *
314 * Collect relevant data.
315 */
316 static void PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
317 PropSheetInfo * psInfo)
318 {
319 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
320 DWORD dwFlags = lppsh->dwFlags;
321
322 psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
323
324 memcpy(&psInfo->ppshheader,lppsh,dwSize);
325 TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
326 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
327 debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
328
329 if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
330 psInfo->ppshheader.pszCaption = NULL;
331 else
332 {
333 if (HIWORD(lppsh->pszCaption))
334 {
335 int len = MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, NULL, 0);
336 WCHAR *caption = Alloc( len*sizeof (WCHAR) );
337
338 MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, caption, len);
339 psInfo->ppshheader.pszCaption = caption;
340 }
341 }
342 psInfo->nPages = lppsh->nPages;
343
344 if (dwFlags & PSH_USEPSTARTPAGE)
345 {
346 TRACE("PSH_USEPSTARTPAGE is on\n");
347 psInfo->active_page = 0;
348 }
349 else
350 psInfo->active_page = lppsh->u2.nStartPage;
351
352 PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
353 }
354
355 /******************************************************************************
356 * PROPSHEET_CollectSheetInfoW
357 *
358 * Collect relevant data.
359 */
360 static void PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
361 PropSheetInfo * psInfo)
362 {
363 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
364 DWORD dwFlags = lppsh->dwFlags;
365
366 psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
367
368 memcpy(&psInfo->ppshheader,lppsh,dwSize);
369 TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t%s\nnPages\t\t%d\npfnCallback\t%p\n",
370 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
371
372 if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
373 psInfo->ppshheader.pszCaption = NULL;
374 else
375 {
376 if (HIWORD(lppsh->pszCaption))
377 {
378 int len = strlenW(lppsh->pszCaption);
379 WCHAR *caption = Alloc( (len+1)*sizeof(WCHAR) );
380
381 psInfo->ppshheader.pszCaption = strcpyW( caption, lppsh->pszCaption );
382 }
383 }
384 psInfo->nPages = lppsh->nPages;
385
386 if (dwFlags & PSH_USEPSTARTPAGE)
387 {
388 TRACE("PSH_USEPSTARTPAGE is on\n");
389 psInfo->active_page = 0;
390 }
391 else
392 psInfo->active_page = lppsh->u2.nStartPage;
393
394 PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
395 }
396
397 /******************************************************************************
398 * PROPSHEET_CollectPageInfo
399 *
400 * Collect property sheet data.
401 * With code taken from DIALOG_ParseTemplate32.
402 */
403 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
404 PropSheetInfo * psInfo,
405 int index, BOOL resize)
406 {
407 const DLGTEMPLATE* pTemplate;
408 const WORD* p;
409 DWORD dwFlags;
410 int width, height;
411
412 if (!lppsp)
413 return FALSE;
414
415 TRACE("\n");
416 psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
417 psInfo->proppage[index].hwndPage = 0;
418 psInfo->proppage[index].isDirty = FALSE;
419
420 /*
421 * Process property page flags.
422 */
423 dwFlags = lppsp->dwFlags;
424 psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
425 psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
426 psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
427
428 /* as soon as we have a page with the help flag, set the sheet flag on */
429 if (psInfo->proppage[index].hasHelp)
430 psInfo->hasHelp = TRUE;
431
432 /*
433 * Process page template.
434 */
435 if (dwFlags & PSP_DLGINDIRECT)
436 pTemplate = lppsp->u.pResource;
437 else if(dwFlags & PSP_INTERNAL_UNICODE )
438 {
439 HRSRC hResource = FindResourceW(lppsp->hInstance,
440 lppsp->u.pszTemplate,
441 (LPWSTR)RT_DIALOG);
442 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
443 hResource);
444 pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
445 }
446 else
447 {
448 HRSRC hResource = FindResourceA(lppsp->hInstance,
449 (LPCSTR)lppsp->u.pszTemplate,
450 (LPSTR)RT_DIALOG);
451 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
452 hResource);
453 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
454 }
455
456 /*
457 * Extract the size of the page and the caption.
458 */
459 if (!pTemplate)
460 return FALSE;
461
462 p = (const WORD *)pTemplate;
463
464 if (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
465 {
466 /* DLGTEMPLATEEX (not defined in any std. header file) */
467
468 p++; /* dlgVer */
469 p++; /* signature */
470 p += 2; /* help ID */
471 p += 2; /* ext style */
472 p += 2; /* style */
473 }
474 else
475 {
476 /* DLGTEMPLATE */
477
478 p += 2; /* style */
479 p += 2; /* ext style */
480 }
481
482 p++; /* nb items */
483 p++; /* x */
484 p++; /* y */
485 width = (WORD)*p; p++;
486 height = (WORD)*p; p++;
487
488 /* Special calculation for interior wizard pages so the largest page is
489 * calculated correctly. We need to add all the padding and space occupied
490 * by the header so the width and height sums up to the whole wizard client
491 * area. */
492 if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
493 (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
494 !(dwFlags & PSP_HIDEHEADER))
495 {
496 height += 2 * WIZARD_PADDING + WIZARD_HEADER_HEIGHT;
497 width += 2 * WIZARD_PADDING;
498 }
499 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
500 {
501 height += 2 * WIZARD_PADDING;
502 width += 2 * WIZARD_PADDING;
503 }
504
505 /* remember the largest width and height */
506 if (resize)
507 {
508 if (width > psInfo->width)
509 psInfo->width = width;
510
511 if (height > psInfo->height)
512 psInfo->height = height;
513 }
514
515 /* menu */
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 /* class */
530 switch ((WORD)*p)
531 {
532 case 0x0000:
533 p++;
534 break;
535 case 0xffff:
536 p += 2;
537 break;
538 default:
539 p += lstrlenW( p ) + 1;
540 break;
541 }
542
543 /* Extract the caption */
544 psInfo->proppage[index].pszText = p;
545 TRACE("Tab %d %s\n",index,debugstr_w( p ));
546 p += lstrlenW( p ) + 1;
547
548 if (dwFlags & PSP_USETITLE)
549 {
550 WCHAR szTitle[256];
551 const WCHAR *pTitle;
552 static const WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
553 WCHAR *text;
554 int len;
555
556 if ( !HIWORD( lppsp->pszTitle ) )
557 {
558 if (!LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle,szTitle,sizeof(szTitle)/sizeof(szTitle[0]) ))
559 {
560 pTitle = pszNull;
561 FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
562 }
563 else
564 pTitle = szTitle;
565 }
566 else
567 pTitle = lppsp->pszTitle;
568
569 len = strlenW(pTitle);
570 text = Alloc( (len+1)*sizeof (WCHAR) );
571 psInfo->proppage[index].pszText = strcpyW( text, pTitle);
572 }
573
574 /*
575 * Build the image list for icons
576 */
577 if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
578 {
579 HICON hIcon;
580 int icon_cx = GetSystemMetrics(SM_CXSMICON);
581 int icon_cy = GetSystemMetrics(SM_CYSMICON);
582
583 if (dwFlags & PSP_USEICONID)
584 hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
585 icon_cx, icon_cy, LR_DEFAULTCOLOR);
586 else
587 hIcon = lppsp->u2.hIcon;
588
589 if ( hIcon )
590 {
591 if (psInfo->hImageList == 0 )
592 psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
593
594 ImageList_AddIcon(psInfo->hImageList, hIcon);
595 }
596
597 }
598
599 return TRUE;
600 }
601
602 /******************************************************************************
603 * PROPSHEET_CreateDialog
604 *
605 * Creates the actual property sheet.
606 */
607 static INT_PTR PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
608 {
609 LRESULT ret;
610 LPCVOID template;
611 LPVOID temp = 0;
612 HRSRC hRes;
613 DWORD resSize;
614 WORD resID = IDD_PROPSHEET;
615
616 TRACE("\n");
617 if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
618 resID = IDD_WIZARD;
619
620 if( psInfo->unicode )
621 {
622 if(!(hRes = FindResourceW(COMCTL32_hModule,
623 MAKEINTRESOURCEW(resID),
624 (LPWSTR)RT_DIALOG)))
625 return -1;
626 }
627 else
628 {
629 if(!(hRes = FindResourceA(COMCTL32_hModule,
630 MAKEINTRESOURCEA(resID),
631 (LPSTR)RT_DIALOG)))
632 return -1;
633 }
634
635 if(!(template = LoadResource(COMCTL32_hModule, hRes)))
636 return -1;
637
638 /*
639 * Make a copy of the dialog template.
640 */
641 resSize = SizeofResource(COMCTL32_hModule, hRes);
642
643 temp = Alloc(resSize);
644
645 if (!temp)
646 return -1;
647
648 memcpy(temp, template, resSize);
649
650 if (psInfo->ppshheader.dwFlags & PSH_NOCONTEXTHELP)
651 {
652 if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
653 ((MyDLGTEMPLATEEX*)temp)->style &= ~DS_CONTEXTHELP;
654 else
655 ((DLGTEMPLATE*)temp)->style &= ~DS_CONTEXTHELP;
656 }
657 if ((psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) &&
658 (psInfo->ppshheader.dwFlags & PSH_WIZARDCONTEXTHELP))
659 {
660 if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
661 ((MyDLGTEMPLATEEX*)temp)->style |= DS_CONTEXTHELP;
662 else
663 ((DLGTEMPLATE*)temp)->style |= DS_CONTEXTHELP;
664 }
665
666 if (psInfo->useCallback)
667 (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
668
669 /* NOTE: MSDN states "Returns a positive value if successful, or -1
670 * otherwise for modal property sheets.", but this is wrong. The
671 * actual return value is either TRUE (success), FALSE (cancel) or
672 * -1 (error). */
673 if( psInfo->unicode )
674 {
675 ret = (INT_PTR)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
676 (LPDLGTEMPLATEW) temp,
677 psInfo->ppshheader.hwndParent,
678 PROPSHEET_DialogProc,
679 (LPARAM)psInfo);
680 if ( !ret ) ret = -1;
681 }
682 else
683 {
684 ret = (INT_PTR)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
685 (LPDLGTEMPLATEA) temp,
686 psInfo->ppshheader.hwndParent,
687 PROPSHEET_DialogProc,
688 (LPARAM)psInfo);
689 if ( !ret ) ret = -1;
690 }
691
692 Free(temp);
693
694 return ret;
695 }
696
697 /******************************************************************************
698 * PROPSHEET_SizeMismatch
699 *
700 * Verify that the tab control and the "largest" property sheet page dlg. template
701 * match in size.
702 */
703 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, const PropSheetInfo* psInfo)
704 {
705 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
706 RECT rcOrigTab, rcPage;
707
708 /*
709 * Original tab size.
710 */
711 GetClientRect(hwndTabCtrl, &rcOrigTab);
712 TRACE("orig tab %s\n", wine_dbgstr_rect(&rcOrigTab));
713
714 /*
715 * Biggest page size.
716 */
717 rcPage.left = 0;
718 rcPage.top = 0;
719 rcPage.right = psInfo->width;
720 rcPage.bottom = psInfo->height;
721
722 MapDialogRect(hwndDlg, &rcPage);
723 TRACE("biggest page %s\n", wine_dbgstr_rect(&rcPage));
724
725 if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
726 return TRUE;
727 if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
728 return TRUE;
729
730 return FALSE;
731 }
732
733 /******************************************************************************
734 * PROPSHEET_AdjustSize
735 *
736 * Resizes the property sheet and the tab control to fit the largest page.
737 */
738 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
739 {
740 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
741 HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
742 RECT rc,tabRect;
743 int tabOffsetX, tabOffsetY, buttonHeight;
744 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
745 RECT units;
746
747 /* Get the height of buttons */
748 GetClientRect(hwndButton, &rc);
749 buttonHeight = rc.bottom;
750
751 /*
752 * Biggest page size.
753 */
754 rc.left = 0;
755 rc.top = 0;
756 rc.right = psInfo->width;
757 rc.bottom = psInfo->height;
758
759 MapDialogRect(hwndDlg, &rc);
760
761 /* retrieve the dialog units */
762 units.left = units.right = 4;
763 units.top = units.bottom = 8;
764 MapDialogRect(hwndDlg, &units);
765
766 /*
767 * Resize the tab control.
768 */
769 GetClientRect(hwndTabCtrl,&tabRect);
770
771 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
772
773 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
774 {
775 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
776 psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
777 }
778
779 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
780 {
781 rc.right = rc.left + tabRect.right - tabRect.left;
782 psInfo->width = MulDiv((rc.right - rc.left),4,units.left);
783 }
784
785 SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
786
787 tabOffsetX = -(rc.left);
788 tabOffsetY = -(rc.top);
789
790 rc.right -= rc.left;
791 rc.bottom -= rc.top;
792 TRACE("setting tab %p, rc (0,0)-(%d,%d)\n",
793 hwndTabCtrl, rc.right, rc.bottom);
794 SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
795 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
796
797 GetClientRect(hwndTabCtrl, &rc);
798
799 TRACE("tab client rc %s\n", wine_dbgstr_rect(&rc));
800
801 rc.right += ((padding.x * 2) + tabOffsetX);
802 rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
803
804 /*
805 * Resize the property sheet.
806 */
807 TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
808 hwndDlg, rc.right, rc.bottom);
809 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
810 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
811 return TRUE;
812 }
813
814 /******************************************************************************
815 * PROPSHEET_AdjustSizeWizard
816 *
817 * Resizes the property sheet to fit the largest page.
818 */
819 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, const PropSheetInfo* psInfo)
820 {
821 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
822 RECT rc, lineRect, dialogRect;
823
824 /* Biggest page size */
825 rc.left = 0;
826 rc.top = 0;
827 rc.right = psInfo->width;
828 rc.bottom = psInfo->height;
829 MapDialogRect(hwndDlg, &rc);
830
831 TRACE("Biggest page %s\n", wine_dbgstr_rect(&rc));
832
833 /* Add space for the buttons row */
834 GetWindowRect(hwndLine, &lineRect);
835 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&lineRect, 2);
836 GetClientRect(hwndDlg, &dialogRect);
837 rc.bottom += dialogRect.bottom - lineRect.top - 1;
838
839 /* Convert the client coordinates to window coordinates */
840 AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);
841
842 /* Resize the property sheet */
843 TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
844 hwndDlg, rc.right, rc.bottom);
845 SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
846 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
847
848 return TRUE;
849 }
850
851 /******************************************************************************
852 * PROPSHEET_AdjustButtons
853 *
854 * Adjusts the buttons' positions.
855 */
856 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, const PropSheetInfo* psInfo)
857 {
858 HWND hwndButton = GetDlgItem(hwndParent, IDOK);
859 RECT rcSheet;
860 int x, y;
861 int num_buttons = 2;
862 int buttonWidth, buttonHeight;
863 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
864
865 if (psInfo->hasApply)
866 num_buttons++;
867
868 if (psInfo->hasHelp)
869 num_buttons++;
870
871 /*
872 * Obtain the size of the buttons.
873 */
874 GetClientRect(hwndButton, &rcSheet);
875 buttonWidth = rcSheet.right;
876 buttonHeight = rcSheet.bottom;
877
878 /*
879 * Get the size of the property sheet.
880 */
881 GetClientRect(hwndParent, &rcSheet);
882
883 /*
884 * All buttons will be at this y coordinate.
885 */
886 y = rcSheet.bottom - (padding.y + buttonHeight);
887
888 /*
889 * Position OK button and make it default.
890 */
891 hwndButton = GetDlgItem(hwndParent, IDOK);
892
893 x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
894
895 SetWindowPos(hwndButton, 0, x, y, 0, 0,
896 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
897
898 SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
899
900
901 /*
902 * Position Cancel button.
903 */
904 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
905
906 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
907
908 SetWindowPos(hwndButton, 0, x, y, 0, 0,
909 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
910
911 /*
912 * Position Apply button.
913 */
914 hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
915
916 if (psInfo->hasApply)
917 {
918 if (psInfo->hasHelp)
919 x = rcSheet.right - ((padding.x + buttonWidth) * 2);
920 else
921 x = rcSheet.right - (padding.x + buttonWidth);
922
923 SetWindowPos(hwndButton, 0, x, y, 0, 0,
924 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
925
926 EnableWindow(hwndButton, FALSE);
927 }
928 else
929 ShowWindow(hwndButton, SW_HIDE);
930
931 /*
932 * Position Help button.
933 */
934 hwndButton = GetDlgItem(hwndParent, IDHELP);
935
936 if (psInfo->hasHelp)
937 {
938 x = rcSheet.right - (padding.x + buttonWidth);
939
940 SetWindowPos(hwndButton, 0, x, y, 0, 0,
941 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
942 }
943 else
944 ShowWindow(hwndButton, SW_HIDE);
945
946 return TRUE;
947 }
948
949 /******************************************************************************
950 * PROPSHEET_AdjustButtonsWizard
951 *
952 * Adjusts the buttons' positions.
953 */
954 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
955 const PropSheetInfo* psInfo)
956 {
957 HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
958 HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
959 HWND hwndLineHeader = GetDlgItem(hwndParent, IDC_SUNKEN_LINEHEADER);
960 RECT rcSheet;
961 int x, y;
962 int num_buttons = 3;
963 int buttonWidth, buttonHeight, lineHeight, lineWidth;
964 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
965
966 if (psInfo->hasHelp)
967 num_buttons++;
968 if (psInfo->hasFinish)
969 num_buttons++;
970
971 /*
972 * Obtain the size of the buttons.