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 modal dialogs currently shown
65 static wxWindowList wxModalDialogs
;
67 // ----------------------------------------------------------------------------
69 // ----------------------------------------------------------------------------
71 IMPLEMENT_DYNAMIC_CLASS(wxDialog
, wxTopLevelWindow
)
73 BEGIN_EVENT_TABLE(wxDialog
, wxTopLevelWindow
)
74 EVT_BUTTON(wxID_OK
, wxDialog::OnOK
)
75 EVT_BUTTON(wxID_APPLY
, wxDialog::OnApply
)
76 EVT_BUTTON(wxID_CANCEL
, wxDialog::OnCancel
)
78 EVT_CHAR_HOOK(wxDialog::OnCharHook
)
80 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged
)
82 EVT_CLOSE(wxDialog::OnCloseWindow
)
85 // ============================================================================
87 // ============================================================================
89 // ----------------------------------------------------------------------------
90 // wxDialog construction
91 // ----------------------------------------------------------------------------
95 m_oldFocus
= (wxWindow
*)NULL
;
99 m_windowDisabler
= (wxWindowDisabler
*)NULL
;
101 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
104 bool wxDialog::Create(wxWindow
*parent
,
106 const wxString
& title
,
110 const wxString
& name
)
114 SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG
);
116 if ( !wxTopLevelWindow::Create(parent
, id
, title
, pos
, size
, style
, name
) )
119 m_oldFocus
= FindFocus();
127 x
= wxDIALOG_DEFAULT_X
;
129 y
= wxDIALOG_DEFAULT_Y
;
132 width
= wxDIALOG_DEFAULT_WIDTH
;
134 height
= wxDIALOG_DEFAULT_HEIGHT
;
136 // All dialogs should really have this style
137 m_windowStyle
|= wxTAB_TRAVERSAL
;
139 WXDWORD extendedStyle
= MakeExtendedStyle(m_windowStyle
);
140 if (m_windowStyle
& wxSTAY_ON_TOP
)
141 extendedStyle
|= WS_EX_TOPMOST
;
144 if (m_exStyle
& wxDIALOG_EX_CONTEXTHELP
)
145 extendedStyle
|= WS_EX_CONTEXTHELP
;
148 // Allows creation of dialogs with & without captions under MSWindows,
149 // resizeable or not (but a resizeable dialog always has caption -
150 // otherwise it would look too strange)
152 if ( style
& wxRESIZE_BORDER
)
153 dlg
= wxT("wxResizeableDialog");
154 else if ( style
& wxCAPTION
)
155 dlg
= wxT("wxCaptionDialog");
157 dlg
= wxT("wxNoCaptionDialog");
159 #ifdef __WXMICROWIN__
160 extern const wxChar
*wxFrameClassName
;
162 int msflags
= WS_OVERLAPPED
|WS_POPUP
;
163 if (style
& wxCAPTION
)
164 msflags
|= WS_CAPTION
;
165 if (style
& wxCLIP_CHILDREN
)
166 msflags
|= WS_CLIPCHILDREN
;
167 if ((style
& wxTHICK_FRAME
) == 0)
168 msflags
|= WS_BORDER
;
169 MSWCreate(m_windowId
, parent
, wxFrameClassName
, this, NULL
,
176 MSWCreate(m_windowId
, parent
, NULL
, this, NULL
,
178 0, // style is not used if we have dlg template
182 HWND hwnd
= (HWND
)GetHWND();
186 wxFAIL_MSG(_("Failed to create dialog. You probably forgot to include wx/msw/wx.rc in your resources."));
191 #ifndef __WXMICROWIN__
192 SubclassWin(GetHWND());
195 SetWindowText(hwnd
, title
);
200 bool wxDialog::EnableCloseButton(bool enable
)
202 #ifndef __WXMICROWIN__
203 // get system (a.k.a. window) menu
204 HMENU hmenu
= ::GetSystemMenu(GetHwnd(), FALSE
/* get it */);
207 wxLogLastError(_T("GetSystemMenu"));
212 // enabling/disabling the close item from it also automatically
213 // disables/enabling the close title bar button
214 if ( !::EnableMenuItem(hmenu
, SC_CLOSE
,
215 MF_BYCOMMAND
| (enable
? MF_ENABLED
: MF_GRAYED
)) )
217 wxLogLastError(_T("EnableMenuItem(SC_CLOSE)"));
222 // update appearance immediately
223 if ( !::DrawMenuBar(GetHwnd()) )
225 wxLogLastError(_T("DrawMenuBar"));
232 void wxDialog::SetModal(bool flag
)
236 m_windowStyle
|= wxDIALOG_MODAL
;
238 wxModelessWindows
.DeleteObject(this);
242 m_windowStyle
&= ~wxDIALOG_MODAL
;
244 wxModelessWindows
.Append(this);
248 wxDialog::~wxDialog()
250 m_isBeingDeleted
= TRUE
;
252 // this will also reenable all the other windows for a modal dialog
256 // ----------------------------------------------------------------------------
258 // ----------------------------------------------------------------------------
260 // By default, pressing escape cancels the dialog
261 void wxDialog::OnCharHook(wxKeyEvent
& event
)
265 // "Esc" works as an accelerator for the "Cancel" button, but it
266 // shouldn't close the dialog which doesn't have any cancel button
267 if ( (event
.m_keyCode
== WXK_ESCAPE
) && FindWindow(wxID_CANCEL
) )
269 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
270 cancelEvent
.SetEventObject( this );
271 GetEventHandler()->ProcessEvent(cancelEvent
);
273 // ensure that there is another message for this window so the
274 // ShowModal loop will exit and won't get stuck in GetMessage().
275 ::PostMessage(GetHwnd(), WM_NULL
, 0, 0);
281 // We didn't process this event.
285 // ----------------------------------------------------------------------------
286 // showing the dialogs
287 // ----------------------------------------------------------------------------
289 bool wxDialog::IsModal() const
291 return (GetWindowStyleFlag() & wxDIALOG_MODAL
) != 0;
294 bool wxDialog::IsModalShowing() const
296 return wxModalDialogs
.Find((wxDialog
*)this) != NULL
; // const_cast
299 void wxDialog::DoShowModal()
301 wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") );
302 wxCHECK_RET( IsModal(), _T("can't DoShowModal() modeless dialog") );
304 wxModalDialogs
.Append(this);
306 wxWindow
*parent
= GetParent();
308 wxWindow
* oldFocus
= m_oldFocus
;
310 // We have to remember the HWND because we need to check
311 // the HWND still exists (oldFocus can be garbage when the dialog
312 // exits, if it has been destroyed)
313 HWND hwndOldFocus
= 0;
315 hwndOldFocus
= (HWND
) oldFocus
->GetHWND();
317 // remember where the focus was
322 hwndOldFocus
= GetHwndOf(parent
);
325 // disable all other app windows
326 wxASSERT_MSG( !m_windowDisabler
, _T("disabling windows twice?") );
328 m_windowDisabler
= new wxWindowDisabler(this);
330 // enter the modal loop
331 while ( IsModalShowing() )
334 wxMutexGuiLeaveOrEnter();
335 #endif // wxUSE_THREADS
337 while ( !wxTheApp
->Pending() && wxTheApp
->ProcessIdle() )
340 // a message came or no more idle processing to do
341 wxTheApp
->DoMessage();
345 // Note that this code MUST NOT access the dialog object's data
346 // in case the object has been deleted (which will be the case
347 // for a modal dialog that has been destroyed before calling EndModal).
348 if ( oldFocus
&& (oldFocus
!= this) && ::IsWindow(hwndOldFocus
))
350 // This is likely to prove that the object still exists
351 if (wxFindWinFromHandle((WXHWND
) hwndOldFocus
) == oldFocus
)
352 oldFocus
->SetFocus();
356 bool wxDialog::Show(bool show
)
360 // if we had disabled other app windows, reenable them back now because
361 // if they stay disabled Windows will activate another window (one
362 // which is enabled, anyhow) and we will lose activation
363 if ( m_windowDisabler
)
365 delete m_windowDisabler
;
366 m_windowDisabler
= NULL
;
370 // ShowModal() may be called for already shown dialog
371 if ( !wxDialogBase::Show(show
) && !(show
&& IsModal()) )
379 // usually will result in TransferDataToWindow() being called
387 // modal dialog needs a parent window, so try to find one
390 wxWindow
*parent
= wxTheApp
->GetTopWindow();
391 if ( parent
&& parent
!= this && parent
->IsShown() )
396 // VZ: to make dialog behave properly we should reparent
397 // the dialog for Windows as well - unfortunately,
398 // following the docs for SetParent() results in this
399 // code which plainly doesn't work
401 long dwStyle
= ::GetWindowLong(GetHwnd(), GWL_STYLE
);
402 dwStyle
&= ~WS_POPUP
;
404 ::SetWindowLong(GetHwnd(), GWL_STYLE
, dwStyle
);
405 ::SetParent(GetHwnd(), GetHwndOf(parent
));
412 else // end of modal dialog
414 // this will cause IsModalShowing() return FALSE and our local
415 // message loop will terminate
416 wxModalDialogs
.DeleteObject(this);
423 // a special version for Show(TRUE) for modal dialogs which returns return code
424 int wxDialog::ShowModal()
433 return GetReturnCode();
436 // NB: this function (surprizingly) may be called for both modal and modeless
437 // dialogs and should work for both of them
438 void wxDialog::EndModal(int retCode
)
440 SetReturnCode(retCode
);
445 // ----------------------------------------------------------------------------
446 // wxWin event handlers
447 // ----------------------------------------------------------------------------
450 void wxDialog::OnOK(wxCommandEvent
& WXUNUSED(event
))
452 if ( Validate() && TransferDataFromWindow() )
458 void wxDialog::OnApply(wxCommandEvent
& WXUNUSED(event
))
461 TransferDataFromWindow();
463 // TODO probably need to disable the Apply button until things change again
466 void wxDialog::OnCancel(wxCommandEvent
& WXUNUSED(event
))
468 EndModal(wxID_CANCEL
);
471 void wxDialog::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
))
473 // We'll send a Cancel message by default, which may close the dialog.
474 // Check for looping if the Cancel event handler calls Close().
476 // Note that if a cancel button and handler aren't present in the dialog,
477 // nothing will happen when you close the dialog via the window manager, or
478 // via Close(). We wouldn't want to destroy the dialog by default, since
479 // the dialog may have been created on the stack. However, this does mean
480 // that calling dialog->Close() won't delete the dialog unless the handler
481 // for wxID_CANCEL does so. So use Destroy() if you want to be sure to
482 // destroy the dialog. The default OnCancel (above) simply ends a modal
483 // dialog, and hides a modeless dialog.
485 // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global
486 // lists here? don't dare to change it now, but should be done later!
487 static wxList closing
;
489 if ( closing
.Member(this) )
492 closing
.Append(this);
494 wxCommandEvent
cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED
, wxID_CANCEL
);
495 cancelEvent
.SetEventObject( this );
496 GetEventHandler()->ProcessEvent(cancelEvent
); // This may close the dialog
498 closing
.DeleteObject(this);
501 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent
& WXUNUSED(event
))
506 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
));
511 // ---------------------------------------------------------------------------
512 // dialog window proc
513 // ---------------------------------------------------------------------------
515 long wxDialog::MSWWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
518 bool processed
= FALSE
;
522 #if 0 // now that we got owner window right it doesn't seem to be needed
524 switch ( LOWORD(wParam
) )
528 if ( IsModalShowing() && GetParent() )
530 // bring the owner window to top as the standard dialog
534 GetHwndOf(GetParent()),
543 wxLogLastError(wxT("SetWindowPos(SWP_NOACTIVATE)"));
546 // fall through to process it normally as well
552 // if we can't close, tell the system that we processed the
553 // message - otherwise it would close us
554 processed
= !Close();
557 #ifndef __WXMICROWIN__
559 // we want to override the busy cursor for modal dialogs:
560 // typically, wxBeginBusyCursor() is called and then a modal dialog
561 // is shown, but the modal dialog shouldn't have hourglass cursor
562 if ( IsModalShowing() && wxIsBusy() )
564 // set our cursor for all windows (but see below)
565 wxCursor cursor
= m_cursor
;
567 cursor
= wxCURSOR_ARROW
;
569 ::SetCursor(GetHcursorOf(cursor
));
571 // in any case, stop here and don't let wxWindow process this
572 // message (it would set the busy cursor)
575 // but return FALSE to tell the child window (if the event
576 // comes from one of them and not from ourselves) that it can
577 // set its own cursor if it has one: thus, standard controls
578 // (e.g. text ctrl) still have correct cursors in a dialog
579 // invoked while wxIsBusy()
583 #endif // __WXMICROWIN__
587 rc
= wxWindow::MSWWindowProc(message
, wParam
, lParam
);
594 // Define for each class of dialog and control
595 WXHBRUSH
wxDialog::OnCtlColor(WXHDC
WXUNUSED(pDC
),
596 WXHWND
WXUNUSED(pWnd
),
597 WXUINT
WXUNUSED(nCtlColor
),
602 return (WXHBRUSH
)Ctl3dCtlColorEx(message
, wParam
, lParam
);
605 #endif // wxUSE_CTL3D