/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck * 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 #include #include #include #include #include #include #include #include #include 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 < KIWAY_PLAYER_COUNT; ++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::GetPlayerFrame( FRAME_T aFrameType ) { if( unsigned( aFrameType ) >= KIWAY_PLAYER_COUNT ) return NULL; return m_player[aFrameType]; } 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 ) >= 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 NULL; } // return the previously opened window KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType ); if( frame ) return frame; if( doCreate ) { FACE_T face_type = KifaceType( aFrameType ); wxASSERT( face_type != FACE_T(-1) ); KIFACE* kiface = KiFACE( face_type ); wxASSERT( kiface ); if( kiface ) { 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 ) >= 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 == NULL ) // Already closed return true; if( frame->Close( doForce ) ) { m_player[aFrameType] = 0; 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::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 ) { // 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( 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(); } } } bool KIWAY::ProcessEvent( wxEvent& aEvent ) { KIWAY_EXPRESS* mail = dynamic_cast( &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(); } }