1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/dialog.cpp 
   3 // Purpose:     wxDialog class 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #include "wx/dialog.h" 
  33     #include "wx/button.h" 
  34     #include "wx/settings.h" 
  39 #include "wx/msw/private.h" 
  40 #include "wx/evtloop.h" 
  41 #include "wx/ptr_scpd.h" 
  43 #include "wx/msw/wrapcdlg.h" 
  45 #if defined(__SMARTPHONE__) && defined(__WXWINCE__) 
  46     #include "wx/msw/wince/resources.h" 
  47 #endif // __SMARTPHONE__ && __WXWINCE__ 
  49 #if wxUSE_TOOLBAR && defined(__POCKETPC__) 
  50 #include "wx/toolbar.h" 
  53 // ---------------------------------------------------------------------------- 
  55 // ---------------------------------------------------------------------------- 
  57 #if wxUSE_EXTENDED_RTTI 
  58 WX_DEFINE_FLAGS( wxDialogStyle 
) 
  60 wxBEGIN_FLAGS( wxDialogStyle 
) 
  61     // new style border flags, we put them first to 
  62     // use them for streaming out 
  63     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  64     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  65     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  66     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  67     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  68     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  70     // old style border flags 
  71     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  72     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  73     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  74     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  75     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  76     wxFLAGS_MEMBER(wxNO_BORDER
) 
  78     // standard window styles 
  79     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  80     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  83     wxFLAGS_MEMBER(wxWS_EX_VALIDATE_RECURSIVELY
) 
  84     wxFLAGS_MEMBER(wxSTAY_ON_TOP
) 
  85     wxFLAGS_MEMBER(wxCAPTION
) 
  86 #if WXWIN_COMPATIBILITY_2_6 
  87     wxFLAGS_MEMBER(wxTHICK_FRAME
) 
  88 #endif // WXWIN_COMPATIBILITY_2_6 
  89     wxFLAGS_MEMBER(wxSYSTEM_MENU
) 
  90     wxFLAGS_MEMBER(wxRESIZE_BORDER
) 
  91 #if WXWIN_COMPATIBILITY_2_6 
  92     wxFLAGS_MEMBER(wxRESIZE_BOX
) 
  93 #endif // WXWIN_COMPATIBILITY_2_6 
  94     wxFLAGS_MEMBER(wxCLOSE_BOX
) 
  95     wxFLAGS_MEMBER(wxMAXIMIZE_BOX
) 
  96     wxFLAGS_MEMBER(wxMINIMIZE_BOX
) 
  97 wxEND_FLAGS( wxDialogStyle 
) 
  99 IMPLEMENT_DYNAMIC_CLASS_XTI(wxDialog
, wxTopLevelWindow
,"wx/dialog.h") 
 101 wxBEGIN_PROPERTIES_TABLE(wxDialog
) 
 102     wxPROPERTY( Title
, wxString
, SetTitle
, GetTitle
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 103     wxPROPERTY_FLAGS( WindowStyle 
, wxDialogStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 104 wxEND_PROPERTIES_TABLE() 
 106 wxBEGIN_HANDLERS_TABLE(wxDialog
) 
 107 wxEND_HANDLERS_TABLE() 
 109 wxCONSTRUCTOR_6( wxDialog 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxString 
, Title 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle
) 
 112 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxTopLevelWindow
) 
 115 BEGIN_EVENT_TABLE(wxDialog
, wxDialogBase
) 
 116     EVT_BUTTON(wxID_OK
, wxDialog::OnOK
) 
 117     EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
) 
 118     EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
) 
 120     EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
) 
 122     EVT_CLOSE(wxDialog::OnCloseWindow
) 
 125 // ---------------------------------------------------------------------------- 
 127 // ---------------------------------------------------------------------------- 
 129 // this is simply a container for any data we need to implement modality which 
 130 // allows us to avoid changing wxDialog each time the implementation changes 
 131 class wxDialogModalData
 
 134     wxDialogModalData(wxDialog 
*dialog
) : m_evtLoop(dialog
) { } 
 147     wxModalEventLoop m_evtLoop
; 
 150 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData
) 
 152 // ============================================================================ 
 154 // ============================================================================ 
 156 // ---------------------------------------------------------------------------- 
 157 // wxDialog construction 
 158 // ---------------------------------------------------------------------------- 
 160 void wxDialog::Init() 
 162     m_oldFocus 
