1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2016, 2018-2021 - TortoiseGit
4 // Copyright (C) 2003-2008,2010 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "resource.h" //if you defined some IDS_MSGBOX_xxxx this include is needed!
22 #include "messagebox.h"
23 #include "ClipboardHelper.h"
24 #include "SmartHandle.h"
25 #include <afxtaskdialog.h>
27 #include "LoadIconEx.h"
29 #define BTN_OFFSET 100 // use an offset in order to not interfere with IDYES and so on...
31 CMessageBox::CMessageBox()
38 , m_bDestroyIcon(FALSE
)
43 SecureZeroMemory(&m_LogFont
, sizeof(LOGFONT
));
46 CMessageBox::~CMessageBox()
49 ::DestroyIcon(m_hIcon
);
52 static HWND
GetMainHWND(HWND hWnd
)
54 if (!hWnd
|| !IsWindow(hWnd
))
55 return CWnd::GetSafeOwner_(nullptr, nullptr);
59 UINT
CMessageBox::ShowCheck(HWND hWnd
, UINT nMessage
, UINT nCaption
, int nDef
, LPCWSTR icon
, UINT nButton1
, UINT nButton2
, UINT nButton3
, LPCWSTR lpRegistry
, UINT nCheckMessage
/* = nullptr*/, BOOL
* bChecked
)
67 sButton1
.LoadString(nButton1
);
68 sButton2
.LoadString(nButton2
);
69 sButton3
.LoadString(nButton3
);
70 sMessage
.LoadString(nMessage
);
71 sCaption
.LoadString(nCaption
);
72 nCheckMsg
.LoadString(nCheckMessage
);
73 return CMessageBox::ShowCheck(hWnd
, sMessage
, sCaption
, nDef
, icon
, sButton1
, sButton2
, sButton3
, lpRegistry
, nCheckMsg
, bChecked
);
76 UINT
CMessageBox::ShowCheck(HWND hWnd
, LPCWSTR lpMessage
, LPCWSTR lpCaption
, int nDef
, LPCWSTR icon
, LPCWSTR lpButton1
, LPCWSTR lpButton2
, LPCWSTR lpButton3
, LPCWSTR lpRegistry
, LPCWSTR lpCheckMessage
/* = nullptr*/, BOOL
* bChecked
)
78 //check the registry if we have to show the box or just return with the last used return value
79 //this would be the case if the user pressed "do not show again".
83 #ifdef XMESSAGEBOX_APPREGPATH
84 path
= XMESSAGEBOX_APPREGPATH
;
86 path
= "Software\\TortoiseGit\\";
87 path
+= AfxGetApp()->m_pszProfileName
;
89 if (lpRegistry
&& *lpRegistry
&& RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_EXECUTE
, &hKey
)==ERROR_SUCCESS
)
91 DWORD size
= sizeof(dwRetVal
);
93 if (RegQueryValueEx(hKey
, lpRegistry
, nullptr, &type
, reinterpret_cast<BYTE
*>(&dwRetVal
), &size
) == ERROR_SUCCESS
)
95 ASSERT(type
==REG_DWORD
);
97 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Using stored value %ld for \"%s\"\n", dwRetVal
, lpMessage
);
98 return static_cast<UINT
>(dwRetVal
); //return with the last saved value
106 if (CTaskDialog::IsSupported())
108 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_USE_COMMAND_LINKS
| TDF_POSITION_RELATIVE_TO_WINDOW
);
109 taskdlg
.AddCommandControl(BTN_OFFSET
+ 1, lpButton1
);
110 if (lpButton2
&& *lpButton2
)
111 taskdlg
.AddCommandControl(BTN_OFFSET
+ 2, lpButton2
);
112 if (lpButton3
&& *lpButton3
)
113 taskdlg
.AddCommandControl(BTN_OFFSET
+ 3, lpButton3
);
114 taskdlg
.SetDefaultCommandControl(BTN_OFFSET
+ nDef
);
115 taskdlg
.SetMainIcon(icon
);
118 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
119 taskdlg
.SetVerificationCheckboxText(L
"do not show again");
122 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
123 taskdlg
.SetVerificationCheckboxText(m_i18l
);
127 taskdlg
.SetVerificationCheckboxText(lpCheckMessage
);
128 int result
= static_cast<int>(taskdlg
.DoModal(GetMainHWND(hWnd
))) - BTN_OFFSET
;
130 *bChecked
= taskdlg
.GetVerificationCheckboxState();
131 if (lpRegistry
&& *lpRegistry
&& taskdlg
.GetVerificationCheckboxState())
132 SetRegistryValue(lpRegistry
, result
);
138 box
.m_bShowCheck
= TRUE
;
139 box
.m_sRegistryValue
= lpRegistry
;
142 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
143 box
.m_sCheckbox
= L
"do not show again";
146 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
147 box
.m_sCheckbox
= m_i18l
;
151 box
.m_sCheckbox
= lpCheckMessage
;
152 box
.m_bChecked
= bChecked
? *bChecked
: FALSE
;
153 box
.m_sButton1
= lpButton1
;
154 box
.m_sButton2
= lpButton2
;
155 box
.m_sButton3
= lpButton3
;
156 box
.m_hIcon
= LoadIconEx(AfxGetResourceHandle(), icon
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
158 box
.m_hIcon
= LoadIconEx(nullptr, icon
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
160 box
.m_bDestroyIcon
= TRUE
;
163 int result
= box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, nDef
);
165 *bChecked
= box
.m_bChecked
;
169 UINT
CMessageBox::Show(HWND hWnd
, LPCWSTR lpMessage
, LPCWSTR lpCaption
, int nDef
, LPCWSTR icon
, LPCWSTR lpButton1
, LPCWSTR lpButton2
/* = nullptr*/, LPCWSTR lpButton3
/* = nullptr*/)
171 if (CTaskDialog::IsSupported())
173 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_USE_COMMAND_LINKS
| TDF_POSITION_RELATIVE_TO_WINDOW
);
174 taskdlg
.AddCommandControl(BTN_OFFSET
+ 1, lpButton1
);
175 if (lpButton2
&& *lpButton2
)
176 taskdlg
.AddCommandControl(BTN_OFFSET
+ 2, lpButton2
);
177 if (lpButton3
&& *lpButton3
)
178 taskdlg
.AddCommandControl(BTN_OFFSET
+ 3, lpButton3
);
179 taskdlg
.SetDefaultCommandControl(BTN_OFFSET
+ nDef
);
180 taskdlg
.SetMainIcon(icon
);
181 return static_cast<UINT
>(taskdlg
.DoModal(GetMainHWND(hWnd
))) - BTN_OFFSET
;
184 box
.m_sButton1
= lpButton1
;
185 box
.m_sButton2
= lpButton2
;
186 box
.m_sButton3
= lpButton3
;
187 box
.m_hIcon
= LoadIconEx(AfxGetResourceHandle(), icon
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
189 box
.m_hIcon
= LoadIconEx(nullptr, icon
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
191 box
.m_bDestroyIcon
= TRUE
;
194 return box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, nDef
);
197 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, int nDef
, LPCWSTR icon
, UINT nButton1
, UINT nButton2
, UINT nButton3
)
204 sButton1
.LoadString(nButton1
);
205 sButton2
.LoadString(nButton2
);
206 sButton3
.LoadString(nButton3
);
207 sMessage
.LoadString(nMessage
);
208 sCaption
.LoadString(nCaption
);
209 return CMessageBox::Show(hWnd
, sMessage
, sCaption
, nDef
, icon
, sButton1
, sButton2
, sButton3
);
212 UINT
CMessageBox::ShowCheck(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, LPCWSTR lpRegistry
, UINT nCheckMessage
, BOOL
* bChecked
)
217 sMessage
.LoadString(nMessage
);
218 sCaption
.LoadString(nCaption
);
219 sCheckMsg
.LoadString(nCheckMessage
);
220 return CMessageBox::ShowCheck(hWnd
, sMessage
, sCaption
, uType
, lpRegistry
, sCheckMsg
, bChecked
);
223 UINT
CMessageBox::ShowCheck(HWND hWnd
, LPCWSTR lpMessage
, LPCWSTR lpCaption
, UINT uType
, LPCWSTR lpRegistry
, LPCWSTR lpCheckMessage
, BOOL
* bChecked
)
225 //check the registry if we have to show the box or just return with the last used return value
226 //this would be the case if the user pressed "do not show again".
230 #ifdef XMESSAGEBOX_APPREGPATH
231 path
= XMESSAGEBOX_APPREGPATH
;
233 path
= "Software\\TortoiseGit\\";
234 path
+= AfxGetApp()->m_pszProfileName
;
236 if (lpRegistry
&& *lpRegistry
&& RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_EXECUTE
, &hKey
)==ERROR_SUCCESS
)
238 DWORD size
= sizeof(dwRetVal
);
240 if (RegQueryValueEx(hKey
, lpRegistry
, nullptr, &type
, reinterpret_cast<BYTE
*>(&dwRetVal
), &size
) == ERROR_SUCCESS
)
242 ASSERT(type
==REG_DWORD
);
244 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Using stored value %ld for \"%s\"\n", dwRetVal
, lpMessage
);
245 return static_cast<UINT
>(dwRetVal
); // return with the last saved value
252 if (CTaskDialog::IsSupported() && !(uType
& MB_HELP
) && !((uType
& 0xf) == MB_ABORTRETRYIGNORE
) && !((uType
& 0xf) == MB_CANCELTRYCONTINUE
))
254 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_POSITION_RELATIVE_TO_WINDOW
);
256 switch (uType
& 0xf0)
258 case MB_ICONEXCLAMATION
:
259 taskdlg
.SetMainIcon(IDI_EXCLAMATION
);
261 case MB_ICONASTERISK
:
262 taskdlg
.SetMainIcon(IDI_ASTERISK
);
264 case MB_ICONQUESTION
:
265 taskdlg
.SetMainIcon(IDI_QUESTION
);
268 taskdlg
.SetMainIcon(IDI_HAND
);
271 // set up the buttons
274 case MB_ABORTRETRYIGNORE
:
277 case MB_CANCELTRYCONTINUE
:
281 taskdlg
.SetCommonButtons(TDCBF_OK_BUTTON
| TDCBF_CANCEL_BUTTON
);
282 switch (uType
& 0xf00)
285 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
290 taskdlg
.SetCommonButtons(TDCBF_RETRY_BUTTON
| TDCBF_CANCEL_BUTTON
);
291 switch (uType
& 0xf00)
294 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
299 taskdlg
.SetCommonButtons(TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
);
300 switch (uType
& 0xf00)
303 taskdlg
.SetDefaultCommandControl(IDNO
);
308 taskdlg
.SetCommonButtons(TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
| TDCBF_CANCEL_BUTTON
);
309 switch (uType
& 0xf00)
312 taskdlg
.SetDefaultCommandControl(IDNO
);
315 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
321 taskdlg
.SetCommonButtons(TDCBF_OK_BUTTON
);
326 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
327 taskdlg
.SetVerificationCheckboxText(L
"do not show again");
330 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
331 taskdlg
.SetVerificationCheckboxText(m_i18l
);
335 taskdlg
.SetVerificationCheckboxText(lpCheckMessage
);
336 int result
= static_cast<int>(taskdlg
.DoModal(GetMainHWND(hWnd
)));
338 *bChecked
= taskdlg
.GetVerificationCheckboxState();
339 if (lpRegistry
&& *lpRegistry
&& taskdlg
.GetVerificationCheckboxState())
340 SetRegistryValue(lpRegistry
, result
);
346 box
.m_bShowCheck
= TRUE
;
347 box
.m_sRegistryValue
= lpRegistry
;
350 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
351 box
.m_sCheckbox
= L
"do not show again";
354 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
355 box
.m_sCheckbox
= m_i18l
;
359 box
.m_sCheckbox
= lpCheckMessage
;
360 box
.m_bChecked
= bChecked
? *bChecked
: FALSE
;
363 int result
= box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, box
.FillBoxStandard(uType
));
365 *bChecked
= box
.m_bChecked
;
369 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, LPCWSTR sHelpPath
)
373 sMessage
.LoadString(nMessage
);
374 sCaption
.LoadString(nCaption
);
375 return CMessageBox::Show(hWnd
, sMessage
, sCaption
, uType
, sHelpPath
);
378 UINT
CMessageBox::Show(HWND hWnd
, LPCWSTR lpMessage
, LPCWSTR lpCaption
, UINT uType
, LPCWSTR sHelpPath
)
385 box
.SetHelpPath(sHelpPath
);
386 return box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, box
.FillBoxStandard(uType
));
389 return ::MessageBox(GetMainHWND(hWnd
), lpMessage
, lpCaption
, uType
);
392 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, UINT nHelpID
)
396 sMessage
.LoadString(nMessage
);
397 sCaption
.LoadString(nCaption
);
404 box
.SetHelpID(nHelpID
);
405 return box
.GoModal(CWnd::FromHandle(hWnd
), sCaption
, sMessage
, box
.FillBoxStandard(uType
));
408 return ::MessageBox(GetMainHWND(hWnd
), sMessage
, sCaption
, uType
);
411 bool CMessageBox::RemoveRegistryKey(LPCWSTR lpRegistry
)
415 #ifdef XMESSAGEBOX_APPREGPATH
416 path
= XMESSAGEBOX_APPREGPATH
;
418 path
= "Software\\TortoiseGit\\";
419 path
+= AfxGetApp()->m_pszProfileName
;
421 if (RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_WRITE
, &hKey
) == ERROR_SUCCESS
)
423 bool ret
= !!RegDeleteValue(hKey
, lpRegistry
);
430 int CMessageBox::FillBoxStandard(UINT uType
)
434 m_uCancelRet
= IDCANCEL
;
435 //load the icons according to uType
436 switch (uType
& 0xf0)
438 case MB_ICONEXCLAMATION
:
439 m_hIcon
= LoadIconEx(nullptr, IDI_EXCLAMATION
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
440 ::MessageBeep(MB_ICONEXCLAMATION
);
442 case MB_ICONASTERISK
:
443 m_hIcon
= LoadIconEx(nullptr, IDI_ASTERISK
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
444 ::MessageBeep(MB_ICONASTERISK
);
446 case MB_ICONQUESTION
:
447 m_hIcon
= LoadIconEx(nullptr, IDI_QUESTION
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
448 ::MessageBeep(MB_ICONQUESTION
);
451 m_hIcon
= LoadIconEx(nullptr, IDI_HAND
, GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
452 ::MessageBeep(MB_ICONHAND
);
455 //set up the button texts
458 case MB_ABORTRETRYIGNORE
:
459 #ifndef IDS_MSGBOX_ABORT
460 m_sButton1
= "&Abort";
462 m_i18l
.LoadString(IDS_MSGBOX_ABORT
);
465 m_uButton1Ret
= IDABORT
;
466 #ifndef IDS_MSGBOX_RETRY
467 m_sButton2
= "&Retry";
469 m_i18l
.LoadString(IDS_MSGBOX_RETRY
);
472 m_uButton2Ret
= IDRETRY
;
473 #ifndef IDS_MSGBOX_IGNORE
474 m_sButton3
= "&Ignore";
476 m_i18l
.LoadString(IDS_MSGBOX_IGNORE
);
479 m_uButton3Ret
= IDIGNORE
;
481 case MB_CANCELTRYCONTINUE
:
482 #ifndef IDS_MSGBOX_CANCEL
483 m_sButton1
= "Cancel";
485 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
488 m_uButton1Ret
= IDCANCEL
;
489 #ifndef IDS_MSGBOX_TRYAGAIN
490 m_sButton2
= "&Try Again";
492 m_i18l
.LoadString(IDS_MSGBOX_TRYAGAIN
);
495 m_uButton2Ret
= IDTRYAGAIN
;
496 #ifndef IDS_MSGBOX_CONTINUE
497 m_sButton3
= "&Continue";
499 m_i18l
.LoadString(IDS_MSGBOX_CONTINUE
);
502 m_uButton3Ret
= IDCONTINUE
;
505 #ifndef IDS_MSGBOX_OK
508 m_i18l
.LoadString(IDS_MSGBOX_OK
);
511 m_uButton1Ret
= IDOK
;
512 #ifndef IDS_MSGBOX_CANCEL
513 m_sButton2
= "Cancel";
515 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
518 m_uButton2Ret
= IDCANCEL
;
521 #ifndef IDS_MSGBOX_RETRY
522 m_sButton1
= "&Retry";
524 m_i18l
.LoadString(IDS_MSGBOX_RETRY
);
527 m_uButton1Ret
= IDRETRY
;
528 #ifndef IDS_MSGBOX_CANCEL
529 m_sButton2
= "Cancel";
531 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
534 m_uButton2Ret
= IDCANCEL
;
537 #ifndef IDS_MSGBOX_YES
540 m_i18l
.LoadString(IDS_MSGBOX_YES
);
543 m_uButton1Ret
= IDYES
;
544 #ifndef IDS_MSGBOX_NO
547 m_i18l
.LoadString(IDS_MSGBOX_NO
);
550 m_uButton2Ret
= IDNO
;
553 #ifndef IDS_MSGBOX_YES
556 m_i18l
.LoadString(IDS_MSGBOX_YES
);
559 m_uButton1Ret
= IDYES
;
560 #ifndef IDS_MSGBOX_NO
563 m_i18l
.LoadString(IDS_MSGBOX_NO
);
566 m_uButton2Ret
= IDNO
;
567 #ifndef IDS_MSGBOX_CANCEL
568 m_sButton3
= "Cancel";
570 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
573 m_uButton3Ret
= IDCANCEL
;
577 #ifndef IDS_MSGBOX_OK
580 m_i18l
.LoadString(IDS_MSGBOX_OK
);
584 //now set the default button
585 switch (uType
& 0xf00)
594 // do we need to add a help button?
598 #ifndef IDS_MSGBOX_HELP
601 m_i18l
.LoadString(IDS_MSGBOX_HELP
);
604 if (m_sButton2
.IsEmpty())
606 m_sButton2
= sHelpText
;
607 m_uButton2Ret
= IDHELP
;
609 else if (m_sButton3
.IsEmpty())
611 m_sButton3
= sHelpText
;
612 m_uButton3Ret
= IDHELP
;
618 UINT
CMessageBox::GoModal(CWnd
* pWnd
, const CString
& title
, const CString
& msg
, int nDefaultButton
)
620 NONCLIENTMETRICS ncm
= { 0 };
621 ncm
.cbSize
= sizeof(NONCLIENTMETRICS
);
622 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICS
), &ncm
, 0);
624 memcpy(&m_LogFont
, &(ncm
.lfMessageFont
), sizeof(LOGFONT
));
626 //the problem with the LOGFONT lfHeight is that it is not in pixels,
627 //but the dialog template needs the height in pixels.
628 //We need to convert those values first:
633 pDC
= GetDesktopWindow()->GetDC();
638 hdc
= ::GetDC(nullptr);
641 HWND hw
= pWnd
? pWnd
->m_hWnd
: nullptr;
642 int defButton
= nDefaultButton
== 1 ? MB_DEFBUTTON1
: nDefaultButton
== 2 ? MB_DEFBUTTON2
: nDefaultButton
== 3 ? MB_DEFBUTTON3
: 0;
643 return ::MessageBox(hw
, msg
, title
, m_uType
| defButton
);
646 int pix
= -CDPIAware::Instance().PixelsToPointsY(m_LogFont
.lfHeight
);
647 CDlgTemplate dialogTemplate
= CDlgTemplate(title
, WS_CAPTION
| DS_CENTER
,
648 0, 0, 0, 0, m_LogFont
.lfFaceName
, pix
);
649 dialogTemplate
.AddButton(L
"Button1", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 1) ? BS_DEFPUSHBUTTON
: 0), 0,
650 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON1
);
651 dialogTemplate
.AddButton(L
"Button2", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 2) ? BS_DEFPUSHBUTTON
: 0), 0,
652 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON2
);
653 dialogTemplate
.AddButton(L
"Button3", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 3) ? BS_DEFPUSHBUTTON
: 0), 0,
654 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON3
);
655 dialogTemplate
.AddButton(L
"Checkbox", WS_CHILD
| WS_TABSTOP
| BS_AUTOCHECKBOX
, 0,
656 0, 0, 0, 0, IDC_MESSAGEBOX_CHECKBOX
);
658 m_nDefButton
= nDefaultButton
;
660 InitModalIndirect(dialogTemplate
, pWnd
);
662 return static_cast<UINT
>(DoModal());
665 void CMessageBox::SetRegistryValue(const CString
& sValue
, DWORD value
)
667 if (sValue
.IsEmpty())
671 #ifdef XMESSAGEBOX_APPREGPATH
672 path
= XMESSAGEBOX_APPREGPATH
;
674 path
= "Software\\TortoiseGit\\";
675 path
+= AfxGetApp()->m_pszProfileName
;
679 if (RegCreateKeyEx(HKEY_CURRENT_USER
, path
, 0, nullptr, REG_OPTION_NON_VOLATILE
, KEY_WRITE
, nullptr, &hKey
, &disp
) != ERROR_SUCCESS
)
683 RegSetValueEx(hKey
, sValue
, 0, REG_DWORD
, reinterpret_cast<const BYTE
*>(&value
), sizeof(value
));
693 CSize
DrawText(CDC
* pDC
, CRect rect
, const CString
& str
, LOGFONT font
, BOOL bCalculate
= FALSE
)
700 CPoint pt
= rect
.TopLeft();
704 memcpy(&lf
, &font
, sizeof(LOGFONT
));
707 tempFont
.CreateFontIndirect(&lf
);
709 CFont
* pOldFont
= pDC
->SelectObject(&tempFont
);
711 TEXTMETRIC textMetric
;
712 pDC
->GetTextMetrics(&textMetric
);
713 int nHeight
= textMetric
.tmHeight
;
714 int nWidth
= textMetric
.tmAveCharWidth
;
718 //iterate through all characters of the string
719 for (int i
= 0; i
<= str
.GetLength(); ++i
)
722 if (i
< str
.GetLength())
724 switch (str
.GetAt(i
))
735 strText
+= str
.GetAt(i
);
739 else // Immitates new line at the end of the string
744 if (!strText
.IsEmpty())
747 pDC
->TextOut(ptCur
.x
, ptCur
.y
, strText
);
748 CSize s
= pDC
->GetTextExtent(strText
);
758 sz
.cx
= max(sz
.cx
, ptCur
.x
- pt
.x
);
764 int nTemp
= (ptCur
.x
- pt
.x
) % (nWidth
* 4);
768 ptCur
.x
+= (nWidth
* 4) - nTemp
;
770 ptCur
.x
+= nWidth
* 4;
776 //Gets real height of the tooltip
777 sz
.cy
= ptCur
.y
- pt
.y
;
779 pDC
->SelectObject(pOldFont
);
780 tempFont
.DeleteObject();
785 CSize
CMessageBox::GetTextSize(const CString
& str
)
788 GetWindowRect(&rect
);
794 memDC
.CreateCompatibleDC(pDC
);
795 bitmap
.CreateCompatibleBitmap(pDC
, rect
.Width(), rect
.Height());
796 CBitmap
* pOldBitmap
= memDC
.SelectObject(&bitmap
);
798 //get the minimum size of the rectangle of the tooltip
799 CSize sz
= DrawText(&memDC
, rect
, str
, m_LogFont
, TRUE
);
801 memDC
.SelectObject(pOldBitmap
);
803 bitmap
.DeleteObject();
810 CSize
CMessageBox::GetIconSize(HICON hIcon
)
817 //get icon dimensions
818 if (::GetIconInfo(hIcon
, &ii
))
820 sz
.cx
= static_cast<DWORD
>(ii
.xHotspot
* 2);
821 sz
.cy
= static_cast<DWORD
>(ii
.yHotspot
* 2);
822 //release icon mask bitmaps
824 ::DeleteObject(ii
.hbmMask
);
826 ::DeleteObject(ii
.hbmColor
);
833 CSize
CMessageBox::GetButtonSize()
836 int nButtons
= 0; //number of buttons - 1
838 SetDlgItemText(IDC_MESSAGEBOX_BUTTON1
, m_sButton1
);
839 SetDlgItemText(IDC_MESSAGEBOX_BUTTON2
, m_sButton2
);
840 //GetDlgItem(IDC_MESSAGEBOX_BUTTON2)->SendMessage(BM_SETSTYLE, BS_DEFPUSHBUTTON, 1);
841 SetDlgItemText(IDC_MESSAGEBOX_BUTTON3
, m_sButton3
);
842 SetDlgItemText(IDC_MESSAGEBOX_CHECKBOX
, m_sCheckbox
);
844 CSize sz1
= GetTextSize(m_sButton1
);
845 CSize sz2
= GetTextSize(m_sButton2
);
846 CSize sz3
= GetTextSize(m_sButton3
);
848 sz1
.cx
+= CDPIAware::Instance().ScaleX(2 * MESSAGEBOX_BUTTONX
);
849 sz1
.cy
+= CDPIAware::Instance().ScaleY(2 * MESSAGEBOX_BUTTONY
);
853 sz2
.cx
+= CDPIAware::Instance().ScaleX(2 * MESSAGEBOX_BUTTONX
);
854 sz2
.cy
+= CDPIAware::Instance().ScaleY(2 * MESSAGEBOX_BUTTONY
);
859 sz3
.cx
+= CDPIAware::Instance().ScaleX(2 * MESSAGEBOX_BUTTONX
);
860 sz3
.cy
+= CDPIAware::Instance().ScaleY(2 * MESSAGEBOX_BUTTONY
);
864 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(0, 0, sz1
.cx
, sz1
.cy
);
865 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(0, 0, sz2
.cx
, sz2
.cy
);
866 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->MoveWindow(0, 0, sz3
.cx
, sz3
.cy
);
868 sz
.cx
= sz1
.cx
+ sz2
.cx
+ sz3
.cx
+ (nButtons
* CDPIAware::Instance().ScaleX(MESSAGEBOX_BUTTONMARGIN
));
869 sz
.cy
= max(sz1
.cy
, sz2
.cy
);
870 sz
.cy
= max(sz
.cy
, sz3
.cy
);
874 CSize szCheck
= GetTextSize(m_sCheckbox
);
875 szCheck
.cx
+= 2*GetSystemMetrics(SM_CXMENUCHECK
);
876 szCheck
.cy
+= 2*MESSAGEBOX_BUTTONY
;
877 sz
.cx
= max(sz
.cx
, szCheck
.cx
);
878 sz
.cy
+= szCheck
.cy
+ CDPIAware::Instance().ScaleY(MESSAGEBOX_BUTTONCHECKMARGIN
);
879 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->MoveWindow(0, 0, szCheck
.cx
, szCheck
.cy
);
885 BEGIN_MESSAGE_MAP(CMessageBox
, CDialog
)
887 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON1
, OnButton1
)
888 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON2
, OnButton2
)
889 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON3
, OnButton3
)
892 void CMessageBox::OnPaint()
894 CPaintDC
dc(this); // device context for painting
898 GetClientRect(&rect
);
899 GetClientRect(&drawrect
);
901 //create a memory device-context. This is done to help reduce
902 //screen flicker, since we will paint the entire control to the
903 //off screen device context first.
906 memDC
.CreateCompatibleDC(&dc
);
907 bitmap
.CreateCompatibleBitmap(&dc
, rect
.Width(), rect
.Height());
908 CBitmap
* pOldBitmap
= memDC
.SelectObject(&bitmap
);
910 memDC
.BitBlt(rect
.left
, rect
.top
, rect
.Width(), rect
.Height(), &dc
, 0,0, SRCCOPY
);
912 memDC
.SetBkMode(TRANSPARENT
);
913 memDC
.SetBkColor(GetSysColor(COLOR_WINDOW
));
914 memDC
.SetTextColor(GetSysColor(COLOR_WINDOWTEXT
));
916 //OnDrawBackground();
917 drawrect
.DeflateRect(CDPIAware::Instance().ScaleX(MESSAGEBOX_BORDERMARGINX
), CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
));
920 DrawIconEx(memDC
.m_hDC
, drawrect
.left
, max(drawrect
.top
, drawrect
.top
+
921 ((drawrect
.Height() - m_szAllButtons
.cy
- CDPIAware::Instance().ScaleY(MESSAGEBOX_TEXTBUTTONMARGIN
) - m_szIcon
.cy
) / 2)),
922 m_hIcon
, m_szIcon
.cx
, m_szIcon
.cy
, 0, nullptr, DI_NORMAL
);
924 drawrect
.left
+= m_szIcon
.cx
+ CDPIAware::Instance().ScaleX(MESSAGEBOX_ICONMARGIN
);
925 if (m_szIcon
.cy
> m_szText
.cy
)
926 drawrect
.top
+= (m_szIcon
.cy
- m_szText
.cy
) / 2;
929 DrawText(&memDC
, drawrect
, m_sMessage
, m_LogFont
);
931 //Copy the memory device context back into the original DC.
932 dc
.BitBlt(rect
.left
, rect
.top
, rect
.Width(), rect
.Height(), &memDC
, 0,0, SRCCOPY
);
935 memDC
.SelectObject(pOldBitmap
);
937 bitmap
.DeleteObject();
940 void CMessageBox::OnButton1()
942 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
945 SetRegistryValue(m_sRegistryValue
, m_uButton1Ret
);
949 EndDialog(m_uButton1Ret
);
952 void CMessageBox::OnButton2()
954 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
957 SetRegistryValue(m_sRegistryValue
, m_uButton2Ret
);
961 if ((m_uButton2Ret
== IDHELP
)&&(!m_sHelpPath
.IsEmpty()))
963 typedef HWND (WINAPI
* FPHH
)(HWND
, LPCWSTR
, UINT
, DWORD
);
964 FPHH pHtmlHelp
= nullptr; // Function pointer
965 CAutoLibrary hInstHtmlHelp
= AtlLoadSystemLibraryUsingFullPath(L
"HHCtrl.ocx");
966 HWND hHelp
= nullptr;
969 reinterpret_cast<FARPROC
&>(pHtmlHelp
) = GetProcAddress(hInstHtmlHelp
, "HtmlHelpW");
971 hHelp
= pHtmlHelp(m_hWnd
, static_cast<LPCWSTR
>(m_sHelpPath
), HH_DISPLAY_TOPIC
, NULL
);
974 ::MessageBox(m_hWnd
, L
"could not show help file", L
"Help", MB_ICONERROR
);
976 else if (m_uButton2Ret
== IDHELP
)
979 EndDialog(m_uButton2Ret
);
982 void CMessageBox::OnButton3()
984 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
987 SetRegistryValue(m_sRegistryValue
, m_uButton3Ret
);
991 if ((m_uButton3Ret
== IDHELP
)&&(!m_sHelpPath
.IsEmpty()))
993 typedef HWND (WINAPI
* FPHH
)(HWND
, LPCWSTR
, UINT
, DWORD
);
994 FPHH pHtmlHelp
= nullptr; // Function pointer
995 CAutoLibrary hInstHtmlHelp
= AtlLoadSystemLibraryUsingFullPath(L
"HHCtrl.ocx");
996 HWND hHelp
= nullptr;
999 reinterpret_cast<FARPROC
&>(pHtmlHelp
) = GetProcAddress(hInstHtmlHelp
, "HtmlHelpW");
1001 hHelp
= pHtmlHelp(m_hWnd
, static_cast<LPCWSTR
>(m_sHelpPath
), HH_DISPLAY_TOPIC
, NULL
);
1004 ::MessageBox(m_hWnd
, L
"could not show help file", L
"Help", MB_ICONERROR
);
1006 else if (m_uButton3Ret
== IDHELP
)
1009 EndDialog(m_uButton3Ret
);
1012 void CMessageBox::OnCancel()
1014 if (m_uCancelRet
== IDCANCEL
)
1015 EndDialog(m_uCancelRet
);
1016 //__super::OnCancel();
1019 BOOL
CMessageBox::OnInitDialog()
1021 __super::OnInitDialog();
1023 CRect
rect(0, 0, 0, 0);
1025 //determine the required size of the message box
1026 m_szText
= GetTextSize(m_sMessage
);
1027 CSize szIcon
= GetIconSize(m_hIcon
);
1028 CSize szButtons
= GetButtonSize();
1031 szIconText
.cx
= m_szText
.cx
+ szIcon
.cx
+ CDPIAware::Instance().ScaleX((szIcon
.cx
== 0) ? MESSAGEBOX_ICONMARGIN
: (2 * MESSAGEBOX_ICONMARGIN
));
1032 szIconText
.cy
= max(szIcon
.cy
, m_szText
.cy
);
1034 rect
.right
= max(szButtons
.cx
, szIconText
.cx
);
1035 rect
.right
+= 2*GetSystemMetrics(SM_CXBORDER
);
1036 rect
.right
+= CDPIAware::Instance().ScaleX(2 * MESSAGEBOX_BORDERMARGINX
);
1037 rect
.bottom
= szIconText
.cy
;
1038 rect
.bottom
+= szButtons
.cy
;
1039 rect
.bottom
+= CDPIAware::Instance().ScaleY(2 * MESSAGEBOX_BORDERMARGINY
+ MESSAGEBOX_TEXTBUTTONMARGIN
);
1040 rect
.bottom
+= GetSystemMetrics(SM_CYCAPTION
);
1041 rect
.bottom
+= 2 * GetSystemMetrics(SM_CYFIXEDFRAME
);
1042 rect
.bottom
+= 2*GetSystemMetrics(SM_CYBORDER
);
1047 GetClientRect(rect
);
1049 //now size and position the buttons as we need them
1050 ASSERT(!m_sButton1
.IsEmpty()); //at least the first button must be there!
1051 if (m_sButton2
.IsEmpty())
1055 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt
);
1057 rt
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1058 rt
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1059 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt
);
1060 //hide the other two buttons
1061 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->ShowWindow(SW_HIDE
);
1062 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->ShowWindow(SW_HIDE
);
1064 else if (m_sButton3
.IsEmpty())
1069 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt1
);
1070 ScreenToClient(rt1
);
1071 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->GetWindowRect(rt2
);
1072 ScreenToClient(rt2
);
1073 rt1
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1074 rt1
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1075 rt2
.MoveToX(rt1
.right
+ CDPIAware::Instance().ScaleX(MESSAGEBOX_BUTTONMARGIN
));
1076 rt2
.MoveToY(rect
.bottom
-CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1077 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt1
);
1078 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(rt2
);
1079 //hide the third button
1080 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->ShowWindow(SW_HIDE
);
1086 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(buttonrect
);
1090 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt1
);
1091 ScreenToClient(rt1
);
1092 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->GetWindowRect(rt2
);
1093 ScreenToClient(rt2
);
1094 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->GetWindowRect(rt3
);
1095 ScreenToClient(rt3
);
1096 rt1
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1097 rt1
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1098 rt2
.MoveToX(rt1
.right
+ CDPIAware::Instance().ScaleX(MESSAGEBOX_BUTTONMARGIN
));
1099 rt2
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1100 rt3
.MoveToX(rt2
.right
+ CDPIAware::Instance().ScaleX(MESSAGEBOX_BUTTONMARGIN
));
1101 rt3
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - m_szButtons
.cy
);
1102 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt1
);
1103 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(rt2
);
1104 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->MoveWindow(rt3
);
1109 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->GetWindowRect(rt
);
1111 rt
.MoveToX(rect
.left
+ CDPIAware::Instance().ScaleX(MESSAGEBOX_BORDERMARGINX
) /*+ ((rect.Width() - szButtons.cx)/2)*/);
1112 rt
.MoveToY(rect
.bottom
- CDPIAware::Instance().ScaleY(MESSAGEBOX_BORDERMARGINY
) - szButtons
.cy
);
1113 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->MoveWindow(rt
);
1114 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->ShowWindow(SW_SHOW
);
1117 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->ShowWindow(SW_HIDE
);
1119 SetWindowPos(&CWnd::wndTopMost
,0,0,0,0,SWP_NOMOVE
|SWP_NOSIZE
);
1120 SetForegroundWindow();
1121 SetFocus(); //Just playing safe
1123 if (m_nDefButton
== 1)
1124 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->SetFocus();
1125 if (m_nDefButton
== 2)
1126 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->SetFocus();
1127 if (m_nDefButton
== 3)
1128 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->SetFocus();
1130 return FALSE
; // return TRUE unless you set the focus to a control
1131 // EXCEPTION: OCX Property Pages should return FALSE
1134 BOOL
CMessageBox::PreTranslateMessage(MSG
* pMsg
)
1136 if (pMsg
->message
== WM_KEYDOWN
)
1138 switch (pMsg
->wParam
)
1143 if (GetAsyncKeyState(VK_CONTROL
)&0x8000)
1145 CClipboardHelper clipboardHelper
;
1146 if(clipboardHelper
.Open(GetSafeHwnd()))
1149 CStringA sClipboard
= CStringA(m_sMessage
);
1150 HGLOBAL hClipboardData
= CClipboardHelper::GlobalAlloc(sClipboard
.GetLength()+1);
1151 auto pchData
= static_cast<char*>(GlobalLock(hClipboardData
));
1153 strcpy_s(pchData
, sClipboard
.GetLength() + 1, static_cast<LPCSTR
>(sClipboard
));
1154 GlobalUnlock(hClipboardData
);
1155 SetClipboardData(CF_TEXT
,hClipboardData
);
1163 switch (m_uType
& 0xf)
1165 case MB_ABORTRETRYIGNORE
:
1166 EndDialog(m_uButton1Ret
);
1168 case MB_CANCELTRYCONTINUE
:
1169 EndDialog(m_uButton1Ret
);
1172 EndDialog(m_uButton2Ret
);
1174 case MB_RETRYCANCEL
:
1175 EndDialog(m_uButton2Ret
);
1178 EndDialog(m_uButton2Ret
);
1180 case MB_YESNOCANCEL
:
1181 EndDialog(m_uButton3Ret
);
1189 return __super::PreTranslateMessage(pMsg
);