]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dialog.cpp
argc == 0 bug fixed
[wxWidgets.git] / src / msw / dialog.cpp
CommitLineData
2bda0e17
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: 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
dc1c4b62 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13#pragma implementation "dialog.h"
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20#pragma hdrstop
21#endif
22
23#ifndef WX_PRECOMP
24#include "wx/dialog.h"
25#include "wx/utils.h"
26#include "wx/frame.h"
27#include "wx/app.h"
28#include "wx/settings.h"
29#endif
30
31#include "wx/msw/private.h"
dbda9e86 32#include "wx/log.h"
2bda0e17 33
47d67540 34#if wxUSE_COMMON_DIALOGS
2bda0e17
KB
35#include <commdlg.h>
36#endif
37
38#define wxDIALOG_DEFAULT_X 300
39#define wxDIALOG_DEFAULT_Y 300
40
41// Lists to keep track of windows, so we can disable/enable them
42// for modal dialogs
43wxList wxModalDialogs;
44wxList wxModelessWindows; // Frames and modeless dialogs
cde9f08e 45extern wxList WXDLLEXPORT wxPendingDelete;
2bda0e17
KB
46
47#if !USE_SHARED_LIBRARY
462e2437
VZ
48 IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxPanel)
49
50 BEGIN_EVENT_TABLE(wxDialog, wxPanel)
51 EVT_SIZE(wxDialog::OnSize)
52 EVT_BUTTON(wxID_OK, wxDialog::OnOK)
53 EVT_BUTTON(wxID_APPLY, wxDialog::OnApply)
54 EVT_BUTTON(wxID_CANCEL, wxDialog::OnCancel)
55 EVT_CHAR_HOOK(wxDialog::OnCharHook)
56 EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged)
57 EVT_CLOSE(wxDialog::OnCloseWindow)
58 END_EVENT_TABLE()
2bda0e17
KB
59#endif
60
2bda0e17
KB
61bool wxDialog::MSWOnClose(void)
62{
63 return Close();
64}
65
66wxDialog::wxDialog(void)
67{
68 m_isShown = FALSE;
69 m_modalShowing = FALSE;
70
71 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
2bda0e17
KB
72}
73
debe6624 74bool wxDialog::Create(wxWindow *parent, wxWindowID id,
462e2437
VZ
75 const wxString& title,
76 const wxPoint& pos,
77 const wxSize& size,
78 long style,
79 const wxString& name)
2bda0e17 80{
462e2437
VZ
81 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
82 SetName(name);
83
84 if (!parent)
85 wxTopLevelWindows.Append(this);
2bda0e17 86
462e2437 87 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
2bda0e17 88
462e2437 89 if (parent) parent->AddChild(this);
2bda0e17 90
462e2437
VZ
91 if ( id == -1 )
92 m_windowId = (int)NewControlId();
93 else
94 m_windowId = id;
95
96 int x = pos.x;
97 int y = pos.y;
98 int width = size.x;
99 int height = size.y;
100
101 if (x < 0) x = wxDIALOG_DEFAULT_X;
102 if (y < 0) y = wxDIALOG_DEFAULT_Y;
103
104 m_windowStyle = style;
105
106 m_isShown = FALSE;
107 m_modalShowing = FALSE;
108
109 if (width < 0)
110 width = 500;
111 if (height < 0)
112 height = 500;
113
114 WXDWORD extendedStyle = MakeExtendedStyle(m_windowStyle);
115 if (m_windowStyle & wxSTAY_ON_TOP)
116 extendedStyle |= WS_EX_TOPMOST;
117
118 // Allows creation of dialogs with & without captions under MSWindows,
119 // resizeable or not (but a resizeable dialog always has caption -
120 // otherwise it would look too strange)
121 const char *dlg;
122 if ( style & wxTHICK_FRAME )
123 dlg = "wxResizeableDialog";
124 else if ( style & wxCAPTION )
125 dlg = "wxCaptionDialog";
126 else
127 dlg = "wxNoCaptionDialog";
128 MSWCreate(m_windowId, parent, NULL, this, NULL,
129 x, y, width, height,
130 0, // style is not used if we have dlg template
131 dlg,
132 extendedStyle);
2bda0e17 133
462e2437 134 HWND hwnd = (HWND)GetHWND();
2bda0e17 135
462e2437
VZ
136 if ( !hwnd )
137 {
138 wxLogError(_("Failed to created dialog."));
2bda0e17 139
462e2437
VZ
140 return FALSE;
141 }
2bda0e17 142
462e2437
VZ
143 SubclassWin(GetHWND());
144
145 SetWindowText(hwnd, title);
146 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
2bda0e17 147
462e2437 148 return TRUE;
2bda0e17
KB
149}
150
debe6624 151void wxDialog::SetModal(bool flag)
2bda0e17 152{
dc1c4b62
VZ
153 if ( flag )
154 m_windowStyle |= wxDIALOG_MODAL ;
155 else
156 if ( m_windowStyle & wxDIALOG_MODAL )
157 m_windowStyle -= wxDIALOG_MODAL ;
2bda0e17
KB
158
159 wxModelessWindows.DeleteObject(this);
160 if (!flag)
161 wxModelessWindows.Append(this);
162}
163
164wxDialog::~wxDialog()
165{
166 m_isBeingDeleted = TRUE;
167
168 wxTopLevelWindows.DeleteObject(this);
169
170 if (m_modalShowing)
171 {
172 Show(FALSE);
173 // For some reason, wxWindows can activate another task altogether
174 // when a frame is destroyed after a modal dialog has been invoked.
175 // Try to bring the parent to the top.
176 // dfgg: I moved this following line from end of function -
177 // must not call if another window is on top!!
178 // This can often happen with Close() and delayed deleting
179 if (GetParent() && GetParent()->GetHWND())
180 ::BringWindowToTop((HWND) GetParent()->GetHWND());
181 }
182
183 m_modalShowing = FALSE;
184 if ( GetHWND() )
185 ShowWindow((HWND) GetHWND(), SW_HIDE);
186
187 if ( (GetWindowStyleFlag() & wxDIALOG_MODAL) != wxDIALOG_MODAL )
188 wxModelessWindows.DeleteObject(this);
189
190 UnsubclassWin();
191
192 // If this is the last top-level window, exit.
193 if (wxTheApp && (wxTopLevelWindows.Number() == 0))
194 {
195 wxTheApp->SetTopWindow(NULL);
196
197 if (wxTheApp->GetExitOnFrameDelete())
198 {
199 PostQuitMessage(0);
200 }
201 }
202}
203
204// By default, pressing escape cancels the dialog
205void wxDialog::OnCharHook(wxKeyEvent& event)
206{
207 if (GetHWND())
208 {
209 if (event.m_keyCode == WXK_ESCAPE)
210 {
2a47d3c1
JS
211 // Behaviour changed in 2.0: we'll send a Cancel message
212 // to the dialog instead of Close.
213 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
214 cancelEvent.SetEventObject( this );
215 GetEventHandler()->ProcessEvent(cancelEvent);
2bda0e17 216
2a47d3c1 217 return;
2bda0e17
KB
218 }
219 }
220 // We didn't process this event.
221 event.Skip();
222}
223
224void wxDialog::OnPaint(wxPaintEvent& event)
225{
dc1c4b62
VZ
226 // No: if you call the default procedure, it makes
227 // the following painting code not work.
228// wxWindow::OnPaint(event);
2bda0e17
KB
229}
230
231void wxDialog::Fit(void)
232{
233 wxWindow::Fit();
234}
235
debe6624 236void wxDialog::Iconize(bool WXUNUSED(iconize))
2bda0e17
KB
237{
238 // Windows dialog boxes can't be iconized
239}
240
241bool wxDialog::IsIconized(void) const
242{
243 return FALSE;
244}
245
721b32e0 246void wxDialog::DoSetClientSize(int width, int height)
2bda0e17
KB
247{
248 HWND hWnd = (HWND) GetHWND();
249 RECT rect;
2de8030d 250 ::GetClientRect(hWnd, &rect);
2bda0e17
KB
251
252 RECT rect2;
253 GetWindowRect(hWnd, &rect2);
254
255 // Find the difference between the entire window (title bar and all)
256 // and the client area; add this to the new client size to move the
257 // window
258 int actual_width = rect2.right - rect2.left - rect.right + width;
259 int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
260
261 MoveWindow(hWnd, rect2.left, rect2.top, actual_width, actual_height, TRUE);
debe6624 262
2bda0e17 263 wxSizeEvent event(wxSize(actual_width, actual_height), m_windowId);
debe6624 264 event.SetEventObject( this );
2bda0e17 265 GetEventHandler()->ProcessEvent(event);
2bda0e17
KB
266}
267
268void wxDialog::GetPosition(int *x, int *y) const
269{
270 HWND hWnd = (HWND) GetHWND();
271 RECT rect;
272 GetWindowRect(hWnd, &rect);
273
274 *x = rect.left;
275 *y = rect.top;
276}
277
278bool wxDialog::IsShown(void) const
279{
280 return m_isShown;
281}
282
debe6624 283bool wxDialog::Show(bool show)
2bda0e17
KB
284{
285 m_isShown = show;
286
287 if (show)
288 InitDialog();
289
290 bool modal = ((GetWindowStyleFlag() & wxDIALOG_MODAL) == wxDIALOG_MODAL) ;
291
292#if WXGARBAGE_COLLECTION_ON /* MATTHEW: GC */
293 if (!modal) {
294 if (show) {
295 if (!wxModelessWindows.Member(this))
dc1c4b62 296 wxModelessWindows.Append(this);
2bda0e17
KB
297 } else
298 wxModelessWindows.DeleteObject(this);
299 }
300 if (show) {
301 if (!wxTopLevelWindows.Member(this))
302 wxTopLevelWindows.Append(this);
303 } else
304 wxTopLevelWindows.DeleteObject(this);
305#endif
306
307 if (modal)
308 {
309 if (show)
310 {
dc1c4b62
VZ
311 m_hwndOldFocus = (WXHWND)::GetFocus();
312
2bda0e17
KB
313 if (m_modalShowing)
314 {
315 BringWindowToTop((HWND) GetHWND());
316 return TRUE;
317 }
318
319 m_modalShowing = TRUE;
320 wxNode *node = wxModalDialogs.First();
321 while (node)
322 {
323 wxDialog *box = (wxDialog *)node->Data();
324 if (box != this)
325 ::EnableWindow((HWND) box->GetHWND(), FALSE);
326 node = node->Next();
327 }
9c9cff0b
VZ
328
329 // if we don't do it, some window might be deleted while we have pointers
330 // to them in our disabledWindows list and the program will crash when it
331 // will try to reenable them after the modal dialog end
332 wxTheApp->DeletePendingObjects();
333 wxList disabledWindows;
334
2bda0e17
KB
335 node = wxModelessWindows.First();
336 while (node)
337 {
338 wxWindow *win = (wxWindow *)node->Data();
dc1c4b62
VZ
339 if (::IsWindowEnabled((HWND) win->GetHWND()))
340 {
2bda0e17 341 ::EnableWindow((HWND) win->GetHWND(), FALSE);
9c9cff0b 342 disabledWindows.Append(win);
dc1c4b62 343 }
2bda0e17
KB
344 node = node->Next();
345 }
346
347 ShowWindow((HWND) GetHWND(), SW_SHOW);
348 EnableWindow((HWND) GetHWND(), TRUE);
349 BringWindowToTop((HWND) GetHWND());
350
351 if (!wxModalDialogs.Member(this))
352 wxModalDialogs.Append(this);
353
354 MSG msg;
355 // Must test whether this dialog still exists: we may not process
356 // a message before the deletion.
357 while (wxModalDialogs.Member(this) && m_modalShowing && GetMessage(&msg, NULL, 0, 0))
358 {
0a54c4a8
VZ
359 if ( m_acceleratorTable.Ok() &&
360 ::TranslateAccelerator((HWND)GetHWND(),
361 (HACCEL)m_acceleratorTable.GetHACCEL(),
362 &msg) )
567da5c6
JS
363 {
364 // Have processed the message
365 }
0a54c4a8 366 else if ( !wxTheApp->ProcessMessage((WXMSG *)&msg) )
2bda0e17
KB
367 {
368 TranslateMessage(&msg);
369 DispatchMessage(&msg);
370 }
9838df2c
JS
371
372 // If we get crashes (as per George Tasker's message) with nested modal dialogs,
373 // we should try removing the m_modalShowing test
374
2bda0e17 375 if (m_modalShowing && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
dc1c4b62
VZ
376 // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered
377 // a Show(FALSE) in the mean time!!!
378 // Without the test, we might delete the dialog before the end of modal showing.
2bda0e17
KB
379 {
380 while (wxTheApp->ProcessIdle() && m_modalShowing)
381 {
382 // Keep going until we decide we've done enough
383 }
384 }
385 }
386 // dfgg: now must specifically re-enable all other app windows that we disabled earlier
9c9cff0b 387 node=disabledWindows.First();
2bda0e17 388 while(node) {
dc1c4b62 389 wxWindow* win = (wxWindow*) node->Data();
9838df2c
JS
390 if (wxModalDialogs.Member(win) || wxModelessWindows.Member(win))
391 {
392 HWND hWnd = (HWND) win->GetHWND();
393 if (::IsWindow(hWnd))
394 ::EnableWindow(hWnd,TRUE);
395 }
2bda0e17
KB
396 node=node->Next();
397 }
398 }
dc1c4b62 399 else // !show
2bda0e17 400 {
dc1c4b62
VZ
401 ::SetFocus((HWND)m_hwndOldFocus);
402
2bda0e17
KB
403 wxModalDialogs.DeleteObject(this);
404
405 wxNode *last = wxModalDialogs.Last();
406
407 // If there's still a modal dialog active, we
408 // enable it, else we enable all modeless windows
409 if (last)
410 {
411 wxDialog *box = (wxDialog *)last->Data();
412 HWND hwnd = (HWND) box->GetHWND();
413 if (box->m_winEnabled)
414 EnableWindow(hwnd, TRUE);
415 BringWindowToTop(hwnd);
416 }
417 else
418 {
419 wxNode *node = wxModelessWindows.First();
420 while (node)
421 {
422 wxWindow *win = (wxWindow *)node->Data();
423 HWND hwnd = (HWND) win->GetHWND();
424 // Only enable again if not user-disabled.
425 if (win->IsUserEnabled())
426 EnableWindow(hwnd, TRUE);
427 node = node->Next();
428 }
429 }
430 // Try to highlight the correct window (the parent)
431 HWND hWndParent = 0;
432 if (GetParent())
433 {
434 hWndParent = (HWND) GetParent()->GetHWND();
435 if (hWndParent)
436 ::BringWindowToTop(hWndParent);
437 }
438 ShowWindow((HWND) GetHWND(), SW_HIDE);
439 m_modalShowing = FALSE;
440 }
441 }
dc1c4b62 442 else // !modal
2bda0e17
KB
443 {
444 if (show)
445 {
446 ShowWindow((HWND) GetHWND(), SW_SHOW);
447 BringWindowToTop((HWND) GetHWND());
448 }
449 else
450 {
451 // Try to highlight the correct window (the parent)
452 HWND hWndParent = 0;
453 if (GetParent())
454 {
455 hWndParent = (HWND) GetParent()->GetHWND();
456 if (hWndParent)
457 ::BringWindowToTop(hWndParent);
458 }
459 ShowWindow((HWND) GetHWND(), SW_HIDE);
460 }
461 }
462 return TRUE;
463}
464
465void wxDialog::SetTitle(const wxString& title)
466{
467 SetWindowText((HWND) GetHWND(), (const char *)title);
468}
469
470wxString wxDialog::GetTitle(void) const
471{
472 GetWindowText((HWND) GetHWND(), wxBuffer, 1000);
473 return wxString(wxBuffer);
474}
475
debe6624 476void wxDialog::Centre(int direction)
2bda0e17
KB
477{
478 int x_offset,y_offset ;
479 int display_width, display_height;
480 int width, height, x, y;
dfc54541
JS
481 wxWindow *parent = GetParent();
482 if ((direction & wxCENTER_FRAME) && parent)
2bda0e17 483 {
dfc54541
JS
484 parent->GetPosition(&x_offset,&y_offset) ;
485 parent->GetSize(&display_width,&display_height) ;
2bda0e17
KB
486 }
487 else
2bda0e17
KB
488 {
489 wxDisplaySize(&display_width, &display_height);
490 x_offset = 0 ;
491 y_offset = 0 ;
492 }
493
2bda0e17
KB
494 GetSize(&width, &height);
495 GetPosition(&x, &y);
496
497 if (direction & wxHORIZONTAL)
498 x = (int)((display_width - width)/2);
499 if (direction & wxVERTICAL)
500 y = (int)((display_height - height)/2);
501
502 SetSize(x+x_offset, y+y_offset, width, height);
503}
504
505// Replacement for Show(TRUE) for modal dialogs - returns return code
506int wxDialog::ShowModal(void)
507{
dc1c4b62
VZ
508 m_windowStyle |= wxDIALOG_MODAL;
509 Show(TRUE);
510 return GetReturnCode();
2bda0e17
KB
511}
512
513void wxDialog::EndModal(int retCode)
514{
dc1c4b62
VZ
515 SetReturnCode(retCode);
516 Show(FALSE);
2bda0e17
KB
517}
518
519// Define for each class of dialog and control
debe6624 520WXHBRUSH wxDialog::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor,
dc1c4b62 521 WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
2bda0e17 522{
1f112209 523#if wxUSE_CTL3D
2bda0e17
KB
524 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
525 return (WXHBRUSH) hbrush;
526#else
527 return 0;
528#endif
529}
530
531// Standard buttons
532void wxDialog::OnOK(wxCommandEvent& event)
533{
dc1c4b62
VZ
534 if ( Validate() && TransferDataFromWindow() )
535 {
2bda0e17
KB
536 if ( IsModal() )
537 EndModal(wxID_OK);
538 else
539 {
387a3b02
JS
540 SetReturnCode(wxID_OK);
541 this->Show(FALSE);
2bda0e17 542 }
dc1c4b62 543 }
2bda0e17
KB
544}
545
546void wxDialog::OnApply(wxCommandEvent& event)
547{
dc1c4b62
VZ
548 if (Validate())
549 TransferDataFromWindow();
550 // TODO probably need to disable the Apply button until things change again
2bda0e17
KB
551}
552
553void wxDialog::OnCancel(wxCommandEvent& event)
554{
555 if ( IsModal() )
556 EndModal(wxID_CANCEL);
557 else
558 {
559 SetReturnCode(wxID_CANCEL);
2a47d3c1 560 this->Show(FALSE);
2bda0e17
KB
561 }
562}
563
e3065973 564void wxDialog::OnCloseWindow(wxCloseEvent& event)
2bda0e17 565{
e3065973 566 // We'll send a Cancel message by default,
2bda0e17 567 // which may close the dialog.
e3065973
JS
568 // Check for looping if the Cancel event handler calls Close().
569
570 // Note that if a cancel button and handler aren't present in the dialog,
571 // nothing will happen when you close the dialog via the window manager, or
572 // via Close().
573 // We wouldn't want to destroy the dialog by default, since the dialog may have been
574 // created on the stack.
575 // However, this does mean that calling dialog->Close() won't delete the dialog
576 // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be
577 // sure to destroy the dialog.
578 // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog.
2bda0e17
KB
579
580 static wxList closing;
e3065973 581
2bda0e17 582 if ( closing.Member(this) )
e3065973
JS
583 return;
584
2bda0e17 585 closing.Append(this);
e3065973 586
387a3b02
JS
587 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
588 cancelEvent.SetEventObject( this );
e3065973 589 GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog
2bda0e17
KB
590
591 closing.DeleteObject(this);
e3065973 592}
2bda0e17 593
e3065973
JS
594// Destroy the window (delayed, if a managed window)
595bool wxDialog::Destroy(void)
596{
597 if (!wxPendingDelete.Member(this))
598 wxPendingDelete.Append(this);
599 return TRUE;
2bda0e17
KB
600}
601
94b49b93
JS
602void wxDialog::OnSize(wxSizeEvent& WXUNUSED(event))
603{
604 // if we're using constraints - do use them
605 #if wxUSE_CONSTRAINTS
606 if ( GetAutoLayout() ) {
607 Layout();
608 }
609 #endif
610}
611
2bda0e17
KB
612void wxDialog::OnSysColourChanged(wxSysColourChangedEvent& event)
613{
1f112209 614#if wxUSE_CTL3D
2bda0e17
KB
615 Ctl3dColorChange();
616#else
617 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
2bda0e17
KB
618 Refresh();
619#endif
462e2437 620}