OSDN Git Service

Use __super
[winmerge-jp/winmerge-jp.git] / Src / Common / MessageBoxDialog.cpp
1 /*
2  *      Extended MFC message boxes -- Version 1.1a
3  *      Copyright (c) 2004 Michael P. Mehl. All rights reserved.
4  *
5  *      The contents of this file are subject to the Mozilla Public License
6  *      Version 1.1a (the "License"); you may not use this file except in
7  *      compliance with the License. You may obtain a copy of the License at 
8  *      http://www.mozilla.org/MPL/.
9  *
10  *      Software distributed under the License is distributed on an "AS IS" basis,
11  *      WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  *      for the specific language governing rights and limitations under the
13  *      License. 
14  *
15  *      The Original Code is Copyright (c) 2004 Michael P. Mehl. All rights
16  *      reserved. The Initial Developer of the Original Code is Michael P. Mehl
17  *      <michael.mehl@web.de>.
18  *
19  *      Alternatively, the contents of this file may be used under the terms of
20  *      the GNU Lesser General Public License Version 2.1 (the "LGPL License"),
21  *      in which case the provisions of LGPL License are applicable instead of
22  *      those above. If you wish to allow use of your version of this file only
23  *      under the terms of the LGPL License and not to allow others to use your
24  *      version of this file under the MPL, indicate your decision by deleting
25  *      the provisions above and replace them with the notice and other provisions
26  *      required by the LGPL License. If you do not delete the provisions above,
27  *      a recipient may use your version of this file under either the MPL or
28  *      the LGPL License.
29  */
30
31 /*
32  *      The flag MB_DONT_DISPLAY_AGAIN or MB_DONT_ASK_AGAIN is stored in the registry
33  *  See GenerateRegistryKey for the creation of the key
34  *  The "normal" rule is to use the help Id as identifier
35  *  And it is really simple to just repeat the text ID as help ID
36  *  (for message formed with AfxFormatString, repeat the ID used to format the string)
37  *
38  *  Search for MB_DONT_DISPLAY_AGAIN and MB_DONT_ASK_AGAIN for all the
39  *  concerned AfxMessageBox
40  */
41
42 #include "StdAfx.h"
43
44 #include "MessageBoxDialog.h"
45
46 #ifdef _DEBUG
47 #define new DEBUG_NEW
48 #endif
49
50 using std::vector;
51
52 IMPLEMENT_DYNAMIC(CMessageBoxDialog, CDialog)
53
54 //////////////////////////////////////////////////////////////////////////////
55 // Layout values (in dialog units).
56
57 #define CX_BORDER                                       8               // Width of the border.
58 #define CY_BORDER                                       8               // Height of the border.
59
60 #define CX_CHECKBOX_ADDON                       14              // Additional width of the checkbox.
61
62 #define CX_BUTTON                                       51              // Standard width of a button.
63 #define CY_BUTTON                                       15              // Standard height of a button.
64 #define CX_BUTTON_BORDER                        4               // Standard border for a button.
65 #define CY_BUTTON_BORDER                        1               // Standard border for a button.
66 #define CX_BUTTON_SPACE                         4               // Standard space for a button.
67
68 #define CX_DLGUNIT_BASE                         1000    // Values used for converting
69 #define CY_DLGUNIT_BASE                         1000    // dialog units to pixels.
70
71 //////////////////////////////////////////////////////////////////////////////
72 // Timer values.
73
74 #define MESSAGE_BOX_TIMER                       2201    // Event identifier for the timer.
75
76 //////////////////////////////////////////////////////////////////////////////
77 // Constructors and destructors of the class.
78
79 /*
80  *      Constructor of the class.
81  *
82  *      This constructor is used to provide the strings directly without providing
83  *      resource IDs from which these strings should be retrieved. If no title is
84  *      given, the application name will be used as the title of the dialog.
85  */
86  CMessageBoxDialog::CMessageBoxDialog ( CWnd* pParent, CString strMessage, 
87         CString strTitle, UINT nStyle, UINT nHelp, const CString& strRegistryKey ) 
88         : CDialog ( CMessageBoxDialog::IDD, pParent )
89         , m_strMessage(strMessage)
90         , m_strTitle(strTitle.IsEmpty() ? AfxGetAppName() : strTitle)
91         , m_nStyle(nStyle)
92         , m_nHelp(nHelp)
93         , m_hIcon(nullptr)
94         , m_nTimeoutSeconds(0)
95         , m_bTimeoutDisabled(false)
96         , m_nTimeoutTimer(0)
97         , m_strRegistryKey(strRegistryKey)
98         , m_nDefaultButton(IDC_STATIC)
99         , m_nEscapeButton(IDC_STATIC)
100         , m_sDialogUnit(CSize(0, 0))
101         , m_sIcon(CSize(0, 0))
102         , m_sMessage(CSize(0, 0))
103         , m_sCheckbox(CSize(0, 0))
104         , m_sButton(CSize(0, 0))
105 {
106         // Enable the active accessibility.
107         ASSERT(!strMessage.IsEmpty());
108
109         m_aButtons.clear();
110
111         NONCLIENTMETRICS ncm = { sizeof NONCLIENTMETRICS };
112         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof NONCLIENTMETRICS, &ncm, 0);
113         m_font.CreateFontIndirect(&ncm.lfMessageFont);
114
115         LOGFONT lf = { 0 };
116         HTHEME hTheme = OpenThemeData(nullptr, _T("TEXTSTYLE"));
117         if (hTheme != nullptr && SUCCEEDED(GetThemeFont(hTheme, nullptr, TEXT_MAININSTRUCTION, 0, TMT_FONT, &lf)))
118         {
119                 m_fontMainInstruction.CreateFontIndirect(&lf);
120                 GetThemeColor(hTheme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &m_clrMainInstructionFont);
121                 CloseThemeData(hTheme);
122         }
123         else
124         {
125                 m_fontMainInstruction.CreateFontIndirect(&ncm.lfMessageFont);
126                 m_clrMainInstructionFont = GetSysColor(COLOR_WINDOWTEXT);
127         }
128 }
129
130 /*
131  *      Constructor of the class.
132  *
133  *      This constructor is used to load the strings for the title and the message
134  *      text from the resources of this project. If no title is given, the
135  *      application name will be used as the title of the dialog.
136  */
137 CMessageBoxDialog::CMessageBoxDialog ( CWnd* pParent, UINT nMessageID,
138         UINT nTitleID, UINT nStyle, UINT nHelp, const CString& strRegistryKey ) 
139         : CMessageBoxDialog(pParent, 
140                 LoadResString(nMessageID).c_str(), 
141                 nTitleID == 0 ? AfxGetAppName() : LoadResString(nTitleID).c_str(), 
142                 nStyle, nHelp, strRegistryKey )
143 {
144 }
145
146 /*
147  *      Destructor of the class.
148  */
149 CMessageBoxDialog::~CMessageBoxDialog ( )
150 {
151 }
152
153 //////////////////////////////////////////////////////////////////////////////
154 // Methods for setting and retrieving dialog options.
155
156 /*
157  *      Method for setting the style of the message box.
158  */
159 inline void CMessageBoxDialog::SetStyle ( UINT nStyle )
160 {
161         // Set the style of the message box.
162         m_nStyle = nStyle;
163 }
164
165 /*
166  *      Method for retrieving the style of the message box.
167  */
168 inline UINT CMessageBoxDialog::GetStyle ( )
169 {
170         // Return the current style of the message box.
171         return m_nStyle;
172 }
173
174 /*
175  *      Method for setting the message to be displayed in the message box.
176  */
177 inline void CMessageBoxDialog::SetMessage ( LPCTSTR strMessage )
178 {
179         ASSERT(*strMessage != '\0');
180
181         // Save the message text.
182         m_strMessage = strMessage;
183 }
184
185 /*
186  *      Methods for setting the message to be displayed in the message box.
187  */
188 inline void CMessageBoxDialog::SetMessage ( UINT nMessageID )
189 {
190         // Load the message from the resources.
191         m_strMessage = LoadResString(nMessageID);
192         ASSERT(!m_strMessage.empty());
193 }
194
195 /*
196  *      Method for retrieving the message to be displayed in the message box.
197  */
198 inline const String &CMessageBoxDialog::GetMessage ( )
199 {
200         // Return the message text.
201         return m_strMessage;
202 }
203
204 /*
205  *      Method for setting the title to be displayed in the message box.
206  */
207 inline void CMessageBoxDialog::SetTitle ( LPCTSTR strTitle )
208 {
209         // Check whether a title was given.
210         if ( *strTitle == '\0' )
211         {
212                 // Use the application name as the title.
213                 strTitle = AfxGetAppName();
214         }
215
216         // Save the title.
217         m_strTitle = strTitle;
218 }
219
220 /*
221  *      Method for setting the title to be displayed in the message box.
222  */
223 inline void CMessageBoxDialog::SetTitle ( UINT nTitleID )
224 {
225         // Check whether an ID was given.
226         if ( nTitleID == 0 )
227         {
228                 // Use the application name as the title.
229                 m_strTitle = AfxGetAppName();
230         }
231         else
232         {
233                 // Try to load the string from the resources.
234                 m_strTitle = LoadResString(nTitleID);
235                 ASSERT(!m_strTitle.empty());
236         }
237 }
238
239 /*
240  *      Method for retrieving the title to be displayed in the message box.
241  */
242 inline const String &CMessageBoxDialog::GetTitle ( )
243 {
244         // Return the title of the message box.
245         return m_strTitle;
246 }
247
248 /*
249  *      Method for setting the icon to be displayed in the message box.
250  */
251 inline void CMessageBoxDialog::SetMessageIcon ( HICON hIcon )
252 {
253         ASSERT(hIcon != nullptr);
254
255         // Save the icon.
256         m_hIcon = hIcon;
257 }
258
259 /*
260  *      Method for setting the icon to be displayed in the message box.
261  */
262 inline void CMessageBoxDialog::SetMessageIcon ( UINT nIconID )
263 {
264         // Try to load the given icon.
265         m_hIcon = AfxGetApp()->LoadIcon(nIconID);
266
267         ASSERT(m_hIcon != nullptr);
268 }
269
270 /*
271  *      Method for retrieving the icon to be displayed in the message box.
272  */
273 inline HICON CMessageBoxDialog::GetMessageIcon ( )
274 {
275         // Return the icon for the message box.
276         return m_hIcon;
277 }
278
279 /*
280  *      Method for setting a timeout.
281  *
282  *      A timeout is a countdown, which starts, when the message box is displayed.
283  *      There are two modes for a timeout: The "un-disabled" or "enabled" timeout
284  *      means, that the user can choose any button, but if he doesn't choose one,
285  *      the default button will be assumed as being chossen, when the countdown is
286  *      finished. The other mode, a "disabled" countdown is something like a nag
287  *      screen. All buttons will be disabled, until the countdown is finished.
288  *      After that, the user can click any button.
289  */
290 void CMessageBoxDialog::SetTimeout ( UINT nSeconds, bool bDisabled /*= false*/)
291 {
292         // Save the settings for the timeout.
293         m_nTimeoutSeconds       = nSeconds;
294         m_bTimeoutDisabled      = bDisabled;
295 }
296
297 /*
298  *      Method for retrieving the seconds for a timeout.
299  */
300 inline UINT CMessageBoxDialog::GetTimeoutSeconds ( )
301 {
302         // Return the seconds for the timeout.
303         return m_nTimeoutSeconds;
304 }
305
306 /*
307  *      Method for retrieving whether a timeout is disabled.
308  */
309 inline bool CMessageBoxDialog::GetTimeoutDisabled ( )
310 {
311         // Return the flag whether the timeout is disabled.
312         return m_bTimeoutDisabled;
313 }
314
315 //////////////////////////////////////////////////////////////////////////////
316 // Methods for handling the stored states.
317
318 /*
319  *      Method for resetting the message boxes stored in the registry.
320  *
321  *      This method removes all results of formerly displayed message boxes from
322  *      the registry and therefore resets the state of the message boxes. Even
323  *      those, where the user checked "Don't display/ask again" will again be
324  *      displayed.
325  */
326 void CMessageBoxDialog::ResetMessageBoxes ( )
327 {
328         // Try to retrieve a handle to the application object.
329         CWinApp* pApplication = AfxGetApp();
330
331         ASSERT(pApplication != nullptr);
332
333         // Check whether a handle was retrieved.
334         if ( pApplication != nullptr )
335         {
336                 // Delete the message box results stored in the registry.
337                 pApplication->WriteProfileString(REGISTRY_SECTION_MESSAGEBOX, nullptr, nullptr);
338         }
339 }
340
341 CString CMessageBoxDialog::GenerateRegistryKey(UINT nMessageID, UINT nHelpID)
342 {
343         CMessageBoxDialog dlg{ nullptr, nMessageID, 0, 0, nHelpID };
344         return dlg.GenerateRegistryKey();
345 }
346
347 //////////////////////////////////////////////////////////////////////////////
348 // Methods for handling common window functions.
349
350 /*
351  *      Parameters for passing to ModelessMessageBoxThread.
352  */
353 struct ModelessMesssageBoxParam
354 {
355         CString strMessage;
356         UINT nType;
357 };
358
359 /*
360  *      Thread for displaying the message boxes asyncronously.
361  */
362 static UINT ModelessMesssageBoxThread(LPVOID lpParam)
363 {
364         struct ModelessMesssageBoxParam *p = (struct ModelessMesssageBoxParam *)lpParam;
365         
366         // Create the message box dialog.
367         CMessageBoxDialog dlgMessage(nullptr, p->strMessage, _T(""), p->nType);
368         
369         delete p;
370
371         // Display the message box dialog
372         dlgMessage.Create(CMessageBoxDialog::IDD);
373         dlgMessage.ShowWindow(SW_SHOW);
374         dlgMessage.RunModalLoop();
375         dlgMessage.DestroyWindow();
376
377         return 0;
378 }
379
380 /*
381  * Method for retrieving the former result of the message box from the registry.
382  */
383 int CMessageBoxDialog::GetFormerResult()
384 {
385         // Check whether the registry key was already generated.
386         if (m_strRegistryKey.IsEmpty())
387         {
388                 // Create the registry key for this dialog.
389                 m_strRegistryKey = GenerateRegistryKey();
390         }
391
392         // Try to read the former result of the message box from the registry.
393         return AfxGetApp()->GetProfileInt(
394                 REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, (-1));
395 }
396
397 /*
398  * Method for storing the former result of the message box to the registry.
399  */
400 int CMessageBoxDialog::SetFormerResult(int nResult)
401 {
402         int nOldResult = GetFormerResult();
403         // Try to write the former result of the message box to the registry.
404         AfxGetApp()->WriteProfileInt(
405                 REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, nResult);
406         return nOldResult;
407 }
408
409 /*
410  *      Method for displaying the dialog.
411  *
412  *      If the MB_DONT_DISPLAY_AGAIN or MB_DONT_ASK_AGAIN flag is set, this
413  *      method will check, whether a former result for this dialog was stored
414  *      in the registry. If yes, the former result will be returned without
415  *      displaying the dialog. Otherwise the message box will be displayed in
416  *      the normal way.
417  */
418 INT_PTR CMessageBoxDialog::DoModal ( )
419 {
420         // Check whether the result may be retrieved from the registry.
421         if ( ( m_nStyle & MB_DONT_DISPLAY_AGAIN ) ||
422                 ( m_nStyle & MB_DONT_ASK_AGAIN ) )
423         {
424                 // Try to read the former result of the message box from the registry.
425                 int nFormerResult = GetFormerResult();
426
427                 // Check whether a result was retrieved.
428                 if ( nFormerResult != (-1) )
429                 {
430                         // Return the former result without displaying the dialog.
431                         return nFormerResult;
432                 }
433         }
434
435         if (m_nStyle & MB_MODELESS) {
436                 // Show the messsage box dialog asyncronously.
437                 ModelessMesssageBoxParam *pParam = new ModelessMesssageBoxParam();
438                 pParam->strMessage = m_strMessage.c_str();
439                 pParam->nType      = m_nStyle & ~MB_MODELESS;
440                 AfxBeginThread(ModelessMesssageBoxThread, pParam);
441                 return IDOK;
442         }
443
444         // Call the parent method.
445         return __super::DoModal();
446 }
447
448 /*
449  *      Method for closing the dialog.
450  *
451  *      If the MB_DONT_DISPLAY_AGAIN or MB_DONT_ASK_AGAIN flag is set, this
452  *      method will check, one of the checkbox was marked to save the result in
453  *      the registry. If yes, the result of this dialog will be stored in the
454  *      registry.
455  */
456 void CMessageBoxDialog::EndDialog ( int nResult )
457 {
458         // Create a variable for storing the state of the checkbox.
459         bool bDontDisplayAgain = false;
460
461         // Try to access the checkbox control.
462         CWnd* pCheckboxWnd = GetDlgItem(IDCHECKBOX);
463
464         // Check whether the control can be accessed.
465         if ( pCheckboxWnd != nullptr )
466         {
467                 // Check whether the checkbox is checked.
468                 bDontDisplayAgain = 
469                         ( ((CButton*)pCheckboxWnd)->GetCheck() == BST_CHECKED );
470         }
471
472         // Check whether the result may be stored in the registry.
473         if ( ( ( m_nStyle & MB_DONT_DISPLAY_AGAIN ) && bDontDisplayAgain ) ||
474                 ( ( m_nStyle & MB_DONT_ASK_AGAIN ) && bDontDisplayAgain ) )
475         {
476                 SetFormerResult(nResult);
477         }
478         
479         // Call the parent method.
480         __super::EndDialog(nResult);
481 }
482
483 /*
484  *      Method for initializing the dialog.
485  *
486  *      This method is used for initializing the dialog. It will create the
487  *      content of the dialog, which means it will create all controls and will
488  *      size the dialog to fit it's content.
489  */
490 BOOL CMessageBoxDialog::OnInitDialog ( )
491 {
492         // Call the parent method.
493         if ( !__super::OnInitDialog() )
494         {
495                 // Return with an error.
496                 return FALSE;
497         }
498
499         // Set the title of the dialog.
500         SetWindowText(m_strTitle.c_str());
501
502         // Set the help ID of the dialog.
503         SetHelpID(m_nHelp);
504
505         // Parse the style of the message box.
506         ParseStyle();
507
508         // Create the elements of the dialog.
509         m_tooltips.Create(this);
510         CreateIconControl();
511         CreateMessageControl();
512         CreateCheckboxControl();
513         CreateButtonControls();
514
515         // Define the layout of the dialog.
516         DefineLayout();
517
518         // Check whether no sound should be generated.
519         if ( !( m_nStyle & MB_NO_SOUND ) )
520         {
521                 // Do a beep.
522                 MessageBeep(m_nStyle & MB_ICONMASK);
523         }
524
525         // Check whether the window should be system modal.
526         if ( m_nStyle & MB_SYSTEMMODAL )
527         {
528                 // Modify the style of the window.
529                 ModifyStyle(0, DS_SYSMODAL);
530         }
531
532         // Check whether to bring the window to the foreground.
533         if ( m_nStyle & MB_SETFOREGROUND )
534         {
535                 // Bring the window to the foreground.
536                 SetForegroundWindow();
537         }
538
539         // Check whether the window should be the topmost window.
540         if ( m_nStyle & MB_TOPMOST )
541         {
542                 // Modify the style of the window.
543                 ModifyStyleEx(0, WS_EX_TOPMOST);
544         }
545
546         // Check whether an escape button was defined.
547         if ( m_nEscapeButton == IDC_STATIC )
548         {
549                 // Disable the close item from the system menu.
550                 GetSystemMenu(FALSE)->EnableMenuItem(SC_CLOSE, MF_GRAYED);
551         }
552
553         // Check whether a timeout is set.
554         if ( m_nTimeoutSeconds > 0 )
555         {
556                 // Check whether it's a disabled timeout.
557                 if ( m_bTimeoutDisabled )
558                 {
559                         // Run through all created buttons.
560             for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
561                         {
562                                 // Try to retrieve a handle for the button.
563                 CWnd* pButtonWnd = GetDlgItem(iter->nID);
564
565                                 ASSERT(pButtonWnd != nullptr);
566
567                                 // Check whether the handle was retrieved.
568                                 if ( pButtonWnd != nullptr )
569                                 {
570                                         // Disable the button.
571                                         pButtonWnd->EnableWindow(FALSE);
572                                 }
573                         }
574
575                         // Try to retrieve the handle of the checkbox.
576                         CWnd* pCheckboxWnd = GetDlgItem(IDCHECKBOX);
577
578                         // Check whether the checkbox handle was retrieved.
579                         if ( pCheckboxWnd != nullptr )
580                         {
581                                 // Disable the checkbox.
582                                 pCheckboxWnd->EnableWindow(FALSE);
583                         }
584                 }
585
586                 // Install a timer.
587                 m_nTimeoutTimer = SetTimer(MESSAGE_BOX_TIMER, 1000, nullptr);
588         }
589
590         // Check whether a default button was defined.
591         if ( m_nDefaultButton != IDC_STATIC )
592         {
593                 // Set the focus to the default button.
594                 GetDlgItem(m_nDefaultButton)->SetFocus();
595
596                 // Set the default ID of the dialog.
597                 SetDefID(m_nDefaultButton);
598
599                 // Return FALSE to set the focus correctly.
600                 return FALSE;
601         }
602
603         // Everything seems to be done successfully.
604         return TRUE;
605 }
606
607 /*
608  *      Method for handling command messages.
609  *
610  *      This method will handle command messages, which are those messages, which
611  *      are generated, when a user clicks a button of the dialog.
612  */
613 BOOL CMessageBoxDialog::OnCmdMsg ( UINT nID, int nCode, void* pExtra,
614         AFX_CMDHANDLERINFO* pHandlerInfo )
615 {
616         // Check whether it's the help button.
617         if ( ( nID == IDHELP ) && ( nCode == CN_COMMAND ) )
618         {
619                 // Display the help for this message box.
620                 OnHelp();
621
622                 // The message has been processed successfully.
623                 return TRUE;
624         }
625
626         // Check whether the ID of the control element is interesting for us.
627         if ( ( nID != IDC_STATIC ) && ( nID != IDCHECKBOX ) && 
628                 ( nCode == CN_COMMAND ) )
629         {
630                 // End the dialog with the given ID.
631                 EndDialog(nID);
632
633                 // The message has been processed successfully.
634                 return TRUE;
635         }
636
637         // Call the parent method.
638         return __super::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
639 }
640
641 /*
642  *      Method for handling messages before dispatching them.
643  *
644  *      This message will handle message before they get dispatched the normal way
645  *      and will therefore implement the additional behavior of this dialog.
646  */
647 BOOL CMessageBoxDialog::PreTranslateMessage ( MSG* pMsg )
648 {
649         if (pMsg->message == WM_LBUTTONDOWN ||
650                 pMsg->message == WM_LBUTTONUP ||
651                 pMsg->message == WM_MOUSEMOVE)
652         {
653                 m_tooltips.RelayEvent(pMsg);
654         }
655
656         // Check whether it's a key message and whether it's not a disable timeout.
657         if ( pMsg->message == WM_KEYDOWN )
658         {
659                 // Check whether a disabled timeout is running.
660                 if ( m_bTimeoutDisabled && ( m_nTimeoutSeconds > 0 ) )
661                 {
662                         // Stop here and do nothing until the timeout is finished.
663                         return TRUE;
664                 }
665
666                 // Check whether it's the return key.
667                 if ( pMsg->wParam == VK_RETURN )
668                 {
669                         // Try to retrieve the current focus.
670                         CWnd* pFocusWnd = GetFocus();
671
672                         // Check whether a handle was retrieved.
673                         if ( pFocusWnd != nullptr )
674                         {
675                                 // Try to determine the ID of the element.
676                                 int nID = pFocusWnd->GetDlgCtrlID();
677
678                                 // Run through the list of defined buttons.
679                                 for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
680                                 {
681                                         // Check whether the ID is a button.
682                                         if ( iter->nID == static_cast<UINT>(nID) )
683                                         {
684                                                 // Save this ID as the default ID.
685                                                 m_nDefaultButton = nID;
686
687                                                 // Break the loop to save time.
688                                                 break;
689                                         }
690                                 }
691
692                                 // End the dialog with the default command.
693                                 EndDialog(m_nDefaultButton);
694
695                                 // The message has been processed successfully.
696                                 return TRUE;
697                         }
698                 }
699
700                 // Check whether it's the escape key.
701                 if ( ( pMsg->wParam == VK_ESCAPE ) || ( pMsg->wParam == VK_CANCEL ) )
702                 {
703                         // Check whether an escape button was defined.
704                         if ( m_nEscapeButton != IDC_STATIC )
705                         {
706                                 // End the dialog with this ID.
707                                 EndDialog(m_nEscapeButton);
708                         }
709
710                         // The message has been processed successfully.
711                         return TRUE;
712                 }
713         }
714
715         // Call the parent method.
716         return __super::PreTranslateMessage(pMsg);
717 }
718
719 /*
720  *      Method for handling a timer event.
721  *
722  *      When a timeout for the message box is set, this method will perform the
723  *      update of the dialog controls every second.
724  */
725 void CMessageBoxDialog::OnTimer ( UINT_PTR nIDEvent )
726 {
727         // Check whether the event is interesting for us.
728         if ( nIDEvent == MESSAGE_BOX_TIMER )
729         {
730                 // Decrease the remaining seconds.
731                 m_nTimeoutSeconds--;
732
733                 // Check whether the timeout is finished.
734                 if ( m_nTimeoutSeconds == 0 )
735                 {
736                         // Kill the timer for this event and reset the handle.
737                         KillTimer(m_nTimeoutTimer);
738
739                         // Check whether it has been a disabled timeout.
740                         if ( m_bTimeoutDisabled )
741                         {
742                                 // Run through all defined buttons.
743                                 for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
744                                 {
745                                         // Try to retrieve a handle to access the button.
746                                         CWnd* pButtonWnd = GetDlgItem(iter->nID);
747
748                                         ASSERT(pButtonWnd != nullptr);
749
750                                         // Check whether a handle was retrieved.
751                                         if ( pButtonWnd != nullptr )
752                                         {
753                                                 // Enable the button again.
754                                                 pButtonWnd->EnableWindow(TRUE);
755                                         }
756                                 }
757
758                                 // Try to retrieve a handle for the checkbox.
759                                 CWnd* pCheckboxWnd = GetDlgItem(IDCHECKBOX);
760
761                                 // Check whether the checkbox was found.
762                                 if ( pCheckboxWnd != nullptr )
763                                 {
764                                         // Enable the checkbox.
765                                         pCheckboxWnd->EnableWindow(TRUE);
766                                 }
767                         }
768                         else
769                         {
770                                 // End the dialog with the default button.
771                                 EndDialog(m_nDefaultButton);
772                         }
773                 }
774
775                 // Run through the list of defined buttons.
776                 for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
777                 {
778                         // Check whether this button is the default button.
779                         if ( iter->nID == static_cast<UINT>(m_nDefaultButton) )
780                         {
781                                 // Try to load the text for the button.
782                                 String strButtonText = LoadResString(iter->nTitle);
783                                 // Check whether the timeout is finished.
784                                 if ( m_nTimeoutSeconds > 0 )
785                                 {
786                                         // Add the remaining seconds to the text of the button.
787                                         TCHAR szTimeoutSeconds[40];
788                                         wsprintf(szTimeoutSeconds, _T(" = %u"), m_nTimeoutSeconds);
789                                         strButtonText += szTimeoutSeconds;
790                                 }
791                                 // Set the text of the button.
792                                 SetDlgItemText(iter->nID, strButtonText.c_str());
793                         }
794                 }
795         }
796
797         // Call the parent method.
798         __super::OnTimer(nIDEvent);
799 }
800
801 BOOL CMessageBoxDialog::OnEraseBkgnd(CDC* pDC)
802 {
803         CRect rect;
804         GetClientRect(&rect);
805         pDC->FillSolidRect(&rect, ::GetSysColor(COLOR_BTNFACE));
806         rect.bottom = rect.bottom - YDialogUnitToPixel(CY_BUTTON + CY_BORDER * 2);
807         pDC->FillSolidRect(&rect, ::GetSysColor(COLOR_WINDOW));
808         return TRUE;
809 }
810
811 HBRUSH CMessageBoxDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
812 {
813         if (nCtlColor == CTLCOLOR_STATIC)
814         {
815                 pDC->SetBkMode(OPAQUE);
816                 pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
817                 pDC->SetTextColor(m_clrMainInstructionFont);
818                 return static_cast<HBRUSH>(GetSysColorBrush(COLOR_WINDOW));
819         }
820         return __super::OnCtlColor(pDC, pWnd, nCtlColor);
821 }
822
823 //////////////////////////////////////////////////////////////////////////////
824 // Other dialog handling methods.
825
826 /*
827  *      Method for handling window messages.
828  */
829 BOOL CMessageBoxDialog::OnWndMsg ( UINT message, WPARAM wParam, LPARAM lParam,
830         LRESULT* pResult )
831 {
832         // Check whether to close the dialog.
833         if ( message == WM_CLOSE )
834         {
835                 // Check whether a disabled timeout is running.
836                 if ( m_bTimeoutDisabled && ( m_nTimeoutSeconds > 0 ) )
837                 {
838                         // Stop here and do nothing until the timeout is finished.
839                         return TRUE;
840                 }
841
842                 // Check whether an escape button is defined.
843                 if ( m_nEscapeButton != IDC_STATIC )
844                 {
845                         // End the dialog with this command.
846                         EndDialog(m_nEscapeButton);
847                 }
848
849                 // The message was handled successfully.
850                 return TRUE;
851         }
852
853         // Call the parent method.
854         return __super::OnWndMsg(message, wParam, lParam, pResult);
855 }
856
857 BEGIN_MESSAGE_MAP(CMessageBoxDialog, CDialog)
858         ON_WM_TIMER()
859         ON_WM_ERASEBKGND()
860         ON_WM_CTLCOLOR()
861 END_MESSAGE_MAP()
862
863 //////////////////////////////////////////////////////////////////////////////
864 // Helper methods.
865
866 /*
867  *      Method for generating a registry key.
868  *
869  *      This method tries to create a registry key, which will be used for storing
870  *      the result of the message box, if the MB_DONT_DISPLAY_AGAIN or the
871  *      MB_DONT_ASK_AGAIN flag is set.
872  */
873 CString CMessageBoxDialog::GenerateRegistryKey ( )
874 {
875         // Create a string to store the registry key.
876         CString strRegistryKey = _T("");
877
878         // Check whether a help ID is given.
879         if ( m_nHelp != 0 )
880         {
881                 // Simply use the help ID, because we assume, it's unique.
882                 strRegistryKey.Format(_T("%u"), m_nHelp);
883         }
884         else
885         {
886                 // POSSIBLE BUG: The following algorithm for creating a checksum is
887                 // very simple and may not ensure, the registry key is really unique.
888                 // But for now it may be enough.
889
890                 // Create a variable to store the checksum.
891                 int nChecksum = 0;
892
893                 // Run through the message string.
894         for ( String::size_type i = 0; i < m_strMessage.length(); i++ )
895                 {
896                         // Get the char at the given position and add it to the checksum.
897                         nChecksum += static_cast<int>(static_cast<int>(m_strMessage[i]) * i);
898                 }
899
900                 // Convert the checksum to a string.
901                 strRegistryKey.Format(_T("%d"), nChecksum);
902         }
903
904         // Return the registry key.
905         return strRegistryKey;
906 }
907
908 /*
909  *      Method for adding a button to the list of buttons.
910  *
911  *      This method adds a button to the list of buttons, which will be created in
912  *      the dialog, but it will not create the button control itself.
913  */
914 void CMessageBoxDialog::AddButton ( UINT nID, UINT nTitle, bool bIsDefault /*= false*/,
915         bool bIsEscape /*= false*/ )
916 {
917         // Create a new structure to store the button information.
918         MSGBOXBTN bButton = { nID, nTitle };
919
920         // Add the button to the list of buttons.
921     m_aButtons.push_back(bButton);
922
923         // Check whether this button is the default button.
924         if ( bIsDefault )
925         {
926                 // Save the ID of the button as the ID of the default button.
927                 m_nDefaultButton = nID;
928         }
929
930         // Check whether this button is the escape button.
931         if ( bIsEscape )
932         {
933                 // Save the ID of the button as the ID of the escape button.
934                 m_nEscapeButton = nID;
935         }
936 }
937
938 /*
939  *      Method for converting a dialog unit x value to a pixel value.
940  */
941 inline int CMessageBoxDialog::XDialogUnitToPixel ( int x )
942 {
943         // Check whether the dimension of a dialog unit has already been determined.
944         if ( m_sDialogUnit.cx == 0 )
945         {
946                 // Create a rect for mapping it to the dialog rect.
947                 CRect rcDialog(0, 0, CX_DLGUNIT_BASE, CY_DLGUNIT_BASE);
948
949                 // Map the rect to the dialog.
950                 MapDialogRect(rcDialog);
951
952                 // Save the rect.
953                 m_sDialogUnit = rcDialog.Size();
954         }
955
956         // Return the converted value.
957         return ( x * m_sDialogUnit.cx / CX_DLGUNIT_BASE );
958 }
959
960 /*
961  *      Method for converting a dialog unit y value to a pixel value.
962  */
963 inline int CMessageBoxDialog::YDialogUnitToPixel ( int y )
964 {
965         // Check whether the dimension of a dialog unit has already been determined.
966         if ( m_sDialogUnit.cy == 0 )
967         {
968                 // Create a rect for mapping it to the dialog rect.
969                 CRect rcDialog(0, 0, CX_DLGUNIT_BASE, CY_DLGUNIT_BASE);
970
971                 // Map the rect to the dialog.
972                 MapDialogRect(rcDialog);
973
974                 // Save the rect.
975                 m_sDialogUnit = rcDialog.Size();
976         }
977
978         // Return the converted value.
979         return ( y * m_sDialogUnit.cy / CY_DLGUNIT_BASE );
980 }
981
982 /*
983  *      Method for parsing the given style.
984  *
985  *      This method will parse the given style for the message box and will create
986  *      the elements of the dialog box according to it. If you want to add more
987  *      user defined styles, simply modify this method.
988  */
989 void CMessageBoxDialog::ParseStyle ( )
990 {
991         // Switch the style of the buttons.
992         switch ( m_nStyle & MB_TYPEMASK )
993         {
994
995                 case MB_OKCANCEL:
996
997                         // Add two buttons: "Ok" and "Cancel".
998                         AddButton(IDOK, IDS_MESSAGEBOX_OK, true);
999                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, false, true);
1000
1001                         break;
1002
1003                 case MB_ABORTRETRYIGNORE:
1004
1005                         // Add three buttons: "Abort", "Retry" and "Ignore".
1006                         AddButton(IDABORT, IDS_MESSAGEBOX_ABORT, true);
1007                         AddButton(IDRETRY, IDS_MESSAGEBOX_RETRY);
1008                         AddButton(IDIGNORE, IDS_MESSAGEBOX_IGNORE);
1009
1010                         break;
1011
1012                 case MB_YESNOCANCEL:
1013
1014                         // Add three buttons: "Yes", "No" and "Cancel".
1015                         AddButton(IDYES, IDS_MESSAGEBOX_YES, true);
1016
1017                         // Check whether to add a "Yes to all" button.
1018                         if ( m_nStyle & MB_YES_TO_ALL )
1019                         {
1020                                 // Add the additional button.
1021                                 AddButton(IDYESTOALL, IDS_MESSAGEBOX_YES_TO_ALL);
1022                         }
1023
1024                         AddButton(IDNO, IDS_MESSAGEBOX_NO);
1025
1026                         // Check whether to add a "No to all" button.
1027                         if ( m_nStyle & MB_NO_TO_ALL )
1028                         {
1029                                 // Add the additional button.
1030                                 AddButton(IDNOTOALL, IDS_MESSAGEBOX_NO_TO_ALL);
1031                         }
1032
1033                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, false, true);
1034
1035                         break;
1036
1037                 case MB_YESNO:
1038                         
1039                         // Add two buttons: "Yes" and "No".
1040                         AddButton(IDYES, IDS_MESSAGEBOX_YES, true);
1041
1042                         // Check whether to add a "Yes to all" button.
1043                         if ( m_nStyle & MB_YES_TO_ALL )
1044                         {
1045                                 // Add the additional button.
1046                                 AddButton(IDYESTOALL, IDS_MESSAGEBOX_YES_TO_ALL);
1047                         }
1048
1049                         AddButton(IDNO, IDS_MESSAGEBOX_NO);
1050
1051                         // Check whether to add a "No to all" button.
1052                         if ( m_nStyle & MB_NO_TO_ALL )
1053                         {
1054                                 // Add the additional button.
1055                                 AddButton(IDNOTOALL, IDS_MESSAGEBOX_NO_TO_ALL);
1056                         }
1057
1058                         break;
1059
1060                 case MB_RETRYCANCEL:
1061
1062                         // Add two buttons: "Retry" and "Cancel".
1063                         AddButton(IDRETRY, IDS_MESSAGEBOX_RETRY, true);
1064                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, false, true);
1065
1066                         break;
1067
1068                 case MB_CANCELTRYCONTINUE:
1069
1070                         // Add three buttons: "Cancel", "Try again" and "Continue".
1071                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, true, true);
1072                         AddButton(IDTRYAGAIN, IDS_MESSAGEBOX_RETRY);
1073                         AddButton(IDCONTINUE, IDS_MESSAGEBOX_CONTINUE);
1074
1075                         break;
1076
1077                 case MB_CONTINUEABORT:
1078
1079                         // Add two buttons: "Continue" and "Abort".
1080                         AddButton(IDCONTINUE, IDS_MESSAGEBOX_CONTINUE, true);
1081                         AddButton(IDABORT, IDS_MESSAGEBOX_ABORT);
1082
1083                         break;
1084
1085                 case MB_SKIPSKIPALLCANCEL:
1086
1087                         // Add three buttons: "Skip", "Skip all" and "Cancel".
1088                         AddButton(IDSKIP, IDS_MESSAGEBOX_SKIP, true);
1089                         AddButton(IDSKIPALL, IDS_MESSAGEBOX_SKIPALL);
1090                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, false, true);
1091
1092                         break;
1093
1094                 case MB_IGNOREIGNOREALLCANCEL:
1095
1096                         // Add three buttons: "Ignore", "Ignore all" and "Cancel".
1097                         AddButton(IDIGNORE, IDS_MESSAGEBOX_IGNORE, true);
1098                         AddButton(IDIGNOREALL, IDS_MESSAGEBOX_IGNOREALL);
1099                         AddButton(IDCANCEL, IDS_MESSAGEBOX_CANCEL, false, true);
1100
1101                         break;
1102
1103                 default:
1104                 case MB_OK:
1105
1106                         // Add just one button: "Ok".
1107                         AddButton(IDOK, IDS_MESSAGEBOX_OK, true, true);
1108
1109                         break;
1110
1111         }
1112
1113         // Check whether to add a help button.
1114         if ( m_nStyle & MB_HELP )
1115         {
1116                 // Add the help button.
1117                 AddButton(IDHELP, IDS_MESSAGEBOX_HELP);
1118         }
1119
1120         // Check whether a default button was defined.
1121         if ( m_nStyle & MB_DEFMASK )
1122         {
1123                 // Create a variable to store the index of the default button.
1124         vector<MSGBOXBTN>::size_type nDefaultIndex = 0;
1125
1126                 // Switch the default button.
1127                 switch ( m_nStyle & MB_DEFMASK )
1128                 {
1129
1130                         case MB_DEFBUTTON1:
1131
1132                                 // Set the index of the default button.
1133                                 nDefaultIndex = 0;
1134
1135                                 break;
1136
1137                         case MB_DEFBUTTON2:
1138
1139                                 // Set the index of the default button.
1140                                 nDefaultIndex = 1;
1141
1142                                 break;
1143
1144                         case MB_DEFBUTTON3:
1145
1146                                 // Set the index of the default button.
1147                                 nDefaultIndex = 2;
1148
1149                                 break;
1150
1151                         case MB_DEFBUTTON4:
1152
1153                                 // Set the index of the default button.
1154                                 nDefaultIndex = 3;
1155
1156                                 break;
1157
1158                         case MB_DEFBUTTON5:
1159
1160                                 // Set the index of the default button.
1161                                 nDefaultIndex = 4;
1162
1163                                 break;
1164
1165                         case MB_DEFBUTTON6:
1166
1167                                 // Set the index of the default button.
1168                                 nDefaultIndex = 5;
1169
1170                                 break;
1171
1172                 }
1173
1174                 // Check whether enough buttons are available.
1175                 if ( m_aButtons.size() >= ( nDefaultIndex + 1 ) )
1176                 {
1177                         // Set the new default button.
1178                         m_nDefaultButton = m_aButtons[nDefaultIndex].nID;
1179                 }
1180         }
1181
1182         // Check whether an icon was specified.
1183         if ( ( m_nStyle & MB_ICONMASK ) && ( m_hIcon == nullptr ) )
1184         {
1185                 // Switch the icon.
1186                 switch ( m_nStyle & MB_ICONMASK )
1187                 {
1188
1189                         case MB_ICONEXCLAMATION:
1190
1191                                 // Load the icon with the exclamation mark.
1192                                 m_hIcon = AfxGetApp()->LoadStandardIcon(IDI_EXCLAMATION);
1193
1194                                 break;
1195
1196                         case MB_ICONHAND:
1197
1198                                 // Load the icon with the error symbol.
1199                                 m_hIcon = AfxGetApp()->LoadStandardIcon(IDI_HAND);
1200
1201                                 break;
1202
1203                         case MB_ICONQUESTION:
1204
1205                                 // Load the icon with the question mark.
1206                                 m_hIcon = AfxGetApp()->LoadStandardIcon(IDI_QUESTION);
1207
1208                                 break;
1209
1210                         case MB_ICONASTERISK:
1211
1212                                 // Load the icon with the information symbol.
1213                                 m_hIcon = AfxGetApp()->LoadStandardIcon(IDI_ASTERISK);
1214
1215                                 break;
1216
1217                 }
1218         }
1219 }
1220
1221 /*
1222  *      Method for creating the icon control.
1223  *
1224  *      This method will check whether the handle for an icon was defined and if
1225  *      yes it will create an control in the dialog to display that icon.
1226  */
1227 void CMessageBoxDialog::CreateIconControl ( )
1228 {
1229         // Check whether an icon was defined.
1230         if ( m_hIcon != nullptr )
1231         {
1232                 // Create a structure to read information about the icon.
1233                 ICONINFO iiIconInfo;
1234
1235                 // Retrieve information about the icon.
1236                 GetIconInfo(m_hIcon, &iiIconInfo);
1237
1238                 ASSERT(iiIconInfo.fIcon);
1239
1240                 // Create a handle to access the bitmap information of the icon.
1241                 BITMAP bmIcon;
1242
1243                 // Retrieve the bitmap information of the icon.
1244                 GetObject((HGDIOBJ)iiIconInfo.hbmColor, sizeof(bmIcon), &bmIcon);
1245
1246                 // Save the size of the icon.
1247                 m_sIcon.cx = bmIcon.bmWidth;
1248                 m_sIcon.cy = bmIcon.bmHeight;
1249
1250                 // Create a dummy rect for the icon control.
1251                 CRect rcDummy;
1252
1253                 // Create the control for the icon.
1254                 m_stcIcon.Create(nullptr, WS_CHILD | WS_VISIBLE | WS_DISABLED | SS_ICON,
1255                         rcDummy, this, (UINT)IDC_STATIC);
1256
1257                 // Set the icon of the control.
1258                 m_stcIcon.SetIcon(m_hIcon);
1259         }
1260 }
1261
1262 /*
1263  *      Method for creating the text control.
1264  *
1265  *      This method create the control displaying the text of the message for the
1266  *      message box. It will also try to determine the size required for the
1267  *      message.
1268  */
1269 void CMessageBoxDialog::CreateMessageControl ( )
1270 {
1271         ASSERT(!m_strMessage.empty());
1272
1273         // Create a DC for accessing the display driver.
1274         CDC dcDisplay;
1275         dcDisplay.CreateDC(_T("DISPLAY"), nullptr, nullptr, nullptr);
1276
1277         // Select the new font and store the old one.
1278         CFont* pOldFont = dcDisplay.SelectObject(&m_fontMainInstruction);
1279
1280         // Define the maximum width of the message.
1281         int nMaxWidth = ( GetSystemMetrics(SM_CXSCREEN) / 2 ) + 100;
1282
1283         // Check whether an icon is displayed.
1284         if ( m_hIcon != nullptr )
1285         {
1286                 // Decrease the maximum width.
1287                 nMaxWidth -= m_sIcon.cx + 2 * XDialogUnitToPixel(CX_BORDER);
1288         }
1289
1290         // Create a rect with the maximum width.
1291         CRect rcMessage(0, 0, nMaxWidth, nMaxWidth);
1292
1293         // Draw the text and retrieve the size of the text.
1294         dcDisplay.DrawText(m_strMessage.c_str(), rcMessage, DT_LEFT | DT_NOPREFIX | 
1295                 DT_WORDBREAK | DT_EXPANDTABS | DT_CALCRECT);
1296
1297         // Save the size required for the message.
1298         m_sMessage = rcMessage.Size();
1299
1300         // Select the old font again.
1301         dcDisplay.SelectObject(pOldFont);
1302
1303         // Create a dummy rect for the control.
1304         CRect rcDummy;
1305
1306         // Create a variable with the style of the control.
1307         DWORD dwStyle = WS_CHILD | WS_VISIBLE;
1308
1309         // Check whether the text should be right aligned.
1310         if ( m_nStyle & MB_RIGHT )
1311         {
1312                 // Align the text to the right.
1313                 dwStyle |= SS_RIGHT;
1314         }
1315         else
1316         {
1317                 // Align the text to the left.
1318                 dwStyle |= SS_LEFT;
1319         }
1320
1321         // Create the static control for the message.
1322         String strMessage = m_strMessage;
1323         strutils::replace(strMessage, _T("&"), _T("&&"));
1324         m_stcMessage.Create(strMessage.c_str(), dwStyle, rcDummy, this,
1325                 (UINT)IDC_STATIC);
1326
1327         // Check whether the text will be read from right to left.
1328         if ( m_nStyle & MB_RTLREADING )
1329         {
1330                 // Change the extended style of the control.
1331                 m_stcMessage.ModifyStyleEx(0, WS_EX_RTLREADING);
1332         }
1333
1334         // Set the font of the dialog.
1335         m_stcMessage.SetFont(&m_fontMainInstruction);
1336 }
1337
1338 /*
1339  *      Method for creating the checkbox control.
1340  *
1341  *      If the user either specified the MB_DONT_DISPLAY_AGAIN or
1342  *      MB_DONT_ASK_AGAIN flag, this method will create a checkbox in the dialog
1343  *      for enabling the user to disable this dialog in the future.
1344  */
1345 void CMessageBoxDialog::CreateCheckboxControl ( )
1346 {
1347         // Check whether a checkbox is required.
1348         if ( ( m_nStyle & MB_DONT_DISPLAY_AGAIN ) ||
1349                 ( m_nStyle & MB_DONT_ASK_AGAIN ) )
1350         {
1351                 // Create a variable for storing the title of the checkbox.
1352                 String strCheckboxTitle;
1353
1354                 // Check which style is used.
1355                 if ( m_nStyle & MB_DONT_DISPLAY_AGAIN )
1356                 {
1357                         // Load the string for the checkbox.
1358                         strCheckboxTitle = LoadResString(IDS_MESSAGEBOX_DONT_DISPLAY_AGAIN);
1359                 }
1360                 else
1361                 {
1362                         // Check which style is used.
1363                         if ( m_nStyle & MB_DONT_ASK_AGAIN )
1364                         {
1365                                 // Load the string for the checkbox.
1366                                 strCheckboxTitle = LoadResString(IDS_MESSAGEBOX_DONT_ASK_AGAIN);
1367                         }
1368                 }
1369
1370                 ASSERT(!strCheckboxTitle.empty());
1371
1372                 // Create a handle to access the DC of the dialog.
1373                 CClientDC dc(this);
1374
1375                 // Retrieve the font for this dialog and select it.
1376                 CFont* pWndFont = &m_font;
1377                 CFont* pOldFont = dc.SelectObject(pWndFont);
1378
1379                 // Retrieve the size of the text.
1380                 m_sCheckbox = dc.GetTextExtent(strCheckboxTitle.c_str(), static_cast<int>(strCheckboxTitle.length()));
1381
1382                 // Add the additional value to the width of the checkbox.
1383                 m_sCheckbox.cx += XDialogUnitToPixel(CX_CHECKBOX_ADDON);
1384
1385                 // Select the old font again.
1386                 dc.SelectObject(pOldFont);
1387
1388                 // Create a dummy rect for the checkbox.
1389                 CRect rcDummy;
1390
1391                 // Create a handle for the checkbox.
1392                 CButton btnCheckbox;
1393
1394                 // Create the checkbox.
1395                 btnCheckbox.Create(strCheckboxTitle.c_str(), WS_CHILD | WS_VISIBLE | 
1396                         WS_TABSTOP | BS_AUTOCHECKBOX, rcDummy, this, IDCHECKBOX);
1397
1398                 // Check whether the checkbox should be marked checked at startup.
1399                 if ( m_nStyle & MB_DEFAULT_CHECKED )
1400                 {
1401                         // Mark the checkbox.
1402                         btnCheckbox.SetCheck(BST_CHECKED);
1403                 }
1404
1405                 // Set the font of the control.
1406                 btnCheckbox.SetFont(pWndFont);
1407
1408                 // Add a tooltip to the checkbox
1409                 m_tooltips.AddTool(&btnCheckbox, LoadResString(IDS_MESSAGEBOX_CHECKBOX_TOOLTIP).c_str());
1410
1411                 // Remove the subclassing again.
1412                 btnCheckbox.UnsubclassWindow();
1413         }
1414 }
1415
1416 /*
1417  *      Method for creating the button controls.
1418  *
1419  *      According to the list of buttons, which should be displayed in this
1420  *      message box, this method will create them and add them to the dialog.
1421  */
1422 void CMessageBoxDialog::CreateButtonControls ( )
1423 {
1424         // Initialize the control with the size of the button.
1425         m_sButton = CSize(XDialogUnitToPixel(CX_BUTTON),
1426                 YDialogUnitToPixel(CY_BUTTON));
1427
1428         // Create a handle to access the DC of the dialog.
1429         CClientDC dc(this);
1430
1431         // Retrieve the font for this dialog and select it.
1432         CFont* pWndFont = &m_font;
1433         CFont* pOldFont = dc.SelectObject(pWndFont);
1434
1435         // Create a dummy rect.
1436         CRect rcDummy;
1437
1438         // Run through all buttons defined in the list of the buttons.
1439         for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
1440         {
1441                 // Create a string and load the title of the button.
1442                 String strButtonText = LoadResString(iter->nTitle);
1443                 // Check whether there's a timeout set.
1444                 if ( m_nTimeoutSeconds > 0 )
1445                 {
1446                         // Add the remaining seconds to the text of the button.
1447                         TCHAR szTimeoutSeconds[40];
1448                         wsprintf(szTimeoutSeconds, _T(" = %u"), m_nTimeoutSeconds);
1449                         strButtonText += szTimeoutSeconds;
1450                 }
1451
1452                 // Retrieve the size of the text.
1453                 CSize sButtonText = dc.GetTextExtent(strButtonText.c_str(), static_cast<int>(strButtonText.length()));
1454
1455                 // Resize the button.
1456                 m_sButton.cx = max(m_sButton.cx, sButtonText.cx);
1457                 m_sButton.cy = max(m_sButton.cy, sButtonText.cy);
1458
1459                 // Create a new handle for creating a button control.
1460                 CButton btnControl;
1461
1462                 // Create the button.
1463                 btnControl.Create(strButtonText.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP,
1464                         rcDummy, this, iter->nID);
1465
1466                 // Set the font of the control.
1467                 btnControl.SetFont(pWndFont);
1468
1469                 // Remove the subclassing again.
1470                 btnControl.UnsubclassWindow();
1471         }
1472
1473         // Add margins to the button size.
1474         m_sButton.cx += 2 * XDialogUnitToPixel(CX_BUTTON_BORDER);
1475         m_sButton.cy += 2 * XDialogUnitToPixel(CY_BUTTON_BORDER);
1476
1477         // Select the old font again.
1478         dc.SelectObject(pOldFont);
1479 }
1480
1481 /*
1482  *      Method for defining the layout of the dialog.
1483  *
1484  *      This method will define the actual layout of the dialog. This layout is
1485  *      based on the created controls for the dialog.
1486  */
1487 void CMessageBoxDialog::DefineLayout ( )
1488 {
1489         // Create a variable for storing the size of the dialog.
1490         CSize sClient = CSize(4 * XDialogUnitToPixel(CX_BORDER),
1491                 3 * YDialogUnitToPixel(CY_BORDER));
1492
1493         // Create a variable to store the left position for a control element.
1494         int nXPosition = XDialogUnitToPixel(CX_BORDER) * 2;
1495         int nYPosition = YDialogUnitToPixel(CY_BORDER) * 2;
1496
1497         // Check whether an icon is defined.
1498         if ( m_hIcon != nullptr )
1499         {
1500                 // Move the icon control.
1501                 m_stcIcon.MoveWindow(XDialogUnitToPixel(CX_BORDER) * 2, 
1502                         YDialogUnitToPixel(CY_BORDER) * 2, m_sIcon.cx, m_sIcon.cy);
1503
1504                 // Add the size of the icon to the size of the dialog.
1505                 sClient.cx += m_sIcon.cx + XDialogUnitToPixel(CX_BORDER);
1506                 sClient.cy += m_sIcon.cy + YDialogUnitToPixel(CY_BORDER);
1507
1508                 // Increase the x position for following control elements.
1509                 nXPosition += m_sIcon.cx + XDialogUnitToPixel(CX_BORDER);
1510         }
1511
1512         // Change the size of the dialog according to the size of the message.
1513         sClient.cx += m_sMessage.cx + XDialogUnitToPixel(CX_BORDER);
1514         sClient.cy = max(sClient.cy, m_sMessage.cy + 2 * 
1515                 YDialogUnitToPixel(CY_BORDER) + YDialogUnitToPixel(CY_BORDER / 2));
1516
1517         // Set the position of the message text.
1518         if (m_sMessage.cy < m_sIcon.cy)
1519                 nYPosition += (m_sIcon.cy - m_sMessage.cy) / 2;
1520         m_stcMessage.MoveWindow(nXPosition, nYPosition, m_sMessage.cx,
1521                 m_sMessage.cy);
1522
1523         // Define the new y position.
1524         nYPosition += m_sMessage.cy + YDialogUnitToPixel(CY_BORDER) +
1525                 YDialogUnitToPixel(CY_BORDER / 2);
1526
1527         // Check whether an checkbox is defined.
1528         if ( ( m_nStyle & MB_DONT_DISPLAY_AGAIN ) ||
1529                 ( m_nStyle & MB_DONT_ASK_AGAIN ) )
1530         {
1531                 // Try to determine the control element for the checkbox.
1532                 CWnd* pCheckboxWnd = GetDlgItem(IDCHECKBOX);
1533
1534                 ASSERT(pCheckboxWnd != nullptr);
1535
1536                 // Check whether the control was retrieved.
1537                 if ( pCheckboxWnd != nullptr )
1538                 {
1539                         // Move the checkbox window.
1540                         pCheckboxWnd->MoveWindow(nXPosition, nYPosition, m_sCheckbox.cx,
1541                                 m_sCheckbox.cy);
1542
1543                         // Resize the dialog if necessary.
1544                         sClient.cx = max(sClient.cx, nXPosition + m_sCheckbox.cx +
1545                                 XDialogUnitToPixel(CX_BORDER));
1546                         sClient.cy = max(sClient.cy, nYPosition + m_sCheckbox.cy +
1547                                 YDialogUnitToPixel(CY_BORDER));
1548                 }
1549         }
1550
1551         // Calculate the width of the buttons.
1552         int cxButtons = static_cast<int>(
1553                 ( m_aButtons.size() - 1 ) * XDialogUnitToPixel(CX_BUTTON_SPACE) +
1554                 m_aButtons.size() * m_sButton.cx);
1555         int cyButtons = m_sButton.cy;
1556
1557         // Add the size of the buttons to the dialog.
1558         sClient.cx = max(sClient.cx, 2 * XDialogUnitToPixel(CX_BORDER) + cxButtons);
1559         sClient.cy += cyButtons + YDialogUnitToPixel(CY_BORDER) * 2;
1560
1561         // Calculate the start y position for the buttons.
1562         int nXButtonPosition = ( sClient.cx - cxButtons ) / 2;
1563         int nYButtonPosition = sClient.cy - YDialogUnitToPixel(CY_BORDER) - 
1564                 m_sButton.cy;
1565
1566         // Check whether the buttons should be right aligned.
1567         if ( m_nStyle & MB_RIGHT_ALIGN )
1568         {
1569                 // Right align the buttons.
1570                 nXButtonPosition = sClient.cx - cxButtons - 
1571                         XDialogUnitToPixel(CX_BORDER);
1572         }
1573
1574         // Run through all buttons.
1575         for (vector<MSGBOXBTN>::iterator iter = m_aButtons.begin(); iter != m_aButtons.end(); ++iter)
1576         {
1577                 // Try to retrieve the handle to access the button.
1578                 CWnd* pButton = GetDlgItem(iter->nID);
1579
1580                 ASSERT(pButton != nullptr);
1581
1582                 // Check whether the handle was retrieved successfully.
1583                 if ( pButton != nullptr )
1584                 {
1585                         // Move the button.
1586                         pButton->MoveWindow(nXButtonPosition, nYButtonPosition, 
1587                                 m_sButton.cx, m_sButton.cy);
1588
1589                         // Set the new x position of the next button.
1590                         nXButtonPosition += m_sButton.cx + 
1591                                 XDialogUnitToPixel(CX_BUTTON_SPACE);
1592                 }
1593         }
1594
1595         // Set the dimensions of the dialog.
1596         CRect rcClient(0, 0, sClient.cx, sClient.cy);
1597
1598         // Calculate the window rect.
1599         CalcWindowRect(rcClient);
1600
1601         // Move the window.
1602         MoveWindow(rcClient);
1603
1604         // Center the window.
1605         CenterWindow();
1606 }