760 lines
24 KiB
C++
760 lines
24 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
* Copyright (C) 2014-2023 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 2
|
|
* 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-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <cstring>
|
|
|
|
#include <core/ignore.h>
|
|
#include <macros.h>
|
|
#include <kiway.h>
|
|
#include <kiway_player.h>
|
|
#include <kiway_express.h>
|
|
#include <pgm_base.h>
|
|
#include <config.h>
|
|
#include <core/arraydim.h>
|
|
#include <id.h>
|
|
#include <kiplatform/app.h>
|
|
#include <kiplatform/environment.h>
|
|
#include <settings/settings_manager.h>
|
|
#include <tool/action_manager.h>
|
|
#include <logging.h>
|
|
|
|
#include <wx/dynlib.h>
|
|
#include <wx/stdpaths.h>
|
|
#include <wx/debug.h>
|
|
#include <wx/utils.h>
|
|
#include <confirm.h>
|
|
|
|
#ifdef KICAD_USE_SENTRY
|
|
#include <sentry.h>
|
|
#endif
|
|
|
|
KIFACE* KIWAY::m_kiface[KIWAY_FACE_COUNT];
|
|
int KIWAY::m_kiface_version[KIWAY_FACE_COUNT];
|
|
|
|
|
|
|
|
KIWAY::KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop ):
|
|
m_program( aProgram ), m_ctl( aCtlBits ), m_top( nullptr ), m_blockingDialog( wxID_NONE )
|
|
{
|
|
SetTop( aTop ); // hook player_destroy_handler() into aTop.
|
|
|
|
// Set the array of all known frame window IDs to empty = wxID_NONE,
|
|
// once they are be created, they are added with FRAME_T as index to this array.
|
|
// Note: A non empty entry does not mean the frame still exists.
|
|
// It means only the frame was created at least once. It can be destroyed after.
|
|
// These entries are not cleared automatically on window closing. The purpose is just
|
|
// to allow a call to wxWindow::FindWindowById() using a FRAME_T frame type
|
|
for( int n = 0; n < KIWAY_PLAYER_COUNT; n++ )
|
|
m_playerFrameId[n] = wxID_NONE;
|
|
}
|
|
|
|
|
|
#if 0
|
|
// Any event types derived from wxCommandEvt, like wxWindowDestroyEvent, are
|
|
// propagated upwards to parent windows if not handled below. Therefore the
|
|
// m_top window should receive all wxWindowDestroyEvents originating from
|
|
// KIWAY_PLAYERs. It does anyways, but now player_destroy_handler eavesdrops
|
|
// on that event stream looking for KIWAY_PLAYERs being closed.
|
|
|
|
void KIWAY::player_destroy_handler( wxWindowDestroyEvent& event )
|
|
{
|
|
// Currently : do nothing
|
|
event.Skip(); // skip to who, the wxApp? I'm the top window.
|
|
}
|
|
#endif
|
|
|
|
|
|
void KIWAY::SetTop( wxFrame* aTop )
|
|
{
|
|
#if 0
|
|
if( m_top )
|
|
{
|
|
m_top->Disconnect( wxEVT_DESTROY,
|
|
wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ),
|
|
nullptr, this );
|
|
}
|
|
|
|
if( aTop )
|
|
{
|
|
aTop->Connect( wxEVT_DESTROY,
|
|
wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ),
|
|
nullptr, this );
|
|
}
|
|
#endif
|
|
|
|
m_top = aTop;
|
|
}
|
|
|
|
|
|
const wxString KIWAY::dso_search_path( FACE_T aFaceId )
|
|
{
|
|
const char* name;
|
|
|
|
switch( aFaceId )
|
|
{
|
|
case FACE_SCH: name = KIFACE_PREFIX "eeschema"; break;
|
|
case FACE_PCB: name = KIFACE_PREFIX "pcbnew"; break;
|
|
case FACE_CVPCB: name = KIFACE_PREFIX "cvpcb"; break;
|
|
case FACE_GERBVIEW: name = KIFACE_PREFIX "gerbview"; break;
|
|
case FACE_PL_EDITOR: name = KIFACE_PREFIX "pl_editor"; break;
|
|
case FACE_PCB_CALCULATOR: name = KIFACE_PREFIX "pcb_calculator"; break;
|
|
case FACE_BMP2CMP: name = KIFACE_PREFIX "bitmap2component"; break;
|
|
case FACE_PYTHON: name = KIFACE_PREFIX "kipython"; break;
|
|
|
|
default:
|
|
wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
|
|
return wxEmptyString;
|
|
}
|
|
|
|
#ifndef __WXMAC__
|
|
wxString path;
|
|
|
|
if( m_ctl & (KFCTL_STANDALONE | KFCTL_CPP_PROJECT_SUITE) )
|
|
{
|
|
// The 2 *.cpp program launchers: single_top.cpp and kicad.cpp expect
|
|
// the *.kiface's to reside in same directory as their binaries do.
|
|
path = wxStandardPaths::Get().GetExecutablePath();
|
|
}
|
|
|
|
wxFileName fn = path;
|
|
#else
|
|
// we have the dso's in main OSX bundle kicad.app/Contents/PlugIns
|
|
wxFileName fn = Pgm().GetExecutablePath();
|
|
fn.AppendDir( wxT( "Contents" ) );
|
|
fn.AppendDir( wxT( "PlugIns" ) );
|
|
#endif
|
|
|
|
fn.SetName( name );
|
|
|
|
// To speed up development, it's sometimes nice to run kicad from inside
|
|
// the build path. In that case, each program will be in a subdirectory.
|
|
// To find the DSOs, we need to go up one directory and then enter a subdirectory.
|
|
|
|
if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
|
|
{
|
|
#ifdef __WXMAC__
|
|
// On Mac, all of the kifaces are placed in the kicad.app bundle, even though the individual
|
|
// standalone binaries are placed in separate bundles before the make install step runs.
|
|
// So, we have to jump up to the kicad directory, then the PlugIns section of the kicad
|
|
// bundle.
|
|
fn = wxStandardPaths::Get().GetExecutablePath();
|
|
|
|
fn.RemoveLastDir();
|
|
fn.RemoveLastDir();
|
|
fn.RemoveLastDir();
|
|
fn.RemoveLastDir();
|
|
fn.AppendDir( wxT( "kicad" ) );
|
|
fn.AppendDir( wxT( "kicad.app" ) );
|
|
fn.AppendDir( wxT( "Contents" ) );
|
|
fn.AppendDir( wxT( "PlugIns" ) );
|
|
fn.SetName( name );
|
|
#else
|
|
const char* dirName;
|
|
|
|
// The subdirectories usually have the same name as the kiface
|
|
switch( aFaceId )
|
|
{
|
|
case FACE_PL_EDITOR: dirName = "pagelayout_editor"; break;
|
|
case FACE_PYTHON: dirName = "scripting"; break;
|
|
default: dirName = name + 1; break;
|
|
}
|
|
|
|
fn.RemoveLastDir();
|
|
fn.AppendDir( dirName );
|
|
#endif
|
|
}
|
|
|
|
// Here a "suffix" == an extension with a preceding '.',
|
|
// so skip the preceding '.' to get an extension
|
|
fn.SetExt( &KIFACE_SUFFIX[1] );
|
|
|
|
return fn.GetFullPath();
|
|
}
|
|
|
|
|
|
PROJECT& KIWAY::Prj() const
|
|
{
|
|
return Pgm().GetSettingsManager().Prj();
|
|
}
|
|
|
|
|
|
KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad )
|
|
{
|
|
// Since this will be called from python, cannot assume that code will
|
|
// not pass a bad aFaceId.
|
|
if( (unsigned) aFaceId >= arrayDim( m_kiface ) )
|
|
{
|
|
// @todo : throw an exception here for python's benefit, at least that
|
|
// way it gets some explanatory text.
|
|
|
|
wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
|
|
return nullptr;
|
|
}
|
|
|
|
// return the previously loaded KIFACE, if it was.
|
|
if( m_kiface[aFaceId] )
|
|
return m_kiface[aFaceId];
|
|
|
|
// DSO with KIFACE has not been loaded yet, does caller want to load it?
|
|
if( doLoad )
|
|
{
|
|
wxString dname = dso_search_path( aFaceId );
|
|
|
|
// Insert DLL search path for kicad_3dsg from build dir
|
|
if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
|
|
{
|
|
wxFileName myPath = wxStandardPaths::Get().GetExecutablePath();
|
|
|
|
if( !myPath.GetPath().EndsWith( wxT( "pcbnew" ) ) )
|
|
{
|
|
myPath.RemoveLastDir();
|
|
myPath.AppendDir( wxT( "pcbnew" ) );
|
|
KIPLATFORM::APP::AddDynamicLibrarySearchPath( myPath.GetPath() );
|
|
}
|
|
}
|
|
|
|
wxString msg;
|
|
|
|
#ifdef KICAD_WIN32_VERIFY_CODESIGN
|
|
bool codeSignOk = KIPLATFORM::ENV::VerifyFileSignature( dname );
|
|
if( !codeSignOk )
|
|
{
|
|
msg.Printf( _( "Failed to verify kiface library '%s' signature." ), dname );
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
#endif
|
|
|
|
wxDynamicLibrary dso;
|
|
|
|
void* addr = nullptr;
|
|
|
|
// For some reason wxDynamicLibrary::Load() crashes in some languages
|
|
// (chinese for instance) when loading the dynamic library.
|
|
// The crash happens for Eeschema.
|
|
// So switch to "C" locale during loading (LC_COLLATE is enough).
|
|
int lc_new_type = LC_COLLATE;
|
|
std::string user_locale = setlocale( lc_new_type, nullptr );
|
|
setlocale( lc_new_type, "C" );
|
|
|
|
bool success = dso.Load( dname, wxDL_VERBATIM | wxDL_NOW | wxDL_GLOBAL );
|
|
|
|
setlocale( lc_new_type, user_locale.c_str() );
|
|
|
|
#ifdef KICAD_USE_SENTRY
|
|
if( Pgm().IsSentryOptedIn() )
|
|
{
|
|
msg = wxString::Format( "Loading kiface %d", aFaceId );
|
|
sentry_value_t crumb = sentry_value_new_breadcrumb( "navigation", msg.utf8_str() );
|
|
sentry_value_set_by_key( crumb, "category", sentry_value_new_string( "kiway.kiface" ) );
|
|
sentry_value_set_by_key( crumb, "level", sentry_value_new_string( "info" ) );
|
|
sentry_add_breadcrumb( crumb );
|
|
}
|
|
#endif
|
|
|
|
if( !success )
|
|
{
|
|
// Failure: error reporting UI was done via wxLogSysError().
|
|
// No further reporting required here. Apparently this is not true on all
|
|
// platforms and/or wxWidgets builds and KiCad will crash. Throwing the exception
|
|
// here and catching it in the KiCad launcher resolves the crash issue. See bug
|
|
// report https://bugs.launchpad.net/kicad/+bug/1577786.
|
|
|
|
msg.Printf( _( "Failed to load kiface library '%s'." ), dname );
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == nullptr )
|
|
{
|
|
// Failure: error reporting UI was done via wxLogSysError().
|
|
// No further reporting required here. Assume the same thing applies here as
|
|
// above with the Load() call. This has not been tested.
|
|
msg.Printf( _( "Could not read instance name and version from kiface library '%s'." ),
|
|
dname );
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
else
|
|
{
|
|
KIFACE_GETTER_FUNC* ki_getter = (KIFACE_GETTER_FUNC*) addr;
|
|
|
|
KIFACE* kiface = ki_getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program );
|
|
|
|
// KIFACE_GETTER_FUNC function comment (API) says the non-NULL is unconditional.
|
|
wxASSERT_MSG( kiface,
|
|
wxT( "attempted DSO has a bug, failed to return a KIFACE*" ) );
|
|
|
|
wxDllType dsoHandle = dso.Detach();
|
|
|
|
bool startSuccess = false;
|
|
|
|
// Give the DSO a single chance to do its "process level" initialization.
|
|
// "Process level" specifically means stay away from any projects in there.
|
|
|
|
try
|
|
{
|
|
startSuccess = kiface->OnKifaceStart( m_program, m_ctl );
|
|
}
|
|
catch (...)
|
|
{
|
|
// OnKiFaceStart may generate an exception
|
|
// Before we continue and ultimately unload our module to retry we need
|
|
// to process the exception before we delete the free the memory space the exception resides in
|
|
Pgm().HandleException( std::current_exception() );
|
|
}
|
|
|
|
if( startSuccess )
|
|
{
|
|
return m_kiface[aFaceId] = kiface;
|
|
}
|
|
else
|
|
{
|
|
// Usually means cancelled initial global library setup
|
|
// But it could have been an exception/failure
|
|
// Let the module go out of scope to unload
|
|
dso.Attach( dsoHandle );
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// In any of the failure cases above, dso.Unload() should be called here
|
|
// by dso destructor.
|
|
// However:
|
|
|
|
// There is a file installation bug. We only look for KIFACE's which we know
|
|
// to exist, and we did not find one. If we do not find one, this is an
|
|
// installation bug.
|
|
|
|
msg = wxString::Format( _( "Fatal Installation Bug. File:\n"
|
|
"'%s'\ncould not be loaded\n" ), dname );
|
|
|
|
if( ! wxFileExists( dname ) )
|
|
msg << _( "It is missing.\n" );
|
|
else
|
|
msg << _( "Perhaps a shared library (.dll or .so) file is missing.\n" );
|
|
|
|
msg << _( "From command line: argv[0]:\n'" );
|
|
msg << wxStandardPaths::Get().GetExecutablePath() << wxT( "'\n" );
|
|
|
|
// This is a fatal error, one from which we cannot recover, nor do we want
|
|
// to protect against in client code which would require numerous noisy
|
|
// tests in numerous places. So we inform the user that the installation
|
|
// is bad. This exception will likely not get caught until way up in the
|
|
// wxApp derivative, at which point the process will exit gracefully.
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
KIWAY::FACE_T KIWAY::KifaceType( FRAME_T aFrameType )
|
|
{
|
|
switch( aFrameType )
|
|
{
|
|
case FRAME_SCH:
|
|
case FRAME_SCH_SYMBOL_EDITOR:
|
|
case FRAME_SCH_VIEWER:
|
|
case FRAME_SCH_VIEWER_MODAL:
|
|
case FRAME_SIMULATOR:
|
|
return FACE_SCH;
|
|
|
|
case FRAME_PCB_EDITOR:
|
|
case FRAME_FOOTPRINT_EDITOR:
|
|
case FRAME_FOOTPRINT_VIEWER:
|
|
case FRAME_FOOTPRINT_VIEWER_MODAL:
|
|
case FRAME_FOOTPRINT_WIZARD:
|
|
case FRAME_PCB_DISPLAY3D:
|
|
return FACE_PCB;
|
|
|
|
case FRAME_CVPCB:
|
|
case FRAME_CVPCB_DISPLAY:
|
|
return FACE_CVPCB;
|
|
|
|
case FRAME_PYTHON:
|
|
return FACE_PYTHON;
|
|
|
|
case FRAME_GERBER:
|
|
return FACE_GERBVIEW;
|
|
|
|
case FRAME_PL_EDITOR:
|
|
return FACE_PL_EDITOR;
|
|
|
|
case FRAME_CALC:
|
|
return FACE_PCB_CALCULATOR;
|
|
|
|
case FRAME_BM2CMP:
|
|
return FACE_BMP2CMP;
|
|
|
|
default:
|
|
return FACE_T( -1 );
|
|
}
|
|
}
|
|
|
|
|
|
KIWAY_PLAYER* KIWAY::GetPlayerFrame( FRAME_T aFrameType )
|
|
{
|
|
wxWindowID storedId = m_playerFrameId[aFrameType];
|
|
|
|
if( storedId == wxID_NONE )
|
|
return nullptr;
|
|
|
|
wxWindow* frame = wxWindow::FindWindowById( storedId );
|
|
|
|
// Since wxWindow::FindWindow*() is not cheap (especially if the window does not exist),
|
|
// clear invalid entries to save CPU on repeated calls that do not lead to frame creation
|
|
if( !frame )
|
|
m_playerFrameId[aFrameType].compare_exchange_strong( storedId, wxID_NONE );
|
|
|
|
return static_cast<KIWAY_PLAYER*>( frame );
|
|
}
|
|
|
|
|
|
KIWAY_PLAYER* KIWAY::Player( FRAME_T aFrameType, bool doCreate, wxTopLevelWindow* aParent )
|
|
{
|
|
// Since this will be called from python, cannot assume that code will
|
|
// not pass a bad aFrameType.
|
|
if( (unsigned) aFrameType >= KIWAY_PLAYER_COUNT )
|
|
{
|
|
// @todo : throw an exception here for python's benefit, at least that
|
|
// way it gets some explanatory text.
|
|
|
|
wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
|
|
return nullptr;
|
|
}
|
|
|
|
// return the previously opened window
|
|
KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
|
|
|
|
if( frame )
|
|
return frame;
|
|
|
|
if( doCreate )
|
|
{
|
|
try
|
|
{
|
|
#ifdef KICAD_USE_SENTRY
|
|
if( Pgm().IsSentryOptedIn() )
|
|
{
|
|
wxString msg = wxString::Format( "Creating window type %d", aFrameType );
|
|
sentry_value_t crumb = sentry_value_new_breadcrumb( "navigation", msg.utf8_str() );
|
|
sentry_value_set_by_key( crumb, "category",
|
|
sentry_value_new_string( "kiway.player" ) );
|
|
sentry_value_set_by_key( crumb, "level", sentry_value_new_string( "info" ) );
|
|
sentry_add_breadcrumb( crumb );
|
|
}
|
|
#endif
|
|
|
|
FACE_T face_type = KifaceType( aFrameType );
|
|
KIFACE* kiface = KiFACE( face_type );
|
|
|
|
if( !kiface )
|
|
return nullptr;
|
|
|
|
frame = (KIWAY_PLAYER*) kiface->CreateKiWindow(
|
|
aParent, // Parent window of frame in modal mode,
|
|
// NULL in non modal mode
|
|
aFrameType,
|
|
this,
|
|
m_ctl // questionable need, these same flags
|
|
// were passed to KIFACE::OnKifaceStart()
|
|
);
|
|
|
|
m_playerFrameId[aFrameType].store( frame->GetId() );
|
|
return frame;
|
|
}
|
|
catch( ... )
|
|
{
|
|
Pgm().HandleException( std::current_exception() );
|
|
wxLogError( _( "Error loading editor." ) );
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
bool KIWAY::PlayerClose( FRAME_T aFrameType, bool doForce )
|
|
{
|
|
// Since this will be called from python, cannot assume that code will
|
|
// not pass a bad aFrameType.
|
|
if( (unsigned) aFrameType >= KIWAY_PLAYER_COUNT )
|
|
{
|
|
// @todo : throw an exception here for python's benefit, at least that
|
|
// way it gets some explanatory text.
|
|
|
|
wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
|
|
return false;
|
|
}
|
|
|
|
KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
|
|
|
|
if( frame == nullptr ) // Already closed
|
|
return true;
|
|
|
|
#ifdef KICAD_USE_SENTRY
|
|
if( Pgm().IsSentryOptedIn() )
|
|
{
|
|
wxString msg = wxString::Format( "Closing window type %d", aFrameType );
|
|
sentry_value_t crumb = sentry_value_new_breadcrumb( "navigation", msg.utf8_str() );
|
|
sentry_value_set_by_key( crumb, "category",
|
|
sentry_value_new_string( "kiway.playerclose" ) );
|
|
sentry_value_set_by_key( crumb, "level", sentry_value_new_string( "info" ) );
|
|
sentry_add_breadcrumb( crumb );
|
|
}
|
|
#endif
|
|
|
|
if( frame->NonUserClose( doForce ) )
|
|
{
|
|
m_playerFrameId[aFrameType] = wxID_NONE;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool KIWAY::PlayersClose( bool doForce )
|
|
{
|
|
bool ret = true;
|
|
|
|
for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
|
|
ret = ret && PlayerClose( FRAME_T( i ), doForce );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void KIWAY::PlayerDidClose( FRAME_T aFrameType )
|
|
{
|
|
m_playerFrameId[aFrameType] = wxID_NONE;
|
|
}
|
|
|
|
|
|
void KIWAY::ExpressMail( FRAME_T aDestination, MAIL_T aCommand, std::string& aPayload,
|
|
wxWindow* aSource )
|
|
{
|
|
KIWAY_EXPRESS mail( aDestination, aCommand, aPayload, aSource );
|
|
|
|
ProcessEvent( mail );
|
|
}
|
|
|
|
|
|
void KIWAY::GetActions( std::vector<TOOL_ACTION*>& aActions ) const
|
|
{
|
|
for( TOOL_ACTION* action : ACTION_MANAGER::GetActionList() )
|
|
aActions.push_back( action );
|
|
}
|
|
|
|
|
|
void KIWAY::SetLanguage( int aLanguage )
|
|
{
|
|
wxString errMsg;
|
|
bool ret = false;
|
|
|
|
{
|
|
// Only allow the traces to be logged by wx. We use our own system to log when the
|
|
// OS doesn't support the language, so we want to hide the wx error.
|
|
WX_LOG_TRACE_ONLY logtraceOnly;
|
|
Pgm().SetLanguageIdentifier( aLanguage );
|
|
ret = Pgm().SetLanguage( errMsg );
|
|
}
|
|
|
|
if( !ret )
|
|
{
|
|
wxString lang;
|
|
|
|
for( unsigned ii = 0; LanguagesList[ii].m_KI_Lang_Identifier != 0; ii++ )
|
|
{
|
|
if( aLanguage == LanguagesList[ii].m_KI_Lang_Identifier )
|
|
{
|
|
if( LanguagesList[ii].m_DoNotTranslate )
|
|
lang = LanguagesList[ii].m_Lang_Label;
|
|
else
|
|
lang = wxGetTranslation( LanguagesList[ii].m_Lang_Label );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
DisplayErrorMessage( nullptr,
|
|
wxString::Format( _( "Unable to switch language to %s" ), lang ),
|
|
errMsg );
|
|
return;
|
|
}
|
|
|
|
#if 1
|
|
// This is a risky hack that goes away if we allow the language to be
|
|
// set only from the top most frame if !Kiface.IsSingle()
|
|
|
|
// Only for the C++ project manager, and not for the python one and not for
|
|
// single_top do we look for the EDA_BASE_FRAME as the top level window.
|
|
// For single_top this is not needed because that window is registered in
|
|
// the array below.
|
|
if( m_ctl & KFCTL_CPP_PROJECT_SUITE )
|
|
{
|
|
// A dynamic_cast could be better, but creates link issues
|
|
// (some basic_frame functions not found) on some platforms,
|
|
// so a static_cast is used.
|
|
EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
|
|
|
|
if( top )
|
|
top->ShowChangedLanguage();
|
|
}
|
|
#endif
|
|
|
|
for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
|
|
{
|
|
KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
|
|
|
|
if( frame )
|
|
{
|
|
frame->ShowChangedLanguage();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
|
|
{
|
|
if( m_ctl & KFCTL_CPP_PROJECT_SUITE )
|
|
{
|
|
// A dynamic_cast could be better, but creates link issues
|
|
// (some basic_frame functions not found) on some platforms,
|
|
// so a static_cast is used.
|
|
EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
|
|
|
|
if( top )
|
|
top->CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
|
|
}
|
|
|
|
for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
|
|
{
|
|
KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
|
|
|
|
if( frame )
|
|
frame->CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
|
|
}
|
|
}
|
|
|
|
|
|
void KIWAY::ProjectChanged()
|
|
{
|
|
#ifdef KICAD_USE_SENTRY
|
|
sentry_value_t crumb = sentry_value_new_breadcrumb( "navigation", "Changing project" );
|
|
sentry_value_set_by_key( crumb, "category", sentry_value_new_string( "kiway.projectchanged" ) );
|
|
sentry_value_set_by_key( crumb, "level", sentry_value_new_string( "info" ) );
|
|
sentry_add_breadcrumb( crumb );
|
|
#endif
|
|
|
|
if( m_ctl & KFCTL_CPP_PROJECT_SUITE )
|
|
{
|
|
// A dynamic_cast could be better, but creates link issues
|
|
// (some basic_frame functions not found) on some platforms,
|
|
// so a static_cast is used.
|
|
EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
|
|
|
|
if( top )
|
|
top->ProjectChanged();
|
|
}
|
|
|
|
for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
|
|
{
|
|
KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
|
|
|
|
if( frame )
|
|
frame->ProjectChanged();
|
|
}
|
|
}
|
|
|
|
wxWindow* KIWAY::GetBlockingDialog()
|
|
{
|
|
return wxWindow::FindWindowById( m_blockingDialog );
|
|
}
|
|
|
|
void KIWAY::SetBlockingDialog( wxWindow* aWin )
|
|
{
|
|
if( !aWin )
|
|
m_blockingDialog = wxID_NONE;
|
|
else
|
|
m_blockingDialog = aWin->GetId();
|
|
}
|
|
|
|
|
|
bool KIWAY::ProcessEvent( wxEvent& aEvent )
|
|
{
|
|
KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent );
|
|
|
|
if( mail )
|
|
{
|
|
FRAME_T dest = mail->Dest();
|
|
|
|
// see if recipient is alive
|
|
KIWAY_PLAYER* alive = Player( dest, false );
|
|
|
|
if( alive )
|
|
{
|
|
#if 1
|
|
return alive->ProcessEvent( aEvent );
|
|
#else
|
|
alive->KiwayMailIn( *mail );
|
|
return true;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int KIWAY::ProcessJob( KIWAY::FACE_T aFace, JOB* job )
|
|
{
|
|
KIFACE* kiface = KiFACE( aFace );
|
|
|
|
return kiface->HandleJob( job );
|
|
}
|
|
|
|
|
|
void KIWAY::OnKiCadExit()
|
|
{
|
|
if( m_ctl & KFCTL_CPP_PROJECT_SUITE )
|
|
{
|
|
// A dynamic_cast could be better, but creates link issues
|
|
// (some basic_frame functions not found) on some platforms,
|
|
// so a static_cast is used.
|
|
EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
|
|
|
|
if( top )
|
|
top->Close( false );
|
|
}
|
|
}
|
|
|
|
|
|
void KIWAY::OnKiwayEnd()
|
|
{
|
|
for( KIFACE* i : m_kiface )
|
|
{
|
|
if( i )
|
|
i->OnKifaceEnd();
|
|
}
|
|
}
|