KiCad PCB EDA Suite
kiway.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2014-2017 KiCad Developers, see CHANGELOG.TXT for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <cstring>
26 
27 #include <macros.h>
28 #include <kiway.h>
29 #include <kiway_player.h>
30 #include <kiway_express.h>
31 #include <pgm_base.h>
32 #include <config.h>
33 #include <id.h>
35 
36 #include <wx/stdpaths.h>
37 #include <wx/debug.h>
38 #include <wx/utils.h>
39 
40 
41 KIFACE* KIWAY::m_kiface[KIWAY_FACE_COUNT];
42 int KIWAY::m_kiface_version[KIWAY_FACE_COUNT];
43 
44 
45 
46 KIWAY::KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop ):
47  m_program( aProgram ), m_ctl( aCtlBits ), m_top( 0 )
48 {
49  SetTop( aTop ); // hook player_destroy_handler() into aTop.
50 
51 
52  // Prepare the room to store the frame names, once they will be created
53  // with FRAME_T type as index in this table.
54  // (note this is a list of frame names, but a non empty entry
55  // does not mean the frame still exists. It means only the frame was created
56  // at least once. It can be destroyed after. These entries are not cleared.
57  // the purpose is just to allow a call to wxWindow::FindWindowByName(), from
58  // a FRAME_T frame type
59  m_playerFrameName.Add( wxEmptyString, KIWAY_PLAYER_COUNT );
60 }
61 
62 
63 #if 0
64 // Any event types derived from wxCommandEvt, like wxWindowDestroyEvent, are
65 // propagated upwards to parent windows if not handled below. Therefore the
66 // m_top window should receive all wxWindowDestroyEvents originating from
67 // KIWAY_PLAYERs. It does anyways, but now player_destroy_handler eavesdrops
68 // on that event stream looking for KIWAY_PLAYERs being closed.
69 
70 void KIWAY::player_destroy_handler( wxWindowDestroyEvent& event )
71 {
72  // Currently : do nothing
73  event.Skip(); // skip to who, the wxApp? I'm the top window.
74 }
75 #endif
76 
77 
78 void KIWAY::SetTop( wxFrame* aTop )
79 {
80 #if 0
81  if( m_top )
82  {
83  m_top->Disconnect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this );
84  }
85 
86  if( aTop )
87  {
88  aTop->Connect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this );
89  }
90 #endif
91 
92  m_top = aTop;
93 }
94 
95 
96 const wxString KIWAY::dso_search_path( FACE_T aFaceId )
97 {
98  const char* name;
99 
100  switch( aFaceId )
101  {
102  case FACE_SCH: name = KIFACE_PREFIX "eeschema"; break;
103  case FACE_PCB: name = KIFACE_PREFIX "pcbnew"; break;
104  case FACE_CVPCB: name = KIFACE_PREFIX "cvpcb"; break;
105  case FACE_GERBVIEW: name = KIFACE_PREFIX "gerbview"; break;
106  case FACE_PL_EDITOR: name = KIFACE_PREFIX "pl_editor"; break;
107  case FACE_PCB_CALCULATOR: name = KIFACE_PREFIX "pcb_calculator"; break;
108  case FACE_BMP2CMP: name = KIFACE_PREFIX "bitmap2component"; break;
109 
110  default:
111  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
112  return wxEmptyString;
113  }
114 
115 #ifndef __WXMAC__
116  wxString path;
117 
119  {
120  // The 2 *.cpp program launchers: single_top.cpp and kicad.cpp expect
121  // the *.kiface's to reside in same directory as their binaries do.
122  // Not so for python launcher, identified by KFCTL_PY_PROJECT_SUITE
123  path = wxStandardPaths::Get().GetExecutablePath();
124  }
125 
126  wxFileName fn = path;
127 #else
128  // we have the dso's in main OSX bundle kicad.app/Contents/PlugIns
129  wxFileName fn = Pgm().GetExecutablePath();
130  fn.AppendDir( wxT( "Contents" ) );
131  fn.AppendDir( wxT( "PlugIns" ) );
132 #endif
133 
134  fn.SetName( name );
135 
136 #ifdef DEBUG
137  // To speed up development, it's sometimes nice to run kicad from inside
138  // the build path. In that case, each program will be in a subdirectory.
139  // To find the DSOs, we need to go up one directory and then enter a subdirectory.
140 
141  if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
142  {
143 #ifdef __WXMAC__
144  fn = wxStandardPaths::Get().GetExecutablePath();
145  fn.RemoveLastDir();
146  fn.AppendDir( wxT( "PlugIns" ) );
147  fn.SetName( name );
148 #else
149  const char* dirName;
150 
151  // The subdirectories usually have the same name as the kiface
152  switch( aFaceId )
153  {
154  case FACE_PL_EDITOR: dirName = "pagelayout_editor"; break;
155  default: dirName = name + 1; break;
156  }
157 
158  fn.RemoveLastDir();
159  fn.AppendDir( dirName );
160 #endif
161  }
162 #endif
163 
164  // Here a "suffix" == an extension with a preceding '.',
165  // so skip the preceding '.' to get an extension
166  fn.SetExt( KIFACE_SUFFIX + 1 ); // + 1 => &KIFACE_SUFFIX[1]
167 
168  return fn.GetFullPath();
169 }
170 
171 
173 {
174  return Pgm().GetSettingsManager().Prj();
175 }
176 
177 
178 KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad )
179 {
180  // Since this will be called from python, cannot assume that code will
181  // not pass a bad aFaceId.
182  if( (unsigned) aFaceId >= arrayDim( m_kiface ) )
183  {
184  // @todo : throw an exception here for python's benefit, at least that
185  // way it gets some explanatory text.
186 
187  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
188  return NULL;
189  }
190 
191  // return the previously loaded KIFACE, if it was.
192  if( m_kiface[aFaceId] )
193  return m_kiface[aFaceId];
194 
195  wxString msg;
196 
197  // DSO with KIFACE has not been loaded yet, does caller want to load it?
198  if( doLoad )
199  {
200  wxString dname = dso_search_path( aFaceId );
201 
202  wxDynamicLibrary dso;
203 
204  void* addr = NULL;
205 
206  // For some reason wxDynamicLibrary::Load() crashes in some languages
207  // (chinese for instance) when loading the dynamic library.
208  // The crash happens for Eeschema.
209  // So switch to "C" locale during loading (LC_COLLATE is enough).
210  int lc_new_type = LC_COLLATE;
211  std::string user_locale = setlocale( lc_new_type, nullptr );
212  setlocale( lc_new_type, "C" );
213 
214  bool success = dso.Load( dname, wxDL_VERBATIM | wxDL_NOW | wxDL_GLOBAL );
215 
216  setlocale( lc_new_type, user_locale.c_str() );
217 
218  if( !success )
219  {
220  // Failure: error reporting UI was done via wxLogSysError().
221  // No further reporting required here. Apparently this is not true on all
222  // platforms and/or wxWidgets builds and KiCad will crash. Throwing the exception
223  // here and catching it in the KiCad launcher resolves the crash issue. See bug
224  // report https://bugs.launchpad.net/kicad/+bug/1577786.
225 
226  msg.Printf( _( "Failed to load kiface library \"%s\"." ), dname );
227  THROW_IO_ERROR( msg );
228  }
229  else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == NULL )
230  {
231  // Failure: error reporting UI was done via wxLogSysError().
232  // No further reporting required here. Assume the same thing applies here as
233  // above with the Load() call. This has not been tested.
234  msg.Printf(
235  _( "Could not read instance name and version symbol form kiface library \"%s\"." ),
236  dname );
237  THROW_IO_ERROR( msg );
238  }
239  else
240  {
241  KIFACE_GETTER_FUNC* getter = (KIFACE_GETTER_FUNC*) addr;
242 
243  KIFACE* kiface = getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program );
244 
245  // KIFACE_GETTER_FUNC function comment (API) says the non-NULL is unconditional.
246  wxASSERT_MSG( kiface,
247  wxT( "attempted DSO has a bug, failed to return a KIFACE*" ) );
248 
249  // Give the DSO a single chance to do its "process level" initialization.
250  // "Process level" specifically means stay away from any projects in there.
251  if( kiface->OnKifaceStart( m_program, m_ctl ) )
252  {
253  // Tell dso's wxDynamicLibrary destructor not to Unload() the program image.
254  (void) dso.Detach();
255 
256  return m_kiface[aFaceId] = kiface;
257  }
258  }
259 
260  // In any of the failure cases above, dso.Unload() should be called here
261  // by dso destructor.
262  // However:
263 
264  // There is a file installation bug. We only look for KIFACE_I's which we know
265  // to exist, and we did not find one. If we do not find one, this is an
266  // installation bug.
267 
268  msg = wxString::Format( _(
269  "Fatal Installation Bug. File:\n"
270  "\"%s\"\ncould not be loaded\n" ), dname );
271 
272  if( ! wxFileExists( dname ) )
273  msg << _( "It is missing.\n" );
274  else
275  msg << _( "Perhaps a shared library (.dll or .so) file is missing.\n" );
276 
277  msg << _( "From command line: argv[0]:\n'" );
278  msg << wxStandardPaths::Get().GetExecutablePath() << wxT( "'\n" );
279 
280  // This is a fatal error, one from which we cannot recover, nor do we want
281  // to protect against in client code which would require numerous noisy
282  // tests in numerous places. So we inform the user that the installation
283  // is bad. This exception will likely not get caught until way up in the
284  // wxApp derivative, at which point the process will exit gracefully.
285  THROW_IO_ERROR( msg );
286  }
287 
288  return NULL;
289 }
290 
291 
293 {
294  switch( aFrameType )
295  {
296  case FRAME_SCH:
298  case FRAME_SCH_VIEWER:
300  case FRAME_SIMULATOR:
301  return FACE_SCH;
302 
303  case FRAME_PCB_EDITOR:
308  case FRAME_PCB_DISPLAY3D:
309  return FACE_PCB;
310 
311  case FRAME_CVPCB:
312  case FRAME_CVPCB_DISPLAY:
313  return FACE_CVPCB;
314 
315  case FRAME_GERBER:
316  return FACE_GERBVIEW;
317 
318  case FRAME_PL_EDITOR:
319  return FACE_PL_EDITOR;
320 
321  case FRAME_CALC:
322  return FACE_PCB_CALCULATOR;
323 
324  case FRAME_BM2CMP:
325  return FACE_BMP2CMP;
326 
327  default:
328  return FACE_T( -1 );
329  }
330 }
331 
332 
334 {
335  if( m_playerFrameName[aFrameType].IsEmpty() )
336  return NULL;
337 
338  return static_cast<KIWAY_PLAYER*>( wxWindow::FindWindowByName( m_playerFrameName[aFrameType] ) );
339 }
340 
341 
342 KIWAY_PLAYER* KIWAY::Player( FRAME_T aFrameType, bool doCreate, wxTopLevelWindow* aParent )
343 {
344  // Since this will be called from python, cannot assume that code will
345  // not pass a bad aFrameType.
346  if( (unsigned) aFrameType >= KIWAY_PLAYER_COUNT )
347  {
348  // @todo : throw an exception here for python's benefit, at least that
349  // way it gets some explanatory text.
350 
351  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
352  return NULL;
353  }
354 
355  // return the previously opened window
356  KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
357 
358  if( frame )
359  return frame;
360 
361  if( doCreate )
362  {
363  FACE_T face_type = KifaceType( aFrameType );
364  wxASSERT( face_type != FACE_T(-1) );
365 
366  KIFACE* kiface = KiFACE( face_type );
367  wxASSERT( kiface );
368 
369  if( kiface )
370  {
371  frame = (KIWAY_PLAYER*) kiface->CreateWindow(
372  aParent, // Parent window of frame in modal mode, NULL in non modal mode
373  aFrameType,
374  this,
375  m_ctl // questionable need, these same flags where passed
376  // to the KIFACE::OnKifaceStart()
377  );
378  wxASSERT( frame );
379 
380  m_playerFrameName[aFrameType] = frame->GetName();
381 
382  return frame;
383  }
384  }
385 
386  return NULL;
387 }
388 
389 
390 bool KIWAY::PlayerClose( FRAME_T aFrameType, bool doForce )
391 {
392  // Since this will be called from python, cannot assume that code will
393  // not pass a bad aFrameType.
394  if( (unsigned) aFrameType >= KIWAY_PLAYER_COUNT )
395  {
396  // @todo : throw an exception here for python's benefit, at least that
397  // way it gets some explanatory text.
398 
399  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
400  return false;
401  }
402 
403  KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
404 
405  if( frame == NULL ) // Already closed
406  return true;
407 
408  if( frame->Close( doForce ) )
409  return true;
410 
411  return false;
412 }
413 
414 
415 bool KIWAY::PlayersClose( bool doForce )
416 {
417  bool ret = true;
418 
419  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
420  {
421  ret = ret && PlayerClose( FRAME_T( i ), doForce );
422  }
423 
424  return ret;
425 }
426 
427 
428 void KIWAY::ExpressMail( FRAME_T aDestination, MAIL_T aCommand, std::string& aPayload, wxWindow* aSource )
429 {
430  KIWAY_EXPRESS mail( aDestination, aCommand, aPayload, aSource );
431 
432  ProcessEvent( mail );
433 }
434 
435 
436 void KIWAY::SetLanguage( int aLanguage )
437 {
438  Pgm().SetLanguageIdentifier( aLanguage );
439  Pgm().SetLanguage();
440 
441 #if 1
442  // This is a risky hack that goes away if we allow the language to be
443  // set only from the top most frame if !Kiface.IsSingle()
444 
445  // Only for the C++ project manager, and not for the python one and not for
446  // single_top do we look for the EDA_BASE_FRAME as the top level window.
447  // For single_top this is not needed because that window is registered in
448  // the array below.
450  {
451  // A dynamic_cast could be better, but creates link issues
452  // (some basic_frame functions not found) on some platforms,
453  // so a static_cast is used.
454  EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
455 
456  if( top )
457  top->ShowChangedLanguage();
458  }
459 #endif
460 
461  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
462  {
463  KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
464 
465  if( frame )
466  {
467  frame->ShowChangedLanguage();
468  }
469  }
470 }
471 
472 void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged )
473 {
474 #if 1
476  {
477  // A dynamic_cast could be better, but creates link issues
478  // (some basic_frame functions not found) on some platforms,
479  // so a static_cast is used.
480  EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
481 
482  if( top )
483  top->CommonSettingsChanged( aEnvVarsChanged );
484  }
485 #endif
486 
487  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
488  {
489  KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
490 
491  if( frame )
492  frame->CommonSettingsChanged( aEnvVarsChanged );
493  }
494 }
495 
496 
497 bool KIWAY::ProcessEvent( wxEvent& aEvent )
498 {
499  KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent );
500 
501  if( mail )
502  {
503  FRAME_T dest = mail->Dest();
504 
505  // see if recipient is alive
506  KIWAY_PLAYER* alive = Player( dest, false );
507 
508  if( alive )
509  {
510 #if 1
511  return alive->ProcessEvent( aEvent );
512 #else
513  alive->KiwayMailIn( *mail );
514  return true;
515 #endif
516  }
517  }
518 
519  return false;
520 }
521 
522 
524 {
526  {
527  // A dynamic_cast could be better, but creates link issues
528  // (some basic_frame functions not found) on some platforms,
529  // so a static_cast is used.
530  EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
531 
532  if( top )
533  top->Close( false );
534  }
535 }
536 
537 
539 {
540  for( KIFACE* i : m_kiface )
541  {
542  if( i )
543  i->OnKifaceEnd();
544  }
545 }
#define KFCTL_CPP_PROJECT_SUITE
Am running under C++ project mgr, possibly with others.
Definition: kiway.h:159
BITMAP2CMP_SETTINGS kiface
VTBL_ENTRY bool PlayersClose(bool doForce)
Function PlayersClose calls the KIWAY_PLAYER::Close( bool force ) function on all the windows and if ...
Definition: kiway.cpp:415
KIWAY_PLAYER is a wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of ...
Definition: kiway_player.h:59
KIWAY_EXPRESS carries a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition: kiway_express.h:39
void OnKiCadExit()
Definition: kiway.cpp:523
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104
PROJECT holds project specific data.
Definition: project.h:61
static FACE_T KifaceType(FRAME_T aFrameType)
Function KifaceType is a simple mapping function which returns the FACE_T which is known to implement...
Definition: kiway.cpp:292
KIWAY_PLAYER * GetPlayerFrame(FRAME_T aFrameType)
Definition: kiway.cpp:333
VTBL_ENTRY void CommonSettingsChanged(bool aEnvVarsChanged)
Function CommonSettingsChanged Calls CommonSettingsChanged() on all KIWAY_PLAYERs.
Definition: kiway.cpp:472
PGM_BASE keeps program (whole process) data for KiCad programs.
Definition: pgm_base.h:137
void CommonSettingsChanged(bool aEnvVarsChanged) override
Notification event that some of the common (suite-wide) settings have changed.
#define KIFACE_VERSION
Definition: kiway.h:111
FRAME_T
Enum FRAME_T is the set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:34
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:172
KIWAY(PGM_BASE *aProgram, int aCtlBits, wxFrame *aTop=NULL)
Definition: kiway.cpp:46
PGM_BASE * m_program
Definition: kiway.h:432
VTBL_ENTRY void SetLanguage(int aLanguage)
Function SetLanguage changes the language and then calls ShowChangedLanguage() on all KIWAY_PLAYERs.
Definition: kiway.cpp:436
void OnKiwayEnd()
Definition: kiway.cpp:538
#define KIFACE_INSTANCE_NAME_AND_VERSION
Definition: kiway.h:116
This file contains miscellaneous commonly used macros and functions.
MAIL_T
Enum MAIL_T is the set of mail types sendable via KIWAY::ExpressMail() and supplied as the aCommand p...
Definition: mail_type.h:37
#define NULL
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:342
VTBL_ENTRY bool PlayerClose(FRAME_T aFrameType, bool doForce)
Function PlayerClose calls the KIWAY_PLAYER::Close( bool force ) function on the window and if not ve...
Definition: kiway.cpp:390
bool ProcessEvent(wxEvent &aEvent) override
Definition: kiway.cpp:497
KIFACE * KIFACE_GETTER_FUNC(int *aKIFACEversion, int aKIWAYversion, PGM_BASE *aProgram)
Function Pointer KIFACE_GETTER_FUNC points to the one and only KIFACE export.
Definition: kiway.h:467
int m_ctl
Definition: kiway.h:433
virtual void KiwayMailIn(KIWAY_EXPRESS &aEvent)
Function KiwayMailIn receives KIWAY_EXPRESS messages from other players.
pcbnew DSO
Definition: kiway.h:282
#define THROW_IO_ERROR(msg)
const wxString dso_search_path(FACE_T aFaceId)
Get the [path &] name of the DSO holding the requested FACE_T.
Definition: kiway.cpp:96
VTBL_ENTRY KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Function KiFACE returns the KIFACE* given a FACE_T.
Definition: kiway.cpp:178
int PGM_BASE * aProgram
Definition: cvpcb/cvpcb.cpp:97
void SetTop(wxFrame *aTop)
Function SetTop tells this KIWAY about the top most frame in the program and optionally allows it to ...
Definition: kiway.cpp:78
FACE_T
Known KIFACE implementations.
Definition: kiway.h:279
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Definition: macros.h:160
eeschema DSO
Definition: kiway.h:281
VTBL_ENTRY void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=NULL)
Function ExpressMail send aPayload to aDestination from aSource.
Definition: kiway.cpp:428
FRAME_T Dest()
Function Dest returns the destination player id of the message.
Definition: kiway_express.h:46
see class PGM_BASE
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define _(s)
Definition: 3d_actions.cpp:33
The base frame for deriving all KiCad main window classes.
KIFACE is used by a participant in the KIWAY alchemy.
Definition: kiway.h:150
static int m_kiface_version[KIWAY_FACE_COUNT]
Definition: kiway.h:430
wxArrayString m_playerFrameName
Definition: kiway.h:443
wxFrame * m_top
Definition: kiway.h:435
virtual void ShowChangedLanguage()
Redraw the menus and what not in current language.
bool ProcessEvent(wxEvent &aEvent) override
Override the default process event handler to implement the auto save feature.
static KIFACE * m_kiface[KIWAY_FACE_COUNT]
Definition: kiway.h:429
#define KFCTL_STANDALONE
Am running as a standalone Top.
Definition: kiway.h:158