1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxDialog class
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "dialog.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
24 #include "wx/dialog.h"
28 #include "wx/settings.h"
33 #include "wx/msw/private.h"
36 #if wxUSE_COMMON_DIALOGS
40 #define wxDIALOG_DEFAULT_X 300
41 #define wxDIALOG_DEFAULT_Y 300
43 // Lists to keep track of windows, so we can disable/enable them
45 wxWindowList wxModalDialogs
;
46 wxWindowList wxModelessWindows
; // Frames and modeless dialogs
47 extern wxList WXDLLEXPORT wxPendingDelete
;
49 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxPanel
)
51 BEGIN_EVENT_TABLE(wxDialog
, wxPanel
)
52 EVT_SIZE(wxDialog::OnSize
)
53 EVT_BUTTON(wxID_OK
, wxDialog::OnOK
)
54 EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
)
55 EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
)
56 EVT_CHAR_HOOK(wxDialog::OnCharHook
)
57 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
)
58 EVT_CLOSE(wxDialog::OnCloseWindow
)
64 m_modalShowing
= FALSE
;
66 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
69 bool wxDialog::Create(wxWindow
*parent
, wxWindowID id
,
70 const wxString
& title
,
80 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
84 wxTopLevelWindows
.Append(this);
86 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
88 if (parent
) parent
->AddChild(this);
91 m_windowId
= (int)NewControlId();
100 if (x
< 0) x
= wxDIALOG_DEFAULT_X
;
101 if (y
< 0) y
= wxDIALOG_DEFAULT_Y
;
103 m_windowStyle
= style
;
106 m_modalShowing
= FALSE
;
113 // All dialogs should really have this style
114 m_windowStyle
|= wxTAB_TRAVERSAL
;
116 WXDWORD extendedStyle
= MakeExtendedStyle(m_windowStyle
);
117 if (m_windowStyle
& wxSTAY_ON_TOP
)
118 extendedStyle
|= WS_EX_TOPMOST
;
120 // Allows creation of dialogs with & without captions under MSWindows,
121 // resizeable or not (but a resizeable dialog always has caption -
122 // otherwise it would look too strange)
124 if ( style
& wxRESIZE_BORDER
)
125 dlg
= wxT("wxResizeableDialog");
126 else if ( style
& wxCAPTION
)
127 dlg
= wxT("wxCaptionDialog");
129 dlg
= wxT("wxNoCaptionDialog");
130 MSWCreate(m_windowId
, parent
, NULL
, this, NULL
,
132 0, // style is not used if we have dlg template
136 HWND hwnd
= (HWND
)GetHWND();
140 wxLogError(_("Failed to create dialog."));
145 SubclassWin(GetHWND());
147 SetWindowText(hwnd
, title
);
148 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
153 void wxDialog::SetModal(bool flag
)
156 m_windowStyle
|= wxDIALOG_MODAL
;
158 if ( m_windowStyle
& wxDIALOG_MODAL
)
159 m_windowStyle
-= wxDIALOG_MODAL
;
161 wxModelessWindows
.DeleteObject(this);
163 wxModelessWindows
.Append(this);
166 wxDialog::~wxDialog()
168 m_isBeingDeleted
= TRUE
;
170 wxTopLevelWindows
.DeleteObject(this);
174 // VZ: this is bogus and breaks focus handling - it won't be returned to the
175 // window which had it previosuly if we do this
179 // For some reason, wxWindows can activate another task altogether
180 // when a frame is destroyed after a modal dialog has been invoked.
181 // Try to bring the parent to the top.
182 // dfgg: I moved this following line from end of function -
183 // must not call if another window is on top!!
184 // This can often happen with Close() and delayed deleting
185 if (GetParent() && GetParent()->GetHWND())
186 ::BringWindowToTop((HWND
) GetParent()->GetHWND());
190 m_modalShowing
= FALSE
;
192 if ( (GetWindowStyleFlag() & wxDIALOG_MODAL
) != wxDIALOG_MODAL
)
193 wxModelessWindows
.DeleteObject(this);
195 // If this is the last top-level window, exit.
196 if (wxTheApp
&& (wxTopLevelWindows
.Number() == 0))
198 wxTheApp
->SetTopWindow(NULL
);
200 if (wxTheApp
->GetExitOnFrameDelete())
207 // By default, pressing escape cancels the dialog
208 void wxDialog::OnCharHook(wxKeyEvent
& event
)
212 if (event
.m_keyCode
== WXK_ESCAPE
)
214 // Behaviour changed in 2.0: we'll send a Cancel message
215 // to the dialog instead of Close.
216 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
217 cancelEvent
.SetEventObject( this );
218 GetEventHandler()->ProcessEvent(cancelEvent
);
220 // ensure that there is another message for this window so the
221 // ShowModal loop will exit and won't get stuck in GetMessage().
222 ::PostMessage(GetHwnd(), WM_NULL
, 0, 0);
226 // We didn't process this event.
230 void wxDialog::OnPaint(wxPaintEvent
& event
)
232 // No: if you call the default procedure, it makes
233 // the following painting code not work.
234 // wxWindow::OnPaint(event);
242 void wxDialog::Iconize(bool WXUNUSED(iconize
))
244 // Windows dialog boxes can't be iconized
247 bool wxDialog::IsIconized() const
252 void wxDialog::DoSetClientSize(int width
, int height
)
254 HWND hWnd
= (HWND
) GetHWND();
256 ::GetClientRect(hWnd
, &rect
);
259 GetWindowRect(hWnd
, &rect2
);
261 // Find the difference between the entire window (title bar and all)
262 // and the client area; add this to the new client size to move the
264 int actual_width
= rect2
.right
- rect2
.left
- rect
.right
+ width
;
265 int actual_height
= rect2
.bottom
- rect2
.top
- rect
.bottom
+ height
;
267 MoveWindow(hWnd
, rect2
.left
, rect2
.top
, actual_width
, actual_height
, TRUE
);
269 wxSizeEvent
event(wxSize(actual_width
, actual_height
), m_windowId
);
270 event
.SetEventObject( this );
271 GetEventHandler()->ProcessEvent(event
);
274 void wxDialog::GetPosition(int *x
, int *y
) const
276 HWND hWnd
= (HWND
) GetHWND();
278 GetWindowRect(hWnd
, &rect
);
284 bool wxDialog::IsShown() const
289 bool wxDialog::IsModal() const
291 return wxModalDialogs
.Find((wxDialog
*)this) != 0; // const_cast
294 bool wxDialog::Show(bool show
)
301 bool modal
= ((GetWindowStyleFlag() & wxDIALOG_MODAL
) == wxDIALOG_MODAL
) ;
303 #if WXGARBAGE_COLLECTION_ON /* MATTHEW: GC */
306 if (!wxModelessWindows
.Find(this))
307 wxModelessWindows
.Append(this);
309 wxModelessWindows
.DeleteObject(this);
312 if (!wxTopLevelWindows
.Find(this))
313 wxTopLevelWindows
.Append(this);
315 wxTopLevelWindows
.DeleteObject(this);
322 // find the top level window which had focus before - we will restore
325 for ( HWND hwnd
= ::GetFocus(); hwnd
; hwnd
= ::GetParent(hwnd
) )
327 m_hwndOldFocus
= (WXHWND
)hwnd
;
332 BringWindowToTop((HWND
) GetHWND());
336 m_modalShowing
= TRUE
;
337 wxNode
*node
= wxModalDialogs
.First();
340 wxDialog
*box
= (wxDialog
*)node
->Data();
342 ::EnableWindow((HWND
) box
->GetHWND(), FALSE
);
346 // if we don't do it, some window might be deleted while we have pointers
347 // to them in our disabledWindows list and the program will crash when it
348 // will try to reenable them after the modal dialog end
349 wxTheApp
->DeletePendingObjects();
350 wxList disabledWindows
;
352 node
= wxModelessWindows
.First();
355 wxWindow
*win
= (wxWindow
*)node
->Data();
356 if (::IsWindowEnabled((HWND
) win
->GetHWND()))
358 ::EnableWindow((HWND
) win
->GetHWND(), FALSE
);
359 disabledWindows
.Append(win
);
364 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
365 EnableWindow((HWND
) GetHWND(), TRUE
);
366 BringWindowToTop((HWND
) GetHWND());
368 if ( !wxModalDialogs
.Find(this) )
369 wxModalDialogs
.Append(this);
372 // Must test whether this dialog still exists: we may not process
373 // a message before the deletion.
374 while (wxModalDialogs
.Find(this) && m_modalShowing
&& GetMessage(&msg
, NULL
, 0, 0))
376 if ( m_acceleratorTable
.Ok() &&
377 ::TranslateAccelerator((HWND
)GetHWND(),
378 (HACCEL
)m_acceleratorTable
.GetHACCEL(),
381 // Have processed the message
383 else if ( !wxTheApp
->ProcessMessage((WXMSG
*)&msg
) )
385 TranslateMessage(&msg
);
386 DispatchMessage(&msg
);
389 // If we get crashes (as per George Tasker's message) with nested modal dialogs,
390 // we should try removing the m_modalShowing test
392 if (m_modalShowing
&& !::PeekMessage(&msg
, 0, 0, 0, PM_NOREMOVE
))
393 // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered
394 // a Show(FALSE) in the mean time!!!
395 // Without the test, we might delete the dialog before the end of modal showing.
397 while (wxTheApp
->ProcessIdle() && m_modalShowing
)
399 // Keep going until we decide we've done enough
403 // dfgg: now must specifically re-enable all other app windows that we disabled earlier
404 node
=disabledWindows
.First();
406 wxWindow
* win
= (wxWindow
*) node
->Data();
407 if (wxModalDialogs
.Find(win
) || wxModelessWindows
.Find(win
))
409 HWND hWnd
= (HWND
) win
->GetHWND();
410 if (::IsWindow(hWnd
))
411 ::EnableWindow(hWnd
,TRUE
);
418 ::SetFocus((HWND
)m_hwndOldFocus
);
420 wxModalDialogs
.DeleteObject(this);
422 wxNode
*last
= wxModalDialogs
.Last();
424 // If there's still a modal dialog active, we
425 // enable it, else we enable all modeless windows
428 // VZ: I don't understand what this is supposed to do, so I'll leave
429 // it out for now and look for horrible consequences
430 wxDialog
*box
= (wxDialog
*)last
->Data();
431 HWND hwnd
= (HWND
) box
->GetHWND();
433 if (box
->IsUserEnabled())
435 EnableWindow(hwnd
, TRUE
);
436 BringWindowToTop(hwnd
);
440 wxNode
*node
= wxModelessWindows
.First();
443 wxWindow
*win
= (wxWindow
*)node
->Data();
444 HWND hwnd
= (HWND
) win
->GetHWND();
445 // Only enable again if not user-disabled.
447 if (win
->IsUserEnabled())
449 EnableWindow(hwnd
, TRUE
);
453 // Try to highlight the correct window (the parent)
457 hWndParent
= (HWND
) GetParent()->GetHWND();
459 ::BringWindowToTop(hWndParent
);
461 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
462 m_modalShowing
= FALSE
;
469 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
470 BringWindowToTop((HWND
) GetHWND());
474 // Try to highlight the correct window (the parent)
478 hWndParent
= (HWND
) GetParent()->GetHWND();
480 ::BringWindowToTop(hWndParent
);
484 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
490 void wxDialog::SetTitle(const wxString
& title
)
492 SetWindowText((HWND
) GetHWND(), title
.c_str());
495 wxString
wxDialog::GetTitle() const
497 GetWindowText((HWND
) GetHWND(), wxBuffer
, 1000);
498 return wxString(wxBuffer
);
501 void wxDialog::Centre(int direction
)
503 int x_offset
,y_offset
;
504 int display_width
, display_height
;
505 int width
, height
, x
, y
;
506 wxWindow
*parent
= GetParent();
507 if ((direction
& wxCENTER_FRAME
) && parent
)
509 parent
->GetPosition(&x_offset
,&y_offset
) ;
510 parent
->GetSize(&display_width
,&display_height
) ;
514 wxDisplaySize(&display_width
, &display_height
);
519 GetSize(&width
, &height
);
522 if (direction
& wxHORIZONTAL
)
523 x
= (int)((display_width
- width
)/2);
524 if (direction
& wxVERTICAL
)
525 y
= (int)((display_height
- height
)/2);
527 SetSize(x
+x_offset
, y
+y_offset
, width
, height
);
530 // Replacement for Show(TRUE) for modal dialogs - returns return code
531 int wxDialog::ShowModal()
533 m_windowStyle
|= wxDIALOG_MODAL
;
535 return GetReturnCode();
538 void wxDialog::EndModal(int retCode
)
540 SetReturnCode(retCode
);
544 // Define for each class of dialog and control
545 WXHBRUSH
wxDialog::OnCtlColor(WXHDC pDC
, WXHWND pWnd
, WXUINT nCtlColor
,
546 WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
549 HBRUSH hbrush
= Ctl3dCtlColorEx(message
, wParam
, lParam
);
550 return (WXHBRUSH
) hbrush
;
557 void wxDialog::OnOK(wxCommandEvent
& event
)
559 if ( Validate() && TransferDataFromWindow() )
565 SetReturnCode(wxID_OK
);
571 void wxDialog::OnApply(wxCommandEvent
& event
)
574 TransferDataFromWindow();
575 // TODO probably need to disable the Apply button until things change again
578 void wxDialog::OnCancel(wxCommandEvent
& event
)
581 EndModal(wxID_CANCEL
);
584 SetReturnCode(wxID_CANCEL
);
589 void wxDialog::OnCloseWindow(wxCloseEvent
& event
)
591 // We'll send a Cancel message by default,
592 // which may close the dialog.
593 // Check for looping if the Cancel event handler calls Close().
595 // Note that if a cancel button and handler aren't present in the dialog,
596 // nothing will happen when you close the dialog via the window manager, or
598 // We wouldn't want to destroy the dialog by default, since the dialog may have been
599 // created on the stack.
600 // However, this does mean that calling dialog->Close() won't delete the dialog
601 // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be
602 // sure to destroy the dialog.
603 // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog.
605 static wxList closing
;
607 if ( closing
.Member(this) )
610 closing
.Append(this);
612 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
613 cancelEvent
.SetEventObject( this );
614 GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog
616 closing
.DeleteObject(this);
619 // Destroy the window (delayed, if a managed window)
620 bool wxDialog::Destroy()
622 if (!wxPendingDelete
.Member(this))
623 wxPendingDelete
.Append(this);
627 void wxDialog::OnSize(wxSizeEvent
& WXUNUSED(event
))
629 // if we're using constraints - do use them
630 #if wxUSE_CONSTRAINTS
631 if ( GetAutoLayout() )
638 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& event
)
643 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
648 // ---------------------------------------------------------------------------
649 // dialog window proc
650 // ---------------------------------------------------------------------------
652 long wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
655 bool processed
= FALSE
;
660 // if we can't close, tell the system that we processed the
661 // message - otherwise it would close us
662 processed
= !Close();
667 rc
= wxWindow::MSWWindowProc(message
, wParam
, lParam
);