X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5ac5e40e4181aaf7fd79ca445113d7a228a70cb8..bf2c43c76e2819be443ab1d830ab68d9569d66b1:/src/common/wincmn.cpp diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 787d08b13f..5d50b212b2 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -4,7 +4,6 @@ // Author: Julian Smart, Vadim Zeitlin // Modified by: // Created: 13/07/98 -// RCS-ID: $Id$ // Copyright: (c) wxWidgets team // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -73,6 +72,7 @@ #endif #include "wx/platinfo.h" +#include "wx/recguard.h" #include "wx/private/window.h" #ifdef __WINDOWS__ @@ -89,6 +89,14 @@ wxMenu *wxCurrentPopupMenu = NULL; extern WXDLLEXPORT_DATA(const char) wxPanelNameStr[] = "panel"; +namespace wxMouseCapture +{ + +// Check if the given window is in the capture stack. +bool IsInCaptureStack(wxWindowBase* win); + +} // wxMouseCapture + // ---------------------------------------------------------------------------- // static data // ---------------------------------------------------------------------------- @@ -448,7 +456,9 @@ bool wxWindowBase::ToggleWindowStyle(int flag) // common clean up wxWindowBase::~wxWindowBase() { - wxASSERT_MSG( GetCapture() != this, wxT("attempt to destroy window with mouse capture") ); + wxASSERT_MSG( !wxMouseCapture::IsInCaptureStack(this), + "Destroying window before releasing mouse capture: this " + "will result in a crash later." ); // FIXME if these 2 cases result from programming errors in the user code // we should probably assert here instead of silently fixing them @@ -3203,72 +3213,106 @@ wxHitTest wxWindowBase::DoHitTest(wxCoord x, wxCoord y) const // mouse capture // ---------------------------------------------------------------------------- -struct WXDLLEXPORT wxWindowNext +// Private data used for mouse capture tracking. +namespace wxMouseCapture +{ + +// Stack of the windows which previously had the capture, the top most element +// is the window that has the mouse capture now. +// +// NB: We use wxVector and not wxStack to be able to examine all of the stack +// elements for debug checks, but only the stack operations should be +// performed with this vector. +wxVector stack; + +// Flag preventing reentrancy in {Capture,Release}Mouse(). +wxRecursionGuardFlag changing; + +bool IsInCaptureStack(wxWindowBase* win) { - wxWindow *win; - wxWindowNext *next; -} *wxWindowBase::ms_winCaptureNext = NULL; -wxWindow *wxWindowBase::ms_winCaptureCurrent = NULL; -bool wxWindowBase::ms_winCaptureChanging = false; + for ( wxVector::const_iterator it = stack.begin(); + it != stack.end(); + ++it ) + { + if ( *it == win ) + return true; + } + + return false; +} + +} // wxMouseCapture void wxWindowBase::CaptureMouse() { wxLogTrace(wxT("mousecapture"), wxT("CaptureMouse(%p)"), static_cast(this)); - wxASSERT_MSG( !ms_winCaptureChanging, wxT("recursive CaptureMouse call?") ); + wxRecursionGuard guard(wxMouseCapture::changing); + wxASSERT_MSG( !guard.IsInside(), wxT("recursive CaptureMouse call?") ); - ms_winCaptureChanging = true; + wxASSERT_MSG( !wxMouseCapture::IsInCaptureStack(this), + "Recapturing the mouse in the same window?" ); wxWindow *winOld = GetCapture(); if ( winOld ) - { ((wxWindowBase*) winOld)->DoReleaseMouse(); - // save it on stack - wxWindowNext *item = new wxWindowNext; - item->win = winOld; - item->next = ms_winCaptureNext; - ms_winCaptureNext = item; - } - //else: no mouse capture to save - DoCaptureMouse(); - ms_winCaptureCurrent = (wxWindow*)this; - ms_winCaptureChanging = false; + wxMouseCapture::stack.push_back(static_cast(this)); } void wxWindowBase::ReleaseMouse() { wxLogTrace(wxT("mousecapture"), wxT("ReleaseMouse(%p)"), static_cast(this)); - wxASSERT_MSG( !ms_winCaptureChanging, wxT("recursive ReleaseMouse call?") ); - - wxASSERT_MSG( GetCapture() == this, - "attempt to release mouse, but this window hasn't captured it" ); - wxASSERT_MSG( ms_winCaptureCurrent == this, - "attempt to release mouse, but this window hasn't captured it" ); + wxRecursionGuard guard(wxMouseCapture::changing); + wxASSERT_MSG( !guard.IsInside(), wxT("recursive ReleaseMouse call?") ); - ms_winCaptureChanging = true; +#if wxDEBUG_LEVEL + wxWindow* const winCapture = GetCapture(); + if ( !winCapture ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Releasing mouse in %p(%s) but it is not captured", + this, GetClassInfo()->GetClassName() + ) + ); + } + else if ( winCapture != this ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Releasing mouse in %p(%s) but it is captured by %p(%s)", + this, GetClassInfo()->GetClassName(), + winCapture, winCapture->GetClassInfo()->GetClassName() + ) + ); + } +#endif // wxDEBUG_LEVEL DoReleaseMouse(); - ms_winCaptureCurrent = NULL; - if ( ms_winCaptureNext ) - { - ((wxWindowBase*)ms_winCaptureNext->win)->DoCaptureMouse(); - ms_winCaptureCurrent = ms_winCaptureNext->win; + wxCHECK_RET( !wxMouseCapture::stack.empty(), + "Releasing mouse capture but capture stack empty?" ); + wxCHECK_RET( wxMouseCapture::stack.back() == this, + "Window releasing mouse capture not top of capture stack?" ); - wxWindowNext *item = ms_winCaptureNext; - ms_winCaptureNext = item->next; - delete item; - } - //else: stack is empty, no previous capture + wxMouseCapture::stack.pop_back(); - ms_winCaptureChanging = false; + // Restore the capture to the previous window, if any. + if ( !wxMouseCapture::stack.empty() ) + { + ((wxWindowBase*)wxMouseCapture::stack.back())->DoCaptureMouse(); + } wxLogTrace(wxT("mousecapture"), - (const wxChar *) wxT("After ReleaseMouse() mouse is captured by %p"), + wxT("After ReleaseMouse() mouse is captured by %p"), static_cast(GetCapture())); } @@ -3291,26 +3335,17 @@ void wxWindowBase::NotifyCaptureLost() { // don't do anything if capture lost was expected, i.e. resulted from // a wx call to ReleaseMouse or CaptureMouse: - if ( ms_winCaptureChanging ) + wxRecursionGuard guard(wxMouseCapture::changing); + if ( guard.IsInside() ) return; // if the capture was lost unexpectedly, notify every window that has // capture (on stack or current) about it and clear the stack: - - if ( ms_winCaptureCurrent ) + while ( !wxMouseCapture::stack.empty() ) { - DoNotifyWindowAboutCaptureLost(ms_winCaptureCurrent); - ms_winCaptureCurrent = NULL; - } - - while ( ms_winCaptureNext ) - { - wxWindowNext *item = ms_winCaptureNext; - ms_winCaptureNext = item->next; - - DoNotifyWindowAboutCaptureLost(item->win); + DoNotifyWindowAboutCaptureLost(wxMouseCapture::stack.back()); - delete item; + wxMouseCapture::stack.pop_back(); } }