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 // ----------------------------------------------------------------------------
103 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
106 bool wxDialog::Create(wxWindow
*parent
, wxWindowID id
,
107 const wxString
& title
,
111 const wxString
& name
)
113 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
117 wxTopLevelWindows
.Append(this);
119 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
121 if (parent
) parent
->AddChild(this);
124 m_windowId
= (int)NewControlId();
134 x
= wxDIALOG_DEFAULT_X
;
136 y
= wxDIALOG_DEFAULT_Y
;
138 m_windowStyle
= style
;
143 width
= wxDIALOG_DEFAULT_WIDTH
;
145 height
= wxDIALOG_DEFAULT_HEIGHT
;
147 // All dialogs should really have this style
148 m_windowStyle
|= wxTAB_TRAVERSAL
;
150 WXDWORD extendedStyle
= MakeExtendedStyle(m_windowStyle
);
151 if (m_windowStyle
& wxSTAY_ON_TOP
)
152 extendedStyle
|= WS_EX_TOPMOST
;
154 // Allows creation of dialogs with & without captions under MSWindows,
155 // resizeable or not (but a resizeable dialog always has caption -
156 // otherwise it would look too strange)
158 if ( style
& wxRESIZE_BORDER
)
159 dlg
= wxT("wxResizeableDialog");
160 else if ( style
& wxCAPTION
)
161 dlg
= wxT("wxCaptionDialog");
163 dlg
= wxT("wxNoCaptionDialog");
164 MSWCreate(m_windowId
, parent
, NULL
, this, NULL
,
166 0, // style is not used if we have dlg template
170 HWND hwnd
= (HWND
)GetHWND();
174 wxLogError(_("Failed to create dialog."));
179 SubclassWin(GetHWND());
181 SetWindowText(hwnd
, title
);
182 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
187 void wxDialog::SetModal(bool flag
)
191 m_windowStyle
|= wxDIALOG_MODAL
;
193 wxModelessWindows
.DeleteObject(this);
197 m_windowStyle
&= ~wxDIALOG_MODAL
;
199 wxModelessWindows
.Append(this);
203 wxDialog::~wxDialog()
205 m_isBeingDeleted
= TRUE
;
207 wxTopLevelWindows
.DeleteObject(this);
211 // VZ: this is bogus and breaks focus handling - it won't be returned to
212 // the window which had it previosuly if we do this
216 // For some reason, wxWindows can activate another task altogether
217 // when a frame is destroyed after a modal dialog has been invoked.
218 // Try to bring the parent to the top.
219 // dfgg: I moved this following line from end of function -
220 // must not call if another window is on top!!
221 // This can often happen with Close() and delayed deleting
222 if (GetParent() && GetParent()->GetHWND())
223 ::BringWindowToTop((HWND
) GetParent()->GetHWND());
226 m_modalShowing
= FALSE
;
230 wxModelessWindows
.DeleteObject(this);
232 // If this is the last top-level window, exit.
233 if ( wxTheApp
&& (wxTopLevelWindows
.Number() == 0) )
235 wxTheApp
->SetTopWindow(NULL
);
237 if ( wxTheApp
->GetExitOnFrameDelete() )
239 ::PostQuitMessage(0);
244 // ----------------------------------------------------------------------------
246 // ----------------------------------------------------------------------------
248 // By default, pressing escape cancels the dialog
249 void wxDialog::OnCharHook(wxKeyEvent
& event
)
253 if (event
.m_keyCode
== WXK_ESCAPE
)
255 // Behaviour changed in 2.0: we'll send a Cancel message
256 // to the dialog instead of Close.
257 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
258 cancelEvent
.SetEventObject( this );
259 GetEventHandler()->ProcessEvent(cancelEvent
);
261 // ensure that there is another message for this window so the
262 // ShowModal loop will exit and won't get stuck in GetMessage().
263 ::PostMessage(GetHwnd(), WM_NULL
, 0, 0);
267 // We didn't process this event.
271 // ----------------------------------------------------------------------------
272 // Windows dialog boxes can't be iconized
273 // ----------------------------------------------------------------------------
275 void wxDialog::Iconize(bool WXUNUSED(iconize
))
279 bool wxDialog::IsIconized() const
284 // ----------------------------------------------------------------------------
285 // size/position handling
286 // ----------------------------------------------------------------------------
288 void wxDialog::DoSetClientSize(int width
, int height
)
290 HWND hWnd
= (HWND
) GetHWND();
292 ::GetClientRect(hWnd
, &rect
);
295 GetWindowRect(hWnd
, &rect2
);
297 // Find the difference between the entire window (title bar and all)
298 // and the client area; add this to the new client size to move the
300 int actual_width
= rect2
.right
- rect2
.left
- rect
.right
+ width
;
301 int actual_height
= rect2
.bottom
- rect2
.top
- rect
.bottom
+ height
;
303 MoveWindow(hWnd
, rect2
.left
, rect2
.top
, actual_width
, actual_height
, TRUE
);
305 wxSizeEvent
event(wxSize(actual_width
, actual_height
), m_windowId
);
306 event
.SetEventObject( this );
307 GetEventHandler()->ProcessEvent(event
);
310 void wxDialog::DoGetPosition(int *x
, int *y
) const
313 GetWindowRect(GetHwnd(), &rect
);
321 // ----------------------------------------------------------------------------
322 // showing the dialogs
323 // ----------------------------------------------------------------------------
325 bool wxDialog::IsModal() const
327 return (GetWindowStyleFlag() & wxDIALOG_MODAL
) != 0;
330 // VZ: this is the old version unchanged (reindented only), it will be removed
331 // as soon as we're sure the new one works correctly
334 bool wxDialog::Show(bool show
)
341 bool modal
= IsModal();
347 // find the top level window which had focus before - we will restore
350 for ( HWND hwnd
= ::GetFocus(); hwnd
; hwnd
= ::GetParent(hwnd
) )
352 m_hwndOldFocus
= (WXHWND
)hwnd
;
357 BringWindowToTop((HWND
) GetHWND());
361 m_modalShowing
= TRUE
;
362 wxNode
*node
= wxModalDialogs
.First();
365 wxDialog
*box
= (wxDialog
*)node
->Data();
367 ::EnableWindow((HWND
) box
->GetHWND(), FALSE
);
371 // if we don't do it, some window might be deleted while we have pointers
372 // to them in our disabledWindows list and the program will crash when it
373 // will try to reenable them after the modal dialog end
374 wxTheApp
->DeletePendingObjects();
375 wxList disabledWindows
;
377 node
= wxModelessWindows
.First();
380 wxWindow
*win
= (wxWindow
*)node
->Data();
381 if (::IsWindowEnabled((HWND
) win
->GetHWND()))
383 ::EnableWindow((HWND
) win
->GetHWND(), FALSE
);
384 disabledWindows
.Append(win
);
389 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
390 EnableWindow((HWND
) GetHWND(), TRUE
);
391 BringWindowToTop((HWND
) GetHWND());
393 if ( !wxModalDialogs
.Find(this) )
394 wxModalDialogs
.Append(this);
397 // Must test whether this dialog still exists: we may not process
398 // a message before the deletion.
399 while (wxModalDialogs
.Find(this) && m_modalShowing
&& GetMessage(&msg
, NULL
, 0, 0))
401 if ( m_acceleratorTable
.Ok() &&
402 ::TranslateAccelerator((HWND
)GetHWND(),
403 (HACCEL
)m_acceleratorTable
.GetHACCEL(),
406 // Have processed the message
408 else if ( !wxTheApp
->ProcessMessage((WXMSG
*)&msg
) )
410 TranslateMessage(&msg
);
411 DispatchMessage(&msg
);
414 // If we get crashes (as per George Tasker's message) with nested modal dialogs,
415 // we should try removing the m_modalShowing test
417 if (m_modalShowing
&& !::PeekMessage(&msg
, 0, 0, 0, PM_NOREMOVE
))
418 // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered
419 // a Show(FALSE) in the mean time!!!
420 // Without the test, we might delete the dialog before the end of modal showing.
422 while (wxTheApp
->ProcessIdle() && m_modalShowing
)
424 // Keep going until we decide we've done enough
428 // dfgg: now must specifically re-enable all other app windows that we disabled earlier
429 node
=disabledWindows
.First();
431 wxWindow
* win
= (wxWindow
*) node
->Data();
432 if (wxModalDialogs
.Find(win
) || wxModelessWindows
.Find(win
))
434 HWND hWnd
= (HWND
) win
->GetHWND();
435 if (::IsWindow(hWnd
))
436 ::EnableWindow(hWnd
,TRUE
);
443 ::SetFocus((HWND
)m_hwndOldFocus
);
445 wxModalDialogs
.DeleteObject(this);
447 wxNode
*last
= wxModalDialogs
.Last();
449 // If there's still a modal dialog active, we
450 // enable it, else we enable all modeless windows
453 // VZ: I don't understand what this is supposed to do, so I'll leave
454 // it out for now and look for horrible consequences
455 wxDialog
*box
= (wxDialog
*)last
->Data();
456 HWND hwnd
= (HWND
) box
->GetHWND();
458 if (box
->IsUserEnabled())
460 EnableWindow(hwnd
, TRUE
);
461 BringWindowToTop(hwnd
);
465 wxNode
*node
= wxModelessWindows
.First();
468 wxWindow
*win
= (wxWindow
*)node
->Data();
469 HWND hwnd
= (HWND
) win
->GetHWND();
470 // Only enable again if not user-disabled.
472 if (win
->IsUserEnabled())
474 EnableWindow(hwnd
, TRUE
);
478 // Try to highlight the correct window (the parent)
482 hWndParent
= (HWND
) GetParent()->GetHWND();
484 ::BringWindowToTop(hWndParent
);
486 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
487 m_modalShowing
= FALSE
;
494 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
495 BringWindowToTop((HWND
) GetHWND());
499 // Try to highlight the correct window (the parent)
503 hWndParent
= (HWND
) GetParent()->GetHWND();
505 ::BringWindowToTop(hWndParent
);
509 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
518 bool wxDialog::IsModalShowing() const
520 return wxModalDialogs
.Find((wxDialog
*)this) != NULL
; // const_cast
523 void wxDialog::DoShowModal()
525 wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") );
527 wxModalDialogs
.Append(this);
529 wxWindow
*parent
= GetParent();
531 // remember where the focus was
532 wxWindow
*winFocus
= FindFocus();
539 winFocus
= wxTheApp
->GetTopWindow();
542 // disable the parent window first
543 HWND hwndParent
= parent
? GetHwndOf(parent
) : (HWND
)NULL
;
546 ::EnableWindow(hwndParent
, FALSE
);
549 // enter the modal loop
550 while ( IsModalShowing() )
553 wxMutexGuiLeaveOrEnter();
554 #endif // wxUSE_THREADS
556 while ( !wxTheApp
->Pending() && wxTheApp
->ProcessIdle() )
559 // a message came or no more idle processing to do
560 wxTheApp
->DoMessage();
563 // reenable the parent window if any
566 ::EnableWindow(hwndParent
, TRUE
);
570 if ( winFocus
&& (winFocus
!= this) )
572 winFocus
->SetFocus();
576 bool wxDialog::Show(bool show
)
578 if ( !wxDialogBase::Show(show
) )
586 // usually will result in TransferDataToWindow() being called
596 else // end of modal dialog
598 // this will cause IsModalShowing() return FALSE and our local
599 // message loop will terminate
600 wxModalDialogs
.DeleteObject(this);
609 // Replacement for Show(TRUE) for modal dialogs - returns return code
610 int wxDialog::ShowModal()
612 m_windowStyle
|= wxDIALOG_MODAL
;
614 return GetReturnCode();
617 // NB: this function (surprizingly) may be called for both modal and modeless
618 // dialogs and should work for both of them
619 void wxDialog::EndModal(int retCode
)
621 SetReturnCode(retCode
);
625 // ----------------------------------------------------------------------------
626 // wxWin event handlers
627 // ----------------------------------------------------------------------------
630 void wxDialog::OnOK(wxCommandEvent
& event
)
632 if ( Validate() && TransferDataFromWindow() )
638 void wxDialog::OnApply(wxCommandEvent
& event
)
641 TransferDataFromWindow();
643 // TODO probably need to disable the Apply button until things change again
646 void wxDialog::OnCancel(wxCommandEvent
& event
)
648 EndModal(wxID_CANCEL
);
651 void wxDialog::OnCloseWindow(wxCloseEvent
& event
)
653 // We'll send a Cancel message by default, which may close the dialog.
654 // Check for looping if the Cancel event handler calls Close().
656 // Note that if a cancel button and handler aren't present in the dialog,
657 // nothing will happen when you close the dialog via the window manager, or
658 // via Close(). We wouldn't want to destroy the dialog by default, since
659 // the dialog may have been created on the stack. However, this does mean
660 // that calling dialog->Close() won't delete the dialog unless the handler
661 // for wxID_CANCEL does so. So use Destroy() if you want to be sure to
662 // destroy the dialog. The default OnCancel (above) simply ends a modal
663 // dialog, and hides a modeless dialog.
665 // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global
666 // lists here? don't dare to change it now, but should be done later!
667 static wxList closing
;
669 if ( closing
.Member(this) )
672 closing
.Append(this);
674 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
675 cancelEvent
.SetEventObject( this );
676 GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog
678 closing
.DeleteObject(this);
681 // Destroy the window (delayed, if a managed window)
682 bool wxDialog::Destroy()
684 wxCHECK_MSG( !wxPendingDelete
.Member(this), FALSE
,
685 _T("wxDialog destroyed twice") );
687 wxPendingDelete
.Append(this);
692 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& event
)
697 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
702 // ---------------------------------------------------------------------------
703 // dialog window proc
704 // ---------------------------------------------------------------------------
706 long wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
709 bool processed
= FALSE
;
714 // if we can't close, tell the system that we processed the
715 // message - otherwise it would close us
716 processed
= !Close();
721 rc
= wxWindow::MSWWindowProc(message
, wParam
, lParam
);
728 // Define for each class of dialog and control
729 WXHBRUSH
wxDialog::OnCtlColor(WXHDC
WXUNUSED(pDC
),
730 WXHWND
WXUNUSED(pWnd
),
731 WXUINT
WXUNUSED(nCtlColor
),
736 return (WXHBRUSH
)Ctl3dCtlColorEx(message
, wParam
, lParam
);
739 #endif // wxUSE_CTL3D