1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/dialog.cpp 
   3 // Purpose:     wxDialog class 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart and Markus Holzem 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  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" 
  44 #if wxUSE_COMMON_DIALOGS 
  48 // ---------------------------------------------------------------------------- 
  50 // ---------------------------------------------------------------------------- 
  52 // default dialog pos and size 
  54 #define wxDIALOG_DEFAULT_X 300 
  55 #define wxDIALOG_DEFAULT_Y 300 
  57 #define wxDIALOG_DEFAULT_WIDTH 500 
  58 #define wxDIALOG_DEFAULT_HEIGHT 500 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 // all objects to be deleted during next idle processing - from window.cpp 
  65 extern wxList WXDLLEXPORT wxPendingDelete
; 
  67 // all frames and modeless dialogs - not static, used in frame.cpp, mdi.cpp &c 
  68 wxWindowList wxModelessWindows
; 
  70 // all modal dialogs currently shown 
  71 static wxWindowList wxModalDialogs
; 
  73 // ---------------------------------------------------------------------------- 
  75 // ---------------------------------------------------------------------------- 
  77 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxPanel
) 
  79 BEGIN_EVENT_TABLE(wxDialog
, wxPanel
) 
  80     EVT_BUTTON(wxID_OK
, wxDialog::OnOK
) 
  81     EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
) 
  82     EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
) 
  84     EVT_CHAR_HOOK(wxDialog::OnCharHook
) 
  86     EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
) 
  88     EVT_CLOSE(wxDialog::OnCloseWindow
) 
  91 // ============================================================================ 
  93 // ============================================================================ 
  95 // ---------------------------------------------------------------------------- 
  96 // wxDialog construction 
  97 // ---------------------------------------------------------------------------- 
 101     m_oldFocus 
= (wxWindow 
*)NULL
; 
 104     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
)); 
 107 bool wxDialog::Create(wxWindow 
*parent
, wxWindowID id
, 
 108                       const wxString
& title
, 
 112                       const wxString
& name
) 
 114     m_oldFocus 
= FindFocus(); 
 116     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
)); 
 119     wxTopLevelWindows
.Append(this); 
 121     //  windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL); 
 123     if (parent
) parent
->AddChild(this); 
 126         m_windowId 
= (int)NewControlId(); 
 136         x 
= wxDIALOG_DEFAULT_X
; 
 138         y 
= wxDIALOG_DEFAULT_Y
; 
 140     m_windowStyle 
= style
; 
 145         width 
= wxDIALOG_DEFAULT_WIDTH
; 
 147         height 
= wxDIALOG_DEFAULT_HEIGHT
; 
 149     // All dialogs should really have this style 
 150     m_windowStyle 
|= wxTAB_TRAVERSAL
; 
 152     WXDWORD extendedStyle 
= MakeExtendedStyle(m_windowStyle
); 
 153     if (m_windowStyle 
& wxSTAY_ON_TOP
) 
 154         extendedStyle 
|= WS_EX_TOPMOST
; 
 156     // Allows creation of dialogs with & without captions under MSWindows, 
 157     // resizeable or not (but a resizeable dialog always has caption - 
 158     // otherwise it would look too strange) 
 160     if ( style 
& wxRESIZE_BORDER 
) 
 161         dlg 
= wxT("wxResizeableDialog"); 
 162     else if ( style 
& wxCAPTION 
) 
 163         dlg 
= wxT("wxCaptionDialog"); 
 165         dlg 
= wxT("wxNoCaptionDialog"); 
 166     MSWCreate(m_windowId
, parent
, NULL
, this, NULL
, 
 168               0, // style is not used if we have dlg template 
 172     HWND hwnd 
= (HWND
)GetHWND(); 
 176         wxFAIL_MSG(_("Failed to create dialog. You probably forgot to include wx/msw/wx.rc in your resources.")); 
 181     SubclassWin(GetHWND()); 
 183     SetWindowText(hwnd
, title
); 
 184     SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
)); 
 189 void wxDialog::SetModal(bool flag
) 
 193         m_windowStyle 
|= wxDIALOG_MODAL
; 
 195         wxModelessWindows
.DeleteObject(this); 
 199         m_windowStyle 
&= ~wxDIALOG_MODAL
; 
 201         wxModelessWindows
.Append(this); 
 205 wxDialog::~wxDialog() 
 207     m_isBeingDeleted 
= TRUE
; 
 209     wxTopLevelWindows
.DeleteObject(this); 
 211     // this will call BringWindowToTop() if necessary to bring back our parent 
 216         wxModelessWindows
.DeleteObject(this); 
 218     // If this is the last top-level window, exit. 
 219     if ( wxTheApp 
&& (wxTopLevelWindows
.Number() == 0) ) 
 221         wxTheApp
->SetTopWindow(NULL
); 
 223         if ( wxTheApp
->GetExitOnFrameDelete() ) 
 225             ::PostQuitMessage(0); 
 230 // ---------------------------------------------------------------------------- 
 232 // ---------------------------------------------------------------------------- 
 234 // By default, pressing escape cancels the dialog 
 235 void wxDialog::OnCharHook(wxKeyEvent
& event
) 
 239         // "Esc" works as an accelerator for the "Cancel" button, but it 
 240         // shouldn't close the dialog which doesn't have any cancel button 
 241         if ( (event
.m_keyCode 
== WXK_ESCAPE
) && FindWindow(wxID_CANCEL
) ) 
 243             wxCommandEvent 
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
); 
 244             cancelEvent
