]> cygwin.com Git - cygwin-apps/setup.git/blob - propsheet.cc
* CHANGES: Update.
[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
27 //#include <shlwapi.h>
28 // ...but since there is no shlwapi.h in mingw yet:
29 typedef struct _DllVersionInfo
30 {
31 DWORD cbSize;
32 DWORD dwMajorVersion;
33 DWORD dwMinorVersion;
34 DWORD dwBuildNumber;
35 DWORD dwPlatformID;
36 }
37 DLLVERSIONINFO;
38 typedef HRESULT CALLBACK (*DLLGETVERSIONPROC) (DLLVERSIONINFO * pdvi);
39 #define PROPSHEETHEADER_V1_SIZE 40
40
41 // Sort of a "hidden" Windows structure. Used in the PropSheetCallback.
42 #include <pshpack1.h>
43 typedef struct DLGTEMPLATEEX
44 {
45 WORD dlgVer;
46 WORD signature;
47 DWORD helpID;
48 DWORD exStyle;
49 DWORD style;
50 WORD cDlgItems;
51 short x;
52 short y;
53 short cx;
54 short cy;
55 }
56 DLGTEMPLATEEX, *LPDLGTEMPLATEEX;
57 #include <poppack.h>
58
59 PropSheet::PropSheet ()
60 {
61 }
62
63 PropSheet::~PropSheet ()
64 {
65 }
66
67 HPROPSHEETPAGE *
68 PropSheet::CreatePages ()
69 {
70 HPROPSHEETPAGE *retarray;
71
72 // Create the return array
73 retarray = new HPROPSHEETPAGE[PropertyPages.size()];
74
75 // Create the pages with CreatePropertySheetPage().
76 // We do it here rather than in the PropertyPages themselves
77 // because, for reasons known only to Microsoft, these handles will be
78 // destroyed by the property sheet before the PropertySheet() call returns,
79 // at least if it's modal (don't know about modeless).
80 unsigned int i;
81 for (i = 0; i < PropertyPages.size(); i++)
82 {
83 retarray[i] =
84 CreatePropertySheetPage (PropertyPages[i]->GetPROPSHEETPAGEPtr ());
85
86 // Set position info
87 if (i == 0)
88 {
89 PropertyPages[i]->YouAreFirst ();
90 }
91 else if (i == PropertyPages.size() - 1)
92 {
93 PropertyPages[i]->YouAreLast ();
94 }
95 else
96 {
97 PropertyPages[i]->YouAreMiddle ();
98 }
99 }
100
101 return retarray;
102 }
103
104 // Stuff needed by the PropSheet wndproc hook
105 struct PropSheetData
106 {
107 WNDPROC oldWndProc;
108 bool clientRectValid;
109 RECTWrapper lastClientRect;
110 bool gotPage;
111 RECTWrapper pageRect;
112 bool hasMinRect;
113 RECTWrapper minRect;
114
115 PropSheetData ()
116 {
117 oldWndProc = 0;
118 clientRectValid = false;
119 gotPage = false;
120 hasMinRect = false;
121 }
122
123 // @@@ Ugly. Really only works because only one PS is used now.
124 static PropSheetData& Instance()
125 {
126 static PropSheetData TheInstance;
127 return TheInstance;
128 }
129 };
130
131 static ControlAdjuster::ControlInfo PropSheetControlsInfo[] = {
132 {0x3023, CP_RIGHT, CP_BOTTOM}, // Back
133 {0x3024, CP_RIGHT, CP_BOTTOM}, // Next
134 {0x3025, CP_RIGHT, CP_BOTTOM}, // Finish
135 {0x3026, CP_STRETCH, CP_BOTTOM}, // Line above buttons
136 { 2, CP_RIGHT, CP_BOTTOM}, // Cancel
137 {0, CP_LEFT, CP_TOP}
138 };
139
140 static bool IsDialog (HWND hwnd)
141 {
142 char className[7];
143 GetClassName (hwnd, className, sizeof (className));
144
145 return (strcmp (className, "#32770") == 0);
146 }
147
148 BOOL CALLBACK EnumPages (HWND hwnd, LPARAM lParam)
149 {
150 // Is it really a dialog?
151 if (IsDialog (hwnd))
152 {
153 PropSheetData& psd = PropSheetData::Instance();
154 SetWindowPos (hwnd, 0, psd.pageRect.left, psd.pageRect.top,
155 psd.pageRect.width (), psd.pageRect.height (),
156 SWP_NOACTIVATE | SWP_NOZORDER);
157 }
158
159 return TRUE;
160 }
161
162 static LRESULT CALLBACK PropSheetWndProc (HWND hwnd, UINT uMsg,
163 WPARAM wParam, LPARAM lParam)
164 {
165 PropSheetData& psd = PropSheetData::Instance();
166 switch (uMsg)
167 {
168 case WM_SIZE:
169 {
170 RECTWrapper clientRect;
171 GetClientRect (hwnd, &clientRect);
172
173 /*
174 When the window is minimized, the client rect is reduced to
175 (0,0-0,0), which causes child adjusting to screw slightly up. Work
176 around by not adjusting child upon minimization - it isn't really
177 needed then, anyway.
178 */
179 if (wParam != SIZE_MINIMIZED)
180 {
181 /*
182 The first time we get a WM_SIZE, the client rect will be all zeros.
183 */
184 if (psd.clientRectValid)
185 {
186 const int dX =
187 clientRect.width () - psd.lastClientRect.width ();
188 const int dY =
189 clientRect.height () - psd.lastClientRect.height ();
190
191 ControlAdjuster::AdjustControls (hwnd, PropSheetControlsInfo,
192 dX, dY);
193
194 psd.pageRect.right += dX;
195 psd.pageRect.bottom += dY;
196
197 /*
198 The pages are child windows, but don't have IDs.
199 So change them by enumerating all childs and adjust all
200 dialogs among them.
201 */
202 if (psd.gotPage)
203 EnumChildWindows (hwnd, &EnumPages, 0);
204 }
205 else
206 {
207 psd.clientRectValid = true;
208 }
209 /*
210 Store away the current size and use it as the minmal window size.
211 */
212 if (!psd.hasMinRect)
213 {
214 GetWindowRect (hwnd, &psd.minRect);
215 psd.hasMinRect = true;
216 }
217
218 psd.lastClientRect = clientRect;
219 }
220 }
221 break;
222 case WM_GETMINMAXINFO:
223 {
224 if (psd.hasMinRect)
225 {
226 LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
227 mmi->ptMinTrackSize.x = psd.minRect.width ();
228 mmi->ptMinTrackSize.y = psd.minRect.height ();
229 }
230 }
231 break;
232 }
233
234 return CallWindowProc (psd.oldWndProc,
235 hwnd, uMsg, wParam, lParam);
236 }
237
238 static int CALLBACK
239 PropSheetProc (HWND hwndDlg, UINT uMsg, LPARAM lParam)
240 {
241 switch (uMsg)
242 {
243 case PSCB_PRECREATE:
244 {
245 const LONG additionalStyle =
246 (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
247 // Add a minimize box to the sheet/wizard.
248 if (((LPDLGTEMPLATEEX) lParam)->signature == 0xFFFF)
249 {
250 ((LPDLGTEMPLATEEX) lParam)->style |= additionalStyle;
251 }
252 else
253 {
254 ((LPDLGTEMPLATE) lParam)->style |= additionalStyle;
255 }
256 }
257 return TRUE;
258 case PSCB_INITIALIZED:
259 {
260 /*
261 Hook into the window proc.
262 We need to catch some messages for resizing.
263 */
264 PropSheetData::Instance().oldWndProc =
265 (WNDPROC)GetWindowLongPtr (hwndDlg, GWLP_WNDPROC);
266 SetWindowLongPtr (hwndDlg, GWLP_WNDPROC,
267 (LONG_PTR)&PropSheetWndProc);
268 }
269 return TRUE;
270 }
271 return TRUE;
272 }
273
274 static DWORD
275 GetPROPSHEETHEADERSize ()
276 {
277 // For compatibility with all versions of comctl32.dll, we have to do this.
278
279 DLLVERSIONINFO vi;
280 HMODULE mod;
281 DLLGETVERSIONPROC DllGetVersion;
282 DWORD retval = 0;
283
284
285 // This 'isn't safe' in a DLL, according to MSDN
286 mod = LoadLibrary ("comctl32.dll");
287
288 DllGetVersion = (DLLGETVERSIONPROC) GetProcAddress (mod, "DllGetVersion");
289 if (DllGetVersion == NULL)
290 {
291 // Something's wildly broken, punt.
292 retval = PROPSHEETHEADER_V1_SIZE;
293 }
294 else
295 {
296 vi.cbSize = sizeof (DLLVERSIONINFO);
297 DllGetVersion (&vi);
298
299 if ((vi.dwMajorVersion < 4) ||
300 ((vi.dwMajorVersion == 4) && (vi.dwMinorVersion < 71)))
301 {
302 // Recent.
303 retval = sizeof (PROPSHEETHEADER);
304 }
305 else
306 {
307 // Old (== Win95/NT4 w/o IE 4 or better)
308 retval = PROPSHEETHEADER_V1_SIZE;
309 }
310 }
311
312 FreeLibrary (mod);
313
314 return retval;
315 }
316
317 bool
318 PropSheet::Create (const Window * Parent, DWORD Style)
319 {
320 PROPSHEETHEADER p;
321
322 PageHandles = CreatePages ();
323
324 p.dwSize = GetPROPSHEETHEADERSize ();
325 p.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK
326 /*| PSH_MODELESS */ | PSH_USEICONID;
327 if (Parent != NULL)
328 {
329 p.hwndParent = Parent->GetHWND ();
330 }
331 else
332 {
333 p.hwndParent = NULL;
334 }
335 p.hInstance = GetInstance ();
336 p.nPages = PropertyPages.size();
337 p.pszIcon = MAKEINTRESOURCE(IDI_CYGWIN);
338 p.nStartPage = 0;
339 p.phpage = PageHandles;
340 p.pfnCallback = PropSheetProc;
341
342
343 // The winmain event loop actually resides in here.
344 PropertySheet (&p);
345
346 // Do a modeless property sheet...
347 //SetHWND((HWND)PropertySheet(&p));
348 /*Show(SW_SHOWNORMAL);
349
350 // ...but pretend it's modal
351 MessageLoop();
352 MessageBox(NULL, "DONE", NULL, MB_OK);
353
354 // FIXME: Enable the parent before destroying this window to prevent another window
355 // from becoming the foreground window
356 // ala: EnableWindow(<parent_hwnd>, TRUE);
357 //DestroyWindow(WindowHandle);
358 */
359 SetHWND (NULL);
360
361
362 return true;
363 }
364
365 void
366 PropSheet::SetHWNDFromPage (HWND h)
367 {
368 // If we're a modal dialog, there's no way for us to know our window handle unless
369 // one of our pages tells us through this function.
370 SetHWND (h);
371 }
372
373 /*
374 Adjust the size of a page so that it fits nicely into the window.
375 */
376 void
377 PropSheet::AdjustPageSize (HWND page)
378 {
379 PropSheetData& psd = PropSheetData::Instance();
380 if (!psd.clientRectValid) return;
381
382 /*
383 It's probably not obvious what's done here:
384 When this method is called the first time, the first page is already
385 created and sized, but at the coordinates (0,0). The sheet, however,
386 isn't in it's final size. My guess is that the sheet first creates the
387 page, and then resizes itself to have the right metrics to contain the
388 page and moves it to it's position. For our purposes, however, we need
389 the final metrics of the page. So, the first time this method is called,
390 we basically grab the size of the page, but calculate the top/left coords
391 ourselves.
392 */
393
394 if (!psd.gotPage)
395 {
396 psd.gotPage = true;
397
398 RECTWrapper& pageRect = psd.pageRect;
399 ::GetWindowRect (page, &pageRect);
400 // We want client coords.
401 ::ScreenToClient (page, (LPPOINT)&pageRect.left);
402 ::ScreenToClient (page, (LPPOINT)&pageRect.right);
403
404 LONG dialogBaseUnits = ::GetDialogBaseUnits ();
405 // The margins in DUs are a result of "educated guesses" and T&E.
406 int marginX = MulDiv (5, LOWORD(dialogBaseUnits), 4);
407 int marginY = MulDiv (5, HIWORD(dialogBaseUnits), 8);
408
409 pageRect.move (marginX, marginY);
410 }
411
412 SetWindowPos (page, 0, psd.pageRect.left, psd.pageRect.top,
413 psd.pageRect.width (), psd.pageRect.height (),
414 SWP_NOACTIVATE | SWP_NOZORDER);
415 }
416
417 void
418 PropSheet::AddPage (PropertyPage * p)
419 {
420 // Add a page to the property sheet.
421 p->YouAreBeingAddedToASheet (this);
422 PropertyPages.push_back(p);
423 }
424
425 bool
426 PropSheet::SetActivePage (int i)
427 {
428 // Posts a message to the message queue, so this won't block
429 return static_cast < bool > (::PropSheet_SetCurSel (GetHWND (), NULL, i));
430 }
431
432 bool
433 PropSheet::SetActivePageByID (int resource_id)
434 {
435 // Posts a message to the message queue, so this won't block
436 return static_cast < bool >
437 (::PropSheet_SetCurSelByID (GetHWND (), resource_id));
438 }
439
440 void
441 PropSheet::SetButtons (DWORD flags)
442 {
443 // Posts a message to the message queue, so this won't block
444 ::PropSheet_SetWizButtons (GetHWND (), flags);
445 }
446
447 void
448 PropSheet::PressButton (int button)
449 {
450 ::PropSheet_PressButton (GetHWND (), button);
451 }
This page took 0.054596 seconds and 5 git commands to generate.