]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dialog.cpp
Make "More windows..." menu item in MDI "Window" menu work in wxMSW.
[wxWidgets.git] / src / msw / dialog.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
b6c588e1 2// Name: src/msw/dialog.cpp
2bda0e17
KB
3// Purpose: wxDialog class
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
b6c588e1
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
b6c588e1 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
fdf565fe 27#include "wx/dialog.h"
643e9cf9 28#include "wx/testing.h"
fdf565fe 29
2bda0e17 30#ifndef WX_PRECOMP
57bd4c60 31 #include "wx/msw/wrapcdlg.h"
b6c588e1
VZ
32 #include "wx/utils.h"
33 #include "wx/frame.h"
34 #include "wx/app.h"
95e92d90 35 #include "wx/button.h"
b6c588e1
VZ
36 #include "wx/settings.h"
37 #include "wx/intl.h"
38 #include "wx/log.h"
4e3e485b 39 #include "wx/toolbar.h"
2bda0e17
KB
40#endif
41
42#include "wx/msw/private.h"
ac8d0c11 43#include "wx/evtloop.h"
664e1314 44#include "wx/scopedptr.h"
2bda0e17 45
3180bc0e 46#if defined(__SMARTPHONE__) && defined(__WXWINCE__)
ef6d716b 47 #include "wx/msw/wince/resources.h"
3180bc0e 48#endif // __SMARTPHONE__ && __WXWINCE__
ef6d716b 49
b6c588e1
VZ
50// ----------------------------------------------------------------------------
51// wxWin macros
52// ----------------------------------------------------------------------------
53
6757b5e3
VZ
54// ----------------------------------------------------------------------------
55// wxDialogModalData
56// ----------------------------------------------------------------------------
57
8c7f5f03
VZ
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
6757b5e3
VZ
60class wxDialogModalData
61{
62public:
8c7f5f03 63 wxDialogModalData(wxDialog *dialog) : m_evtLoop(dialog) { }
6757b5e3 64
8c7f5f03 65 void RunLoop()
6757b5e3 66 {
6757b5e3
VZ
67 m_evtLoop.Run();
68 }
69
70 void ExitLoop()
71 {
6757b5e3
VZ
72 m_evtLoop.Exit();
73 }
74
6757b5e3 75private:
8c7f5f03 76 wxModalEventLoop m_evtLoop;
6757b5e3
VZ
77};
78
259c43f6 79wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData)
8c7f5f03 80
b6c588e1
VZ
81// ============================================================================
82// implementation
83// ============================================================================
84
85// ----------------------------------------------------------------------------
86// wxDialog construction
87// ----------------------------------------------------------------------------
88
b0a6bb75 89void wxDialog::Init()
2bda0e17 90{
044fe836 91 m_isShown = false;
6757b5e3 92 m_modalData = NULL;
ec5f0c24
JS
93#if wxUSE_TOOLBAR && defined(__POCKETPC__)
94 m_dialogToolBar = NULL;
95#endif
40636dcb 96#if wxUSE_DIALOG_SIZEGRIP
2c66581e 97 m_hGripper = 0;
40636dcb 98#endif // wxUSE_DIALOG_SIZEGRIP
2bda0e17
KB
99}
100
b3daa5a3
VZ
101bool wxDialog::Create(wxWindow *parent,
102 wxWindowID id,
462e2437
VZ
103 const wxString& title,
104 const wxPoint& pos,
105 const wxSize& size,
106 long style,
107 const wxString& name)
2bda0e17 108{
82c9f85c 109 SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG);
bd9d76cb 110
706bb5f9 111 // All dialogs should really have this style
b225f659 112 style |= wxTAB_TRAVERSAL;
2bda0e17 113
b225f659 114 if ( !wxTopLevelWindow::Create(parent, id, title, pos, size, style, name) )
044fe836 115 return false;
00233716
VZ
116
117 if ( !m_hasFont )
6f951810 118 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
e1bdd507 119
3180bc0e 120#if defined(__SMARTPHONE__) && defined(__WXWINCE__)
ef6d716b
WS
121 SetLeftMenu(wxID_OK, _("OK"));
122#endif
ec5f0c24
JS
123#if wxUSE_TOOLBAR && defined(__POCKETPC__)
124 CreateToolBar();
125#endif
ef6d716b 126
40636dcb 127#if wxUSE_DIALOG_SIZEGRIP
952f8d3c 128 if ( HasFlag(wxRESIZE_BORDER) )
eddc468e 129 {
2c66581e
VZ
130 CreateGripper();
131
eddc468e
VZ
132 Connect(wxEVT_CREATE,
133 wxWindowCreateEventHandler(wxDialog::OnWindowCreate));
134 }
40636dcb 135#endif // wxUSE_DIALOG_SIZEGRIP
eddc468e 136
044fe836 137 return true;
2bda0e17
KB
138}
139
2bda0e17
KB
140wxDialog::~wxDialog()
141{
b0a6bb75 142 // this will also reenable all the other windows for a modal dialog
044fe836 143 Show(false);
2c66581e 144
40636dcb 145#if wxUSE_DIALOG_SIZEGRIP
2c66581e 146 DestroyGripper();
40636dcb 147#endif // wxUSE_DIALOG_SIZEGRIP
2bda0e17
KB
148}
149
b6c588e1
VZ
150// ----------------------------------------------------------------------------
151// showing the dialogs
152// ----------------------------------------------------------------------------
22cf5fec 153
b6c588e1 154bool wxDialog::Show(bool show)
2bda0e17 155{
044fe836
VZ
156 if ( show == IsShown() )
157 return false;
158
6757b5e3 159 if ( !show && m_modalData )
86ad564e 160 {
6757b5e3
VZ
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
b0a6bb75 163 // if they stay disabled Windows will activate another window (one
6757b5e3
VZ
164 // which is enabled, anyhow) when we're hidden in the base class Show()
165 // and we will lose activation
166 m_modalData->ExitLoop();
86ad564e
JS
167 }
168
044fe836 169 if ( show )
b6c588e1 170 {
3aa8e4ea
JS
171 if (CanDoLayoutAdaptation())
172 DoLayoutAdaptation();
173
044fe836
VZ
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();
b6c588e1 178 }
2bda0e17 179
044fe836
VZ
180 wxDialogBase::Show(show);
181
b6c588e1
VZ
182 if ( show )
183 {
2bc44d62
VZ
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
2b5f62a0
VZ
187 //
188 // NB: normally we should call it just the first time but doing it
189 // every time is simpler than keeping a flag
2bc44d62
VZ
190 const wxSize size = GetClientSize();
191 ::SendMessage(GetHwnd(), WM_SIZE,
192 SIZE_RESTORED, MAKELPARAM(size.x, size.y));
b6c588e1 193 }
2bda0e17 194
044fe836 195 return true;
2bda0e17
KB
196}
197
f46f4c86 198// show dialog modally
a23fd0e1 199int wxDialog::ShowModal()
2bda0e17 200{
643e9cf9
VS
201 WX_TESTING_SHOW_MODAL_HOOK();
202
9a83f860 203 wxASSERT_MSG( !IsModal(), wxT("ShowModal() can't be called twice") );
f46f4c86 204
f46f4c86
VZ
205 Show();
206
207 // EndModal may have been called from InitDialog handler (called from
89424d9b 208 // inside Show()) and hidden the dialog back again
4b5e178a 209 if ( IsShown() )
5e1febfa 210 {
f46f4c86 211 // enter and run the modal loop
88a67391
VZ
212 wxDialogModalDataTiedPtr modalData(&m_modalData,
213 new wxDialogModalData(this));
214 modalData->RunLoop();
f46f4c86 215 }
5e1febfa 216
b6c588e1 217 return GetReturnCode();
2bda0e17
KB
218}
219
220void wxDialog::EndModal(int retCode)
221{
9a83f860 222 wxASSERT_MSG( IsModal(), wxT("EndModal() called for non modal dialog") );
f46f4c86 223
b6c588e1 224 SetReturnCode(retCode);
6a088435 225
12b58624
VZ
226 Hide();
227}
228
2c66581e
VZ
229// ----------------------------------------------------------------------------
230// wxDialog gripper handling
231// ----------------------------------------------------------------------------
232
40636dcb
VZ
233#if wxUSE_DIALOG_SIZEGRIP
234
2c66581e
VZ
235void wxDialog::SetWindowStyleFlag(long style)
236{
237 wxDialogBase::SetWindowStyleFlag(style);
238
952f8d3c 239 if ( HasFlag(wxRESIZE_BORDER) )
2c66581e
VZ
240 CreateGripper();
241 else
242 DestroyGripper();
243}
244
245void wxDialog::CreateGripper()
246{
952f8d3c 247 if ( !m_hGripper )
2c66581e 248 {
952f8d3c 249 // just create it here, it will be positioned and shown later
2c66581e
VZ
250 m_hGripper = (WXHWND)::CreateWindow
251 (
252 wxT("SCROLLBAR"),
253 wxT(""),
952f8d3c
VZ
254 WS_CHILD |
255 WS_CLIPSIBLINGS |
2c66581e
VZ
256 SBS_SIZEGRIP |
257 SBS_SIZEBOX |
258 SBS_SIZEBOXBOTTOMRIGHTALIGN,
259 0, 0, 0, 0,
260 GetHwnd(),
261 0,
262 wxGetInstance(),
263 NULL
264 );
2c66581e
VZ
265 }
266}
267
268void wxDialog::DestroyGripper()
269{
270 if ( m_hGripper )
271 {
eddc468e
VZ
272 // we used to have trouble with gripper appearing on top (and hence
273 // overdrawing) the other, real, dialog children -- check that this
f78c70f3
VZ
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() ||
c3b46177 278 ::GetWindow((HWND)m_hGripper, GW_HWNDNEXT) == 0,
9a83f860 279 wxT("Bug in wxWidgets: gripper should be at the bottom of Z-order") );
2c66581e
VZ
280 ::DestroyWindow((HWND) m_hGripper);
281 m_hGripper = 0;
282 }
283}
284
285void wxDialog::ShowGripper(bool show)
286{
9a83f860 287 wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
2c66581e 288
952f8d3c
VZ
289 if ( show )
290 ResizeGripper();
291
2c66581e
VZ
292 ::ShowWindow((HWND)m_hGripper, show ? SW_SHOW : SW_HIDE);
293}
294
295void wxDialog::ResizeGripper()
296{
9a83f860 297 wxASSERT_MSG( m_hGripper, wxT("shouldn't be called if we have no gripper") );
2c66581e
VZ
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
eddc468e
VZ
310void 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
40636dcb
VZ
323#endif // wxUSE_DIALOG_SIZEGRIP
324
b6c588e1
VZ
325// ----------------------------------------------------------------------------
326// wxWin event handlers
327// ----------------------------------------------------------------------------
2bda0e17 328
9ceeecb9
JS
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.
333bool wxDialog::DoOK()
334{
f55fee08
VZ
335 const int idOk = GetAffirmativeId();
336 if ( EmulateButtonClickIfPresent(idOk) )
9ceeecb9 337 return true;
9ceeecb9 338
f55fee08
VZ
339 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetAffirmativeId());
340 event.SetEventObject(this);
341
937013e0 342 return HandleWindowEvent(event);
9ceeecb9 343}
f55fee08 344#endif // __POCKETPC__
9ceeecb9 345
ec5f0c24
JS
346#if wxUSE_TOOLBAR && defined(__POCKETPC__)
347// create main toolbar by calling OnCreateToolBar()
348wxToolBar* 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
356wxToolBar *wxDialog::OnCreateToolBar(long style,
357 wxWindowID winid,
358 const wxString& name)
359{
360 return new wxToolMenuBar(this, winid,
361 wxDefaultPosition, wxDefaultSize,
362 style, name);
660296aa 363}
ec5f0c24 364#endif
9ceeecb9 365
42e69d6b 366// ---------------------------------------------------------------------------
f55fee08 367// dialog Windows messages processing
42e69d6b
VZ
368// ---------------------------------------------------------------------------
369
c140b7e7 370WXLRESULT wxDialog::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
42e69d6b 371{
c140b7e7 372 WXLRESULT rc = 0;
044fe836 373 bool processed = false;
42e69d6b
VZ
374
375 switch ( message )
376 {
0fc58b86
RR
377#ifdef __WXWINCE__
378 // react to pressing the OK button in the title
379 case WM_COMMAND:
ef6d716b
WS
380 {
381 switch ( LOWORD(wParam) )
0fc58b86 382 {
9ceeecb9 383#ifdef __POCKETPC__
ef6d716b 384 case IDOK:
9ceeecb9
JS
385 processed = DoOK();
386 if (!processed)
b554cf63 387 processed = !Close();
9ceeecb9
JS
388#endif
389#ifdef __SMARTPHONE__
ef6d716b
WS
390 case IDM_LEFT:
391 case IDM_RIGHT:
392 processed = HandleCommand( LOWORD(wParam) , 0 , NULL );
0fc58b86 393 break;
ef6d716b 394#endif // __SMARTPHONE__
0fc58b86
RR
395 }
396 break;
ef6d716b 397 }
044fe836 398#endif
42e69d6b
VZ
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;
abceee76 404
f73f67be 405 case WM_SIZE:
40636dcb 406#if wxUSE_DIALOG_SIZEGRIP
2c66581e
VZ
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);
2c66581e
VZ
417 }
418 }
40636dcb 419#endif // wxUSE_DIALOG_SIZEGRIP
2c66581e 420
d13b34d3 421 // the Windows dialogs unfortunately are not meant to be resizable
f73f67be
VZ
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);
044fe836 428 processed = true;
e441e1f4 429 if ( HasFlag(wxFULL_REPAINT_ON_RESIZE) )
f73f67be 430 {
044fe836 431 ::InvalidateRect(GetHwnd(), NULL, false /* erase bg */);
f73f67be
VZ
432 }
433 break;
434
04ef50df 435#ifndef __WXMICROWIN__
abceee76
VZ
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
bfbd6dc1 439 // is shown, but the modal dialog shouldn't have hourglass cursor
f46f4c86 440 if ( IsModal() && wxIsBusy() )
abceee76 441 {
bfbd6dc1
VZ
442 // set our cursor for all windows (but see below)
443 wxCursor cursor = m_cursor;
a1b806b9 444 if ( !cursor.IsOk() )
bfbd6dc1 445 cursor = wxCURSOR_ARROW;
abceee76 446
bfbd6dc1
VZ
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)
044fe836 451 processed = true;
bfbd6dc1 452
044fe836 453 // but return false to tell the child window (if the event
bfbd6dc1
VZ
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()
044fe836 458 rc = false;
abceee76 459 }
bfbd6dc1 460 break;
82c9f85c 461#endif // __WXMICROWIN__
42e69d6b
VZ
462 }
463
464 if ( !processed )
465 rc = wxWindow::MSWWindowProc(message, wParam, lParam);
466
467 return rc;
468}