.SetEventObject( this ); 
 245             GetEventHandler()->ProcessEvent(cancelEvent
); 
 247             // ensure that there is another message for this window so the 
 248             // ShowModal loop will exit and won't get stuck in GetMessage(). 
 249             ::PostMessage(GetHwnd(), WM_NULL
, 0, 0); 
 255     // We didn't process this event. 
 259 // ---------------------------------------------------------------------------- 
 260 // Windows dialog boxes can't be iconized 
 261 // ---------------------------------------------------------------------------- 
 263 void wxDialog::Iconize(bool WXUNUSED(iconize
)) 
 267 bool wxDialog::IsIconized() const 
 272 // ---------------------------------------------------------------------------- 
 273 // size/position handling 
 274 // ---------------------------------------------------------------------------- 
 276 void wxDialog::DoSetClientSize(int width
, int height
) 
 278     HWND hWnd 
= (HWND
) GetHWND(); 
 280     ::GetClientRect(hWnd
, &rect
); 
 283     GetWindowRect(hWnd
, &rect2
); 
 285     // Find the difference between the entire window (title bar and all) 
 286     // and the client area; add this to the new client size to move the 
 288     int actual_width 
= rect2
.right 
- rect2
.left 
- rect
.right 
+ width
; 
 289     int actual_height 
= rect2
.bottom 
- rect2
.top 
- rect
.bottom 
+ height
; 
 291     MoveWindow(hWnd
, rect2
.left
, rect2
.top
, actual_width
, actual_height
, TRUE
); 
 293     wxSizeEvent 
event(wxSize(actual_width
, actual_height
), m_windowId
); 
 294     event
.SetEventObject( this ); 
 295     GetEventHandler()->ProcessEvent(event
); 
 298 void wxDialog::DoGetPosition(int *x
, int *y
) const 
 301     GetWindowRect(GetHwnd(), &rect
); 
 309 // ---------------------------------------------------------------------------- 
 310 // showing the dialogs 
 311 // ---------------------------------------------------------------------------- 
 313 bool wxDialog::IsModal() const 
 315     return (GetWindowStyleFlag() & wxDIALOG_MODAL
) != 0; 
 318 bool wxDialog::IsModalShowing() const 
 320     return wxModalDialogs
.Find((wxDialog 
*)this) != NULL
; // const_cast 
 323 void wxDialog::DoShowModal() 
 325     wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") ); 
 326     wxCHECK_RET( IsModal(), _T("can't DoShowModal() modeless dialog") ); 
 328     wxModalDialogs
.Append(this); 
 330     wxWindow 
*parent 
= GetParent(); 
 332     wxWindow
* oldFocus 
= m_oldFocus
; 
 334     // We have to remember the HWND because we need to check 
 335     // the HWND still exists (oldFocus can be garbage when the dialog 
 336     // exits, if it has been destroyed) 
 337     HWND hwndOldFocus 
= 0; 
 339         hwndOldFocus 
= (HWND
) oldFocus
->GetHWND(); 
 341     // inside this block, all app windows are disabled 
 343         wxWindowDisabler 
wd(this); 
 345         // remember where the focus was 
 350                 hwndOldFocus 
= (HWND
) parent
->GetHWND(); 
 353         // enter the modal loop 
 354         while ( IsModalShowing() ) 
 357             wxMutexGuiLeaveOrEnter(); 
 358 #endif // wxUSE_THREADS 
 360             while ( !wxTheApp
->Pending() && wxTheApp
->ProcessIdle() ) 
 363             // a message came or no more idle processing to do 
 364             wxTheApp
->DoMessage(); 
 370         ::SetActiveWindow(GetHwndOf(parent
)); 
 374     // Note that this code MUST NOT access the dialog object's data 
 375     // in case the object has been deleted (which will be the case 
 376     // for a modal dialog that has been destroyed before calling EndModal). 
 377     if ( oldFocus 
&& (oldFocus 
!= this) && ::IsWindow(hwndOldFocus
)) 
 379         // This is likely to prove that the object still exists 
 380         if (wxFindWinFromHandle((WXHWND
) hwndOldFocus
) == oldFocus
) 
 381             oldFocus
->SetFocus(); 
 385 bool wxDialog::Show(bool show
) 
 387     // The following is required when the parent has been disabled, (modal 
 388     // dialogs, or modeless dialogs with disabling such as wxProgressDialog). 
 389     // Otherwise the parent disappears behind other windows when the dialog is 
 393         wxWindow 
*parent 
= GetParent(); 
 396             ::BringWindowToTop(GetHwndOf(parent
)); 
 400     // ShowModal() may be called for already shown dialog 
 401     if ( !wxDialogBase::Show(show
) && !(show 
&& IsModal()) ) 
 409         // usually will result in TransferDataToWindow() being called 
 417             // modal dialog needs a parent window, so try to find one 
 420                 wxWindow 
