]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/msgdlg.cpp
9d91acab813b36b8c5d6710edc6910adb7e4a453
   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 #include "wx/msgdlg.h" 
  23 // there is no hook support under CE so we can't use the code for message box 
  26     #define wxUSE_MSGBOX_HOOK 1 
  28     #define wxUSE_MSGBOX_HOOK 0 
  34     #include "wx/dialog.h" 
  36         #include "wx/hashmap.h" 
  40 #include "wx/msw/private.h" 
  41 #include "wx/msw/private/button.h" 
  42 #include "wx/msw/private/metrics.h" 
  45     #include "wx/fontutil.h" 
  50     #include "wx/msw/wince/missing.h" 
  53 IMPLEMENT_CLASS(wxMessageDialog
, wxDialog
) 
  57 // there can potentially be one message box per thread so we use a hash map 
  58 // with thread ids as keys and (currently shown) message boxes as values 
  60 // TODO: replace this with wxTLS once it's available 
  61 WX_DECLARE_HASH_MAP(unsigned long, wxMessageDialog 
*, 
  62                     wxIntegerHash
, wxIntegerEqual
, 
  68 wxMessageDialogMap
& HookMap() 
  70     static wxMessageDialogMap s_Map
; 
  75 } // anonymous namespace 
  79 wxMessageDialog::HookFunction(int code
, WXWPARAM wParam
, WXLPARAM lParam
) 
  81     // Find the thread-local instance of wxMessageDialog 
  82     const DWORD tid 
= ::GetCurrentThreadId(); 
  83     wxMessageDialogMap::iterator node 
= HookMap().find(tid
); 
  84     wxCHECK_MSG( node 
!= HookMap().end(), false, 
  85                     wxT("bogus thread id in wxMessageDialog::Hook") ); 
  87     wxMessageDialog 
*  const wnd 
= node
->second
; 
  89     const HHOOK hhook 
= (HHOOK
)wnd
->m_hook
; 
  90     const LRESULT rc 
= ::CallNextHookEx(hhook
, code
, wParam
, lParam
); 
  92     if ( code 
== HCBT_ACTIVATE 
) 
  94         // we won't need this hook any longer 
  95         ::UnhookWindowsHookEx(hhook
); 
  99         wnd
->SetHWND((HWND
)wParam
); 
 101         // update the labels if necessary: we need to do it before centering 
 102         // the dialog as this can change its size 
 103         if ( wnd
->HasCustomLabels() ) 
 104             wnd
->AdjustButtonLabels(); 
 106         // centre the message box on its parent if requested 
 107         if ( wnd
->GetMessageDialogStyle() & wxCENTER 
) 
 108             wnd
->Center(); // center on parent 
 109         //else: default behaviour, center on screen 
 111         // there seems to be no reason to leave it set 
 121 // helper of AdjustButtonLabels(): set window position expressed in screen 
 122 // coordinates, whether the window is child or top level 
 123 void MoveWindowToScreenRect(HWND hwnd
, RECT rc
) 
 125     if ( const HWND hwndParent 
= ::GetAncestor(hwnd
, GA_PARENT
) ) 
 127         // map to parent window coordinates (notice that a RECT is laid out as 
 128         // 2 consecutive POINTs) 
 129         ::MapWindowPoints(HWND_DESKTOP
, hwndParent
, 
 130                           reinterpret_cast<POINT 
*>(&rc
), 2); 
 135                  rc
.right 
- rc
.left
, rc
.bottom 
- rc
.top
, 
 139 // helper of AdjustButtonLabels(): move the given window by dx 
 141 // works for both child and top level windows 
 142 void OffsetWindow(HWND hwnd
, int dx
) 
 144     RECT rc 
= wxGetWindowRect(hwnd
); 
 149     MoveWindowToScreenRect(hwnd
, rc
); 
 152 } // anonymous namespace 
 154 void wxMessageDialog::AdjustButtonLabels() 
 156     // changing the button labels is the easy part but we also need to ensure 
 157     // that the buttons are big enough for the label strings and increase their 
 158     // size (and hence the size of the message box itself) if they are not 
 160     // TODO-RTL: check whether this works correctly in RTL 
 162     // the order in this array is the one in which buttons appear in the 
 164     const static struct ButtonAccessors
 
 167         wxString (wxMessageDialog::*getter
)() const; 
 171         { IDYES
,    &wxMessageDialog::GetYesLabel    
}, 
 172         { IDNO
,     &wxMessageDialog::GetNoLabel     
}, 
 173         { IDOK
,     &wxMessageDialog::GetOKLabel     
}, 
 174         { IDCANCEL
, &wxMessageDialog::GetCancelLabel 
}, 
 177     // this contains the amount by which we increased the message box width 
 180     const NONCLIENTMETRICS
& ncm 
= wxMSWImpl::GetNonClientMetrics(); 
 181     const wxFont 
fontMsgBox(wxNativeFontInfo(ncm
.lfMessageFont
)); 
 183     // we want to use this font in GetTextExtent() calls below but we don't 
 184     // want to send WM_SETFONT to the message box, who knows how is it going to 
 185     // react to it (right now it doesn't seem to do anything but what if this 
 187     wxWindowBase::SetFont(fontMsgBox
); 
 189     for ( unsigned n 
= 0; n 
< WXSIZEOF(buttons
); n
++ ) 
 191         const HWND hwndBtn 
