KiCad PCB EDA Suite
project.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-2017 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <wx/stdpaths.h>
25 
26 #include <fctsys.h>
27 #include <macros.h>
28 #include <pgm_base.h>
29 #include <project.h>
30 #include <common.h> // NAMELESS_PROJECT
31 #include <confirm.h>
32 #include <kicad_string.h>
33 #include <config_params.h>
35 #include <fp_lib_table.h>
36 #include <kiway.h>
37 #include <kiface_ids.h>
38 
39 
41 {
42  memset( m_elems, 0, sizeof(m_elems) );
43 }
44 
45 
47 {
48  // careful here, this should work, but the virtual destructor may not
49  // be in the same link image as PROJECT.
50  for( unsigned i = 0; i < DIM( m_elems ); ++i )
51  {
52  SetElem( ELEM_T( i ), NULL );
53  }
54 }
55 
56 
58 {
59  ElemsClear();
60 }
61 
62 
63 void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
64 {
65  // Compare paths, rather than inodes, to be less surprising to the user.
66  // Create a temporary wxFileName to normalize the path
67  wxFileName candidate_path( aFullPathAndName );
68 
69  // Edge transitions only. This is what clears the project
70  // data using the Clear() function.
71  if( m_project_name.GetFullPath() != candidate_path.GetFullPath() )
72  {
73  Clear(); // clear the data when the project changes.
74 
75  DBG(printf( "%s: old:'%s' new:'%s'\n", __func__, TO_UTF8( GetProjectFullName() ), TO_UTF8( aFullPathAndName ) );)
76 
77  m_project_name = aFullPathAndName;
78 
79  wxASSERT( m_project_name.IsAbsolute() );
80 
81  wxASSERT( m_project_name.GetExt() == ProjectFileExtension );
82 
83  // until multiple projects are in play, set an environment variable for the
84  // the project pointer.
85  {
86  wxString path = m_project_name.GetPath();
87 
88  wxSetEnv( PROJECT_VAR_NAME, path );
89  }
90  }
91 }
92 
93 
94 const wxString PROJECT::GetProjectFullName() const
95 {
96  return m_project_name.GetFullPath();
97 }
98 
99 
100 const wxString PROJECT::GetProjectPath() const
101 {
102  return m_project_name.GetPathWithSep();
103 }
104 
105 
106 const wxString PROJECT::GetProjectName() const
107 {
108  return m_project_name.GetName();
109 }
110 
111 
112 const wxString PROJECT::SymbolLibTableName() const
113 {
114  return libTableName( "sym-lib-table" );
115 }
116 
117 
118 const wxString PROJECT::FootprintLibTblName() const
119 {
120  return libTableName( "fp-lib-table" );
121 }
122 
123 
124 const wxString PROJECT::libTableName( const wxString& aLibTableName ) const
125 {
126  wxFileName fn = GetProjectFullName();
127  wxString path = fn.GetPath();
128 
129  // DBG(printf( "path:'%s' fn:'%s'\n", TO_UTF8(path), TO_UTF8(fn.GetFullPath()) );)
130 
131  // if there's no path to the project name, or the name as a whole is bogus or its not
132  // write-able then use a template file.
133  if( !fn.GetDirCount() || !fn.IsOk() || !wxFileName::IsDirWritable( path ) )
134  {
135  // return a template filename now.
136 
137  // this next line is likely a problem now, since it relies on an
138  // application title which is no longer constant or known. This next line needs
139  // to be re-thought out.
140 
141 #ifndef __WXMAC__
142  fn.AssignDir( wxStandardPaths::Get().GetUserConfigDir() );
143 #else
144  // don't pollute home folder, temp folder seems to be more appropriate
145  fn.AssignDir( wxStandardPaths::Get().GetTempDir() );
146 #endif
147 
148 #if defined( __WINDOWS__ )
149  fn.AppendDir( wxT( "kicad" ) );
150 #endif
151 
152  /*
153  * The library table name used when no project file is passed to the appropriate
154  * code. This is used temporarily to store the project specific library table
155  * until the project file being edited is saved. It is then moved to the correct
156  * file in the folder where the project file is saved.
157  */
158  fn.SetName( "prj-" + aLibTableName );
159  }
160  else // normal path.
161  {
162  fn.SetName( aLibTableName );
163  }
164 
165  fn.ClearExt();
166 
167  return fn.GetFullPath();
168 }
169 
170 
171 void PROJECT::SetRString( RSTRING_T aIndex, const wxString& aString )
172 {
173  unsigned ndx = unsigned( aIndex );
174 
175  if( ndx < DIM( m_rstrings ) )
176  {
177  m_rstrings[ndx] = aString;
178  }
179  else
180  {
181  wxASSERT( 0 ); // bad index
182  }
183 }
184 
185 
186 const wxString& PROJECT::GetRString( RSTRING_T aIndex )
187 {
188  unsigned ndx = unsigned( aIndex );
189 
190  if( ndx < DIM( m_rstrings ) )
191  {
192  return m_rstrings[ndx];
193  }
194  else
195  {
196  static wxString no_cookie_for_you;
197 
198  wxASSERT( 0 ); // bad index
199 
200  return no_cookie_for_you;
201  }
202 }
203 
204 
206 {
207  // This is virtual, so implement it out of line
208 
209  if( unsigned( aIndex ) < DIM( m_elems ) )
210  {
211  return m_elems[aIndex];
212  }
213  return NULL;
214 }
215 
216 
217 void PROJECT::SetElem( ELEM_T aIndex, _ELEM* aElem )
218 {
219  // This is virtual, so implement it out of line
220 
221  if( unsigned( aIndex ) < DIM( m_elems ) )
222  {
223 #if defined(DEBUG) && 0
224  if( aIndex == ELEM_SCH_PART_LIBS )
225  {
226  printf( "%s: &m_elems[%i]:%p aElem:%p\n", __func__, aIndex, &m_elems[aIndex], aElem );
227  }
228 #endif
229  delete m_elems[aIndex];
230  m_elems[aIndex] = aElem;
231  }
232 }
233 
234 
235 static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString& aDestination )
236 {
237  if( aDestination.IsEmpty() )
238  {
239  DBG( printf( "%s: destination is empty.\n", __func__ );)
240  return false;
241  }
242 
243  wxString templateFile = wxT( "kicad." ) + ProjectFileExtension;
244 
245  wxString kicad_pro_template = aSearchS.FindValidPath( templateFile );
246 
247  if( !kicad_pro_template )
248  {
249  DBG( printf( "%s: template file '%s' not found using search paths.\n", __func__, TO_UTF8( templateFile ) );)
250 
251  wxFileName templ( wxStandardPaths::Get().GetDocumentsDir(),
252  wxT( "kicad" ), ProjectFileExtension );
253 
254  if( !templ.IsFileReadable() )
255  {
256  wxString msg = wxString::Format( _(
257  "Unable to find \"%s\" template config file." ),
258  GetChars( templateFile ) );
259 
260  DisplayErrorMessage( nullptr, _( "Error copying project file template" ), msg );
261 
262  return false;
263  }
264 
265  kicad_pro_template = templ.GetFullPath();
266  }
267 
268  DBG( printf( "%s: using template file '%s' as project file.\n", __func__, TO_UTF8( kicad_pro_template ) );)
269 
270  // Verify aDestination can be created. if this is not the case, wxCopyFile
271  // will generate a crappy log error message, and we *do not want* this kind
272  // of stupid message
273  wxFileName fn( aDestination );
274  bool success = true;
275 
276  if( fn.IsOk() && fn.IsDirWritable() )
277  success = wxCopyFile( kicad_pro_template, aDestination );
278  else
279  {
280  wxLogMessage( _( "Cannot create prj file \"%s\" (Directory not writable)" ),
281  GetChars( aDestination) );
282  success = false;
283  }
284 
285  return success;
286 }
287 
288 
289 wxConfigBase* PROJECT::configCreate( const SEARCH_STACK& aSList,
290  const wxString& aGroupName, const wxString& aProjectFileName )
291 {
292  wxConfigBase* cfg = 0;
293  wxString cur_pro_fn = !aProjectFileName ? GetProjectFullName() : aProjectFileName;
294 
295  if( wxFileName( cur_pro_fn ).IsFileReadable() )
296  {
297  // Note: currently, aGroupName is not used.
298  // Previoulsy, the version of aGroupName was tested, but it
299  // was useless, and if the version is important,
300  // this is not the right place here, because configCreate does know anything
301  // about info stored in this config file.
302  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
303  return cfg;
304  }
305 
306  // No suitable pro file was found, either does not exist, or not readable.
307  // Use the template kicad.pro file. Find it by using caller's SEARCH_STACK.
308  copy_pro_file_template( aSList, cur_pro_fn );
309 
310  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
311 
312  return cfg;
313 }
314 
315 
316 void PROJECT::ConfigSave( const SEARCH_STACK& aSList, const wxString& aGroupName,
317  const PARAM_CFG_ARRAY& aParams, const wxString& aFileName )
318 {
319  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aFileName ) );
320 
321  if( !cfg.get() )
322  {
323  // could not find template
324  return;
325  }
326 
327  cfg->SetPath( wxT( "/" ) );
328 
329  cfg->Write( wxT( "update" ), DateAndTime() );
330 
331  // @todo: pass in aLastClient wxString:
332  cfg->Write( wxT( "last_client" ), Pgm().App().GetAppName() );
333 
334  // Save parameters
335  cfg->DeleteGroup( aGroupName ); // Erase all data
336  cfg->Flush();
337 
338  cfg->SetPath( aGroupName );
339  cfg->Write( wxT( "version" ), CONFIG_VERSION );
340 
341  cfg->SetPath( wxT( "/" ) );
342 
343  wxConfigSaveParams( cfg.get(), aParams, aGroupName );
344 
345  cfg->SetPath( wxT( "/" ) );
346 
347  // cfg is deleted here by std::unique_ptr, that saves the *.pro file to disk
348 }
349 
350 
351 bool PROJECT::ConfigLoad( const SEARCH_STACK& aSList, const wxString& aGroupName,
352  const PARAM_CFG_ARRAY& aParams, const wxString& aForeignProjectFileName )
353 {
354  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aForeignProjectFileName ) );
355 
356  if( !cfg.get() )
357  {
358  // could not find template
359  return false;
360  }
361 
362  cfg->SetPath( wxCONFIG_PATH_SEPARATOR );
363 
364  wxString timestamp = cfg->Read( wxT( "update" ) );
365 
366  m_pro_date_and_time = timestamp;
367 
368  // We do not want expansion of env var values when reading our project config file
369  bool state = cfg.get()->IsExpandingEnvVars();
370  cfg.get()->SetExpandEnvVars( false );
371 
372  wxConfigLoadParams( cfg.get(), aParams, aGroupName );
373 
374  cfg.get()->SetExpandEnvVars( state );
375 
376  return true;
377 }
378 
379 
380 const wxString PROJECT::AbsolutePath( const wxString& aFileName ) const
381 {
382  wxFileName fn = aFileName;
383 
384  if( !fn.IsAbsolute() )
385  {
386  wxString pro_dir = wxPathOnly( GetProjectFullName() );
387  fn.Normalize( wxPATH_NORM_ALL, pro_dir );
388  }
389 
390  return fn.GetFullPath();
391 }
392 
393 
395 {
396  // This is a lazy loading function, it loads the project specific table when
397  // that table is asked for, not before.
398 
400 
401  // its gotta be NULL or a FP_LIB_TABLE, or a bug.
402  wxASSERT( !tbl || dynamic_cast<FP_LIB_TABLE*>( tbl ) );
403 
404  if( !tbl )
405  {
406  // Stack the project specific FP_LIB_TABLE overlay on top of the global table.
407  // ~FP_LIB_TABLE() will not touch the fallback table, so multiple projects may
408  // stack this way, all using the same global fallback table.
409  KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB );
410  if( kiface )
412 
413  wxASSERT( tbl );
414  SetElem( ELEM_FPTBL, tbl );
415 
416  wxString projectFpLibTableFileName = FootprintLibTblName();
417 
418  try
419  {
420  tbl->Load( projectFpLibTableFileName );
421  }
422  catch( const IO_ERROR& ioe )
423  {
424  DisplayErrorMessage( NULL, _( "Error loading project footprint library table" ), ioe.What() );
425  }
426  }
427 
428  return tbl;
429 }
wxConfigBase * configCreate(const SEARCH_STACK &aSList, const wxString &aGroupName, const wxString &aProjectFileName=wxEmptyString)
Function configCreate loads a *.pro file and returns a wxConfigBase.
Definition: project.cpp:289
_ELEM * m_elems[ELEM_COUNT]
Definition: project.h:326
#define DIM(x)
of elements in an array
Definition: macros.h:98
A list of parameters type.
void Clear()
Function Clear clears the _ELEMs and RSTRINGs.
Definition: project.h:235
VTBL_ENTRY void ConfigSave(const SEARCH_STACK &aSList, const wxString &aGroupName, const PARAM_CFG_ARRAY &aParams, const wxString &aFileName=wxEmptyString)
Function ConfigSave saves the current "project" parameters into the wxConfigBase* derivative...
Definition: project.cpp:316
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:199
This file is part of the common library.
const wxString ProjectFileExtension
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:36
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:94
The common library.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
void wxConfigSaveParams(wxConfigBase *aCfg, const PARAM_CFG_ARRAY &aList, const wxString &aGroup)
Function wxConfigSaveParams writes aList of PARAM_CFG_ARRAY elements to save configuration values to ...
Class SEARCH_STACK looks for files in a number of places.
Definition: search_stack.h:41
VTBL_ENTRY _ELEM * GetElem(ELEM_T aIndex)
Typically wrapped somewhere else in a more meaningful function wrapper.
Definition: project.cpp:205
VTBL_ENTRY void * IfaceOrAddress(int aDataId)=0
Function IfaceOrAddress returns a pointer to the requested object.
void wxConfigLoadParams(wxConfigBase *aCfg, const PARAM_CFG_ARRAY &aList, const wxString &aGroup)
Function wxConfigLoadParams uses aList of PARAM_CFG_ARRAY elements to load configuration values from ...
This file contains miscellaneous commonly used macros and functions.
VTBL_ENTRY void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:217
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
VTBL_ENTRY const wxString SymbolLibTableName() const
Return the path and file name of this projects symbol library table.
Definition: project.cpp:112
wxFileName m_project_name
<fullpath>/<basename>.pro
Definition: project.h:319
VTBL_ENTRY const wxString AbsolutePath(const wxString &aFileName) const
Function AbsolutePath fixes up aFileName if it is relative to the project&#39;s directory to be an absolu...
Definition: project.cpp:380
The common library.
Class KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within...
Definition: kiway.h:258
pcbnew DSO
Definition: kiway.h:267
VTBL_ENTRY KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Function KiFACE returns the KIFACE* given a FACE_T.
Definition: kiway.cpp:148
VTBL_ENTRY const wxString FootprintLibTblName() const
Function FootprintLibTblName returns the path and filename of this project&#39;s fp-lib-table, i.e.
Definition: project.cpp:118
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
VTBL_ENTRY void SetProjectFullName(const wxString &aFullPathAndName)
Function SetProjectFullName sets the: 1) full directory, 2) basename, and 3) extension of the project...
Definition: project.cpp:63
VTBL_ENTRY void ElemsClear()
Function ElemsClear deletes all the _ELEMs and set their pointers to NULL.
Definition: project.cpp:46
static bool copy_pro_file_template(const SEARCH_STACK &aSearchS, const wxString &aDestination)
Definition: project.cpp:235
PROJECT()
Definition: project.cpp:40
Return a new FP_LIB_TABLE copying the global table.
Definition: kiface_ids.h:46
VTBL_ENTRY void SetRString(RSTRING_T aStringId, const wxString &aString)
Function SetRString stores a "retained string", which is any session and project specific string iden...
Definition: project.cpp:171
VTBL_ENTRY FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
VTBL_ENTRY const wxString & GetRString(RSTRING_T aStringId)
Function GetRString returns a "retained string", which is any session and project specific string ide...
Definition: project.cpp:186
ELEM_T
Enum ELEM_T is the set of _ELEMs that a PROJECT can hold.
Definition: project.h:201
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
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
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
RSTRING_T
Retain a number of project specific wxStrings, enumerated here:
Definition: project.h:160
wxString m_rstrings[RSTRING_COUNT]
Definition: project.h:323
size_t i
Definition: json11.cpp:597
Class KIFACE is used by a participant in the KIWAY alchemy.
Definition: kiway.h:151
#define CONFIG_VERSION
Definition: config_params.h:56
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:100
The common library.
A PROJECT can hold stuff it knows nothing about, in the form of _ELEM derivatives.
Definition: project.h:66
return & kiface
Definition: pcbnew.cpp:191
#define DBG(x)
Definition: fctsys.h:33
VTBL_ENTRY bool ConfigLoad(const SEARCH_STACK &aSearchS, const wxString &aGroupName, const PARAM_CFG_ARRAY &aParams, const wxString &aForeignConfigFileName=wxEmptyString)
Function ConfigLoad reads a subset of parameters from the "project" file.
Definition: project.cpp:351
wxString m_pro_date_and_time
Definition: project.h:320
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:71
~PROJECT()
Definition: project.cpp:57
const wxString libTableName(const wxString &aLibTableName) const
Return the full path and file name of the project specific library table aLibTableName.
Definition: project.cpp:124
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
wxString DateAndTime()
Function DateAndTime.
Definition: string.cpp:229
VTBL_ENTRY const wxString GetProjectName() const
Function GetProjectName returns the short name of the project.
Definition: project.cpp:106