*parent 
= wxTheApp
->GetTopWindow(); 
 421                 if ( parent 
&& parent 
!= this && parent
->IsShown() ) 
 430         else // end of modal dialog 
 432             // this will cause IsModalShowing() return FALSE and our local 
 433             // message loop will terminate 
 434             wxModalDialogs
.DeleteObject(this); 
 441 // a special version for Show(TRUE) for modal dialogs which returns return code 
 442 int wxDialog::ShowModal() 
 451     return GetReturnCode(); 
 454 // NB: this function (surprizingly) may be called for both modal and modeless 
 455 //     dialogs and should work for both of them 
 456 void wxDialog::EndModal(int retCode
) 
 458     SetReturnCode(retCode
); 
 463 // ---------------------------------------------------------------------------- 
 464 // wxWin event handlers 
 465 // ---------------------------------------------------------------------------- 
 468 void wxDialog::OnOK(wxCommandEvent
& event
) 
 470   if ( Validate() && TransferDataFromWindow() ) 
 476 void wxDialog::OnApply(wxCommandEvent
& event
) 
 479         TransferDataFromWindow(); 
 481     // TODO probably need to disable the Apply button until things change again 
 484 void wxDialog::OnCancel(wxCommandEvent
& event
) 
 486     EndModal(wxID_CANCEL
); 
 489 void wxDialog::OnCloseWindow(wxCloseEvent
& event
) 
 491     // We'll send a Cancel message by default, which may close the dialog. 
 492     // Check for looping if the Cancel event handler calls Close(). 
 494     // Note that if a cancel button and handler aren't present in the dialog, 
 495     // nothing will happen when you close the dialog via the window manager, or 
 496     // via Close(). We wouldn't want to destroy the dialog by default, since 
 497     // the dialog may have been created on the stack. However, this does mean 
 498     // that calling dialog->Close() won't delete the dialog unless the handler 
 499     // for wxID_CANCEL does so. So use Destroy() if you want to be sure to 
 500     // destroy the dialog. The default OnCancel (above) simply ends a modal 
 501     // dialog, and hides a modeless dialog. 
 503     // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global 
 504     //     lists here? don't dare to change it now, but should be done later! 
 505     static wxList closing
; 
 507     if ( closing
.Member(this) ) 
 510     closing
.Append(this); 
 512     wxCommandEvent 
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
); 
 513     cancelEvent
.SetEventObject( this ); 
 514     GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog 
 516     closing
.DeleteObject(this); 
 519 // Destroy the window (delayed, if a managed window) 
 520 bool wxDialog::Destroy() 
 522     wxCHECK_MSG( !wxPendingDelete
.Member(this), FALSE
, 
 523                  _T("wxDialog destroyed twice") ); 
 525     wxPendingDelete
.Append(this); 
 530 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& event
) 
 535     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
)); 
 540 // --------------------------------------------------------------------------- 
 541 // dialog window proc 
 542 // --------------------------------------------------------------------------- 
 544 long wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
 547     bool processed 
= FALSE
; 
 552             switch ( LOWORD(wParam
) ) 
 556                     if ( IsModalShowing() && GetParent() ) 
 558                         // bring the owner window to top as the standard dialog 
 562                                     GetHwndOf(GetParent()), 
 571                             wxLogLastError(wxT("SetWindowPos(SWP_NOACTIVATE)")); 
 574                     // fall through to process it normally as well 
 579             // if we can't close, tell the system that we processed the 
 580             // message - otherwise it would close us 
 581             processed 
= !Close(); 
 585             // we want to override the busy cursor for modal dialogs: 
 586             // typically, wxBeginBusyCursor() is called and then a modal dialog 
 587             // is shown, but the modal dialog shouldn't have hourglass cursor 
 588             if ( IsModalShowing() && wxIsBusy() ) 
 590                 // set our cursor for all windows (but see below) 
 591                 wxCursor cursor 
= m_cursor
; 
 593                     cursor 
= wxCURSOR_ARROW
; 
 595                 ::SetCursor(GetHcursorOf(cursor
)); 
 597                 // in any case, stop here and don't let wxWindow process this 
 598                 // message (it would set the busy cursor) 
 601                 // but return FALSE to tell the child window (if the event 
 602                 // comes from one of them and not from ourselves) that it can 
 603                 // set its own cursor if it has one: thus, standard controls 
 604                 // (e.g. text ctrl) still have correct cursors in a dialog 
 605                 // invoked while wxIsBusy() 
 612         rc 
= wxWindow::MSWWindowProc(message
, wParam
, lParam
); 
 619 // Define for each class of dialog and control 
 620 WXHBRUSH 
wxDialog::OnCtlColor(WXHDC 
WXUNUSED(pDC
), 
 621                               WXHWND 
WXUNUSED(pWnd
), 
 622                               WXUINT 
WXUNUSED(nCtlColor
), 
 627     return (WXHBRUSH
)Ctl3dCtlColorEx(message
, wParam
, lParam
); 
 630 #endif // wxUSE_CTL3D