= (wxWindow 
*)NULL
; 
 165     m_endModalCalled 
= false; 
 166 #if wxUSE_TOOLBAR && defined(__POCKETPC__) 
 167     m_dialogToolBar 
= NULL
; 
 171 bool wxDialog::Create(wxWindow 
*parent
, 
 173                       const wxString
& title
, 
 177                       const wxString
& name
) 
 179     SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG
); 
 181     // save focus before doing anything which can potentially change it 
 182     m_oldFocus 
= FindFocus(); 
 184     // All dialogs should really have this style 
 185     style 
|= wxTAB_TRAVERSAL
; 
 187     if ( !wxTopLevelWindow::Create(parent
, id
, title
, pos
, size
, style
, name
) ) 
 191         SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 193 #if defined(__SMARTPHONE__) && defined(__WXWINCE__) 
 194     SetLeftMenu(wxID_OK
, _("OK")); 
 196 #if wxUSE_TOOLBAR && defined(__POCKETPC__) 
 203 #if WXWIN_COMPATIBILITY_2_6 
 206 wxDialog::wxDialog(wxWindow 
*parent
, 
 207                    const wxString
& title
, 
 208                    bool WXUNUSED(modal
), 
 214                    const wxString
& name
) 
 218     Create(parent
, wxID_ANY
, title
, wxPoint(x
, y
), wxSize(w
, h
), style
, name
); 
 221 void wxDialog::SetModal(bool WXUNUSED(flag
)) 
 223     // nothing to do, obsolete method 
 226 #endif // WXWIN_COMPATIBILITY_2_6 
 228 wxDialog::~wxDialog() 
 230     m_isBeingDeleted 
= true; 
 232     // this will also reenable all the other windows for a modal dialog 
 236 // ---------------------------------------------------------------------------- 
 237 // showing the dialogs 
 238 // ---------------------------------------------------------------------------- 
 240 #if WXWIN_COMPATIBILITY_2_6 
 242 bool wxDialog::IsModalShowing() const 
 247 #endif // WXWIN_COMPATIBILITY_2_6 
 249 wxWindow 
*wxDialog::FindSuitableParent() const 
 251     // first try to use the currently active window 
 252     HWND hwndFg 
= ::GetForegroundWindow(); 
 253     wxWindow 
*parent 
= hwndFg 
? wxFindWinFromHandle((WXHWND
)hwndFg
) 
 257         // next try the main app window 
 258         parent 
= wxTheApp
->GetTopWindow(); 
 261     // finally, check if the parent we found is really suitable 
 262     if ( !parent 
|| parent 
== (wxWindow 
*)this || !parent
->IsShown() ) 
 264         // don't use this one 
 271 bool wxDialog::Show(bool show
) 
 273     if ( show 
== IsShown() ) 
 276     if ( !show 
&& m_modalData 
) 
 278         // we need to do this before calling wxDialogBase version because if we 
 279         // had disabled other app windows, they must be reenabled right now as 
 280         // if they stay disabled Windows will activate another window (one 
 281         // which is enabled, anyhow) when we're hidden in the base class Show() 
 282         // and we will lose activation 
 283         m_modalData
->ExitLoop(); 
 288         // this usually will result in TransferDataToWindow() being called 
 289         // which will change the controls values so do it before showing as 
 290         // otherwise we could have some flicker 
 294     wxDialogBase::Show(show
); 
 298         // dialogs don't get WM_SIZE message after creation unlike most (all?) 
 299         // other windows and so could start their life non laid out correctly 
 300         // if we didn't call Layout() from here 
 302         // NB: normally we should call it just the first time but doing it 
 303         //     every time is simpler than keeping a flag 
 310 void wxDialog::Raise() 
 312     ::SetForegroundWindow(GetHwnd()); 
 315 // show dialog modally 
 316 int wxDialog::ShowModal() 
 318     wxASSERT_MSG( !IsModal(), _T("wxDialog::ShowModal() reentered?") ); 
 320     m_endModalCalled 
= false; 
 324     // EndModal may have been called from InitDialog handler (called from 
 325     // inside Show()), which would cause an infinite loop if we didn't take it 
 327     if ( !m_endModalCalled 
) 
 329         // modal dialog needs a parent window, so try to find one 
 330         wxWindow 
