X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/debe6624c1e9d4bf3243381153d1e173c849bcd8..342751763793b5cdad252d731aac0618d0674e5b:/src/msw/dialog.cpp diff --git a/src/msw/dialog.cpp b/src/msw/dialog.cpp index 8cb74e9300..2820cfaa33 100644 --- a/src/msw/dialog.cpp +++ b/src/msw/dialog.cpp @@ -6,7 +6,7 @@ // Created: 01/02/97 // RCS-ID: $Id$ // Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows licence +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #ifdef __GNUG__ @@ -26,11 +26,14 @@ #include "wx/frame.h" #include "wx/app.h" #include "wx/settings.h" +#include "wx/intl.h" +#include "wx/log.h" #endif #include "wx/msw/private.h" +#include "wx/log.h" -#if USE_COMMON_DIALOGS +#if wxUSE_COMMON_DIALOGS #include #endif @@ -39,119 +42,122 @@ // Lists to keep track of windows, so we can disable/enable them // for modal dialogs -wxList wxModalDialogs; -wxList wxModelessWindows; // Frames and modeless dialogs -extern wxList wxPendingDelete; - -#if !USE_SHARED_LIBRARY -IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxPanel) - -BEGIN_EVENT_TABLE(wxDialog, wxPanel) - EVT_BUTTON(wxID_OK, wxDialog::OnOK) - EVT_BUTTON(wxID_APPLY, wxDialog::OnApply) - EVT_BUTTON(wxID_CANCEL, wxDialog::OnCancel) - EVT_CHAR_HOOK(wxDialog::OnCharHook) - EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged) - EVT_CLOSE(wxDialog::OnCloseWindow) -END_EVENT_TABLE() - -#endif - -long wxDialog::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) -{ - return ::CallWindowProc(CASTWNDPROC (FARPROC) m_oldWndProc, (HWND) GetHWND(), (UINT) nMsg, (WPARAM) wParam, (LPARAM) lParam); -} - -bool wxDialog::MSWProcessMessage(WXMSG* pMsg) -{ - return (::IsDialogMessage((HWND) GetHWND(), (MSG*)pMsg) != 0); -} - -bool wxDialog::MSWOnClose(void) -{ - return Close(); -} - -wxDialog::wxDialog(void) +wxWindowList wxModalDialogs; +wxWindowList wxModelessWindows; // Frames and modeless dialogs +extern wxList WXDLLEXPORT wxPendingDelete; + + IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxPanel) + + BEGIN_EVENT_TABLE(wxDialog, wxPanel) + EVT_SIZE(wxDialog::OnSize) + EVT_BUTTON(wxID_OK, wxDialog::OnOK) + EVT_BUTTON(wxID_APPLY, wxDialog::OnApply) + EVT_BUTTON(wxID_CANCEL, wxDialog::OnCancel) + EVT_CHAR_HOOK(wxDialog::OnCharHook) + EVT_SYS_COLOUR_CHANGED(wxDialog::OnSysColourChanged) + EVT_CLOSE(wxDialog::OnCloseWindow) + END_EVENT_TABLE() + +wxDialog::wxDialog() { m_isShown = FALSE; m_modalShowing = FALSE; SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetDefaultBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); } bool wxDialog::Create(wxWindow *parent, wxWindowID id, - const wxString& title, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) { - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetDefaultBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetName(name); - - if (!parent) - wxTopLevelWindows.Append(this); +#if wxUSE_TOOLTIPS + m_hwndToolTip = 0; +#endif -// windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL); + SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); + SetName(name); - if (parent) parent->AddChild(this); + if (!parent) + wxTopLevelWindows.Append(this); - if ( id == -1 ) - m_windowId = (int)NewControlId(); - else - m_windowId = id; + // windowFont = wxTheFontList->FindOrCreateFont(11, wxSWISS, wxNORMAL, wxNORMAL); - int x = pos.x; - int y = pos.y; - int width = size.x; - int height = size.y; + if (parent) parent->AddChild(this); - if (x < 0) x = wxDIALOG_DEFAULT_X; - if (y < 0) y = wxDIALOG_DEFAULT_Y; + if ( id == -1 ) + m_windowId = (int)NewControlId(); + else + m_windowId = id; - m_windowStyle = style; + int x = pos.x; + int y = pos.y; + int width = size.x; + int height = size.y; - m_isShown = FALSE; - m_modalShowing = FALSE; + if (x < 0) x = wxDIALOG_DEFAULT_X; + if (y < 0) y = wxDIALOG_DEFAULT_Y; - if (width < 0) - width = 500; - if (height < 0) - height = 500; + m_windowStyle = style; - WXDWORD extendedStyle = MakeExtendedStyle(m_windowStyle); - if (m_windowStyle & wxSTAY_ON_TOP) - extendedStyle |= WS_EX_TOPMOST; + m_isShown = FALSE; + m_modalShowing = FALSE; - // Allows creation of dialogs with & without captions under MSWindows - if(style & wxCAPTION){ - MSWCreate(m_windowId, (wxWindow *)parent, NULL, this, NULL, x, y, width, height, 0, "wxCaptionDialog", - extendedStyle); - } - else{ - MSWCreate(m_windowId, (wxWindow *)parent, NULL, this, NULL, x, y, width, height, 0, "wxNoCaptionDialog", - extendedStyle); - } + if (width < 0) + width = 500; + if (height < 0) + height = 500; - SubclassWin(GetHWND()); + // All dialogs should really have this style + m_windowStyle |= wxTAB_TRAVERSAL; - SetWindowText((HWND) GetHWND(), (const char *)title); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); + WXDWORD extendedStyle = MakeExtendedStyle(m_windowStyle); + if (m_windowStyle & wxSTAY_ON_TOP) + extendedStyle |= WS_EX_TOPMOST; - return TRUE; + // Allows creation of dialogs with & without captions under MSWindows, + // resizeable or not (but a resizeable dialog always has caption - + // otherwise it would look too strange) + const wxChar *dlg; + if ( style & wxRESIZE_BORDER ) + dlg = wxT("wxResizeableDialog"); + else if ( style & wxCAPTION ) + dlg = wxT("wxCaptionDialog"); + else + dlg = wxT("wxNoCaptionDialog"); + MSWCreate(m_windowId, parent, NULL, this, NULL, + x, y, width, height, + 0, // style is not used if we have dlg template + dlg, + extendedStyle); + + HWND hwnd = (HWND)GetHWND(); + + if ( !hwnd ) + { + wxLogError(_("Failed to create dialog.")); + + return FALSE; + } + + SubclassWin(GetHWND()); + + SetWindowText(hwnd, title); + SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); + + return TRUE; } void wxDialog::SetModal(bool flag) { - if ( flag ) - m_windowStyle |= wxDIALOG_MODAL ; - else - if ( m_windowStyle & wxDIALOG_MODAL ) - m_windowStyle -= wxDIALOG_MODAL ; - + if ( flag ) + m_windowStyle |= wxDIALOG_MODAL ; + else + if ( m_windowStyle & wxDIALOG_MODAL ) + m_windowStyle -= wxDIALOG_MODAL ; + wxModelessWindows.DeleteObject(this); if (!flag) wxModelessWindows.Append(this); @@ -163,9 +169,10 @@ wxDialog::~wxDialog() wxTopLevelWindows.DeleteObject(this); + Show(FALSE); + if (m_modalShowing) { - Show(FALSE); // For some reason, wxWindows can activate another task altogether // when a frame is destroyed after a modal dialog has been invoked. // Try to bring the parent to the top. @@ -177,14 +184,10 @@ wxDialog::~wxDialog() } m_modalShowing = FALSE; - if ( GetHWND() ) - ShowWindow((HWND) GetHWND(), SW_HIDE); if ( (GetWindowStyleFlag() & wxDIALOG_MODAL) != wxDIALOG_MODAL ) wxModelessWindows.DeleteObject(this); - UnsubclassWin(); - // If this is the last top-level window, exit. if (wxTheApp && (wxTopLevelWindows.Number() == 0)) { @@ -204,13 +207,16 @@ void wxDialog::OnCharHook(wxKeyEvent& event) { if (event.m_keyCode == WXK_ESCAPE) { - // Behaviour changed in 2.0: we'll send a Cancel message - // to the dialog instead of Close. - wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); - cancelEvent.SetEventObject( this ); - GetEventHandler()->ProcessEvent(cancelEvent); - - return; + // Behaviour changed in 2.0: we'll send a Cancel message + // to the dialog instead of Close. + wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); + cancelEvent.SetEventObject( this ); + GetEventHandler()->ProcessEvent(cancelEvent); + + // ensure that there is another message for this window so the + // ShowModal loop will exit and won't get stuck in GetMessage(). + ::PostMessage(GetHwnd(), WM_NULL, 0, 0); + return; } } // We didn't process this event. @@ -219,12 +225,12 @@ void wxDialog::OnCharHook(wxKeyEvent& event) void wxDialog::OnPaint(wxPaintEvent& event) { - // No: if you call the default procedure, it makes - // the following painting code not work. -// wxWindow::OnPaint(event); + // No: if you call the default procedure, it makes + // the following painting code not work. +// wxWindow::OnPaint(event); } -void wxDialog::Fit(void) +void wxDialog::Fit() { wxWindow::Fit(); } @@ -234,21 +240,16 @@ void wxDialog::Iconize(bool WXUNUSED(iconize)) // Windows dialog boxes can't be iconized } -bool wxDialog::IsIconized(void) const +bool wxDialog::IsIconized() const { return FALSE; } -void wxDialog::SetSize(int x, int y, int width, int height, int WXUNUSED(sizeFlags)) -{ - wxWindow::SetSize(x, y, width, height); -} - -void wxDialog::SetClientSize(int width, int height) +void wxDialog::DoSetClientSize(int width, int height) { HWND hWnd = (HWND) GetHWND(); RECT rect; - GetClientRect(hWnd, &rect); + ::GetClientRect(hWnd, &rect); RECT rect2; GetWindowRect(hWnd, &rect2); @@ -276,11 +277,16 @@ void wxDialog::GetPosition(int *x, int *y) const *y = rect.top; } -bool wxDialog::IsShown(void) const +bool wxDialog::IsShown() const { return m_isShown; } +bool wxDialog::IsModal() const +{ + return wxModalDialogs.Find((wxDialog *)this) != 0; // const_cast +} + bool wxDialog::Show(bool show) { m_isShown = show; @@ -293,13 +299,13 @@ bool wxDialog::Show(bool show) #if WXGARBAGE_COLLECTION_ON /* MATTHEW: GC */ if (!modal) { if (show) { - if (!wxModelessWindows.Member(this)) - wxModelessWindows.Append(this); + if (!wxModelessWindows.Find(this)) + wxModelessWindows.Append(this); } else wxModelessWindows.DeleteObject(this); } if (show) { - if (!wxTopLevelWindows.Member(this)) + if (!wxTopLevelWindows.Find(this)) wxTopLevelWindows.Append(this); } else wxTopLevelWindows.DeleteObject(this); @@ -309,13 +315,20 @@ bool wxDialog::Show(bool show) { if (show) { - wxList DisabledWindows; + // find the top level window which had focus before - we will restore + // focus to it later + m_hwndOldFocus = 0; + for ( HWND hwnd = ::GetFocus(); hwnd; hwnd = ::GetParent(hwnd) ) + { + m_hwndOldFocus = (WXHWND)hwnd; + } + if (m_modalShowing) { BringWindowToTop((HWND) GetHWND()); return TRUE; } - + m_modalShowing = TRUE; wxNode *node = wxModalDialogs.First(); while (node) @@ -325,15 +338,22 @@ bool wxDialog::Show(bool show) ::EnableWindow((HWND) box->GetHWND(), FALSE); node = node->Next(); } + + // if we don't do it, some window might be deleted while we have pointers + // to them in our disabledWindows list and the program will crash when it + // will try to reenable them after the modal dialog end + wxTheApp->DeletePendingObjects(); + wxList disabledWindows; + node = wxModelessWindows.First(); while (node) { wxWindow *win = (wxWindow *)node->Data(); - if (::IsWindowEnabled((HWND) win->GetHWND())) - { + if (::IsWindowEnabled((HWND) win->GetHWND())) + { ::EnableWindow((HWND) win->GetHWND(), FALSE); - DisabledWindows.Append(win); - } + disabledWindows.Append(win); + } node = node->Next(); } @@ -341,23 +361,34 @@ bool wxDialog::Show(bool show) EnableWindow((HWND) GetHWND(), TRUE); BringWindowToTop((HWND) GetHWND()); - if (!wxModalDialogs.Member(this)) + if ( !wxModalDialogs.Find(this) ) wxModalDialogs.Append(this); MSG msg; // Must test whether this dialog still exists: we may not process // a message before the deletion. - while (wxModalDialogs.Member(this) && m_modalShowing && GetMessage(&msg, NULL, 0, 0)) + while (wxModalDialogs.Find(this) && m_modalShowing && GetMessage(&msg, NULL, 0, 0)) { - if (!IsDialogMessage((HWND) GetHWND(), &msg)) + if ( m_acceleratorTable.Ok() && + ::TranslateAccelerator((HWND)GetHWND(), + (HACCEL)m_acceleratorTable.GetHACCEL(), + &msg) ) + { + // Have processed the message + } + else if ( !wxTheApp->ProcessMessage((WXMSG *)&msg) ) { TranslateMessage(&msg); DispatchMessage(&msg); } + + // If we get crashes (as per George Tasker's message) with nested modal dialogs, + // we should try removing the m_modalShowing test + if (m_modalShowing && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) - // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered - // a Show(FALSE) in the mean time!!! - // Without the test, we might delete the dialog before the end of modal showing. + // dfgg: NB MUST test m_modalShowing again as the message loop could have triggered + // a Show(FALSE) in the mean time!!! + // Without the test, we might delete the dialog before the end of modal showing. { while (wxTheApp->ProcessIdle() && m_modalShowing) { @@ -366,17 +397,22 @@ bool wxDialog::Show(bool show) } } // dfgg: now must specifically re-enable all other app windows that we disabled earlier - node=DisabledWindows.First(); + node=disabledWindows.First(); while(node) { - wxWindow* win = (wxWindow*) node->Data(); - HWND hWnd = (HWND) win->GetHWND(); - if (::IsWindow(hWnd) && (wxModalDialogs.Member(win) || wxModelessWindows.Member(win) )) - ::EnableWindow(hWnd,TRUE); + wxWindow* win = (wxWindow*) node->Data(); + if (wxModalDialogs.Find(win) || wxModelessWindows.Find(win)) + { + HWND hWnd = (HWND) win->GetHWND(); + if (::IsWindow(hWnd)) + ::EnableWindow(hWnd,TRUE); + } node=node->Next(); } } - else + else // !show { + ::SetFocus((HWND)m_hwndOldFocus); + wxModalDialogs.DeleteObject(this); wxNode *last = wxModalDialogs.Last(); @@ -385,9 +421,13 @@ bool wxDialog::Show(bool show) // enable it, else we enable all modeless windows if (last) { + // VZ: I don't understand what this is supposed to do, so I'll leave + // it out for now and look for horrible consequences wxDialog *box = (wxDialog *)last->Data(); HWND hwnd = (HWND) box->GetHWND(); - if (box->m_winEnabled) +#if 0 + if (box->IsUserEnabled()) +#endif // 0 EnableWindow(hwnd, TRUE); BringWindowToTop(hwnd); } @@ -399,7 +439,9 @@ bool wxDialog::Show(bool show) wxWindow *win = (wxWindow *)node->Data(); HWND hwnd = (HWND) win->GetHWND(); // Only enable again if not user-disabled. +#if 0 if (win->IsUserEnabled()) +#endif // 0 EnableWindow(hwnd, TRUE); node = node->Next(); } @@ -416,7 +458,7 @@ bool wxDialog::Show(bool show) m_modalShowing = FALSE; } } - else + else // !modal { if (show) { @@ -433,7 +475,9 @@ bool wxDialog::Show(bool show) if (hWndParent) ::BringWindowToTop(hWndParent); } - ShowWindow((HWND) GetHWND(), SW_HIDE); + + if ( m_hWnd ) + ShowWindow((HWND) GetHWND(), SW_HIDE); } } return TRUE; @@ -441,10 +485,10 @@ bool wxDialog::Show(bool show) void wxDialog::SetTitle(const wxString& title) { - SetWindowText((HWND) GetHWND(), (const char *)title); + SetWindowText((HWND) GetHWND(), title.c_str()); } -wxString wxDialog::GetTitle(void) const +wxString wxDialog::GetTitle() const { GetWindowText((HWND) GetHWND(), wxBuffer, 1000); return wxString(wxBuffer); @@ -455,27 +499,19 @@ void wxDialog::Centre(int direction) int x_offset,y_offset ; int display_width, display_height; int width, height, x, y; - wxFrame *frame ; - if (direction & wxCENTER_FRAME) + wxWindow *parent = GetParent(); + if ((direction & wxCENTER_FRAME) && parent) { - frame = (wxFrame*)GetParent() ; - if (frame) - { - frame->GetPosition(&x_offset,&y_offset) ; - frame->GetSize(&display_width,&display_height) ; - } + parent->GetPosition(&x_offset,&y_offset) ; + parent->GetSize(&display_width,&display_height) ; } else - frame = NULL ; - - if (frame==NULL) { wxDisplaySize(&display_width, &display_height); x_offset = 0 ; y_offset = 0 ; } - GetSize(&width, &height); GetPosition(&x, &y); @@ -488,23 +524,24 @@ void wxDialog::Centre(int direction) } // Replacement for Show(TRUE) for modal dialogs - returns return code -int wxDialog::ShowModal(void) +int wxDialog::ShowModal() { - Show(TRUE); - return GetReturnCode(); + m_windowStyle |= wxDIALOG_MODAL; + Show(TRUE); + return GetReturnCode(); } void wxDialog::EndModal(int retCode) { - SetReturnCode(retCode); - Show(FALSE); + SetReturnCode(retCode); + Show(FALSE); } // Define for each class of dialog and control WXHBRUSH wxDialog::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor, - WXUINT message, WXWPARAM wParam, WXLPARAM lParam) + WXUINT message, WXWPARAM wParam, WXLPARAM lParam) { -#if CTL3D +#if wxUSE_CTL3D HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam); return (WXHBRUSH) hbrush; #else @@ -515,23 +552,23 @@ WXHBRUSH wxDialog::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor, // Standard buttons void wxDialog::OnOK(wxCommandEvent& event) { - if ( Validate() && TransferDataFromWindow() ) - { + if ( Validate() && TransferDataFromWindow() ) + { if ( IsModal() ) EndModal(wxID_OK); else { - SetReturnCode(wxID_OK); - this->Show(FALSE); + SetReturnCode(wxID_OK); + this->Show(FALSE); } - } + } } void wxDialog::OnApply(wxCommandEvent& event) { - if (Validate()) - TransferDataFromWindow(); - // TODO probably need to disable the Apply button until things change again + if (Validate()) + TransferDataFromWindow(); + // TODO probably need to disable the Apply button until things change again } void wxDialog::OnCancel(wxCommandEvent& event) @@ -541,62 +578,89 @@ void wxDialog::OnCancel(wxCommandEvent& event) else { SetReturnCode(wxID_CANCEL); - this->Show(FALSE); + this->Show(FALSE); } } -bool wxDialog::OnClose(void) +void wxDialog::OnCloseWindow(wxCloseEvent& event) { - // Behaviour changed in 2.0: we'll send a Cancel message by default, + // We'll send a Cancel message by default, // which may close the dialog. - // Check for looping if the Cancel event handler calls Close() + // Check for looping if the Cancel event handler calls Close(). + + // Note that if a cancel button and handler aren't present in the dialog, + // nothing will happen when you close the dialog via the window manager, or + // via Close(). + // We wouldn't want to destroy the dialog by default, since the dialog may have been + // created on the stack. + // However, this does mean that calling dialog->Close() won't delete the dialog + // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be + // sure to destroy the dialog. + // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog. static wxList closing; if ( closing.Member(this) ) - return FALSE; + return; closing.Append(this); - wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); - cancelEvent.SetEventObject( this ); - GetEventHandler()->ProcessEvent(cancelEvent); + wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); + cancelEvent.SetEventObject( this ); + GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog closing.DeleteObject(this); - - return FALSE; -} - -void wxDialog::OnCloseWindow(wxCloseEvent& event) -{ - // Compatibility - if ( GetEventHandler()->OnClose() || event.GetForce()) - { - this->Destroy(); - } } // Destroy the window (delayed, if a managed window) -bool wxDialog::Destroy(void) +bool wxDialog::Destroy() { if (!wxPendingDelete.Member(this)) wxPendingDelete.Append(this); return TRUE; } +void wxDialog::OnSize(wxSizeEvent& WXUNUSED(event)) +{ + // if we're using constraints - do use them + #if wxUSE_CONSTRAINTS + if ( GetAutoLayout() ) + { + Layout(); + } + #endif +} + void wxDialog::OnSysColourChanged(wxSysColourChangedEvent& event) { -#if CTL3D +#if wxUSE_CTL3D Ctl3dColorChange(); #else SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetDefaultBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); Refresh(); #endif } +// --------------------------------------------------------------------------- +// dialog window proc +// --------------------------------------------------------------------------- + long wxDialog::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) { - return wxWindow::MSWWindowProc(message, wParam, lParam); -} + long rc = 0; + bool processed = FALSE; + switch ( message ) + { + case WM_CLOSE: + // if we can't close, tell the system that we processed the + // message - otherwise it would close us + processed = !Close(); + break; + } + + if ( !processed ) + rc = wxWindow::MSWWindowProc(message, wParam, lParam); + + return rc; +}