]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dialog.cpp
Fix tab navigation bug with static boxes without enabled children.
[wxWidgets.git] / src / msw / dialog.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dialog.cpp
3 // Purpose: wxDialog class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/dialog.h"
28 #include "wx/modalhook.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/msw/wrapcdlg.h"
32 #include "wx/utils.h"
33 #include "wx/frame.h"
34 #include "wx/app.h"
35 #include "wx/button.h"
36 #include "wx/settings.h"
37 #include "wx/intl.h"
38 #include "wx/log.h"
39 #include "wx/toolbar.h"
40 #endif
41
42 #include "wx/msw/private.h"
43 #include "wx/evtloop.h"
44 #include "wx/scopedptr.h"
45
46 #if defined(__SMARTPHONE__) && defined(__WXWINCE__)
47 #include "wx/msw/wince/resources.h"
48 #endif // __SMARTPHONE__ && __WXWINCE__
49
50 // ----------------------------------------------------------------------------
51 // wxWin macros
52 // ----------------------------------------------------------------------------
53
54 // ----------------------------------------------------------------------------
55 // wxDialogModalData
56 // ----------------------------------------------------------------------------
57
58 // this is simply a container for any data we need to implement modality which
59 // allows us to avoid changing wxDialog each time the implementation changes
60 class wxDialogModalData
61 {
62 public:
63 wxDialogModalData(wxDialog *dialog) : m_evtLoop(dialog) { }
64
65 void RunLoop()
66 {
67 m_evtLoop.Run();
68 }
69
70 void ExitLoop()
71 {
72 m_evtLoop.Exit();
73 }
74
75 private:
76 wxModalEventLoop m_evtLoop;
77 };
78
79 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData)
80
81 // ============================================================================
82 // implementation
83 // ============================================================================
84
85 // ----------------------------------------------------------------------------
86 // wxDialog construction
87 // ----------------------------------------------------------------------------
88
89 void wxDialog::Init()
90 {
91 m_isShown = false;
92 m_modalData = NULL;
93 #if wxUSE_TOOLBAR && defined(__POCKETPC__)
94 m_dialogToolBar = NULL;
95 #endif
96 #if wxUSE_DIALOG_SIZEGRIP
97 m_hGripper = 0;
98 #endif // wxUSE_DIALOG_SIZEGRIP
99 }
100
101 bool wxDialog::Create(wxWindow *parent,
102 wxWindowID id,
103 const wxString& title,
104 const wxPoint& pos,
105 const wxSize& size,
106 long style,
107 const wxString& name)
108 {
109 SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG);
110
111 // All dialogs should really have this style
112 style |= wxTAB_TRAVERSAL;
113
114 if ( !wxTopLevelWindow::Create(parent, id, title, pos, size, style, name) )
115 return false;
116
117 if ( !m_hasFont )
118 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
119
120 #if defined(__SMARTPHONE__) && defined(__WXWINCE__)
121 SetLeftMenu(wxID_OK, _("OK"));
122 #endif
123 #if wxUSE_TOOLBAR && defined(__POCKETPC__)
124 CreateToolBar();
125 #endif
126
127 #if wxUSE_DIALOG_SIZEGRIP
128 if ( HasFlag(wxRESIZE_BORDER) )
129 {
130 CreateGripper();
131
132 Connect(wxEVT_CREATE,
133 wxWindowCreateEventHandler(wxDialog::OnWindowCreate));
134 }
135 #endif // wxUSE_DIALOG_SIZEGRIP
136
137 return true;
138 }
139
140 wxDialog::~wxDialog()
141 {
142 // this will also reenable all the other windows for a modal dialog
143 Show(false);
144
145 #if wxUSE_DIALOG_SIZEGRIP
146 DestroyGripper();
147 #endif // wxUSE_DIALOG_SIZEGRIP
148 }
149
150 // ----------------------------------------------------------------------------
151 // showing the dialogs
152 // ----------------------------------------------------------------------------
153
154 bool wxDialog::Show(bool show)
155 {
156 if ( show == IsShown() )
157 return false;
158
159 if ( !show && m_modalData )
160 {
161 // we need to do this before calling wxDialogBase version because if we
162 // had disabled other app windows, they must be reenabled right now as
163 // if they stay disabled Windows will activate another window (one
164 // which is enabled, anyhow) when we're hidden in the base class Show()
165 // and we will lose activation
166 m_modalData->ExitLoop();
167 }
168
169 if ( show )
170 {
171 if (CanDoLayoutAdaptation())
172 DoLayoutAdaptation();
173
174 // this usually will result in TransferDataToWindow() being called
175 // which will change the controls values so do it before showing as
176 // otherwise we could have some flicker
177 InitDialog();
178 }
179
180 wxDialogBase::Show(show);
181
182 if ( show )
183 {
184 // dialogs don't get WM_SIZE message from ::ShowWindow() for some
185 // reason so generate it ourselves for consistency with frames and
186 // dialogs in other ports
187 //
188 // NB: normally we should call it just the first time but doing it
189 // every time is simpler than keeping a flag
190 const wxSize size = GetClientSize();
191 ::SendMessage(GetHwnd(), WM_SIZE,
192 SIZE_RESTORED, MAKELPARAM(size.x, size.y));
193 }
194
195 return true;
196 }
197
198 // show dialog modally
199 int wxDialog::ShowModal()
200 {
201 WX_HOOK_MODAL_DIALOG();
202
203 wxASSERT_MSG( !IsModal(), wxT("ShowModal() can't be called twice") );
204
205 Show();
206
207 // EndModal may have been called from InitDialog handler (called from
208 // inside Show()) and hidden the dialog back again
209 if ( IsShown() )
210 {
211 // enter and run the modal loop
212 wxDialogModalDataTiedPtr modalData(&m_modalData,
213 new wxDialogModalData(this));
214 modalData->RunLoop();
215 }
216
217 return GetReturnCode();
218 }
219
220 void wxDialog::EndModal(int retCode)
221 {
222 wxASSERT_MSG( IsModal(), wxT("EndModal() called for non modal dialog") );
223
224 SetReturnCode(retCode);
225
226 Hide();
227 }
228
229 // ----------------------------------------------------------------------------
230 // wxDialog gripper handling
231 // ----------------------------------------------------------------------------
232
233 #if wxUSE_DIALOG_SIZEGRIP
234
235 void wxDialog::SetWindowStyleFlag(long style)
236 {
237 wxDialogBase::SetWindowStyleFlag(style);
238
239 if ( HasFlag(wxRESIZE_BORDER) )
240 CreateGripper();
241 else
242 DestroyGripper();
243 }
244
245 void wxDialog::CreateGripper()
246 {
247 if ( !m_hGripper )
248 {
249 // just create it here, it will be positioned and shown later
250 m_hGripper = (WXHWND)::CreateWindow
251 (
252 wxT("SCROLLBAR"),
253 wxT(""),
254 WS_CHILD |
255 WS_CLIPSIBLINGS |
256 SBS_SIZEGRIP |
257 SBS_SIZEBOX |
258 SBS_SIZEBOXBOTTOMRIGHTALIGN,
259 0, 0, 0, 0,
260 GetHwnd(),
261 0,
262 wxGetInstance(),
263 NULL
264 );
265 }
266 }
267
268 void wxDialog::DestroyGripper()
269 {
270 if ( m_hGripper )
271 {
272 // we used to have trouble with gripper appearing on top (and hence
273 // overdrawing) the other, real, dialog children -- check that this
274 // isn't the case automatically (but notice that this could be false if
275 // we're not shown at all as in this case ResizeGripper() might not
276 // have been called yet)
277 wxASSERT_MSG( !IsShown() ||
278 ::GetWindow((HWND)m_hGripper, GW_HWNDNEXT) == 0,
279 wxT("Bug in wxWidgets: gripper should be at the bottom of Z-order") );
280 ::DestroyWindow((HWND) m_hGripper);
281 m_hGripper = 0;
282 }
283 }
284
285 void wxDialog::ShowGripper(bool show)
286 {
287 wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
288
289 if ( show )
290 ResizeGripper();
291
292 ::ShowWindow((HWND)m_hGripper, show ? SW_SHOW : SW_HIDE);
293 }
294
295 void wxDialog::ResizeGripper()
296 {
297 wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
298
299 HWND hwndGripper = (HWND)m_hGripper;
300
301 const wxRect rectGripper = wxRectFromRECT(wxGetWindowRect(hwndGripper));
302 const wxSize size = GetClientSize() - rectGripper.GetSize();
303
304 ::SetWindowPos(hwndGripper, HWND_BOTTOM,
305 size.x, size.y,
306 rectGripper.width, rectGripper.height,
307 SWP_NOACTIVATE);
308 }
309
310 void wxDialog::OnWindowCreate(wxWindowCreateEvent& event)
311 {
312 if ( m_hGripper && IsShown() &&
313 event.GetWindow() && event.GetWindow()->GetParent() == this )
314 {
315 // Put gripper below the newly created child window
316 ::SetWindowPos((HWND)m_hGripper, HWND_BOTTOM, 0, 0, 0, 0,
317 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
318 }
319
320 event.Skip();
321 }
322
323 #endif // wxUSE_DIALOG_SIZEGRIP
324
325 // ----------------------------------------------------------------------------
326 // wxWin event handlers
327 // ----------------------------------------------------------------------------
328
329 #ifdef __POCKETPC__
330 // Responds to the OK button in a PocketPC titlebar. This
331 // can be overridden, or you can change the id used for
332 // sending the event, by calling SetAffirmativeId.
333 bool wxDialog::DoOK()
334 {
335 const int idOk = GetAffirmativeId();
336 if ( EmulateButtonClickIfPresent(idOk) )
337 return true;
338
339 wxCommandEvent event(wxEVT_BUTTON, GetAffirmativeId());
340 event.SetEventObject(this);
341
342 return HandleWindowEvent(event);
343 }
344 #endif // __POCKETPC__
345
346 #if wxUSE_TOOLBAR && defined(__POCKETPC__)
347 // create main toolbar by calling OnCreateToolBar()
348 wxToolBar* wxDialog::CreateToolBar(long style, wxWindowID winid, const wxString& name)
349 {
350 m_dialogToolBar = OnCreateToolBar(style, winid, name);
351
352 return m_dialogToolBar;
353 }
354
355 // return a new toolbar
356 wxToolBar *wxDialog::OnCreateToolBar(long style,
357 wxWindowID winid,
358 const wxString& name)
359 {
360 return new wxToolMenuBar(this, winid,
361 wxDefaultPosition, wxDefaultSize,
362 style, name);
363 }
364 #endif
365
366 // ---------------------------------------------------------------------------
367 // dialog Windows messages processing
368 // ---------------------------------------------------------------------------
369
370 WXLRESULT wxDialog::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
371 {
372 WXLRESULT rc = 0;
373 bool processed = false;
374
375 switch ( message )
376 {
377 #ifdef __WXWINCE__
378 // react to pressing the OK button in the title
379 case WM_COMMAND:
380 {
381 switch ( LOWORD(wParam) )
382 {
383 #ifdef __POCKETPC__
384 case IDOK:
385 processed = DoOK();
386 if (!processed)
387 processed = !Close();
388 #endif
389 #ifdef __SMARTPHONE__
390 case IDM_LEFT:
391 case IDM_RIGHT:
392 processed = HandleCommand( LOWORD(wParam) , 0 , NULL );
393 break;
394 #endif // __SMARTPHONE__
395 }
396 break;
397 }
398 #endif
399 case WM_CLOSE:
400 // if we can't close, tell the system that we processed the
401 // message - otherwise it would close us
402 processed = !Close();
403 break;
404
405 case WM_SIZE:
406 #if wxUSE_DIALOG_SIZEGRIP
407 if ( m_hGripper )
408 {
409 switch ( wParam )
410 {
411 case SIZE_MAXIMIZED:
412 ShowGripper(false);
413 break;
414
415 case SIZE_RESTORED:
416 ShowGripper(true);
417 }
418 }
419 #endif // wxUSE_DIALOG_SIZEGRIP
420
421 // the Windows dialogs unfortunately are not meant to be resizable
422 // at all and their standard class doesn't include CS_[VH]REDRAW
423 // styles which means that the window is not refreshed properly
424 // after the resize and no amount of WS_CLIPCHILDREN/SIBLINGS can
425 // help with it - so we have to refresh it manually which certainly
426 // creates flicker but at least doesn't show garbage on the screen
427 rc = wxWindow::MSWWindowProc(message, wParam, lParam);
428 processed = true;
429 if ( HasFlag(wxFULL_REPAINT_ON_RESIZE) )
430 {
431 ::InvalidateRect(GetHwnd(), NULL, false /* erase bg */);
432 }
433 break;
434
435 #ifndef __WXMICROWIN__
436 case WM_SETCURSOR:
437 // we want to override the busy cursor for modal dialogs:
438 // typically, wxBeginBusyCursor() is called and then a modal dialog
439 // is shown, but the modal dialog shouldn't have hourglass cursor
440 if ( IsModal() && wxIsBusy() )
441 {
442 // set our cursor for all windows (but see below)
443 wxCursor cursor = m_cursor;
444 if ( !cursor.IsOk() )
445 cursor = wxCURSOR_ARROW;
446
447 ::SetCursor(GetHcursorOf(cursor));
448
449 // in any case, stop here and don't let wxWindow process this
450 // message (it would set the busy cursor)
451 processed = true;
452
453 // but return false to tell the child window (if the event
454 // comes from one of them and not from ourselves) that it can
455 // set its own cursor if it has one: thus, standard controls
456 // (e.g. text ctrl) still have correct cursors in a dialog
457 // invoked while wxIsBusy()
458 rc = false;
459 }
460 break;
461 #endif // __WXMICROWIN__
462 }
463
464 if ( !processed )
465 rc = wxDialogBase::MSWWindowProc(message, wParam, lParam);
466
467 return rc;
468 }