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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "dialog.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  32     #include "wx/dialog.h" 
  36     #include "wx/settings.h" 
  41 #include "wx/msw/private.h" 
  43 #include "wx/evtloop.h" 
  45 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__) 
  49 // ---------------------------------------------------------------------------- 
  51 // ---------------------------------------------------------------------------- 
  53 // default dialog pos and size 
  55 #define wxDIALOG_DEFAULT_X 300 
  56 #define wxDIALOG_DEFAULT_Y 300 
  58 #define wxDIALOG_DEFAULT_WIDTH 500 
  59 #define wxDIALOG_DEFAULT_HEIGHT 500 
  61 // ---------------------------------------------------------------------------- 
  63 // ---------------------------------------------------------------------------- 
  65 // all modal dialogs currently shown 
  66 static wxWindowList wxModalDialogs
; 
  68 // ---------------------------------------------------------------------------- 
  70 // ---------------------------------------------------------------------------- 
  72 #if wxUSE_EXTENDED_RTTI 
  73 WX_DEFINE_FLAGS( wxDialogStyle 
) 
  75 wxBEGIN_FLAGS( wxDialogStyle 
) 
  76     // new style border flags, we put them first to 
  77     // use them for streaming out 
  78     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  79     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  80     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  81     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  82     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  83     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  85     // old style border flags 
  86     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  87     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  88     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  89     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  90     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  91     wxFLAGS_MEMBER(wxNO_BORDER
) 
  93     // standard window styles 
  94     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  95     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  98     wxFLAGS_MEMBER(wxDIALOG_MODAL
) 
  99     wxFLAGS_MEMBER(wxDIALOG_MODELESS
) 
 100     wxFLAGS_MEMBER(wxNO_3D
) 
 101     wxFLAGS_MEMBER(wxWS_EX_VALIDATE_RECURSIVELY
) 
 102     wxFLAGS_MEMBER(wxSTAY_ON_TOP
) 
 103     wxFLAGS_MEMBER(wxCAPTION
) 
 104     wxFLAGS_MEMBER(wxTHICK_FRAME
) 
 105     wxFLAGS_MEMBER(wxSYSTEM_MENU
) 
 106     wxFLAGS_MEMBER(wxRESIZE_BORDER
) 
 107     wxFLAGS_MEMBER(wxRESIZE_BOX
) 
 108     wxFLAGS_MEMBER(wxCLOSE_BOX
) 
 109     wxFLAGS_MEMBER(wxMAXIMIZE_BOX
) 
 110     wxFLAGS_MEMBER(wxMINIMIZE_BOX
) 
 111 wxEND_FLAGS( wxDialogStyle 
) 
 113 IMPLEMENT_DYNAMIC_CLASS_XTI(wxDialog
, wxTopLevelWindow
,"wx/dialog.h") 
 115 wxBEGIN_PROPERTIES_TABLE(wxDialog
) 
 116     wxPROPERTY( Title
,wxString
, SetTitle
, GetTitle
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 117     wxPROPERTY_FLAGS( WindowStyle 
, wxDialogStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 118 wxEND_PROPERTIES_TABLE() 
 120 wxBEGIN_HANDLERS_TABLE(wxDialog
) 
 121 wxEND_HANDLERS_TABLE() 
 123 wxCONSTRUCTOR_6( wxDialog 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxString 
, Title 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle
)  
 126 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxTopLevelWindow
) 
 129 BEGIN_EVENT_TABLE(wxDialog
, wxDialogBase
) 
 130     EVT_BUTTON(wxID_OK
, wxDialog::OnOK
) 
 131     EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
) 
 132     EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
) 
 134     EVT_CHAR_HOOK(wxDialog::OnCharHook
) 
 136     EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
) 
 138     EVT_CLOSE(wxDialog::OnCloseWindow
) 
 141 // ============================================================================ 
 143 // ============================================================================ 
 145 // ---------------------------------------------------------------------------- 
 146 // wxDialog construction 
 147 // ---------------------------------------------------------------------------- 
 149 void wxDialog::Init() 
 151     m_oldFocus 