*parent 
= GetParent(); 
 333             parent 
= FindSuitableParent(); 
 336         // remember where the focus was 
 337         wxWindow 
*oldFocus 
= m_oldFocus
; 
 340             // VZ: do we really want to do this? 
 344         // We have to remember the HWND because we need to check 
 345         // the HWND still exists (oldFocus can be garbage when the dialog 
 346         // exits, if it has been destroyed) 
 347         HWND hwndOldFocus 
= oldFocus 
? GetHwndOf(oldFocus
) : NULL
; 
 350         // enter and run the modal loop 
 352             wxDialogModalDataTiedPtr 
modalData(&m_modalData
, 
 353                                                new wxDialogModalData(this)); 
 354             modalData
->RunLoop(); 
 359         // Note that this code MUST NOT access the dialog object's data 
 360         // in case the object has been deleted (which will be the case 
 361         // for a modal dialog that has been destroyed before calling EndModal). 
 362         if ( oldFocus 
&& (oldFocus 
!= this) && ::IsWindow(hwndOldFocus
)) 
 364             // This is likely to prove that the object still exists 
 365             if (wxFindWinFromHandle((WXHWND
) hwndOldFocus
) == oldFocus
) 
 366                 oldFocus
->SetFocus(); 
 370     return GetReturnCode(); 
 373 void wxDialog::EndModal(int retCode
) 
 375     wxASSERT_MSG( IsModal(), _T("EndModal() called for non modal dialog") ); 
 377     m_endModalCalled 
= true; 
 378     SetReturnCode(retCode
); 
 383 void wxDialog::EndDialog(int rc
) 
 391 // ---------------------------------------------------------------------------- 
 392 // wxWin event handlers 
 393 // ---------------------------------------------------------------------------- 
 395 bool wxDialog::EmulateButtonClickIfPresent(int id
) 
 397     wxButton 
*btn 
= wxDynamicCast(FindWindow(id
), wxButton
); 
 399     if ( !btn 
|| !btn
->IsEnabled() || !btn
->IsShown() ) 
 402     btn
->MSWCommand(BN_CLICKED
, 0 /* unused */); 
 407 void wxDialog::OnOK(wxCommandEvent
& WXUNUSED(event
)) 
 409   if ( Validate() && TransferDataFromWindow() ) 
 415 void wxDialog::OnApply(wxCommandEvent
& WXUNUSED(event
)) 
 418         TransferDataFromWindow(); 
 420     // TODO probably need to disable the Apply button until things change again 
 423 void wxDialog::OnCancel(wxCommandEvent
& WXUNUSED(event
)) 
 425     EndDialog(wxID_CANCEL
); 
 428 void wxDialog::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
)) 
 430     // We'll send a Cancel message by default, which may close the dialog. 
 431     // Check for looping if the Cancel event handler calls Close(). 
 433     // Note that if a cancel button and handler aren't present in the dialog, 
 434     // nothing will happen when you close the dialog via the window manager, or 
 435     // via Close(). We wouldn't want to destroy the dialog by default, since 
 436     // the dialog may have been created on the stack. However, this does mean 
 437     // that calling dialog->Close() won't delete the dialog unless the handler 
 438     // for wxID_CANCEL does so. So use Destroy() if you want to be sure to 
 439     // destroy the dialog. The default OnCancel (above) simply ends a modal 
 440     // dialog, and hides a modeless dialog. 
 442     // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global 
 443     //     lists here? don't dare to change it now, but should be done later! 
 444     static wxList closing
; 
 446     if ( closing
.Member(this) ) 
 449     closing
.Append(this); 
 451     wxCommandEvent 
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
); 
 452     cancelEvent
.SetEventObject( this ); 
 453     GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog 
 455     closing
.DeleteObject(this); 
 458 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& WXUNUSED(event
)) 
 460     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)); 
 465 // Responds to the OK button in a PocketPC titlebar. This 
 466 // can be overridden, or you can change the id used for 
 467 // sending the event, by calling SetAffirmativeId. 
 468 bool wxDialog::DoOK() 
 470     const int idOk 
= GetAffirmativeId(); 
 471     if ( EmulateButtonClickIfPresent(idOk
) ) 
 474     wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetAffirmativeId()); 
 475     event
