]> cygwin.com Git - cygwin-apps/setup.git/blob - propsheet.cc
* prereq.cc (PrereqChecker::getUnmetString): Improve dependency list
[cygwin-apps/setup.git] / propsheet.cc
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
28 //#include <shlwapi.h>
29 // ...but since there is no shlwapi.h in mingw yet:
30 typedef struct _DllVersionInfo
31 {
32 DWORD cbSize;
33 DWORD dwMajorVersion;
34 DWORD dwMinorVersion;
35 DWORD dwBuildNumber;
36 DWORD dwPlatformID;
37 }
38 DLLVERSIONINFO;
39 typedef HRESULT CALLBACK (*DLLGETVERSIONPROC) (DLLVERSIONINFO * pdvi);
40 #define PROPSHEETHEADER_V1_SIZE 40
41
42 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
43 #include <pshpack1.h>
44 typedef struct DLGTEMPLATEEX
45 {
46 WORD dlgVer;
47 WORD signature;
48 DWORD helpID;
49 DWORD exStyle;
50 DWORD style;
51 WORD cDlgItems;
52 short x;
53 short y;
54 short cx;
55 short cy;
56 }
57 DLGTEMPLATEEX, *LPDLGTEMPLATEEX;
58 #include <poppack.h>
59
60 PropSheet::PropSheet ()
61 {
62 }
63
64 PropSheet::~PropSheet ()
65 {
66 }
67
68 HPROPSHEETPAGE *
69 PropSheet::CreatePages ()
70 {
71 HPROPSHEETPAGE *retarray;
72
73 // Create the return array
74 retarray = new HPROPSHEETPAGE[PropertyPages.size()];
75
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).
81 unsigned int i;
82 for (i = 0; i < PropertyPages.size(); i++)
83 {
84 retarray[i] =
85 CreatePropertySheetPage (PropertyPages[i]->GetPROPSHEETPAGEPtr ());
86
87 // Set position info
88 if (i == 0)
89 {
90 PropertyPages[i]->YouAreFirst ();
91 }
92 else if (i == PropertyPages.size() - 1)
93 {
94 PropertyPages[i]->YouAreLast ();
95 }
96 else
97 {
98 PropertyPages[i]->YouAreMiddle ();
99 }
100 }
101
102 return retarray;
103 }
104
105 // Stuff needed by the PropSheet wndproc hook
106 struct PropSheetData
107 {
108 WNDPROC oldWndProc;
109 bool clientRectValid;
110 RECTWrapper lastClientRect;
111 bool gotPage;
112 RECTWrapper pageRect;
113 bool hasMinRect;
114 RECTWrapper minRect;
115
116 PropSheetData ()
117 {
118 oldWndProc = 0;
119 clientRectValid = false;
120 gotPage = false;
121 hasMinRect = false;
122 }
123
124 // @@@ Ugly. Really only works because only one PS is used now.
125 static PropSheetData& Instance()
126 {
127 static PropSheetData TheInstance;
128 return TheInstance;
129 }
130 };
131
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
138 {0, CP_LEFT, CP_TOP}
139 };
140
141 static bool IsDialog (HWND hwnd)
142 {
143 char className[7];
144 GetClassName (hwnd, className, sizeof (className));
145
146 return (strcmp (className, "#32770") == 0);
147 }
148
149 BOOL CALLBACK EnumPages (HWND hwnd, LPARAM lParam)
150 {
151 // Is it really a dialog?
152 if (IsDialog (hwnd))
153 {
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);
158 }
159
160 return TRUE;
161 }
162
163 static LRESULT CALLBACK PropSheetWndProc (HWND hwnd, UINT uMsg,
164 WPARAM wParam, LPARAM lParam)
165 {
166 PropSheetData& psd = PropSheetData::Instance();
167 switch (uMsg)
168 {
169 case WM_SYSCOMMAND:
170 if ((wParam & 0xfff0) == SC_CLOSE)
171 goto areyousure;
172 break;
173 case WM_COMMAND:
174 if (wParam != 2)
175 break;
176 areyousure:
177 if (MessageBox(hwnd,
178 "Are you sure you want to exit setup? Any current download or installation will be aborted.",
179 "Exit Cygwin Setup?", MB_YESNO) == IDNO)
180 return 0;
181 break;
182 case WM_SIZE:
183 {
184 RECTWrapper clientRect;
185 GetClientRect (hwnd, &clientRect);
186
187 /*
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
191 needed then, anyway.
192 */
193 if (wParam != SIZE_MINIMIZED)
194 {
195 /*
196 The first time we get a WM_SIZE, the client rect will be all zeros.
197 */
198 if (psd.clientRectValid)
199 {
200 const int dX =
201 clientRect.width () - psd.lastClientRect.width ();
202 const int dY =
203 clientRect.height () - psd.lastClientRect.height ();
204
205 ControlAdjuster::AdjustControls (hwnd, PropSheetControlsInfo,
206 dX, dY);
207
208 psd.pageRect.right += dX;
209 psd.pageRect.bottom += dY;
210
211 /*
212 The pages are child windows, but don't have IDs.
213 So change them by enumerating all childs and adjust all
214 dialogs among them.
215 */
216 if (psd.gotPage)
217 EnumChildWindows (hwnd, &EnumPages, 0);
218 }
219 else
220 {
221 psd.clientRectValid = true;
222 }
223 /*
224 Store away the current size and use it as the minmal window size.
225 */
226 if (!psd.hasMinRect)
227 {
228 GetWindowRect (hwnd, &psd.minRect);
229 psd.hasMinRect = true;
230 }
231
232 psd.lastClientRect = clientRect;
233 }
234 }
235 break;
236 case WM_GETMINMAXINFO:
237 {
238 if (psd.hasMinRect)
239 {
240 LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
241 mmi->ptMinTrackSize.x = psd.minRect.width ();
242 mmi->ptMinTrackSize.y = psd.minRect.height ();
243 }
244 }
245 break;
246 }
247
248 return CallWindowProc (psd.oldWndProc,
249 hwnd, uMsg, wParam, lParam);
250 }
251
252 static int CALLBACK
253 PropSheetProc (HWND hwndDlg, UINT uMsg, LPARAM lParam)
254 {
255 switch (uMsg)
256 {
257 case PSCB_PRECREATE:
258 {
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)
263 {
264 ((LPDLGTEMPLATEEX) lParam)->style |= additionalStyle;
265 }
266 else
267 {
268 ((LPDLGTEMPLATE) lParam)->style |= additionalStyle;
269 }
270 }
271 return TRUE;
272 case PSCB_INITIALIZED:
273 {
274 /*
275 Hook into the window proc.
276 We need to catch some messages for resizing.
277 */
278 PropSheetData::Instance().oldWndProc =
279 (WNDPROC)GetWindowLongPtr (hwndDlg, GWLP_WNDPROC);
280 SetWindowLongPtr (hwndDlg, GWLP_WNDPROC,
281 (LONG_PTR)&PropSheetWndProc);
282 ChooserPage::SetHwndDialog (hwndDlg);
283 }
284 return TRUE;
285 }
286 return TRUE;
287 }
288
289 static DWORD
290 GetPROPSHEETHEADERSize ()
291 {
292 // For compatibility with all versions of comctl32.dll, we have to do this.
293
294 DLLVERSIONINFO vi;
295 HMODULE mod;
296 DLLGETVERSIONPROC DllGetVersion;
297 DWORD retval = 0;
298
299
300 // This 'isn't safe' in a DLL, according to MSDN
301 mod = LoadLibrary ("comctl32.dll");
302
303 DllGetVersion = (DLLGETVERSIONPROC) GetProcAddress (mod, "DllGetVersion");
304 if (DllGetVersion == NULL)
305 {
306 // Something's wildly broken, punt.
307 retval = PROPSHEETHEADER_V1_SIZE;
308 }
309 else
310 {
311 vi.cbSize = sizeof (DLLVERSIONINFO);
312 DllGetVersion (&vi);
313
314 if ((vi.dwMajorVersion < 4) ||
315 ((vi.dwMajorVersion == 4) && (vi.dwMinorVersion < 71)))
316 {
317 // Recent.
318 retval = sizeof (PROPSHEETHEADER);
319 }
320 else
321 {
322 // Old (== Win95/NT4 w/o IE 4 or better)
323 retval = PROPSHEETHEADER_V1_SIZE;
324 }
325 }
326
327 FreeLibrary (mod);
328
329 return retval;
330 }
331
332 bool
333 PropSheet::Create (const Window * Parent, DWORD Style)
334 {
335 PROPSHEETHEADER p;
336
337 PageHandles = CreatePages ();
338
339 p.dwSize = GetPROPSHEETHEADERSize ();
340 p.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK
341 /*| PSH_MODELESS */ | PSH_USEICONID;
342 if (Parent != NULL)
343 {
344 p.hwndParent = Parent->GetHWND ();
345 }
346 else
347 {
348 p.hwndParent = NULL;
349 }
350 p.hInstance = GetInstance ();
351 p.nPages = PropertyPages.size();
352 p.pszIcon = MAKEINTRESOURCE(IDI_CYGWIN);
353 p.nStartPage = 0;
354 p.phpage = PageHandles;
355 p.pfnCallback = PropSheetProc;
356
357
358 // The winmain event loop actually resides in here.
359 PropertySheet (&p);
360
361 // Do a modeless property sheet...
362 //SetHWND((HWND)PropertySheet(&p));
363 /*Show(SW_SHOWNORMAL);
364
365 // ...but pretend it's modal
366 MessageLoop();
367 MessageBox(NULL, "DONE", NULL, MB_OK);
368
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);
373 */
374 SetHWND (NULL);
375
376
377 return true;
378 }
379
380 void
381 PropSheet::SetHWNDFromPage (HWND h)
382 {
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.
385 SetHWND (h);
386 }
387
388 /*
389 Adjust the size of a page so that it fits nicely into the window.
390 */
391 void
392 PropSheet::AdjustPageSize (HWND page)
393 {
394 PropSheetData& psd = PropSheetData::Instance();
395 if (!psd.clientRectValid) return;
396
397 /*
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
406 ourselves.
407 */
408
409 if (!psd.gotPage)
410 {
411 psd.gotPage = true;
412
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);
418
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);
423
424 pageRect.move (marginX, marginY);
425 }
426
427 SetWindowPos (page, 0, psd.pageRect.left, psd.pageRect.top,
428 psd.pageRect.width (), psd.pageRect.height (),
429 SWP_NOACTIVATE | SWP_NOZORDER);
430 }
431
432 void
433 PropSheet::AddPage (PropertyPage * p)
434 {
435 // Add a page to the property sheet.
436 p->YouAreBeingAddedToASheet (this);
437 PropertyPages.push_back(p);
438 }
439
440 bool
441 PropSheet::SetActivePage (int i)
442 {
443 // Posts a message to the message queue, so this won't block
444 return static_cast < bool > (::PropSheet_SetCurSel (GetHWND (), NULL, i));
445 }
446
447 bool
448 PropSheet::SetActivePageByID (int resource_id)
449 {
450 // Posts a message to the message queue, so this won't block
451 return static_cast < bool >
452 (::PropSheet_SetCurSelByID (GetHWND (), resource_id));
453 }
454
455 void
456 PropSheet::SetButtons (DWORD flags)
457 {
458 // Posts a message to the message queue, so this won't block
459 ::PropSheet_SetWizButtons (GetHWND (), flags);
460 }
461
462 void
463 PropSheet::PressButton (int button)
464 {
465 ::PropSheet_PressButton (GetHWND (), button);
466 }
This page took 0.05598 seconds and 5 git commands to generate.