]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dialog.cpp
1. wxListCtrl fixes
[wxWidgets.git] / src / msw / dialog.cpp
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
9 // Licence: wxWindows licence
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"
32 #include "wx/log.h"
33
34 #if wxUSE_COMMON_DIALOGS
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
43 wxList wxModalDialogs;
44 wxList wxModelessWindows; // Frames and modeless dialogs
45 extern wxList WXDLLEXPORT wxPendingDelete;
46
47 #if !USE_SHARED_LIBRARY
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()
59 #endif
60
61 bool wxDialog::MSWOnClose(void)
62 {
63 return Close();
64 }
65
66 wxDialog::wxDialog(void)
67 {
68 m_isShown = FALSE;
69 m_modalShowing = FALSE;
70
71 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
72 }
73
74 bool wxDialog::Create(wxWindow *parent, wxWindowID id,
75 const wxString& title,
76 const wxPoint& pos,
77 const wxSize& size,
78 long style,
79 const wxString& name)
80 {
81 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
82 SetName(name);
83
84 if (!parent)
85 wxTopLevelWindows.Append(this);
86
87 // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL);
88
89 if (parent) parent->AddChild(this);
90
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);
133
134 HWND hwnd = (HWND)GetHWND();
135
136 if ( !hwnd )
137 {
138 wxLogError(_("Failed to created dialog."));
139
140 return FALSE;
141 }
142
143 SubclassWin(GetHWND());
144
145 SetWindowText(hwnd, title);
146 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
147
148 return TRUE;
149 }
150
151 void wxDialog::SetModal(bool flag)
152 {
153 if ( flag )
154 m_windowStyle |= wxDIALOG_MODAL ;
155 else
156 if ( m_windowStyle & wxDIALOG_MODAL )
157 m_windowStyle -= wxDIALOG_MODAL ;
158
159 wxModelessWindows.DeleteObject(this);
160 if (!flag)
161 wxModelessWindows.Append(this);
162 }
163
164 wxDialog::~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
205 void wxDialog::OnCharHook(wxKeyEvent& event)
206 {
207 if (GetHWND())
208 {
209 if (event.m_keyCode == WXK_ESCAPE)
210 {
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);
216
217 return;
218 }
219 }
220 // We didn't process this event.
221 event.Skip();
222 }
223
224 void wxDialog::OnPaint(wxPaintEvent& event)
225 {
226 // No: if you call the default procedure, it makes
227 // the following painting code not work.
228 // wxWindow::OnPaint(event);
229 }
230
231 void wxDialog::Fit(void)
232 {
233 wxWindow::Fit();
234 }
235
236 void wxDialog::Iconize(bool WXUNUSED(iconize))
237 {
238 // Windows dialog boxes can't be iconized
239 }
240
241 bool wxDialog::IsIconized(void) const
242 {
243 return FALSE;
244 }
245
246 void wxDialog::DoSetClientSize(int width, int height)
247 {
248 HWND hWnd = (HWND) GetHWND();
249 RECT rect;
250 ::GetClientRect(hWnd, &rect);
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);
262
263 wxSizeEvent event(wxSize(actual_width, actual_height), m_windowId);
264 event.SetEventObject( this );
265 GetEventHandler()->ProcessEvent(event);
266 }
267
268 void 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
278 bool wxDialog::IsShown(void) const
279 {
280 return m_isShown;
281 }
282
283 bool wxDialog::Show(bool show)
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))
296 wxModelessWindows.Append(this);
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 {
311 m_hwndOldFocus = (WXHWND)::GetFocus();
312
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 }
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
335 node = wxModelessWindows.First();
336 while (node)
337 {
338 wxWindow *win = (wxWindow *)node->Data();
339 if (::IsWindowEnabled((HWND) win->GetHWND()))
340 {
341 ::EnableWindow((HWND) win->GetHWND(), FALSE);
342 disabledWindows.Append(win);
343 }
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 {
359 if ( m_acceleratorTable.Ok() &&
360 ::TranslateAccelerator((HWND)GetHWND(),
361 (HACCEL)m_acceleratorTable.GetHACCEL(),
362 &msg) )
363 {
364 // Have processed the message
365 }
366 else if ( !wxTheApp->ProcessMessage((WXMSG *)&msg) )
367 {
368 TranslateMessage(&msg);
369 DispatchMessage(&msg);
370 }
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
375 if (m_modalShowing && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
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.
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
387 node=disabledWindows.First();
388 while(node) {
389 wxWindow* win = (wxWindow*) node->Data();
390 if (wxModalDialogs.Member(win) || wxModelessWindows.Member(win))
391 {
392 HWND hWnd = (HWND) win->GetHWND();
393 if (::IsWindow(hWnd))
394 ::EnableWindow(hWnd,TRUE);
395 }
396 node=node->Next();
397 }
398 }
399 else // !show
400 {
401 ::SetFocus((HWND)m_hwndOldFocus);
402
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 }
442 else // !modal
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
465 void wxDialog::SetTitle(const wxString& title)
466 {
467 SetWindowText((HWND) GetHWND(), (const char *)title);
468 }
469
470 wxString wxDialog::GetTitle(void) const
471 {
472 GetWindowText((HWND) GetHWND(), wxBuffer, 1000);
473 return wxString(wxBuffer);
474 }
475
476 void wxDialog::Centre(int direction)
477 {
478 int x_offset,y_offset ;
479 int display_width, display_height;
480 int width, height, x, y;
481 wxWindow *parent = GetParent();
482 if ((direction & wxCENTER_FRAME) && parent)
483 {
484 parent->GetPosition(&x_offset,&y_offset) ;
485 parent->GetSize(&display_width,&display_height) ;
486 }
487 else
488 {
489 wxDisplaySize(&display_width, &display_height);
490 x_offset = 0 ;
491 y_offset = 0 ;
492 }
493
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
506 int wxDialog::ShowModal(void)
507 {
508 m_windowStyle |= wxDIALOG_MODAL;
509 Show(TRUE);
510 return GetReturnCode();
511 }
512
513 void wxDialog::EndModal(int retCode)
514 {
515 SetReturnCode(retCode);
516 Show(FALSE);
517 }
518
519 // Define for each class of dialog and control
520 WXHBRUSH wxDialog::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor,
521 WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
522 {
523 #if wxUSE_CTL3D
524 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
525 return (WXHBRUSH) hbrush;
526 #else
527 return 0;
528 #endif
529 }
530
531 // Standard buttons
532 void wxDialog::OnOK(wxCommandEvent& event)
533 {
534 if ( Validate() && TransferDataFromWindow() )
535 {
536 if ( IsModal() )
537 EndModal(wxID_OK);
538 else
539 {
540 SetReturnCode(wxID_OK);
541 this->Show(FALSE);
542 }
543 }
544 }
545
546 void wxDialog::OnApply(wxCommandEvent& event)
547 {
548 if (Validate())
549 TransferDataFromWindow();
550 // TODO probably need to disable the Apply button until things change again
551 }
552
553 void wxDialog::OnCancel(wxCommandEvent& event)
554 {
555 if ( IsModal() )
556 EndModal(wxID_CANCEL);
557 else
558 {
559 SetReturnCode(wxID_CANCEL);
560 this->Show(FALSE);
561 }
562 }
563
564 void wxDialog::OnCloseWindow(wxCloseEvent& event)
565 {
566 // We'll send a Cancel message by default,
567 // which may close the dialog.
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.
579
580 static wxList closing;
581
582 if ( closing.Member(this) )
583 return;
584
585 closing.Append(this);
586
587 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
588 cancelEvent.SetEventObject( this );
589 GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog
590
591 closing.DeleteObject(this);
592 }
593
594 // Destroy the window (delayed, if a managed window)
595 bool wxDialog::Destroy(void)
596 {
597 if (!wxPendingDelete.Member(this))
598 wxPendingDelete.Append(this);
599 return TRUE;
600 }
601
602 void 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
612 void wxDialog::OnSysColourChanged(wxSysColourChangedEvent& event)
613 {
614 #if wxUSE_CTL3D
615 Ctl3dColorChange();
616 #else
617 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
618 Refresh();
619 #endif
620 }