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"
31 #include "wx/msw/private.h"
34 #if wxUSE_COMMON_DIALOGS
38 #define wxDIALOG_DEFAULT_X 300
39 #define wxDIALOG_DEFAULT_Y 300
41 // Lists to keep track of windows, so we can disable/enable them
43 wxList wxModalDialogs
;
44 wxList wxModelessWindows
; // Frames and modeless dialogs
45 extern wxList WXDLLEXPORT wxPendingDelete
;
47 #if !USE_SHARED_LIBRARY
48 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxPanel
)
50 BEGIN_EVENT_TABLE(wxDialog
, wxPanel
)
51 EVT_SIZE(wxDialog::OnSize
)
52 EVT_BUTTON(wxID_OK
, wxDialog::OnOK
)
53 EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
)
54 EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
)
55 EVT_CHAR_HOOK(wxDialog::OnCharHook
)
56 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
)
57 EVT_CLOSE(wxDialog::OnCloseWindow
)
61 bool wxDialog::MSWProcessMessage(WXMSG
* pMsg
)
63 return (::IsDialogMessage((HWND
) GetHWND(), (MSG
*)pMsg
) != 0);
66 bool wxDialog::MSWOnClose(void)
71 wxDialog::wxDialog(void)
74 m_modalShowing
= FALSE
;
76 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
79 bool wxDialog::Create(wxWindow
*parent
, wxWindowID id
,
80 const wxString
& title
,
86 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
90 wxTopLevelWindows
.Append(this);
92 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
94 if (parent
) parent
->AddChild(this);
97 m_windowId
= (int)NewControlId();
106 if (x
< 0) x
= wxDIALOG_DEFAULT_X
;
107 if (y
< 0) y
= wxDIALOG_DEFAULT_Y
;
109 m_windowStyle
= style
;
112 m_modalShowing
= FALSE
;
119 WXDWORD extendedStyle
= MakeExtendedStyle(m_windowStyle
);
120 if (m_windowStyle
& wxSTAY_ON_TOP
)
121 extendedStyle
|= WS_EX_TOPMOST
;
123 // Allows creation of dialogs with & without captions under MSWindows,
124 // resizeable or not (but a resizeable dialog always has caption -
125 // otherwise it would look too strange)
127 if ( style
& wxTHICK_FRAME
)
128 dlg
= "wxResizeableDialog";
129 else if ( style
& wxCAPTION
)
130 dlg
= "wxCaptionDialog";
132 dlg
= "wxNoCaptionDialog";
133 MSWCreate(m_windowId
, parent
, NULL
, this, NULL
,
135 0, // style is not used if we have dlg template
139 HWND hwnd
= (HWND
)GetHWND();
143 wxLogError(_("Failed to created dialog."));
148 SubclassWin(GetHWND());
150 SetWindowText(hwnd
, title
);
151 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
));
156 void wxDialog::SetModal(bool flag
)
159 m_windowStyle
|= wxDIALOG_MODAL
;
161 if ( m_windowStyle
& wxDIALOG_MODAL
)
162 m_windowStyle
-= wxDIALOG_MODAL
;
164 wxModelessWindows
.DeleteObject(this);
166 wxModelessWindows
.Append(this);
169 wxDialog::~wxDialog()
171 m_isBeingDeleted
= TRUE
;
173 wxTopLevelWindows
.DeleteObject(this);
178 // For some reason, wxWindows can activate another task altogether
179 // when a frame is destroyed after a modal dialog has been invoked.
180 // Try to bring the parent to the top.
181 // dfgg: I moved this following line from end of function -
182 // must not call if another window is on top!!
183 // This can often happen with Close() and delayed deleting
184 if (GetParent() && GetParent()->GetHWND())
185 ::BringWindowToTop((HWND
) GetParent()->GetHWND());
188 m_modalShowing
= FALSE
;
190 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
192 if ( (GetWindowStyleFlag() & wxDIALOG_MODAL
) != wxDIALOG_MODAL
)
193 wxModelessWindows
.DeleteObject(this);
197 // If this is the last top-level window, exit.
198 if (wxTheApp
&& (wxTopLevelWindows
.Number() == 0))
200 wxTheApp
->SetTopWindow(NULL
);
202 if (wxTheApp
->GetExitOnFrameDelete())
209 // By default, pressing escape cancels the dialog
210 void wxDialog::OnCharHook(wxKeyEvent
& event
)
214 if (event
.m_keyCode
== WXK_ESCAPE
)
216 // Behaviour changed in 2.0: we'll send a Cancel message
217 // to the dialog instead of Close.
218 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
219 cancelEvent
.SetEventObject( this );
220 GetEventHandler()->ProcessEvent(cancelEvent
);
225 // We didn't process this event.
229 void wxDialog::OnPaint(wxPaintEvent
& event
)
231 // No: if you call the default procedure, it makes
232 // the following painting code not work.
233 // wxWindow::OnPaint(event);
236 void wxDialog::Fit(void)
241 void wxDialog::Iconize(bool WXUNUSED(iconize
))
243 // Windows dialog boxes can't be iconized
246 bool wxDialog::IsIconized(void) const
251 void wxDialog::SetClientSize(int width
, int height
)
253 HWND hWnd
= (HWND
) GetHWND();
255 ::GetClientRect(hWnd
, &rect
);
258 GetWindowRect(hWnd
, &rect2
);
260 // Find the difference between the entire window (title bar and all)
261 // and the client area; add this to the new client size to move the
263 int actual_width
= rect2
.right
- rect2
.left
- rect
.right
+ width
;
264 int actual_height
= rect2
.bottom
- rect2
.top
- rect
.bottom
+ height
;
266 MoveWindow(hWnd
, rect2
.left
, rect2
.top
, actual_width
, actual_height
, TRUE
);
268 wxSizeEvent
event(wxSize(actual_width
, actual_height
), m_windowId
);
269 event
.SetEventObject( this );
270 GetEventHandler()->ProcessEvent(event
);
273 void wxDialog::GetPosition(int *x
, int *y
) const
275 HWND hWnd
= (HWND
) GetHWND();
277 GetWindowRect(hWnd
, &rect
);
283 bool wxDialog::IsShown(void) const
288 bool wxDialog::Show(bool show
)
295 bool modal
= ((GetWindowStyleFlag() & wxDIALOG_MODAL
) == wxDIALOG_MODAL
) ;
297 #if WXGARBAGE_COLLECTION_ON /* MATTHEW: GC */
300 if (!wxModelessWindows
.Member(this))
301 wxModelessWindows
.Append(this);
303 wxModelessWindows
.DeleteObject(this);
306 if (!wxTopLevelWindows
.Member(this))
307 wxTopLevelWindows
.Append(this);
309 wxTopLevelWindows
.DeleteObject(this);
316 m_hwndOldFocus
= (WXHWND
)::GetFocus();
320 BringWindowToTop((HWND
) GetHWND());
324 m_modalShowing
= TRUE
;
325 wxNode
*node
= wxModalDialogs
.First();
328 wxDialog
*box
= (wxDialog
*)node
->Data();
330 ::EnableWindow((HWND
) box
->GetHWND(), FALSE
);
334 // if we don't do it, some window might be deleted while we have pointers
335 // to them in our disabledWindows list and the program will crash when it
336 // will try to reenable them after the modal dialog end
337 wxTheApp
->DeletePendingObjects();
338 wxList disabledWindows
;
340 node
= wxModelessWindows
.First();
343 wxWindow
*win
= (wxWindow
*)node
->Data();
344 if (::IsWindowEnabled((HWND
) win
->GetHWND()))
346 ::EnableWindow((HWND
) win
->GetHWND(), FALSE
);
347 disabledWindows
.Append(win
);
352 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
353 EnableWindow((HWND
) GetHWND(), TRUE
);
354 BringWindowToTop((HWND
) GetHWND());
356 if (!wxModalDialogs
.Member(this))
357 wxModalDialogs
.Append(this);
360 // Must test whether this dialog still exists: we may not process
361 // a message before the deletion.
362 while (wxModalDialogs
.Member(this) && m_modalShowing
&& GetMessage(&msg
, NULL
, 0, 0))
364 if (m_acceleratorTable
.Ok() &&
365 ::TranslateAccelerator((HWND
) GetHWND(), (HACCEL
) m_acceleratorTable
.GetHACCEL(), &msg
))
367 // Have processed the message
369 else if (!IsDialogMessage((HWND
) GetHWND(), &msg
))
371 TranslateMessage(&msg
);
372 DispatchMessage(&msg
);
375 // If we get crashes (as per George Tasker's message) with nested modal dialogs,
376 // we should try removing the m_modalShowing test
378 if (m_modalShowing
&& !::PeekMessage(&msg
, 0, 0, 0, PM_NOREMOVE
))
379 // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered
380 // a Show(FALSE) in the mean time!!!
381 // Without the test, we might delete the dialog before the end of modal showing.
383 while (wxTheApp
->ProcessIdle() && m_modalShowing
)
385 // Keep going until we decide we've done enough
389 // dfgg: now must specifically re-enable all other app windows that we disabled earlier
390 node
=disabledWindows
.First();
392 wxWindow
* win
= (wxWindow
*) node
->Data();
393 if (wxModalDialogs
.Member(win
) || wxModelessWindows
.Member(win
))
395 HWND hWnd
= (HWND
) win
->GetHWND();
396 if (::IsWindow(hWnd
))
397 ::EnableWindow(hWnd
,TRUE
);
404 ::SetFocus((HWND
)m_hwndOldFocus
);
406 wxModalDialogs
.DeleteObject(this);
408 wxNode
*last
= wxModalDialogs
.Last();
410 // If there's still a modal dialog active, we
411 // enable it, else we enable all modeless windows
414 wxDialog
*box
= (wxDialog
*)last
->Data();
415 HWND hwnd
= (HWND
) box
->GetHWND();
416 if (box
->m_winEnabled
)
417 EnableWindow(hwnd
, TRUE
);
418 BringWindowToTop(hwnd
);
422 wxNode
*node
= wxModelessWindows
.First();
425 wxWindow
*win
= (wxWindow
*)node
->Data();
426 HWND hwnd
= (HWND
) win
->GetHWND();
427 // Only enable again if not user-disabled.
428 if (win
->IsUserEnabled())
429 EnableWindow(hwnd
, TRUE
);
433 // Try to highlight the correct window (the parent)
437 hWndParent
= (HWND
) GetParent()->GetHWND();
439 ::BringWindowToTop(hWndParent
);
441 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
442 m_modalShowing
= FALSE
;
449 ShowWindow((HWND
) GetHWND(), SW_SHOW
);
450 BringWindowToTop((HWND
) GetHWND());
454 // Try to highlight the correct window (the parent)
458 hWndParent
= (HWND
) GetParent()->GetHWND();
460 ::BringWindowToTop(hWndParent
);
462 ShowWindow((HWND
) GetHWND(), SW_HIDE
);
468 void wxDialog::SetTitle(const wxString
& title
)
470 SetWindowText((HWND
) GetHWND(), (const char *)title
);
473 wxString
wxDialog::GetTitle(void) const
475 GetWindowText((HWND
) GetHWND(), wxBuffer
, 1000);
476 return wxString(wxBuffer
);
479 void wxDialog::Centre(int direction
)
481 int x_offset
,y_offset
;
482 int display_width
, display_height
;
483 int width
, height
, x
, y
;
484 wxWindow
*parent
= GetParent();
485 if ((direction
& wxCENTER_FRAME
) && parent
)
487 parent
->GetPosition(&x_offset
,&y_offset
) ;
488 parent
->GetSize(&display_width
,&display_height
) ;
492 wxDisplaySize(&display_width
, &display_height
);
497 GetSize(&width
, &height
);
500 if (direction
& wxHORIZONTAL
)
501 x
= (int)((display_width
- width
)/2);
502 if (direction
& wxVERTICAL
)
503 y
= (int)((display_height
- height
)/2);
505 SetSize(x
+x_offset
, y
+y_offset
, width
, height
);
508 // Replacement for Show(TRUE) for modal dialogs - returns return code
509 int wxDialog::ShowModal(void)
511 m_windowStyle
|= wxDIALOG_MODAL
;
513 return GetReturnCode();
516 void wxDialog::EndModal(int retCode
)
518 SetReturnCode(retCode
);
522 // Define for each class of dialog and control
523 WXHBRUSH
wxDialog::OnCtlColor(WXHDC pDC
, WXHWND pWnd
, WXUINT nCtlColor
,
524 WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
527 HBRUSH hbrush
= Ctl3dCtlColorEx(message
, wParam
, lParam
);
528 return (WXHBRUSH
) hbrush
;
535 void wxDialog::OnOK(wxCommandEvent
& event
)
537 if ( Validate() && TransferDataFromWindow() )
543 SetReturnCode(wxID_OK
);
549 void wxDialog::OnApply(wxCommandEvent
& event
)
552 TransferDataFromWindow();
553 // TODO probably need to disable the Apply button until things change again
556 void wxDialog::OnCancel(wxCommandEvent
& event
)
559 EndModal(wxID_CANCEL
);
562 SetReturnCode(wxID_CANCEL
);
567 void wxDialog::OnCloseWindow(wxCloseEvent
& event
)
569 // We'll send a Cancel message by default,
570 // which may close the dialog.
571 // Check for looping if the Cancel event handler calls Close().
573 // Note that if a cancel button and handler aren't present in the dialog,
574 // nothing will happen when you close the dialog via the window manager, or
576 // We wouldn't want to destroy the dialog by default, since the dialog may have been
577 // created on the stack.
578 // However, this does mean that calling dialog->Close() won't delete the dialog
579 // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be
580 // sure to destroy the dialog.
581 // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog.
583 static wxList closing
;
585 if ( closing
.Member(this) )
588 closing
.Append(this);
590 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
591 cancelEvent
.SetEventObject( this );
592 GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog
594 closing
.DeleteObject(this);
597 // Destroy the window (delayed, if a managed window)
598 bool wxDialog::Destroy(void)
600 if (!wxPendingDelete
.Member(this))
601 wxPendingDelete
.Append(this);
605 void wxDialog::OnSize(wxSizeEvent
& WXUNUSED(event
))
607 // if we're using constraints - do use them
608 #if wxUSE_CONSTRAINTS
609 if ( GetAutoLayout() ) {
615 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& event
)
620 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));