Converted to the new debug interface, using script written by Patrik
[wine/testsucceed.git] / dlls / comctl32 / propsheet.c
blobceb20d9db3946b27ebc56665eacd0cf56f848c2d
1 /*
2 * Property Sheets
4 * Copyright 1998 Francis Beaudet
5 * Copyright 1999 Thuy Nguyen
7 * TODO:
8 * - Modeless mode
9 * - Wizard mode
10 * - Unicode property sheets
13 #include <string.h>
14 #include "winbase.h"
15 #include "commctrl.h"
16 #include "prsht.h"
17 #include "winnls.h"
18 #include "comctl32.h"
19 #include "debugtools.h"
22 /******************************************************************************
23 * Data structures
25 typedef struct
27 WORD dlgVer;
28 WORD signature;
29 DWORD helpID;
30 DWORD exStyle;
31 DWORD style;
32 } MyDLGTEMPLATEEX;
34 typedef struct tagPropPageInfo
36 int index; /* corresponds to the index in ppshheader->ppsp */
37 HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
38 HWND hwndPage;
39 BOOL isDirty;
40 LPCWSTR pszText;
41 BOOL hasHelp;
42 BOOL useCallback;
43 } PropPageInfo;
45 typedef struct tagPropSheetInfo
47 LPSTR strPropertiesFor;
48 int nPages;
49 int active_page;
50 LPCPROPSHEETHEADERA ppshheader;
51 BOOL isModeless;
52 BOOL hasHelp;
53 BOOL hasApply;
54 BOOL useCallback;
55 BOOL restartWindows;
56 BOOL rebootSystem;
57 PropPageInfo* proppage;
58 int x;
59 int y;
60 int width;
61 int height;
62 } PropSheetInfo;
64 typedef struct
66 int x;
67 int y;
68 } PADDING_INFO;
70 /******************************************************************************
71 * Defines and global variables
74 const char * PropSheetInfoStr = "PropertySheetInfo";
76 #define MAX_CAPTION_LENGTH 255
77 #define MAX_TABTEXT_LENGTH 255
80 /******************************************************************************
81 * Prototypes
83 static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
84 static BOOL PROPSHEET_IsTooSmall(HWND hwndDlg, PropSheetInfo* psInfo);
85 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
86 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
87 static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
88 PropSheetInfo * psInfo);
89 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
90 PropSheetInfo * psInfo,
91 int index);
92 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
93 PropSheetInfo * psInfo);
94 static int PROPSHEET_CreatePage(HWND hwndParent, int index,
95 const PropSheetInfo * psInfo,
96 LPCPROPSHEETPAGEA ppshpage,
97 BOOL showPage);
98 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
99 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
100 static BOOL PROPSHEET_Apply(HWND hwndDlg);
101 static void PROPSHEET_Cancel(HWND hwndDlg);
102 static void PROPSHEET_Help(HWND hwndDlg);
103 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
104 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
105 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
106 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
107 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
108 int index,
109 HPROPSHEETPAGE hpage);
110 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
111 WPARAM wParam, LPARAM lParam);
112 static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo,
113 int index);
114 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
115 HPROPSHEETPAGE hpage);
117 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
118 int index,
119 HPROPSHEETPAGE hpage);
120 static void PROPSHEET_CleanUp();
121 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
123 BOOL WINAPI
124 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
126 DEFAULT_DEBUG_CHANNEL(propsheet)
128 /******************************************************************************
129 * PROPSHEET_CollectSheetInfo
131 * Collect relevant data.
133 static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
134 PropSheetInfo * psInfo)
136 DWORD dwFlags = lppsh->dwFlags;
138 psInfo->hasHelp = dwFlags & PSH_HASHELP;
139 psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
140 psInfo->useCallback = dwFlags & PSH_USECALLBACK;
141 psInfo->isModeless = dwFlags & PSH_MODELESS;
142 psInfo->ppshheader = lppsh;
143 psInfo->nPages = lppsh->nPages;
145 if (dwFlags & PSH_USEPSTARTPAGE)
147 TRACE("PSH_USEPSTARTPAGE is on");
148 psInfo->active_page = 0;
150 else
151 psInfo->active_page = lppsh->u2.nStartPage;
153 psInfo->restartWindows = FALSE;
154 psInfo->rebootSystem = FALSE;
156 return TRUE;
159 /******************************************************************************
160 * PROPSHEET_CollectPageInfo
162 * Collect property sheet data.
163 * With code taken from DIALOG_ParseTemplate32.
165 BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
166 PropSheetInfo * psInfo,
167 int index)
169 DLGTEMPLATE* pTemplate;
170 const WORD* p;
171 DWORD dwFlags;
172 int width, height;
174 if (psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE)
175 psInfo->proppage[index].hpage = 0;
176 psInfo->proppage[index].hwndPage = 0;
177 psInfo->proppage[index].isDirty = FALSE;
180 * Process property page flags.
182 dwFlags = lppsp->dwFlags;
183 psInfo->proppage[index].useCallback = dwFlags & PSP_USECALLBACK;
184 psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
186 /* as soon as we have a page with the help flag, set the sheet flag on */
187 if (psInfo->proppage[index].hasHelp)
188 psInfo->hasHelp = TRUE;
191 * Process page template.
193 if (dwFlags & PSP_DLGINDIRECT)
194 pTemplate = (DLGTEMPLATE*)lppsp->u1.pResource;
195 else
197 HRSRC hResource = FindResourceA(lppsp->hInstance,
198 lppsp->u1.pszTemplate,
199 RT_DIALOGA);
200 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
201 hResource);
202 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
206 * Extract the size of the page and the caption.
208 p = (const WORD *)pTemplate;
210 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
212 /* DIALOGEX template */
214 p++; /* dlgVer */
215 p++; /* signature */
216 p += 2; /* help ID */
217 p += 2; /* ext style */
218 p += 2; /* style */
220 else
222 /* DIALOG template */
224 p += 2; /* style */
225 p += 2; /* ext style */
228 p++; /* nb items */
229 p++; /* x */
230 p++; /* y */
231 width = (WORD)*p; p++;
232 height = (WORD)*p; p++;
234 /* remember the largest width and height */
235 if (width > psInfo->width)
236 psInfo->width = width;
238 if (height > psInfo->height)
239 psInfo->height = height;
241 /* menu */
242 switch ((WORD)*p)
244 case 0x0000:
245 p++;
246 break;
247 case 0xffff:
248 p += 2;
249 break;
250 default:
251 p += lstrlenW( (LPCWSTR)p ) + 1;
252 break;
255 /* class */
256 switch ((WORD)*p)
258 case 0x0000:
259 p++;
260 break;
261 case 0xffff:
262 p += 2;
263 break;
264 default:
265 p += lstrlenW( (LPCWSTR)p ) + 1;
266 break;
269 /* Extract the caption */
270 psInfo->proppage[index].pszText = (LPCWSTR)p;
271 TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
272 p += lstrlenW((LPCWSTR)p) + 1;
274 return TRUE;
277 /******************************************************************************
278 * PROPSHEET_CreateDialog
280 * Creates the actual property sheet.
282 BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
284 LRESULT ret;
285 LPCVOID template;
286 HRSRC hRes;
288 if(!(hRes = FindResourceA(COMCTL32_hModule,
289 MAKEINTRESOURCEA(IDD_PROPSHEET),
290 RT_DIALOGA)))
291 return FALSE;
293 if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
294 return FALSE;
296 if (psInfo->useCallback)
297 (*(psInfo->ppshheader->pfnCallback))(0, PSCB_PRECREATE, (LPARAM)template);
299 if (psInfo->ppshheader->dwFlags & PSH_MODELESS)
300 ret = CreateDialogIndirectParamA(psInfo->ppshheader->hInstance,
301 (LPDLGTEMPLATEA) template,
302 psInfo->ppshheader->hwndParent,
303 (DLGPROC) PROPSHEET_DialogProc,
304 (LPARAM)psInfo);
305 else
306 ret = DialogBoxIndirectParamA(psInfo->ppshheader->hInstance,
307 (LPDLGTEMPLATEA) template,
308 psInfo->ppshheader->hwndParent,
309 (DLGPROC) PROPSHEET_DialogProc,
310 (LPARAM)psInfo);
312 return ret;
315 /******************************************************************************
316 * PROPSHEET_IsTooSmall
318 * Verify that the resource property sheet is big enough.
320 static BOOL PROPSHEET_IsTooSmall(HWND hwndDlg, PropSheetInfo* psInfo)
322 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
323 RECT rcOrigTab, rcPage;
326 * Original tab size.
328 GetClientRect(hwndTabCtrl, &rcOrigTab);
329 TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
330 rcOrigTab.right, rcOrigTab.bottom);
333 * Biggest page size.
335 rcPage.left = psInfo->x;
336 rcPage.top = psInfo->y;
337 rcPage.right = psInfo->width;
338 rcPage.bottom = psInfo->height;
340 MapDialogRect(hwndDlg, &rcPage);
341 TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
342 rcPage.right, rcPage.bottom);
344 if (rcPage.right > rcOrigTab.right)
345 return TRUE;
347 if (rcPage.bottom > rcOrigTab.bottom)
348 return TRUE;
350 return FALSE;
353 /******************************************************************************
354 * PROPSHEET_AdjustSize
356 * Resizes the property sheet and the tab control to fit the largest page.
358 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
360 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
361 HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
362 RECT rc;
363 int tabOffsetX, tabOffsetY, buttonHeight;
364 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
366 /* Get the height of buttons */
367 GetClientRect(hwndButton, &rc);
368 buttonHeight = rc.bottom;
371 * Biggest page size.
373 rc.left = psInfo->x;
374 rc.top = psInfo->y;
375 rc.right = psInfo->width;
376 rc.bottom = psInfo->height;
378 MapDialogRect(hwndDlg, &rc);
381 * Resize the tab control.
383 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
385 tabOffsetX = -(rc.left);
386 tabOffsetY = -(rc.top);
388 rc.right -= rc.left;
389 rc.bottom -= rc.top;
390 SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
391 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
393 GetClientRect(hwndTabCtrl, &rc);
395 TRACE("tab client rc %d %d %d %d\n",
396 rc.left, rc.top, rc.right, rc.bottom);
398 rc.right += ((padding.x * 2) + tabOffsetX);
399 rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
402 * Resize the property sheet.
404 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
405 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
407 return TRUE;
410 /******************************************************************************
411 * PROPSHEET_AdjustButtons
413 * Adjusts the buttons' positions.
415 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
417 HWND hwndButton = GetDlgItem(hwndParent, IDOK);
418 RECT rcSheet;
419 int x, y;
420 int num_buttons = 2;
421 int buttonWidth, buttonHeight;
422 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
424 if (psInfo->hasApply)
425 num_buttons++;
427 if (psInfo->hasHelp)
428 num_buttons++;
431 * Obtain the size of the buttons.
433 GetClientRect(hwndButton, &rcSheet);
434 buttonWidth = rcSheet.right;
435 buttonHeight = rcSheet.bottom;
438 * Get the size of the property sheet.
440 GetClientRect(hwndParent, &rcSheet);
443 * All buttons will be at this y coordinate.
445 y = rcSheet.bottom - (padding.y + buttonHeight);
448 * Position OK button.
450 hwndButton = GetDlgItem(hwndParent, IDOK);
452 x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
454 SetWindowPos(hwndButton, 0, x, y, 0, 0,
455 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
458 * Position Cancel button.
460 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
462 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
464 SetWindowPos(hwndButton, 0, x, y, 0, 0,
465 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
468 * Position Apply button.
470 hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
472 if (psInfo->hasApply)
474 if (psInfo->hasHelp)
475 x = rcSheet.right - ((padding.x + buttonWidth) * 2);
476 else
477 x = rcSheet.right - (padding.x + buttonWidth);
479 SetWindowPos(hwndButton, 0, x, y, 0, 0,
480 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
482 EnableWindow(hwndButton, FALSE);
484 else
485 ShowWindow(hwndButton, SW_HIDE);
488 * Position Help button.
490 hwndButton = GetDlgItem(hwndParent, IDHELP);
492 if (psInfo->hasHelp)
494 x = rcSheet.right - (padding.x + buttonWidth);
496 SetWindowPos(hwndButton, 0, x, y, 0, 0,
497 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
499 else
500 ShowWindow(hwndButton, SW_HIDE);
502 return TRUE;
505 /******************************************************************************
506 * PROPSHEET_GetPaddingInfo
508 * Returns the layout information.
510 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
512 HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
513 RECT rcTab;
514 POINT tl;
515 PADDING_INFO padding;
517 GetWindowRect(hwndTab, &rcTab);
519 tl.x = rcTab.left;
520 tl.y = rcTab.top;
522 ScreenToClient(hwndDlg, &tl);
524 padding.x = tl.x;
525 padding.y = tl.y;
527 return padding;
530 /******************************************************************************
531 * PROPSHEET_CreateTabControl
533 * Insert the tabs in the tab control.
535 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
536 PropSheetInfo * psInfo)
538 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
539 TCITEMA item;
540 int i, nTabs;
541 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
543 item.mask = TCIF_TEXT;
544 item.pszText = tabtext;
545 item.cchTextMax = MAX_TABTEXT_LENGTH;
547 nTabs = psInfo->ppshheader->nPages;
549 for (i = 0; i < nTabs; i++)
551 WideCharToMultiByte(CP_ACP, 0,
552 (LPCWSTR)psInfo->proppage[i].pszText,
553 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
555 SendMessageA(hwndTabCtrl, TCM_INSERTITEMA, (WPARAM)i, (LPARAM)&item);
558 return TRUE;
561 /******************************************************************************
562 * PROPSHEET_CreatePage
564 * Creates a page.
566 static int PROPSHEET_CreatePage(HWND hwndParent,
567 int index,
568 const PropSheetInfo * psInfo,
569 LPCPROPSHEETPAGEA ppshpage,
570 BOOL showPage)
572 DLGTEMPLATE* pTemplate;
573 HWND hwndPage;
574 RECT rc;
575 PropPageInfo* ppInfo = psInfo->proppage;
576 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
577 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
579 TRACE("index %d\n", index);
581 if (ppshpage->dwFlags & PSP_DLGINDIRECT)
582 pTemplate = (DLGTEMPLATE*)ppshpage->u1.pResource;
583 else
585 HRSRC hResource = FindResourceA(ppshpage->hInstance,
586 ppshpage->u1.pszTemplate,
587 RT_DIALOGA);
588 HGLOBAL hTemplate = LoadResource(ppshpage->hInstance, hResource);
589 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
592 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
594 ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD;
595 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
596 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
597 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
598 ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
600 else
602 pTemplate->style |= WS_CHILD;
603 pTemplate->style &= ~DS_MODALFRAME;
604 pTemplate->style &= ~WS_CAPTION;
605 pTemplate->style &= ~WS_SYSMENU;
606 pTemplate->style &= ~WS_POPUP;
609 if (psInfo->proppage[index].useCallback)
610 (*(ppshpage->pfnCallback))(hwndParent,
611 PSPCB_CREATE,
612 (LPPROPSHEETPAGEA)ppshpage);
614 hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
615 pTemplate,
616 hwndParent,
617 ppshpage->pfnDlgProc,
618 (LPARAM)ppshpage);
620 ppInfo[index].hwndPage = hwndPage;
622 rc.left = psInfo->x;
623 rc.top = psInfo->y;
624 rc.right = psInfo->width;
625 rc.bottom = psInfo->height;
627 MapDialogRect(hwndParent, &rc);
630 * Ask the Tab control to fit this page in.
632 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc);
634 SetWindowPos(hwndPage, HWND_TOP,
635 rc.left + padding.x,
636 rc.top + padding.y,
637 0, 0, SWP_NOSIZE);
639 if (showPage)
640 ShowWindow(hwndPage, SW_SHOW);
641 else
642 ShowWindow(hwndPage, SW_HIDE);
644 return TRUE;
647 /******************************************************************************
648 * PROPSHEET_ShowPage
650 * Displays or creates the specified page.
652 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
654 if (index == psInfo->active_page)
655 return TRUE;
657 ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
659 if (psInfo->proppage[index].hwndPage != 0)
660 ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
661 else
663 LPCPROPSHEETPAGEA ppshpage = PROPSHEET_GetPSPPage(psInfo, index);
664 PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage, TRUE);
667 psInfo->active_page = index;
669 return TRUE;
672 /******************************************************************************
673 * PROPSHEET_Apply
675 static BOOL PROPSHEET_Apply(HWND hwndDlg)
677 int i;
678 NMHDR hdr;
679 HWND hwndPage;
680 LRESULT msgResult;
681 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
682 PropSheetInfoStr);
684 hdr.hwndFrom = hwndDlg;
687 * Send PSN_KILLACTIVE to the current page.
689 hdr.code = PSN_KILLACTIVE;
691 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
693 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr) != FALSE)
694 return FALSE;
697 * Send PSN_APPLY to all pages.
699 hdr.code = PSN_APPLY;
701 for (i = 0; i < psInfo->nPages; i++)
703 hwndPage = psInfo->proppage[i].hwndPage;
704 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
706 if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
707 return FALSE;
710 return TRUE;
713 /******************************************************************************
714 * PROPSHEET_Cancel
716 static void PROPSHEET_Cancel(HWND hwndDlg)
718 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
719 PropSheetInfoStr);
720 HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
721 NMHDR hdr;
723 hdr.hwndFrom = hwndDlg;
724 hdr.code = PSN_QUERYCANCEL;
726 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
727 return;
729 hdr.code = PSN_RESET;
731 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
733 if (psInfo->isModeless)
734 psInfo->active_page = -1; /* makes PSM_GETCURRENTPAGEHWND return NULL */
735 else
736 EndDialog(hwndDlg, FALSE);
739 /******************************************************************************
740 * PROPSHEET_Help
742 static void PROPSHEET_Help(HWND hwndDlg)
744 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
745 PropSheetInfoStr);
746 HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
747 NMHDR hdr;
749 hdr.hwndFrom = hwndDlg;
750 hdr.code = PSN_HELP;
752 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
755 /******************************************************************************
756 * PROPSHEET_Changed
758 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
760 int i;
761 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
762 PropSheetInfoStr);
765 * Set the dirty flag of this page.
767 for (i = 0; i < psInfo->nPages; i++)
769 if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
770 psInfo->proppage[i].isDirty = TRUE;
774 * Enable the Apply button.
776 if (psInfo->hasApply)
778 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
780 EnableWindow(hwndApplyBtn, TRUE);
784 /******************************************************************************
785 * PROPSHEET_UnChanged
787 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
789 int i;
790 BOOL noPageDirty = TRUE;
791 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
792 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
793 PropSheetInfoStr);
795 for (i = 0; i < psInfo->nPages; i++)
797 /* set the specified page as clean */
798 if (psInfo->proppage[i].hwndPage == hwndCleanPage)
799 psInfo->proppage[i].isDirty = FALSE;
801 /* look to see if there's any dirty pages */
802 if (psInfo->proppage[i].isDirty)
803 noPageDirty = FALSE;
807 * Disable Apply button.
809 if (noPageDirty)
810 EnableWindow(hwndApplyBtn, FALSE);
813 /******************************************************************************
814 * PROPSHEET_PressButton
816 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
818 switch (buttonID)
820 case PSBTN_APPLYNOW:
821 SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0);
822 break;
823 case PSBTN_BACK:
824 FIXME("Wizard mode not implemented.\n");
825 break;
826 case PSBTN_CANCEL:
827 SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0);
828 break;
829 case PSBTN_FINISH:
830 FIXME("Wizard mode not implemented.\n");
831 break;
832 case PSBTN_HELP:
833 SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0);
834 break;
835 case PSBTN_NEXT:
836 FIXME("Wizard mode not implemented.\n");
837 break;
838 case PSBTN_OK:
839 SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0);
840 break;
841 default:
842 FIXME("Invalid button index %d\n", buttonID);
846 /******************************************************************************
847 * PROPSHEET_SetCurSel
849 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
850 int index,
851 HPROPSHEETPAGE hpage)
853 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
854 PropSheetInfoStr);
855 HWND hwndPage;
856 HWND hwndHelp = GetDlgItem(hwndDlg, IDHELP);
857 NMHDR hdr;
860 * Notify the current page.
862 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
864 hdr.hwndFrom = hwndDlg;
865 hdr.code = PSN_KILLACTIVE;
867 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
868 return FALSE;
870 if (hpage != NULL)
871 FIXME("Implement HPROPSHEETPAGE!\n");
872 else
873 hwndPage = psInfo->proppage[index].hwndPage;
876 * Notify the new page.
878 hdr.code = PSN_SETACTIVE;
880 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
883 * Display the new page.
885 PROPSHEET_ShowPage(hwndDlg, index, psInfo);
887 if (psInfo->proppage[index].hasHelp)
888 EnableWindow(hwndHelp, TRUE);
889 else
890 EnableWindow(hwndHelp, FALSE);
892 return TRUE;
895 /******************************************************************************
896 * PROPSHEET_SetTitleA
898 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
900 if (dwStyle & PSH_PROPTITLE)
902 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
903 PropSheetInfoStr);
904 char* dest;
905 int lentitle = strlen(lpszText);
906 int lenprop = strlen(psInfo->strPropertiesFor);
908 dest = COMCTL32_Alloc(lentitle + lenprop + 1);
909 strcpy(dest, psInfo->strPropertiesFor);
910 strcat(dest, lpszText);
912 SetWindowTextA(hwndDlg, dest);
913 COMCTL32_Free(dest);
915 else
916 SetWindowTextA(hwndDlg, lpszText);
919 /******************************************************************************
920 * PROPSHEET_QuerySiblings
922 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
923 WPARAM wParam, LPARAM lParam)
925 int i = 0;
926 HWND hwndPage;
927 LRESULT msgResult = 0;
928 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
929 PropSheetInfoStr);
931 while ((i < psInfo->nPages) && (msgResult == 0))
933 hwndPage = psInfo->proppage[i].hwndPage;
934 msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
935 i++;
938 return msgResult;
941 /******************************************************************************
942 * PROPSHEET_GetPSPPage
944 static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo,
945 int index)
947 BOOL usePSP = psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE;
948 LPCPROPSHEETPAGEA lppsp;
949 int realIndex = psInfo->proppage[index].index;
951 if (usePSP)
953 BYTE* pByte;
955 lppsp = psInfo->ppshheader->u3.ppsp;
957 pByte = (BYTE*) lppsp;
959 pByte += (lppsp->dwSize * realIndex);
960 lppsp = (LPCPROPSHEETPAGEA)pByte;
962 else
963 lppsp = (LPCPROPSHEETPAGEA) psInfo->ppshheader->u3.phpage[realIndex];
965 return lppsp;
968 /******************************************************************************
969 * PROPSHEET_AddPage
971 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
972 HPROPSHEETPAGE hpage)
974 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
975 PropSheetInfoStr);
976 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
977 TCITEMA item;
978 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
979 LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage;
982 * Allocate and fill in a new PropPageInfo entry.
984 psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
985 sizeof(PropPageInfo) *
986 (psInfo->nPages + 1));
988 PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages);
989 psInfo->proppage[psInfo->nPages].index = -1;
990 psInfo->proppage[psInfo->nPages].hpage = hpage;
993 * Create the page but don't show it.
995 PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp, FALSE);
998 * Add a new tab to the tab control.
1000 item.mask = TCIF_TEXT;
1001 item.pszText = tabtext;
1002 item.cchTextMax = MAX_TABTEXT_LENGTH;
1004 WideCharToMultiByte(CP_ACP, 0,
1005 (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText,
1006 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
1008 SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1,
1009 (LPARAM)&item);
1011 psInfo->nPages++;
1013 return FALSE;
1016 /******************************************************************************
1017 * PROPSHEET_RemovePage
1019 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1020 int index,
1021 HPROPSHEETPAGE hpage)
1023 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1024 PropSheetInfoStr);
1025 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1026 PropPageInfo* oldPages = psInfo->proppage;
1029 * hpage takes precedence over index.
1031 if (hpage != 0)
1033 index = PROPSHEET_GetPageIndex(hpage, psInfo);
1035 if (index == -1)
1037 TRACE("Could not find page to remove!\n");
1038 return FALSE;
1042 TRACE("total pages %d removing page %d active page %d\n",
1043 psInfo->nPages, index, psInfo->active_page);
1045 * Check if we're removing the active page.
1047 if (index == psInfo->active_page)
1049 if (psInfo->nPages > 1)
1051 if (index > 0)
1053 /* activate previous page */
1054 PROPSHEET_ShowPage(hwndDlg, index - 1, psInfo);
1056 else
1058 /* activate the next page */
1059 PROPSHEET_ShowPage(hwndDlg, index + 1, psInfo);
1062 else
1064 TRACE("Removing the only page, close the dialog!\n");
1066 if (psInfo->isModeless)
1067 psInfo->active_page = -1;
1068 else
1069 EndDialog(hwndDlg, FALSE);
1071 return TRUE;
1075 if (index < psInfo->active_page)
1076 psInfo->active_page--;
1078 /* Remove the tab */
1079 SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0);
1081 psInfo->nPages--;
1082 psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
1084 if (index > 0)
1085 memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
1087 if (index < psInfo->nPages)
1088 memcpy(&psInfo->proppage[index], &oldPages[index + 1],
1089 (psInfo->nPages - index) * sizeof(PropPageInfo));
1091 COMCTL32_Free(oldPages);
1093 return FALSE;
1096 /******************************************************************************
1097 * PROPSHEET_GetPageIndex
1099 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
1100 * the array of PropPageInfo.
1102 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
1104 BOOL found = FALSE;
1105 int index = 0;
1107 while ((index < psInfo->nPages) && (found == FALSE))
1109 if (psInfo->proppage[index].hpage == hpage)
1110 found = TRUE;
1111 else
1112 index++;
1115 if (found == FALSE)
1116 index = -1;
1118 return index;
1121 /******************************************************************************
1122 * PROPSHEET_CleanUp
1124 static void PROPSHEET_CleanUp(HWND hwndDlg)
1126 PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg,
1127 PropSheetInfoStr);
1128 COMCTL32_Free(psInfo->proppage);
1129 COMCTL32_Free(psInfo->strPropertiesFor);
1131 GlobalFree((HGLOBAL)psInfo);
1134 /******************************************************************************
1135 * PropertySheetA (COMCTL32.84)(COMCTL32.83)
1137 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
1139 int bRet = 0;
1140 PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
1141 sizeof(PropSheetInfo));
1142 LPCPROPSHEETPAGEA lppsp;
1143 int i;
1145 PROPSHEET_CollectSheetInfo(lppsh, psInfo);
1147 psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
1148 lppsh->nPages);
1150 for (i = 0; i < lppsh->nPages; i++)
1152 psInfo->proppage[i].index = i;
1153 if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
1154 psInfo->proppage[i].hpage = psInfo->ppshheader->u3.phpage[i];
1155 lppsp = PROPSHEET_GetPSPPage(psInfo, i);
1156 PROPSHEET_CollectPageInfo(lppsp, psInfo, i);
1159 bRet = PROPSHEET_CreateDialog(psInfo);
1161 return bRet;
1164 /******************************************************************************
1165 * PropertySheet32W (COMCTL32.85)
1167 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader)
1169 FIXME("(%p): stub\n", propertySheetHeader);
1171 return -1;
1174 /******************************************************************************
1175 * CreatePropertySheetPageA (COMCTL32.19)(COMCTL32.18)
1177 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
1178 LPCPROPSHEETPAGEA lpPropSheetPage)
1180 PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA));
1182 *ppsp = *lpPropSheetPage;
1184 return (HPROPSHEETPAGE)ppsp;
1187 /******************************************************************************
1188 * CreatePropertySheetPageW (COMCTL32.20)
1190 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
1192 FIXME("(%p): stub\n", lpPropSheetPage);
1194 return 0;
1197 /******************************************************************************
1198 * DestroyPropertySheetPage (COMCTL32.24)
1200 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
1202 COMCTL32_Free(hPropPage);
1204 return TRUE;
1207 /******************************************************************************
1208 * PROPSHEET_DialogProc
1210 BOOL WINAPI
1211 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1213 switch (uMsg)
1215 case WM_INITDIALOG:
1217 PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
1218 char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH);
1219 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1220 LPCPROPSHEETPAGEA ppshpage;
1222 psInfo->strPropertiesFor = strCaption;
1224 GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
1226 PROPSHEET_CreateTabControl(hwnd, psInfo);
1228 if (PROPSHEET_IsTooSmall(hwnd, psInfo))
1230 PROPSHEET_AdjustSize(hwnd, psInfo);
1231 PROPSHEET_AdjustButtons(hwnd, psInfo);
1234 ppshpage = PROPSHEET_GetPSPPage(psInfo, psInfo->active_page);
1235 PROPSHEET_CreatePage(hwnd, psInfo->active_page, psInfo, ppshpage, TRUE);
1236 SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
1238 SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
1240 PROPSHEET_SetTitleA(hwnd,
1241 psInfo->ppshheader->dwFlags,
1242 psInfo->ppshheader->pszCaption);
1244 return TRUE;
1247 case WM_DESTROY:
1248 PROPSHEET_CleanUp(hwnd);
1249 return TRUE;
1251 case WM_CLOSE:
1252 PROPSHEET_Cancel(hwnd);
1253 return TRUE;
1255 case WM_COMMAND:
1257 WORD wID = LOWORD(wParam);
1259 switch (wID)
1261 case IDOK:
1262 case IDC_APPLY_BUTTON:
1264 HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
1266 if (PROPSHEET_Apply(hwnd) == FALSE)
1267 break;
1269 EnableWindow(hwndApplyBtn, FALSE);
1271 if (wID == IDOK)
1273 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1274 PropSheetInfoStr);
1275 int result = TRUE;
1277 if (psInfo->restartWindows)
1278 result = ID_PSRESTARTWINDOWS;
1280 /* reboot system takes precedence over restart windows */
1281 if (psInfo->rebootSystem)
1282 result = ID_PSREBOOTSYSTEM;
1284 if (psInfo->isModeless)
1285 psInfo->active_page = -1;
1286 else
1287 EndDialog(hwnd, result);
1290 break;
1293 case IDCANCEL:
1294 PROPSHEET_Cancel(hwnd);
1295 break;
1297 case IDHELP:
1298 PROPSHEET_Help(hwnd);
1299 break;
1302 return TRUE;
1305 case WM_NOTIFY:
1307 NMHDR* pnmh = (LPNMHDR) lParam;
1309 if (pnmh->code == TCN_SELCHANGE)
1311 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1312 PropSheetInfoStr);
1313 int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
1314 HWND hwndHelp = GetDlgItem(hwnd, IDHELP);
1316 PROPSHEET_ShowPage(hwnd, index, psInfo);
1318 if (psInfo->proppage[index].hasHelp)
1319 EnableWindow(hwndHelp, TRUE);
1320 else
1321 EnableWindow(hwndHelp, FALSE);
1324 return 0;
1327 case PSM_GETCURRENTPAGEHWND:
1329 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1330 PropSheetInfoStr);
1331 HWND hwndPage = 0;
1333 if (psInfo->active_page != -1)
1334 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1336 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage);
1338 return TRUE;
1341 case PSM_CHANGED:
1342 PROPSHEET_Changed(hwnd, (HWND)wParam);
1343 return TRUE;
1345 case PSM_UNCHANGED:
1346 PROPSHEET_UnChanged(hwnd, (HWND)wParam);
1347 return TRUE;
1349 case PSM_GETTABCONTROL:
1351 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1353 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl);
1355 return TRUE;
1358 case PSM_SETCURSEL:
1360 BOOL msgResult;
1362 msgResult = PROPSHEET_SetCurSel(hwnd,
1363 (int)wParam,
1364 (HPROPSHEETPAGE)lParam);
1366 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1368 return TRUE;
1371 case PSM_CANCELTOCLOSE:
1373 HWND hwndOK = GetDlgItem(hwnd, IDOK);
1374 HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
1376 EnableWindow(hwndCancel, FALSE);
1377 SetWindowTextA(hwndOK, "Close"); /* FIXME: hardcoded string */
1379 return TRUE;
1382 case PSM_RESTARTWINDOWS:
1384 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1385 PropSheetInfoStr);
1387 psInfo->restartWindows = TRUE;
1388 return TRUE;
1391 case PSM_REBOOTSYSTEM:
1393 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1394 PropSheetInfoStr);
1396 psInfo->rebootSystem = TRUE;
1397 return TRUE;
1400 case PSM_SETTITLEA:
1401 PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
1402 return TRUE;
1404 case PSM_APPLY:
1406 BOOL msgResult = PROPSHEET_Apply(hwnd);
1408 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1410 return TRUE;
1413 case PSM_QUERYSIBLINGS:
1415 LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
1417 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1419 return TRUE;
1422 case PSM_ADDPAGE:
1423 PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
1424 return TRUE;
1426 case PSM_REMOVEPAGE:
1427 PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
1428 return TRUE;
1430 case PSM_ISDIALOGMESSAGE:
1432 FIXME("Unimplemented msg PSM_ISDIALOGMESSAGE\n");
1433 return 0;
1436 case PSM_PRESSBUTTON:
1437 PROPSHEET_PressButton(hwnd, (int)wParam);
1438 return TRUE;
1440 case PSM_SETTITLEW:
1441 FIXME("Unimplemented msg PSM_SETTITLE32W\n");
1442 return 0;
1443 case PSM_SETWIZBUTTONS:
1444 FIXME("Unimplemented msg PSM_SETWIZBUTTONS\n");
1445 return 0;
1446 case PSM_SETCURSELID:
1447 FIXME("Unimplemented msg PSM_SETCURSELID\n");
1448 return 0;
1449 case PSM_SETFINISHTEXTA:
1450 FIXME("Unimplemented msg PSM_SETFINISHTEXT32A\n");
1451 return 0;
1452 case PSM_SETFINISHTEXTW:
1453 FIXME("Unimplemented msg PSM_SETFINISHTEXT32W\n");
1454 return 0;
1456 default:
1457 return FALSE;