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"
47 #include "wx/fontutil.h"
48 #include "wx/textbuf.h"
49 #include "wx/display.h"
54 #include "wx/msw/wince/missing.h"
57 using namespace wxMSWMessageDialog
;
59 IMPLEMENT_CLASS(wxMessageDialog
, wxDialog
)
63 // there can potentially be one message box per thread so we use a hash map
64 // with thread ids as keys and (currently shown) message boxes as values
66 // TODO: replace this with wxTLS once it's available
67 WX_DECLARE_HASH_MAP(unsigned long, wxMessageDialog
*,
68 wxIntegerHash
, wxIntegerEqual
,
71 // the order in this array is the one in which buttons appear in the
73 const wxMessageDialog::ButtonAccessors
wxMessageDialog::ms_buttons
[] =
75 { IDYES
, &wxMessageDialog::GetYesLabel
},
76 { IDNO
, &wxMessageDialog::GetNoLabel
},
77 { IDOK
, &wxMessageDialog::GetOKLabel
},
78 { IDCANCEL
, &wxMessageDialog::GetCancelLabel
},
84 wxMessageDialogMap
& HookMap()
86 static wxMessageDialogMap s_Map
;
92 All this code is used for adjusting the message box layout when we mess
93 with its contents. It's rather complicated because we try hard to avoid
94 assuming much about the standard layout details and so, instead of just
95 laying out everything ourselves (which would have been so much simpler!)
96 we try to only modify the existing controls positions by offsetting them
97 from their default ones in the hope that this will continue to work with
98 the future Windows versions.
101 // convert the given RECT from screen to client coordinates in place
102 void ScreenRectToClient(HWND hwnd
, RECT
& rc
)
104 // map from desktop (i.e. screen) coordinates to ones of this window
106 // notice that a RECT is laid out as 2 consecutive POINTs so the cast is
108 ::MapWindowPoints(HWND_DESKTOP
, hwnd
, reinterpret_cast<POINT
*>(&rc
), 2);
111 // set window position to the given rect
112 inline void SetWindowRect(HWND hwnd
, const RECT
& rc
)
116 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
120 // set window position expressed in screen coordinates, whether the window is
121 // child or top level
122 void MoveWindowToScreenRect(HWND hwnd
, RECT rc
)
124 ScreenRectToClient(::GetParent(hwnd
), rc
);
126 SetWindowRect(hwnd
, rc
);
129 // helper of AdjustButtonLabels(): move the given window by dx
131 // works for both child and top level windows
132 void OffsetWindow(HWND hwnd
, int dx
)
134 RECT rc
= wxGetWindowRect(hwnd
);
139 MoveWindowToScreenRect(hwnd
, rc
);
142 } // anonymous namespace
146 wxMessageDialog::HookFunction(int code
, WXWPARAM wParam
, WXLPARAM lParam
)
148 // Find the thread-local instance of wxMessageDialog
149 const DWORD tid
= ::GetCurrentThreadId();
150 wxMessageDialogMap::iterator node
= HookMap().find(tid
);
151 wxCHECK_MSG( node
!= HookMap().end(), false,
152 wxT("bogus thread id in wxMessageDialog::Hook") );
154 wxMessageDialog
* const wnd
= node
->second
;
156 const HHOOK hhook
= (HHOOK
)wnd
->m_hook
;
157 const LRESULT rc
= ::CallNextHookEx(hhook
, code
, wParam
, lParam
);
159 if ( code
== HCBT_ACTIVATE
)
161 // we won't need this hook any longer
162 ::UnhookWindowsHookEx(hhook
);
164 HookMap().erase(tid
);
166 wnd
->SetHWND((HWND
)wParam
);
168 // replace the static text with an edit control if the message box is
169 // too big to fit the display
170 wnd
->ReplaceStaticWithEdit();
172 // update the labels if necessary: we need to do it before centering
173 // the dialog as this can change its size
174 if ( wnd
->HasCustomLabels() )
175 wnd
->AdjustButtonLabels();
177 // centre the message box on its parent if requested
178 if ( wnd
->GetMessageDialogStyle() & wxCENTER
)
179 wnd
->Center(); // center on parent
180 //else: default behaviour, center on screen
182 // there seems to be no reason to leave it set
189 void wxMessageDialog::ReplaceStaticWithEdit()
191 // check if the message box fits the display
192 int nDisplay
= wxDisplay::GetFromWindow(this);
193 if ( nDisplay
== wxNOT_FOUND
)
195 const wxRect rectDisplay
= wxDisplay(nDisplay
).GetClientArea();
197 if ( rectDisplay
.Contains(GetRect()) )
204 // find the static control to replace: normally there are two of them, the
205 // icon and the text itself so search for all of them and ignore the icon
207 HWND hwndStatic
= ::FindWindowEx(GetHwnd(), NULL
, wxT("STATIC"), NULL
);
208 if ( ::GetWindowLong(hwndStatic
, GWL_STYLE
) & SS_ICON
)
209 hwndStatic
= ::FindWindowEx(GetHwnd(), hwndStatic
, wxT("STATIC"), NULL
);
213 wxLogDebug("Failed to find the static text control in message box.");
217 // set the right font for GetCharHeight() call below
218 wxWindowBase::SetFont(GetMessageFont());
220 // put the new edit control at the same place
221 RECT rc
= wxGetWindowRect(hwndStatic
);
222 ScreenRectToClient(GetHwnd(), rc
);
224 // but make it less tall so that the message box fits on the screen: we try
225 // to make the message box take no more than 7/8 of the screen to leave
226 // some space above and below it
227 const int hText
= (7*rectDisplay
.height
)/8 -
229 2*::GetSystemMetrics(SM_CYFIXEDFRAME
) +
230 ::GetSystemMetrics(SM_CYCAPTION
) +
231 5*GetCharHeight() // buttons + margins
233 const int dh
= (rc
.bottom
- rc
.top
) - hText
; // vertical space we save
236 // and it also must be wider as it needs a vertical scrollbar (in order
237 // to preserve the word wrap, otherwise the number of lines would change
238 // and we want the control to look as similar as possible to the original)
240 // NB: you would have thought that 2*SM_CXEDGE would be enough but it
241 // isn't, somehow, and the text control breaks lines differently from
242 // the static one so fudge by adding some extra space
243 const int dw
= ::GetSystemMetrics(SM_CXVSCROLL
) +
244 4*::GetSystemMetrics(SM_CXEDGE
);
248 // chop of the trailing new line(s) from the message box text, they are
249 // ignored by the static control but result in extra lines and hence extra
250 // scrollbar position in the edit one
251 wxString
text(wxGetWindowText(hwndStatic
));
252 for ( wxString::reverse_iterator i
= text
.rbegin(); i
!= text
.rend(); ++i
)
256 // found last non-newline char, remove everything after it and stop
257 text
.erase(i
.base() + 1, text
.end());
262 // do create the new control
263 HWND hwndEdit
= ::CreateWindow
266 wxTextBuffer::Translate(text
).wx_str(),
267 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
268 ES_MULTILINE
| ES_READONLY
| ES_AUTOVSCROLL
,
270 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
279 wxLogDebug("Creation of replacement edit control failed in message box");
283 // copy the font from the original control
284 LRESULT hfont
= ::SendMessage(hwndStatic
, WM_GETFONT
, 0, 0);
285 ::SendMessage(hwndEdit
, WM_SETFONT
, hfont
, 0);
288 ::DestroyWindow(hwndStatic
);
291 // shrink and centre the message box vertically and widen it box to account
292 // for the extra scrollbar
293 RECT rcBox
= wxGetWindowRect(GetHwnd());
294 const int hMsgBox
= rcBox
.bottom
- rcBox
.top
- dh
;
295 rcBox
.top
= (rectDisplay
.height
- hMsgBox
)/2;
296 rcBox
.bottom
= rcBox
.top
+ hMsgBox
+ (rectDisplay
.height
- hMsgBox
)%2
;
298 rcBox
.right
+= dw
- dw
/2;
299 SetWindowRect(GetHwnd(), rcBox
);
301 // and adjust all the buttons positions
302 for ( unsigned n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
304 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
306 continue; // it's ok, not all buttons are always present
308 RECT rc
= wxGetWindowRect(hwndBtn
);
313 MoveWindowToScreenRect(hwndBtn
, rc
);
317 void wxMessageDialog::AdjustButtonLabels()
319 // changing the button labels is the easy part but we also need to ensure
320 // that the buttons are big enough for the label strings and increase their
321 // size (and maybe the size of the message box itself) if they are not
323 // TODO-RTL: check whether this works correctly in RTL
325 // we want to use this font in GetTextExtent() calls below but we don't
326 // want to send WM_SETFONT to the message box, who knows how is it going to
327 // react to it (right now it doesn't seem to do anything but what if this
329 wxWindowBase::SetFont(GetMessageFont());
331 // first iteration: find the widest button and update the buttons labels
332 int wBtnOld
= 0, // current buttons width
333 wBtnNew
= 0; // required new buttons width
334 RECT rcBtn
; // stores the button height and y positions
335 unsigned numButtons
= 0; // total number of buttons in the message box
337 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
339 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
341 continue; // it's ok, not all buttons are always present
345 const wxString label
= (this->*ms_buttons
[n
].getter
)();
346 const wxSize sizeLabel
= wxWindowBase::GetTextExtent(label
);
348 // check if the button is big enough for this label
349 const RECT rc
= wxGetWindowRect(hwndBtn
);
352 // initialize wBtnOld using the first button width, all the other
353 // ones should have the same one
354 wBtnOld
= rc
.right
- rc
.left
;
356 rcBtn
= rc
; // remember for use below when we reposition the buttons
360 wxASSERT_MSG( wBtnOld
== rc
.right
- rc
.left
,
361 "all buttons are supposed to be of same width" );
364 const int widthNeeded
= wxMSWButton::GetFittingSize(this, sizeLabel
).x
;
365 if ( widthNeeded
> wBtnNew
)
366 wBtnNew
= widthNeeded
;
368 ::SetWindowText(hwndBtn
, label
.wx_str());
371 if ( wBtnNew
<= wBtnOld
)
373 // all buttons fit, nothing else to do
377 // resize the message box to be wider if needed
378 const int wBoxOld
= wxGetClientRect(GetHwnd()).right
;
380 const int CHAR_WIDTH
= GetCharWidth();
381 const int MARGIN_OUTER
= 2*CHAR_WIDTH
; // margin between box and buttons
382 const int MARGIN_INNER
= CHAR_WIDTH
; // margin between buttons
384 RECT rcBox
= wxGetWindowRect(GetHwnd());
386 const int wAllButtons
= numButtons
*(wBtnNew
+ MARGIN_INNER
) - MARGIN_INNER
;
387 int wBoxNew
= 2*MARGIN_OUTER
+ wAllButtons
;
388 if ( wBoxNew
> wBoxOld
)
390 const int dw
= wBoxNew
- wBoxOld
;
392 rcBox
.right
+= dw
- dw
/2;
394 SetWindowRect(GetHwnd(), rcBox
);
396 // surprisingly, we don't need to resize the static text control, it
397 // seems to adjust itself to the new size, at least under Windows 2003
398 // (TODO: test if this happens on older Windows versions)
400 else // the current width is big enough
406 // finally position all buttons
408 // notice that we have to take into account the difference between window
410 rcBtn
.left
= (rcBox
.left
+ rcBox
.right
- wxGetClientRect(GetHwnd()).right
+
411 wBoxNew
- wAllButtons
) / 2;
412 rcBtn
.right
= rcBtn
.left
+ wBtnNew
;
414 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
416 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
420 MoveWindowToScreenRect(hwndBtn
, rcBtn
);
422 rcBtn
.left
+= wBtnNew
+ MARGIN_INNER
;
423 rcBtn
.right
+= wBtnNew
+ MARGIN_INNER
;
427 #endif // wxUSE_MSGBOX_HOOK
430 wxFont
wxMessageDialog::GetMessageFont()
432 const NONCLIENTMETRICS
& ncm
= wxMSWImpl::GetNonClientMetrics();
433 return wxNativeFontInfo(ncm
.lfMessageFont
);
436 int wxMessageDialog::ShowMessageBox()
438 if ( !wxTheApp
->GetTopWindow() )
440 // when the message box is shown from wxApp::OnInit() (i.e. before the
441 // message loop is entered), this must be done or the next message box
442 // will never be shown - just try putting 2 calls to wxMessageBox() in
443 // OnInit() to see it
444 while ( wxTheApp
->Pending() )
445 wxTheApp
->Dispatch();
448 // use the top level window as parent if none specified
449 m_parent
= GetParentForModalDialog();
450 HWND hWnd
= m_parent
? GetHwndOf(m_parent
) : NULL
;
453 // native message box always uses the current user locale but the program
454 // may be using a different one and in this case we need to manually
455 // translate the button labels to avoid mismatch between the language of
456 // the message box text and its buttons
457 wxLocale
* const loc
= wxGetLocale();
458 if ( loc
&& loc
->GetLanguage() != wxLocale::GetSystemLanguage() )
460 if ( m_dialogStyle
& wxYES_NO
)
462 // use the strings with mnemonics here as the native message box
464 SetYesNoLabels(_("&Yes"), _("&No"));
467 // we may or not have the Ok/Cancel buttons but either we do have them
468 // or we already made the labels custom because we called
469 // SetYesNoLabels() above so doing this does no harm -- and is
470 // necessary in wxYES_NO | wxCANCEL case
472 // note that we don't use mnemonics here for consistency with the
473 // native message box (which probably doesn't use them because
474 // Enter/Esc keys can be already used to dismiss the message box
476 SetOKCancelLabels(_("OK"), _("Cancel"));
480 // translate wx style in MSW
481 unsigned int msStyle
;
482 const long wxStyle
= GetMessageDialogStyle();
483 if ( wxStyle
& wxYES_NO
)
485 #if !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
486 if (wxStyle
& wxCANCEL
)
487 msStyle
= MB_YESNOCANCEL
;
489 #endif // !(__SMARTPHONE__ && __WXWINCE__)
492 if ( wxStyle
& wxNO_DEFAULT
)
493 msStyle
|= MB_DEFBUTTON2
;
494 else if ( wxStyle
& wxCANCEL_DEFAULT
)
495 msStyle
|= MB_DEFBUTTON3
;
497 else // without Yes/No we're going to have an OK button
499 if ( wxStyle
& wxCANCEL
)
501 msStyle
= MB_OKCANCEL
;
503 if ( wxStyle
& wxCANCEL_DEFAULT
)
504 msStyle
|= MB_DEFBUTTON2
;
512 // set the icon style
513 switch ( GetEffectiveIcon() )
516 msStyle
|= MB_ICONHAND
;
520 msStyle
|= MB_ICONEXCLAMATION
;
523 case wxICON_QUESTION
:
524 msStyle
|= MB_ICONQUESTION
;
527 case wxICON_INFORMATION
:
528 msStyle
|= MB_ICONINFORMATION
;
532 if ( wxStyle
& wxSTAY_ON_TOP
)
533 msStyle
|= MB_TOPMOST
;
536 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
537 msStyle
|= MB_RTLREADING
| MB_RIGHT
;
541 msStyle
|= MB_APPLMODAL
;
543 msStyle
|= MB_TASKMODAL
;
545 // per MSDN documentation for MessageBox() we can prefix the message with 2
546 // right-to-left mark characters to tell the function to use RTL layout
547 // (unfortunately this only works in Unicode builds)
548 wxString message
= GetFullMessage();
550 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
552 // NB: not all compilers support \u escapes
553 static const wchar_t wchRLM
= 0x200f;
554 message
.Prepend(wxString(wchRLM
, 2));
556 #endif // wxUSE_UNICODE
558 #if wxUSE_MSGBOX_HOOK
559 // install the hook in any case as we don't know in advance if the message
560 // box is not going to be too big (requiring the replacement of the static
561 // control with an edit one)
562 const DWORD tid
= ::GetCurrentThreadId();
563 m_hook
= ::SetWindowsHookEx(WH_CBT
,
564 &wxMessageDialog::HookFunction
, NULL
, tid
);
565 HookMap()[tid
] = this;
566 #endif // wxUSE_MSGBOX_HOOK
568 // do show the dialog
569 int msAns
= MessageBox(hWnd
, message
.wx_str(), m_caption
.wx_str(), msStyle
);
571 return MSWTranslateReturnCode(msAns
);
574 int wxMessageDialog::ShowModal()
576 #ifdef wxHAS_MSW_TASKDIALOG
577 if ( HasNativeTaskDialog() )
579 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
580 wxCHECK_MSG( taskDialogIndirect
, wxID_CANCEL
, wxS("no task dialog?") );
582 WinStruct
<TASKDIALOGCONFIG
> tdc
;
583 wxMSWTaskDialogConfig
wxTdc( *this );
584 wxTdc
.MSWCommonTaskDialogInit( tdc
);
587 HRESULT hr
= taskDialogIndirect( &tdc
, &msAns
, NULL
, NULL
);
590 wxLogApiError( "TaskDialogIndirect", hr
);
594 return MSWTranslateReturnCode( msAns
);
596 #endif // wxHAS_MSW_TASKDIALOG
598 return ShowMessageBox();
601 void wxMessageDialog::DoCentre(int dir
)
603 #ifdef wxHAS_MSW_TASKDIALOG
604 // Task dialog is always centered on its parent window and trying to center
605 // it manually doesn't work because its HWND is not created yet so don't
606 // even try as this would only result in (debug) error messages.
607 if ( HasNativeTaskDialog() )
609 #endif // wxHAS_MSW_TASKDIALOG
611 wxMessageDialogBase::DoCentre(dir
);
614 // ----------------------------------------------------------------------------
615 // Helpers of the wxMSWMessageDialog namespace
616 // ----------------------------------------------------------------------------
618 #ifdef wxHAS_MSW_TASKDIALOG
620 wxMSWTaskDialogConfig::wxMSWTaskDialogConfig(const wxMessageDialogBase
& dlg
)
621 : buttons(new TASKDIALOG_BUTTON
[3])
623 parent
= dlg
.GetParentForModalDialog();
624 caption
= dlg
.GetCaption();
625 message
= dlg
.GetMessage();
626 extendedMessage
= dlg
.GetExtendedMessage();
628 // Before wxMessageDialog added support for extended message it was common
629 // practice to have long multiline texts in the message box with the first
630 // line playing the role of the main message and the rest of the extended
631 // one. Try to detect such usage automatically here by synthesizing the
632 // extended message on our own if it wasn't given.
633 if ( extendedMessage
.empty() )
635 // Check if there is a blank separating line after the first line (this
636 // is not the same as searching for "\n\n" as we want the automatically
637 // recognized main message be single line to avoid embarrassing false
639 const size_t posNL
= message
.find('\n');
640 if ( posNL
!= wxString::npos
&&
641 posNL
< message
.length() - 1 &&
642 message
[posNL
+ 1 ] == '\n' )
644 extendedMessage
.assign(message
, posNL
+ 2, wxString::npos
);
645 message
.erase(posNL
);
649 iconId
= dlg
.GetEffectiveIcon();
650 style
= dlg
.GetMessageDialogStyle();
651 useCustomLabels
= dlg
.HasCustomLabels();
652 btnYesLabel
= dlg
.GetYesLabel();
653 btnNoLabel
= dlg
.GetNoLabel();
654 btnOKLabel
= dlg
.GetOKLabel();
655 btnCancelLabel
= dlg
.GetCancelLabel();
658 void wxMSWTaskDialogConfig::MSWCommonTaskDialogInit(TASKDIALOGCONFIG
&tdc
)
660 tdc
.dwFlags
= TDF_EXPAND_FOOTER_AREA
| TDF_POSITION_RELATIVE_TO_WINDOW
;
661 tdc
.hInstance
= wxGetInstance();
662 tdc
.pszWindowTitle
= caption
.wx_str();
664 // use the top level window as parent if none specified
665 tdc
.hwndParent
= parent
? GetHwndOf(parent
) : NULL
;
667 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
668 tdc
.dwFlags
|= TDF_RTL_LAYOUT
;
670 // If we have both the main and extended messages, just use them as
671 // intended. However if only one message is given we normally use it as the
672 // content and not as the main instruction because the latter is supposed
673 // to stand out compared to the former and doesn't look good if there is
674 // nothing for it to contrast with. Finally, notice that the extended
675 // message we use here might be automatically extracted from the main
676 // message in our ctor, see comment there.
677 if ( !extendedMessage
.empty() )
679 tdc
.pszMainInstruction
= message
.wx_str();
680 tdc
.pszContent
= extendedMessage
.wx_str();
684 tdc
.pszContent
= message
.wx_str();
687 // set an icon to be used, if possible
691 tdc
.pszMainIcon
= TD_ERROR_ICON
;
695 tdc
.pszMainIcon
= TD_WARNING_ICON
;
698 case wxICON_INFORMATION
:
699 tdc
.pszMainIcon
= TD_INFORMATION_ICON
;
703 // custom label button array that can hold all buttons in use
704 tdc
.pButtons
= buttons
.get();
706 if ( style
& wxYES_NO
)
708 AddTaskDialogButton(tdc
, IDYES
, TDCBF_YES_BUTTON
, btnYesLabel
);
709 AddTaskDialogButton(tdc
, IDNO
, TDCBF_NO_BUTTON
, btnNoLabel
);
711 if (style
& wxCANCEL
)
712 AddTaskDialogButton(tdc
, IDCANCEL
,
713 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
715 if ( style
& wxNO_DEFAULT
)
716 tdc
.nDefaultButton
= IDNO
;
717 else if ( style
& wxCANCEL_DEFAULT
)
718 tdc
.nDefaultButton
= IDCANCEL
;
720 else // without Yes/No we're going to have an OK button
722 AddTaskDialogButton(tdc
, IDOK
, TDCBF_OK_BUTTON
, btnOKLabel
);
724 if ( style
& wxCANCEL
)
726 AddTaskDialogButton(tdc
, IDCANCEL
,
727 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
729 if ( style
& wxCANCEL_DEFAULT
)
730 tdc
.nDefaultButton
= IDCANCEL
;
735 void wxMSWTaskDialogConfig::AddTaskDialogButton(TASKDIALOGCONFIG
&tdc
,
738 const wxString
& customLabel
)
740 if ( useCustomLabels
)
742 // use custom buttons to implement custom labels
743 TASKDIALOG_BUTTON
&tdBtn
= buttons
[tdc
.cButtons
];
745 tdBtn
.nButtonID
= btnCustomId
;
746 tdBtn
.pszButtonText
= customLabel
.wx_str();
751 tdc
.dwCommonButtons
|= btnCommonId
;
755 // Task dialog can be used from different threads (and wxProgressDialog always
756 // uses it from another thread in fact) so protect access to the static
757 // variable below with a critical section.
758 wxCRIT_SECT_DECLARE(gs_csTaskDialogIndirect
);
760 TaskDialogIndirect_t
wxMSWMessageDialog::GetTaskDialogIndirectFunc()
762 // Initialize the function pointer to an invalid value different from NULL
763 // to avoid reloading comctl32.dll and trying to resolve it every time
764 // we're called if task dialog is not available (notice that this may
765 // happen even under Vista+ if we don't use comctl32.dll v6).
766 static const TaskDialogIndirect_t
767 INVALID_TASKDIALOG_FUNC
= reinterpret_cast<TaskDialogIndirect_t
>(-1);
768 static TaskDialogIndirect_t s_TaskDialogIndirect
= INVALID_TASKDIALOG_FUNC
;
770 wxCRIT_SECT_LOCKER(lock
, gs_csTaskDialogIndirect
);
772 if ( s_TaskDialogIndirect
== INVALID_TASKDIALOG_FUNC
)
774 wxLoadedDLL
dllComCtl32("comctl32.dll");
775 wxDL_INIT_FUNC(s_
, TaskDialogIndirect
, dllComCtl32
);
778 return s_TaskDialogIndirect
;
781 #endif // wxHAS_MSW_TASKDIALOG
783 bool wxMSWMessageDialog::HasNativeTaskDialog()
785 #ifdef wxHAS_MSW_TASKDIALOG
786 if ( wxGetWinVersion() >= wxWinVersion_6
)
788 if ( wxMSWMessageDialog::GetTaskDialogIndirectFunc() )
791 #endif // wxHAS_MSW_TASKDIALOG
796 int wxMSWMessageDialog::MSWTranslateReturnCode(int msAns
)
802 wxFAIL_MSG(wxT("unexpected return code"));
822 #endif // wxUSE_MSGDLG