= (wxWindow 
*)NULL
; 
 155     m_windowDisabler 
= (wxWindowDisabler 
*)NULL
; 
 157     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)); 
 160 bool wxDialog::Create(wxWindow 
*parent
, 
 162                       const wxString
& title
, 
 166                       const wxString
& name
) 
 170     SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG
); 
 172     // save focus before doing anything which can potentially change it 
 173     m_oldFocus 
= FindFocus(); 
 175     // All dialogs should really have this style 
 176     style 
|= wxTAB_TRAVERSAL
; 
 178     if ( !wxTopLevelWindow::Create(parent
, id
, title
, pos
, size
, style
, name
) ) 
 181     SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 186 void wxDialog::SetModal(bool flag
) 
 190         m_windowStyle 
|= wxDIALOG_MODAL
; 
 192         wxModelessWindows
.DeleteObject(this); 
 196         m_windowStyle 
&= ~wxDIALOG_MODAL
; 
 198         wxModelessWindows
.Append(this); 
 202 wxDialog::~wxDialog() 
 204     m_isBeingDeleted 
= TRUE
; 
 206     // this will also reenable all the other windows for a modal dialog 
 210 // ---------------------------------------------------------------------------- 
 212 // ---------------------------------------------------------------------------- 
 214 // By default, pressing escape cancels the dialog 
 215 void wxDialog::OnCharHook(wxKeyEvent
& event
) 
 219         // "Esc" works as an accelerator for the "Cancel" button, but it 
 220         // shouldn't close the dialog which doesn't have any cancel button 
 221         if ( (event
.m_keyCode 
== WXK_ESCAPE
) && FindWindow(wxID_CANCEL
) ) 
 223             wxCommandEvent 
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
); 
 224             cancelEvent
.SetEventObject( this ); 
 225             GetEventHandler()->ProcessEvent(cancelEvent
); 
 227             // ensure that there is another message for this window so the 
 228             // ShowModal loop will exit and won't get stuck in GetMessage(). 
 229             ::PostMessage(GetHwnd(), WM_NULL
, 0, 0); 
 235     // We didn't process this event. 
 239 // ---------------------------------------------------------------------------- 
 240 // showing the dialogs 
 241 // ---------------------------------------------------------------------------- 
 243 bool wxDialog::IsModal() const 
 245     return (GetWindowStyleFlag() & wxDIALOG_MODAL
) != 0; 
 248 bool wxDialog::IsModalShowing() const 
 250     return wxModalDialogs
.Find(wxConstCast(this, wxDialog
)) != NULL
; 
 253 wxWindow 
*wxDialog::FindSuitableParent() const 
 255     // first try to use the currently active window 
 256     HWND hwndFg 
= ::GetForegroundWindow(); 
 257     wxWindow 
*parent 
= hwndFg 
? wxFindWinFromHandle((WXHWND
)hwndFg
) 
 261         // next try the main app window 
 262         parent 
= wxTheApp
->GetTopWindow(); 
 265     // finally, check if the parent we found is really suitable 
 266     if ( !parent 
|| parent 
== (wxWindow 
*)this || !parent
->IsShown() ) 
 268         // don't use this one 
 275 void wxDialog::DoShowModal() 
 277     wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") ); 
 278     wxCHECK_RET( IsModal(), _T("can't DoShowModal() modeless dialog") ); 
 280     wxModalDialogs
.Append(this); 
 282     wxWindow 
*parent 
= GetParent(); 
 284     wxWindow
* oldFocus 
= m_oldFocus
; 
 286     // We have to remember the HWND because we need to check 
 287     // the HWND still exists (oldFocus can be garbage when the dialog 
 288     // exits, if it has been destroyed) 
 289     HWND hwndOldFocus 
