]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dialog.cpp
Put wxContextHelp into cshelp.h/cpp, added wxContextHelpButton
[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 and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "dialog.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/dialog.h"
33 #include "wx/utils.h"
34 #include "wx/frame.h"
35 #include "wx/app.h"
36 #include "wx/settings.h"
37 #include "wx/intl.h"
38 #include "wx/log.h"
39 #endif
40
41 #include "wx/msw/private.h"
42 #include "wx/log.h"
43
44 #if wxUSE_COMMON_DIALOGS
45 #include <commdlg.h>
46 #endif
47
48 // ----------------------------------------------------------------------------
49 // constants
50 // ----------------------------------------------------------------------------
51
52 // default dialog pos and size
53
54 #define wxDIALOG_DEFAULT_X 300
55 #define wxDIALOG_DEFAULT_Y 300
56
57 #define wxDIALOG_DEFAULT_WIDTH 500
58 #define wxDIALOG_DEFAULT_HEIGHT 500
59
60 // ----------------------------------------------------------------------------
61 // globals
62 // ----------------------------------------------------------------------------
63
64 // all objects to be deleted during next idle processing - from window.cpp
65 extern wxList WXDLLEXPORT wxPendingDelete;
66
67 // all frames and modeless dialogs - not static, used in frame.cpp, mdi.cpp &c
68 wxWindowList wxModelessWindows;
69
70 // all modal dialogs currently shown
71 static wxWindowList wxModalDialogs;
72
73 // ----------------------------------------------------------------------------
74 // wxWin macros
75 // ----------------------------------------------------------------------------
76
77 IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxPanel)
78
79 BEGIN_EVENT_TABLE(wxDialog, wxPanel)
80 EVT_BUTTON(wxID_OK, wxDialog::OnOK)
81 EVT_BUTTON(wxID_APPLY, wxDialog::OnApply)
82 EVT_BUTTON(wxID_CANCEL, wxDialog::OnCancel)
83
84 EVT_CHAR_HOOK(wxDialog::OnCharHook)
85
86 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged)
87
88 EVT_CLOSE(wxDialog::OnCloseWindow)
89 END_EVENT_TABLE()
90
91 // ============================================================================
92 // implementation
93 // ============================================================================
94
95 // ----------------------------------------------------------------------------
96 // wxDialog construction
97 // ----------------------------------------------------------------------------
98
99 wxDialog::wxDialog()
100 {
101 m_oldFocus = (wxWindow *)NULL;
102 m_isShown = FALSE;
103
104 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
105 }
106
107 bool wxDialog::Create(wxWindow *parent, wxWindowID id,
108 const wxString& title,
109 const wxPoint& pos,
110 const wxSize& size,
111 long style,
112 const wxString& name)
113 {
114 m_oldFocus = FindFocus();
115
116 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
117 SetName(name);
118
119 wxTopLevelWindows.Append(this);
120
121 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
122
123 if (parent) parent->AddChild(this);
124
125 if ( id == -1 )
126 m_windowId = (int)NewControlId();
127 else
128 m_windowId = id;
129
130 int x = pos.x;
131 int y = pos.y;
132 int width = size.x;
133 int height = size.y;
134
135 if (x < 0)
136 x = wxDIALOG_DEFAULT_X;
137 if (y < 0)
138 y = wxDIALOG_DEFAULT_Y;
139
140 m_windowStyle = style;
141
142 m_isShown = FALSE;
143
144 if (width < 0)
145 width = wxDIALOG_DEFAULT_WIDTH;
146 if (height < 0)
147 height = wxDIALOG_DEFAULT_HEIGHT;
148
149 // All dialogs should really have this style
150 m_windowStyle |= wxTAB_TRAVERSAL;
151
152 WXDWORD extendedStyle = MakeExtendedStyle(m_windowStyle);
153 if (m_windowStyle & wxSTAY_ON_TOP)
154 extendedStyle |= WS_EX_TOPMOST;
155 if (m_exStyle & wxFRAME_EX_CONTEXTHELP)
156 extendedStyle |= WS_EX_CONTEXTHELP;
157
158 // Allows creation of dialogs with & without captions under MSWindows,
159 // resizeable or not (but a resizeable dialog always has caption -
160 // otherwise it would look too strange)
161 const wxChar *dlg;
162 if ( style & wxRESIZE_BORDER )
163 dlg = wxT("wxResizeableDialog");
164 else if ( style & wxCAPTION )
165 dlg = wxT("wxCaptionDialog");
166 else
167 dlg = wxT("wxNoCaptionDialog");
168 MSWCreate(m_windowId, parent, NULL, this, NULL,
169 x, y, width, height,
170 0, // style is not used if we have dlg template
171 dlg,
172 extendedStyle);
173
174 HWND hwnd = (HWND)GetHWND();
175
176 if ( !hwnd )
177 {
178 wxFAIL_MSG(_("Failed to create dialog. You probably forgot to include wx/msw/wx.rc in your resources."));
179
180 return FALSE;
181 }
182
183 SubclassWin(GetHWND());
184
185 SetWindowText(hwnd, title);
186 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
187
188 return TRUE;
189 }
190
191 void wxDialog::SetModal(bool flag)
192 {
193 if ( flag )
194 {
195 m_windowStyle |= wxDIALOG_MODAL;
196
197 wxModelessWindows.DeleteObject(this);
198 }
199 else
200 {
201 m_windowStyle &= ~wxDIALOG_MODAL;
202
203 wxModelessWindows.Append(this);
204 }
205 }
206
207 wxDialog::~wxDialog()
208 {
209 m_isBeingDeleted = TRUE;
210
211 wxTopLevelWindows.DeleteObject(this);
212
213 // this will call BringWindowToTop() if necessary to bring back our parent
214 // window to top
215 Show(FALSE);
216
217 if ( !IsModal() )
218 wxModelessWindows.DeleteObject(this);
219
220 // If this is the last top-level window, exit.
221 if ( wxTheApp && (wxTopLevelWindows.Number() == 0) )
222 {
223 wxTheApp->SetTopWindow(NULL);
224
225 if ( wxTheApp->GetExitOnFrameDelete() )
226 {
227 ::PostQuitMessage(0);
228 }
229 }
230 }
231
232 // ----------------------------------------------------------------------------
233 // kbd handling
234 // ----------------------------------------------------------------------------
235
236 // By default, pressing escape cancels the dialog
237 void wxDialog::OnCharHook(wxKeyEvent& event)
238 {
239 if (GetHWND())
240 {
241 // "Esc" works as an accelerator for the "Cancel" button, but it
242 // shouldn't close the dialog which doesn't have any cancel button
243 if ( (event.m_keyCode == WXK_ESCAPE) && FindWindow(wxID_CANCEL) )
244 {
245 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
246 cancelEvent.SetEventObject( this );
247 GetEventHandler()->ProcessEvent(cancelEvent);
248
249 // ensure that there is another message for this window so the
250 // ShowModal loop will exit and won't get stuck in GetMessage().
251 ::PostMessage(GetHwnd(), WM_NULL, 0, 0);
252
253 return;
254 }
255 }
256
257 // We didn't process this event.
258 event.Skip();
259 }
260
261 // ----------------------------------------------------------------------------
262 // Windows dialog boxes can't be iconized
263 // ----------------------------------------------------------------------------
264
265 void wxDialog::Iconize(bool WXUNUSED(iconize))
266 {
267 }
268
269 bool wxDialog::IsIconized() const
270 {
271 return FALSE;
272 }
273
274 // ----------------------------------------------------------------------------
275 // size/position handling
276 // ----------------------------------------------------------------------------
277
278 void wxDialog::DoSetClientSize(int width, int height)
279 {
280 HWND hWnd = (HWND) GetHWND();
281 RECT rect;
282 ::GetClientRect(hWnd, &rect);
283
284 RECT rect2;
285 GetWindowRect(hWnd, &rect2);
286
287 // Find the difference between the entire window (title bar and all)
288 // and the client area; add this to the new client size to move the
289 // window
290 int actual_width = rect2.right - rect2.left - rect.right + width;
291 int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
292
293 MoveWindow(hWnd, rect2.left, rect2.top, actual_width, actual_height, TRUE);
294
295 wxSizeEvent event(wxSize(actual_width, actual_height), m_windowId);
296 event.SetEventObject( this );
297 GetEventHandler()->ProcessEvent(event);
298 }
299
300 void wxDialog::DoGetPosition(int *x, int *y) const
301 {
302 RECT rect;
303 GetWindowRect(GetHwnd(), &rect);
304
305 if ( x )
306 *x = rect.left;
307 if ( y )
308 *y = rect.top;
309 }
310
311 // ----------------------------------------------------------------------------
312 // showing the dialogs
313 // ----------------------------------------------------------------------------
314
315 bool wxDialog::IsModal() const
316 {
317 return (GetWindowStyleFlag() & wxDIALOG_MODAL) != 0;
318 }
319
320 bool wxDialog::IsModalShowing() const
321 {
322 return wxModalDialogs.Find((wxDialog *)this) != NULL; // const_cast
323 }
324
325 void wxDialog::DoShowModal()
326 {
327 wxCHECK_RET( !IsModalShowing(), _T("DoShowModal() called twice") );
328 wxCHECK_RET( IsModal(), _T("can't DoShowModal() modeless dialog") );
329
330 wxModalDialogs.Append(this);
331
332 wxWindow *parent = GetParent();
333
334 wxWindow* oldFocus = m_oldFocus;
335
336 // We have to remember the HWND because we need to check
337 // the HWND still exists (oldFocus can be garbage when the dialog
338 // exits, if it has been destroyed)
339 HWND hwndOldFocus = 0;
340 if (oldFocus)
341 hwndOldFocus = (HWND) oldFocus->GetHWND();
342
343 // inside this block, all app windows are disabled
344 {
345 wxWindowDisabler wd(this);
346
347 // remember where the focus was
348 if ( !oldFocus )
349 {
350 oldFocus = parent;
351 if (parent)
352 hwndOldFocus = (HWND) parent->GetHWND();
353 }
354
355 // enter the modal loop
356 while ( IsModalShowing() )
357 {
358 #if wxUSE_THREADS
359 wxMutexGuiLeaveOrEnter();
360 #endif // wxUSE_THREADS
361
362 while ( !wxTheApp->Pending() && wxTheApp->ProcessIdle() )
363 ;
364
365 // a message came or no more idle processing to do
366 wxTheApp->DoMessage();
367 }
368 }
369
370 #ifdef __WIN32__
371 if ( parent )
372 ::SetActiveWindow(GetHwndOf(parent));
373 #endif // __WIN32__
374
375 // and restore focus
376 // Note that this code MUST NOT access the dialog object's data
377 // in case the object has been deleted (which will be the case
378 // for a modal dialog that has been destroyed before calling EndModal).
379 if ( oldFocus && (oldFocus != this) && ::IsWindow(hwndOldFocus))
380 {
381 // This is likely to prove that the object still exists
382 if (wxFindWinFromHandle((WXHWND) hwndOldFocus) == oldFocus)
383 oldFocus->SetFocus();
384 }
385 }
386
387 bool wxDialog::Show(bool show)
388 {
389 // The following is required when the parent has been disabled, (modal
390 // dialogs, or modeless dialogs with disabling such as wxProgressDialog).
391 // Otherwise the parent disappears behind other windows when the dialog is
392 // hidden.
393 if ( !show )
394 {
395 wxWindow *parent = GetParent();
396 if ( parent )
397 {
398 ::BringWindowToTop(GetHwndOf(parent));
399 }
400 }
401
402 // ShowModal() may be called for already shown dialog
403 if ( !wxDialogBase::Show(show) && !(show && IsModal()) )
404 {
405 // nothing to do
406 return FALSE;
407 }
408
409 if ( show )
410 {
411 // usually will result in TransferDataToWindow() being called
412 InitDialog();
413 }
414
415 if ( IsModal() )
416 {
417 if ( show )
418 {
419 // modal dialog needs a parent window, so try to find one
420 if ( !GetParent() )
421 {
422 wxWindow *parent = wxTheApp->GetTopWindow();
423 if ( parent && parent != this && parent->IsShown() )
424 {
425 // use it
426 m_parent = parent;
427 }
428 }
429
430 DoShowModal();
431 }
432 else // end of modal dialog
433 {
434 // this will cause IsModalShowing() return FALSE and our local
435 // message loop will terminate
436 wxModalDialogs.DeleteObject(this);
437 }
438 }
439
440 return TRUE;
441 }
442
443 // a special version for Show(TRUE) for modal dialogs which returns return code
444 int wxDialog::ShowModal()
445 {
446 if ( !IsModal() )
447 {
448 SetModal(TRUE);
449 }
450
451 Show(TRUE);
452
453 return GetReturnCode();
454 }
455
456 // NB: this function (surprizingly) may be called for both modal and modeless
457 // dialogs and should work for both of them
458 void wxDialog::EndModal(int retCode)
459 {
460 SetReturnCode(retCode);
461
462 Show(FALSE);
463 }
464
465 // ----------------------------------------------------------------------------
466 // wxWin event handlers
467 // ----------------------------------------------------------------------------
468
469 // Standard buttons
470 void wxDialog::OnOK(wxCommandEvent& event)
471 {
472 if ( Validate() && TransferDataFromWindow() )
473 {
474 EndModal(wxID_OK);
475 }
476 }
477
478 void wxDialog::OnApply(wxCommandEvent& event)
479 {
480 if ( Validate() )
481 TransferDataFromWindow();
482
483 // TODO probably need to disable the Apply button until things change again
484 }
485
486 void wxDialog::OnCancel(wxCommandEvent& event)
487 {
488 EndModal(wxID_CANCEL);
489 }
490
491 void wxDialog::OnCloseWindow(wxCloseEvent& event)
492 {
493 // We'll send a Cancel message by default, which may close the dialog.
494 // Check for looping if the Cancel event handler calls Close().
495
496 // Note that if a cancel button and handler aren't present in the dialog,
497 // nothing will happen when you close the dialog via the window manager, or
498 // via Close(). We wouldn't want to destroy the dialog by default, since
499 // the dialog may have been created on the stack. However, this does mean
500 // that calling dialog->Close() won't delete the dialog unless the handler
501 // for wxID_CANCEL does so. So use Destroy() if you want to be sure to
502 // destroy the dialog. The default OnCancel (above) simply ends a modal
503 // dialog, and hides a modeless dialog.
504
505 // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global
506 // lists here? don't dare to change it now, but should be done later!
507 static wxList closing;
508
509 if ( closing.Member(this) )
510 return;
511
512 closing.Append(this);
513
514 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
515 cancelEvent.SetEventObject( this );
516 GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog
517
518 closing.DeleteObject(this);
519 }
520
521 // Destroy the window (delayed, if a managed window)
522 bool wxDialog::Destroy()
523 {
524 wxCHECK_MSG( !wxPendingDelete.Member(this), FALSE,
525 _T("wxDialog destroyed twice") );
526
527 wxPendingDelete.Append(this);
528
529 return TRUE;
530 }
531
532 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent& event)
533 {
534 #if wxUSE_CTL3D
535 Ctl3dColorChange();
536 #else
537 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
538 Refresh();
539 #endif
540 }
541
542 // ---------------------------------------------------------------------------
543 // dialog window proc
544 // ---------------------------------------------------------------------------
545
546 long wxDialog::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
547 {
548 long rc = 0;
549 bool processed = FALSE;
550
551 switch ( message )
552 {
553 case WM_ACTIVATE:
554 switch ( LOWORD(wParam) )
555 {
556 case WA_ACTIVE:
557 case WA_CLICKACTIVE:
558 if ( IsModalShowing() && GetParent() )
559 {
560 // bring the owner window to top as the standard dialog
561 // boxes do
562 if ( !::SetWindowPos
563 (
564 GetHwndOf(GetParent()),
565 GetHwnd(),
566 0, 0,
567 0, 0,
568 SWP_NOACTIVATE |
569 SWP_NOMOVE |
570 SWP_NOSIZE
571 ) )
572 {
573 wxLogLastError(wxT("SetWindowPos(SWP_NOACTIVATE)"));
574 }
575 }
576 // fall through to process it normally as well
577 }
578 break;
579
580 case WM_CLOSE:
581 // if we can't close, tell the system that we processed the
582 // message - otherwise it would close us
583 processed = !Close();
584 break;
585
586 case WM_SETCURSOR:
587 // we want to override the busy cursor for modal dialogs:
588 // typically, wxBeginBusyCursor() is called and then a modal dialog
589 // is shown, but the modal dialog shouldn't have hourglass cursor
590 if ( IsModalShowing() && wxIsBusy() )
591 {
592 // set our cursor for all windows (but see below)
593 wxCursor cursor = m_cursor;
594 if ( !cursor.Ok() )
595 cursor = wxCURSOR_ARROW;
596
597 ::SetCursor(GetHcursorOf(cursor));
598
599 // in any case, stop here and don't let wxWindow process this
600 // message (it would set the busy cursor)
601 processed = TRUE;
602
603 // but return FALSE to tell the child window (if the event
604 // comes from one of them and not from ourselves) that it can
605 // set its own cursor if it has one: thus, standard controls
606 // (e.g. text ctrl) still have correct cursors in a dialog
607 // invoked while wxIsBusy()
608 rc = FALSE;
609 }
610 break;
611 }
612
613 if ( !processed )
614 rc = wxWindow::MSWWindowProc(message, wParam, lParam);
615
616 return rc;
617 }
618
619 #if wxUSE_CTL3D
620
621 // Define for each class of dialog and control
622 WXHBRUSH wxDialog::OnCtlColor(WXHDC WXUNUSED(pDC),
623 WXHWND WXUNUSED(pWnd),
624 WXUINT WXUNUSED(nCtlColor),
625 WXUINT message,
626 WXWPARAM wParam,
627 WXLPARAM lParam)
628 {
629 return (WXHBRUSH)Ctl3dCtlColorEx(message, wParam, lParam);
630 }
631
632 #endif // wxUSE_CTL3D
633