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 <string.h>
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>
34 
35 #include <wx/stdpaths.h>
36 #include <wx/debug.h>
37 
38 
39 KIFACE* KIWAY::m_kiface[KIWAY_FACE_COUNT];
40 int KIWAY::m_kiface_version[KIWAY_FACE_COUNT];
41 
42 
43 
44 KIWAY::KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop ):
45  m_program( aProgram ), m_ctl( aCtlBits ), m_top( 0 )
46 {
47  SetTop( aTop ); // hook player_destroy_handler() into aTop.
48 
49 
50  // Prepare the room to store the frame names, once they will be created
51  // with FRAME_T type as index in this table.
52  // (note this is a list of frame names, but a non empty entry
53  // does not mean the frame still exists. It means only the frame was created
54  // at least once. It can be destroyed after. These entries are not cleared.
55  // the purpose is just to allow a call to wxWindow::FindWindowByName(), from
56  // a FRAME_T frame type
57  m_playerFrameName.Add( wxEmptyString, KIWAY_PLAYER_COUNT );
58 }
59 
60 
61 #if 0
62 // Any event types derived from wxCommandEvt, like wxWindowDestroyEvent, are
63 // propagated upwards to parent windows if not handled below. Therefore the
64 // m_top window should receive all wxWindowDestroyEvents originating from
65 // KIWAY_PLAYERs. It does anyways, but now player_destroy_handler eavesdrops
66 // on that event stream looking for KIWAY_PLAYERs being closed.
67 
68 void KIWAY::player_destroy_handler( wxWindowDestroyEvent& event )
69 {
70  // Currently : do nothing
71  event.Skip(); // skip to who, the wxApp? I'm the top window.
72 }
73 #endif
74 
75 
76 void KIWAY::SetTop( wxFrame* aTop )
77 {
78 #if 0
79  if( m_top )
80  {
81  m_top->Disconnect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this );
82  }
83 
84  if( aTop )
85  {
86  aTop->Connect( wxEVT_DESTROY, wxWindowDestroyEventHandler( KIWAY::player_destroy_handler ), NULL, this );
87  }
88 #endif
89 
90  m_top = aTop;
91 }
92 
93 
94 const wxString KIWAY::dso_search_path( FACE_T aFaceId )
95 {
96  const char* name;
97 
98  switch( aFaceId )
99  {
100  case FACE_SCH: name = KIFACE_PREFIX "eeschema"; break;
101  case FACE_PCB: name = KIFACE_PREFIX "pcbnew"; break;
102  case FACE_CVPCB: name = KIFACE_PREFIX "cvpcb"; break;
103  case FACE_GERBVIEW: name = KIFACE_PREFIX "gerbview"; break;
104  case FACE_PL_EDITOR: name = KIFACE_PREFIX "pl_editor"; break;
105  case FACE_PCB_CALCULATOR: name = KIFACE_PREFIX "pcb_calculator"; break;
106  case FACE_BMP2CMP: name = KIFACE_PREFIX "bitmap2component"; break;
107 
108  default:
109  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
110  return wxEmptyString;
111  }
112 
113 #ifndef __WXMAC__
114  wxString path;
115 
117  {
118  // The 2 *.cpp program launchers: single_top.cpp and kicad.cpp expect
119  // the *.kiface's to reside in same diretory as their binaries do.
120  // Not so for python launcher, identified by KFCTL_PY_PROJECT_SUITE
121  path = wxStandardPaths::Get().GetExecutablePath();
122  }
123 
124  wxFileName fn = path;
125 #else
126  // we have the dso's in main OSX bundle kicad.app/Contents/PlugIns
127  wxFileName fn = Pgm().GetExecutablePath();
128  fn.AppendDir( wxT( "Contents" ) );
129  fn.AppendDir( wxT( "PlugIns" ) );
130 #endif
131 
132  fn.SetName( name );
133 
134  // Here a "suffix" == an extension with a preceding '.',
135  // so skip the preceding '.' to get an extension
136  fn.SetExt( KIFACE_SUFFIX + 1 ); // + 1 => &KIFACE_SUFFIX[1]
137 
138  return fn.GetFullPath();
139 }
140 
141 
143 {
144  return *(PROJECT*) &m_project; // strip const-ness, function really is const.
145 }
146 
147 
148 KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad )
149 {
150  // Since this will be called from python, cannot assume that code will
151  // not pass a bad aFaceId.
152  if( unsigned( aFaceId ) >= DIM( m_kiface ) )
153  {
154  // @todo : throw an exception here for python's benefit, at least that
155  // way it gets some explanatory text.
156 
157  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFaceId" ) );
158  return NULL;
159  }
160 
161  // return the previously loaded KIFACE, if it was.
162  if( m_kiface[aFaceId] )
163  return m_kiface[aFaceId];
164 
165  wxString msg;
166 
167  // DSO with KIFACE has not been loaded yet, does caller want to load it?
168  if( doLoad )
169  {
170  wxString dname = dso_search_path( aFaceId );
171 
172  wxDynamicLibrary dso;
173 
174  void* addr = NULL;
175 
176  if( !dso.Load( dname, wxDL_VERBATIM | wxDL_NOW | wxDL_GLOBAL ) )
177  {
178  // Failure: error reporting UI was done via wxLogSysError().
179  // No further reporting required here. Apparently this is not true on all
180  // platforms and/or wxWidgets builds and KiCad will crash. Throwing the exception
181  // here and catching it in the KiCad launcher resolves the crash issue. See bug
182  // report https://bugs.launchpad.net/kicad/+bug/1577786.
183 
184  msg.Printf( _( "Failed to load kiface library \"%s\"." ), GetChars( dname ) );
185  THROW_IO_ERROR( msg );
186  }
187  else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == NULL )
188  {
189  // Failure: error reporting UI was done via wxLogSysError().
190  // No further reporting required here. Assume the same thing applies here as
191  // above with the Load() call. This has not been tested.
192  msg.Printf(
193  _( "Could not read instance name and version symbol form kiface library \"%s\"." ),
194  GetChars( dname ) );
195  THROW_IO_ERROR( msg );
196  }
197  else
198  {
199  KIFACE_GETTER_FUNC* getter = (KIFACE_GETTER_FUNC*) addr;
200 
201  KIFACE* kiface = getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program );
202 
203  // KIFACE_GETTER_FUNC function comment (API) says the non-NULL is unconditional.
204  wxASSERT_MSG( kiface,
205  wxT( "attempted DSO has a bug, failed to return a KIFACE*" ) );
206 
207  // Give the DSO a single chance to do its "process level" initialization.
208  // "Process level" specifically means stay away from any projects in there.
209  if( kiface->OnKifaceStart( m_program, m_ctl ) )
210  {
211  // Tell dso's wxDynamicLibrary destructor not to Unload() the program image.
212  (void) dso.Detach();
213 
214  return m_kiface[aFaceId] = kiface;
215  }
216  }
217 
218  // In any of the failure cases above, dso.Unload() should be called here
219  // by dso destructor.
220  // However:
221 
222  // There is a file installation bug. We only look for KIFACE_I's which we know
223  // to exist, and we did not find one. If we do not find one, this is an
224  // installation bug.
225 
226  msg = wxString::Format( _(
227  "Fatal Installation Bug. File:\n"
228  "\"%s\"\ncould not be loaded\n" ), GetChars( dname ) );
229 
230  if( ! wxFileExists( dname ) )
231  msg << _( "It is missing.\n" );
232  else
233  msg << _( "Perhaps a shared library (.dll or .so) file is missing.\n" );
234 
235  msg << _( "From command line: argv[0]:\n'" );
236  msg << wxStandardPaths::Get().GetExecutablePath() << wxT( "'\n" );
237 
238  // This is a fatal error, one from which we cannot recover, nor do we want
239  // to protect against in client code which would require numerous noisy
240  // tests in numerous places. So we inform the user that the installation
241  // is bad. This exception will likely not get caught until way up in the
242  // wxApp derivative, at which point the process will exit gracefully.
243  THROW_IO_ERROR( msg );
244  }
245 
246  return NULL;
247 }
248 
249 
251 {
252  switch( aFrameType )
253  {
254  case FRAME_SCH:
256  case FRAME_SCH_VIEWER:
258  case FRAME_SIMULATOR:
259  return FACE_SCH;
260 
261  case FRAME_PCB:
266  case FRAME_PCB_DISPLAY3D:
267  return FACE_PCB;
268 
269  case FRAME_CVPCB:
270  case FRAME_CVPCB_DISPLAY:
271  return FACE_CVPCB;
272 
273  case FRAME_GERBER:
274  return FACE_GERBVIEW;
275 
276  case FRAME_PL_EDITOR:
277  return FACE_PL_EDITOR;
278 
279  case FRAME_CALC:
280  return FACE_PCB_CALCULATOR;
281 
282  case FRAME_BM2CMP:
283  return FACE_BMP2CMP;
284 
285  default:
286  return FACE_T( -1 );
287  }
288 }
289 
290 
292 {
293  if( m_playerFrameName[aFrameType].IsEmpty() )
294  return NULL;
295 
296  return static_cast<KIWAY_PLAYER*>( wxWindow::FindWindowByName( m_playerFrameName[aFrameType] ) );
297 }
298 
299 
300 KIWAY_PLAYER* KIWAY::Player( FRAME_T aFrameType, bool doCreate, wxTopLevelWindow* aParent )
301 {
302  // Since this will be called from python, cannot assume that code will
303  // not pass a bad aFrameType.
304  if( unsigned( aFrameType ) >= KIWAY_PLAYER_COUNT )
305  {
306  // @todo : throw an exception here for python's benefit, at least that
307  // way it gets some explanatory text.
308 
309  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
310  return NULL;
311  }
312 
313  // return the previously opened window
314  KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
315 
316  if( frame )
317  return frame;
318 
319  if( doCreate )
320  {
321  FACE_T face_type = KifaceType( aFrameType );
322  wxASSERT( face_type != FACE_T(-1) );
323 
324  KIFACE* kiface = KiFACE( face_type );
325  wxASSERT( kiface );
326 
327  if( kiface )
328  {
329  frame = (KIWAY_PLAYER*) kiface->CreateWindow(
330  aParent, // Parent window of frame in modal mode, NULL in non modal mode
331  aFrameType,
332  this,
333  m_ctl // questionable need, these same flags where passed
334  // to the KIFACE::OnKifaceStart()
335  );
336  wxASSERT( frame );
337 
338  m_playerFrameName[aFrameType] = frame->GetName();
339 
340  return frame;
341  }
342  }
343 
344  return NULL;
345 }
346 
347 
348 bool KIWAY::PlayerClose( FRAME_T aFrameType, bool doForce )
349 {
350  // Since this will be called from python, cannot assume that code will
351  // not pass a bad aFrameType.
352  if( unsigned( aFrameType ) >= KIWAY_PLAYER_COUNT )
353  {
354  // @todo : throw an exception here for python's benefit, at least that
355  // way it gets some explanatory text.
356 
357  wxASSERT_MSG( 0, wxT( "caller has a bug, passed a bad aFrameType" ) );
358  return false;
359  }
360 
361  KIWAY_PLAYER* frame = GetPlayerFrame( aFrameType );
362 
363  if( frame == NULL ) // Already closed
364  return true;
365 
366  if( frame->Close( doForce ) )
367  return true;
368 
369  return false;
370 }
371 
372 
373 bool KIWAY::PlayersClose( bool doForce )
374 {
375  bool ret = true;
376 
377  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
378  {
379  ret = ret && PlayerClose( FRAME_T( i ), doForce );
380  }
381 
382  return ret;
383 }
384 
385 
386 void KIWAY::ExpressMail( FRAME_T aDestination,
387  MAIL_T aCommand, std::string aPayload, wxWindow* aSource )
388 {
389  KIWAY_EXPRESS mail( aDestination, aCommand, aPayload, aSource );
390 
391  ProcessEvent( mail );
392 }
393 
394 
395 void KIWAY::SetLanguage( int aLanguage )
396 {
397  Pgm().SetLanguageIdentifier( aLanguage );
398  Pgm().SetLanguage();
399 
400 #if 1
401  // This is a risky hack that goes away if we allow the language to be
402  // set only from the top most frame if !Kiface.IsSingle()
403 
404  // Only for the C++ project manager, and not for the python one and not for
405  // single_top do we look for the EDA_BASE_FRAME as the top level window.
406  // For single_top this is not needed because that window is registered in
407  // the array below.
409  {
410  // A dynamic_cast could be better, but creates link issues
411  // (some basic_frame functions not found) on some platforms,
412  // so a static_cast is used.
413  EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
414 
415  if( top )
416  top->ShowChangedLanguage();
417  }
418 #endif
419 
420  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
421  {
422  KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
423 
424  if( frame )
425  {
426  frame->ShowChangedLanguage();
427  }
428  }
429 }
430 
432 {
433 #if 1
435  {
436  // A dynamic_cast could be better, but creates link issues
437  // (some basic_frame functions not found) on some platforms,
438  // so a static_cast is used.
439  EDA_BASE_FRAME* top = static_cast<EDA_BASE_FRAME*>( m_top );
440 
441  if( top )
442  top->ShowChangedIcons();
443  }
444 #endif
445 
446  for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i )
447  {
448  KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i );
449 
450  if( frame )
451  {
452  frame->ShowChangedIcons();
453  }
454  }
455 }
456 
457 
458 bool KIWAY::ProcessEvent( wxEvent& aEvent )
459 {
460  KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent );
461 
462  if( mail )
463  {
464  FRAME_T dest = mail->Dest();
465 
466  // see if recipient is alive
467  KIWAY_PLAYER* alive = Player( dest, false );
468 
469  if( alive )
470  {
471 #if 1
472  return alive->ProcessEvent( aEvent );
473 #else
474  alive->KiwayMailIn( *mail );
475  return true;
476 #endif
477  }
478  }
479 
480  return false;
481 }
482 
483 
485 {
486  for( unsigned i=0; i < DIM( m_kiface ); ++i )
487  {
488  if( m_kiface[i] )
489  m_kiface[i]->OnKifaceEnd();
490  }
491 }
#define KFCTL_CPP_PROJECT_SUITE
Am running under C++ project mgr, possibly with others.
Definition: kiway.h:160
#define DIM(x)
of elements in an array
Definition: macros.h:98
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:373
Class KIWAY_PLAYER is a wxFrame capable of the OpenProjectFiles function, meaning it can load a porti...
Definition: kiway_player.h:111
Class KIWAY_EXPRESS carries a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition: kiway_express.h:39
Class PROJECT holds project specific data.
Definition: project.h:56
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:250
KIWAY_PLAYER * GetPlayerFrame(FRAME_T aFrameType)
Definition: kiway.cpp:291
VTBL_ENTRY const wxString & GetExecutablePath() const
Definition: pgm_base.h:173
virtual void ShowChangedIcons()
Function ShowChangedIcons redraws items menus after a icon was changed option.
Class PGM_BASE keeps program (whole process) data for KiCad programs.
Definition: pgm_base.h:107
#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
KIWAY(PGM_BASE *aProgram, int aCtlBits, wxFrame *aTop=NULL)
Definition: kiway.cpp:44
VTBL_ENTRY void OnKifaceEnd()=0
Function OnKifaceEnd is called just once just before the DSO is to be unloaded.
PGM_BASE * m_program
Definition: kiway.h:416
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:66
VTBL_ENTRY void SetLanguage(int aLanguage)
Function SetLanguage changes the language and then calls ShowChangedLanguage() on all KIWAY_PLAYERs...
Definition: kiway.cpp:395
void OnKiwayEnd()
Definition: kiway.cpp:484
#define KIFACE_INSTANCE_NAME_AND_VERSION
Definition: kiway.h:116
VTBL_ENTRY void ShowChangedIcons()
Function ShowChangedIcons Calls ShowChangedIcons() on all KIWAY_PLAYERs.
Definition: kiway.cpp:431
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
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:386
VTBL_ENTRY wxWindow * CreateWindow(wxWindow *aParent, int aClassId, KIWAY *aKIWAY, int aCtlBits=0)=0
Function CreateWindow creates a wxWindow for the current project.
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:300
VTBL_ENTRY void SetLanguageIdentifier(int menu_id)
Function SetLanguageIdentifier sets in .m_language_id member the wxWidgets language identifier Id fro...
Definition: pgm_base.cpp:838
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:142
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:348
bool ProcessEvent(wxEvent &aEvent) override
Definition: kiway.cpp:458
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:453
PROJECT m_project
Definition: kiway.h:429
int m_ctl
Definition: kiway.h:417
virtual void KiwayMailIn(KIWAY_EXPRESS &aEvent)
Function KiwayMailIn receives KIWAY_EXPRESS messages from other players.
pcbnew DSO
Definition: kiway.h:267
const wxString dso_search_path(FACE_T aFaceId)
Get the [path &] name of the DSO holding the requested FACE_T.
Definition: kiway.cpp:94
VTBL_ENTRY KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Function KiFACE returns the KIFACE* given a FACE_T.
Definition: kiway.cpp:148
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:76
VTBL_ENTRY bool SetLanguage(bool first_time=false)
Function SetLanguage sets the dictionary file name for internationalization.
Definition: pgm_base.cpp:746
FACE_T
Known KIFACE implementations.
Definition: kiway.h:264
eeschema DSO
Definition: kiway.h:266
FRAME_T Dest()
Function Dest returns the destination player id of the message.
Definition: kiway_express.h:46
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
see class PGM_BASE
const char * name
Definition: DXF_plotter.cpp:61
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
Class EDA_BASE_FRAME is the base frame for deriving all KiCad main window classes.
size_t i
Definition: json11.cpp:597
Class KIFACE is used by a participant in the KIWAY alchemy.
Definition: kiway.h:151
static int m_kiface_version[KIWAY_FACE_COUNT]
Definition: kiway.h:414
VTBL_ENTRY bool OnKifaceStart(PGM_BASE *aProgram, int aCtlBits)=0
Function OnKifaceStart is called just once shortly after the DSO is loaded.
wxArrayString m_playerFrameName
Definition: kiway.h:427
return & kiface
Definition: pcbnew.cpp:197
wxFrame * m_top
Definition: kiway.h:419
virtual void ShowChangedLanguage()
Function ShowChangedLanguage redraws the menus and what not in current language.
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
bool ProcessEvent(wxEvent &aEvent) override
Function ProcessEvent overrides the default process event handler to implement the auto save feature...
int PGM_BASE * aProgram
Definition: pcbnew.cpp:195
static KIFACE * m_kiface[KIWAY_FACE_COUNT]
Definition: kiway.h:413
#define KFCTL_STANDALONE
Am running as a standalone Top.
Definition: kiway.h:159