]>
Commit | Line | Data |
---|---|---|
df62e023 | 1 | /* |
25130a4d | 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 PropertyPage class. It works closely with the | |
17 | // PropSheet class to implement a single page of the property sheet. | |
18 | ||
19 | #include "proppage.h" | |
20 | #include "propsheet.h" | |
21 | #include "win32.h" | |
daab12c7 | 22 | #include <shellapi.h> |
b7301c43 | 23 | #include "resource.h" |
f2ff9838 RC |
24 | #include "state.h" |
25 | ||
26 | #include "getopt++/BoolOption.h" | |
4d7a6eeb | 27 | #include "Exception.h" |
daab12c7 | 28 | #include "LogSingleton.h" |
df62e023 RC |
29 | |
30 | bool PropertyPage::DoOnceForSheet = true; | |
31 | ||
ee91d9be RC |
32 | /* |
33 | Sizing information for some controls that are common to all pages. | |
34 | */ | |
35 | static ControlAdjuster::ControlInfo DefaultControlsInfo[] = { | |
a8d753b6 RC |
36 | {IDC_HEADICON, CP_RIGHT, CP_TOP}, |
37 | {IDC_HEADSEPARATOR, CP_STRETCH, CP_TOP}, | |
38 | {0, CP_LEFT, CP_TOP} | |
ee91d9be RC |
39 | }; |
40 | ||
df62e023 RC |
41 | PropertyPage::PropertyPage () |
42 | { | |
43 | proc = NULL; | |
44 | cmdproc = NULL; | |
45 | IsFirst = false; | |
46 | IsLast = false; | |
ee91d9be RC |
47 | |
48 | sizeProcessor.AddControlInfo (DefaultControlsInfo); | |
df62e023 RC |
49 | } |
50 | ||
51 | PropertyPage::~PropertyPage () | |
52 | { | |
53 | } | |
54 | ||
55 | bool PropertyPage::Create (int TemplateID) | |
56 | { | |
57 | return Create (NULL, NULL, TemplateID); | |
58 | } | |
59 | ||
60 | bool PropertyPage::Create (DLGPROC dlgproc, int TemplateID) | |
61 | { | |
62 | return Create (dlgproc, NULL, TemplateID); | |
63 | } | |
64 | ||
65 | bool | |
2f3a74d1 | 66 | PropertyPage::Create (DLGPROC dlgproc, |
df62e023 RC |
67 | BOOL (*cproc) (HWND h, int id, HWND hwndctl, |
68 | UINT code), int TemplateID) | |
69 | { | |
78b032d0 | 70 | memset(&psp, 0, sizeof (PROPSHEETPAGE)); |
df62e023 RC |
71 | psp.dwSize = sizeof (PROPSHEETPAGE); |
72 | psp.dwFlags = 0; | |
73 | psp.hInstance = GetInstance (); | |
74 | psp.pfnDlgProc = FirstDialogProcReflector; | |
5039f845 | 75 | psp.pszTemplate = MAKEINTRESOURCE(TemplateID); |
df62e023 RC |
76 | psp.lParam = (LPARAM) this; |
77 | psp.pfnCallback = NULL; | |
78 | ||
79 | proc = dlgproc; | |
80 | cmdproc = cproc; | |
81 | ||
82 | return true; | |
83 | } | |
84 | ||
e08abe3f | 85 | INT_PTR CALLBACK |
df62e023 RC |
86 | PropertyPage::FirstDialogProcReflector (HWND hwnd, UINT message, |
87 | WPARAM wParam, LPARAM lParam) | |
88 | { | |
89 | PropertyPage *This; | |
90 | ||
91 | if (message != WM_INITDIALOG) | |
92 | { | |
93 | // Don't handle anything until we get a WM_INITDIALOG message, which | |
106d8160 | 94 | // will have our 'this' pointer with it. |
df62e023 RC |
95 | return FALSE; |
96 | } | |
97 | ||
98 | This = (PropertyPage *) (((PROPSHEETPAGE *) lParam)->lParam); | |
99 | ||
e08abe3f YS |
100 | SetWindowLongPtr (hwnd, DWLP_USER, (LONG_PTR) This); |
101 | SetWindowLongPtr (hwnd, DWLP_DLGPROC, (LONG_PTR) DialogProcReflector); | |
df62e023 RC |
102 | |
103 | This->SetHWND (hwnd); | |
104 | return This->DialogProc (message, wParam, lParam); | |
105 | } | |
106 | ||
e08abe3f | 107 | INT_PTR CALLBACK |
df62e023 RC |
108 | PropertyPage::DialogProcReflector (HWND hwnd, UINT message, WPARAM wParam, |
109 | LPARAM lParam) | |
110 | { | |
111 | PropertyPage *This; | |
112 | ||
e08abe3f | 113 | This = (PropertyPage *) GetWindowLongPtr (hwnd, DWLP_USER); |
df62e023 RC |
114 | |
115 | return This->DialogProc (message, wParam, lParam); | |
116 | } | |
117 | ||
e08abe3f | 118 | INT_PTR CALLBACK |
df62e023 RC |
119 | PropertyPage::DialogProc (UINT message, WPARAM wParam, LPARAM lParam) |
120 | { | |
4d7a6eeb MB |
121 | try |
122 | { | |
123 | if (proc != NULL) | |
df62e023 RC |
124 | { |
125 | proc (GetHWND (), message, wParam, lParam); | |
126 | } | |
127 | ||
4d7a6eeb | 128 | switch (message) |
df62e023 | 129 | { |
4d7a6eeb MB |
130 | case WM_INITDIALOG: |
131 | { | |
132 | OnInit (); | |
133 | ||
134 | setTitleFont (); | |
135 | ||
136 | // Call it here so it stores the initial client rect. | |
137 | sizeProcessor.UpdateSize (GetHWND ()); | |
138 | ||
139 | // TRUE = Set focus to default control (in wParam). | |
140 | return TRUE; | |
4d7a6eeb MB |
141 | } |
142 | case WM_NOTIFY: | |
143 | switch (((NMHDR FAR *) lParam)->code) | |
144 | { | |
145 | case PSN_APPLY: | |
37f49caf | 146 | { |
e08abe3f | 147 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, PSNRET_NOERROR); |
37f49caf BD |
148 | return TRUE; |
149 | } | |
4d7a6eeb MB |
150 | case PSN_SETACTIVE: |
151 | { | |
152 | if (DoOnceForSheet) | |
153 | { | |
154 | // Tell our parent PropSheet what its own HWND is. | |
155 | GetOwner ()->SetHWNDFromPage (((NMHDR FAR *) lParam)-> | |
156 | hwndFrom); | |
157 | GetOwner ()->CenterWindow (); | |
158 | DoOnceForSheet = false; | |
159 | } | |
160 | ||
161 | GetOwner ()->AdjustPageSize (GetHWND ()); | |
162 | ||
163 | // Set the wizard buttons apropriately | |
164 | if (IsFirst) | |
165 | { | |
166 | // Disable "Back" on first page. | |
167 | GetOwner ()->SetButtons (PSWIZB_NEXT); | |
168 | } | |
169 | else if (IsLast) | |
170 | { | |
171 | // Disable "Next", enable "Finish" on last page | |
172 | GetOwner ()->SetButtons (PSWIZB_BACK | PSWIZB_FINISH); | |
173 | } | |
174 | else | |
175 | { | |
176 | // Middle page, enable both "Next" and "Back" buttons | |
177 | GetOwner ()->SetButtons (PSWIZB_BACK | PSWIZB_NEXT); | |
178 | } | |
179 | ||
180 | if(!wantsActivation()) | |
181 | { | |
e08abe3f | 182 | ::SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, -1); |
4d7a6eeb MB |
183 | return TRUE; |
184 | } | |
185 | ||
186 | OnActivate (); | |
187 | ||
188 | if (unattended_mode) | |
189 | { | |
190 | // -2 == disable unattended mode, display page | |
191 | // -1 == display page but stay in unattended mode (progress bars) | |
19cd8e05 | 192 | // 0 == skip to next page (in propsheet sequence) |
4d7a6eeb MB |
193 | // IDD_* == skip to specified page |
194 | long nextwindow = OnUnattended(); | |
195 | if (nextwindow == -2) | |
196 | { | |
7a4e611a | 197 | unattended_mode = attended; |
e08abe3f | 198 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, 0); |
4d7a6eeb MB |
199 | return TRUE; |
200 | } | |
201 | else if (nextwindow == -1) | |
202 | { | |
e08abe3f | 203 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, 0); |
4d7a6eeb MB |
204 | return TRUE; |
205 | } | |
206 | else if (nextwindow == 0) | |
207 | { | |
e08abe3f | 208 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, -1); |
4d7a6eeb MB |
209 | return TRUE; |
210 | } | |
211 | else | |
212 | { | |
e08abe3f | 213 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, nextwindow); |
4d7a6eeb MB |
214 | return TRUE; |
215 | } | |
216 | } | |
217 | else | |
218 | { | |
219 | // 0 == Accept activation, -1 = Don't accept | |
e08abe3f | 220 | ::SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, 0); |
4d7a6eeb MB |
221 | return TRUE; |
222 | } | |
223 | ||
224 | } | |
225 | break; | |
226 | case PSN_KILLACTIVE: | |
37f49caf BD |
227 | { |
228 | OnDeactivate (); | |
229 | // FALSE = Allow deactivation | |
e08abe3f | 230 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, FALSE); |
37f49caf BD |
231 | return TRUE; |
232 | } | |
4d7a6eeb MB |
233 | case PSN_WIZNEXT: |
234 | { | |
235 | LONG retval; | |
236 | retval = OnNext (); | |
e08abe3f | 237 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, retval); |
4d7a6eeb MB |
238 | return TRUE; |
239 | } | |
4d7a6eeb MB |
240 | case PSN_WIZBACK: |
241 | { | |
242 | LONG retval; | |
243 | retval = OnBack (); | |
e08abe3f | 244 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, retval); |
4d7a6eeb MB |
245 | return TRUE; |
246 | } | |
4d7a6eeb | 247 | case PSN_WIZFINISH: |
37f49caf | 248 | { |
c6bf266d | 249 | OnFinish (); |
37f49caf | 250 | // False = Allow the wizard to finish |
e08abe3f | 251 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, FALSE); |
37f49caf BD |
252 | return TRUE; |
253 | } | |
254 | case TTN_GETDISPINFO: | |
255 | { | |
256 | return TooltipNotificationHandler (lParam); | |
257 | } | |
4d7a6eeb | 258 | default: |
37f49caf BD |
259 | { |
260 | // Unrecognized notification | |
261 | return FALSE; | |
262 | } | |
4d7a6eeb MB |
263 | } |
264 | break; | |
265 | case WM_COMMAND: | |
266 | { | |
267 | bool retval; | |
268 | ||
269 | retval = | |
270 | OnMessageCmd (LOWORD (wParam), (HWND) lParam, HIWORD (wParam)); | |
271 | if (retval == true) | |
272 | { | |
273 | // Handled, return 0 | |
e08abe3f | 274 | SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT, 0); |
4d7a6eeb MB |
275 | return TRUE; |
276 | } | |
277 | else if (cmdproc != NULL) | |
278 | { | |
4875ac88 MB |
279 | cmdproc (GetHWND(), LOWORD(wParam), (HWND)lParam, HIWORD(wParam)); |
280 | return 0; | |
4d7a6eeb MB |
281 | } |
282 | break; | |
283 | } | |
284 | case WM_SIZE: | |
285 | { | |
286 | sizeProcessor.UpdateSize (GetHWND ()); | |
287 | break; | |
288 | } | |
daab12c7 BD |
289 | case WM_CTLCOLORSTATIC: |
290 | { | |
291 | // check for text controls that we've url-ified that are initializing | |
292 | int id; | |
293 | std::map <int, ClickableURL>::iterator theURL; | |
294 | ||
295 | // get the ID of the control, and look it up in our list | |
296 | if ((id = GetDlgCtrlID ((HWND)lParam)) == 0 || | |
297 | (theURL = urls.find (id)) == urls.end ()) | |
298 | ||
299 | // nope sorry, don't know nothing about this control | |
300 | return FALSE; | |
301 | ||
302 | // set FG = blue, BG = default background for a dialog | |
303 | SetTextColor ((HDC)wParam, RGB (0, 0, 255)); | |
304 | SetBkColor ((HDC)wParam, GetSysColor (COLOR_BTNFACE)); | |
305 | ||
306 | // get the current font, add underline, and set it back | |
307 | if (theURL->second.font == 0) | |
308 | { | |
309 | TEXTMETRIC tm; | |
310 | ||
311 | GetTextMetrics ((HDC)wParam, &tm); | |
312 | LOGFONT lf; | |
313 | memset ((void *)&lf, 0, sizeof(LOGFONT)); | |
314 | lf.lfUnderline = TRUE; | |
315 | lf.lfHeight = tm.tmHeight; | |
316 | lf.lfWeight = tm.tmWeight; | |
317 | lf.lfItalic = tm.tmItalic; | |
318 | lf.lfStrikeOut = tm.tmStruckOut; | |
319 | lf.lfCharSet = tm.tmCharSet; | |
320 | lf.lfOutPrecision = OUT_DEFAULT_PRECIS; | |
321 | lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; | |
322 | lf.lfQuality = DEFAULT_QUALITY; | |
323 | lf.lfPitchAndFamily = tm.tmPitchAndFamily; | |
324 | GetTextFace ((HDC)wParam, LF_FACESIZE, lf.lfFaceName); | |
325 | if ((theURL->second.font = CreateFontIndirect (&lf)) == NULL) | |
157dc2b8 | 326 | Log (LOG_PLAIN) << "Warning: unable to set font for url " |
daab12c7 BD |
327 | << theURL->second.url << endLog; |
328 | } | |
329 | ||
330 | // apply the font | |
331 | SelectObject ((HDC)wParam, theURL->second.font); | |
332 | ||
333 | // make a brush if we have not yet | |
334 | if (theURL->second.brush == NULL) | |
335 | theURL->second.brush = CreateSolidBrush | |
336 | (GetSysColor (COLOR_BTNFACE)); | |
337 | ||
e08abe3f | 338 | return (INT_PTR) theURL->second.brush; |
daab12c7 | 339 | } |
82306ac2 BD |
340 | case WM_MOUSEWHEEL: |
341 | // we do this so that derived classes that wish to process this message | |
342 | // do not need to reimplement the entire WinProc, they can just | |
343 | // provice an OnMouseWheel. (Note that mousewheel events are delivered | |
344 | // to the parent of the window that received the scroll, so it would | |
345 | // not work to just process this message there.) | |
346 | return OnMouseWheel (message, wParam, lParam); | |
1c1913d1 RR |
347 | |
348 | case WM_TIMER: | |
349 | // similar delegation as with WM_MOUSEWHEEL | |
350 | return OnTimerMessage (message, wParam, lParam); | |
351 | ||
4d7a6eeb MB |
352 | default: |
353 | break; | |
df62e023 RC |
354 | } |
355 | ||
4d7a6eeb | 356 | if ((message >= WM_APP) && (message < 0xC000)) |
df62e023 RC |
357 | { |
358 | // It's a private app message | |
359 | return OnMessageApp (message, wParam, lParam); | |
360 | } | |
4d7a6eeb | 361 | } |
703f1a44 | 362 | TOPLEVEL_CATCH(GetHWND (), "DialogProc"); |
df62e023 RC |
363 | |
364 | // Wasn't handled | |
365 | return FALSE; | |
366 | } | |
25130a4d | 367 | |
e08abe3f | 368 | INT_PTR CALLBACK |
82306ac2 BD |
369 | PropertyPage::OnMouseWheel (UINT message, WPARAM wParam, LPARAM lParam) |
370 | { | |
371 | return 1; // not handled; define in a derived class to support this | |
372 | } | |
373 | ||
1c1913d1 RR |
374 | INT_PTR CALLBACK |
375 | PropertyPage::OnTimerMessage (UINT message, WPARAM wParam, LPARAM lParam) | |
376 | { | |
377 | return 1; // not handled; define in a derived class to support this | |
378 | } | |
379 | ||
25130a4d MB |
380 | void |
381 | PropertyPage::setTitleFont () | |
382 | { | |
383 | // These font settings will just silently fail when the resource id | |
384 | // is not present on a page. | |
385 | // Set header title font of each internal page | |
386 | SetDlgItemFont(IDC_STATIC_HEADER_TITLE, "MS Shell Dlg", 8, FW_BOLD); | |
387 | // Set the font for the IDC_STATIC_WELCOME_TITLE | |
388 | SetDlgItemFont(IDC_STATIC_WELCOME_TITLE, "Arial", 12, FW_BOLD); | |
389 | } | |
daab12c7 BD |
390 | |
391 | std::map <int, PropertyPage::ClickableURL> PropertyPage::urls; | |
392 | ||
393 | void | |
08678720 | 394 | PropertyPage::makeClickable (int id, std::string link) |
daab12c7 BD |
395 | // turns a static text control in this dialog into a hyperlink |
396 | { | |
397 | // get the handle of the specified control | |
398 | HWND hctl = ::GetDlgItem (GetHWND (), id); | |
399 | if (hctl == NULL) | |
400 | return; // invalid ID | |
401 | ||
402 | if (urls.find (id) != urls.end ()) | |
403 | return; // already done this one | |
404 | ||
405 | ClickableURL c; | |
406 | c.url = link; | |
407 | c.font = NULL; // these will be created as needed | |
408 | c.brush = NULL; | |
409 | if ((c.origWinProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr (hctl, | |
410 | GWLP_WNDPROC, (LONG_PTR) & PropertyPage::urlWinProc))) == 0) | |
411 | return; // failure | |
412 | ||
413 | // add this to 'urls' so that the dialog and control winprocs know about it | |
414 | urls[id] = c; | |
37f49caf BD |
415 | |
416 | // set a tooltip for the link | |
417 | AddTooltip (id, link.c_str()); | |
daab12c7 BD |
418 | } |
419 | ||
420 | LRESULT CALLBACK | |
421 | PropertyPage::urlWinProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
422 | // a winproc that we use to subclass a static text control to make a URL | |
423 | { | |
424 | int id; | |
425 | std::map <int, ClickableURL>::iterator theURL; | |
426 | ||
427 | // get the ID of the control, and look it up in our list | |
428 | if ((id = GetDlgCtrlID (hwnd)) == 0 || | |
429 | (theURL = urls.find (id)) == urls.end ()) | |
430 | ||
431 | // we were called for a control that we weren't installed on | |
432 | // punt to default winproc | |
433 | return DefWindowProc (hwnd, uMsg, wParam, lParam); | |
434 | ||
435 | switch (uMsg) | |
436 | { | |
437 | case WM_LBUTTONDOWN: | |
438 | { | |
439 | // they clicked our URL! yay! | |
e08abe3f | 440 | intptr_t rc = (intptr_t) ShellExecute (hwnd, "open", |
d2a3615c | 441 | theURL->second.url.c_str (), NULL, NULL, SW_SHOWNORMAL); |
daab12c7 BD |
442 | |
443 | if (rc <= 32) | |
157dc2b8 | 444 | Log (LOG_PLAIN) << "Unable to launch browser for URL " << |
daab12c7 BD |
445 | theURL->second.url << " (rc = " << rc << ")" << endLog; |
446 | break; | |
447 | } | |
448 | case WM_SETCURSOR: | |
449 | { | |
450 | // show the hand cursor when they hover | |
451 | // note: apparently the hand cursor isn't available | |
452 | // on very old versions of win95? So, check return of LoadCursor | |
453 | // and don't attempt SetCursor if it failed | |
454 | HCURSOR c = LoadCursor (NULL, reinterpret_cast<LPCSTR>(IDC_HAND)); | |
455 | if (c) | |
456 | SetCursor (c); | |
457 | return TRUE; | |
458 | } | |
459 | case WM_NCHITTEST: | |
460 | { | |
461 | // normally, a static control returns HTTRANSPARENT for this | |
462 | // which means that we would never receive the SETCURSOR message | |
463 | return HTCLIENT; | |
464 | } | |
465 | case WM_DESTROY: | |
466 | { | |
467 | // clean up | |
468 | WNDPROC saveWinProc = theURL->second.origWinProc; | |
469 | DeleteObject (theURL->second.font); | |
470 | DeleteObject (theURL->second.brush); | |
471 | urls.erase (id); | |
472 | return CallWindowProc (saveWinProc, hwnd, uMsg, wParam, lParam); | |
473 | } | |
474 | } | |
475 | ||
476 | // pass on control to the previous winproc | |
477 | return CallWindowProc (theURL->second.origWinProc, hwnd, uMsg, wParam, lParam); | |
478 | } |