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"
28 //#include <shlwapi.h>
29 // ...but since there is no shlwapi.h in mingw yet:
30 typedef struct _DllVersionInfo
39 typedef HRESULT
CALLBACK (*DLLGETVERSIONPROC
) (DLLVERSIONINFO
* pdvi
);
40 #define PROPSHEETHEADER_V1_SIZE 40
42 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
44 typedef struct DLGTEMPLATEEX
57 DLGTEMPLATEEX
, *LPDLGTEMPLATEEX
;
60 PropSheet::PropSheet ()
64 PropSheet::~PropSheet ()
69 PropSheet::CreatePages ()
71 HPROPSHEETPAGE
*retarray
;
73 // Create the return array
74 retarray
= new HPROPSHEETPAGE
[PropertyPages
.size()];
76 // Create the pages with CreatePropertySheetPage().
77 // We do it here rather than in the PropertyPages themselves
78 // because, for reasons known only to Microsoft, these handles will be
79 // destroyed by the property sheet before the PropertySheet() call returns,
80 // at least if it's modal (don't know about modeless).
82 for (i
= 0; i
< PropertyPages
.size(); i
++)
85 CreatePropertySheetPage (PropertyPages
[i
]->GetPROPSHEETPAGEPtr ());
90 PropertyPages
[i
]->YouAreFirst ();
92 else if (i
== PropertyPages
.size() - 1)
94 PropertyPages
[i
]->YouAreLast ();
98 PropertyPages
[i
]->YouAreMiddle ();
105 // Stuff needed by the PropSheet wndproc hook
109 bool clientRectValid
;
110 RECTWrapper lastClientRect
;
112 RECTWrapper pageRect
;
119 clientRectValid
= false;
124 // @@@ Ugly. Really only works because only one PS is used now.
125 static PropSheetData
& Instance()
127 static PropSheetData TheInstance
;
132 static ControlAdjuster::ControlInfo PropSheetControlsInfo
[] = {
133 {0x3023, CP_RIGHT
, CP_BOTTOM
}, // Back
134 {0x3024, CP_RIGHT
, CP_BOTTOM
}, // Next
135 {0x3025, CP_RIGHT
, CP_BOTTOM
}, // Finish
136 {0x3026, CP_STRETCH
, CP_BOTTOM
}, // Line above buttons
137 { 2, CP_RIGHT
, CP_BOTTOM
}, // Cancel
141 static bool IsDialog (HWND hwnd
)
144 GetClassName (hwnd
, className
, sizeof (className
));
146 return (strcmp (className
, "#32770") == 0);
149 BOOL CALLBACK
EnumPages (HWND hwnd
, LPARAM lParam
)
151 // Is it really a dialog?
154 PropSheetData
& psd
= PropSheetData::Instance();
155 SetWindowPos (hwnd
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
156 psd
.pageRect
.width (), psd
.pageRect
.height (),
157 SWP_NOACTIVATE
| SWP_NOZORDER
);
163 static LRESULT CALLBACK
PropSheetWndProc (HWND hwnd
, UINT uMsg
,
164 WPARAM wParam
, LPARAM lParam
)
166 PropSheetData
& psd
= PropSheetData::Instance();
170 if ((wParam
& 0xfff0) == SC_CLOSE
)
178 "Are you sure you want to exit setup? Any current download or installation will be aborted.",
179 "Exit Cygwin Setup?", MB_YESNO
) == IDNO
)
184 RECTWrapper clientRect
;
185 GetClientRect (hwnd
, &clientRect
);
188 When the window is minimized, the client rect is reduced to
189 (0,0-0,0), which causes child adjusting to screw slightly up. Work
190 around by not adjusting child upon minimization - it isn't really
193 if (wParam
!= SIZE_MINIMIZED
)
196 The first time we get a WM_SIZE, the client rect will be all zeros.
198 if (psd
.clientRectValid
)
201 clientRect
.width () - psd
.lastClientRect
.width ();
203 clientRect
.height () - psd
.lastClientRect
.height ();
205 ControlAdjuster::AdjustControls (hwnd
, PropSheetControlsInfo
,
208 psd
.pageRect
.right
+= dX
;
209 psd
.pageRect
.bottom
+= dY
;
212 The pages are child windows, but don't have IDs.
213 So change them by enumerating all childs and adjust all
217 EnumChildWindows (hwnd
, &EnumPages
, 0);
221 psd
.clientRectValid
= true;
224 Store away the current size and use it as the minmal window size.
228 GetWindowRect (hwnd
, &psd
.minRect
);
229 psd
.hasMinRect
= true;
232 psd
.lastClientRect
= clientRect
;
236 case WM_GETMINMAXINFO
:
240 LPMINMAXINFO mmi
= (LPMINMAXINFO
)lParam
;
241 mmi
->ptMinTrackSize
.x
= psd
.minRect
.width ();
242 mmi
->ptMinTrackSize
.y
= psd
.minRect
.height ();
248 return CallWindowProc (psd
.oldWndProc
,
249 hwnd
, uMsg
, wParam
, lParam
);
253 PropSheetProc (HWND hwndDlg
, UINT uMsg
, LPARAM lParam
)
259 LONG additionalStyle
=
260 (WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_THICKFRAME
);
261 // Add a minimize box to the sheet/wizard.
262 if (((LPDLGTEMPLATEEX
) lParam
)->signature
== 0xFFFF)
264 ((LPDLGTEMPLATEEX
) lParam
)->style
|= additionalStyle
;
268 ((LPDLGTEMPLATE
) lParam
)->style
|= additionalStyle
;
272 case PSCB_INITIALIZED
:
275 Hook into the window proc.
276 We need to catch some messages for resizing.
278 PropSheetData::Instance().oldWndProc
=
279 (WNDPROC
)GetWindowLongPtr (hwndDlg
, GWLP_WNDPROC
);
280 SetWindowLongPtr (hwndDlg
, GWLP_WNDPROC
,
281 (LONG_PTR
)&PropSheetWndProc
);
282 ChooserPage::SetHwndDialog (hwndDlg
);
290 GetPROPSHEETHEADERSize ()
292 // For compatibility with all versions of comctl32.dll, we have to do this.
296 DLLGETVERSIONPROC DllGetVersion
;
300 // This 'isn't safe' in a DLL, according to MSDN
301 mod
= LoadLibrary ("comctl32.dll");
303 DllGetVersion
= (DLLGETVERSIONPROC
) GetProcAddress (mod
, "DllGetVersion");
304 if (DllGetVersion
== NULL
)
306 // Something's wildly broken, punt.
307 retval
= PROPSHEETHEADER_V1_SIZE
;
311 vi
.cbSize
= sizeof (DLLVERSIONINFO
);
314 if ((vi
.dwMajorVersion
< 4) ||
315 ((vi
.dwMajorVersion
== 4) && (vi
.dwMinorVersion
< 71)))
318 retval
= sizeof (PROPSHEETHEADER
);
322 // Old (== Win95/NT4 w/o IE 4 or better)
323 retval
= PROPSHEETHEADER_V1_SIZE
;
333 PropSheet::Create (const Window
* Parent
, DWORD Style
)
337 PageHandles
= CreatePages ();
339 p
.dwSize
= GetPROPSHEETHEADERSize ();
340 p
.dwFlags
= PSH_NOAPPLYNOW
| PSH_WIZARD
| PSH_USECALLBACK
341 /*| PSH_MODELESS */ | PSH_USEICONID
;
344 p
.hwndParent
= Parent
->GetHWND ();
350 p
.hInstance
= GetInstance ();
351 p
.nPages
= PropertyPages
.size();
352 p
.pszIcon
= MAKEINTRESOURCE(IDI_CYGWIN
);
354 p
.phpage
= PageHandles
;
355 p
.pfnCallback
= PropSheetProc
;
358 // The winmain event loop actually resides in here.
361 // Do a modeless property sheet...
362 //SetHWND((HWND)PropertySheet(&p));
363 /*Show(SW_SHOWNORMAL);
365 // ...but pretend it's modal
367 MessageBox(NULL, "DONE", NULL, MB_OK);
369 // FIXME: Enable the parent before destroying this window to prevent another window
370 // from becoming the foreground window
371 // ala: EnableWindow(<parent_hwnd>, TRUE);
372 //DestroyWindow(WindowHandle);
381 PropSheet::SetHWNDFromPage (HWND h
)
383 // If we're a modal dialog, there's no way for us to know our window handle unless
384 // one of our pages tells us through this function.
389 Adjust the size of a page so that it fits nicely into the window.
392 PropSheet::AdjustPageSize (HWND page
)
394 PropSheetData
& psd
= PropSheetData::Instance();
395 if (!psd
.clientRectValid
) return;
398 It's probably not obvious what's done here:
399 When this method is called the first time, the first page is already
400 created and sized, but at the coordinates (0,0). The sheet, however,
401 isn't in it's final size. My guess is that the sheet first creates the
402 page, and then resizes itself to have the right metrics to contain the
403 page and moves it to it's position. For our purposes, however, we need
404 the final metrics of the page. So, the first time this method is called,
405 we basically grab the size of the page, but calculate the top/left coords
413 RECTWrapper
& pageRect
= psd
.pageRect
;
414 ::GetWindowRect (page
, &pageRect
);
415 // We want client coords.
416 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.left
);
417 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.right
);
419 LONG dialogBaseUnits
= ::GetDialogBaseUnits ();
420 // The margins in DUs are a result of "educated guesses" and T&E.
421 int marginX
= MulDiv (5, LOWORD(dialogBaseUnits
), 4);
422 int marginY
= MulDiv (5, HIWORD(dialogBaseUnits
), 8);
424 pageRect
.move (marginX
, marginY
);
427 SetWindowPos (page
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
428 psd
.pageRect
.width (), psd
.pageRect
.height (),
429 SWP_NOACTIVATE
| SWP_NOZORDER
);
433 PropSheet::AddPage (PropertyPage
* p
)
435 // Add a page to the property sheet.
436 p
->YouAreBeingAddedToASheet (this);
437 PropertyPages
.push_back(p
);
441 PropSheet::SetActivePage (int i
)
443 // Posts a message to the message queue, so this won't block
444 return static_cast < bool > (::PropSheet_SetCurSel (GetHWND (), NULL
, i
));
448 PropSheet::SetActivePageByID (int resource_id
)
450 // Posts a message to the message queue, so this won't block
451 return static_cast < bool >
452 (::PropSheet_SetCurSelByID (GetHWND (), resource_id
));
456 PropSheet::SetButtons (DWORD flags
)
458 // Posts a message to the message queue, so this won't block
459 ::PropSheet_SetWizButtons (GetHWND (), flags
);
463 PropSheet::PressButton (int button
)
465 ::PropSheet_PressButton (GetHWND (), button
);