diff --git a/common/dialog_shim.cpp b/common/dialog_shim.cpp index dbfa17568d..13e69d83b0 100644 --- a/common/dialog_shim.cpp +++ b/common/dialog_shim.cpp @@ -42,43 +42,6 @@ #include <algorithm> -/// Toggle a window's "enable" status to disabled, then enabled on destruction. -class WDO_ENABLE_DISABLE -{ - wxWindow* m_win; - -public: - - WDO_ENABLE_DISABLE( wxWindow* aWindow ) : - m_win( aWindow ) - { - if( m_win ) - m_win->Disable(); - } - - ~WDO_ENABLE_DISABLE() - { - if( m_win ) - { - m_win->Enable(); - m_win->Raise(); // let's focus back on the parent window - } - } - - void SuspendForTrueModal() - { - if( m_win ) - m_win->Enable(); - } - - void ResumeAfterTrueModal() - { - if( m_win ) - m_win->Disable(); - } -}; - - BEGIN_EVENT_TABLE( DIALOG_SHIM, wxDialog ) EVT_CHAR_HOOK( DIALOG_SHIM::OnCharHook ) END_EVENT_TABLE() @@ -521,15 +484,7 @@ int DIALOG_SHIM::ShowModal() int DIALOG_SHIM::ShowQuasiModal() { - // This is an exception safe way to zero a pointer before returning. - // Yes, even though DismissModal() clears this first normally, this is - // here in case there's an exception before the dialog is dismissed. - struct NULLER - { - void*& m_what; - NULLER( void*& aPtr ) : m_what( aPtr ) {} - ~NULLER() { m_what = nullptr; } // indeed, set it to NULL on destruction - } clear_this( (void*&) m_qmodal_loop ); + NULLER raii_nuller( (void*&) m_qmodal_loop ); // release the mouse if it's currently captured as the window having it // will be disabled when this dialog is shown -- but will still keep the @@ -545,7 +500,7 @@ int DIALOG_SHIM::ShowQuasiModal() "window?" ) ); // quasi-modal: disable only my "optimal" parent - m_qmodal_parent_disabler = new WDO_ENABLE_DISABLE( parent ); + m_qmodal_parent_disabler = new WINDOW_DISABLER( parent ); // Apple in its infinite wisdom will raise a disabled window before even passing // us the event, so we have no way to stop it. Instead, we must set an order on diff --git a/common/kiway_player.cpp b/common/kiway_player.cpp index fcb1898032..3eccf55fa7 100644 --- a/common/kiway_player.cpp +++ b/common/kiway_player.cpp @@ -32,6 +32,7 @@ #include <wx/utils.h> #include <wx/evtloop.h> #include <wx/socket.h> +#include <core/raii.h> BEGIN_EVENT_TABLE( KIWAY_PLAYER, EDA_BASE_FRAME ) @@ -103,16 +104,7 @@ bool KIWAY_PLAYER::ShowModal( wxString* aResult, wxWindow* aResultantFocusWindow vtable and therefore cross-module capable. */ - // This is an exception safe way to zero a pointer before returning. - // Yes, even though DismissModal() clears this first normally, this is - // here in case there's an exception before the dialog is dismissed. - struct NULLER - { - void*& m_what; - NULLER( void*& aPtr ) : m_what( aPtr ) {} - ~NULLER() { m_what = nullptr; } // indeed, set it to NULL on destruction - } clear_this( (void*&) m_modal_loop ); - + NULLER raii_nuller( (void*&) m_modal_loop ); m_modal_resultant_parent = aResultantFocusWindow; @@ -122,34 +114,21 @@ bool KIWAY_PLAYER::ShowModal( wxString* aResult, wxWindow* aResultantFocusWindow SetFocus(); { - // We have to disable all frames but the modal one. - // wxWindowDisabler does that, but it also disables all top level windows - // We do not want to disable top level windows which are child of the modal one, - // if they are enabled. - // An example is an aui toolbar which was moved - // or a dialog or another frame or miniframe opened by the modal one. - wxWindowList wlist = GetChildren(); - std::vector<wxWindow*> enabledTopLevelWindows; + // Using wxWindowDisabler() has two issues: it will disable top-level windows that are + // our *children* (such as sub-frames), and it will disable all context menus we try to + // put up. Fortunatly we already had to cross this Rubicon for QuasiModal dialogs, so + // we re-use that strategy. + wxWindow* parent = GetParent(); - for( unsigned ii = 0; ii < wlist.size(); ii++ ) - { - if( wlist[ii]->IsTopLevel() && wlist[ii]->IsEnabled() ) - enabledTopLevelWindows.push_back( wlist[ii] ); - } + while( parent && !parent->IsTopLevel() ) + parent = parent->GetParent(); - // exception safe way to disable all top level windows except the modal one, - // re-enables only those that were disabled on exit - wxWindowDisabler toggle( this ); - - for( unsigned ii = 0; ii < enabledTopLevelWindows.size(); ii++ ) - enabledTopLevelWindows[ii]->Enable( true ); + WINDOW_DISABLER raii_parent_disabler( parent ); wxGUIEventLoop event_loop; m_modal_loop = &event_loop; event_loop.Run(); - - } // End of scope for some variables. - // End nesting before setting focus below. + } if( aResult ) *aResult = m_modal_string; diff --git a/include/dialog_shim.h b/include/dialog_shim.h index c490d3fb86..431da278fd 100644 --- a/include/dialog_shim.h +++ b/include/dialog_shim.h @@ -30,6 +30,7 @@ #include <kiway_holder.h> #include <wx/dialog.h> #include <map> +#include <core/raii.h> class EDA_BASE_FRAME; @@ -37,43 +38,6 @@ class wxGridEvent; class wxGUIEventLoop; -struct WINDOW_THAWER -{ - WINDOW_THAWER( wxWindow* aWindow ) - { - m_window = aWindow; - m_freezeCount = 0; - - while( m_window->IsFrozen() ) - { - m_window->Thaw(); - m_freezeCount++; - } - } - - ~WINDOW_THAWER() - { - while( m_freezeCount > 0 ) - { - m_window->Freeze(); - m_freezeCount--; - } - } - -protected: - wxWindow* m_window; - int m_freezeCount; -}; - - -class WDO_ENABLE_DISABLE; - -// These macros are for DIALOG_SHIM only, NOT for KIWAY_PLAYER. KIWAY_PLAYER -// has its own support for quasi modal and its platform specific issues are different -// than for a wxDialog. - #define SHOWQUASIMODAL ShowQuasiModal - #define ENDQUASIMODAL EndQuasiModal - /** * Dialog helper object to sit in the inheritance tree between wxDialog and any class written * by wxFormBuilder. @@ -243,7 +207,7 @@ protected: wxGUIEventLoop* m_qmodal_loop; // points to nested event_loop, NULL means not qmodal // and dismissed bool m_qmodal_showing; - WDO_ENABLE_DISABLE* m_qmodal_parent_disabler; + WINDOW_DISABLER* m_qmodal_parent_disabler; EDA_BASE_FRAME* m_parentFrame; diff --git a/libs/core/include/core/raii.h b/libs/core/include/core/raii.h new file mode 100644 index 0000000000..d4aaedd848 --- /dev/null +++ b/libs/core/include/core/raii.h @@ -0,0 +1,122 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-3.0.html + * or you may search the http://www.gnu.org website for the version 3 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef RAII_H +#define RAII_H + +#include <wx/window.h> + + +/* + * Exception-safe (and 'return' safe) scoped handlers following the "resource allocation is + * initialization" pattern. + */ + + +// Exception-safe method for nulling a pointer +class NULLER +{ +public: + NULLER( void*& aPtr ) + : m_what( aPtr ) + {} + + ~NULLER() + { + m_what = nullptr; + } + +private: + void*& m_what; +}; + + +// Temporarily un-freeze a window, and then re-freeze on destruction +class WINDOW_THAWER +{ +public: + WINDOW_THAWER( wxWindow* aWindow ) + { + m_window = aWindow; + m_freezeCount = 0; + + while( m_window->IsFrozen() ) + { + m_window->Thaw(); + m_freezeCount++; + } + } + + ~WINDOW_THAWER() + { + while( m_freezeCount > 0 ) + { + m_window->Freeze(); + m_freezeCount--; + } + } + +protected: + wxWindow* m_window; + int m_freezeCount; +}; + + +/// Temporarily disable a window, and then re-enable on destruction. +class WINDOW_DISABLER +{ +public: + WINDOW_DISABLER( wxWindow* aWindow ) : + m_win( aWindow ) + { + if( m_win ) + m_win->Disable(); + } + + ~WINDOW_DISABLER() + { + if( m_win ) + { + m_win->Enable(); + m_win->Raise(); // let's focus back on the parent window + } + } + + void SuspendForTrueModal() + { + if( m_win ) + m_win->Enable(); + } + + void ResumeAfterTrueModal() + { + if( m_win ) + m_win->Disable(); + } + +private: + wxWindow* m_win; +}; + + +#endif // RAII_H \ No newline at end of file