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"
31 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
33 typedef struct DLGTEMPLATEEX
46 DLGTEMPLATEEX
, *LPDLGTEMPLATEEX
;
49 PropSheet::PropSheet ()
53 PropSheet::~PropSheet ()
58 PropSheet::CreatePages ()
60 HPROPSHEETPAGE
*retarray
;
62 // Create the return array
63 retarray
= new HPROPSHEETPAGE
[PropertyPages
.size()];
65 // Create the pages with CreatePropertySheetPage().
66 // We do it here rather than in the PropertyPages themselves
67 // because, for reasons known only to Microsoft, these handles will be
68 // destroyed by the property sheet before the PropertySheet() call returns,
69 // at least if it's modal (don't know about modeless).
71 for (i
= 0; i
< PropertyPages
.size(); i
++)
74 CreatePropertySheetPageW (PropertyPages
[i
]->GetPROPSHEETPAGEPtr ());
79 PropertyPages
[i
]->YouAreFirst ();
81 else if (i
== PropertyPages
.size() - 1)
83 PropertyPages
[i
]->YouAreLast ();
87 PropertyPages
[i
]->YouAreMiddle ();
94 // Stuff needed by the PropSheet wndproc hook
99 RECTWrapper lastClientRect
;
101 RECTWrapper pageRect
;
108 clientRectValid
= false;
113 // @@@ Ugly. Really only works because only one PS is used now.
114 static PropSheetData
& Instance()
116 static PropSheetData TheInstance
;
121 static ControlAdjuster::ControlInfo PropSheetControlsInfo
[] = {
122 {0x3023, CP_RIGHT
, CP_BOTTOM
}, // Back
123 {0x3024, CP_RIGHT
, CP_BOTTOM
}, // Next
124 {0x3025, CP_RIGHT
, CP_BOTTOM
}, // Finish
125 {0x3026, CP_STRETCH
, CP_BOTTOM
}, // Line above buttons
126 { 2, CP_RIGHT
, CP_BOTTOM
}, // Cancel
130 static bool IsDialog (HWND hwnd
)
133 GetClassName (hwnd
, className
, sizeof (className
));
135 return (strcmp (className
, "#32770") == 0);
138 BOOL CALLBACK
EnumPages (HWND hwnd
, LPARAM lParam
)
140 // Is it really a dialog?
143 PropSheetData
& psd
= PropSheetData::Instance();
144 SetWindowPos (hwnd
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
145 psd
.pageRect
.width (), psd
.pageRect
.height (),
146 SWP_NOACTIVATE
| SWP_NOZORDER
);
153 GetSizeGripRect(HWND hwnd
, LPRECT rect
)
155 GetClientRect(hwnd
, rect
);
156 rect
->left
= rect
->right
- GetSystemMetrics(SM_CXHSCROLL
);
157 rect
->top
= rect
->bottom
- GetSystemMetrics(SM_CYVSCROLL
);
160 static LRESULT CALLBACK
PropSheetWndProc (HWND hwnd
, UINT uMsg
,
161 WPARAM wParam
, LPARAM lParam
)
163 PropSheetData
& psd
= PropSheetData::Instance();
167 if ((wParam
& 0xfff0) == SC_CLOSE
)
174 if (unattended_mode
== unattended
)
176 if (mbox(hwnd
, IDS_CONFIRM_EXIT
, MB_YESNO
) == IDNO
)
181 RECTWrapper clientRect
;
182 GetClientRect (hwnd
, &clientRect
);
185 When the window is minimized, the client rect is reduced to
186 (0,0-0,0), which causes child adjusting to screw slightly up. Work
187 around by not adjusting child upon minimization - it isn't really
190 if (wParam
!= SIZE_MINIMIZED
)
193 The first time we get a WM_SIZE, the client rect will be all zeros.
195 if (psd
.clientRectValid
)
198 clientRect
.width () - psd
.lastClientRect
.width ();
200 clientRect
.height () - psd
.lastClientRect
.height ();
202 ControlAdjuster::AdjustControls (hwnd
, PropSheetControlsInfo
,
205 psd
.pageRect
.right
+= dX
;
206 psd
.pageRect
.bottom
+= dY
;
209 The pages are child windows, but don't have IDs.
210 So change them by enumerating all childs and adjust all
214 EnumChildWindows (hwnd
, &EnumPages
, 0);
218 psd
.clientRectValid
= true;
221 Store away the current size and use it as the minmal window size.
225 GetWindowRect (hwnd
, &psd
.minRect
);
226 psd
.hasMinRect
= true;
229 psd
.lastClientRect
= clientRect
;
233 case WM_GETMINMAXINFO
:
237 LPMINMAXINFO mmi
= (LPMINMAXINFO
)lParam
;
238 mmi
->ptMinTrackSize
.x
= psd
.minRect
.width ();
239 mmi
->ptMinTrackSize
.y
= psd
.minRect
.height ();
246 GetSizeGripRect(hwnd
, &rect
);
249 HDC dc
= GetDC(hwnd
);
250 DrawFrameControl(dc
, &rect
, DFC_SCROLL
, DFCS_SCROLLSIZEGRIP
);
257 GetSizeGripRect(hwnd
, &rect
);
258 MapWindowPoints(hwnd
, NULL
, reinterpret_cast<POINT
*>(&rect
), 2);
260 // when over the size grip, pretend to be over bottom-right corner of
262 POINT point
= {GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
)};
263 if (PtInRect(&rect
, point
))
264 return HTBOTTOMRIGHT
;
266 // otherwise, fall through...
271 // invalidate the area containing the size grip when the window is
272 // resized, so it's redrawn
274 GetSizeGripRect(hwnd
, &rect
);
275 InvalidateRect(hwnd
, &rect
, FALSE
);
280 return CallWindowProc (psd
.oldWndProc
,
281 hwnd
, uMsg
, wParam
, lParam
);
285 PropSheetProc (HWND hwndDlg
, UINT uMsg
, LPARAM lParam
)
291 LONG additionalStyle
= (WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_THICKFRAME
);
293 // 'noinput' quiet mode adds WS_DISABLE to prevent user interaction
294 // with the propsheet
295 if (UnattendedOption
== QuietNoInput
)
296 additionalStyle
|= WS_DISABLED
;
298 // 'hidden' quiet mode totally hides the propsheet window
299 LONG forbiddenStyle
= 0;
300 if (UnattendedOption
== QuietHidden
)
301 forbiddenStyle
|= WS_VISIBLE
;
303 // Add a minimize box to the sheet/wizard.
304 if (((LPDLGTEMPLATEEX
) lParam
)->signature
== 0xFFFF)
306 ((LPDLGTEMPLATEEX
) lParam
)->style
|= additionalStyle
;
307 ((LPDLGTEMPLATEEX
) lParam
)->style
&= ~forbiddenStyle
;
311 ((LPDLGTEMPLATE
) lParam
)->style
|= additionalStyle
;
312 ((LPDLGTEMPLATE
) lParam
)->style
&= ~forbiddenStyle
;
316 case PSCB_INITIALIZED
:
319 PropSheet() with PSH_USEICONID only sets the small icon,
320 so we must set the big icon ourselves
322 SendMessage(hwndDlg
, WM_SETICON
, ICON_BIG
, (LPARAM
)LoadIcon(GetModuleHandle(NULL
), MAKEINTRESOURCE(IDI_CYGWIN
)));
324 Hook into the window proc.
325 We need to catch some messages for resizing.
327 PropSheetData::Instance().oldWndProc
= (WNDPROC
)GetWindowLongPtrW (hwndDlg
, GWLP_WNDPROC
);
328 SetWindowLongPtrW (hwndDlg
, GWLP_WNDPROC
, (LONG_PTR
)&PropSheetWndProc
);
330 Store the PropSheet HWND in the Chooser, for use in resizing
332 (XXX: this is just silly: The PropSheet HWND is the parent of the
333 PropPage HWND, so chooser has access to that HWND as GetParent()
336 ChooserPage::SetHwndDialog (hwndDlg
);
344 PropSheet::Create (const Window
* Parent
, DWORD Style
)
348 PageHandles
= CreatePages ();
350 p
.dwSize
= sizeof (PROPSHEETHEADERW
);
351 p
.dwFlags
= PSH_NOAPPLYNOW
| PSH_WIZARD
| PSH_USECALLBACK
352 /*| PSH_MODELESS */ | PSH_USEICONID
;
355 p
.hwndParent
= Parent
->GetHWND ();
361 p
.hInstance
= GetInstance ();
362 p
.nPages
= PropertyPages
.size();
363 p
.pszIcon
= MAKEINTRESOURCEW(IDI_CYGWIN
);
365 p
.phpage
= PageHandles
;
366 p
.pfnCallback
= PropSheetProc
;
368 // The winmain event loop actually resides in here.
378 PropSheet::SetHWNDFromPage (HWND h
)
380 // If we're a modal dialog, there's no way for us to know our window handle unless
381 // one of our pages tells us through this function.
386 Adjust the size of a page so that it fits nicely into the window.
389 PropSheet::AdjustPageSize (HWND page
)
391 PropSheetData
& psd
= PropSheetData::Instance();
392 if (!psd
.clientRectValid
) return;
395 It's probably not obvious what's done here:
396 When this method is called the first time, the first page is already
397 created and sized, but at the coordinates (0,0). The sheet, however,
398 isn't in it's final size. My guess is that the sheet first creates the
399 page, and then resizes itself to have the right metrics to contain the
400 page and moves it to it's position. For our purposes, however, we need
401 the final metrics of the page. So, the first time this method is called,
402 we basically grab the size of the page, but calculate the top/left coords
410 RECTWrapper
& pageRect
= psd
.pageRect
;
411 ::GetWindowRect (page
, &pageRect
);
412 // We want client coords.
413 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.left
);
414 ::ScreenToClient (page
, (LPPOINT
)&pageRect
.right
);
416 LONG dialogBaseUnits
= ::GetDialogBaseUnits ();
417 // The margins in DUs are a result of "educated guesses" and T&E.
418 int marginX
= MulDiv (5, LOWORD(dialogBaseUnits
), 4);
419 int marginY
= MulDiv (5, HIWORD(dialogBaseUnits
), 8);
421 pageRect
.move (marginX
, marginY
);
424 SetWindowPos (page
, 0, psd
.pageRect
.left
, psd
.pageRect
.top
,
425 psd
.pageRect
.width (), psd
.pageRect
.height (),
426 SWP_NOACTIVATE
| SWP_NOZORDER
);
430 PropSheet::AddPage (PropertyPage
* p
)
432 // Add a page to the property sheet.
433 p
->YouAreBeingAddedToASheet (this);
434 PropertyPages
.push_back(p
);
438 PropSheet::SetActivePage (int i
)
440 // Posts a message to the message queue, so this won't block
441 return static_cast < bool > (PropSheet_SetCurSel (GetHWND (), NULL
, i
));
445 PropSheet::SetActivePageByID (int resource_id
)
447 // Posts a message to the message queue, so this won't block
448 return static_cast < bool >
449 (PropSheet_SetCurSelByID (GetHWND (), resource_id
));
453 PropSheet::SetButtons (DWORD flags
)
455 // Posts a message to the message queue, so this won't block
456 PropSheet_SetWizButtons (GetHWND (), flags
);
460 PropSheet::PressButton (int button
)
462 PropSheet_PressButton (GetHWND (), button
);