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