= 0; 
 291         hwndOldFocus 
= (HWND
) oldFocus
->GetHWND(); 
 293     // remember where the focus was 
 298             hwndOldFocus 
= GetHwndOf(parent
); 
 301     // disable all other app windows 
 302     wxASSERT_MSG( !m_windowDisabler
, _T("disabling windows twice?") ); 
 304     m_windowDisabler 
= new wxWindowDisabler(this); 
 306     // before entering the modal loop, reset the "is in OnIdle()" flag (see 
 307     // comment in app.cpp) 
 308     extern bool wxIsInOnIdleFlag
; 
 309     bool wasInOnIdle 
= wxIsInOnIdleFlag
; 
 310     wxIsInOnIdleFlag 
= FALSE
; 
 312     // enter the modal loop 
 316     wxIsInOnIdleFlag 
= wasInOnIdle
; 
 319     // Note that this code MUST NOT access the dialog object's data 
 320     // in case the object has been deleted (which will be the case 
 321     // for a modal dialog that has been destroyed before calling EndModal). 
 322     if ( oldFocus 
&& (oldFocus 
!= this) && ::IsWindow(hwndOldFocus
)) 
 324         // This is likely to prove that the object still exists 
 325         if (wxFindWinFromHandle((WXHWND
) hwndOldFocus
) == oldFocus
) 
 326             oldFocus
->SetFocus(); 
 330 bool wxDialog::Show(bool show
) 
 334         // if we had disabled other app windows, reenable them back now because 
 335         // if they stay disabled Windows will activate another window (one 
 336         // which is enabled, anyhow) and we will lose activation 
 337         if ( m_windowDisabler 
) 
 339             delete m_windowDisabler
; 
 340             m_windowDisabler 
= NULL
; 
 344     // ShowModal() may be called for already shown dialog 
 345     if ( !wxDialogBase::Show(show
) && !(show 
&& IsModal()) ) 
 353         // dialogs don't get WM_SIZE message after creation unlike most (all?) 
 354         // other windows and so could start their life non laid out correctly 
 355         // if we didn't call Layout() from here 
 357         // NB: normally we should call it just the first time but doing it 
 358         //     every time is simpler than keeping a flag 
 361         // usually will result in TransferDataToWindow() being called 
 369             // modal dialog needs a parent window, so try to find one 
 372                 m_parent 
= FindSuitableParent(); 
 377         else // end of modal dialog 
 379             // this will cause IsModalShowing() return FALSE and our local 
 380             // message loop will terminate 
 381             wxModalDialogs
.DeleteObject(this); 
 383             // ensure that there is another message for this window so the 
 384             // ShowModal loop will exit and won't get stuck in GetMessage(). 
 385             ::PostMessage(GetHwnd(), WM_NULL
, 0, 0); 
 392 void wxDialog::Raise() 
 394     ::SetForegroundWindow(GetHwnd()); 
 397 // a special version for Show(TRUE) for modal dialogs which returns return code 
 398 int wxDialog::ShowModal() 
 407     return GetReturnCode(); 
 410 // NB: this function (surprizingly) may be called for both modal and modeless 
 411 //     dialogs and should work for both of them 
 412 void wxDialog::EndModal(int retCode
) 
 414     SetReturnCode(retCode
); 
 419 // ---------------------------------------------------------------------------- 
 420 // wxWin event handlers 
 421 // ---------------------------------------------------------------------------- 
 424 void wxDialog::OnOK(wxCommandEvent
& WXUNUSED(event
)) 
 426   if ( Validate() && TransferDataFromWindow() ) 
 432 void wxDialog::OnApply(wxCommandEvent
& WXUNUSED(event
)) 
 435         TransferDataFromWindow(); 
 437     // TODO probably need to disable the Apply button until things change again 
 440 void wxDialog::OnCancel(wxCommandEvent
& WXUNUSED(event
)) 
 442     EndModal(wxID_CANCEL
); 
 445 void wxDialog::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
)) 
 447     // We'll send a Cancel message by default, which may close the dialog. 
 448     // Check for looping if the Cancel event handler calls Close(). 
 450     // Note that if a cancel button and handler aren't present in the dialog, 
 451     // nothing will happen when you close the dialog via the window manager, or 
 452     // via Close(). We wouldn't want to destroy the dialog by default, since 
 453     // the dialog may have been created on the stack. However, this does mean 
 454     // that calling dialog->Close() won't delete the dialog unless the handler 
 455     // for wxID_CANCEL does so. So use Destroy() if you want to be sure to 
 456     // destroy the dialog. The default OnCancel (above) simply ends a modal 
 457     // dialog, and hides a modeless dialog. 
 459     // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global 
 460     //     lists here? don't dare to change it now, but should be done later! 
 461     static wxList closing
