2 * Copyright (c) 2001, 2002, 2003 Gary R. Van Sickle.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
12 * Written by Gary R. Van Sickle <g.r.vansickle@worldnet.att.net>
16 // This is the implementation of the PropSheet class. This class encapsulates
17 // a Windows property sheet / wizard and interfaces with the PropertyPage class.
18 // It's named PropSheet instead of PropertySheet because the latter conflicts with
19 // the Windows function of the same name.
21 #include "propsheet.h"
24 #include "RECTWrapper.h"
25 #include "ControlAdjuster.h"
27 //#include <shlwapi.h>
28 // ...but since there is no shlwapi.h in mingw yet:
29 typedef struct _DllVersionInfo
38 typedef HRESULT
CALLBACK (*DLLGETVERSIONPROC
) (DLLVERSIONINFO
* pdvi
);
39 #define PROPSHEETHEADER_V1_SIZE 40
41 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
43 typedef struct DLGTEMPLATEEX
56 DLGTEMPLATEEX
, *LPDLGTEMPLATEEX
;
59 PropSheet::PropSheet ()
63 PropSheet::~PropSheet ()
68 PropSheet::CreatePages ()
70 HPROPSHEETPAGE
*retarray
;
72 // Create the return array
73 retarray
= new HPROPSHEETPAGE
[PropertyPages
.size()];
75 // Create the pages with CreatePropertySheetPage().
76 // We do it here rather than in the PropertyPages themselves
77 // because, for reasons known only to Microsoft, these handles will be
78 // destroyed by the property sheet before the PropertySheet() call returns,
79 // at least if it's modal (don't know about modeless).
81 for (i
= 0; i
< PropertyPages
.size(); i
++)
84 CreatePropertySheetPage (PropertyPages
[i
]->GetPROPSHEETPAGEPtr ());
89 PropertyPages
[i
]->YouAreFirst ();
91 else if (i
== PropertyPages
.size() - 1)
93 PropertyPages
[i
]->YouAreLast ();
97 PropertyPages
[i
]->YouAreMiddle ();
104 // Stuff needed by the PropSheet wndproc hook
108 bool clientRectValid
;
109 RECTWrapper lastClientRect
;
111 RECTWrapper pageRect
;
118 clientRectValid
= false;
123 // @@@ Ugly. Really only works because only one PS is used now.
124 static PropSheetData
& Instance()
126 static PropSheetData TheInstance
;
131 static ControlAdjuster::ControlInfo PropSheetControlsInfo
[] = {
132 {0x3023, CP_RIGHT
, CP_BOTTOM
}, // Back
133 {0x3024, CP_RIGHT
, CP_BOTTOM
}, // Next
134 {0x3025, CP_RIGHT
, CP_BOTTOM
}, // Finish
135 {0x3026, CP_STRETCH
, CP_BOTTOM
}, // Line above buttons
136 { 2, CP_RIGHT
, CP_BOTTOM
}, // Cancel
140 static bool IsDialog (HWND hwnd
)
143 GetClassName (hwnd
, className
, sizeof (className
));
145 return (strcmp (className
, "#32770") == 0);
148 BOOL CALLBACK
EnumPages (HWND hwnd
, LPARAM lParam
)
150 // Is it really a dialog?
153 PropSheetData
& psd
= PropSheetData::Instance();
154 SetWindowPos (hwnd
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
155 psd
.pageRect
.width (), psd
.pageRect
.height (),
156 SWP_NOACTIVATE
| SWP_NOZORDER
);
162 static LRESULT CALLBACK
PropSheetWndProc (HWND hwnd
, UINT uMsg
,
163 WPARAM wParam
, LPARAM lParam
)
165 PropSheetData
& psd
= PropSheetData::Instance();
170 RECTWrapper clientRect
;
171 GetClientRect (hwnd
, &clientRect
);
174 When the window is minimized, the client rect is reduced to
175 (0,0-0,0), which causes child adjusting to screw slightly up. Work
176 around by not adjusting child upon minimization - it isn't really
179 if (wParam
!= SIZE_MINIMIZED
)
182 The first time we get a WM_SIZE, the client rect will be all zeros.
184 if (psd
.clientRectValid
)
187 clientRect
.width () - psd
.lastClientRect
.width ();
189 clientRect
.height () - psd
.lastClientRect
.height ();
191 ControlAdjuster::AdjustControls (hwnd
, PropSheetControlsInfo
,
194 psd
.pageRect
.right
+= dX
;
195 psd
.pageRect
.bottom
+= dY
;
198 The pages are child windows, but don't have IDs.
199 So change them by enumerating all childs and adjust all
203 EnumChildWindows (hwnd
, &EnumPages
, 0);
207 psd
.clientRectValid
= true;
210 Store away the current size and use it as the minmal window size.
214 GetWindowRect (hwnd
, &psd
.minRect
);
215 psd
.hasMinRect
= true;
218 psd
.lastClientRect
= clientRect
;
222 case WM_GETMINMAXINFO
:
226 LPMINMAXINFO mmi
= (LPMINMAXINFO
)lParam
;
227 mmi
->ptMinTrackSize
.x
= psd
.minRect
.width ();
228 mmi
->ptMinTrackSize
.y
= psd
.minRect
.height ();
234 return CallWindowProc (psd
.oldWndProc
,
235 hwnd
, uMsg
, wParam
, lParam
);
239 PropSheetProc (HWND hwndDlg
, UINT uMsg
, LPARAM lParam
)
245 const LONG additionalStyle
=
246 (WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_THICKFRAME
);
247 // Add a minimize box to the sheet/wizard.
248 if (((LPDLGTEMPLATEEX
) lParam
)->signature
== 0xFFFF)
250 ((LPDLGTEMPLATEEX
) lParam
)->style
|= additionalStyle
;
254 ((LPDLGTEMPLATE
) lParam
)->style
|= additionalStyle
;
258 case PSCB_INITIALIZED
:
261 Hook into the window proc.
262 We need to catch some messages for resizing.
264 PropSheetData::Instance().oldWndProc
=
265 (WNDPROC
)GetWindowLongPtr (hwndDlg
, GWLP_WNDPROC
);
266 SetWindowLongPtr (hwndDlg
, GWLP_WNDPROC
,
267 (LONG_PTR
)&PropSheetWndProc
);
275 GetPROPSHEETHEADERSize ()
277 // For compatibility with all versions of comctl32.dll, we have to do this.
281 DLLGETVERSIONPROC DllGetVersion
;
285 // This 'isn't safe' in a DLL, according to MSDN
286 mod
= LoadLibrary ("comctl32.dll");
288 DllGetVersion
= (DLLGETVERSIONPROC
) GetProcAddress (mod
, "DllGetVersion");
289 if (DllGetVersion
== NULL
)
291 // Something's wildly broken, punt.
292 retval
= PROPSHEETHEADER_V1_SIZE
;
296 vi
.cbSize
= sizeof (DLLVERSIONINFO
);
299 if ((vi
.dwMajorVersion
< 4) ||
300 ((vi
.dwMajorVersion
== 4) && (vi
.dwMinorVersion
< 71)))
303 retval
= sizeof (PROPSHEETHEADER
);
307 // Old (== Win95/NT4 w/o IE 4 or better)
308 retval
= PROPSHEETHEADER_V1_SIZE
;
318 PropSheet::Create (const Window
* Parent
, DWORD Style
)
322 PageHandles
= CreatePages ();
324 p
.dwSize
= GetPROPSHEETHEADERSize ();
325 p
.dwFlags
= PSH_NOAPPLYNOW
| PSH_WIZARD
| PSH_USECALLBACK
326 /*| PSH_MODELESS */ | PSH_USEICONID
;
329 p
.hwndParent
= Parent
->GetHWND ();
335 p
.hInstance
= GetInstance ();
336 p
.nPages
= PropertyPages
.size();
337 p
.pszIcon
= MAKEINTRESOURCE(IDI_CYGWIN
);
339 p
.phpage
= PageHandles
;
340 p
.pfnCallback
= PropSheetProc
;
343 // The winmain event loop actually resides in here.
346 // Do a modeless property sheet...
347 //SetHWND((HWND)PropertySheet(&p));
348 /*Show(SW_SHOWNORMAL);
350 // ...but pretend it's modal
352 MessageBox(NULL, "DONE", NULL, MB_OK);
354 // FIXME: Enable the parent before destroying this window to prevent another window
355 // from becoming the foreground window
356 // ala: EnableWindow(<parent_hwnd>, TRUE);
357 //DestroyWindow(WindowHandle);
366 PropSheet::SetHWNDFromPage (HWND h
)
368 // If we're a modal dialog, there's no way for us to know our window handle unless
369 // one of our pages tells us through this function.
374 Adjust the size of a page so that it fits nicely into the window.
377 PropSheet::AdjustPageSize (HWND page
)
379 PropSheetData
& psd
= PropSheetData::Instance();
380 if (!psd
.clientRectValid
) return;
383 It's probably not obvious what's done here:
384 When this method is called the first time, the first page is already
385 created and sized, but at the coordinates (0,0). The sheet, however,
386 isn't in it's final size. My guess is that the sheet first creates the
387 page, and then resizes itself to have the right metrics to contain the
388 page and moves it to it's position. For our purposes, however, we need
389 the final metrics of the page. So, the first time this method is called,
390 we basically grab the size of the page, but calculate the top/left coords
398 RECTWrapper
& pageRect
= psd
.pageRect
;
399 ::GetWindowRect (page
, &pageRect
);
400 // We want client coords.
401 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.left
);
402 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.right
);
404 LONG dialogBaseUnits
= ::GetDialogBaseUnits ();
405 // The margins in DUs are a result of "educated guesses" and T&E.
406 int marginX
= MulDiv (5, LOWORD(dialogBaseUnits
), 4);
407 int marginY
= MulDiv (5, HIWORD(dialogBaseUnits
), 8);
409 pageRect
.move (marginX
, marginY
);
412 SetWindowPos (page
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
413 psd
.pageRect
.width (), psd
.pageRect
.height (),
414 SWP_NOACTIVATE
| SWP_NOZORDER
);
418 PropSheet::AddPage (PropertyPage
* p
)
420 // Add a page to the property sheet.
421 p
->YouAreBeingAddedToASheet (this);
422 PropertyPages
.push_back(p
);
426 PropSheet::SetActivePage (int i
)
428 // Posts a message to the message queue, so this won't block
429 return static_cast < bool > (::PropSheet_SetCurSel (GetHWND (), NULL
, i
));
433 PropSheet::SetActivePageByID (int resource_id
)
435 // Posts a message to the message queue, so this won't block
436 return static_cast < bool >
437 (::PropSheet_SetCurSelByID (GetHWND (), resource_id
));
441 PropSheet::SetButtons (DWORD flags
)
443 // Posts a message to the message queue, so this won't block
444 ::PropSheet_SetWizButtons (GetHWND (), flags
);
448 PropSheet::PressButton (int button
)
450 ::PropSheet_PressButton (GetHWND (), button
);