]> cygwin.com Git - cygwin-apps/setup.git/blame_incremental - propsheet.cc
Added dpiAwareness element to manifest
[cygwin-apps/setup.git] / propsheet.cc
... / ...
CommitLineData
1/*
2 * Copyright (c) 2001, 2002, 2003 Gary R. Van Sickle.
3 *
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.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Gary R. Van Sickle <g.r.vansickle@worldnet.att.net>
13 *
14 */
15
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.
20
21#include "propsheet.h"
22#include "proppage.h"
23#include "resource.h"
24#include "RECTWrapper.h"
25#include "ControlAdjuster.h"
26#include "choose.h"
27#include "msg.h"
28#include "quiet.h"
29#include "windowsx.h"
30
31// Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
32#include <pshpack1.h>
33typedef struct DLGTEMPLATEEX
34{
35 WORD dlgVer;
36 WORD signature;
37 DWORD helpID;
38 DWORD exStyle;
39 DWORD style;
40 WORD cDlgItems;
41 short x;
42 short y;
43 short cx;
44 short cy;
45}
46DLGTEMPLATEEX, *LPDLGTEMPLATEEX;
47#include <poppack.h>
48
49PropSheet::PropSheet ()
50{
51}
52
53PropSheet::~PropSheet ()
54{
55}
56
57HPROPSHEETPAGE *
58PropSheet::CreatePages ()
59{
60 HPROPSHEETPAGE *retarray;
61
62 // Create the return array
63 retarray = new HPROPSHEETPAGE[PropertyPages.size()];
64
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).
70 unsigned int i;
71 for (i = 0; i < PropertyPages.size(); i++)
72 {
73 retarray[i] =
74 CreatePropertySheetPageW (PropertyPages[i]->GetPROPSHEETPAGEPtr ());
75
76 // Set position info
77 if (i == 0)
78 {
79 PropertyPages[i]->YouAreFirst ();
80 }
81 else if (i == PropertyPages.size() - 1)
82 {
83 PropertyPages[i]->YouAreLast ();
84 }
85 else
86 {
87 PropertyPages[i]->YouAreMiddle ();
88 }
89 }
90
91 return retarray;
92}
93
94// Stuff needed by the PropSheet wndproc hook
95struct PropSheetData
96{
97 WNDPROC oldWndProc;
98 bool clientRectValid;
99 RECTWrapper lastClientRect;
100 bool gotPage;
101 RECTWrapper pageRect;
102 bool hasMinRect;
103 RECTWrapper minRect;
104
105 PropSheetData ()
106 {
107 oldWndProc = 0;
108 clientRectValid = false;
109 gotPage = false;
110 hasMinRect = false;
111 }
112
113// @@@ Ugly. Really only works because only one PS is used now.
114 static PropSheetData& Instance()
115 {
116 static PropSheetData TheInstance;
117 return TheInstance;
118 }
119};
120
121static 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
127 {0, CP_LEFT, CP_TOP}
128};
129
130static bool IsDialog (HWND hwnd)
131{
132 char className[7];
133 GetClassName (hwnd, className, sizeof (className));
134
135 return (strcmp (className, "#32770") == 0);
136}
137
138BOOL CALLBACK EnumPages (HWND hwnd, LPARAM lParam)
139{
140 // Is it really a dialog?
141 if (IsDialog (hwnd))
142 {
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);
147 }
148
149 return TRUE;
150}
151
152static void
153GetSizeGripRect(HWND hwnd, LPRECT rect)
154{
155 GetClientRect(hwnd, rect);
156 rect->left = rect->right - GetSystemMetrics(SM_CXHSCROLL);
157 rect->top = rect->bottom - GetSystemMetrics(SM_CYVSCROLL);
158}
159
160static LRESULT CALLBACK PropSheetWndProc (HWND hwnd, UINT uMsg,
161 WPARAM wParam, LPARAM lParam)
162{
163 PropSheetData& psd = PropSheetData::Instance();
164 switch (uMsg)
165 {
166 case WM_SYSCOMMAND:
167 if ((wParam & 0xfff0) == SC_CLOSE)
168 goto areyousure;
169 break;
170 case WM_COMMAND:
171 if (wParam != 2)
172 break;
173 areyousure:
174 if (unattended_mode == unattended)
175 return 0;
176 if (mbox(hwnd, IDS_CONFIRM_EXIT, MB_YESNO) == IDNO)
177 return 0;
178 break;
179 case WM_SIZE:
180 {
181 RECTWrapper clientRect;
182 GetClientRect (hwnd, &clientRect);
183
184 /*
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
188 needed then, anyway.
189 */
190 if (wParam != SIZE_MINIMIZED)
191 {
192 /*
193 The first time we get a WM_SIZE, the client rect will be all zeros.
194 */
195 if (psd.clientRectValid)
196 {
197 const int dX =
198 clientRect.width () - psd.lastClientRect.width ();
199 const int dY =
200 clientRect.height () - psd.lastClientRect.height ();
201
202 ControlAdjuster::AdjustControls (hwnd, PropSheetControlsInfo,
203 dX, dY);
204
205 psd.pageRect.right += dX;
206 psd.pageRect.bottom += dY;
207
208 /*
209 The pages are child windows, but don't have IDs.
210 So change them by enumerating all childs and adjust all
211 dialogs among them.
212 */
213 if (psd.gotPage)
214 EnumChildWindows (hwnd, &EnumPages, 0);
215 }
216 else
217 {
218 psd.clientRectValid = true;
219 }
220 /*
221 Store away the current size and use it as the minmal window size.
222 */
223 if (!psd.hasMinRect)
224 {
225 GetWindowRect (hwnd, &psd.minRect);
226 psd.hasMinRect = true;
227 }
228
229 psd.lastClientRect = clientRect;
230 }
231 }
232 break;
233 case WM_GETMINMAXINFO:
234 {
235 if (psd.hasMinRect)
236 {
237 LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
238 mmi->ptMinTrackSize.x = psd.minRect.width ();
239 mmi->ptMinTrackSize.y = psd.minRect.height ();
240 }
241 }
242 break;
243 case WM_PAINT:
244 {
245 RECT rect;
246 GetSizeGripRect(hwnd, &rect);
247
248 // draw size grip
249 HDC dc = GetDC(hwnd);
250 DrawFrameControl(dc, &rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
251 ReleaseDC(hwnd, dc);
252 }
253 break;
254 case WM_NCHITTEST:
255 {
256 RECT rect;
257 GetSizeGripRect(hwnd, &rect);
258 MapWindowPoints(hwnd, NULL, reinterpret_cast<POINT *>(&rect), 2);
259
260 // when over the size grip, pretend to be over bottom-right corner of
261 // the sizing border
262 POINT point = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
263 if (PtInRect(&rect, point))
264 return HTBOTTOMRIGHT;
265
266 // otherwise, fall through...
267 }
268 break;
269 case WM_SIZING:
270 {
271 // invalidate the area containing the size grip when the window is
272 // resized, so it's redrawn
273 RECT rect;
274 GetSizeGripRect(hwnd, &rect);
275 InvalidateRect(hwnd, &rect, FALSE);
276 }
277 break;
278 }
279
280 return CallWindowProc (psd.oldWndProc,
281 hwnd, uMsg, wParam, lParam);
282}
283
284static int CALLBACK
285PropSheetProc (HWND hwndDlg, UINT uMsg, LPARAM lParam)
286{
287 switch (uMsg)
288 {
289 case PSCB_PRECREATE:
290 {
291 LONG additionalStyle = (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
292
293 // 'noinput' quiet mode adds WS_DISABLE to prevent user interaction
294 // with the propsheet
295 if (UnattendedOption == QuietNoInput)
296 additionalStyle |= WS_DISABLED;
297
298 // 'hidden' quiet mode totally hides the propsheet window
299 LONG forbiddenStyle = 0;
300 if (UnattendedOption == QuietHidden)
301 forbiddenStyle |= WS_VISIBLE;
302
303 // Add a minimize box to the sheet/wizard.
304 if (((LPDLGTEMPLATEEX) lParam)->signature == 0xFFFF)
305 {
306 ((LPDLGTEMPLATEEX) lParam)->style |= additionalStyle;
307 ((LPDLGTEMPLATEEX) lParam)->style &= ~forbiddenStyle;
308 }
309 else
310 {
311 ((LPDLGTEMPLATE) lParam)->style |= additionalStyle;
312 ((LPDLGTEMPLATE) lParam)->style &= ~forbiddenStyle;
313 }
314 }
315 return TRUE;
316 case PSCB_INITIALIZED:
317 {
318 /*
319 PropSheet() with PSH_USEICONID only sets the small icon,
320 so we must set the big icon ourselves
321 */
322 SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_CYGWIN)));
323 /*
324 Hook into the window proc.
325 We need to catch some messages for resizing.
326 */
327 PropSheetData::Instance().oldWndProc = (WNDPROC)GetWindowLongPtrW (hwndDlg, GWLP_WNDPROC);
328 SetWindowLongPtrW (hwndDlg, GWLP_WNDPROC, (LONG_PTR)&PropSheetWndProc);
329 /*
330 Store the PropSheet HWND in the Chooser, for use in resizing
331
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()
334 anyhow... )
335 */
336 ChooserPage::SetHwndDialog (hwndDlg);
337 }
338 return TRUE;
339 }
340 return TRUE;
341}
342
343bool
344PropSheet::Create (const Window * Parent, DWORD Style)
345{
346 PROPSHEETHEADERW p;
347
348 PageHandles = CreatePages ();
349
350 p.dwSize = sizeof (PROPSHEETHEADERW);
351 p.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK
352 /*| PSH_MODELESS */ | PSH_USEICONID;
353 if (Parent != NULL)
354 {
355 p.hwndParent = Parent->GetHWND ();
356 }
357 else
358 {
359 p.hwndParent = NULL;
360 }
361 p.hInstance = GetInstance ();
362 p.nPages = PropertyPages.size();
363 p.pszIcon = MAKEINTRESOURCEW(IDI_CYGWIN);
364 p.nStartPage = 0;
365 p.phpage = PageHandles;
366 p.pfnCallback = PropSheetProc;
367
368 // The winmain event loop actually resides in here.
369 PropertySheetW (&p);
370
371 SetHWND (NULL);
372
373
374 return true;
375}
376
377void
378PropSheet::SetHWNDFromPage (HWND h)
379{
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.
382 SetHWND (h);
383}
384
385/*
386 Adjust the size of a page so that it fits nicely into the window.
387 */
388void
389PropSheet::AdjustPageSize (HWND page)
390{
391 PropSheetData& psd = PropSheetData::Instance();
392 if (!psd.clientRectValid) return;
393
394 /*
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
403 ourselves.
404 */
405
406 if (!psd.gotPage)
407 {
408 psd.gotPage = true;
409
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);
415
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);
420
421 pageRect.move (marginX, marginY);
422 }
423
424 SetWindowPos (page, 0, psd.pageRect.left, psd.pageRect.top,
425 psd.pageRect.width (), psd.pageRect.height (),
426 SWP_NOACTIVATE | SWP_NOZORDER);
427}
428
429void
430PropSheet::AddPage (PropertyPage * p)
431{
432 // Add a page to the property sheet.
433 p->YouAreBeingAddedToASheet (this);
434 PropertyPages.push_back(p);
435}
436
437bool
438PropSheet::SetActivePage (int i)
439{
440 // Posts a message to the message queue, so this won't block
441 return static_cast < bool > (PropSheet_SetCurSel (GetHWND (), NULL, i));
442}
443
444bool
445PropSheet::SetActivePageByID (int resource_id)
446{
447 // Posts a message to the message queue, so this won't block
448 return static_cast < bool >
449 (PropSheet_SetCurSelByID (GetHWND (), resource_id));
450}
451
452void
453PropSheet::SetButtons (DWORD flags)
454{
455 // Posts a message to the message queue, so this won't block
456 PropSheet_SetWizButtons (GetHWND (), flags);
457}
458
459void
460PropSheet::PressButton (int button)
461{
462 PropSheet_PressButton (GetHWND (), button);
463}
This page took 0.026927 seconds and 6 git commands to generate.