1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/msgdlg.cpp
3 // Purpose: wxMessageDialog
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 // there is no hook support under CE so we can't use the code for message box
24 #define wxUSE_MSGBOX_HOOK 1
26 #define wxUSE_MSGBOX_HOOK 0
30 #include "wx/msgdlg.h"
34 #include "wx/msw/private.h"
36 #include "wx/hashmap.h"
40 #include "wx/ptr_scpd.h"
41 #include "wx/dynlib.h"
42 #include "wx/msw/private/button.h"
43 #include "wx/msw/private/metrics.h"
44 #include "wx/msw/private/msgdlg.h"
45 #include "wx/testing.h"
48 #include "wx/fontutil.h"
49 #include "wx/textbuf.h"
50 #include "wx/display.h"
55 #include "wx/msw/wince/missing.h"
58 // Interestingly, this symbol currently seems to be absent from Platform SDK
59 // headers but it is documented at MSDN.
60 #ifndef TDF_SIZE_TO_CONTENT
61 #define TDF_SIZE_TO_CONTENT 0x1000000
64 using namespace wxMSWMessageDialog
;
66 IMPLEMENT_CLASS(wxMessageDialog
, wxDialog
)
70 // there can potentially be one message box per thread so we use a hash map
71 // with thread ids as keys and (currently shown) message boxes as values
73 // TODO: replace this with wxTLS once it's available
74 WX_DECLARE_HASH_MAP(unsigned long, wxMessageDialog
*,
75 wxIntegerHash
, wxIntegerEqual
,
78 // the order in this array is the one in which buttons appear in the
80 const wxMessageDialog::ButtonAccessors
wxMessageDialog::ms_buttons
[] =
82 { IDYES
, &wxMessageDialog::GetYesLabel
},
83 { IDNO
, &wxMessageDialog::GetNoLabel
},
84 { IDOK
, &wxMessageDialog::GetOKLabel
},
85 { IDCANCEL
, &wxMessageDialog::GetCancelLabel
},
91 wxMessageDialogMap
& HookMap()
93 static wxMessageDialogMap s_Map
;
99 All this code is used for adjusting the message box layout when we mess
100 with its contents. It's rather complicated because we try hard to avoid
101 assuming much about the standard layout details and so, instead of just
102 laying out everything ourselves (which would have been so much simpler!)
103 we try to only modify the existing controls positions by offsetting them
104 from their default ones in the hope that this will continue to work with
105 the future Windows versions.
108 // convert the given RECT from screen to client coordinates in place
109 void ScreenRectToClient(HWND hwnd
, RECT
& rc
)
111 // map from desktop (i.e. screen) coordinates to ones of this window
113 // notice that a RECT is laid out as 2 consecutive POINTs so the cast is
115 ::MapWindowPoints(HWND_DESKTOP
, hwnd
, reinterpret_cast<POINT
*>(&rc
), 2);
118 // set window position to the given rect
119 inline void SetWindowRect(HWND hwnd
, const RECT
& rc
)
123 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
127 // set window position expressed in screen coordinates, whether the window is
128 // child or top level
129 void MoveWindowToScreenRect(HWND hwnd
, RECT rc
)
131 ScreenRectToClient(::GetParent(hwnd
), rc
);
133 SetWindowRect(hwnd
, rc
);
136 // helper of AdjustButtonLabels(): move the given window by dx
138 // works for both child and top level windows
139 void OffsetWindow(HWND hwnd
, int dx
)
141 RECT rc
= wxGetWindowRect(hwnd
);
146 MoveWindowToScreenRect(hwnd
, rc
);
149 } // anonymous namespace
153 wxMessageDialog::HookFunction(int code
, WXWPARAM wParam
, WXLPARAM lParam
)
155 // Find the thread-local instance of wxMessageDialog
156 const DWORD tid
= ::GetCurrentThreadId();
157 wxMessageDialogMap::iterator node
= HookMap().find(tid
);
158 wxCHECK_MSG( node
!= HookMap().end(), false,
159 wxT("bogus thread id in wxMessageDialog::Hook") );
161 wxMessageDialog
* const wnd
= node
->second
;
163 const HHOOK hhook
= (HHOOK
)wnd
->m_hook
;
164 const LRESULT rc
= ::CallNextHookEx(hhook
, code
, wParam
, lParam
);
166 if ( code
== HCBT_ACTIVATE
)
168 // we won't need this hook any longer
169 ::UnhookWindowsHookEx(hhook
);
171 HookMap().erase(tid
);
173 wnd
->SetHWND((HWND
)wParam
);
175 // replace the static text with an edit control if the message box is
176 // too big to fit the display
177 wnd
->ReplaceStaticWithEdit();
179 // update the labels if necessary: we need to do it before centering
180 // the dialog as this can change its size
181 if ( wnd
->HasCustomLabels() )
182 wnd
->AdjustButtonLabels();
184 // centre the message box on its parent if requested
185 if ( wnd
->GetMessageDialogStyle() & wxCENTER
)
186 wnd
->Center(); // center on parent
187 //else: default behaviour, center on screen
189 // there seems to be no reason to leave it set
196 void wxMessageDialog::ReplaceStaticWithEdit()
198 // check if the message box fits the display
199 int nDisplay
= wxDisplay::GetFromWindow(this);
200 if ( nDisplay
== wxNOT_FOUND
)
202 const wxRect rectDisplay
= wxDisplay(nDisplay
).GetClientArea();
204 if ( rectDisplay
.Contains(GetRect()) )
211 // find the static control to replace: normally there are two of them, the
212 // icon and the text itself so search for all of them and ignore the icon
214 HWND hwndStatic
= ::FindWindowEx(GetHwnd(), NULL
, wxT("STATIC"), NULL
);
215 if ( ::GetWindowLong(hwndStatic
, GWL_STYLE
) & SS_ICON
)
216 hwndStatic
= ::FindWindowEx(GetHwnd(), hwndStatic
, wxT("STATIC"), NULL
);
220 wxLogDebug("Failed to find the static text control in message box.");
224 // set the right font for GetCharHeight() call below
225 wxWindowBase::SetFont(GetMessageFont());
227 // put the new edit control at the same place
228 RECT rc
= wxGetWindowRect(hwndStatic
);
229 ScreenRectToClient(GetHwnd(), rc
);
231 // but make it less tall so that the message box fits on the screen: we try
232 // to make the message box take no more than 7/8 of the screen to leave
233 // some space above and below it
234 const int hText
= (7*rectDisplay
.height
)/8 -
236 2*::GetSystemMetrics(SM_CYFIXEDFRAME
) +
237 ::GetSystemMetrics(SM_CYCAPTION
) +
238 5*GetCharHeight() // buttons + margins
240 const int dh
= (rc
.bottom
- rc
.top
) - hText
; // vertical space we save
243 // and it also must be wider as it needs a vertical scrollbar (in order
244 // to preserve the word wrap, otherwise the number of lines would change
245 // and we want the control to look as similar as possible to the original)
247 // NB: you would have thought that 2*SM_CXEDGE would be enough but it
248 // isn't, somehow, and the text control breaks lines differently from
249 // the static one so fudge by adding some extra space
250 const int dw
= ::GetSystemMetrics(SM_CXVSCROLL
) +
251 4*::GetSystemMetrics(SM_CXEDGE
);
255 // chop of the trailing new line(s) from the message box text, they are
256 // ignored by the static control but result in extra lines and hence extra
257 // scrollbar position in the edit one
258 wxString
text(wxGetWindowText(hwndStatic
));
259 for ( wxString::reverse_iterator i
= text
.rbegin(); i
!= text
.rend(); ++i
)
263 // found last non-newline char, remove anything after it if
264 // necessary and stop in any case
265 if ( i
!= text
.rbegin() )
266 text
.erase(i
.base() + 1, text
.end());
271 // do create the new control
272 HWND hwndEdit
= ::CreateWindow
275 wxTextBuffer::Translate(text
).t_str(),
276 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
277 ES_MULTILINE
| ES_READONLY
| ES_AUTOVSCROLL
,
279 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
288 wxLogDebug("Creation of replacement edit control failed in message box");
292 // copy the font from the original control
293 LRESULT hfont
= ::SendMessage(hwndStatic
, WM_GETFONT
, 0, 0);
294 ::SendMessage(hwndEdit
, WM_SETFONT
, hfont
, 0);
297 ::DestroyWindow(hwndStatic
);
300 // shrink and centre the message box vertically and widen it box to account
301 // for the extra scrollbar
302 RECT rcBox
= wxGetWindowRect(GetHwnd());
303 const int hMsgBox
= rcBox
.bottom
- rcBox
.top
- dh
;
304 rcBox
.top
= (rectDisplay
.height
- hMsgBox
)/2;
305 rcBox
.bottom
= rcBox
.top
+ hMsgBox
+ (rectDisplay
.height
- hMsgBox
)%2
;
307 rcBox
.right
+= dw
- dw
/2;
308 SetWindowRect(GetHwnd(), rcBox
);
310 // and adjust all the buttons positions
311 for ( unsigned n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
313 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
315 continue; // it's ok, not all buttons are always present
317 RECT rc
= wxGetWindowRect(hwndBtn
);
322 MoveWindowToScreenRect(hwndBtn
, rc
);
326 void wxMessageDialog::AdjustButtonLabels()
328 // changing the button labels is the easy part but we also need to ensure
329 // that the buttons are big enough for the label strings and increase their
330 // size (and maybe the size of the message box itself) if they are not
332 // TODO-RTL: check whether this works correctly in RTL
334 // we want to use this font in GetTextExtent() calls below but we don't
335 // want to send WM_SETFONT to the message box, who knows how is it going to
336 // react to it (right now it doesn't seem to do anything but what if this
338 wxWindowBase::SetFont(GetMessageFont());
340 // first iteration: find the widest button and update the buttons labels
341 int wBtnOld
= 0, // current buttons width
342 wBtnNew
= 0; // required new buttons width
343 RECT rcBtn
; // stores the button height and y positions
344 unsigned numButtons
= 0; // total number of buttons in the message box
346 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
348 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
350 continue; // it's ok, not all buttons are always present
354 const wxString label
= (this->*ms_buttons
[n
].getter
)();
355 const wxSize sizeLabel
= wxWindowBase::GetTextExtent(label
);
357 // check if the button is big enough for this label
358 const RECT rc
= wxGetWindowRect(hwndBtn
);
361 // initialize wBtnOld using the first button width, all the other
362 // ones should have the same one
363 wBtnOld
= rc
.right
- rc
.left
;
365 rcBtn
= rc
; // remember for use below when we reposition the buttons
369 wxASSERT_MSG( wBtnOld
== rc
.right
- rc
.left
,
370 "all buttons are supposed to be of same width" );
373 const int widthNeeded
= wxMSWButton::GetFittingSize(this, sizeLabel
).x
;
374 if ( widthNeeded
> wBtnNew
)
375 wBtnNew
= widthNeeded
;
377 ::SetWindowText(hwndBtn
, label
.t_str());
380 if ( wBtnNew
<= wBtnOld
)
382 // all buttons fit, nothing else to do
386 // resize the message box to be wider if needed
387 const int wBoxOld
= wxGetClientRect(GetHwnd()).right
;
389 const int CHAR_WIDTH
= GetCharWidth();
390 const int MARGIN_OUTER
= 2*CHAR_WIDTH
; // margin between box and buttons
391 const int MARGIN_INNER
= CHAR_WIDTH
; // margin between buttons
393 RECT rcBox
= wxGetWindowRect(GetHwnd());
395 const int wAllButtons
= numButtons
*(wBtnNew
+ MARGIN_INNER
) - MARGIN_INNER
;
396 int wBoxNew
= 2*MARGIN_OUTER
+ wAllButtons
;
397 if ( wBoxNew
> wBoxOld
)
399 const int dw
= wBoxNew
- wBoxOld
;
401 rcBox
.right
+= dw
- dw
/2;
403 SetWindowRect(GetHwnd(), rcBox
);
405 // surprisingly, we don't need to resize the static text control, it
406 // seems to adjust itself to the new size, at least under Windows 2003
407 // (TODO: test if this happens on older Windows versions)
409 else // the current width is big enough
415 // finally position all buttons
417 // notice that we have to take into account the difference between window
419 rcBtn
.left
= (rcBox
.left
+ rcBox
.right
- wxGetClientRect(GetHwnd()).right
+
420 wBoxNew
- wAllButtons
) / 2;
421 rcBtn
.right
= rcBtn
.left
+ wBtnNew
;
423 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
425 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
429 MoveWindowToScreenRect(hwndBtn
, rcBtn
);
431 rcBtn
.left
+= wBtnNew
+ MARGIN_INNER
;
432 rcBtn
.right
+= wBtnNew
+ MARGIN_INNER
;
436 #endif // wxUSE_MSGBOX_HOOK
439 wxFont
wxMessageDialog::GetMessageFont()
441 const NONCLIENTMETRICS
& ncm
= wxMSWImpl::GetNonClientMetrics();
442 return wxNativeFontInfo(ncm
.lfMessageFont
);
445 int wxMessageDialog::ShowMessageBox()
447 if ( !wxTheApp
->GetTopWindow() )
449 // when the message box is shown from wxApp::OnInit() (i.e. before the
450 // message loop is entered), this must be done or the next message box
451 // will never be shown - just try putting 2 calls to wxMessageBox() in
452 // OnInit() to see it
453 while ( wxTheApp
->Pending() )
454 wxTheApp
->Dispatch();
457 // use the top level window as parent if none specified
458 m_parent
= GetParentForModalDialog();
459 HWND hWnd
= m_parent
? GetHwndOf(m_parent
) : NULL
;
462 // native message box always uses the current user locale but the program
463 // may be using a different one and in this case we need to manually
464 // translate the default button labels (if they're non default we have no
465 // way to translate them and so we must assume they were already
466 // translated) to avoid mismatch between the language of the message box
467 // text and its buttons
468 wxLocale
* const loc
= wxGetLocale();
469 if ( loc
&& loc
->GetLanguage() != wxLocale::GetSystemLanguage() )
471 if ( m_dialogStyle
& wxYES_NO
&&
472 (GetCustomYesLabel().empty() && GetCustomNoLabel().empty()) )
475 // use the strings with mnemonics here as the native message box
477 SetYesNoLabels(_("&Yes"), _("&No"));
480 // we may or not have the Ok/Cancel buttons but either we do have them
481 // or we already made the labels custom because we called
482 // SetYesNoLabels() above so doing this does no harm -- and is
483 // necessary in wxYES_NO | wxCANCEL case
485 // note that we don't use mnemonics here for consistency with the
486 // native message box (which probably doesn't use them because
487 // Enter/Esc keys can be already used to dismiss the message box
489 if ( GetCustomOKLabel().empty() && GetCustomCancelLabel().empty() )
490 SetOKCancelLabels(_("OK"), _("Cancel"));
494 // translate wx style in MSW
495 unsigned int msStyle
;
496 const long wxStyle
= GetMessageDialogStyle();
497 if ( wxStyle
& wxYES_NO
)
499 #if !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
500 if (wxStyle
& wxCANCEL
)
501 msStyle
= MB_YESNOCANCEL
;
503 #endif // !(__SMARTPHONE__ && __WXWINCE__)
506 if ( wxStyle
& wxNO_DEFAULT
)
507 msStyle
|= MB_DEFBUTTON2
;
508 else if ( wxStyle
& wxCANCEL_DEFAULT
)
509 msStyle
|= MB_DEFBUTTON3
;
511 else // without Yes/No we're going to have an OK button
513 if ( wxStyle
& wxCANCEL
)
515 msStyle
= MB_OKCANCEL
;
517 if ( wxStyle
& wxCANCEL_DEFAULT
)
518 msStyle
|= MB_DEFBUTTON2
;
526 if ( wxStyle
& wxHELP
)
531 // set the icon style
532 switch ( GetEffectiveIcon() )
535 msStyle
|= MB_ICONHAND
;
539 msStyle
|= MB_ICONEXCLAMATION
;
542 case wxICON_QUESTION
:
543 msStyle
|= MB_ICONQUESTION
;
546 case wxICON_INFORMATION
:
547 msStyle
|= MB_ICONINFORMATION
;
551 if ( wxStyle
& wxSTAY_ON_TOP
)
552 msStyle
|= MB_TOPMOST
;
555 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
556 msStyle
|= MB_RTLREADING
| MB_RIGHT
;
560 msStyle
|= MB_APPLMODAL
;
562 msStyle
|= MB_TASKMODAL
;
564 // per MSDN documentation for MessageBox() we can prefix the message with 2
565 // right-to-left mark characters to tell the function to use RTL layout
566 // (unfortunately this only works in Unicode builds)
567 wxString message
= GetFullMessage();
569 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
571 // NB: not all compilers support \u escapes
572 static const wchar_t wchRLM
= 0x200f;
573 message
.Prepend(wxString(wchRLM
, 2));
575 #endif // wxUSE_UNICODE
577 #if wxUSE_MSGBOX_HOOK
578 // install the hook in any case as we don't know in advance if the message
579 // box is not going to be too big (requiring the replacement of the static
580 // control with an edit one)
581 const DWORD tid
= ::GetCurrentThreadId();
582 m_hook
= ::SetWindowsHookEx(WH_CBT
,
583 &wxMessageDialog::HookFunction
, NULL
, tid
);
584 HookMap()[tid
] = this;
585 #endif // wxUSE_MSGBOX_HOOK
587 // do show the dialog
588 int msAns
= MessageBox(hWnd
, message
.t_str(), m_caption
.t_str(), msStyle
);
590 return MSWTranslateReturnCode(msAns
);
593 int wxMessageDialog::ShowModal()
595 WX_TESTING_SHOW_MODAL_HOOK();
597 #ifdef wxHAS_MSW_TASKDIALOG
598 if ( HasNativeTaskDialog() )
600 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
601 wxCHECK_MSG( taskDialogIndirect
, wxID_CANCEL
, wxS("no task dialog?") );
603 WinStruct
<TASKDIALOGCONFIG
> tdc
;
604 wxMSWTaskDialogConfig
wxTdc( *this );
605 wxTdc
.MSWCommonTaskDialogInit( tdc
);
608 HRESULT hr
= taskDialogIndirect( &tdc
, &msAns
, NULL
, NULL
);
611 wxLogApiError( "TaskDialogIndirect", hr
);
615 // In case only an "OK" button was specified we actually created a
616 // "Cancel" button (see comment in MSWCommonTaskDialogInit). This
617 // results in msAns being IDCANCEL while we want IDOK (just like
618 // how the native MessageBox function does with only an "OK" button).
619 if ( (msAns
== IDCANCEL
)
620 && !(GetMessageDialogStyle() & (wxYES_NO
|wxCANCEL
)) )
625 return MSWTranslateReturnCode( msAns
);
627 #endif // wxHAS_MSW_TASKDIALOG
629 return ShowMessageBox();
632 void wxMessageDialog::DoCentre(int dir
)
634 #ifdef wxHAS_MSW_TASKDIALOG
635 // Task dialog is always centered on its parent window and trying to center
636 // it manually doesn't work because its HWND is not created yet so don't
637 // even try as this would only result in (debug) error messages.
638 if ( HasNativeTaskDialog() )
640 #endif // wxHAS_MSW_TASKDIALOG
642 wxMessageDialogBase::DoCentre(dir
);
645 // ----------------------------------------------------------------------------
646 // Helpers of the wxMSWMessageDialog namespace
647 // ----------------------------------------------------------------------------
649 #ifdef wxHAS_MSW_TASKDIALOG
651 wxMSWTaskDialogConfig::wxMSWTaskDialogConfig(const wxMessageDialogBase
& dlg
)
652 : buttons(new TASKDIALOG_BUTTON
[MAX_BUTTONS
])
654 parent
= dlg
.GetParentForModalDialog();
655 caption
= dlg
.GetCaption();
656 message
= dlg
.GetMessage();
657 extendedMessage
= dlg
.GetExtendedMessage();
659 // Before wxMessageDialog added support for extended message it was common
660 // practice to have long multiline texts in the message box with the first
661 // line playing the role of the main message and the rest of the extended
662 // one. Try to detect such usage automatically here by synthesizing the
663 // extended message on our own if it wasn't given.
664 if ( extendedMessage
.empty() )
666 // Check if there is a blank separating line after the first line (this
667 // is not the same as searching for "\n\n" as we want the automatically
668 // recognized main message be single line to avoid embarrassing false
670 const size_t posNL
= message
.find('\n');
671 if ( posNL
!= wxString::npos
&&
672 posNL
< message
.length() - 1 &&
673 message
[posNL
+ 1 ] == '\n' )
675 extendedMessage
.assign(message
, posNL
+ 2, wxString::npos
);
676 message
.erase(posNL
);
680 iconId
= dlg
.GetEffectiveIcon();
681 style
= dlg
.GetMessageDialogStyle();
682 useCustomLabels
= dlg
.HasCustomLabels();
683 btnYesLabel
= dlg
.GetYesLabel();
684 btnNoLabel
= dlg
.GetNoLabel();
685 btnOKLabel
= dlg
.GetOKLabel();
686 btnCancelLabel
= dlg
.GetCancelLabel();
687 btnHelpLabel
= dlg
.GetHelpLabel();
690 void wxMSWTaskDialogConfig::MSWCommonTaskDialogInit(TASKDIALOGCONFIG
&tdc
)
692 // Use TDF_SIZE_TO_CONTENT to try to prevent Windows from truncating or
693 // ellipsizing the message text. This doesn't always work as Windows will
694 // still do it if the message contains too long "words" (i.e. runs of the
695 // text without spaces) but at least it ensures that the message text is
696 // fully shown for reasonably-sized words whereas without it using almost
697 // any file system path in a message box would result in truncation.
698 tdc
.dwFlags
= TDF_EXPAND_FOOTER_AREA
|
699 TDF_POSITION_RELATIVE_TO_WINDOW
|
701 tdc
.hInstance
= wxGetInstance();
702 tdc
.pszWindowTitle
= caption
.t_str();
704 // use the top level window as parent if none specified
705 tdc
.hwndParent
= parent
? GetHwndOf(parent
) : NULL
;
707 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
708 tdc
.dwFlags
|= TDF_RTL_LAYOUT
;
710 // If we have both the main and extended messages, just use them as
711 // intended. However if only one message is given we normally use it as the
712 // content and not as the main instruction because the latter is supposed
713 // to stand out compared to the former and doesn't look good if there is
714 // nothing for it to contrast with. Finally, notice that the extended
715 // message we use here might be automatically extracted from the main
716 // message in our ctor, see comment there.
717 if ( !extendedMessage
.empty() )
719 tdc
.pszMainInstruction
= message
.t_str();
720 tdc
.pszContent
= extendedMessage
.t_str();
724 tdc
.pszContent
= message
.t_str();
727 // set an icon to be used, if possible
731 tdc
.pszMainIcon
= TD_ERROR_ICON
;
735 tdc
.pszMainIcon
= TD_WARNING_ICON
;
738 case wxICON_INFORMATION
:
739 tdc
.pszMainIcon
= TD_INFORMATION_ICON
;
743 // custom label button array that can hold all buttons in use
744 tdc
.pButtons
= buttons
.get();
746 if ( style
& wxYES_NO
)
748 AddTaskDialogButton(tdc
, IDYES
, TDCBF_YES_BUTTON
, btnYesLabel
);
749 AddTaskDialogButton(tdc
, IDNO
, TDCBF_NO_BUTTON
, btnNoLabel
);
751 if (style
& wxCANCEL
)
752 AddTaskDialogButton(tdc
, IDCANCEL
,
753 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
755 if ( style
& wxNO_DEFAULT
)
756 tdc
.nDefaultButton
= IDNO
;
757 else if ( style
& wxCANCEL_DEFAULT
)
758 tdc
.nDefaultButton
= IDCANCEL
;
760 else // without Yes/No we're going to have an OK button
762 if ( style
& wxCANCEL
)
764 AddTaskDialogButton(tdc
, IDOK
, TDCBF_OK_BUTTON
, btnOKLabel
);
765 AddTaskDialogButton(tdc
, IDCANCEL
,
766 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
768 if ( style
& wxCANCEL_DEFAULT
)
769 tdc
.nDefaultButton
= IDCANCEL
;
773 // We actually create a "Cancel" button instead because we want to
774 // allow closing the dialog box with Escape (and also Alt-F4 or
775 // clicking the close button in the title bar) which wouldn't work
776 // without a Cancel button.
777 if ( !useCustomLabels
)
779 useCustomLabels
= true;
780 btnOKLabel
= _("OK");
783 AddTaskDialogButton(tdc
, IDCANCEL
, TDCBF_CANCEL_BUTTON
, btnOKLabel
);
787 if ( style
& wxHELP
)
789 // There is no support for "Help" button in the task dialog, it can
790 // only show "Retry" or "Close" ones.
791 useCustomLabels
= true;
793 AddTaskDialogButton(tdc
, IDHELP
, 0 /* not used */, btnHelpLabel
);
797 void wxMSWTaskDialogConfig::AddTaskDialogButton(TASKDIALOGCONFIG
&tdc
,
800 const wxString
& customLabel
)
802 if ( useCustomLabels
)
804 // use custom buttons to implement custom labels
805 TASKDIALOG_BUTTON
&tdBtn
= buttons
[tdc
.cButtons
];
807 tdBtn
.nButtonID
= btnCustomId
;
808 tdBtn
.pszButtonText
= customLabel
.t_str();
811 // We should never have more than 4 buttons currently as this is the
812 // maximal number of buttons supported by the message dialog.
813 wxASSERT_MSG( tdc
.cButtons
<= MAX_BUTTONS
, wxT("Too many buttons") );
817 tdc
.dwCommonButtons
|= btnCommonId
;
821 // Task dialog can be used from different threads (and wxProgressDialog always
822 // uses it from another thread in fact) so protect access to the static
823 // variable below with a critical section.
824 wxCRIT_SECT_DECLARE(gs_csTaskDialogIndirect
);
826 TaskDialogIndirect_t
wxMSWMessageDialog::GetTaskDialogIndirectFunc()
828 // Initialize the function pointer to an invalid value different from NULL
829 // to avoid reloading comctl32.dll and trying to resolve it every time
830 // we're called if task dialog is not available (notice that this may
831 // happen even under Vista+ if we don't use comctl32.dll v6).
832 static const TaskDialogIndirect_t
833 INVALID_TASKDIALOG_FUNC
= reinterpret_cast<TaskDialogIndirect_t
>(-1);
834 static TaskDialogIndirect_t s_TaskDialogIndirect
= INVALID_TASKDIALOG_FUNC
;
836 wxCRIT_SECT_LOCKER(lock
, gs_csTaskDialogIndirect
);
838 if ( s_TaskDialogIndirect
== INVALID_TASKDIALOG_FUNC
)
840 wxLoadedDLL
dllComCtl32("comctl32.dll");
841 wxDL_INIT_FUNC(s_
, TaskDialogIndirect
, dllComCtl32
);
844 return s_TaskDialogIndirect
;
847 #endif // wxHAS_MSW_TASKDIALOG
849 bool wxMSWMessageDialog::HasNativeTaskDialog()
851 #ifdef wxHAS_MSW_TASKDIALOG
852 if ( wxGetWinVersion() >= wxWinVersion_6
)
854 if ( wxMSWMessageDialog::GetTaskDialogIndirectFunc() )
857 #endif // wxHAS_MSW_TASKDIALOG
862 int wxMSWMessageDialog::MSWTranslateReturnCode(int msAns
)
868 wxFAIL_MSG(wxT("unexpected return code"));
891 #endif // wxUSE_MSGDLG