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 // 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
).wx_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
.wx_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
.wx_str(), m_caption
.wx_str(), msStyle
); 
 589     return MSWTranslateReturnCode(msAns
); 
 592 int wxMessageDialog::ShowModal() 
 594 #ifdef wxHAS_MSW_TASKDIALOG 
 595     if ( HasNativeTaskDialog() ) 
 597         TaskDialogIndirect_t taskDialogIndirect 
= GetTaskDialogIndirectFunc(); 
 598         wxCHECK_MSG( taskDialogIndirect
, wxID_CANCEL
, wxS("no task dialog?") ); 
 600         WinStruct
<TASKDIALOGCONFIG
> tdc
; 
 601         wxMSWTaskDialogConfig 
wxTdc( *this ); 
 602         wxTdc
.MSWCommonTaskDialogInit( tdc 
); 
 605         HRESULT hr 
= taskDialogIndirect( &tdc
, &msAns
, NULL
, NULL 
); 
 608             wxLogApiError( "TaskDialogIndirect", hr 
); 
 612         // In case only an "OK" button was specified we actually created a 
 613         // "Cancel" button (see comment in MSWCommonTaskDialogInit). This 
 614         // results in msAns being IDCANCEL while we want IDOK (just like 
 615         // how the native MessageBox function does with only an "OK" button). 
 616         if ( (msAns 
== IDCANCEL
) 
 617             && !(GetMessageDialogStyle() & (wxYES_NO
|wxCANCEL
)) ) 
 622         return MSWTranslateReturnCode( msAns 
); 
 624 #endif // wxHAS_MSW_TASKDIALOG 
 626     return ShowMessageBox(); 
 629 void wxMessageDialog::DoCentre(int dir
) 
 631 #ifdef wxHAS_MSW_TASKDIALOG 
 632     // Task dialog is always centered on its parent window and trying to center 
 633     // it manually doesn't work because its HWND is not created yet so don't 
 634     // even try as this would only result in (debug) error messages. 
 635     if ( HasNativeTaskDialog() ) 
 637 #endif // wxHAS_MSW_TASKDIALOG 
 639     wxMessageDialogBase::DoCentre(dir
); 
 642 // ---------------------------------------------------------------------------- 
 643 // Helpers of the wxMSWMessageDialog namespace 
 644 // ---------------------------------------------------------------------------- 
 646 #ifdef wxHAS_MSW_TASKDIALOG 
 648 wxMSWTaskDialogConfig::wxMSWTaskDialogConfig(const wxMessageDialogBase
& dlg
) 
 649                      : buttons(new TASKDIALOG_BUTTON
[MAX_BUTTONS
]) 
 651     parent 
= dlg
.GetParentForModalDialog(); 
 652     caption 
= dlg
.GetCaption(); 
 653     message 
= dlg
.GetMessage(); 
 654     extendedMessage 
= dlg
.GetExtendedMessage(); 
 656     // Before wxMessageDialog added support for extended message it was common 
 657     // practice to have long multiline texts in the message box with the first 
 658     // line playing the role of the main message and the rest of the extended 
 659     // one. Try to detect such usage automatically here by synthesizing the 
 660     // extended message on our own if it wasn't given. 
 661     if ( extendedMessage
.empty() ) 
 663         // Check if there is a blank separating line after the first line (this 
 664         // is not the same as searching for "\n\n" as we want the automatically 
 665         // recognized main message be single line to avoid embarrassing false 
 667         const size_t posNL 
= message
.find('\n'); 
 668         if ( posNL 
!= wxString::npos 
&& 
 669                 posNL 
< message
.length() - 1 && 
 670                     message
[posNL 
+ 1 ] == '\n' ) 
 672             extendedMessage
.assign(message
, posNL 
+ 2, wxString::npos
); 
 673             message
.erase(posNL
); 
 677     iconId 
= dlg
.GetEffectiveIcon(); 
 678     style 
= dlg
.GetMessageDialogStyle(); 
 679     useCustomLabels 
