1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2016 - 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 #define BTN_OFFSET 100 // use an offset in order to not interfere with IDYES and so on...
29 CMessageBox::CMessageBox(void)
36 , m_bDestroyIcon(FALSE
)
41 SecureZeroMemory(&m_LogFont
, sizeof(LOGFONT
));
44 CMessageBox::~CMessageBox(void)
47 ::DestroyIcon(m_hIcon
);
50 static HWND
GetMainHWND(HWND hWnd
)
52 if (!hWnd
|| !IsWindow(hWnd
))
53 return CWnd::GetSafeOwner_(nullptr, nullptr);
57 UINT
CMessageBox::ShowCheck(HWND hWnd
, UINT nMessage
, UINT nCaption
, int nDef
, LPCTSTR icon
, UINT nButton1
, UINT nButton2
, UINT nButton3
, LPCTSTR lpRegistry
, UINT nCheckMessage
/* = nullptr*/, BOOL
* bChecked
)
65 sButton1
.LoadString(nButton1
);
66 sButton2
.LoadString(nButton2
);
67 sButton3
.LoadString(nButton3
);
68 sMessage
.LoadString(nMessage
);
69 sCaption
.LoadString(nCaption
);
70 nCheckMsg
.LoadString(nCheckMessage
);
71 return CMessageBox::ShowCheck(hWnd
, sMessage
, sCaption
, nDef
, icon
, sButton1
, sButton2
, sButton3
, lpRegistry
, nCheckMsg
, bChecked
);
74 UINT
CMessageBox::ShowCheck(HWND hWnd
, LPCTSTR lpMessage
, LPCTSTR lpCaption
, int nDef
, LPCTSTR icon
, LPCTSTR lpButton1
, LPCTSTR lpButton2
, LPCTSTR lpButton3
, LPCTSTR lpRegistry
, LPCTSTR lpCheckMessage
/* = nullptr*/, BOOL
* bChecked
)
76 //check the registry if we have to show the box or just return with the last used return value
77 //this would be the case if the user pressed "do not show again".
81 #ifdef XMESSAGEBOX_APPREGPATH
82 path
= XMESSAGEBOX_APPREGPATH
;
84 path
= "Software\\TortoiseGit\\";
85 path
+= AfxGetApp()->m_pszProfileName
;
87 if (lpRegistry
&& *lpRegistry
&& RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_EXECUTE
, &hKey
)==ERROR_SUCCESS
)
89 int size
= sizeof(dwRetVal
);
91 if (RegQueryValueEx(hKey
, lpRegistry
, nullptr, &type
, (BYTE
*)&dwRetVal
,(LPDWORD
)&size
) == ERROR_SUCCESS
)
93 ASSERT(type
==REG_DWORD
);
95 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Using stored value %ld for \"%s\"\n", dwRetVal
, lpMessage
);
96 return (UINT
)dwRetVal
; //return with the last saved value
104 if (CTaskDialog::IsSupported())
106 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_USE_COMMAND_LINKS
| TDF_POSITION_RELATIVE_TO_WINDOW
);
107 taskdlg
.AddCommandControl(BTN_OFFSET
+ 1, lpButton1
);
108 if (lpButton2
&& *lpButton2
)
109 taskdlg
.AddCommandControl(BTN_OFFSET
+ 2, lpButton2
);
110 if (lpButton3
&& *lpButton3
)
111 taskdlg
.AddCommandControl(BTN_OFFSET
+ 3, lpButton3
);
112 taskdlg
.SetDefaultCommandControl(BTN_OFFSET
+ nDef
);
113 taskdlg
.SetMainIcon(icon
);
116 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
117 taskdlg
.SetVerificationCheckboxText(L
"do not show again");
120 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
121 taskdlg
.SetVerificationCheckboxText(m_i18l
);
125 taskdlg
.SetVerificationCheckboxText(lpCheckMessage
);
126 int result
= (int)taskdlg
.DoModal(GetMainHWND(hWnd
)) - BTN_OFFSET
;
128 *bChecked
= taskdlg
.GetVerificationCheckboxState();
129 if (lpRegistry
&& *lpRegistry
&& taskdlg
.GetVerificationCheckboxState())
130 SetRegistryValue(lpRegistry
, result
);
136 box
.m_bShowCheck
= TRUE
;
137 box
.m_sRegistryValue
= lpRegistry
;
140 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
141 box
.m_sCheckbox
= L
"do not show again";
144 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
145 box
.m_sCheckbox
= m_i18l
;
149 box
.m_sCheckbox
= lpCheckMessage
;
150 box
.m_bChecked
= bChecked
? *bChecked
: FALSE
;
151 box
.m_sButton1
= lpButton1
;
152 box
.m_sButton2
= lpButton2
;
153 box
.m_sButton3
= lpButton3
;
154 box
.m_hIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), icon
, IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
);
156 box
.m_hIcon
= (HICON
)::LoadImage(nullptr, icon
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
158 box
.m_bDestroyIcon
= TRUE
;
161 int result
= box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, nDef
);
163 *bChecked
= box
.m_bChecked
;
167 UINT
CMessageBox::Show(HWND hWnd
, LPCTSTR lpMessage
, LPCTSTR lpCaption
, int nDef
, LPCTSTR icon
, LPCTSTR lpButton1
, LPCTSTR lpButton2
/* = nullptr*/, LPCTSTR lpButton3
/* = nullptr*/)
169 if (CTaskDialog::IsSupported())
171 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_USE_COMMAND_LINKS
| TDF_POSITION_RELATIVE_TO_WINDOW
);
172 taskdlg
.AddCommandControl(BTN_OFFSET
+ 1, lpButton1
);
173 if (lpButton2
&& *lpButton2
)
174 taskdlg
.AddCommandControl(BTN_OFFSET
+ 2, lpButton2
);
175 if (lpButton3
&& *lpButton3
)
176 taskdlg
.AddCommandControl(BTN_OFFSET
+ 3, lpButton3
);
177 taskdlg
.SetDefaultCommandControl(BTN_OFFSET
+ nDef
);
178 taskdlg
.SetMainIcon(icon
);
179 return (UINT
)taskdlg
.DoModal(GetMainHWND(hWnd
)) - BTN_OFFSET
;
182 box
.m_sButton1
= lpButton1
;
183 box
.m_sButton2
= lpButton2
;
184 box
.m_sButton3
= lpButton3
;
185 box
.m_hIcon
= (HICON
)::LoadImage(AfxGetResourceHandle(), icon
, IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
);
187 box
.m_hIcon
= (HICON
)::LoadImage(nullptr, icon
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
189 box
.m_bDestroyIcon
= TRUE
;
192 return box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, nDef
);
195 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, int nDef
, LPCTSTR icon
, UINT nButton1
, UINT nButton2
, UINT nButton3
)
202 sButton1
.LoadString(nButton1
);
203 sButton2
.LoadString(nButton2
);
204 sButton3
.LoadString(nButton3
);
205 sMessage
.LoadString(nMessage
);
206 sCaption
.LoadString(nCaption
);
207 return CMessageBox::Show(hWnd
, sMessage
, sCaption
, nDef
, icon
, sButton1
, sButton2
, sButton3
);
210 UINT
CMessageBox::ShowCheck(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, LPCTSTR lpRegistry
, UINT nCheckMessage
, BOOL
*bChecked
)
215 sMessage
.LoadString(nMessage
);
216 sCaption
.LoadString(nCaption
);
217 sCheckMsg
.LoadString(nCheckMessage
);
218 return CMessageBox::ShowCheck(hWnd
, sMessage
, sCaption
, uType
, lpRegistry
, sCheckMsg
, bChecked
);
221 UINT
CMessageBox::ShowCheck(HWND hWnd
, LPCTSTR lpMessage
, LPCTSTR lpCaption
, UINT uType
, LPCTSTR lpRegistry
, LPCTSTR lpCheckMessage
, BOOL
*bChecked
)
223 //check the registry if we have to show the box or just return with the last used return value
224 //this would be the case if the user pressed "do not show again".
228 #ifdef XMESSAGEBOX_APPREGPATH
229 path
= XMESSAGEBOX_APPREGPATH
;
231 path
= "Software\\TortoiseGit\\";
232 path
+= AfxGetApp()->m_pszProfileName
;
234 if (lpRegistry
&& *lpRegistry
&& RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_EXECUTE
, &hKey
)==ERROR_SUCCESS
)
236 int size
= sizeof(dwRetVal
);
238 if (RegQueryValueEx(hKey
, lpRegistry
, nullptr, &type
, (BYTE
*)&dwRetVal
,(LPDWORD
)&size
) == ERROR_SUCCESS
)
240 ASSERT(type
==REG_DWORD
);
242 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Using stored value %ld for \"%s\"\n", dwRetVal
, lpMessage
);
243 return (UINT
)dwRetVal
; //return with the last saved value
250 if (CTaskDialog::IsSupported() && !(uType
& MB_HELP
) && !((uType
& 0xf) == MB_ABORTRETRYIGNORE
) && !((uType
& 0xf) == MB_CANCELTRYCONTINUE
))
252 CTaskDialog
taskdlg(lpMessage
, L
"", lpCaption
, 0, TDF_POSITION_RELATIVE_TO_WINDOW
);
254 switch (uType
& 0xf0)
256 case MB_ICONEXCLAMATION
:
257 taskdlg
.SetMainIcon(IDI_EXCLAMATION
);
259 case MB_ICONASTERISK
:
260 taskdlg
.SetMainIcon(IDI_ASTERISK
);
262 case MB_ICONQUESTION
:
263 taskdlg
.SetMainIcon(IDI_QUESTION
);
266 taskdlg
.SetMainIcon(IDI_HAND
);
269 // set up the buttons
272 case MB_ABORTRETRYIGNORE
:
275 case MB_CANCELTRYCONTINUE
:
279 taskdlg
.SetCommonButtons(TDCBF_OK_BUTTON
| TDCBF_CANCEL_BUTTON
);
280 switch (uType
& 0xf00)
283 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
288 taskdlg
.SetCommonButtons(TDCBF_RETRY_BUTTON
| TDCBF_CANCEL_BUTTON
);
289 switch (uType
& 0xf00)
292 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
297 taskdlg
.SetCommonButtons(TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
);
298 switch (uType
& 0xf00)
301 taskdlg
.SetDefaultCommandControl(IDNO
);
306 taskdlg
.SetCommonButtons(TDCBF_YES_BUTTON
| TDCBF_NO_BUTTON
| TDCBF_CANCEL_BUTTON
);
307 switch (uType
& 0xf00)
310 taskdlg
.SetDefaultCommandControl(IDNO
);
313 taskdlg
.SetDefaultCommandControl(IDCANCEL
);
319 taskdlg
.SetCommonButtons(TDCBF_OK_BUTTON
);
324 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
325 taskdlg
.SetVerificationCheckboxText(L
"do not show again");
328 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
329 taskdlg
.SetVerificationCheckboxText(m_i18l
);
333 taskdlg
.SetVerificationCheckboxText(lpCheckMessage
);
334 int result
= (int)taskdlg
.DoModal(GetMainHWND(hWnd
));
336 *bChecked
= taskdlg
.GetVerificationCheckboxState();
337 if (lpRegistry
&& *lpRegistry
&& taskdlg
.GetVerificationCheckboxState())
338 SetRegistryValue(lpRegistry
, result
);
344 box
.m_bShowCheck
= TRUE
;
345 box
.m_sRegistryValue
= lpRegistry
;
348 #ifndef IDS_MSGBOX_DONOTSHOWAGAIN
349 box
.m_sCheckbox
= L
"do not show again";
352 m_i18l
.LoadString(IDS_MSGBOX_DONOTSHOWAGAIN
);
353 box
.m_sCheckbox
= m_i18l
;
357 box
.m_sCheckbox
= lpCheckMessage
;
358 box
.m_bChecked
= bChecked
? *bChecked
: FALSE
;
361 int result
= box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, box
.FillBoxStandard(uType
));
363 *bChecked
= box
.m_bChecked
;
367 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, LPCTSTR sHelpPath
)
371 sMessage
.LoadString(nMessage
);
372 sCaption
.LoadString(nCaption
);
373 return CMessageBox::Show(hWnd
, sMessage
, sCaption
, uType
, sHelpPath
);
376 UINT
CMessageBox::Show(HWND hWnd
, LPCTSTR lpMessage
, LPCTSTR lpCaption
, UINT uType
, LPCTSTR sHelpPath
)
383 box
.SetHelpPath(sHelpPath
);
384 return box
.GoModal(CWnd::FromHandle(hWnd
), lpCaption
, lpMessage
, box
.FillBoxStandard(uType
));
387 return ::MessageBox(GetMainHWND(hWnd
), lpMessage
, lpCaption
, uType
);
390 UINT
CMessageBox::Show(HWND hWnd
, UINT nMessage
, UINT nCaption
, UINT uType
, UINT nHelpID
)
394 sMessage
.LoadString(nMessage
);
395 sCaption
.LoadString(nCaption
);
402 box
.SetHelpID(nHelpID
);
403 return box
.GoModal(CWnd::FromHandle(hWnd
), sCaption
, sMessage
, box
.FillBoxStandard(uType
));
406 return ::MessageBox(GetMainHWND(hWnd
), sMessage
, sCaption
, uType
);
409 bool CMessageBox::RemoveRegistryKey(LPCTSTR lpRegistry
)
413 #ifdef XMESSAGEBOX_APPREGPATH
414 path
= XMESSAGEBOX_APPREGPATH
;
416 path
= "Software\\TortoiseGit\\";
417 path
+= AfxGetApp()->m_pszProfileName
;
419 if (RegOpenKeyEx(HKEY_CURRENT_USER
, path
, 0, KEY_WRITE
, &hKey
) == ERROR_SUCCESS
)
421 bool ret
= !!RegDeleteValue(hKey
, lpRegistry
);
428 int CMessageBox::FillBoxStandard(UINT uType
)
432 m_uCancelRet
= IDCANCEL
;
433 //load the icons according to uType
434 switch (uType
& 0xf0)
436 case MB_ICONEXCLAMATION
:
437 m_hIcon
= (HICON
)::LoadImage(nullptr, IDI_EXCLAMATION
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
438 ::MessageBeep(MB_ICONEXCLAMATION
);
440 case MB_ICONASTERISK
:
441 m_hIcon
= (HICON
)::LoadImage(nullptr, IDI_ASTERISK
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
442 ::MessageBeep(MB_ICONASTERISK
);
444 case MB_ICONQUESTION
:
445 m_hIcon
= (HICON
)::LoadImage(nullptr, IDI_QUESTION
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
446 ::MessageBeep(MB_ICONQUESTION
);
449 m_hIcon
= (HICON
)::LoadImage(nullptr, IDI_HAND
, IMAGE_ICON
, 0, 0, LR_SHARED
| LR_DEFAULTSIZE
);
450 ::MessageBeep(MB_ICONHAND
);
453 //set up the button texts
456 case MB_ABORTRETRYIGNORE
:
457 #ifndef IDS_MSGBOX_ABORT
458 m_sButton1
= "&Abort";
460 m_i18l
.LoadString(IDS_MSGBOX_ABORT
);
463 m_uButton1Ret
= IDABORT
;
464 #ifndef IDS_MSGBOX_RETRY
465 m_sButton2
= "&Retry";
467 m_i18l
.LoadString(IDS_MSGBOX_RETRY
);
470 m_uButton2Ret
= IDRETRY
;
471 #ifndef IDS_MSGBOX_IGNORE
472 m_sButton3
= "&Ignore";
474 m_i18l
.LoadString(IDS_MSGBOX_IGNORE
);
477 m_uButton3Ret
= IDIGNORE
;
479 case MB_CANCELTRYCONTINUE
:
480 #ifndef IDS_MSGBOX_CANCEL
481 m_sButton1
= "Cancel";
483 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
486 m_uButton1Ret
= IDCANCEL
;
487 #ifndef IDS_MSGBOX_TRYAGAIN
488 m_sButton2
= "&Try Again";
490 m_i18l
.LoadString(IDS_MSGBOX_TRYAGAIN
);
493 m_uButton2Ret
= IDTRYAGAIN
;
494 #ifndef IDS_MSGBOX_CONTINUE
495 m_sButton3
= "&Continue";
497 m_i18l
.LoadString(IDS_MSGBOX_CONTINUE
);
500 m_uButton3Ret
= IDCONTINUE
;
503 #ifndef IDS_MSGBOX_OK
506 m_i18l
.LoadString(IDS_MSGBOX_OK
);
509 m_uButton1Ret
= IDOK
;
510 #ifndef IDS_MSGBOX_CANCEL
511 m_sButton2
= "Cancel";
513 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
516 m_uButton2Ret
= IDCANCEL
;
519 #ifndef IDS_MSGBOX_RETRY
520 m_sButton1
= "&Retry";
522 m_i18l
.LoadString(IDS_MSGBOX_RETRY
);
525 m_uButton1Ret
= IDRETRY
;
526 #ifndef IDS_MSGBOX_CANCEL
527 m_sButton2
= "Cancel";
529 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
532 m_uButton2Ret
= IDCANCEL
;
535 #ifndef IDS_MSGBOX_YES
538 m_i18l
.LoadString(IDS_MSGBOX_YES
);
541 m_uButton1Ret
= IDYES
;
542 #ifndef IDS_MSGBOX_NO
545 m_i18l
.LoadString(IDS_MSGBOX_NO
);
548 m_uButton2Ret
= IDNO
;
551 #ifndef IDS_MSGBOX_YES
554 m_i18l
.LoadString(IDS_MSGBOX_YES
);
557 m_uButton1Ret
= IDYES
;
558 #ifndef IDS_MSGBOX_NO
561 m_i18l
.LoadString(IDS_MSGBOX_NO
);
564 m_uButton2Ret
= IDNO
;
565 #ifndef IDS_MSGBOX_CANCEL
566 m_sButton3
= "Cancel";
568 m_i18l
.LoadString(IDS_MSGBOX_CANCEL
);
571 m_uButton3Ret
= IDCANCEL
;
575 #ifndef IDS_MSGBOX_OK
578 m_i18l
.LoadString(IDS_MSGBOX_OK
);
582 //now set the default button
583 switch (uType
& 0xf00)
592 // do we need to add a help button?
596 #ifndef IDS_MSGBOX_HELP
599 m_i18l
.LoadString(IDS_MSGBOX_HELP
);
602 if (m_sButton2
.IsEmpty())
604 m_sButton2
= sHelpText
;
605 m_uButton2Ret
= IDHELP
;
607 else if (m_sButton3
.IsEmpty())
609 m_sButton3
= sHelpText
;
610 m_uButton3Ret
= IDHELP
;
616 UINT
CMessageBox::GoModal(CWnd
* pWnd
, const CString
& title
, const CString
& msg
, int nDefaultButton
)
618 NONCLIENTMETRICS ncm
= { 0 };
619 ncm
.cbSize
= sizeof(NONCLIENTMETRICS
);
620 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICS
), &ncm
, 0);
622 memcpy(&m_LogFont
, &(ncm
.lfMessageFont
), sizeof(LOGFONT
));
624 //the problem with the LOGFONT lfHeight is that it is not in pixels,
625 //but the dialog template needs the height in pixels.
626 //We need to convert those values first:
631 pDC
= GetDesktopWindow()->GetDC();
636 hdc
= ::GetDC(nullptr);
639 HWND hw
= pWnd
? pWnd
->m_hWnd
: nullptr;
640 int defButton
= nDefaultButton
== 1 ? MB_DEFBUTTON1
: nDefaultButton
== 2 ? MB_DEFBUTTON2
: nDefaultButton
== 3 ? MB_DEFBUTTON3
: 0;
641 return ::MessageBox(hw
, msg
, title
, m_uType
| defButton
);
644 int pix
= -MulDiv(m_LogFont
.lfHeight
, 72, GetDeviceCaps(hdc
, LOGPIXELSY
));
645 CDlgTemplate dialogTemplate
= CDlgTemplate(title
, WS_CAPTION
| DS_CENTER
,
646 0, 0, 0, 0, m_LogFont
.lfFaceName
, pix
);
647 dialogTemplate
.AddButton(L
"Button1", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 1) ? BS_DEFPUSHBUTTON
: 0), 0,
648 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON1
);
649 dialogTemplate
.AddButton(L
"Button2", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 2) ? BS_DEFPUSHBUTTON
: 0), 0,
650 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON2
);
651 dialogTemplate
.AddButton(L
"Button3", WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_PUSHBUTTON
| ((nDefaultButton
== 3) ? BS_DEFPUSHBUTTON
: 0), 0,
652 2 + 3, 62, 56, 13, IDC_MESSAGEBOX_BUTTON3
);
653 dialogTemplate
.AddButton(L
"Checkbox", WS_CHILD
| WS_TABSTOP
| BS_AUTOCHECKBOX
, 0,
654 0, 0, 0, 0, IDC_MESSAGEBOX_CHECKBOX
);
656 m_nDefButton
= nDefaultButton
;
658 InitModalIndirect(dialogTemplate
, pWnd
);
660 return (UINT
)DoModal();
663 void CMessageBox::SetRegistryValue(const CString
& sValue
, DWORD value
)
665 if (sValue
.IsEmpty())
669 #ifdef XMESSAGEBOX_APPREGPATH
670 path
= XMESSAGEBOX_APPREGPATH
;
672 path
= "Software\\TortoiseGit\\";
673 path
+= AfxGetApp()->m_pszProfileName
;
677 if (RegCreateKeyEx(HKEY_CURRENT_USER
, path
, 0, L
"", REG_OPTION_NON_VOLATILE
, KEY_WRITE
, nullptr, &hKey
, &disp
) != ERROR_SUCCESS
)
681 RegSetValueEx(hKey
, sValue
, 0, REG_DWORD
,(const BYTE
*) &value
, sizeof(value
));
691 CSize
DrawText(CDC
* pDC
, CRect rect
, const CString
& str
, LOGFONT font
, BOOL bCalculate
= FALSE
)
698 CPoint pt
= rect
.TopLeft();
702 memcpy(&lf
, &font
, sizeof(LOGFONT
));
705 tempFont
.CreateFontIndirect(&lf
);
707 CFont
* pOldFont
= pDC
->SelectObject(&tempFont
);
709 TEXTMETRIC textMetric
;
710 pDC
->GetTextMetrics(&textMetric
);
711 int nHeight
= textMetric
.tmHeight
;
712 int nWidth
= textMetric
.tmAveCharWidth
;
716 //iterate through all characters of the string
717 for (int i
= 0; i
<= str
.GetLength(); ++i
)
720 if (i
< str
.GetLength())
722 switch (str
.GetAt(i
))
733 strText
+= str
.GetAt(i
);
737 else // Immitates new line at the end of the string
742 if (!strText
.IsEmpty())
745 pDC
->TextOut(ptCur
.x
, ptCur
.y
, strText
);
746 CSize s
= pDC
->GetTextExtent(strText
);
756 sz
.cx
= max(sz
.cx
, ptCur
.x
- pt
.x
);
762 int nTemp
= (ptCur
.x
- pt
.x
) % (nWidth
* 4);
766 ptCur
.x
+= (nWidth
* 4) - nTemp
;
768 ptCur
.x
+= nWidth
* 4;
774 //Gets real height of the tooltip
775 sz
.cy
= ptCur
.y
- pt
.y
;
777 pDC
->SelectObject(pOldFont
);
778 tempFont
.DeleteObject();
783 CSize
CMessageBox::GetTextSize(const CString
& str
)
786 GetWindowRect(&rect
);
792 memDC
.CreateCompatibleDC(pDC
);
793 bitmap
.CreateCompatibleBitmap(pDC
, rect
.Width(), rect
.Height());
794 CBitmap
* pOldBitmap
= memDC
.SelectObject(&bitmap
);
796 //get the minimum size of the rectangle of the tooltip
797 CSize sz
= DrawText(&memDC
, rect
, str
, m_LogFont
, TRUE
);
799 memDC
.SelectObject(pOldBitmap
);
801 bitmap
.DeleteObject();
808 CSize
CMessageBox::GetIconSize(HICON hIcon
)
815 //get icon dimensions
816 if (::GetIconInfo(hIcon
, &ii
))
818 sz
.cx
= (DWORD
)(ii
.xHotspot
* 2);
819 sz
.cy
= (DWORD
)(ii
.yHotspot
* 2);
820 //release icon mask bitmaps
822 ::DeleteObject(ii
.hbmMask
);
824 ::DeleteObject(ii
.hbmColor
);
831 CSize
CMessageBox::GetButtonSize()
834 int nButtons
= 0; //number of buttons - 1
836 SetDlgItemText(IDC_MESSAGEBOX_BUTTON1
, m_sButton1
);
837 SetDlgItemText(IDC_MESSAGEBOX_BUTTON2
, m_sButton2
);
838 //GetDlgItem(IDC_MESSAGEBOX_BUTTON2)->SendMessage(BM_SETSTYLE, BS_DEFPUSHBUTTON, 1);
839 SetDlgItemText(IDC_MESSAGEBOX_BUTTON3
, m_sButton3
);
840 SetDlgItemText(IDC_MESSAGEBOX_CHECKBOX
, m_sCheckbox
);
842 CSize sz1
= GetTextSize(m_sButton1
);
843 CSize sz2
= GetTextSize(m_sButton2
);
844 CSize sz3
= GetTextSize(m_sButton3
);
846 sz1
.cx
+= 2*MESSAGEBOX_BUTTONX
;
847 sz1
.cy
+= 2*MESSAGEBOX_BUTTONY
;
851 sz2
.cx
+= 2*MESSAGEBOX_BUTTONX
;
852 sz2
.cy
+= 2*MESSAGEBOX_BUTTONY
;
857 sz3
.cx
+= 2*MESSAGEBOX_BUTTONX
;
858 sz3
.cy
+= 2*MESSAGEBOX_BUTTONY
;
862 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(0, 0, sz1
.cx
, sz1
.cy
);
863 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(0, 0, sz2
.cx
, sz2
.cy
);
864 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->MoveWindow(0, 0, sz3
.cx
, sz3
.cy
);
866 sz
.cx
= sz1
.cx
+ sz2
.cx
+ sz3
.cx
+ (nButtons
* MESSAGEBOX_BUTTONMARGIN
);
867 sz
.cy
= max(sz1
.cy
, sz2
.cy
);
868 sz
.cy
= max(sz
.cy
, sz3
.cy
);
872 CSize szCheck
= GetTextSize(m_sCheckbox
);
873 szCheck
.cx
+= 2*GetSystemMetrics(SM_CXMENUCHECK
);
874 szCheck
.cy
+= 2*MESSAGEBOX_BUTTONY
;
875 sz
.cx
= max(sz
.cx
, szCheck
.cx
);
876 sz
.cy
+= szCheck
.cy
+ MESSAGEBOX_BUTTONCHECKMARGIN
;
877 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->MoveWindow(0, 0, szCheck
.cx
, szCheck
.cy
);
883 BEGIN_MESSAGE_MAP(CMessageBox
, CDialog
)
885 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON1
, OnButton1
)
886 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON2
, OnButton2
)
887 ON_BN_CLICKED(IDC_MESSAGEBOX_BUTTON3
, OnButton3
)
890 void CMessageBox::OnPaint()
892 CPaintDC
dc(this); // device context for painting
896 GetClientRect(&rect
);
897 GetClientRect(&drawrect
);
899 //create a memory device-context. This is done to help reduce
900 //screen flicker, since we will paint the entire control to the
901 //off screen device context first.
904 memDC
.CreateCompatibleDC(&dc
);
905 bitmap
.CreateCompatibleBitmap(&dc
, rect
.Width(), rect
.Height());
906 CBitmap
* pOldBitmap
= memDC
.SelectObject(&bitmap
);
908 memDC
.BitBlt(rect
.left
, rect
.top
, rect
.Width(), rect
.Height(), &dc
, 0,0, SRCCOPY
);
910 memDC
.SetBkMode(TRANSPARENT
);
911 memDC
.SetBkColor(GetSysColor(COLOR_WINDOW
));
912 memDC
.SetTextColor(GetSysColor(COLOR_WINDOWTEXT
));
914 //OnDrawBackground();
915 drawrect
.DeflateRect(MESSAGEBOX_BORDERMARGINX
, MESSAGEBOX_BORDERMARGINY
);
918 DrawIconEx(memDC
.m_hDC
, drawrect
.left
, max(drawrect
.top
, drawrect
.top
+
919 ((drawrect
.Height() - m_szAllButtons
.cy
- MESSAGEBOX_TEXTBUTTONMARGIN
- m_szIcon
.cy
) / 2)),
920 m_hIcon
, m_szIcon
.cx
, m_szIcon
.cy
, 0, nullptr, DI_NORMAL
);
922 drawrect
.left
+= m_szIcon
.cx
+ MESSAGEBOX_ICONMARGIN
;
923 if (m_szIcon
.cy
> m_szText
.cy
)
924 drawrect
.top
+= (m_szIcon
.cy
- m_szText
.cy
) / 2;
927 DrawText(&memDC
, drawrect
, m_sMessage
, m_LogFont
);
929 //Copy the memory device context back into the original DC.
930 dc
.BitBlt(rect
.left
, rect
.top
, rect
.Width(), rect
.Height(), &memDC
, 0,0, SRCCOPY
);
933 memDC
.SelectObject(pOldBitmap
);
935 bitmap
.DeleteObject();
938 void CMessageBox::OnButton1()
940 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
943 SetRegistryValue(m_sRegistryValue
, m_uButton1Ret
);
947 EndDialog(m_uButton1Ret
);
950 void CMessageBox::OnButton2()
952 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
955 SetRegistryValue(m_sRegistryValue
, m_uButton2Ret
);
959 if ((m_uButton2Ret
== IDHELP
)&&(!m_sHelpPath
.IsEmpty()))
961 typedef HWND (WINAPI
* FPHH
)(HWND
, LPCWSTR
, UINT
, DWORD
);
962 FPHH pHtmlHelp
= nullptr; // Function pointer
963 CAutoLibrary hInstHtmlHelp
= AtlLoadSystemLibraryUsingFullPath(L
"HHCtrl.ocx");
964 HWND hHelp
= nullptr;
967 (FARPROC
&)pHtmlHelp
= GetProcAddress(hInstHtmlHelp
, "HtmlHelpW");
969 hHelp
= pHtmlHelp(m_hWnd
, (LPCTSTR
)m_sHelpPath
, HH_DISPLAY_TOPIC
, NULL
);
972 ::MessageBox(m_hWnd
, L
"could not show help file", L
"Help", MB_ICONERROR
);
974 else if (m_uButton2Ret
== IDHELP
)
977 EndDialog(m_uButton2Ret
);
980 void CMessageBox::OnButton3()
982 if (GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->SendMessage(BM_GETCHECK
, 0, 0)==BST_CHECKED
)
985 SetRegistryValue(m_sRegistryValue
, m_uButton3Ret
);
989 if ((m_uButton3Ret
== IDHELP
)&&(!m_sHelpPath
.IsEmpty()))
991 typedef HWND (WINAPI
* FPHH
)(HWND
, LPCWSTR
, UINT
, DWORD
);
992 FPHH pHtmlHelp
= nullptr; // Function pointer
993 CAutoLibrary hInstHtmlHelp
= AtlLoadSystemLibraryUsingFullPath(L
"HHCtrl.ocx");
994 HWND hHelp
= nullptr;
997 (FARPROC
&)pHtmlHelp
= GetProcAddress(hInstHtmlHelp
, "HtmlHelpW");
999 hHelp
= pHtmlHelp(m_hWnd
, (LPCTSTR
)m_sHelpPath
, HH_DISPLAY_TOPIC
, NULL
);
1002 ::MessageBox(m_hWnd
, L
"could not show help file", L
"Help", MB_ICONERROR
);
1004 else if (m_uButton3Ret
== IDHELP
)
1007 EndDialog(m_uButton3Ret
);
1010 void CMessageBox::OnCancel()
1012 if (m_uCancelRet
== IDCANCEL
)
1013 EndDialog(m_uCancelRet
);
1014 //__super::OnCancel();
1017 BOOL
CMessageBox::OnInitDialog()
1019 __super::OnInitDialog();
1021 CRect
rect(0, 0, 0, 0);
1023 //determine the required size of the message box
1024 m_szText
= GetTextSize(m_sMessage
);
1025 CSize szIcon
= GetIconSize(m_hIcon
);
1026 CSize szButtons
= GetButtonSize();
1029 szIconText
.cx
= m_szText
.cx
+ szIcon
.cx
+ ((szIcon
.cx
== 0) ? MESSAGEBOX_ICONMARGIN
: (2 * MESSAGEBOX_ICONMARGIN
));
1030 szIconText
.cy
= max(szIcon
.cy
, m_szText
.cy
);
1032 rect
.right
= max(szButtons
.cx
, szIconText
.cx
);
1033 rect
.right
+= 2*GetSystemMetrics(SM_CXBORDER
);
1034 rect
.right
+= 2*MESSAGEBOX_BORDERMARGINX
;
1035 rect
.bottom
= szIconText
.cy
;
1036 rect
.bottom
+= szButtons
.cy
;
1037 rect
.bottom
+= 2*MESSAGEBOX_BORDERMARGINY
+ MESSAGEBOX_TEXTBUTTONMARGIN
;
1038 rect
.bottom
+= GetSystemMetrics(SM_CYCAPTION
);
1039 rect
.bottom
+= 2 * GetSystemMetrics(SM_CYFIXEDFRAME
);
1040 rect
.bottom
+= 2*GetSystemMetrics(SM_CYBORDER
);
1045 GetClientRect(rect
);
1047 //now size and position the buttons as we need them
1048 ASSERT(!m_sButton1
.IsEmpty()); //at least the first button must be there!
1049 if (m_sButton2
.IsEmpty())
1053 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt
);
1055 rt
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1056 rt
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1057 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt
);
1058 //hide the other two buttons
1059 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->ShowWindow(SW_HIDE
);
1060 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->ShowWindow(SW_HIDE
);
1062 else if (m_sButton3
.IsEmpty())
1067 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt1
);
1068 ScreenToClient(rt1
);
1069 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->GetWindowRect(rt2
);
1070 ScreenToClient(rt2
);
1071 rt1
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1072 rt1
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1073 rt2
.MoveToX(rt1
.right
+ MESSAGEBOX_BUTTONMARGIN
);
1074 rt2
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1075 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt1
);
1076 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(rt2
);
1077 //hide the third button
1078 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->ShowWindow(SW_HIDE
);
1084 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(buttonrect
);
1088 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->GetWindowRect(rt1
);
1089 ScreenToClient(rt1
);
1090 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->GetWindowRect(rt2
);
1091 ScreenToClient(rt2
);
1092 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->GetWindowRect(rt3
);
1093 ScreenToClient(rt3
);
1094 rt1
.MoveToX(rect
.left
+ ((rect
.Width() - m_szButtons
.cx
)/2));
1095 rt1
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1096 rt2
.MoveToX(rt1
.right
+ MESSAGEBOX_BUTTONMARGIN
);
1097 rt2
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1098 rt3
.MoveToX(rt2
.right
+ MESSAGEBOX_BUTTONMARGIN
);
1099 rt3
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- m_szButtons
.cy
);
1100 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->MoveWindow(rt1
);
1101 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->MoveWindow(rt2
);
1102 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->MoveWindow(rt3
);
1107 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->GetWindowRect(rt
);
1109 rt
.MoveToX(rect
.left
+ MESSAGEBOX_BORDERMARGINX
/*+ ((rect.Width() - szButtons.cx)/2)*/);
1110 rt
.MoveToY(rect
.bottom
- MESSAGEBOX_BORDERMARGINY
- szButtons
.cy
);
1111 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->MoveWindow(rt
);
1112 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->ShowWindow(SW_SHOW
);
1115 GetDlgItem(IDC_MESSAGEBOX_CHECKBOX
)->ShowWindow(SW_HIDE
);
1117 SetWindowPos(&CWnd::wndTopMost
,0,0,0,0,SWP_NOMOVE
|SWP_NOSIZE
);
1118 SetForegroundWindow();
1119 SetFocus(); //Just playing safe
1121 if (m_nDefButton
== 1)
1122 GetDlgItem(IDC_MESSAGEBOX_BUTTON1
)->SetFocus();
1123 if (m_nDefButton
== 2)
1124 GetDlgItem(IDC_MESSAGEBOX_BUTTON2
)->SetFocus();
1125 if (m_nDefButton
== 3)
1126 GetDlgItem(IDC_MESSAGEBOX_BUTTON3
)->SetFocus();
1128 return FALSE
; // return TRUE unless you set the focus to a control
1129 // EXCEPTION: OCX Property Pages should return FALSE
1132 BOOL
CMessageBox::PreTranslateMessage(MSG
* pMsg
)
1134 if (pMsg
->message
== WM_KEYDOWN
)
1136 switch (pMsg
->wParam
)
1141 if (GetAsyncKeyState(VK_CONTROL
)&0x8000)
1143 CClipboardHelper clipboardHelper
;
1144 if(clipboardHelper
.Open(GetSafeHwnd()))
1147 CStringA sClipboard
= CStringA(m_sMessage
);
1148 HGLOBAL hClipboardData
= CClipboardHelper::GlobalAlloc(sClipboard
.GetLength()+1);
1149 char * pchData
= (char*)GlobalLock(hClipboardData
);
1151 strcpy_s(pchData
, sClipboard
.GetLength()+1, (LPCSTR
)sClipboard
);
1152 GlobalUnlock(hClipboardData
);
1153 SetClipboardData(CF_TEXT
,hClipboardData
);
1161 switch (m_uType
& 0xf)
1163 case MB_ABORTRETRYIGNORE
:
1164 EndDialog(m_uButton1Ret
);
1166 case MB_CANCELTRYCONTINUE
:
1167 EndDialog(m_uButton1Ret
);
1170 EndDialog(m_uButton2Ret
);
1172 case MB_RETRYCANCEL
:
1173 EndDialog(m_uButton2Ret
);
1176 EndDialog(m_uButton2Ret
);
1178 case MB_YESNOCANCEL
:
1179 EndDialog(m_uButton3Ret
);
1187 return __super::PreTranslateMessage(pMsg
);