.SetEventObject(this); 
 477     return GetEventHandler()->ProcessEvent(event
); 
 479 #endif // __POCKETPC__ 
 481 #if wxUSE_TOOLBAR && defined(__POCKETPC__) 
 482 // create main toolbar by calling OnCreateToolBar() 
 483 wxToolBar
* wxDialog::CreateToolBar(long style
, wxWindowID winid
, const wxString
& name
) 
 485     m_dialogToolBar 
= OnCreateToolBar(style
, winid
, name
); 
 487     return m_dialogToolBar
; 
 490 // return a new toolbar 
 491 wxToolBar 
*wxDialog::OnCreateToolBar(long style
, 
 493                                        const wxString
& name
) 
 495     return new wxToolMenuBar(this, winid
, 
 496                          wxDefaultPosition
, wxDefaultSize
, 
 501 // --------------------------------------------------------------------------- 
 502 // dialog Windows messages processing 
 503 // --------------------------------------------------------------------------- 
 505 bool wxDialog::MSWProcessMessage(WXMSG
* pMsg
) 
 507     const MSG 
* const msg 
= wx_reinterpret_cast(MSG 
*, pMsg
); 
 508     if ( msg
->message 
== WM_KEYDOWN 
&& msg
->wParam 
== VK_ESCAPE 
) 
 510         int idCancel 
= GetEscapeId(); 
 514                 // don't handle Esc specially at all 
 518                 // this value is special: it means translate Esc to wxID_CANCEL 
 519                 // but if there is no such button, then fall back to wxID_OK 
 520                 if ( EmulateButtonClickIfPresent(wxID_CANCEL
) ) 
 526                 // translate Esc to button press for the button with given id 
 527                 if ( EmulateButtonClickIfPresent(idCancel
) ) 
 532     return wxDialogBase::MSWProcessMessage(pMsg
); 
 535 WXLRESULT 
wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
 538     bool processed 
= false; 
 543         // react to pressing the OK button in the title 
 546             switch ( LOWORD(wParam
) ) 
 552                         processed 
= !Close(); 
 554 #ifdef __SMARTPHONE__ 
 557                     processed 
= HandleCommand( LOWORD(wParam
) , 0 , NULL 
); 
 559 #endif // __SMARTPHONE__ 
 565             // if we can't close, tell the system that we processed the 
 566             // message - otherwise it would close us 
 567             processed 
= !Close(); 
 571             // the Windows dialogs unfortunately are not meant to be resizeable 
 572             // at all and their standard class doesn't include CS_[VH]REDRAW 
 573             // styles which means that the window is not refreshed properly 
 574             // after the resize and no amount of WS_CLIPCHILDREN/SIBLINGS can 
 575             // help with it - so we have to refresh it manually which certainly 
 576             // creates flicker but at least doesn't show garbage on the screen 
 577             rc 
= wxWindow::MSWWindowProc(message
, wParam
, lParam
); 
 579             if ( HasFlag(wxFULL_REPAINT_ON_RESIZE
) ) 
 581                 ::InvalidateRect(GetHwnd(), NULL
, false /* erase bg */); 
 585 #ifndef __WXMICROWIN__ 
 587             // we want to override the busy cursor for modal dialogs: 
 588             // typically, wxBeginBusyCursor() is called and then a modal dialog 
 589             // is shown, but the modal dialog shouldn't have hourglass cursor 
 590             if ( IsModal() && wxIsBusy() ) 
 592                 // set our cursor for all windows (but see below) 
 593                 wxCursor cursor 
= m_cursor
; 
 595                     cursor 
= wxCURSOR_ARROW
; 
 597                 ::SetCursor(GetHcursorOf(cursor
)); 
 599                 // in any case, stop here and don't let wxWindow process this 
 600                 // message (it would set the busy cursor) 
 603                 // but return false to tell the child window (if the event 
 604                 // comes from one of them and not from ourselves) that it can 
 605                 // set its own cursor if it has one: thus, standard controls 
 606                 // (e.g. text ctrl) still have correct cursors in a dialog 
 607                 // invoked while wxIsBusy() 
 611 #endif // __WXMICROWIN__ 
 615         rc 
= wxWindow::MSWWindowProc(message
, wParam
, lParam
);