7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-03-30 06:56:57 +00:00

Allow context menus in footprint chooser when called from Eeschema.

Also moves several RAII utility classes (some of which
were duplicated in open code) to a common header file.
This commit is contained in:
Jeff Young 2025-01-25 17:08:40 +00:00
parent 9d051d096e
commit b74c2791f4
4 changed files with 137 additions and 117 deletions
common
include
libs/core/include/core

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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