; 
 463     if ( closing
.Member(this) ) 
 466     closing
.Append(this); 
 468     wxCommandEvent 
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
); 
 469     cancelEvent
.SetEventObject( this ); 
 470     GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog 
 472     closing
.DeleteObject(this); 
 475 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& WXUNUSED(event
)) 
 480     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
)); 
 485 // --------------------------------------------------------------------------- 
 486 // dialog window proc 
 487 // --------------------------------------------------------------------------- 
 489 long wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
 492     bool processed 
= FALSE
; 
 497             // if we can't close, tell the system that we processed the 
 498             // message - otherwise it would close us 
 499             processed 
= !Close(); 
 503             // the Windows dialogs unfortunately are not meant to be resizeable 
 504             // at all and their standard class doesn't include CS_[VH]REDRAW 
 505             // styles which means that the window is not refreshed properly 
 506             // after the resize and no amount of WS_CLIPCHILDREN/SIBLINGS can 
 507             // help with it - so we have to refresh it manually which certainly 
 508             // creates flicker but at least doesn't show garbage on the screen 
 509             rc 
= wxWindow::MSWWindowProc(message
, wParam
, lParam
); 
 511             if ( !HasFlag(wxNO_FULL_REPAINT_ON_RESIZE
) ) 
 513                 ::InvalidateRect(GetHwnd(), NULL
, FALSE 
/* erase bg */); 
 517 #ifndef __WXMICROWIN__ 
 519             // we want to override the busy cursor for modal dialogs: 
 520             // typically, wxBeginBusyCursor() is called and then a modal dialog 
 521             // is shown, but the modal dialog shouldn't have hourglass cursor 
 522             if ( IsModalShowing() && wxIsBusy() ) 
 524                 // set our cursor for all windows (but see below) 
 525                 wxCursor cursor 
= m_cursor
; 
 527                     cursor 
= wxCURSOR_ARROW
; 
 529                 ::SetCursor(GetHcursorOf(cursor
)); 
 531                 // in any case, stop here and don't let wxWindow process this 
 532                 // message (it would set the busy cursor) 
 535                 // but return FALSE to tell the child window (if the event 
 536                 // comes from one of them and not from ourselves) that it can 
 537                 // set its own cursor if it has one: thus, standard controls 
 538                 // (e.g. text ctrl) still have correct cursors in a dialog 
 539                 // invoked while wxIsBusy() 
 543 #endif // __WXMICROWIN__ 
 547         rc 
= wxWindow::MSWWindowProc(message
, wParam
, lParam
); 
 554 // Define for each class of dialog and control 
 555 WXHBRUSH 
wxDialog::OnCtlColor(WXHDC 
WXUNUSED(pDC
), 
 556                               WXHWND 
WXUNUSED(pWnd
), 
 557                               WXUINT 
WXUNUSED(nCtlColor
), 
 562     return (WXHBRUSH
)Ctl3dCtlColorEx(message
, wParam
, lParam
); 
 565 #endif // wxUSE_CTL3D