1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/msgdlg.cpp
3 // Purpose: wxMessageDialog
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
20 // there is no hook support under CE so we can't use the code for message box
23 #define wxUSE_MSGBOX_HOOK 1
25 #define wxUSE_MSGBOX_HOOK 0
29 #include "wx/msgdlg.h"
33 #include "wx/msw/private.h"
35 #include "wx/hashmap.h"
39 #include "wx/ptr_scpd.h"
40 #include "wx/dynlib.h"
41 #include "wx/msw/private/button.h"
42 #include "wx/msw/private/metrics.h"
43 #include "wx/msw/private/msgdlg.h"
44 #include "wx/modalhook.h"
45 #include "wx/fontutil.h"
48 #include "wx/textbuf.h"
49 #include "wx/display.h"
54 #include "wx/msw/wince/missing.h"
57 // Interestingly, this symbol currently seems to be absent from Platform SDK
58 // headers but it is documented at MSDN.
59 #ifndef TDF_SIZE_TO_CONTENT
60 #define TDF_SIZE_TO_CONTENT 0x1000000
63 using namespace wxMSWMessageDialog
;
65 IMPLEMENT_CLASS(wxMessageDialog
, wxDialog
)
69 // there can potentially be one message box per thread so we use a hash map
70 // with thread ids as keys and (currently shown) message boxes as values
72 // TODO: replace this with wxTLS once it's available
73 WX_DECLARE_HASH_MAP(unsigned long, wxMessageDialog
*,
74 wxIntegerHash
, wxIntegerEqual
,
77 // the order in this array is the one in which buttons appear in the
79 const wxMessageDialog::ButtonAccessors
wxMessageDialog::ms_buttons
[] =
81 { IDYES
, &wxMessageDialog::GetYesLabel
},
82 { IDNO
, &wxMessageDialog::GetNoLabel
},
83 { IDOK
, &wxMessageDialog::GetOKLabel
},
84 { IDCANCEL
, &wxMessageDialog::GetCancelLabel
},
90 wxMessageDialogMap
& HookMap()
92 static wxMessageDialogMap s_Map
;
98 All this code is used for adjusting the message box layout when we mess
99 with its contents. It's rather complicated because we try hard to avoid
100 assuming much about the standard layout details and so, instead of just
101 laying out everything ourselves (which would have been so much simpler!)
102 we try to only modify the existing controls positions by offsetting them
103 from their default ones in the hope that this will continue to work with
104 the future Windows versions.
107 // convert the given RECT from screen to client coordinates in place
108 void ScreenRectToClient(HWND hwnd
, RECT
& rc
)
110 // map from desktop (i.e. screen) coordinates to ones of this window
112 // notice that a RECT is laid out as 2 consecutive POINTs so the cast is
114 ::MapWindowPoints(HWND_DESKTOP
, hwnd
, reinterpret_cast<POINT
*>(&rc
), 2);
117 // set window position to the given rect
118 inline void SetWindowRect(HWND hwnd
, const RECT
& rc
)
122 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
126 // set window position expressed in screen coordinates, whether the window is
127 // child or top level
128 void MoveWindowToScreenRect(HWND hwnd
, RECT rc
)
130 ScreenRectToClient(::GetParent(hwnd
), rc
);
132 SetWindowRect(hwnd
, rc
);
135 // helper of AdjustButtonLabels(): move the given window by dx
137 // works for both child and top level windows
138 void OffsetWindow(HWND hwnd
, int dx
)
140 RECT rc
= wxGetWindowRect(hwnd
);
145 MoveWindowToScreenRect(hwnd
, rc
);
148 } // anonymous namespace
152 wxMessageDialog::HookFunction(int code
, WXWPARAM wParam
, WXLPARAM lParam
)
154 // Find the thread-local instance of wxMessageDialog
155 const DWORD tid
= ::GetCurrentThreadId();
156 wxMessageDialogMap::iterator node
= HookMap().find(tid
);
157 wxCHECK_MSG( node
!= HookMap().end(), false,
158 wxT("bogus thread id in wxMessageDialog::Hook") );
160 wxMessageDialog
* const wnd
= node
->second
;
162 const HHOOK hhook
= (HHOOK
)wnd
->m_hook
;
163 const LRESULT rc
= ::CallNextHookEx(hhook
, code
, wParam
, lParam
);
165 if ( code
== HCBT_ACTIVATE
)
167 // we won't need this hook any longer
168 ::UnhookWindowsHookEx(hhook
);
170 HookMap().erase(tid
);
172 wnd
->SetHWND((HWND
)wParam
);
174 // replace the static text with an edit control if the message box is
175 // too big to fit the display
176 wnd
->ReplaceStaticWithEdit();
178 // update the labels if necessary: we need to do it before centering
179 // the dialog as this can change its size
180 if ( wnd
->HasCustomLabels() )
181 wnd
->AdjustButtonLabels();
183 // centre the message box on its parent if requested
184 if ( wnd
->GetMessageDialogStyle() & wxCENTER
)
185 wnd
->Center(); // center on parent
186 //else: default behaviour, center on screen
188 // there seems to be no reason to leave it set
195 void wxMessageDialog::ReplaceStaticWithEdit()
197 // check if the message box fits the display
198 int nDisplay
= wxDisplay::GetFromWindow(this);
199 if ( nDisplay
== wxNOT_FOUND
)
201 const wxRect rectDisplay
= wxDisplay(nDisplay
).GetClientArea();
203 if ( rectDisplay
.Contains(GetRect()) )
210 // find the static control to replace: normally there are two of them, the
211 // icon and the text itself so search for all of them and ignore the icon
213 HWND hwndStatic
= ::FindWindowEx(GetHwnd(), NULL
, wxT("STATIC"), NULL
);
214 if ( ::GetWindowLong(hwndStatic
, GWL_STYLE
) & SS_ICON
)
215 hwndStatic
= ::FindWindowEx(GetHwnd(), hwndStatic
, wxT("STATIC"), NULL
);
219 wxLogDebug("Failed to find the static text control in message box.");
223 // set the right font for GetCharHeight() call below
224 wxWindowBase::SetFont(GetMessageFont());
226 // put the new edit control at the same place
227 RECT rc
= wxGetWindowRect(hwndStatic
);
228 ScreenRectToClient(GetHwnd(), rc
);
230 // but make it less tall so that the message box fits on the screen: we try
231 // to make the message box take no more than 7/8 of the screen to leave
232 // some space above and below it
233 const int hText
= (7*rectDisplay
.height
)/8 -
235 2*::GetSystemMetrics(SM_CYFIXEDFRAME
) +
236 ::GetSystemMetrics(SM_CYCAPTION
) +
237 5*GetCharHeight() // buttons + margins
239 const int dh
= (rc
.bottom
- rc
.top
) - hText
; // vertical space we save
242 // and it also must be wider as it needs a vertical scrollbar (in order
243 // to preserve the word wrap, otherwise the number of lines would change
244 // and we want the control to look as similar as possible to the original)
246 // NB: you would have thought that 2*SM_CXEDGE would be enough but it
247 // isn't, somehow, and the text control breaks lines differently from
248 // the static one so fudge by adding some extra space
249 const int dw
= ::GetSystemMetrics(SM_CXVSCROLL
) +
250 4*::GetSystemMetrics(SM_CXEDGE
);
254 // chop of the trailing new line(s) from the message box text, they are
255 // ignored by the static control but result in extra lines and hence extra
256 // scrollbar position in the edit one
257 wxString
text(wxGetWindowText(hwndStatic
));
258 for ( wxString::reverse_iterator i
= text
.rbegin(); i
!= text
.rend(); ++i
)
262 // found last non-newline char, remove anything after it if
263 // necessary and stop in any case
264 if ( i
!= text
.rbegin() )
265 text
.erase(i
.base() + 1, text
.end());
270 // do create the new control
271 HWND hwndEdit
= ::CreateWindow
274 wxTextBuffer::Translate(text
).t_str(),
275 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
276 ES_MULTILINE
| ES_READONLY
| ES_AUTOVSCROLL
,
278 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
,
287 wxLogDebug("Creation of replacement edit control failed in message box");
291 // copy the font from the original control
292 LRESULT hfont
= ::SendMessage(hwndStatic
, WM_GETFONT
, 0, 0);
293 ::SendMessage(hwndEdit
, WM_SETFONT
, hfont
, 0);
296 ::DestroyWindow(hwndStatic
);
299 // shrink and centre the message box vertically and widen it box to account
300 // for the extra scrollbar
301 RECT rcBox
= wxGetWindowRect(GetHwnd());
302 const int hMsgBox
= rcBox
.bottom
- rcBox
.top
- dh
;
303 rcBox
.top
= (rectDisplay
.height
- hMsgBox
)/2;
304 rcBox
.bottom
= rcBox
.top
+ hMsgBox
+ (rectDisplay
.height
- hMsgBox
)%2
;
306 rcBox
.right
+= dw
- dw
/2;
307 SetWindowRect(GetHwnd(), rcBox
);
309 // and adjust all the buttons positions
310 for ( unsigned n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
312 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
314 continue; // it's ok, not all buttons are always present
316 RECT rc
= wxGetWindowRect(hwndBtn
);
321 MoveWindowToScreenRect(hwndBtn
, rc
);
325 void wxMessageDialog::AdjustButtonLabels()
327 // changing the button labels is the easy part but we also need to ensure
328 // that the buttons are big enough for the label strings and increase their
329 // size (and maybe the size of the message box itself) if they are not
331 // TODO-RTL: check whether this works correctly in RTL
333 // we want to use this font in GetTextExtent() calls below but we don't
334 // want to send WM_SETFONT to the message box, who knows how is it going to
335 // react to it (right now it doesn't seem to do anything but what if this
337 wxWindowBase::SetFont(GetMessageFont());
339 // first iteration: find the widest button and update the buttons labels
340 int wBtnOld
= 0, // current buttons width
341 wBtnNew
= 0; // required new buttons width
342 RECT rcBtn
; // stores the button height and y positions
343 unsigned numButtons
= 0; // total number of buttons in the message box
345 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
347 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
349 continue; // it's ok, not all buttons are always present
353 const wxString label
= (this->*ms_buttons
[n
].getter
)();
354 const wxSize sizeLabel
= wxWindowBase::GetTextExtent(label
);
356 // check if the button is big enough for this label
357 const RECT rc
= wxGetWindowRect(hwndBtn
);
360 // initialize wBtnOld using the first button width, all the other
361 // ones should have the same one
362 wBtnOld
= rc
.right
- rc
.left
;
364 rcBtn
= rc
; // remember for use below when we reposition the buttons
368 wxASSERT_MSG( wBtnOld
== rc
.right
- rc
.left
,
369 "all buttons are supposed to be of same width" );
372 const int widthNeeded
= wxMSWButton::GetFittingSize(this, sizeLabel
).x
;
373 if ( widthNeeded
> wBtnNew
)
374 wBtnNew
= widthNeeded
;
376 ::SetWindowText(hwndBtn
, label
.t_str());
379 if ( wBtnNew
<= wBtnOld
)
381 // all buttons fit, nothing else to do
385 // resize the message box to be wider if needed
386 const int wBoxOld
= wxGetClientRect(GetHwnd()).right
;
388 const int CHAR_WIDTH
= GetCharWidth();
389 const int MARGIN_OUTER
= 2*CHAR_WIDTH
; // margin between box and buttons
390 const int MARGIN_INNER
= CHAR_WIDTH
; // margin between buttons
392 RECT rcBox
= wxGetWindowRect(GetHwnd());
394 const int wAllButtons
= numButtons
*(wBtnNew
+ MARGIN_INNER
) - MARGIN_INNER
;
395 int wBoxNew
= 2*MARGIN_OUTER
+ wAllButtons
;
396 if ( wBoxNew
> wBoxOld
)
398 const int dw
= wBoxNew
- wBoxOld
;
400 rcBox
.right
+= dw
- dw
/2;
402 SetWindowRect(GetHwnd(), rcBox
);
404 // surprisingly, we don't need to resize the static text control, it
405 // seems to adjust itself to the new size, at least under Windows 2003
406 // (TODO: test if this happens on older Windows versions)
408 else // the current width is big enough
414 // finally position all buttons
416 // notice that we have to take into account the difference between window
418 rcBtn
.left
= (rcBox
.left
+ rcBox
.right
- wxGetClientRect(GetHwnd()).right
+
419 wBoxNew
- wAllButtons
) / 2;
420 rcBtn
.right
= rcBtn
.left
+ wBtnNew
;
422 for ( n
= 0; n
< WXSIZEOF(ms_buttons
); n
++ )
424 const HWND hwndBtn
= ::GetDlgItem(GetHwnd(), ms_buttons
[n
].id
);
428 MoveWindowToScreenRect(hwndBtn
, rcBtn
);
430 rcBtn
.left
+= wBtnNew
+ MARGIN_INNER
;
431 rcBtn
.right
+= wBtnNew
+ MARGIN_INNER
;
435 #endif // wxUSE_MSGBOX_HOOK
438 wxFont
wxMessageDialog::GetMessageFont()
440 const NONCLIENTMETRICS
& ncm
= wxMSWImpl::GetNonClientMetrics();
441 return wxNativeFontInfo(ncm
.lfMessageFont
);
444 int wxMessageDialog::ShowMessageBox()
446 if ( !wxTheApp
->GetTopWindow() )
448 // when the message box is shown from wxApp::OnInit() (i.e. before the
449 // message loop is entered), this must be done or the next message box
450 // will never be shown - just try putting 2 calls to wxMessageBox() in
451 // OnInit() to see it
452 while ( wxTheApp
->Pending() )
453 wxTheApp
->Dispatch();
456 // use the top level window as parent if none specified
457 m_parent
= GetParentForModalDialog();
458 HWND hWnd
= m_parent
? GetHwndOf(m_parent
) : NULL
;
461 // native message box always uses the current user locale but the program
462 // may be using a different one and in this case we need to manually
463 // translate the default button labels (if they're non default we have no
464 // way to translate them and so we must assume they were already
465 // translated) to avoid mismatch between the language of the message box
466 // text and its buttons
467 wxLocale
* const loc
= wxGetLocale();
468 if ( loc
&& loc
->GetLanguage() != wxLocale::GetSystemLanguage() )
470 if ( m_dialogStyle
& wxYES_NO
&&
471 (GetCustomYesLabel().empty() && GetCustomNoLabel().empty()) )
474 // use the strings with mnemonics here as the native message box
476 SetYesNoLabels(_("&Yes"), _("&No"));
479 // we may or not have the Ok/Cancel buttons but either we do have them
480 // or we already made the labels custom because we called
481 // SetYesNoLabels() above so doing this does no harm -- and is
482 // necessary in wxYES_NO | wxCANCEL case
484 // note that we don't use mnemonics here for consistency with the
485 // native message box (which probably doesn't use them because
486 // Enter/Esc keys can be already used to dismiss the message box
488 if ( GetCustomOKLabel().empty() && GetCustomCancelLabel().empty() )
489 SetOKCancelLabels(_("OK"), _("Cancel"));
493 // translate wx style in MSW
494 unsigned int msStyle
;
495 const long wxStyle
= GetMessageDialogStyle();
496 if ( wxStyle
& wxYES_NO
)
498 #if !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
499 if (wxStyle
& wxCANCEL
)
500 msStyle
= MB_YESNOCANCEL
;
502 #endif // !(__SMARTPHONE__ && __WXWINCE__)
505 if ( wxStyle
& wxNO_DEFAULT
)
506 msStyle
|= MB_DEFBUTTON2
;
507 else if ( wxStyle
& wxCANCEL_DEFAULT
)
508 msStyle
|= MB_DEFBUTTON3
;
510 else // without Yes/No we're going to have an OK button
512 if ( wxStyle
& wxCANCEL
)
514 msStyle
= MB_OKCANCEL
;
516 if ( wxStyle
& wxCANCEL_DEFAULT
)
517 msStyle
|= MB_DEFBUTTON2
;
525 if ( wxStyle
& wxHELP
)
530 // set the icon style
531 switch ( GetEffectiveIcon() )
534 msStyle
|= MB_ICONHAND
;
538 msStyle
|= MB_ICONEXCLAMATION
;
541 case wxICON_QUESTION
:
542 msStyle
|= MB_ICONQUESTION
;
545 case wxICON_INFORMATION
:
546 msStyle
|= MB_ICONINFORMATION
;
550 if ( wxStyle
& wxSTAY_ON_TOP
)
551 msStyle
|= MB_TOPMOST
;
554 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
555 msStyle
|= MB_RTLREADING
| MB_RIGHT
;
559 msStyle
|= MB_APPLMODAL
;
561 msStyle
|= MB_TASKMODAL
;
563 // per MSDN documentation for MessageBox() we can prefix the message with 2
564 // right-to-left mark characters to tell the function to use RTL layout
565 // (unfortunately this only works in Unicode builds)
566 wxString message
= GetFullMessage();
568 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
570 // NB: not all compilers support \u escapes
571 static const wchar_t wchRLM
= 0x200f;
572 message
.Prepend(wxString(wchRLM
, 2));
574 #endif // wxUSE_UNICODE
576 #if wxUSE_MSGBOX_HOOK
577 // install the hook in any case as we don't know in advance if the message
578 // box is not going to be too big (requiring the replacement of the static
579 // control with an edit one)
580 const DWORD tid
= ::GetCurrentThreadId();
581 m_hook
= ::SetWindowsHookEx(WH_CBT
,
582 &wxMessageDialog::HookFunction
, NULL
, tid
);
583 HookMap()[tid
] = this;
584 #endif // wxUSE_MSGBOX_HOOK
586 // do show the dialog
587 int msAns
= MessageBox(hWnd
, message
.t_str(), m_caption
.t_str(), msStyle
);
589 int ret
= MSWTranslateReturnCode(msAns
);
594 int wxMessageDialog::ShowModal()
596 WX_HOOK_MODAL_DIALOG();
598 #ifdef wxHAS_MSW_TASKDIALOG
599 if ( HasNativeTaskDialog() )
601 TaskDialogIndirect_t taskDialogIndirect
= GetTaskDialogIndirectFunc();
602 wxCHECK_MSG( taskDialogIndirect
, wxID_CANCEL
, wxS("no task dialog?") );
604 WinStruct
<TASKDIALOGCONFIG
> tdc
;
605 wxMSWTaskDialogConfig
wxTdc( *this );
606 wxTdc
.MSWCommonTaskDialogInit( tdc
);
609 HRESULT hr
= taskDialogIndirect( &tdc
, &msAns
, NULL
, NULL
);
612 wxLogApiError( "TaskDialogIndirect", hr
);
616 // In case only an "OK" button was specified we actually created a
617 // "Cancel" button (see comment in MSWCommonTaskDialogInit). This
618 // results in msAns being IDCANCEL while we want IDOK (just like
619 // how the native MessageBox function does with only an "OK" button).
620 if ( (msAns
== IDCANCEL
)
621 && !(GetMessageDialogStyle() & (wxYES_NO
|wxCANCEL
)) )
626 int ret
= MSWTranslateReturnCode(msAns
);
630 #endif // wxHAS_MSW_TASKDIALOG
632 return ShowMessageBox();
635 long wxMessageDialog::GetEffectiveIcon() const
637 // only use the auth needed icon if available, otherwise fallback to the default logic
638 if ( (m_dialogStyle
& wxICON_AUTH_NEEDED
) &&
639 wxMSWMessageDialog::HasNativeTaskDialog() )
641 return wxICON_AUTH_NEEDED
;
644 return wxMessageDialogBase::GetEffectiveIcon();
647 void wxMessageDialog::DoCentre(int dir
)
649 #ifdef wxHAS_MSW_TASKDIALOG
650 // Task dialog is always centered on its parent window and trying to center
651 // it manually doesn't work because its HWND is not created yet so don't
652 // even try as this would only result in (debug) error messages.
653 if ( HasNativeTaskDialog() )
655 #endif // wxHAS_MSW_TASKDIALOG
657 wxMessageDialogBase::DoCentre(dir
);
660 // ----------------------------------------------------------------------------
661 // Helpers of the wxMSWMessageDialog namespace
662 // ----------------------------------------------------------------------------
664 #ifdef wxHAS_MSW_TASKDIALOG
666 wxMSWTaskDialogConfig::wxMSWTaskDialogConfig(const wxMessageDialogBase
& dlg
)
667 : buttons(new TASKDIALOG_BUTTON
[MAX_BUTTONS
])
669 parent
= dlg
.GetParentForModalDialog();
670 caption
= dlg
.GetCaption();
671 message
= dlg
.GetMessage();
672 extendedMessage
= dlg
.GetExtendedMessage();
674 // Before wxMessageDialog added support for extended message it was common
675 // practice to have long multiline texts in the message box with the first
676 // line playing the role of the main message and the rest of the extended
677 // one. Try to detect such usage automatically here by synthesizing the
678 // extended message on our own if it wasn't given.
679 if ( extendedMessage
.empty() )
681 // Check if there is a blank separating line after the first line (this
682 // is not the same as searching for "\n\n" as we want the automatically
683 // recognized main message be single line to avoid embarrassing false
685 const size_t posNL
= message
.find('\n');
686 if ( posNL
!= wxString::npos
&&
687 posNL
< message
.length() - 1 &&
688 message
[posNL
+ 1 ] == '\n' )
690 extendedMessage
.assign(message
, posNL
+ 2, wxString::npos
);
691 message
.erase(posNL
);
695 iconId
= dlg
.GetEffectiveIcon();
696 style
= dlg
.GetMessageDialogStyle();
697 useCustomLabels
= dlg
.HasCustomLabels();
698 btnYesLabel
= dlg
.GetYesLabel();
699 btnNoLabel
= dlg
.GetNoLabel();
700 btnOKLabel
= dlg
.GetOKLabel();
701 btnCancelLabel
= dlg
.GetCancelLabel();
702 btnHelpLabel
= dlg
.GetHelpLabel();
705 void wxMSWTaskDialogConfig::MSWCommonTaskDialogInit(TASKDIALOGCONFIG
&tdc
)
707 // Use TDF_SIZE_TO_CONTENT to try to prevent Windows from truncating or
708 // ellipsizing the message text. This doesn't always work as Windows will
709 // still do it if the message contains too long "words" (i.e. runs of the
710 // text without spaces) but at least it ensures that the message text is
711 // fully shown for reasonably-sized words whereas without it using almost
712 // any file system path in a message box would result in truncation.
713 tdc
.dwFlags
= TDF_EXPAND_FOOTER_AREA
|
714 TDF_POSITION_RELATIVE_TO_WINDOW
|
716 tdc
.hInstance
= wxGetInstance();
717 tdc
.pszWindowTitle
= caption
.t_str();
719 // use the top level window as parent if none specified
720 tdc
.hwndParent
= parent
? GetHwndOf(parent
) : NULL
;
722 if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft
)
723 tdc
.dwFlags
|= TDF_RTL_LAYOUT
;
725 // If we have both the main and extended messages, just use them as
726 // intended. However if only one message is given we normally use it as the
727 // content and not as the main instruction because the latter is supposed
728 // to stand out compared to the former and doesn't look good if there is
729 // nothing for it to contrast with. Finally, notice that the extended
730 // message we use here might be automatically extracted from the main
731 // message in our ctor, see comment there.
732 if ( !extendedMessage
.empty() )
734 tdc
.pszMainInstruction
= message
.t_str();
735 tdc
.pszContent
= extendedMessage
.t_str();
739 tdc
.pszContent
= message
.t_str();
742 // set an icon to be used, if possible
746 tdc
.pszMainIcon
= TD_ERROR_ICON
;
750 tdc
.pszMainIcon
= TD_WARNING_ICON
;
753 case wxICON_INFORMATION
:
754 tdc
.pszMainIcon
= TD_INFORMATION_ICON
;
757 case wxICON_AUTH_NEEDED
:
758 tdc
.pszMainIcon
= TD_SHIELD_ICON
;
762 // custom label button array that can hold all buttons in use
763 tdc
.pButtons
= buttons
.get();
765 if ( style
& wxYES_NO
)
767 AddTaskDialogButton(tdc
, IDYES
, TDCBF_YES_BUTTON
, btnYesLabel
);
768 AddTaskDialogButton(tdc
, IDNO
, TDCBF_NO_BUTTON
, btnNoLabel
);
770 if (style
& wxCANCEL
)
771 AddTaskDialogButton(tdc
, IDCANCEL
,
772 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
774 if ( style
& wxNO_DEFAULT
)
775 tdc
.nDefaultButton
= IDNO
;
776 else if ( style
& wxCANCEL_DEFAULT
)
777 tdc
.nDefaultButton
= IDCANCEL
;
779 else // without Yes/No we're going to have an OK button
781 if ( style
& wxCANCEL
)
783 AddTaskDialogButton(tdc
, IDOK
, TDCBF_OK_BUTTON
, btnOKLabel
);
784 AddTaskDialogButton(tdc
, IDCANCEL
,
785 TDCBF_CANCEL_BUTTON
, btnCancelLabel
);
787 if ( style
& wxCANCEL_DEFAULT
)
788 tdc
.nDefaultButton
= IDCANCEL
;
792 // We actually create a "Cancel" button instead because we want to
793 // allow closing the dialog box with Escape (and also Alt-F4 or
794 // clicking the close button in the title bar) which wouldn't work
795 // without a Cancel button.
796 if ( !useCustomLabels
)
798 useCustomLabels
= true;
799 btnOKLabel
= _("OK");
802 AddTaskDialogButton(tdc
, IDCANCEL
, TDCBF_CANCEL_BUTTON
, btnOKLabel
);
806 if ( style
& wxHELP
)
808 // There is no support for "Help" button in the task dialog, it can
809 // only show "Retry" or "Close" ones.
810 useCustomLabels
= true;
812 AddTaskDialogButton(tdc
, IDHELP
, 0 /* not used */, btnHelpLabel
);
816 void wxMSWTaskDialogConfig::AddTaskDialogButton(TASKDIALOGCONFIG
&tdc
,
819 const wxString
& customLabel
)
821 if ( useCustomLabels
)
823 // use custom buttons to implement custom labels
824 TASKDIALOG_BUTTON
&tdBtn
= buttons
[tdc
.cButtons
];
826 tdBtn
.nButtonID
= btnCustomId
;
827 tdBtn
.pszButtonText
= customLabel
.t_str();
830 // We should never have more than 4 buttons currently as this is the
831 // maximal number of buttons supported by the message dialog.
832 wxASSERT_MSG( tdc
.cButtons
<= MAX_BUTTONS
, wxT("Too many buttons") );
836 tdc
.dwCommonButtons
|= btnCommonId
;
840 // Task dialog can be used from different threads (and wxProgressDialog always
841 // uses it from another thread in fact) so protect access to the static
842 // variable below with a critical section.
843 wxCRIT_SECT_DECLARE(gs_csTaskDialogIndirect
);
845 TaskDialogIndirect_t
wxMSWMessageDialog::GetTaskDialogIndirectFunc()
847 // Initialize the function pointer to an invalid value different from NULL
848 // to avoid reloading comctl32.dll and trying to resolve it every time
849 // we're called if task dialog is not available (notice that this may
850 // happen even under Vista+ if we don't use comctl32.dll v6).
851 static const TaskDialogIndirect_t
852 INVALID_TASKDIALOG_FUNC
= reinterpret_cast<TaskDialogIndirect_t
>(-1);
853 static TaskDialogIndirect_t s_TaskDialogIndirect
= INVALID_TASKDIALOG_FUNC
;
855 wxCRIT_SECT_LOCKER(lock
, gs_csTaskDialogIndirect
);
857 if ( s_TaskDialogIndirect
== INVALID_TASKDIALOG_FUNC
)
859 wxLoadedDLL
dllComCtl32("comctl32.dll");
860 wxDL_INIT_FUNC(s_
, TaskDialogIndirect
, dllComCtl32
);
863 return s_TaskDialogIndirect
;
866 #endif // wxHAS_MSW_TASKDIALOG
868 bool wxMSWMessageDialog::HasNativeTaskDialog()
870 #ifdef wxHAS_MSW_TASKDIALOG
871 if ( wxGetWinVersion() >= wxWinVersion_6
)
873 if ( wxMSWMessageDialog::GetTaskDialogIndirectFunc() )
876 #endif // wxHAS_MSW_TASKDIALOG
881 int wxMSWMessageDialog::MSWTranslateReturnCode(int msAns
)
887 wxFAIL_MSG(wxT("unexpected return code"));
910 #endif // wxUSE_MSGDLG