Modal dialogs now correctly use wxEventLoop.
[wxWidgets.git] / src / os2 / dialog.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dialog.cpp
3 // Purpose: wxDialog class
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/14/99
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifndef WX_PRECOMP
16 #include "wx/dialog.h"
17 #include "wx/utils.h"
18 #include "wx/frame.h"
19 #include "wx/app.h"
20 #include "wx/settings.h"
21 #include "wx/intl.h"
22 #include "wx/log.h"
23 #endif
24
25 #include "wx/os2/private.h"
26 #include "wx/log.h"
27 #include "wx/evtloop.h"
28 #include "wx/ptr_scpd.h"
29
30 #define wxDIALOG_DEFAULT_X 300
31 #define wxDIALOG_DEFAULT_Y 300
32
33 #define wxDIALOG_DEFAULT_WIDTH 500
34 #define wxDIALOG_DEFAULT_HEIGHT 500
35
36 IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxTopLevelWindow)
37
38 BEGIN_EVENT_TABLE(wxDialog, wxDialogBase)
39 EVT_BUTTON(wxID_OK, wxDialog::OnOK)
40 EVT_BUTTON(wxID_APPLY, wxDialog::OnApply)
41 EVT_BUTTON(wxID_CANCEL, wxDialog::OnCancel)
42 EVT_CHAR_HOOK(wxDialog::OnCharHook)
43 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged)
44 EVT_CLOSE(wxDialog::OnCloseWindow)
45 END_EVENT_TABLE()
46
47 // ----------------------------------------------------------------------------
48 // wxDialogModalData
49 // ----------------------------------------------------------------------------
50
51 // this is simply a container for any data we need to implement modality which
52 // allows us to avoid changing wxDialog each time the implementation changes
53 class wxDialogModalData
54 {
55 public:
56 wxDialogModalData(wxDialog *dialog) : m_evtLoop(dialog) { }
57
58 void RunLoop()
59 {
60 m_evtLoop.Run();
61 }
62
63 void ExitLoop()
64 {
65 m_evtLoop.Exit();
66 }
67
68 private:
69 wxModalEventLoop m_evtLoop;
70 };
71
72 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData);
73
74 // ============================================================================
75 // implementation
76 // ============================================================================
77
78 // ----------------------------------------------------------------------------
79 // wxDialog construction
80 // ----------------------------------------------------------------------------
81
82 void wxDialog::Init()
83 {
84 m_pOldFocus = (wxWindow *)NULL;
85 m_isShown = FALSE;
86 m_pWindowDisabler = (wxWindowDisabler *)NULL;
87 m_modalData = NULL;
88 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
89 } // end of wxDialog::Init
90
91 bool wxDialog::Create(
92 wxWindow* pParent
93 , wxWindowID vId
94 , const wxString& rsTitle
95 , const wxPoint& rPos
96 , const wxSize& rSize
97 , long lStyle
98 , const wxString& rsName
99 )
100 {
101 long lX = rPos.x;
102 long lY = rPos.y;
103 long lWidth = rSize.x;
104 long lHeight = rSize.y;
105 const char* zDlg;
106 WXDWORD dwExtendedStyle = 0L;
107 HWND hWnd;
108
109 Init();
110 SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG);
111
112 //
113 // Save focus before doing anything which can potentially change it
114 //
115 m_pOldFocus = FindFocus();
116
117 //
118 // All dialogs should really have this style
119 //
120 lStyle |= wxTAB_TRAVERSAL;
121
122 if (!wxTopLevelWindow::Create( pParent
123 ,vId
124 ,rsTitle
125 ,rPos
126 ,rSize
127 ,lStyle
128 ,rsName
129 ))
130 return FALSE;
131 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
132
133 //
134 // Must defer setting the title until after dialog is created and sized
135 //
136 if (!rsTitle.IsNull())
137 SetTitle(rsTitle);
138 return TRUE;
139 } // end of wxDialog::Create
140
141 void wxDialog::SetModal(
142 bool bFlag
143 )
144 {
145 if (bFlag)
146 {
147 m_windowStyle |= wxDIALOG_MODAL ;
148 wxModelessWindows.DeleteObject(this);
149 }
150 else
151 {
152 m_windowStyle &= ~wxDIALOG_MODAL ;
153 wxModelessWindows.Append(this);
154 }
155 } // end of wxDialog::SetModal
156
157 wxDialog::~wxDialog()
158 {
159 m_isBeingDeleted = TRUE;
160 Show(FALSE);
161 } // end of wxDialog::~wxDialog
162
163 //
164 // By default, pressing escape cancels the dialog
165 //
166 void wxDialog::OnCharHook(
167 wxKeyEvent& rEvent
168 )
169 {
170 if (GetHWND())
171 {
172 if (rEvent.m_keyCode == WXK_ESCAPE)
173 {
174 //
175 // Behaviour changed in 2.0: we'll send a Cancel message
176 // to the dialog instead of Close.
177 //
178 wxCommandEvent vCancelEvent( wxEVT_COMMAND_BUTTON_CLICKED
179 ,wxID_CANCEL
180 );
181
182 vCancelEvent.SetEventObject( this );
183 GetEventHandler()->ProcessEvent(vCancelEvent);
184
185 //
186 // Ensure that there is another message for this window so the
187 // ShowModal loop will exit and won't get stuck in GetMessage().
188 //
189 ::WinPostMsg(GetHwnd(), WM_NULL, 0, 0);
190 return;
191 }
192 }
193 // We didn't process this event.
194 rEvent.Skip();
195 }
196
197 // ----------------------------------------------------------------------------
198 // showing the dialogs
199 // ----------------------------------------------------------------------------
200
201 bool wxDialog::IsModal() const
202 {
203 return (GetWindowStyleFlag() & wxDIALOG_MODAL) != 0;
204 } // end of wxDialog::IsModal
205
206 bool wxDialog::IsModalShowing() const
207 {
208 return m_modalData != NULL; // const_cast
209 } // end of wxDialog::IsModalShowing
210
211 void wxDialog::DoShowModal()
212 {
213 wxWindow* pParent = GetParent();
214 wxWindow* pOldFocus = m_pOldFocus;
215 HWND hWndOldFocus = 0;
216
217 wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") );
218 wxCHECK_RET( IsModal(), _T("can't DoShowModal() modeless dialog") );
219
220 if (pOldFocus)
221 hWndOldFocus = (HWND)pOldFocus->GetHWND();
222
223 //
224 // Remember where the focus was
225 //
226 if (!pOldFocus)
227 {
228 pOldFocus = pParent;
229 if (pParent)
230 hWndOldFocus = GetHwndOf(pParent);
231 }
232
233 //
234 // Disable all other app windows
235 //
236 wxASSERT_MSG(!m_pWindowDisabler, _T("disabling windows twice?"));
237
238 //
239 // Before entering the modal loop, reset the "is in OnIdle()" flag (see
240 // comment in app.cpp)
241 //
242 extern bool gbInOnIdle;
243 bool bWasInOnIdle = gbInOnIdle;
244
245 gbInOnIdle = FALSE;
246
247 // enter the modal loop
248 {
249 wxDialogModalDataTiedPtr modalData(&m_modalData,
250 new wxDialogModalData(this));
251 modalData->RunLoop();
252 }
253 gbInOnIdle = bWasInOnIdle;
254
255 //
256 // and restore focus
257 // Note that this code MUST NOT access the dialog object's data
258 // in case the object has been deleted (which will be the case
259 // for a modal dialog that has been destroyed before calling EndModal).
260 //
261 if (pOldFocus && (pOldFocus != this) && ::WinIsWindow(vHabmain, hWndOldFocus))
262 {
263 //
264 // This is likely to prove that the object still exists
265 //
266 if (wxFindWinFromHandle((WXHWND) hWndOldFocus) == pOldFocus)
267 pOldFocus->SetFocus();
268 }
269 } // end of wxDialog::DoShowModal
270
271 bool wxDialog::Show(
272 bool bShow
273 )
274 {
275 if (!bShow)
276 {
277 //
278 // If we had disabled other app windows, reenable them back now because
279 // if they stay disabled Windows will activate another window (one
280 // which is enabled, anyhow) and we will lose activation. We really don't
281 // do this in OS/2 since PM does this for us.
282 //
283 if (m_pWindowDisabler)
284 {
285 delete m_pWindowDisabler;
286 m_pWindowDisabler = NULL;
287 }
288 if ( m_modalData )
289 m_modalData->ExitLoop();
290 }
291
292 //
293 // ShowModal() may be called for already shown dialog
294 //
295 if (!wxDialogBase::Show(bShow) && !(bShow && IsModal()))
296 {
297 //
298 // Nothing to do
299 //
300 return FALSE;
301 }
302
303 if (bShow)
304 {
305 // dialogs don't get WM_SIZE message after creation unlike most (all?)
306 // other windows and so could start their life non laid out correctly
307 // if we didn't call Layout() from here
308 //
309 // NB: normally we should call it just the first time but doing it
310 // every time is simpler than keeping a flag
311 // Layout();
312
313 //
314 // Usually will result in TransferDataToWindow() being called
315 //
316 InitDialog();
317 }
318
319 if (GetTitle().c_str())
320 ::WinSetWindowText((HWND)GetHwnd(), GetTitle().c_str());
321 if (IsModal())
322 {
323 if (bShow)
324 {
325 //
326 // Modal dialog needs a parent window, so try to find one
327 //
328 if (!GetParent())
329 {
330 wxWindow* pParent = wxTheApp->GetTopWindow();
331
332 if ( pParent && pParent != this && pParent->IsShown() )
333 {
334 //
335 // Use it
336 //
337 m_parent = pParent;
338
339 }
340 }
341 DoShowModal();
342 }
343 }
344 return TRUE;
345 } // end of wxDialog::Show
346
347 //
348 // Replacement for Show(TRUE) for modal dialogs - returns return code
349 //
350 int wxDialog::ShowModal()
351 {
352 if (!IsModal())
353 {
354 SetModal(TRUE);
355 }
356 Show(TRUE);
357 return GetReturnCode();
358 } // end of wxDialog::ShowModal
359
360 void wxDialog::EndModal(
361 int nRetCode
362 )
363 {
364 SetReturnCode(nRetCode);
365 Show(FALSE);
366 } // end of wxDialog::EndModal
367
368 // ----------------------------------------------------------------------------
369 // wxWin event handlers
370 // ----------------------------------------------------------------------------
371
372 void wxDialog::OnApply(
373 wxCommandEvent& rEvent
374 )
375 {
376 if (Validate())
377 TransferDataFromWindow();
378 } // end of wxDialog::OnApply
379
380 // Standard buttons
381 void wxDialog::OnOK(
382 wxCommandEvent& rEvent
383 )
384 {
385 if ( Validate() && TransferDataFromWindow() )
386 {
387 EndModal(wxID_OK);
388 }
389 } // end of wxDialog::OnOK
390
391 void wxDialog::OnCancel(
392 wxCommandEvent& rEvent
393 )
394 {
395 EndModal(wxID_CANCEL);
396 } // end of wxDialog::OnCancel
397
398 void wxDialog::OnCloseWindow(
399 wxCloseEvent& rEvent
400 )
401 {
402 //
403 // We'll send a Cancel message by default,
404 // which may close the dialog.
405 // Check for looping if the Cancel event handler calls Close().
406 //
407 // Note that if a cancel button and handler aren't present in the dialog,
408 // nothing will happen when you close the dialog via the window manager, or
409 // via Close().
410 // We wouldn't want to destroy the dialog by default, since the dialog may have been
411 // created on the stack.
412 // However, this does mean that calling dialog->Close() won't delete the dialog
413 // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be
414 // sure to destroy the dialog.
415 // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog.
416 //
417
418 //
419 // Ugh??? This is not good but until I figure out a global list it'll have to do
420 //
421 static wxList closing;
422
423 if ( closing.Member(this) )
424 return;
425
426 closing.Append(this);
427
428 wxCommandEvent vCancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
429
430 vCancelEvent.SetEventObject( this );
431 GetEventHandler()->ProcessEvent(vCancelEvent); // This may close the dialog
432
433 closing.DeleteObject(this);
434 } // end of wxDialog::OnCloseWindow
435
436 void wxDialog::OnSysColourChanged(
437 wxSysColourChangedEvent& rEvent
438 )
439 {
440 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
441 Refresh();
442 } // end of wxDialog::OnSysColourChanged
443
444 MRESULT wxDialog::OS2WindowProc(
445 WXUINT uMessage
446 , WXWPARAM wParam
447 , WXLPARAM lParam
448 )
449 {
450 MRESULT rc = 0;
451 bool bProcessed = FALSE;
452
453 switch (uMessage)
454 {
455 case WM_CLOSE:
456 //
457 // If we can't close, tell the system that we processed the
458 // message - otherwise it would close us
459 //
460 bProcessed = !Close();
461 break;
462 }
463
464 if (!bProcessed)
465 rc = wxWindow::OS2WindowProc( uMessage
466 ,wParam
467 ,lParam
468 );
469 return rc;
470 } // end of wxDialog::OS2WindowProc
471