2 * Copyright (c) 2001, 2002, 2003 Gary R. Van Sickle.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by Gary R. Van Sickle <g.r.vansickle@worldnet.att.net>
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.
20 #include "propsheet.h"
26 #include "getopt++/BoolOption.h"
27 #include "Exception.h"
28 #include "LogSingleton.h"
30 bool PropertyPage::DoOnceForSheet
= true;
33 Sizing information for some controls that are common to all pages.
35 static ControlAdjuster::ControlInfo DefaultControlsInfo
[] = {
36 {IDC_HEADICON
, CP_RIGHT
, CP_TOP
},
37 {IDC_HEADSEPARATOR
, CP_STRETCH
, CP_TOP
},
41 PropertyPage::PropertyPage ()
48 sizeProcessor
.AddControlInfo (DefaultControlsInfo
);
51 PropertyPage::~PropertyPage ()
55 bool PropertyPage::Create (int TemplateID
)
57 return Create (NULL
, NULL
, TemplateID
);
60 bool PropertyPage::Create (DLGPROC dlgproc
, int TemplateID
)
62 return Create (dlgproc
, NULL
, TemplateID
);
66 PropertyPage::Create (DLGPROC dlgproc
,
67 BOOL (*cproc
) (HWND h
, int id
, HWND hwndctl
,
68 UINT code
), int TemplateID
)
70 memset(&psp
, 0, sizeof (PROPSHEETPAGEW
));
71 psp
.dwSize
= sizeof (PROPSHEETPAGEW
);
73 psp
.hInstance
= GetInstance ();
74 psp
.pfnDlgProc
= FirstDialogProcReflector
;
75 psp
.pszTemplate
= MAKEINTRESOURCEW(TemplateID
);
76 psp
.lParam
= (LPARAM
) this;
77 psp
.pfnCallback
= NULL
;
86 PropertyPage::FirstDialogProcReflector (HWND hwnd
, UINT message
,
87 WPARAM wParam
, LPARAM lParam
)
91 if (message
!= WM_INITDIALOG
)
93 // Don't handle anything until we get a WM_INITDIALOG message, which
94 // will have our 'this' pointer with it.
98 This
= (PropertyPage
*) (((LPPROPSHEETPAGEW
) lParam
)->lParam
);
100 SetWindowLongPtr (hwnd
, DWLP_USER
, (LONG_PTR
) This
);
101 SetWindowLongPtrW (hwnd
, DWLP_DLGPROC
, (LONG_PTR
) DialogProcReflector
);
103 This
->SetHWND (hwnd
);
104 return This
->DialogProc (message
, wParam
, lParam
);
108 PropertyPage::DialogProcReflector (HWND hwnd
, UINT message
, WPARAM wParam
,
113 This
= (PropertyPage
*) GetWindowLongPtr (hwnd
, DWLP_USER
);
115 return This
->DialogProc (message
, wParam
, lParam
);
119 PropertyPage::DialogProc (UINT message
, WPARAM wParam
, LPARAM lParam
)
125 proc (GetHWND (), message
, wParam
, lParam
);
136 // Call it here so it stores the initial client rect.
137 sizeProcessor
.UpdateSize (GetHWND ());
139 // TRUE = Set focus to default control (in wParam).
144 NMHDR
*pNmHdr
= (NMHDR
*) lParam
;
146 // offer to subclass first
148 if (OnNotify (pNmHdr
, &result
))
150 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, result
);
154 switch (pNmHdr
->code
)
158 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, PSNRET_NOERROR
);
165 // Tell our parent PropSheet what its own HWND is.
166 GetOwner ()->SetHWNDFromPage (((NMHDR FAR
*) lParam
)->
168 GetOwner ()->CenterWindow ();
169 DoOnceForSheet
= false;
172 GetOwner ()->AdjustPageSize (GetHWND ());
174 // Set the wizard buttons apropriately
177 // Disable "Back" on first page.
178 GetOwner ()->SetButtons (PSWIZB_NEXT
);
182 // Disable "Next", enable "Finish" on last page
183 GetOwner ()->SetButtons (PSWIZB_BACK
| PSWIZB_FINISH
);
187 // Middle page, enable both "Next" and "Back" buttons
188 GetOwner ()->SetButtons (PSWIZB_BACK
| PSWIZB_NEXT
);
191 if(!wantsActivation())
193 ::SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, -1);
201 // -2 == disable unattended mode, display page
202 // -1 == display page but stay in unattended mode (progress bars)
203 // 0 == skip to next page (in propsheet sequence)
204 // IDD_* == skip to specified page
205 long nextwindow
= OnUnattended();
206 if (nextwindow
== -2)
208 unattended_mode
= attended
;
209 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, 0);
212 else if (nextwindow
== -1)
214 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, 0);
217 else if (nextwindow
== 0)
219 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, -1);
224 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, nextwindow
);
230 // 0 == Accept activation, -1 = Don't accept
231 ::SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, 0);
240 // FALSE = Allow deactivation
241 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, FALSE
);
248 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, retval
);
255 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, retval
);
261 // False = Allow the wizard to finish
262 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, FALSE
);
265 case TTN_GETDISPINFOW
:
267 return TooltipNotificationHandler (lParam
);
271 // Unrecognized notification
282 OnMessageCmd (LOWORD (wParam
), (HWND
) lParam
, HIWORD (wParam
));
286 SetWindowLongPtr (GetHWND (), DWLP_MSGRESULT
, 0);
289 else if (cmdproc
!= NULL
)
291 cmdproc (GetHWND(), LOWORD(wParam
), (HWND
)lParam
, HIWORD(wParam
));
298 sizeProcessor
.UpdateSize (GetHWND ());
301 case WM_CTLCOLORSTATIC
:
303 // check for text controls that we've url-ified that are initializing
305 std::map
<int, ClickableURL
>::iterator theURL
;
307 // get the ID of the control, and look it up in our list
308 if ((id
= GetDlgCtrlID ((HWND
)lParam
)) == 0 ||
309 (theURL
= urls
.find (id
)) == urls
.end ())
311 // nope sorry, don't know nothing about this control
314 // set FG = blue, BG = default background for a dialog
315 SetTextColor ((HDC
)wParam
, RGB (0, 0, 255));
316 SetBkColor ((HDC
)wParam
, GetSysColor (COLOR_BTNFACE
));
318 // get the current font, add underline, and set it back
319 if (theURL
->second
.font
== 0)
323 GetTextMetrics ((HDC
)wParam
, &tm
);
325 memset ((void *)&lf
, 0, sizeof(LOGFONT
));
326 lf
.lfUnderline
= TRUE
;
327 lf
.lfHeight
= tm
.tmHeight
;
328 lf
.lfWeight
= tm
.tmWeight
;
329 lf
.lfItalic
= tm
.tmItalic
;
330 lf
.lfStrikeOut
= tm
.tmStruckOut
;
331 lf
.lfCharSet
= tm
.tmCharSet
;
332 lf
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
333 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
334 lf
.lfQuality
= DEFAULT_QUALITY
;
335 lf
.lfPitchAndFamily
= tm
.tmPitchAndFamily
;
336 GetTextFace ((HDC
)wParam
, LF_FACESIZE
, lf
.lfFaceName
);
337 if ((theURL
->second
.font
= CreateFontIndirect (&lf
)) == NULL
)
338 Log (LOG_PLAIN
) << "Warning: unable to set font for url "
339 << theURL
->second
.url
<< endLog
;
343 SelectObject ((HDC
)wParam
, theURL
->second
.font
);
345 // make a brush if we have not yet
346 if (theURL
->second
.brush
== NULL
)
347 theURL
->second
.brush
= CreateSolidBrush
348 (GetSysColor (COLOR_BTNFACE
));
350 return (INT_PTR
) theURL
->second
.brush
;
353 // we do this so that derived classes that wish to process this message
354 // do not need to reimplement the entire WinProc, they can just
355 // provice an OnMouseWheel. (Note that mousewheel events are delivered
356 // to the parent of the window that received the scroll, so it would
357 // not work to just process this message there.)
358 return OnMouseWheel (message
, wParam
, lParam
);
361 // similar delegation as with WM_MOUSEWHEEL
362 return OnTimerMessage (message
, wParam
, lParam
);
368 if ((message
>= WM_APP
) && (message
< 0xC000))
370 // It's a private app message
371 return OnMessageApp (message
, wParam
, lParam
);
374 TOPLEVEL_CATCH(GetHWND (), "DialogProc");
381 PropertyPage::OnMouseWheel (UINT message
, WPARAM wParam
, LPARAM lParam
)
383 return 1; // not handled; define in a derived class to support this
387 PropertyPage::OnTimerMessage (UINT message
, WPARAM wParam
, LPARAM lParam
)
389 return 1; // not handled; define in a derived class to support this
393 PropertyPage::setTitleFont ()
395 // These font settings will just silently fail when the resource id
396 // is not present on a page.
397 // Set header title font of each internal page
398 SetDlgItemFont(IDC_STATIC_HEADER_TITLE
, "MS Shell Dlg", 8, FW_BOLD
);
399 // Set the font for the IDC_STATIC_WELCOME_TITLE
400 SetDlgItemFont(IDC_STATIC_WELCOME_TITLE
, "Arial", 12, FW_BOLD
);
403 std::map
<int, PropertyPage::ClickableURL
> PropertyPage::urls
;
406 PropertyPage::makeClickable (int id
, std::string link
)
407 // turns a static text control in this dialog into a hyperlink
409 // get the handle of the specified control
410 HWND hctl
= ::GetDlgItem (GetHWND (), id
);
412 return; // invalid ID
414 if (urls
.find (id
) != urls
.end ())
415 return; // already done this one
419 c
.font
= NULL
; // these will be created as needed
421 if ((c
.origWinProc
= reinterpret_cast<WNDPROC
>(SetWindowLongPtr (hctl
,
422 GWLP_WNDPROC
, (LONG_PTR
) & PropertyPage::urlWinProc
))) == 0)
425 // add this to 'urls' so that the dialog and control winprocs know about it
428 // set a tooltip for the link
429 AddTooltip (id
, link
.c_str());
433 PropertyPage::urlWinProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
434 // a winproc that we use to subclass a static text control to make a URL
437 std::map
<int, ClickableURL
>::iterator theURL
;
439 // get the ID of the control, and look it up in our list
440 if ((id
= GetDlgCtrlID (hwnd
)) == 0 ||
441 (theURL
= urls
.find (id
)) == urls
.end ())
443 // we were called for a control that we weren't installed on
444 // punt to default winproc
445 return DefWindowProc (hwnd
, uMsg
, wParam
, lParam
);
451 // they clicked our URL! yay!
452 intptr_t rc
= (intptr_t) ShellExecute (hwnd
, "open",
453 theURL
->second
.url
.c_str (), NULL
, NULL
, SW_SHOWNORMAL
);
456 Log (LOG_PLAIN
) << "Unable to launch browser for URL " <<
457 theURL
->second
.url
<< " (rc = " << rc
<< ")" << endLog
;
462 // show the hand cursor when they hover
463 // note: apparently the hand cursor isn't available
464 // on very old versions of win95? So, check return of LoadCursor
465 // and don't attempt SetCursor if it failed
466 HCURSOR c
= LoadCursor (NULL
, reinterpret_cast<LPCSTR
>(IDC_HAND
));
473 // normally, a static control returns HTTRANSPARENT for this
474 // which means that we would never receive the SETCURSOR message
480 WNDPROC saveWinProc
= theURL
->second
.origWinProc
;
481 DeleteObject (theURL
->second
.font
);
482 DeleteObject (theURL
->second
.brush
);
484 return CallWindowProc (saveWinProc
, hwnd
, uMsg
, wParam
, lParam
);
488 // pass on control to the previous winproc
489 return CallWindowProc (theURL
->second
.origWinProc
, hwnd
, uMsg
, wParam
, lParam
);