= ::GetDlgItem(GetHwnd(), buttons
[n
].id
); 
 193             continue;   // it's ok, not all buttons are always present 
 195         const wxString label 
= (this->*buttons
[n
].getter
)(); 
 196         const wxSize sizeLabel 
= wxWindowBase::GetTextExtent(label
); 
 198         // check if the button is big enough for this label 
 199         RECT rc 
= wxGetWindowRect(hwndBtn
); 
 200         const int widthOld 
= rc
.right 
- rc
.left
; 
 201         const int widthNew 
= wxMSWButton::GetFittingSize(this, sizeLabel
).x
; 
 202         const int dw 
= widthNew 
- widthOld
; 
 205             // we need to resize the button 
 207             MoveWindowToScreenRect(hwndBtn
, rc
); 
 209             // and also move all the other buttons 
 210             for ( unsigned m 
= n 
+ 1; m 
< WXSIZEOF(buttons
); m
++ ) 
 212                 const HWND hwndBtnNext 
= ::GetDlgItem(GetHwnd(), buttons
[m
].id
); 
 214                     OffsetWindow(hwndBtnNext
, dw
); 
 220         ::SetWindowText(hwndBtn
, label
.wx_str()); 
 224     // resize the message box itself if needed 
 226         OffsetWindow(GetHwnd(), dx
); 
 228     // surprisingly, we don't need to resize the static text control, it seems 
 229     // to adjust itself to the new size, at least under Windows 2003 
 230     // (TODO: test if this happens on older Windows versions) 
 233 #endif // wxUSE_MSGBOX_HOOK 
 236 int wxMessageDialog::ShowModal() 
 238     if ( !wxTheApp
->GetTopWindow() ) 
 240         // when the message box is shown from wxApp::OnInit() (i.e. before the 
 241         // message loop is entered), this must be done or the next message box 
 242         // will never be shown - just try putting 2 calls to wxMessageBox() in 
 243         // OnInit() to see it 
 244         while ( wxTheApp
->Pending() ) 
 245             wxTheApp
->Dispatch(); 
 248     // use the top level window as parent if none specified 
 250         m_parent 
= FindSuitableParent(); 
 251     HWND hWnd 
= m_parent 
? GetHwndOf(m_parent
) : NULL
; 
 253     // translate wx style in MSW 
 254     unsigned int msStyle 
= MB_OK
; 
 255     const long wxStyle 
= GetMessageDialogStyle(); 
 256     if (wxStyle 
& wxYES_NO
) 
 258 #if !(defined(__SMARTPHONE__) && defined(__WXWINCE__)) 
 259         if (wxStyle 
& wxCANCEL
) 
 260             msStyle 
= MB_YESNOCANCEL
; 
 262 #endif // !(__SMARTPHONE__ && __WXWINCE__) 
 265         if (wxStyle 
& wxNO_DEFAULT
) 
 266             msStyle 
|= MB_DEFBUTTON2
; 
 271         if (wxStyle 
& wxCANCEL
) 
 272             msStyle 
= MB_OKCANCEL
; 
 276     if (wxStyle 
& wxICON_EXCLAMATION
) 
 277         msStyle 
|= MB_ICONEXCLAMATION
; 
 278     else if (wxStyle 
& wxICON_HAND
) 
 279         msStyle 
|= MB_ICONHAND
; 
 280     else if (wxStyle 
& wxICON_INFORMATION
) 
 281         msStyle 
|= MB_ICONINFORMATION
; 
 282     else if (wxStyle 
& wxICON_QUESTION
) 
 283         msStyle 
|= MB_ICONQUESTION
; 
 285     if ( wxStyle 
& wxSTAY_ON_TOP 
) 
 286         msStyle 
|= MB_TOPMOST
; 
 289     if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft 
) 
 290         msStyle 
|= MB_RTLREADING 
| MB_RIGHT
; 
 294         msStyle 
|= MB_APPLMODAL
; 
 296         msStyle 
|= MB_TASKMODAL
; 
 298     // per MSDN documentation for MessageBox() we can prefix the message with 2 
 299     // right-to-left mark characters to tell the function to use RTL layout 
 300     // (unfortunately this only works in Unicode builds) 
 301     wxString message 
= GetFullMessage(); 
 303     if ( wxTheApp
->GetLayoutDirection() == wxLayout_RightToLeft 
) 
 305         // NB: not all compilers support \u escapes 
 306         static const wchar_t wchRLM 
= 0x200f; 
 307         message
.Prepend(wxString(wchRLM
, 2)); 
 309 #endif // wxUSE_UNICODE 
 311 #if wxUSE_MSGBOX_HOOK 
 312     // install the hook if we need to position the dialog in a non-default way 
 313     // or change the labels 
 314     if ( (wxStyle 
& wxCENTER
) || HasCustomLabels() ) 
 316         const DWORD tid 
= ::GetCurrentThreadId(); 
 317         m_hook 
= ::SetWindowsHookEx(WH_CBT
, 
 318                                     &wxMessageDialog::HookFunction
, NULL
, tid
); 
 319         HookMap()[tid
] = this; 
 321 #endif // wxUSE_MSGBOX_HOOK 
 323     // do show the dialog 
 324     int msAns 
= MessageBox(hWnd
, message
.wx_str(), m_caption
.wx_str(), msStyle
); 
 329             wxFAIL_MSG(_T("unexpected ::MessageBox() return code")); 
 348 #endif // wxUSE_MSGDLG