source: trunk/src/comctl32/propsheet.c@ 7310

Last change on this file since 7310 was 7304, checked in by sandervl, 24 years ago

Latest Wine port of property sheet control added

File size: 65.9 KB
Line 
1/*
2 * Property Sheets
3 *
4 * Copyright 1998 Francis Beaudet
5 * Copyright 1999 Thuy Nguyen
6 *
7 * TODO:
8 * - Tab order
9 * - Unicode property sheets
10 */
11
12#include <string.h>
13#include "winbase.h"
14#include "commctrl.h"
15#include "prsht.h"
16#include "winnls.h"
17#include "comctl32.h"
18#include "debugtools.h"
19#include "heap.h"
20
21
22/******************************************************************************
23 * Data structures
24 */
25typedef struct
26{
27 WORD dlgVer;
28 WORD signature;
29 DWORD helpID;
30 DWORD exStyle;
31 DWORD style;
32} MyDLGTEMPLATEEX;
33
34typedef struct tagPropPageInfo
35{
36 HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
37 HWND hwndPage;
38 BOOL isDirty;
39 LPCWSTR pszText;
40 BOOL hasHelp;
41 BOOL useCallback;
42 BOOL hasIcon;
43} PropPageInfo;
44
45typedef struct tagPropSheetInfo
46{
47 HWND hwnd;
48 PROPSHEETHEADERA ppshheader;
49 LPSTR strPropertiesFor;
50 int nPages;
51 int active_page;
52 BOOL isModeless;
53 BOOL hasHelp;
54 BOOL hasApply;
55 BOOL useCallback;
56 BOOL restartWindows;
57 BOOL rebootSystem;
58 BOOL activeValid;
59 PropPageInfo* proppage;
60 int x;
61 int y;
62 int width;
63 int height;
64 HIMAGELIST hImageList;
65} PropSheetInfo;
66
67typedef struct
68{
69 int x;
70 int y;
71} PADDING_INFO;
72
73/******************************************************************************
74 * Defines and global variables
75 */
76
77const char * PropSheetInfoStr = "PropertySheetInfo";
78
79#define MAX_CAPTION_LENGTH 255
80#define MAX_TABTEXT_LENGTH 255
81#define MAX_BUTTONTEXT_LENGTH 64
82
83/******************************************************************************
84 * Prototypes
85 */
86static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
87static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo);
88static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
89static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
90static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
91 PropSheetInfo * psInfo);
92static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
93 PropSheetInfo * psInfo,
94 int index);
95static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
96 PropSheetInfo * psInfo);
97static BOOL PROPSHEET_CreatePage(HWND hwndParent, int index,
98 const PropSheetInfo * psInfo,
99 LPCPROPSHEETPAGEA ppshpage);
100static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
101static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
102static BOOL PROPSHEET_Back(HWND hwndDlg);
103static BOOL PROPSHEET_Next(HWND hwndDlg);
104static BOOL PROPSHEET_Finish(HWND hwndDlg);
105static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam);
106static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam);
107static void PROPSHEET_Help(HWND hwndDlg);
108static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
109static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
110static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
111static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText);
112static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
113static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
114static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
115 int index,
116 int skipdir,
117 HPROPSHEETPAGE hpage);
118static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
119 WPARAM wParam, LPARAM lParam);
120static BOOL PROPSHEET_AddPage(HWND hwndDlg,
121 HPROPSHEETPAGE hpage);
122
123static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
124 int index,
125 HPROPSHEETPAGE hpage);
126static void PROPSHEET_CleanUp();
127static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
128static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags);
129static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
130static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg);
131
132BOOL WINAPI
133PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
134
135DEFAULT_DEBUG_CHANNEL(propsheet);
136
137/******************************************************************************
138 * PROPSHEET_CollectSheetInfo
139 *
140 * Collect relevant data.
141 */
142static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
143 PropSheetInfo * psInfo)
144{
145 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
146 DWORD dwFlags = lppsh->dwFlags;
147
148 psInfo->hasHelp = dwFlags & PSH_HASHELP;
149 psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
150 psInfo->useCallback = dwFlags & PSH_USECALLBACK;
151 psInfo->isModeless = dwFlags & PSH_MODELESS;
152
153 memcpy(&psInfo->ppshheader,lppsh,dwSize);
154 TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%04x\nhInstance\t%08x\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
155#ifdef __WIN32OS2__
156 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, HIWORD(lppsh->pszCaption) ? lppsh->pszCaption : NULL, lppsh->nPages, lppsh->pfnCallback);
157#else
158 lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, lppsh->pszCaption, lppsh->nPages, lppsh->pfnCallback);
159#endif
160
161 if (HIWORD(lppsh->pszCaption))
162 {
163 psInfo->ppshheader.pszCaption = HeapAlloc( GetProcessHeap(), 0, strlen(lppsh->pszCaption)+1 );
164 strcpy( (char *)psInfo->ppshheader.pszCaption, lppsh->pszCaption );
165 }
166 psInfo->nPages = lppsh->nPages;
167
168 if (dwFlags & PSH_USEPSTARTPAGE)
169 {
170 TRACE("PSH_USEPSTARTPAGE is on");
171 psInfo->active_page = 0;
172 }
173 else
174 psInfo->active_page = lppsh->u2.nStartPage;
175
176 if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
177 psInfo->active_page = 0;
178
179 psInfo->restartWindows = FALSE;
180 psInfo->rebootSystem = FALSE;
181 psInfo->hImageList = 0;
182 psInfo->activeValid = FALSE;
183
184 return TRUE;
185}
186
187/******************************************************************************
188 * PROPSHEET_CollectPageInfo
189 *
190 * Collect property sheet data.
191 * With code taken from DIALOG_ParseTemplate32.
192 */
193BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
194 PropSheetInfo * psInfo,
195 int index)
196{
197 DLGTEMPLATE* pTemplate;
198 const WORD* p;
199 DWORD dwFlags;
200 int width, height;
201
202 TRACE("\n");
203 psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
204 psInfo->proppage[index].hwndPage = 0;
205 psInfo->proppage[index].isDirty = FALSE;
206
207 /*
208 * Process property page flags.
209 */
210 dwFlags = lppsp->dwFlags;
211 psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
212 psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
213 psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
214
215 /* as soon as we have a page with the help flag, set the sheet flag on */
216 if (psInfo->proppage[index].hasHelp)
217 psInfo->hasHelp = TRUE;
218
219 /*
220 * Process page template.
221 */
222 if (dwFlags & PSP_DLGINDIRECT)
223 pTemplate = (DLGTEMPLATE*)lppsp->u1.pResource;
224 else
225 {
226 HRSRC hResource = FindResourceA(lppsp->hInstance,
227 lppsp->u1.pszTemplate,
228 RT_DIALOGA);
229 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
230 hResource);
231 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
232 }
233
234 /*
235 * Extract the size of the page and the caption.
236 */
237 if (!pTemplate)
238 return FALSE;
239
240 p = (const WORD *)pTemplate;
241
242 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
243 {
244 /* DIALOGEX template */
245
246 p++; /* dlgVer */
247 p++; /* signature */
248 p += 2; /* help ID */
249 p += 2; /* ext style */
250 p += 2; /* style */
251 }
252 else
253 {
254 /* DIALOG template */
255
256 p += 2; /* style */
257 p += 2; /* ext style */
258 }
259
260 p++; /* nb items */
261 p++; /* x */
262 p++; /* y */
263 width = (WORD)*p; p++;
264 height = (WORD)*p; p++;
265
266 /* remember the largest width and height */
267 if (width > psInfo->width)
268 psInfo->width = width;
269
270 if (height > psInfo->height)
271 psInfo->height = height;
272
273 /* menu */
274 switch ((WORD)*p)
275 {
276 case 0x0000:
277 p++;
278 break;
279 case 0xffff:
280 p += 2;
281 break;
282 default:
283 p += lstrlenW( (LPCWSTR)p ) + 1;
284 break;
285 }
286
287 /* class */
288 switch ((WORD)*p)
289 {
290 case 0x0000:
291 p++;
292 break;
293 case 0xffff:
294 p += 2;
295 break;
296 default:
297 p += lstrlenW( (LPCWSTR)p ) + 1;
298 break;
299 }
300
301 /* Extract the caption */
302 psInfo->proppage[index].pszText = (LPCWSTR)p;
303 TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
304 p += lstrlenW((LPCWSTR)p) + 1;
305
306 if (dwFlags & PSP_USETITLE)
307 {
308 if ( !HIWORD( lppsp->pszTitle ) )
309 {
310 char szTitle[256];
311
312 if (LoadStringA( lppsp->hInstance, (UINT)lppsp->pszTitle,szTitle,256 )) {
313 psInfo->proppage[index].pszText = HEAP_strdupAtoW( GetProcessHeap(), 0, szTitle );
314 } else {
315 psInfo->proppage[index].pszText = HEAP_strdupAtoW( GetProcessHeap(), 0, "(null)" );
316 FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
317 }
318
319 } else
320 psInfo->proppage[index].pszText = HEAP_strdupAtoW(GetProcessHeap(),
321 0,
322 lppsp->pszTitle);
323 }
324
325 /*
326 * Build the image list for icons
327 */
328 if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
329 {
330 HICON hIcon;
331 int icon_cx = GetSystemMetrics(SM_CXSMICON);
332 int icon_cy = GetSystemMetrics(SM_CYSMICON);
333
334 if (dwFlags & PSP_USEICONID)
335 hIcon = LoadImageA(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
336 icon_cx, icon_cy, LR_DEFAULTCOLOR);
337 else
338 hIcon = lppsp->u2.hIcon;
339
340 if ( hIcon )
341 {
342 if (psInfo->hImageList == 0 )
343 psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
344
345 ImageList_AddIcon(psInfo->hImageList, hIcon);
346 }
347
348 }
349
350 return TRUE;
351}
352
353
354/******************************************************************************
355 * PROPSHEET_CreateDialog
356 *
357 * Creates the actual property sheet.
358 */
359BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
360{
361 LRESULT ret;
362 LPCVOID template;
363 LPVOID temp = 0;
364 HRSRC hRes;
365 DWORD resSize;
366 WORD resID = IDD_PROPSHEET;
367
368 TRACE("\n");
369 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
370 resID = IDD_WIZARD;
371
372 if(!(hRes = FindResourceA(COMCTL32_hModule,
373 MAKEINTRESOURCEA(resID),
374 RT_DIALOGA)))
375 return FALSE;
376
377 if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
378 return FALSE;
379
380 /*
381 * Make a copy of the dialog template.
382 */
383 resSize = SizeofResource(COMCTL32_hModule, hRes);
384
385 temp = COMCTL32_Alloc(resSize);
386
387 if (!temp)
388 return FALSE;
389
390 memcpy(temp, template, resSize);
391
392 if (psInfo->useCallback)
393 (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
394
395 if (!(psInfo->ppshheader.dwFlags & PSH_MODELESS))
396 ret = DialogBoxIndirectParamA(psInfo->ppshheader.hInstance,
397 (LPDLGTEMPLATEA) temp,
398 psInfo->ppshheader.hwndParent,
399 (DLGPROC) PROPSHEET_DialogProc,
400 (LPARAM)psInfo);
401 else
402 ret = CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
403 (LPDLGTEMPLATEA) temp,
404 psInfo->ppshheader.hwndParent,
405 (DLGPROC) PROPSHEET_DialogProc,
406 (LPARAM)psInfo);
407
408 COMCTL32_Free(temp);
409
410 return ret;
411}
412
413/******************************************************************************
414 * PROPSHEET_SizeMismatch
415 *
416 * Verify that the tab control and the "largest" property sheet page dlg. template
417 * match in size.
418 */
419static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo)
420{
421 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
422 RECT rcOrigTab, rcPage;
423
424 /*
425 * Original tab size.
426 */
427 GetClientRect(hwndTabCtrl, &rcOrigTab);
428 TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
429 rcOrigTab.right, rcOrigTab.bottom);
430
431 /*
432 * Biggest page size.
433 */
434 rcPage.left = psInfo->x;
435 rcPage.top = psInfo->y;
436 rcPage.right = psInfo->width;
437 rcPage.bottom = psInfo->height;
438
439 MapDialogRect(hwndDlg, &rcPage);
440 TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
441 rcPage.right, rcPage.bottom);
442
443 if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
444 return TRUE;
445 if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
446 return TRUE;
447
448 return FALSE;
449}
450
451/******************************************************************************
452 * PROPSHEET_IsTooSmallWizard
453 *
454 * Verify that the default property sheet is big enough.
455 */
456static BOOL PROPSHEET_IsTooSmallWizard(HWND hwndDlg, PropSheetInfo* psInfo)
457{
458 RECT rcSheetRect, rcPage, rcLine, rcSheetClient;
459 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
460 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
461
462 GetClientRect(hwndDlg, &rcSheetClient);
463 GetWindowRect(hwndDlg, &rcSheetRect);
464 GetWindowRect(hwndLine, &rcLine);
465
466 /* Remove the space below the sunken line */
467 rcSheetClient.bottom -= (rcSheetRect.bottom - rcLine.top);
468
469 /* Remove the buffer zone all around the edge */
470 rcSheetClient.bottom -= (padding.y * 2);
471 rcSheetClient.right -= (padding.x * 2);
472
473 /*
474 * Biggest page size.
475 */
476 rcPage.left = psInfo->x;
477 rcPage.top = psInfo->y;
478 rcPage.right = psInfo->width;
479 rcPage.bottom = psInfo->height;
480
481 MapDialogRect(hwndDlg, &rcPage);
482 TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
483 rcPage.right, rcPage.bottom);
484
485 if (rcPage.right > rcSheetClient.right)
486 return TRUE;
487
488 if (rcPage.bottom > rcSheetClient.bottom)
489 return TRUE;
490
491 return FALSE;
492}
493
494/******************************************************************************
495 * PROPSHEET_AdjustSize
496 *
497 * Resizes the property sheet and the tab control to fit the largest page.
498 */
499static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
500{
501 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
502 HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
503 RECT rc,tabRect;
504 int tabOffsetX, tabOffsetY, buttonHeight;
505 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
506 RECT units;
507
508 /* Get the height of buttons */
509 GetClientRect(hwndButton, &rc);
510 buttonHeight = rc.bottom;
511
512 /*
513 * Biggest page size.
514 */
515 rc.left = psInfo->x;
516 rc.top = psInfo->y;
517 rc.right = psInfo->width;
518 rc.bottom = psInfo->height;
519
520 MapDialogRect(hwndDlg, &rc);
521
522 /* retrieve the dialog units */
523 units.left = units.right = 4;
524 units.top = units.bottom = 8;
525 MapDialogRect(hwndDlg, &units);
526
527 /*
528 * Resize the tab control.
529 */
530 GetClientRect(hwndTabCtrl,&tabRect);
531
532 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
533
534 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
535 {
536 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
537 psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
538 }
539
540 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
541 {
542 rc.right = rc.left + tabRect.right - tabRect.left;
543 psInfo->width = MulDiv((rc.right - rc.left),4,units.left);
544 }
545
546 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
547
548 tabOffsetX = -(rc.left);
549 tabOffsetY = -(rc.top);
550
551 rc.right -= rc.left;
552 rc.bottom -= rc.top;
553 SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
554 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
555
556 GetClientRect(hwndTabCtrl, &rc);
557
558 TRACE("tab client rc %d %d %d %d\n",
559 rc.left, rc.top, rc.right, rc.bottom);
560
561 rc.right += ((padding.x * 2) + tabOffsetX);
562 rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
563
564 /*
565 * Resize the property sheet.
566 */
567 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
568 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
569 return TRUE;
570}
571
572/******************************************************************************
573 * PROPSHEET_AdjustSizeWizard
574 *
575 * Resizes the property sheet to fit the largest page.
576 */
577static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo)
578{
579 HWND hwndButton = GetDlgItem(hwndDlg, IDCANCEL);
580 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
581 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
582 RECT rc,tabRect;
583 int buttonHeight, lineHeight;
584 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
585 RECT units;
586
587 /* Get the height of buttons */
588 GetClientRect(hwndButton, &rc);
589 buttonHeight = rc.bottom;
590
591 GetClientRect(hwndLine, &rc);
592 lineHeight = rc.bottom;
593
594 /* retrieve the dialog units */
595 units.left = units.right = 4;
596 units.top = units.bottom = 8;
597 MapDialogRect(hwndDlg, &units);
598
599 /*
600 * Biggest page size.
601 */
602 rc.left = psInfo->x;
603 rc.top = psInfo->y;
604 rc.right = psInfo->width;
605 rc.bottom = psInfo->height;
606
607 MapDialogRect(hwndDlg, &rc);
608
609 GetClientRect(hwndTabCtrl,&tabRect);
610
611 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
612 {
613 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
614 psInfo->height = MulDiv((rc.bottom - rc.top), 8, units.top);
615 }
616
617 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
618 {
619 rc.right = rc.left + tabRect.right - tabRect.left;
620 psInfo->width = MulDiv((rc.right - rc.left), 4, units.left);
621 }
622
623 TRACE("Biggest page %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom);
624
625 /* Make room */
626 rc.right += (padding.x * 2);
627 rc.bottom += (buttonHeight + (5 * padding.y) + lineHeight);
628
629 /*
630 * Resize the property sheet.
631 */
632 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
633 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
634 return TRUE;
635}
636
637/******************************************************************************
638 * PROPSHEET_AdjustButtons
639 *
640 * Adjusts the buttons' positions.
641 */
642static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
643{
644 HWND hwndButton = GetDlgItem(hwndParent, IDOK);
645 RECT rcSheet;
646 int x, y;
647 int num_buttons = 2;
648 int buttonWidth, buttonHeight;
649 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
650
651 if (psInfo->hasApply)
652 num_buttons++;
653
654 if (psInfo->hasHelp)
655 num_buttons++;
656
657 /*
658 * Obtain the size of the buttons.
659 */
660 GetClientRect(hwndButton, &rcSheet);
661 buttonWidth = rcSheet.right;
662 buttonHeight = rcSheet.bottom;
663
664 /*
665 * Get the size of the property sheet.
666 */
667 GetClientRect(hwndParent, &rcSheet);
668
669 /*
670 * All buttons will be at this y coordinate.
671 */
672 y = rcSheet.bottom - (padding.y + buttonHeight);
673
674 /*
675 * Position OK button.
676 */
677 hwndButton = GetDlgItem(hwndParent, IDOK);
678
679 x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
680
681 SetWindowPos(hwndButton, 0, x, y, 0, 0,
682 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
683
684 /*
685 * Position Cancel button.
686 */
687 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
688
689 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
690
691 SetWindowPos(hwndButton, 0, x, y, 0, 0,
692 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
693
694 /*
695 * Position Apply button.
696 */
697 hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
698
699 if (psInfo->hasApply)
700 {
701 if (psInfo->hasHelp)
702 x = rcSheet.right - ((padding.x + buttonWidth) * 2);
703 else
704 x = rcSheet.right - (padding.x + buttonWidth);
705
706 SetWindowPos(hwndButton, 0, x, y, 0, 0,
707 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
708
709 EnableWindow(hwndButton, FALSE);
710 }
711 else
712 ShowWindow(hwndButton, SW_HIDE);
713
714 /*
715 * Position Help button.
716 */
717 hwndButton = GetDlgItem(hwndParent, IDHELP);
718
719 if (psInfo->hasHelp)
720 {
721 x = rcSheet.right - (padding.x + buttonWidth);
722
723 SetWindowPos(hwndButton, 0, x, y, 0, 0,
724 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
725 }
726 else
727 ShowWindow(hwndButton, SW_HIDE);
728
729 return TRUE;
730}
731
732/******************************************************************************
733 * PROPSHEET_AdjustButtonsWizard
734 *
735 * Adjusts the buttons' positions.
736 */
737static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
738 PropSheetInfo* psInfo)
739{
740 HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
741 HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
742 RECT rcSheet;
743 int x, y;
744 int num_buttons = 3;
745 int buttonWidth, buttonHeight, lineHeight, lineWidth;
746 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
747
748 if (psInfo->hasHelp)
749 num_buttons++;
750
751 /*
752 * Obtain the size of the buttons.
753 */
754 GetClientRect(hwndButton, &rcSheet);
755 buttonWidth = rcSheet.right;
756 buttonHeight = rcSheet.bottom;
757
758 GetClientRect(hwndLine, &rcSheet);
759 lineHeight = rcSheet.bottom;
760
761 /*
762 * Get the size of the property sheet.
763 */
764 GetClientRect(hwndParent, &rcSheet);
765
766 /*
767 * All buttons will be at this y coordinate.
768 */
769 y = rcSheet.bottom - (padding.y + buttonHeight);
770
771 /*
772 * Position the Next and the Finish buttons.
773 */
774 hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
775
776 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
777
778 SetWindowPos(hwndButton, 0, x, y, 0, 0,
779 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
780
781 hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
782
783 SetWindowPos(hwndButton, 0, x, y, 0, 0,
784 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
785
786 ShowWindow(hwndButton, SW_HIDE);
787
788 /*
789 * Position the Back button.
790 */
791 hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
792
793 x -= buttonWidth;
794
795 SetWindowPos(hwndButton, 0, x, y, 0, 0,
796 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
797
798 /*
799 * Position the Cancel button.
800 */
801 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
802
803 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 2));
804
805 SetWindowPos(hwndButton, 0, x, y, 0, 0,
806 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
807
808 /*
809 * Position Help button.
810 */
811 hwndButton = GetDlgItem(hwndParent, IDHELP);
812
813 if (psInfo->hasHelp)
814 {
815 x = rcSheet.right - (padding.x + buttonWidth);
816
817 SetWindowPos(hwndButton, 0, x, y, 0, 0,
818 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
819 }
820 else
821 ShowWindow(hwndButton, SW_HIDE);
822
823 /*
824 * Position and resize the sunken line.
825 */
826 x = padding.x;
827 y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
828
829 GetClientRect(hwndParent, &rcSheet);
830 lineWidth = rcSheet.right - (padding.x * 2);
831
832 SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
833 SWP_NOZORDER | SWP_NOACTIVATE);
834
835 return TRUE;
836}
837
838/******************************************************************************
839 * PROPSHEET_GetPaddingInfo
840 *
841 * Returns the layout information.
842 */
843static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
844{
845 HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
846 RECT rcTab;
847 POINT tl;
848 PADDING_INFO padding;
849
850 GetWindowRect(hwndTab, &rcTab);
851
852 tl.x = rcTab.left;
853 tl.y = rcTab.top;
854
855 ScreenToClient(hwndDlg, &tl);
856
857 padding.x = tl.x;
858 padding.y = tl.y;
859
860 return padding;
861}
862
863/******************************************************************************
864 * PROPSHEET_GetPaddingInfoWizard
865 *
866 * Returns the layout information.
867 * Vertical spacing is the distance between the line and the buttons.
868 * Do NOT use the Help button to gather padding information when it isn't mapped
869 * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
870 * for it in this case !
871 * FIXME: I'm not sure about any other coordinate problems with these evil
872 * buttons. Fix it in case additional problems appear or maybe calculate
873 * a padding in a completely different way, as this is somewhat messy.
874 */
875static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
876 psInfo)
877{
878 PADDING_INFO padding;
879 RECT rc;
880 HWND hwndControl;
881 INT idButton;
882 POINT ptButton, ptLine;
883
884 TRACE("\n");
885 if (psInfo->hasHelp)
886 {
887 idButton = IDHELP;
888 }
889 else
890 {
891 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
892 {
893 idButton = IDC_NEXT_BUTTON;
894 }
895 else
896 {
897 /* hopefully this is ok */
898 idButton = IDCANCEL;
899 }
900 }
901
902 hwndControl = GetDlgItem(hwndDlg, idButton);
903 GetWindowRect(hwndControl, &rc);
904
905 ptButton.x = rc.left;
906 ptButton.y = rc.top;
907
908 ScreenToClient(hwndDlg, &ptButton);
909
910 /* Line */
911 hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
912 GetWindowRect(hwndControl, &rc);
913
914 ptLine.x = 0;
915 ptLine.y = rc.bottom;
916
917 ScreenToClient(hwndDlg, &ptLine);
918
919 padding.y = ptButton.y - ptLine.y;
920
921 if (padding.y < 0)
922 ERR("padding negative ! Please report this !\n");
923
924 /* this is most probably not correct, but the best we have now */
925 padding.x = padding.y;
926 return padding;
927}
928
929/******************************************************************************
930 * PROPSHEET_CreateTabControl
931 *
932 * Insert the tabs in the tab control.
933 */
934static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
935 PropSheetInfo * psInfo)
936{
937 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
938 TCITEMA item;
939 int i, nTabs;
940 int iImage = 0;
941 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
942
943 TRACE("\n");
944 item.mask = TCIF_TEXT;
945 item.pszText = tabtext;
946 item.cchTextMax = MAX_TABTEXT_LENGTH;
947
948 nTabs = psInfo->nPages;
949
950 /*
951 * Set the image list for icons.
952 */
953 if (psInfo->hImageList)
954 {
955 SendMessageA(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
956 }
957
958 for (i = 0; i < nTabs; i++)
959 {
960 if ( psInfo->proppage[i].hasIcon )
961 {
962 item.mask |= TCIF_IMAGE;
963 item.iImage = iImage++;
964 }
965 else
966 {
967 item.mask &= ~TCIF_IMAGE;
968 }
969
970 WideCharToMultiByte(CP_ACP, 0,
971 (LPCWSTR)psInfo->proppage[i].pszText,
972 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
973
974 SendMessageA(hwndTabCtrl, TCM_INSERTITEMA, (WPARAM)i, (LPARAM)&item);
975 }
976
977 return TRUE;
978}
979
980/******************************************************************************
981 * PROPSHEET_CreatePage
982 *
983 * Creates a page.
984 */
985static BOOL PROPSHEET_CreatePage(HWND hwndParent,
986 int index,
987 const PropSheetInfo * psInfo,
988 LPCPROPSHEETPAGEA ppshpage)
989{
990 DLGTEMPLATE* pTemplate;
991 HWND hwndPage;
992 RECT rc;
993 PropPageInfo* ppInfo = psInfo->proppage;
994 PADDING_INFO padding;
995 UINT pageWidth,pageHeight;
996 DWORD resSize;
997 LPVOID temp = NULL;
998
999 TRACE("index %d\n", index);
1000
1001#ifdef __WIN32OS2__
1002 //AH: Check if ppshpage is valid
1003 if (ppshpage == NULL)
1004 {
1005 dprintf(("COMCTL32:PROPSHEET_CreatePage: ERROR!!! ppshpage == NULL!!!\n"));
1006 return FALSE;
1007 }
1008#endif
1009
1010 if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1011 pTemplate = (DLGTEMPLATE*)ppshpage->u1.pResource;
1012 else
1013 {
1014 HRSRC hResource;
1015 HANDLE hTemplate;
1016
1017 hResource = FindResourceA(ppshpage->hInstance,
1018 ppshpage->u1.pszTemplate,
1019 RT_DIALOGA);
1020 if(!hResource)
1021 return FALSE;
1022
1023 resSize = SizeofResource(ppshpage->hInstance, hResource);
1024
1025 hTemplate = LoadResource(ppshpage->hInstance, hResource);
1026 if(!hTemplate)
1027 return FALSE;
1028
1029 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
1030 /*
1031 * Make a copy of the dialog template to make it writable
1032 */
1033 temp = COMCTL32_Alloc(resSize);
1034 if (!temp)
1035 return FALSE;
1036
1037 memcpy(temp, pTemplate, resSize);
1038 pTemplate = temp;
1039 }
1040
1041 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
1042 {
1043 ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | DS_CONTROL;
1044 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
1045 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
1046 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
1047 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
1048 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED;
1049 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE;
1050#ifdef __WIN32OS2__
1051 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_THICKFRAME;
1052#endif
1053 }
1054 else
1055 {
1056 pTemplate->style |= WS_CHILD | DS_CONTROL;
1057 pTemplate->style &= ~DS_MODALFRAME;
1058 pTemplate->style &= ~WS_CAPTION;
1059 pTemplate->style &= ~WS_SYSMENU;
1060 pTemplate->style &= ~WS_POPUP;
1061 pTemplate->style &= ~WS_DISABLED;
1062 pTemplate->style &= ~WS_VISIBLE;
1063#ifdef __WIN32OS2__
1064 pTemplate->style &= ~WS_THICKFRAME;
1065#endif
1066 }
1067
1068 if (psInfo->proppage[index].useCallback)
1069 (*(ppshpage->pfnCallback))(hwndParent,
1070 PSPCB_CREATE,
1071 (LPPROPSHEETPAGEA)ppshpage);
1072
1073 hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1074 pTemplate,
1075 hwndParent,
1076 ppshpage->pfnDlgProc,
1077 (LPARAM)ppshpage);
1078 /* Free a no more needed copy */
1079 if(temp)
1080 COMCTL32_Free(temp);
1081
1082 ppInfo[index].hwndPage = hwndPage;
1083
1084 rc.left = psInfo->x;
1085 rc.top = psInfo->y;
1086 rc.right = psInfo->width;
1087 rc.bottom = psInfo->height;
1088
1089 MapDialogRect(hwndParent, &rc);
1090
1091 pageWidth = rc.right - rc.left;
1092 pageHeight = rc.bottom - rc.top;
1093
1094 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
1095 padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
1096 else
1097 {
1098 /*
1099 * Ask the Tab control to fit this page in.
1100 */
1101
1102 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1103 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc);
1104 padding = PROPSHEET_GetPaddingInfo(hwndParent);
1105 }
1106
1107 SetWindowPos(hwndPage, HWND_TOP,
1108 rc.left + padding.x,
1109 rc.top + padding.y,
1110 pageWidth, pageHeight, 0);
1111
1112 return TRUE;
1113}
1114
1115/******************************************************************************
1116 * PROPSHEET_ShowPage
1117 *
1118 * Displays or creates the specified page.
1119 */
1120static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1121{
1122 HWND hwndTabCtrl;
1123
1124 TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1125 if (index == psInfo->active_page)
1126 {
1127 if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1128 SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1129 return TRUE;
1130 }
1131
1132 if (psInfo->proppage[index].hwndPage == 0)
1133 {
1134 LPCPROPSHEETPAGEA ppshpage;
1135
1136 ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[index].hpage;
1137 PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1138 }
1139
1140 if (psInfo->active_page != -1)
1141 ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1142
1143 ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1144
1145 /* Synchronize current selection with tab control
1146 * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1147 hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1148 SendMessageA(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1149
1150 psInfo->active_page = index;
1151 psInfo->activeValid = TRUE;
1152
1153 return TRUE;
1154}
1155
1156/******************************************************************************
1157 * PROPSHEET_Back
1158 */
1159static BOOL PROPSHEET_Back(HWND hwndDlg)
1160{
1161 BOOL res;
1162 PSHNOTIFY psn;
1163 HWND hwndPage;
1164 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1165 PropSheetInfoStr);
1166 LRESULT result;
1167
1168 TRACE("active_page %d\n", psInfo->active_page);
1169 if (psInfo->active_page < 0)
1170 return FALSE;
1171
1172 psn.hdr.code = PSN_WIZBACK;
1173 psn.hdr.hwndFrom = hwndDlg;
1174 psn.hdr.idFrom = 0;
1175 psn.lParam = 0;
1176
1177 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1178
1179 result = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1180 if (result == -1)
1181 return FALSE;
1182
1183 if (psInfo->active_page > 0)
1184 {
1185 res = PROPSHEET_CanSetCurSel(hwndDlg);
1186 if(res != FALSE)
1187 {
1188 res = PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page - 1, -1, 0);
1189 }
1190 }
1191
1192 return TRUE;
1193}
1194
1195/******************************************************************************
1196 * PROPSHEET_Next
1197 */
1198static BOOL PROPSHEET_Next(HWND hwndDlg)
1199{
1200 PSHNOTIFY psn;
1201 HWND hwndPage;
1202 LRESULT msgResult = 0;
1203 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1204 PropSheetInfoStr);
1205
1206 TRACE("active_page %d\n", psInfo->active_page);
1207 if (psInfo->active_page < 0)
1208 return FALSE;
1209
1210 psn.hdr.code = PSN_WIZNEXT;
1211 psn.hdr.hwndFrom = hwndDlg;
1212 psn.hdr.idFrom = 0;
1213 psn.lParam = 0;
1214
1215 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1216
1217 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1218 if (msgResult == -1)
1219 return FALSE;
1220
1221 if(PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1222 {
1223 PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page + 1, 1, 0);
1224 }
1225
1226 return TRUE;
1227}
1228
1229/******************************************************************************
1230 * PROPSHEET_Finish
1231 */
1232static BOOL PROPSHEET_Finish(HWND hwndDlg)
1233{
1234 PSHNOTIFY psn;
1235 HWND hwndPage;
1236 LRESULT msgResult = 0;
1237 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1238 PropSheetInfoStr);
1239
1240 TRACE("active_page %d\n", psInfo->active_page);
1241 if (psInfo->active_page < 0)
1242 return FALSE;
1243
1244 psn.hdr.code = PSN_WIZFINISH;
1245 psn.hdr.hwndFrom = hwndDlg;
1246 psn.hdr.idFrom = 0;
1247 psn.lParam = 0;
1248
1249 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1250
1251 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1252
1253 TRACE("msg result %ld\n", msgResult);
1254
1255 if (msgResult != 0)
1256 return FALSE;
1257
1258 if (psInfo->isModeless)
1259 psInfo->activeValid = FALSE;
1260 else
1261 EndDialog(hwndDlg, TRUE);
1262
1263 return TRUE;
1264}
1265
1266/******************************************************************************
1267 * PROPSHEET_Apply
1268 */
1269static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1270{
1271 int i;
1272 HWND hwndPage;
1273 PSHNOTIFY psn;
1274 LRESULT msgResult;
1275 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1276 PropSheetInfoStr);
1277
1278 TRACE("active_page %d\n", psInfo->active_page);
1279 if (psInfo->active_page < 0)
1280 return FALSE;
1281
1282 psn.hdr.hwndFrom = hwndDlg;
1283 psn.hdr.idFrom = 0;
1284 psn.lParam = 0;
1285
1286
1287 /*
1288 * Send PSN_KILLACTIVE to the current page.
1289 */
1290 psn.hdr.code = PSN_KILLACTIVE;
1291
1292 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1293
1294 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1295 return FALSE;
1296
1297 /*
1298 * Send PSN_APPLY to all pages.
1299 */
1300 psn.hdr.code = PSN_APPLY;
1301 psn.lParam = lParam;
1302
1303 for (i = 0; i < psInfo->nPages; i++)
1304 {
1305 hwndPage = psInfo->proppage[i].hwndPage;
1306 if (hwndPage)
1307 {
1308 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1309 if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
1310 return FALSE;
1311 }
1312 }
1313
1314 if(lParam)
1315 {
1316 psInfo->activeValid = FALSE;
1317 }
1318 else if(psInfo->active_page >= 0)
1319 {
1320 psn.hdr.code = PSN_SETACTIVE;
1321 psn.lParam = 0;
1322 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1323 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1324 }
1325
1326 return TRUE;
1327}
1328
1329/******************************************************************************
1330 * PROPSHEET_Cancel
1331 */
1332static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1333{
1334 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1335 PropSheetInfoStr);
1336 HWND hwndPage;
1337 PSHNOTIFY psn;
1338 int i;
1339
1340 TRACE("active_page %d\n", psInfo->active_page);
1341 if (psInfo->active_page < 0)
1342 return;
1343
1344 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1345 psn.hdr.code = PSN_QUERYCANCEL;
1346 psn.hdr.hwndFrom = hwndDlg;
1347 psn.hdr.idFrom = 0;
1348 psn.lParam = 0;
1349
1350 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1351 return;
1352
1353 psn.hdr.code = PSN_RESET;
1354 psn.lParam = lParam;
1355
1356 for (i = 0; i < psInfo->nPages; i++)
1357 {
1358 hwndPage = psInfo->proppage[i].hwndPage;
1359
1360 if (hwndPage)
1361 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1362 }
1363
1364 if (psInfo->isModeless)
1365 {
1366 /* makes PSM_GETCURRENTPAGEHWND return NULL */
1367 psInfo->activeValid = FALSE;
1368 }
1369 else
1370 EndDialog(hwndDlg, FALSE);
1371}
1372
1373/******************************************************************************
1374 * PROPSHEET_Help
1375 */
1376static void PROPSHEET_Help(HWND hwndDlg)
1377{
1378 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1379 PropSheetInfoStr);
1380 HWND hwndPage;
1381 PSHNOTIFY psn;
1382
1383 TRACE("active_page %d\n", psInfo->active_page);
1384 if (psInfo->active_page < 0)
1385 return;
1386
1387 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1388 psn.hdr.code = PSN_HELP;
1389 psn.hdr.hwndFrom = hwndDlg;
1390 psn.hdr.idFrom = 0;
1391 psn.lParam = 0;
1392
1393 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1394}
1395
1396/******************************************************************************
1397 * PROPSHEET_Changed
1398 */
1399static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1400{
1401 int i;
1402 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1403 PropSheetInfoStr);
1404
1405 TRACE("\n");
1406 if (!psInfo) return;
1407 /*
1408 * Set the dirty flag of this page.
1409 */
1410 for (i = 0; i < psInfo->nPages; i++)
1411 {
1412 if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1413 psInfo->proppage[i].isDirty = TRUE;
1414 }
1415
1416 /*
1417 * Enable the Apply button.
1418 */
1419 if (psInfo->hasApply)
1420 {
1421 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1422
1423 EnableWindow(hwndApplyBtn, TRUE);
1424 }
1425}
1426
1427/******************************************************************************
1428 * PROPSHEET_UnChanged
1429 */
1430static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1431{
1432 int i;
1433 BOOL noPageDirty = TRUE;
1434 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1435 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1436 PropSheetInfoStr);
1437
1438 TRACE("\n");
1439 if ( !psInfo ) return;
1440 for (i = 0; i < psInfo->nPages; i++)
1441 {
1442 /* set the specified page as clean */
1443 if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1444 psInfo->proppage[i].isDirty = FALSE;
1445
1446 /* look to see if there's any dirty pages */
1447 if (psInfo->proppage[i].isDirty)
1448 noPageDirty = FALSE;
1449 }
1450
1451 /*
1452 * Disable Apply button.
1453 */
1454 if (noPageDirty)
1455 EnableWindow(hwndApplyBtn, FALSE);
1456}
1457
1458/******************************************************************************
1459 * PROPSHEET_PressButton
1460 */
1461static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1462{
1463 TRACE("buttonID %d\n", buttonID);
1464 switch (buttonID)
1465 {
1466 case PSBTN_APPLYNOW:
1467 SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0);
1468 break;
1469 case PSBTN_BACK:
1470 PROPSHEET_Back(hwndDlg);
1471 break;
1472 case PSBTN_CANCEL:
1473 SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0);
1474 break;
1475 case PSBTN_FINISH:
1476 PROPSHEET_Finish(hwndDlg);
1477 break;
1478 case PSBTN_HELP:
1479 SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0);
1480 break;
1481 case PSBTN_NEXT:
1482 PROPSHEET_Next(hwndDlg);
1483 break;
1484 case PSBTN_OK:
1485 SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0);
1486 break;
1487 default:
1488 FIXME("Invalid button index %d\n", buttonID);
1489 }
1490}
1491
1492
1493/*************************************************************************
1494 * BOOL PROPSHEET_CanSetCurSel [Internal]
1495 *
1496 * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1497 *
1498 * PARAMS
1499 * hwndDlg [I] handle to a Dialog hWnd
1500 *
1501 * RETURNS
1502 * TRUE if Current Selection can change
1503 *
1504 * NOTES
1505 */
1506static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1507{
1508 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1509 PropSheetInfoStr);
1510 HWND hwndPage;
1511 PSHNOTIFY psn;
1512 BOOL res = FALSE;
1513
1514 TRACE("active_page %d\n", psInfo->active_page);
1515 if (!psInfo)
1516 {
1517 res = FALSE;
1518 goto end;
1519 }
1520
1521 if (psInfo->active_page < 0)
1522 {
1523 res = TRUE;
1524 goto end;
1525 }
1526
1527 /*
1528 * Notify the current page.
1529 */
1530 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1531 psn.hdr.code = PSN_KILLACTIVE;
1532 psn.hdr.hwndFrom = hwndDlg;
1533 psn.hdr.idFrom = 0;
1534 psn.lParam = 0;
1535
1536 res = !SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1537
1538end:
1539 TRACE("<-- %d\n", res);
1540 return res;
1541}
1542
1543/******************************************************************************
1544 * PROPSHEET_SetCurSel
1545 */
1546static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1547 int index,
1548 int skipdir,
1549 HPROPSHEETPAGE hpage
1550 )
1551{
1552 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr);
1553 HWND hwndHelp = GetDlgItem(hwndDlg, IDHELP);
1554
1555 TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1556 /* hpage takes precedence over index */
1557 if (hpage != NULL)
1558 index = PROPSHEET_GetPageIndex(hpage, psInfo);
1559
1560 if (index < 0 || index >= psInfo->nPages)
1561 {
1562 TRACE("Could not find page to select!\n");
1563 return FALSE;
1564 }
1565
1566 while (1) {
1567 int result;
1568 PSHNOTIFY psn;
1569
1570 psn.hdr.code = PSN_SETACTIVE;
1571 psn.hdr.hwndFrom = hwndDlg;
1572 psn.hdr.idFrom = 0;
1573 psn.lParam = 0;
1574
1575 if (!psInfo->proppage[index].hwndPage) {
1576 LPCPROPSHEETPAGEA ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[index].hpage;
1577 PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1578 }
1579
1580 result = SendMessageA(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1581 if (!result)
1582 break;
1583 if (result == -1) {
1584 index+=skipdir;
1585 if (index < 0) {
1586 index = 0;
1587 FIXME("Tried to skip before first property sheet page!\n");
1588 break;
1589 }
1590 if (index >= psInfo->nPages) {
1591 FIXME("Tried to skip after last property sheet page!\n");
1592 index = psInfo->nPages-1;
1593 break;
1594 }
1595 }
1596 }
1597 /*
1598 * Display the new page.
1599 */
1600 PROPSHEET_ShowPage(hwndDlg, index, psInfo);
1601
1602 if (psInfo->proppage[index].hasHelp)
1603 EnableWindow(hwndHelp, TRUE);
1604 else
1605 EnableWindow(hwndHelp, FALSE);
1606
1607 return TRUE;
1608}
1609
1610/******************************************************************************
1611 * PROPSHEET_SetTitleA
1612 */
1613static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
1614{
1615 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr);
1616 char szTitle[256];
1617
1618 TRACE("'%s' (style %08lx)\n", lpszText, dwStyle);
1619 if (HIWORD(lpszText) == 0) {
1620 if (!LoadStringA(psInfo->ppshheader.hInstance,
1621 LOWORD(lpszText), szTitle, sizeof(szTitle)-1))
1622 return;
1623 lpszText = szTitle;
1624 }
1625 if (dwStyle & PSH_PROPTITLE)
1626 {
1627 char* dest;
1628 int lentitle = strlen(lpszText);
1629 int lenprop = strlen(psInfo->strPropertiesFor);
1630
1631 dest = COMCTL32_Alloc(lentitle + lenprop + 1);
1632 strcpy(dest, psInfo->strPropertiesFor);
1633 strcat(dest, lpszText);
1634
1635 SetWindowTextA(hwndDlg, dest);
1636 COMCTL32_Free(dest);
1637 }
1638 else
1639 SetWindowTextA(hwndDlg, lpszText);
1640}
1641
1642/******************************************************************************
1643 * PROPSHEET_SetFinishTextA
1644 */
1645static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
1646{
1647 HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1648
1649 TRACE("'%s'\n", lpszText);
1650 /* Set text, show and enable the Finish button */
1651 SetWindowTextA(hwndButton, lpszText);
1652 ShowWindow(hwndButton, SW_SHOW);
1653 EnableWindow(hwndButton, TRUE);
1654
1655 /* Make it default pushbutton */
1656 SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1657
1658 /* Hide Back button */
1659 hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1660 ShowWindow(hwndButton, SW_HIDE);
1661
1662 /* Hide Next button */
1663 hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1664 ShowWindow(hwndButton, SW_HIDE);
1665}
1666
1667/******************************************************************************
1668 * PROPSHEET_QuerySiblings
1669 */
1670static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
1671 WPARAM wParam, LPARAM lParam)
1672{
1673 int i = 0;
1674 HWND hwndPage;
1675 LRESULT msgResult = 0;
1676 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1677 PropSheetInfoStr);
1678
1679 while ((i < psInfo->nPages) && (msgResult == 0))
1680 {
1681 hwndPage = psInfo->proppage[i].hwndPage;
1682 msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
1683 i++;
1684 }
1685
1686 return msgResult;
1687}
1688
1689
1690/******************************************************************************
1691 * PROPSHEET_AddPage
1692 */
1693static BOOL PROPSHEET_AddPage(HWND hwndDlg,
1694 HPROPSHEETPAGE hpage)
1695{
1696 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1697 PropSheetInfoStr);
1698 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1699 TCITEMA item;
1700 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
1701 LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage;
1702
1703 TRACE("hpage %p\n", hpage);
1704 /*
1705 * Allocate and fill in a new PropPageInfo entry.
1706 */
1707 psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
1708 sizeof(PropPageInfo) *
1709 (psInfo->nPages + 1));
1710 if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
1711 return FALSE;
1712
1713 psInfo->proppage[psInfo->nPages].hpage = hpage;
1714
1715 if (ppsp->dwFlags & PSP_PREMATURE)
1716 {
1717 /* Create the page but don't show it */
1718 PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
1719 }
1720
1721 /*
1722 * Add a new tab to the tab control.
1723 */
1724 item.mask = TCIF_TEXT;
1725 item.pszText = tabtext;
1726 item.cchTextMax = MAX_TABTEXT_LENGTH;
1727
1728 WideCharToMultiByte(CP_ACP, 0,
1729 (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText,
1730 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
1731
1732 SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1,
1733 (LPARAM)&item);
1734
1735 psInfo->nPages++;
1736
1737 /* If it is the only page - show it */
1738 if(psInfo->nPages == 1)
1739 PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
1740 return TRUE;
1741}
1742
1743/******************************************************************************
1744 * PROPSHEET_RemovePage
1745 */
1746static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1747 int index,
1748 HPROPSHEETPAGE hpage)
1749{
1750 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1751 PropSheetInfoStr);
1752 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1753 PropPageInfo* oldPages;
1754
1755 TRACE("index %d, hpage %p\n", index, hpage);
1756 if (!psInfo) {
1757 return FALSE;
1758 }
1759 oldPages = psInfo->proppage;
1760 /*
1761 * hpage takes precedence over index.
1762 */
1763 if (hpage != 0)
1764 {
1765 index = PROPSHEET_GetPageIndex(hpage, psInfo);
1766 }
1767
1768 /* Make sure that index is within range */
1769 if (index < 0 || index >= psInfo->nPages)
1770 {
1771 TRACE("Could not find page to remove!\n");
1772 return FALSE;
1773 }
1774
1775 TRACE("total pages %d removing page %d active page %d\n",
1776 psInfo->nPages, index, psInfo->active_page);
1777 /*
1778 * Check if we're removing the active page.
1779 */
1780 if (index == psInfo->active_page)
1781 {
1782 if (psInfo->nPages > 1)
1783 {
1784 if (index > 0)
1785 {
1786 /* activate previous page */
1787 PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
1788 }
1789 else
1790 {
1791 /* activate the next page */
1792 PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
1793 psInfo->active_page = index;
1794 }
1795 }
1796 else
1797 {
1798 psInfo->active_page = -1;
1799 if (!psInfo->isModeless)
1800 {
1801 EndDialog(hwndDlg, FALSE);
1802 return TRUE;
1803 }
1804 }
1805 }
1806 else if (index < psInfo->active_page)
1807 psInfo->active_page--;
1808
1809 /* Destroy page dialog window */
1810 DestroyWindow(psInfo->proppage[index].hwndPage);
1811
1812 /* Free page resources */
1813 if(psInfo->proppage[index].hpage)
1814 {
1815 PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[index].hpage;
1816
1817 if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
1818 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[index].pszText);
1819
1820 DestroyPropertySheetPage(psInfo->proppage[index].hpage);
1821 }
1822
1823 /* Remove the tab */
1824 SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0);
1825
1826 psInfo->nPages--;
1827 psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
1828
1829 if (index > 0)
1830 memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
1831
1832 if (index < psInfo->nPages)
1833 memcpy(&psInfo->proppage[index], &oldPages[index + 1],
1834 (psInfo->nPages - index) * sizeof(PropPageInfo));
1835
1836 COMCTL32_Free(oldPages);
1837
1838 return FALSE;
1839}
1840
1841/******************************************************************************
1842 * PROPSHEET_SetWizButtons
1843 *
1844 * This code will work if (and assumes that) the Next button is on top of the
1845 * Finish button. ie. Finish comes after Next in the Z order.
1846 * This means make sure the dialog template reflects this.
1847 *
1848 */
1849static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
1850{
1851 HWND hwndBack = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1852 HWND hwndNext = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1853 HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1854
1855 TRACE("%ld\n", dwFlags);
1856
1857 EnableWindow(hwndBack, FALSE);
1858 EnableWindow(hwndNext, FALSE);
1859 EnableWindow(hwndFinish, FALSE);
1860
1861 if (dwFlags & PSWIZB_BACK)
1862 EnableWindow(hwndBack, TRUE);
1863
1864 if (dwFlags & PSWIZB_NEXT)
1865 {
1866 /* Hide the Finish button */
1867 ShowWindow(hwndFinish, SW_HIDE);
1868
1869 /* Show and enable the Next button */
1870 ShowWindow(hwndNext, SW_SHOW);
1871 EnableWindow(hwndNext, TRUE);
1872
1873 /* Set the Next button as the default pushbutton */
1874 SendMessageA(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1875 }
1876
1877 if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
1878 {
1879 /* Hide the Next button */
1880 ShowWindow(hwndNext, SW_HIDE);
1881
1882 /* Show the Finish button */
1883 ShowWindow(hwndFinish, SW_SHOW);
1884
1885 if (dwFlags & PSWIZB_FINISH)
1886 EnableWindow(hwndFinish, TRUE);
1887
1888 /* Set the Finish button as the default pushbutton */
1889 SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1890 }
1891}
1892
1893/******************************************************************************
1894 * PROPSHEET_GetPageIndex
1895 *
1896 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
1897 * the array of PropPageInfo.
1898 */
1899static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
1900{
1901 BOOL found = FALSE;
1902 int index = 0;
1903
1904 TRACE("hpage %p\n", hpage);
1905 while ((index < psInfo->nPages) && (found == FALSE))
1906 {
1907 if (psInfo->proppage[index].hpage == hpage)
1908 found = TRUE;
1909 else
1910 index++;
1911 }
1912
1913 if (found == FALSE)
1914 index = -1;
1915
1916 return index;
1917}
1918
1919/******************************************************************************
1920 * PROPSHEET_CleanUp
1921 */
1922static void PROPSHEET_CleanUp(HWND hwndDlg)
1923{
1924 int i;
1925 PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg,
1926 PropSheetInfoStr);
1927
1928 TRACE("\n");
1929 if (HIWORD(psInfo->ppshheader.pszCaption))
1930 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->ppshheader.pszCaption);
1931
1932 for (i = 0; i < psInfo->nPages; i++)
1933 {
1934 PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
1935
1936 if(psInfo->proppage[i].hwndPage)
1937 DestroyWindow(psInfo->proppage[i].hwndPage);
1938
1939 if(psp)
1940 {
1941 if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
1942 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[i].pszText);
1943
1944 DestroyPropertySheetPage(psInfo->proppage[i].hpage);
1945 }
1946 }
1947
1948 COMCTL32_Free(psInfo->proppage);
1949 COMCTL32_Free(psInfo->strPropertiesFor);
1950 ImageList_Destroy(psInfo->hImageList);
1951
1952 GlobalFree((HGLOBAL)psInfo);
1953}
1954
1955/******************************************************************************
1956 * PropertySheet (COMCTL32.87)
1957 * PropertySheetA (COMCTL32.88)
1958 */
1959INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
1960{
1961 int bRet = 0;
1962 PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
1963 sizeof(PropSheetInfo));
1964 int i, n;
1965 BYTE* pByte;
1966
1967 PROPSHEET_CollectSheetInfo(lppsh, psInfo);
1968
1969 psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
1970 lppsh->nPages);
1971 pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
1972
1973 for (n = i = 0; i < lppsh->nPages; i++, n++)
1974 {
1975 if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
1976 psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
1977 else
1978 {
1979 psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
1980 pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
1981 }
1982
1983 if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEA)psInfo->proppage[n].hpage,
1984 psInfo, n))
1985 {
1986 if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
1987 DestroyPropertySheetPage(psInfo->proppage[n].hpage);
1988 n--;
1989 psInfo->nPages--;
1990 }
1991 }
1992
1993 bRet = PROPSHEET_CreateDialog(psInfo);
1994
1995 return bRet;
1996}
1997
1998/******************************************************************************
1999 * PropertySheetW (COMCTL32.89)
2000 */
2001INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader)
2002{
2003 FIXME("(%p): stub\n", propertySheetHeader);
2004
2005 return -1;
2006}
2007
2008/******************************************************************************
2009 * CreatePropertySheetPage (COMCTL32.18)
2010 * CreatePropertySheetPageA (COMCTL32.19)
2011 */
2012HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2013 LPCPROPSHEETPAGEA lpPropSheetPage)
2014{
2015 PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA));
2016
2017 memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2018
2019 if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u1.pszTemplate ) )
2020 {
2021 ppsp->u1.pszTemplate = HeapAlloc( GetProcessHeap(),0,strlen(lpPropSheetPage->u1.pszTemplate)+1 );
2022 strcpy( (char *)ppsp->u1.pszTemplate, lpPropSheetPage->u1.pszTemplate );
2023 }
2024 if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2025 {
2026 ppsp->u2.pszIcon = HeapAlloc( GetProcessHeap(), 0, strlen(lpPropSheetPage->u2.pszIcon)+1 );
2027 strcpy( (char *)ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon );
2028 }
2029
2030 if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2031 {
2032 ppsp->pszTitle = HeapAlloc( GetProcessHeap(), 0, strlen(lpPropSheetPage->pszTitle)+1 );
2033 strcpy( (char *)ppsp->pszTitle, lpPropSheetPage->pszTitle );
2034 }
2035 else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2036 ppsp->pszTitle = NULL;
2037
2038 return (HPROPSHEETPAGE)ppsp;
2039}
2040
2041/******************************************************************************
2042 * CreatePropertySheetPageW (COMCTL32.20)
2043 */
2044HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
2045{
2046 FIXME("(%p): stub\n", lpPropSheetPage);
2047
2048 return 0;
2049}
2050
2051/******************************************************************************
2052 * DestroyPropertySheetPage (COMCTL32.24)
2053 */
2054BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2055{
2056 PROPSHEETPAGEA *psp = (PROPSHEETPAGEA *)hPropPage;
2057
2058 if (!psp)
2059 return FALSE;
2060
2061 if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u1.pszTemplate ) )
2062 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u1.pszTemplate);
2063
2064 if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2065 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u2.pszIcon);
2066
2067 if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2068 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->pszTitle);
2069
2070 COMCTL32_Free(hPropPage);
2071
2072 return TRUE;
2073}
2074
2075/******************************************************************************
2076 * PROPSHEET_IsDialogMessage
2077 */
2078static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
2079{
2080 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr);
2081
2082 TRACE("\n");
2083 if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
2084 return FALSE;
2085
2086 if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
2087 {
2088 int new_page = 0;
2089 INT dlgCode = SendMessageA(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
2090
2091 if (!(dlgCode & DLGC_WANTMESSAGE))
2092 {
2093 switch (lpMsg->wParam)
2094 {
2095 case VK_TAB:
2096 if (GetKeyState(VK_SHIFT) & 0x8000)
2097 new_page = -1;
2098 else
2099 new_page = 1;
2100 break;
2101
2102 case VK_NEXT: new_page = 1; break;
2103 case VK_PRIOR: new_page = -1; break;
2104 }
2105 }
2106
2107 if (new_page)
2108 {
2109 if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
2110 {
2111 new_page += psInfo->active_page;
2112
2113 if (new_page < 0)
2114 new_page = psInfo->nPages - 1;
2115 else if (new_page >= psInfo->nPages)
2116 new_page = 0;
2117
2118 PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
2119 }
2120
2121 return TRUE;
2122 }
2123 }
2124
2125 return IsDialogMessageA(hwnd, lpMsg);
2126}
2127
2128/******************************************************************************
2129 * PROPSHEET_DialogProc
2130 */
2131BOOL WINAPI
2132PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2133{
2134 switch (uMsg)
2135 {
2136 case WM_INITDIALOG:
2137 {
2138 PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
2139 char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH);
2140 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2141 LPCPROPSHEETPAGEA ppshpage;
2142 int idx;
2143
2144 SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
2145
2146 /*
2147 * psInfo->hwnd is not being used by WINE code - it exists
2148 * for compatibility with "real" Windoze. The same about
2149 * SetWindowLong - WINE is only using the PropSheetInfoStr
2150 * property.
2151 */
2152 psInfo->hwnd = hwnd;
2153 SetWindowLongA(hwnd,DWL_USER,(LONG)psInfo);
2154
2155 /*
2156 * Small icon in the title bar.
2157 */
2158 if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
2159 (psInfo->ppshheader.dwFlags & PSH_USEHICON))
2160 {
2161 HICON hIcon;
2162 int icon_cx = GetSystemMetrics(SM_CXSMICON);
2163 int icon_cy = GetSystemMetrics(SM_CYSMICON);
2164
2165 if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
2166 hIcon = LoadImageA(psInfo->ppshheader.hInstance,
2167 psInfo->ppshheader.u1.pszIcon,
2168 IMAGE_ICON,
2169 icon_cx, icon_cy,
2170 LR_DEFAULTCOLOR);
2171 else
2172 hIcon = psInfo->ppshheader.u1.hIcon;
2173
2174 SendMessageA(hwnd, WM_SETICON, 0, hIcon);
2175 }
2176
2177 if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
2178 SendMessageA(hwnd, WM_SETICON, 0, psInfo->ppshheader.u1.hIcon);
2179
2180 psInfo->strPropertiesFor = strCaption;
2181
2182 GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
2183
2184 PROPSHEET_CreateTabControl(hwnd, psInfo);
2185
2186 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
2187 {
2188 if (PROPSHEET_IsTooSmallWizard(hwnd, psInfo))
2189 {
2190 PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
2191 PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
2192 }
2193 }
2194 else
2195 {
2196 if (PROPSHEET_SizeMismatch(hwnd, psInfo))
2197 {
2198 PROPSHEET_AdjustSize(hwnd, psInfo);
2199 PROPSHEET_AdjustButtons(hwnd, psInfo);
2200 }
2201 }
2202
2203 if (psInfo->useCallback)
2204 (*(psInfo->ppshheader.pfnCallback))(hwnd,
2205 PSCB_INITIALIZED, (LPARAM)0);
2206
2207 idx = psInfo->active_page;
2208 ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[idx].hpage;
2209 psInfo->active_page = -1;
2210
2211 PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
2212
2213 /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
2214 * as some programs call TCM_GETCURSEL to get the current selection
2215 * from which to switch to the next page */
2216 SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
2217
2218 if (!HIWORD(psInfo->ppshheader.pszCaption) &&
2219 psInfo->ppshheader.hInstance)
2220 {
2221 char szText[256];
2222
2223 if (LoadStringA(psInfo->ppshheader.hInstance,
2224 (UINT)psInfo->ppshheader.pszCaption, szText, 255))
2225 PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader.dwFlags, szText);
2226 }
2227 else
2228 {
2229 PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader.dwFlags,
2230 psInfo->ppshheader.pszCaption);
2231 }
2232
2233 return TRUE;
2234 }
2235
2236 case WM_DESTROY:
2237 PROPSHEET_CleanUp(hwnd);
2238 return TRUE;
2239
2240 case WM_CLOSE:
2241 PROPSHEET_Cancel(hwnd, 1);
2242 return TRUE;
2243
2244 case WM_COMMAND:
2245 {
2246 WORD wID = LOWORD(wParam);
2247
2248 switch (wID)
2249 {
2250 case IDOK:
2251 case IDC_APPLY_BUTTON:
2252 {
2253 HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
2254
2255 if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
2256 break;
2257
2258 if (wID == IDOK)
2259 {
2260 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2261 PropSheetInfoStr);
2262 int result = TRUE;
2263
2264 if (psInfo->restartWindows)
2265 result = ID_PSRESTARTWINDOWS;
2266
2267 /* reboot system takes precedence over restart windows */
2268 if (psInfo->rebootSystem)
2269 result = ID_PSREBOOTSYSTEM;
2270
2271 if (psInfo->isModeless)
2272 psInfo->activeValid = FALSE;
2273 else
2274 EndDialog(hwnd, result);
2275 }
2276 else
2277 EnableWindow(hwndApplyBtn, FALSE);
2278
2279 break;
2280 }
2281
2282 case IDC_BACK_BUTTON:
2283 PROPSHEET_Back(hwnd);
2284 break;
2285
2286 case IDC_NEXT_BUTTON:
2287 PROPSHEET_Next(hwnd);
2288 break;
2289
2290 case IDC_FINISH_BUTTON:
2291 PROPSHEET_Finish(hwnd);
2292 break;
2293
2294 case IDCANCEL:
2295 PROPSHEET_Cancel(hwnd, 0);
2296 break;
2297
2298 case IDHELP:
2299 PROPSHEET_Help(hwnd);
2300 break;
2301
2302#ifdef __WIN32OS2__
2303 default:
2304 {
2305 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2306 PropSheetInfoStr);
2307 if(psInfo->active_page != -1)
2308 {
2309 return SendMessageA(psInfo->proppage[psInfo->active_page].hwndPage,
2310 uMsg, wParam, lParam);
2311 }
2312 }
2313#endif
2314 }
2315
2316 return TRUE;
2317 }
2318
2319 case WM_NOTIFY:
2320 {
2321 NMHDR* pnmh = (LPNMHDR) lParam;
2322
2323 if (pnmh->code == TCN_SELCHANGE)
2324 {
2325 int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
2326 PROPSHEET_SetCurSel(hwnd, index, 1, 0);
2327 }
2328
2329 if(pnmh->code == TCN_SELCHANGING)
2330 {
2331 BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
2332 SetWindowLongA(hwnd, DWL_MSGRESULT, !bRet);
2333 return TRUE;
2334 }
2335
2336 return FALSE;
2337 }
2338
2339 case PSM_GETCURRENTPAGEHWND:
2340 {
2341 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2342 PropSheetInfoStr);
2343 HWND hwndPage = 0;
2344
2345 if (psInfo->activeValid && psInfo->active_page != -1)
2346 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
2347
2348 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage);
2349
2350 return TRUE;
2351 }
2352
2353 case PSM_CHANGED:
2354 PROPSHEET_Changed(hwnd, (HWND)wParam);
2355 return TRUE;
2356
2357 case PSM_UNCHANGED:
2358 PROPSHEET_UnChanged(hwnd, (HWND)wParam);
2359 return TRUE;
2360
2361 case PSM_GETTABCONTROL:
2362 {
2363 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2364
2365 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl);
2366
2367 return TRUE;
2368 }
2369
2370 case PSM_SETCURSEL:
2371 {
2372 BOOL msgResult;
2373
2374 msgResult = PROPSHEET_CanSetCurSel(hwnd);
2375 if(msgResult != FALSE)
2376 {
2377 msgResult = PROPSHEET_SetCurSel(hwnd,
2378 (int)wParam,
2379 1,
2380 (HPROPSHEETPAGE)lParam);
2381 }
2382
2383 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2384
2385 return TRUE;
2386 }
2387
2388 case PSM_CANCELTOCLOSE:
2389 {
2390 char buf[MAX_BUTTONTEXT_LENGTH];
2391 HWND hwndOK = GetDlgItem(hwnd, IDOK);
2392 HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
2393
2394 EnableWindow(hwndCancel, FALSE);
2395 if (LoadStringA(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
2396 SetWindowTextA(hwndOK, buf);
2397
2398 return FALSE;
2399 }
2400
2401 case PSM_RESTARTWINDOWS:
2402 {
2403 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2404 PropSheetInfoStr);
2405
2406 psInfo->restartWindows = TRUE;
2407 return TRUE;
2408 }
2409
2410 case PSM_REBOOTSYSTEM:
2411 {
2412 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2413 PropSheetInfoStr);
2414
2415 psInfo->rebootSystem = TRUE;
2416 return TRUE;
2417 }
2418
2419 case PSM_SETTITLEA:
2420 PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
2421 return TRUE;
2422
2423 case PSM_APPLY:
2424 {
2425 BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
2426
2427 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2428
2429 return TRUE;
2430 }
2431
2432 case PSM_QUERYSIBLINGS:
2433 {
2434 LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
2435
2436 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2437
2438 return TRUE;
2439 }
2440
2441 case PSM_ADDPAGE:
2442 {
2443 /*
2444 * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
2445 * a return value. This is not true. PSM_ADDPAGE returns TRUE
2446 * on success or FALSE otherwise, as specified on MSDN Online.
2447 * Also see the MFC code for
2448 * CPropertySheet::AddPage(CPropertyPage* pPage).
2449 */
2450
2451 BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
2452
2453 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2454
2455 return TRUE;
2456 }
2457
2458 case PSM_REMOVEPAGE:
2459 PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
2460 return TRUE;
2461
2462 case PSM_ISDIALOGMESSAGE:
2463 {
2464 BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
2465 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2466 return TRUE;
2467 }
2468
2469 case PSM_PRESSBUTTON:
2470 PROPSHEET_PressButton(hwnd, (int)wParam);
2471 return TRUE;
2472
2473 case PSM_SETFINISHTEXTA:
2474 PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
2475 return TRUE;
2476
2477 case PSM_SETWIZBUTTONS:
2478 PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
2479 return TRUE;
2480
2481 case PSM_SETTITLEW:
2482 FIXME("Unimplemented msg PSM_SETTITLE32W\n");
2483 return FALSE;
2484
2485 case PSM_SETCURSELID:
2486 FIXME("Unimplemented msg PSM_SETCURSELID\n");
2487 return FALSE;
2488
2489 case PSM_SETFINISHTEXTW:
2490 FIXME("Unimplemented msg PSM_SETFINISHTEXT32W\n");
2491 return FALSE;
2492
2493 default:
2494 return FALSE;
2495 }
2496
2497 return FALSE;
2498}
2499
Note: See TracBrowser for help on using the repository browser.