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"
29 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
31 typedef struct DLGTEMPLATEEX
44 DLGTEMPLATEEX
, *LPDLGTEMPLATEEX
;
47 PropSheet::PropSheet ()
51 PropSheet::~PropSheet ()
56 PropSheet::CreatePages ()
58 HPROPSHEETPAGE
*retarray
;
60 // Create the return array
61 retarray
= new HPROPSHEETPAGE
[PropertyPages
.size()];
63 // Create the pages with CreatePropertySheetPage().
64 // We do it here rather than in the PropertyPages themselves
65 // because, for reasons known only to Microsoft, these handles will be
66 // destroyed by the property sheet before the PropertySheet() call returns,
67 // at least if it's modal (don't know about modeless).
69 for (i
= 0; i
< PropertyPages
.size(); i
++)
72 CreatePropertySheetPageW (PropertyPages
[i
]->GetPROPSHEETPAGEPtr ());
77 PropertyPages
[i
]->YouAreFirst ();
79 else if (i
== PropertyPages
.size() - 1)
81 PropertyPages
[i
]->YouAreLast ();
85 PropertyPages
[i
]->YouAreMiddle ();
92 // Stuff needed by the PropSheet wndproc hook
97 RECTWrapper lastClientRect
;
106 clientRectValid
= false;
111 // @@@ Ugly. Really only works because only one PS is used now.
112 static PropSheetData
& Instance()
114 static PropSheetData TheInstance
;
119 static ControlAdjuster::ControlInfo PropSheetControlsInfo
[] = {
120 {0x3023, CP_RIGHT
, CP_BOTTOM
}, // Back
121 {0x3024, CP_RIGHT
, CP_BOTTOM
}, // Next
122 {0x3025, CP_RIGHT
, CP_BOTTOM
}, // Finish
123 {0x3026, CP_STRETCH
, CP_BOTTOM
}, // Line above buttons
124 { 2, CP_RIGHT
, CP_BOTTOM
}, // Cancel
128 static bool IsDialog (HWND hwnd
)
131 GetClassName (hwnd
, className
, sizeof (className
));
133 return (strcmp (className
, "#32770") == 0);
136 BOOL CALLBACK
EnumPages (HWND hwnd
, LPARAM lParam
)
138 // Is it really a dialog?
141 PropSheetData
& psd
= PropSheetData::Instance();
142 SetWindowPos (hwnd
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
143 psd
.pageRect
.width (), psd
.pageRect
.height (),
144 SWP_NOACTIVATE
| SWP_NOZORDER
);
150 static LRESULT CALLBACK
PropSheetWndProc (HWND hwnd
, UINT uMsg
,
151 WPARAM wParam
, LPARAM lParam
)
153 PropSheetData
& psd
= PropSheetData::Instance();
157 if ((wParam
& 0xfff0) == SC_CLOSE
)
164 if (unattended_mode
== unattended
)
166 if (mbox(hwnd
, IDS_CONFIRM_EXIT
, MB_YESNO
) == IDNO
)
171 RECTWrapper clientRect
;
172 GetClientRect (hwnd
, &clientRect
);
175 When the window is minimized, the client rect is reduced to
176 (0,0-0,0), which causes child adjusting to screw slightly up. Work
177 around by not adjusting child upon minimization - it isn't really
180 if (wParam
!= SIZE_MINIMIZED
)
183 The first time we get a WM_SIZE, the client rect will be all zeros.
185 if (psd
.clientRectValid
)
188 clientRect
.width () - psd
.lastClientRect
.width ();
190 clientRect
.height () - psd
.lastClientRect
.height ();
192 ControlAdjuster::AdjustControls (hwnd
, PropSheetControlsInfo
,
195 psd
.pageRect
.right
+= dX
;
196 psd
.pageRect
.bottom
+= dY
;
199 The pages are child windows, but don't have IDs.
200 So change them by enumerating all childs and adjust all
204 EnumChildWindows (hwnd
, &EnumPages
, 0);
208 psd
.clientRectValid
= true;
211 Store away the current size and use it as the minmal window size.
215 GetWindowRect (hwnd
, &psd
.minRect
);
216 psd
.hasMinRect
= true;
219 psd
.lastClientRect
= clientRect
;
223 case WM_GETMINMAXINFO
:
227 LPMINMAXINFO mmi
= (LPMINMAXINFO
)lParam
;
228 mmi
->ptMinTrackSize
.x
= psd
.minRect
.width ();
229 mmi
->ptMinTrackSize
.y
= psd
.minRect
.height ();
235 return CallWindowProc (psd
.oldWndProc
,
236 hwnd
, uMsg
, wParam
, lParam
);
240 PropSheetProc (HWND hwndDlg
, UINT uMsg
, LPARAM lParam
)
246 LONG additionalStyle
=
247 (WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_THICKFRAME
);
248 // Add a minimize box to the sheet/wizard.
249 if (((LPDLGTEMPLATEEX
) lParam
)->signature
== 0xFFFF)
251 ((LPDLGTEMPLATEEX
) lParam
)->style
|= additionalStyle
;
255 ((LPDLGTEMPLATE
) lParam
)->style
|= additionalStyle
;
259 case PSCB_INITIALIZED
:
262 PropSheet() with PSH_USEICONID only sets the small icon,
263 so we must set the big icon ourselves
265 SendMessage(hwndDlg
, WM_SETICON
, ICON_BIG
, (LPARAM
)LoadIcon(GetModuleHandle(NULL
), MAKEINTRESOURCE(IDI_CYGWIN
)));
267 Hook into the window proc.
268 We need to catch some messages for resizing.
270 PropSheetData::Instance().oldWndProc
= (WNDPROC
)GetWindowLongPtrW (hwndDlg
, GWLP_WNDPROC
);
271 SetWindowLongPtrW (hwndDlg
, GWLP_WNDPROC
, (LONG_PTR
)&PropSheetWndProc
);
273 Store the PropSheet HWND in the Chooser, for use in resizing
275 (XXX: this is just silly: The PropSheet HWND is the parent of the
276 PropPage HWND, so chooser has access to that HWND as GetParent()
279 ChooserPage::SetHwndDialog (hwndDlg
);
287 PropSheet::Create (const Window
* Parent
, DWORD Style
)
291 PageHandles
= CreatePages ();
293 p
.dwSize
= sizeof (PROPSHEETHEADERW
);
294 p
.dwFlags
= PSH_NOAPPLYNOW
| PSH_WIZARD
| PSH_USECALLBACK
295 /*| PSH_MODELESS */ | PSH_USEICONID
;
298 p
.hwndParent
= Parent
->GetHWND ();
304 p
.hInstance
= GetInstance ();
305 p
.nPages
= PropertyPages
.size();
306 p
.pszIcon
= MAKEINTRESOURCEW(IDI_CYGWIN
);
308 p
.phpage
= PageHandles
;
309 p
.pfnCallback
= PropSheetProc
;
311 // The winmain event loop actually resides in here.
321 PropSheet::SetHWNDFromPage (HWND h
)
323 // If we're a modal dialog, there's no way for us to know our window handle unless
324 // one of our pages tells us through this function.
329 Adjust the size of a page so that it fits nicely into the window.
332 PropSheet::AdjustPageSize (HWND page
)
334 PropSheetData
& psd
= PropSheetData::Instance();
335 if (!psd
.clientRectValid
) return;
338 It's probably not obvious what's done here:
339 When this method is called the first time, the first page is already
340 created and sized, but at the coordinates (0,0). The sheet, however,
341 isn't in it's final size. My guess is that the sheet first creates the
342 page, and then resizes itself to have the right metrics to contain the
343 page and moves it to it's position. For our purposes, however, we need
344 the final metrics of the page. So, the first time this method is called,
345 we basically grab the size of the page, but calculate the top/left coords
353 RECTWrapper
& pageRect
= psd
.pageRect
;
354 ::GetWindowRect (page
, &pageRect
);
355 // We want client coords.
356 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.left
);
357 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.right
);
359 LONG dialogBaseUnits
= ::GetDialogBaseUnits ();
360 // The margins in DUs are a result of "educated guesses" and T&E.
361 int marginX
= MulDiv (5, LOWORD(dialogBaseUnits
), 4);
362 int marginY
= MulDiv (5, HIWORD(dialogBaseUnits
), 8);
364 pageRect
.move (marginX
, marginY
);
367 SetWindowPos (page
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
368 psd
.pageRect
.width (), psd
.pageRect
.height (),
369 SWP_NOACTIVATE
| SWP_NOZORDER
);
373 PropSheet::AddPage (PropertyPage
* p
)
375 // Add a page to the property sheet.
376 p
->YouAreBeingAddedToASheet (this);
377 PropertyPages
.push_back(p
);
381 PropSheet::SetActivePage (int i
)
383 // Posts a message to the message queue, so this won't block
384 return static_cast < bool > (PropSheet_SetCurSel (GetHWND (), NULL
, i
));
388 PropSheet::SetActivePageByID (int resource_id
)
390 // Posts a message to the message queue, so this won't block
391 return static_cast < bool >
392 (PropSheet_SetCurSelByID (GetHWND (), resource_id
));
396 PropSheet::SetButtons (DWORD flags
)
398 // Posts a message to the message queue, so this won't block
399 PropSheet_SetWizButtons (GetHWND (), flags
);
403 PropSheet::PressButton (int button
)
405 PropSheet_PressButton (GetHWND (), button
);