diff options
Diffstat (limited to 'common/kiway.cpp')
-rw-r--r-- | common/kiway.cpp | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/common/kiway.cpp b/common/kiway.cpp new file mode 100644 index 0000000..7919b9c --- /dev/null +++ b/common/kiway.cpp @@ -0,0 +1,432 @@ +/* + * 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 KiCad Developers, see CHANGELOG.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 <string.h> + +#include <macros.h> +#include <kiway.h> +#include <kiway_player.h> +#include <kiway_express.h> +#include <pgm_base.h> +#include <config.h> +#include <id.h> + +#include <wx/stdpaths.h> +#include <wx/debug.h> + + +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( 0 ) +{ + SetTop( aTop ); // hook player_destroy_handler() into aTop. + + memset( m_player, 0, sizeof( m_player ) ); +} + + +// Any event types derived from wxCommandEvt, like wxWindowDestroyEvent, are +// propogated 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 ) +{ + wxWindow* w = event.GetWindow(); + + for( unsigned i=0; i<DIM(m_player); ++i ) + { + // if destroying one of our flock, then mark it as deceased. + if( (wxWindow*) m_player[i] == w ) + { + DBG(printf( "%s: m_player[%u] destroyed: %s\n", + __func__, i, TO_UTF8( m_player[i]->GetName() ) );) + + m_player[i] = 0; + } + } + + event.Skip(); // skip to who, the wxApp? I'm the top window. +} + + +void KIWAY::SetTop( wxFrame* aTop ) +{ + if( m_top ) + { + m_top->Disconnect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this ); + } + + if( aTop ) + { + aTop->Connect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this ); + } + + m_top = aTop; +} + + +const wxString KIWAY::dso_full_path( FACE_T aFaceId ) +{ + const wxChar* name; + + switch( aFaceId ) + { + case FACE_SCH: name = KIFACE_PREFIX wxT( "eeschema" ); break; + case FACE_PCB: name = KIFACE_PREFIX wxT( "pcbnew" ); break; + case FACE_CVPCB: name = KIFACE_PREFIX wxT( "cvpcb" ); break; + case FACE_GERBVIEW: name = KIFACE_PREFIX wxT( "gerbview" ); break; + case FACE_PL_EDITOR: name = KIFACE_PREFIX wxT( "pl_editor" ); break; + case FACE_PCB_CALCULATOR: name = KIFACE_PREFIX wxT( "pcb_calculator" ); break; + case FACE_BMP2CMP: name = KIFACE_PREFIX wxT( "bitmap2component" ); break; + + default: + wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) ); + return wxEmptyString; + } + +#ifndef __WXMAC__ + wxFileName fn = wxStandardPaths::Get().GetExecutablePath(); +#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 ); + + // Here a "suffix" == an extension with a preceding '.', + // so skip the preceding '.' to get an extension + fn.SetExt( KIFACE_SUFFIX + 1 ); // + 1 => &KIFACE_SUFFIX[1] + + return fn.GetFullPath(); +} + + +PROJECT& KIWAY::Prj() const +{ + return *(PROJECT*) &m_project; // strip const-ness, function really is const. +} + + +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 ) >= DIM( 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 NULL; + } + + // 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_full_path( aFaceId ); + + wxDynamicLibrary dso; + + void* addr = NULL; + + if( !dso.Load( dname, wxDL_VERBATIM | wxDL_NOW | wxDL_GLOBAL ) ) + { + // Failure: error reporting UI was done via wxLogSysError(). + // No further reporting required here. + } + + else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == NULL ) + { + // Failure: error reporting UI was done via wxLogSysError(). + // No further reporting required here. + } + + else + { + KIFACE_GETTER_FUNC* getter = (KIFACE_GETTER_FUNC*) addr; + + KIFACE* kiface = 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*" ) ); + + // Give the DSO a single chance to do its "process level" initialization. + // "Process level" specifically means stay away from any projects in there. + if( kiface->OnKifaceStart( m_program, m_ctl ) ) + { + // Tell dso's wxDynamicLibrary destructor not to Unload() the program image. + (void) dso.Detach(); + + return m_kiface[aFaceId] = kiface; + } + } + + // 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_I's which we know + // to exist, and we did not find one. If we do not find one, this is an + // installation bug. + + wxString msg = wxString::Format( wxT( + "Fatal Installation Bug. File:\n" + "'%s'\ncould not be loaded\n" ), GetChars( dname ) ); + + if( ! wxFileExists( dname ) ) + msg << wxT( "It is missing.\n" ); + else + msg << wxT( "Perhaps a shared library (.dll or .so) file is missing.\n" ); + + msg << wxT( "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 NULL; +} + + +KIWAY::FACE_T KIWAY::KifaceType( FRAME_T aFrameType ) +{ + switch( aFrameType ) + { + case FRAME_SCH: + case FRAME_SCH_LIB_EDITOR: + case FRAME_SCH_VIEWER: + case FRAME_SCH_VIEWER_MODAL: + return FACE_SCH; + + case FRAME_PCB: + case FRAME_PCB_MODULE_EDITOR: + case FRAME_PCB_MODULE_VIEWER: + case FRAME_PCB_MODULE_VIEWER_MODAL: + case FRAME_PCB_FOOTPRINT_WIZARD_MODAL: + case FRAME_PCB_DISPLAY3D: + return FACE_PCB; + + case FRAME_CVPCB: + case FRAME_CVPCB_DISPLAY: + return FACE_CVPCB; + + 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::Player( FRAME_T aFrameType, bool doCreate ) +{ + // Since this will be called from python, cannot assume that code will + // not pass a bad aFrameType. + if( unsigned( aFrameType ) >= DIM( m_player ) ) + { + // @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 NULL; + } + + // return the previously opened window + if( m_player[aFrameType] ) + return m_player[aFrameType]; + + if( doCreate ) + { + FACE_T face_type = KifaceType( aFrameType ); + wxASSERT( face_type != FACE_T(-1) ); + + KIFACE* kiface = KiFACE( face_type ); + wxASSERT( kiface ); + + if( kiface ) + { + KIWAY_PLAYER* frame = (KIWAY_PLAYER*) kiface->CreateWindow( + m_top, + aFrameType, + this, + m_ctl // questionable need, these same flags where passed to the KIFACE::OnKifaceStart() + ); + wxASSERT( frame ); + + return m_player[aFrameType] = frame; + } + } + + return NULL; +} + + +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 ) >= DIM( m_player ) ) + { + // @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; + } + + if( m_player[aFrameType] ) + { + if( m_player[aFrameType]->Close( doForce ) ) + { + m_player[aFrameType] = 0; + return true; + } + + return false; + } + + return true; // window is closed already. +} + + +bool KIWAY::PlayersClose( bool doForce ) +{ + bool ret = true; + + for( unsigned i=0; i < DIM( m_player ); ++i ) + { + ret = ret && PlayerClose( FRAME_T( i ), doForce ); + } + + return ret; +} + + +void KIWAY::ExpressMail( FRAME_T aDestination, + MAIL_T aCommand, const std::string& aPayload, wxWindow* aSource ) +{ + KIWAY_EXPRESS mail( aDestination, aCommand, aPayload, aSource ); + + ProcessEvent( mail ); +} + + +void KIWAY::SetLanguage( int aLanguage ) +{ + Pgm().SetLanguageIdentifier( aLanguage ); + Pgm().SetLanguage(); + +#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 ) + { + EDA_BASE_FRAME* top = (EDA_BASE_FRAME*) m_top; + if( top ) + top->ShowChangedLanguage(); + } +#endif + + for( unsigned i=0; i < DIM( m_player ); ++i ) + { + KIWAY_PLAYER* frame = m_player[i]; + + if( frame ) + { + frame->ShowChangedLanguage(); + } + } +} + + +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; +} + + +void KIWAY::OnKiwayEnd() +{ + for( unsigned i=0; i < DIM( m_kiface ); ++i ) + { + if( m_kiface[i] ) + m_kiface[i]->OnKifaceEnd(); + } +} + |