]> cygwin.com Git - cygwin-apps/setup.git/blame - propsheet.cc
Added dpiAwareness element to manifest
[cygwin-apps/setup.git] / propsheet.cc
CommitLineData
df62e023 1/*
56a7c49e 2 * Copyright (c) 2001, 2002, 2003 Gary R. Van Sickle.
df62e023
RC
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"
62489576 23#include "resource.h"
ee91d9be
RC
24#include "RECTWrapper.h"
25#include "ControlAdjuster.h"
00fa5f6c 26#include "choose.h"
17d64747 27#include "msg.h"
cfc01ee7 28#include "quiet.h"
668cc4d7 29#include "windowsx.h"
df62e023 30
b7301c43
RC
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>
df62e023
RC
48
49PropSheet::PropSheet ()
50{
df62e023
RC
51}
52
53PropSheet::~PropSheet ()
54{
55}
56
57HPROPSHEETPAGE *
58PropSheet::CreatePages ()
59{
60 HPROPSHEETPAGE *retarray;
61
62 // Create the return array
56a7c49e 63 retarray = new HPROPSHEETPAGE[PropertyPages.size()];
df62e023
RC
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).
56a7c49e
MB
70 unsigned int i;
71 for (i = 0; i < PropertyPages.size(); i++)
df62e023
RC
72 {
73 retarray[i] =
b567162a 74 CreatePropertySheetPageW (PropertyPages[i]->GetPROPSHEETPAGEPtr ());
df62e023
RC
75
76 // Set position info
77 if (i == 0)
78 {
79 PropertyPages[i]->YouAreFirst ();
80 }
56a7c49e 81 else if (i == PropertyPages.size() - 1)
df62e023
RC
82 {
83 PropertyPages[i]->YouAreLast ();
84 }
85 else
86 {
87 PropertyPages[i]->YouAreMiddle ();
88 }
89 }
90
91 return retarray;
92}
93
ee91d9be
RC
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;
4b6e5406
RC
102 bool hasMinRect;
103 RECTWrapper minRect;
30718d6f 104
ee91d9be
RC
105 PropSheetData ()
106 {
107 oldWndProc = 0;
108 clientRectValid = false;
109 gotPage = false;
4b6e5406 110 hasMinRect = false;
ee91d9be 111 }
30718d6f 112
ee91d9be
RC
113// @@@ Ugly. Really only works because only one PS is used now.
114 static PropSheetData& Instance()
115 {
116 static PropSheetData TheInstance;
30718d6f 117 return TheInstance;
ee91d9be
RC
118 }
119};
120
121static ControlAdjuster::ControlInfo PropSheetControlsInfo[] = {
a8d753b6
RC
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}
ee91d9be
RC
128};
129
130static bool IsDialog (HWND hwnd)
131{
132 char className[7];
133 GetClassName (hwnd, className, sizeof (className));
30718d6f 134
ee91d9be
RC
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();
30718d6f
CF
144 SetWindowPos (hwnd, 0, psd.pageRect.left, psd.pageRect.top,
145 psd.pageRect.width (), psd.pageRect.height (),
ee91d9be
RC
146 SWP_NOACTIVATE | SWP_NOZORDER);
147 }
148
149 return TRUE;
150}
151
668cc4d7
JT
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
30718d6f 160static LRESULT CALLBACK PropSheetWndProc (HWND hwnd, UINT uMsg,
ee91d9be
RC
161 WPARAM wParam, LPARAM lParam)
162{
163 PropSheetData& psd = PropSheetData::Instance();
164 switch (uMsg)
165 {
c8356810 166 case WM_SYSCOMMAND:
f2558685
CF
167 if ((wParam & 0xfff0) == SC_CLOSE)
168 goto areyousure;
169 break;
c8356810 170 case WM_COMMAND:
f2558685 171 if (wParam != 2)
c8356810 172 break;
f2558685 173 areyousure:
85789118
JT
174 if (unattended_mode == unattended)
175 return 0;
17d64747 176 if (mbox(hwnd, IDS_CONFIRM_EXIT, MB_YESNO) == IDNO)
f2558685
CF
177 return 0;
178 break;
ee91d9be
RC
179 case WM_SIZE:
180 {
30718d6f
CF
181 RECTWrapper clientRect;
182 GetClientRect (hwnd, &clientRect);
183
ee91d9be 184 /*
30718d6f
CF
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
4b6e5406
RC
187 around by not adjusting child upon minimization - it isn't really
188 needed then, anyway.
ee91d9be 189 */
4b6e5406 190 if (wParam != SIZE_MINIMIZED)
ee91d9be 191 {
ee91d9be 192 /*
4b6e5406
RC
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 ();
30718d6f
CF
201
202 ControlAdjuster::AdjustControls (hwnd, PropSheetControlsInfo,
4b6e5406 203 dX, dY);
30718d6f 204
4b6e5406
RC
205 psd.pageRect.right += dX;
206 psd.pageRect.bottom += dY;
30718d6f 207
4b6e5406
RC
208 /*
209 The pages are child windows, but don't have IDs.
30718d6f 210 So change them by enumerating all childs and adjust all
4b6e5406
RC
211 dialogs among them.
212 */
213 if (psd.gotPage)
30718d6f 214 EnumChildWindows (hwnd, &EnumPages, 0);
4b6e5406
RC
215 }
216 else
217 {
218 psd.clientRectValid = true;
219 }
220 /*
221 Store away the current size and use it as the minmal window size.
ee91d9be 222 */
4b6e5406
RC
223 if (!psd.hasMinRect)
224 {
225 GetWindowRect (hwnd, &psd.minRect);
6bfd5236 226 psd.hasMinRect = true;
4b6e5406 227 }
30718d6f 228
4b6e5406 229 psd.lastClientRect = clientRect;
ee91d9be 230 }
4b6e5406
RC
231 }
232 break;
233 case WM_GETMINMAXINFO:
234 {
235 if (psd.hasMinRect)
ee91d9be 236 {
4b6e5406
RC
237 LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
238 mmi->ptMinTrackSize.x = psd.minRect.width ();
239 mmi->ptMinTrackSize.y = psd.minRect.height ();
ee91d9be 240 }
ee91d9be
RC
241 }
242 break;
668cc4d7
JT
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;
ee91d9be 278 }
30718d6f
CF
279
280 return CallWindowProc (psd.oldWndProc,
ee91d9be
RC
281 hwnd, uMsg, wParam, lParam);
282}
283
b7301c43
RC
284static int CALLBACK
285PropSheetProc (HWND hwndDlg, UINT uMsg, LPARAM lParam)
286{
287 switch (uMsg)
288 {
289 case PSCB_PRECREATE:
290 {
cfc01ee7
JT
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
b7301c43
RC
303 // Add a minimize box to the sheet/wizard.
304 if (((LPDLGTEMPLATEEX) lParam)->signature == 0xFFFF)
305 {
ee91d9be 306 ((LPDLGTEMPLATEEX) lParam)->style |= additionalStyle;
cfc01ee7 307 ((LPDLGTEMPLATEEX) lParam)->style &= ~forbiddenStyle;
b7301c43
RC
308 }
309 else
310 {
ee91d9be 311 ((LPDLGTEMPLATE) lParam)->style |= additionalStyle;
cfc01ee7 312 ((LPDLGTEMPLATE) lParam)->style &= ~forbiddenStyle;
b7301c43
RC
313 }
314 }
315 return TRUE;
ee91d9be
RC
316 case PSCB_INITIALIZED:
317 {
d1b5a517
JT
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)));
30718d6f 323 /*
ee91d9be
RC
324 Hook into the window proc.
325 We need to catch some messages for resizing.
326 */
75bfcddb
JT
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 */
00fa5f6c 336 ChooserPage::SetHwndDialog (hwndDlg);
ee91d9be
RC
337 }
338 return TRUE;
b7301c43
RC
339 }
340 return TRUE;
341}
342
b7301c43
RC
343bool
344PropSheet::Create (const Window * Parent, DWORD Style)
df62e023 345{
b567162a 346 PROPSHEETHEADERW p;
df62e023
RC
347
348 PageHandles = CreatePages ();
349
b567162a 350 p.dwSize = sizeof (PROPSHEETHEADERW);
62489576
MB
351 p.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK
352 /*| PSH_MODELESS */ | PSH_USEICONID;
df62e023
RC
353 if (Parent != NULL)
354 {
355 p.hwndParent = Parent->GetHWND ();
356 }
357 else
358 {
359 p.hwndParent = NULL;
360 }
361 p.hInstance = GetInstance ();
56a7c49e 362 p.nPages = PropertyPages.size();
b567162a 363 p.pszIcon = MAKEINTRESOURCEW(IDI_CYGWIN);
df62e023
RC
364 p.nStartPage = 0;
365 p.phpage = PageHandles;
b7301c43 366 p.pfnCallback = PropSheetProc;
df62e023 367
58ee6135 368 // The winmain event loop actually resides in here.
b567162a 369 PropertySheetW (&p);
df62e023 370
df62e023
RC
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
ee91d9be
RC
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
30718d6f 399 page, and then resizes itself to have the right metrics to contain the
ee91d9be 400 page and moves it to it's position. For our purposes, however, we need
4b6e5406 401 the final metrics of the page. So, the first time this method is called,
ee91d9be
RC
402 we basically grab the size of the page, but calculate the top/left coords
403 ourselves.
404 */
30718d6f 405
ee91d9be
RC
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
30718d6f
CF
424 SetWindowPos (page, 0, psd.pageRect.left, psd.pageRect.top,
425 psd.pageRect.width (), psd.pageRect.height (),
ee91d9be
RC
426 SWP_NOACTIVATE | SWP_NOZORDER);
427}
428
df62e023
RC
429void
430PropSheet::AddPage (PropertyPage * p)
431{
432 // Add a page to the property sheet.
433 p->YouAreBeingAddedToASheet (this);
56a7c49e 434 PropertyPages.push_back(p);
df62e023
RC
435}
436
b7301c43
RC
437bool
438PropSheet::SetActivePage (int i)
df62e023
RC
439{
440 // Posts a message to the message queue, so this won't block
db09d718 441 return static_cast < bool > (PropSheet_SetCurSel (GetHWND (), NULL, i));
df62e023
RC
442}
443
b7301c43
RC
444bool
445PropSheet::SetActivePageByID (int resource_id)
df62e023
RC
446{
447 // Posts a message to the message queue, so this won't block
448 return static_cast < bool >
db09d718 449 (PropSheet_SetCurSelByID (GetHWND (), resource_id));
df62e023
RC
450}
451
452void
453PropSheet::SetButtons (DWORD flags)
454{
455 // Posts a message to the message queue, so this won't block
db09d718 456 PropSheet_SetWizButtons (GetHWND (), flags);
df62e023
RC
457}
458
459void
460PropSheet::PressButton (int button)
461{
db09d718 462 PropSheet_PressButton (GetHWND (), button);
df62e023 463}
This page took 0.203545 seconds and 6 git commands to generate.