= dlg
.HasCustomLabels(); 
 680     btnYesLabel 
= dlg
.GetYesLabel(); 
 681     btnNoLabel 
= dlg
.GetNoLabel(); 
 682     btnOKLabel 
= dlg
.GetOKLabel(); 
 683     btnCancelLabel 
= dlg
.GetCancelLabel(); 
 684     btnHelpLabel 
= dlg
.GetHelpLabel(); 
 687 void wxMSWTaskDialogConfig::MSWCommonTaskDialogInit(TASKDIALOGCONFIG 
&tdc
) 
 689     // Use TDF_SIZE_TO_CONTENT to try to prevent Windows from truncating or 
 690     // ellipsizing the message text. This doesn't always work as Windows will 
 691     // still do it if the message contains too long "words" (i.e. runs of the 
 692     // text without spaces) but at least it ensures that the message text is 
 693     // fully shown for reasonably-sized words whereas without it using almost 
 694     // any file system path in a message box would result in truncation. 
 695     tdc
.dwFlags 
= TDF_EXPAND_FOOTER_AREA 
| 
 696                   TDF_POSITION_RELATIVE_TO_WINDOW 
| 
 698     tdc
.hInstance 
= wxGetInstance(); 
 699     tdc
.pszWindowTitle 
= caption
.wx_str(); 
 701     // use the top level window as parent if none specified 
 702     tdc
.hwndParent 
= parent 
? GetHwndOf(parent
) : NULL
; 
 704     if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft 
) 
 705         tdc
.dwFlags 
|= TDF_RTL_LAYOUT
; 
 707     // If we have both the main and extended messages, just use them as 
 708     // intended. However if only one message is given we normally use it as the 
 709     // content and not as the main instruction because the latter is supposed 
 710     // to stand out compared to the former and doesn't look good if there is 
 711     // nothing for it to contrast with. Finally, notice that the extended 
 712     // message we use here might be automatically extracted from the main 
 713     // message in our ctor, see comment there. 
 714     if ( !extendedMessage
.empty() ) 
 716         tdc
.pszMainInstruction 
= message
.wx_str(); 
 717         tdc
.pszContent 
= extendedMessage
.wx_str(); 
 721         tdc
.pszContent 
= message
.wx_str(); 
 724     // set an icon to be used, if possible 
 728             tdc
.pszMainIcon 
= TD_ERROR_ICON
; 
 732             tdc
.pszMainIcon 
= TD_WARNING_ICON
; 
 735         case wxICON_INFORMATION
: 
 736             tdc
.pszMainIcon 
= TD_INFORMATION_ICON
; 
 740     // custom label button array that can hold all buttons in use 
 741     tdc
.pButtons 
= buttons
.get(); 
 743     if ( style 
& wxYES_NO 
) 
 745         AddTaskDialogButton(tdc
, IDYES
, TDCBF_YES_BUTTON
, btnYesLabel
); 
 746         AddTaskDialogButton(tdc
, IDNO
,  TDCBF_NO_BUTTON
,  btnNoLabel
); 
 748         if (style 
& wxCANCEL
) 
 749             AddTaskDialogButton(tdc
, IDCANCEL
, 
 750                                 TDCBF_CANCEL_BUTTON
, btnCancelLabel
); 
 752         if ( style 
& wxNO_DEFAULT 
) 
 753             tdc
.nDefaultButton 
= IDNO
; 
 754         else if ( style 
& wxCANCEL_DEFAULT 
) 
 755             tdc
.nDefaultButton 
= IDCANCEL
; 
 757     else // without Yes/No we're going to have an OK button 
 759         if ( style 
& wxCANCEL 
) 
 761             AddTaskDialogButton(tdc
, IDOK
, TDCBF_OK_BUTTON
, btnOKLabel
); 
 762             AddTaskDialogButton(tdc
, IDCANCEL
, 
 763                                 TDCBF_CANCEL_BUTTON
, btnCancelLabel
); 
 765             if ( style 
& wxCANCEL_DEFAULT 
) 
 766                 tdc
