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-2018 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 #include <trace_helpers.h>
39 
40 
42 {
43  memset( m_elems, 0, sizeof(m_elems) );
44 }
45 
46 
48 {
49  // careful here, this should work, but the virtual destructor may not
50  // be in the same link image as PROJECT.
51  for( unsigned i = 0; i < DIM( m_elems ); ++i )
52  {
53  SetElem( ELEM_T( i ), NULL );
54  }
55 }
56 
57 
59 {
60  ElemsClear();
61 }
62 
63 
64 void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
65 {
66  // Compare paths, rather than inodes, to be less surprising to the user.
67  // Create a temporary wxFileName to normalize the path
68  wxFileName candidate_path( aFullPathAndName );
69 
70  // Edge transitions only. This is what clears the project
71  // data using the Clear() function.
72  if( m_project_name.GetFullPath() != candidate_path.GetFullPath() )
73  {
74  Clear(); // clear the data when the project changes.
75 
76  wxLogTrace( tracePathsAndFiles, "%s: old:'%s' new:'%s'", __func__,
77  TO_UTF8( GetProjectFullName() ), TO_UTF8( aFullPathAndName ) );
78 
79  m_project_name = aFullPathAndName;
80 
81  wxASSERT( m_project_name.IsAbsolute() );
82 
83  wxASSERT( m_project_name.GetExt() == ProjectFileExtension );
84 
85  // until multiple projects are in play, set an environment variable for the
86  // the project pointer.
87  {
88  wxString path = m_project_name.GetPath();
89 
90  wxSetEnv( PROJECT_VAR_NAME, path );
91  }
92  }
93 }
94 
95 
96 const wxString PROJECT::GetProjectFullName() const
97 {
98  return m_project_name.GetFullPath();
99 }
100 
101 
102 const wxString PROJECT::GetProjectPath() const
103 {
104  return m_project_name.GetPathWithSep();
105 }
106 
107 
108 const wxString PROJECT::GetProjectName() const
109 {
110  return m_project_name.GetName();
111 }
112 
113 
114 const wxString PROJECT::SymbolLibTableName() const
115 {
116  return libTableName( "sym-lib-table" );
117 }
118 
119 
120 const wxString PROJECT::FootprintLibTblName() const
121 {
122  return libTableName( "fp-lib-table" );
123 }
124 
125 
126 const wxString PROJECT::libTableName( const wxString& aLibTableName ) const
127 {
128  wxFileName fn = GetProjectFullName();
129  wxString path = fn.GetPath();
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  wxLogTrace( tracePathsAndFiles, "%s: destination is empty.", __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  wxLogTrace( tracePathsAndFiles, "%s: template file '%s' not found using search paths.",
250  __func__, TO_UTF8( templateFile ) );
251 
252  wxFileName templ( wxStandardPaths::Get().GetDocumentsDir(),
253  wxT( "kicad" ), ProjectFileExtension );
254 
255  if( !templ.IsFileReadable() )
256  {
257  wxString msg = wxString::Format( _(
258  "Unable to find \"%s\" template config file." ),
259  GetChars( templateFile ) );
260 
261  DisplayErrorMessage( nullptr, _( "Error copying project file template" ), msg );
262 
263  return false;
264  }
265 
266  kicad_pro_template = templ.GetFullPath();
267  }
268 
269  wxLogTrace( tracePathsAndFiles, "%s: using template file '%s' as project file.",
270  __func__, TO_UTF8( kicad_pro_template ) );
271 
272  // Verify aDestination can be created. if this is not the case, wxCopyFile
273  // will generate a crappy log error message, and we *do not want* this kind
274  // of stupid message
275  wxFileName fn( aDestination );
276  bool success = true;
277 
278  if( fn.IsOk() && fn.IsDirWritable() )
279  success = wxCopyFile( kicad_pro_template, aDestination );
280  else
281  {
282  wxLogMessage( _( "Cannot create prj file \"%s\" (Directory not writable)" ),
283  GetChars( aDestination) );
284  success = false;
285  }
286 
287  return success;
288 }
289 
290 
291 wxConfigBase* PROJECT::configCreate( const SEARCH_STACK& aSList,
292  const wxString& aGroupName, const wxString& aProjectFileName )
293 {
294  wxConfigBase* cfg = 0;
295  wxString cur_pro_fn = !aProjectFileName ? GetProjectFullName() : aProjectFileName;
296 
297  if( wxFileName( cur_pro_fn ).IsFileReadable() )
298  {
299  // Note: currently, aGroupName is not used.
300  // Previoulsy, the version of aGroupName was tested, but it
301  // was useless, and if the version is important,
302  // this is not the right place here, because configCreate does know anything
303  // about info stored in this config file.
304  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
305  return cfg;
306  }
307 
308  // No suitable pro file was found, either does not exist, or not readable.
309  // Use the template kicad.pro file. Find it by using caller's SEARCH_STACK.
310  copy_pro_file_template( aSList, cur_pro_fn );
311 
312  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
313 
314  return cfg;
315 }
316 
317 
318 void PROJECT::ConfigSave( const SEARCH_STACK& aSList, const wxString& aGroupName,
319  const PARAM_CFG_ARRAY& aParams, const wxString& aFileName )
320 {
321  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aFileName ) );
322 
323  if( !cfg.get() )
324  {
325  // could not find template
326  return;
327  }
328 
329  cfg->SetPath( wxT( "/" ) );
330 
331  cfg->Write( wxT( "update" ), DateAndTime() );
332 
333  // @todo: pass in aLastClient wxString:
334  cfg->Write( wxT( "last_client" ), Pgm().App().GetAppName() );
335 
336  // Save parameters
337  cfg->DeleteGroup( aGroupName ); // Erase all data
338  cfg->Flush();
339 
340  cfg->SetPath( aGroupName );
341  cfg->Write( wxT( "version" ), CONFIG_VERSION );
342 
343  cfg->SetPath( wxT( "/" ) );
344 
345  wxConfigSaveParams( cfg.get(), aParams, aGroupName );
346 
347  cfg->SetPath( wxT( "/" ) );
348 
349  // cfg is deleted here by std::unique_ptr, that saves the *.pro file to disk
350 }
351 
352 
353 bool PROJECT::ConfigLoad( const SEARCH_STACK& aSList, const wxString& aGroupName,
354  const PARAM_CFG_ARRAY& aParams, const wxString& aForeignProjectFileName )
355 {
356  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName,
357  aForeignProjectFileName ) );
358 
359  if( !cfg.get() )
360  {
361  // could not find template
362  return false;
363  }
364 
365  // We do not want expansion of env var values when reading our project config file
366  cfg.get()->SetExpandEnvVars( false );
367 
368  cfg->SetPath( wxCONFIG_PATH_SEPARATOR );
369 
370  wxString timestamp = cfg->Read( wxT( "update" ) );
371 
372  m_pro_date_and_time = timestamp;
373 
374  wxConfigLoadParams( cfg.get(), aParams, aGroupName );
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 || tbl->Type() == FP_LIB_TABLE_T );
403 
404  if( !tbl )
405  {
406  // Build a new project specific FP_LIB_TABLE with the global table as a fallback.
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 
411  if( kiface )
413 
414  wxASSERT( tbl );
415  SetElem( ELEM_FPTBL, tbl );
416 
417  wxString projectFpLibTableFileName = FootprintLibTblName();
418 
419  try
420  {
421  tbl->Load( projectFpLibTableFileName );
422  }
423  catch( const IO_ERROR& ioe )
424  {
425  DisplayErrorMessage( NULL, _( "Error loading project footprint library table" ),
426  ioe.What() );
427  }
428  }
429 
430  return tbl;
431 }
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:291
_ELEM * m_elems[ELEM_COUNT]
Definition: project.h:329
#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:238
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:318
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:259
This file is part of the common library.
const std::string ProjectFileExtension
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:37
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:96
The common library.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:66
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:114
wxFileName m_project_name
<fullpath>/<basename>.pro
Definition: project.h:322
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
Return a new FP_LIB_TABLE with the global table installed as a fallback.
Definition: kiface_ids.h:46
wxLogTrace helper definitions.
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:120
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:64
VTBL_ENTRY void ElemsClear()
Function ElemsClear deletes all the _ELEMs and set their pointers to NULL.
Definition: project.cpp:47
static bool copy_pro_file_template(const SEARCH_STACK &aSearchS, const wxString &aDestination)
Definition: project.cpp:235
PROJECT()
Definition: project.cpp:41
KICAD_T Type() override
Definition: fp_lib_table.h:108
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:204
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:163
wxString m_rstrings[RSTRING_COUNT]
Definition: project.h:326
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:102
The common library.
A PROJECT can hold stuff it knows nothing about, in the form of _ELEM derivatives.
Definition: project.h:67
return & kiface
Definition: pcbnew.cpp:219
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:353
wxString m_pro_date_and_time
Definition: project.h:323
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:73
~PROJECT()
Definition: project.cpp:58
const wxString libTableName(const wxString &aLibTableName) const
Return the full path and file name of the project specific library table aLibTableName.
Definition: project.cpp:126
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
wxString DateAndTime()
Function DateAndTime.
Definition: string.cpp:306
VTBL_ENTRY const wxString GetProjectName() const
Function GetProjectName returns the short name of the project.
Definition: project.cpp:108