]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/dialog.cpp
Fix duplicate wxContextMenuEvent generation in wxMSW.
[wxWidgets.git] / src / msw / dialog.cpp
... / ...
CommitLineData
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/testing.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
60class wxDialogModalData
61{
62public:
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
75private:
76 wxModalEventLoop m_evtLoop;
77};
78
79wxDEFINE_TIED_SCOPED_PTR_TYPE(wxDialogModalData)
80
81// ============================================================================
82// implementation
83// ============================================================================
84
85// ----------------------------------------------------------------------------
86// wxDialog construction
87// ----------------------------------------------------------------------------
88
89void 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
101bool 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
140wxDialog::~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
154bool 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
199int wxDialog::ShowModal()
200{
201 WX_TESTING_SHOW_MODAL_HOOK();
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
220void 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
235void wxDialog::SetWindowStyleFlag(long style)
236{
237 wxDialogBase::SetWindowStyleFlag(style);
238
239 if ( HasFlag(wxRESIZE_BORDER) )
240 CreateGripper();
241 else
242 DestroyGripper();
243}
244
245void 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
268void 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
285void 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
295void 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
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
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.
333bool 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()
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);
363}
364#endif
365
366// ---------------------------------------------------------------------------
367// dialog Windows messages processing
368// ---------------------------------------------------------------------------
369
370WXLRESULT 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}