.nDefaultButton 
= IDCANCEL
; 
 770             // We actually create a "Cancel" button instead because we want to 
 771             // allow closing the dialog box with Escape (and also Alt-F4 or 
 772             // clicking the close button in the title bar) which wouldn't work 
 773             // without a Cancel button. 
 774             if ( !useCustomLabels 
) 
 776                 useCustomLabels 
= true; 
 777                 btnOKLabel 
= _("OK"); 
 780             AddTaskDialogButton(tdc
, IDCANCEL
, TDCBF_CANCEL_BUTTON
, btnOKLabel
); 
 784     if ( style 
& wxHELP 
) 
 786         // There is no support for "Help" button in the task dialog, it can 
 787         // only show "Retry" or "Close" ones. 
 788         useCustomLabels 
= true; 
 790         AddTaskDialogButton(tdc
, IDHELP
, 0 /* not used */, btnHelpLabel
); 
 794 void wxMSWTaskDialogConfig::AddTaskDialogButton(TASKDIALOGCONFIG 
&tdc
, 
 797                                                 const wxString
& customLabel
) 
 799     if ( useCustomLabels 
) 
 801         // use custom buttons to implement custom labels 
 802         TASKDIALOG_BUTTON 
&tdBtn 
= buttons
[tdc
.cButtons
]; 
 804         tdBtn
.nButtonID 
= btnCustomId
; 
 805         tdBtn
.pszButtonText 
= customLabel
.wx_str(); 
 808         // We should never have more than 4 buttons currently as this is the 
 809         // maximal number of buttons supported by the message dialog. 
 810         wxASSERT_MSG( tdc
.cButtons 
<= MAX_BUTTONS
, wxT("Too many buttons") ); 
 814         tdc
.dwCommonButtons 
|= btnCommonId
; 
 818 // Task dialog can be used from different threads (and wxProgressDialog always 
 819 // uses it from another thread in fact) so protect access to the static 
 820 // variable below with a critical section. 
 821 wxCRIT_SECT_DECLARE(gs_csTaskDialogIndirect
); 
 823 TaskDialogIndirect_t 
wxMSWMessageDialog::GetTaskDialogIndirectFunc() 
 825     // Initialize the function pointer to an invalid value different from NULL 
 826     // to avoid reloading comctl32.dll and trying to resolve it every time 
 827     // we're called if task dialog is not available (notice that this may 
 828     // happen even under Vista+ if we don't use comctl32.dll v6). 
 829     static const TaskDialogIndirect_t
 
 830         INVALID_TASKDIALOG_FUNC 
= reinterpret_cast<TaskDialogIndirect_t
>(-1); 
 831     static TaskDialogIndirect_t s_TaskDialogIndirect 
= INVALID_TASKDIALOG_FUNC
; 
 833     wxCRIT_SECT_LOCKER(lock
, gs_csTaskDialogIndirect
); 
 835     if ( s_TaskDialogIndirect 
== INVALID_TASKDIALOG_FUNC 
) 
 837         wxLoadedDLL 
dllComCtl32("comctl32.dll"); 
 838         wxDL_INIT_FUNC(s_
, TaskDialogIndirect
, dllComCtl32
); 
 841     return s_TaskDialogIndirect
; 
 844 #endif // wxHAS_MSW_TASKDIALOG 
 846 bool wxMSWMessageDialog::HasNativeTaskDialog() 
 848 #ifdef wxHAS_MSW_TASKDIALOG 
 849     if ( wxGetWinVersion() >= wxWinVersion_6 
) 
 851         if ( wxMSWMessageDialog::GetTaskDialogIndirectFunc() ) 
 854 #endif // wxHAS_MSW_TASKDIALOG 
 859 int wxMSWMessageDialog::MSWTranslateReturnCode(int msAns
) 
 865             wxFAIL_MSG(wxT("unexpected return code")); 
 888 #endif // wxUSE_MSGDLG