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  DBG( printf( "%s: clearing all _ELEMS for project %s\n", __func__, TO_UTF8( GetProjectFullName() ) );)
49 
50  // careful here, this should work, but the virtual destructor may not
51  // be in the same link image as PROJECT.
52  for( unsigned i = 0; i < DIM( m_elems ); ++i )
53  {
54  SetElem( ELEM_T( i ), NULL );
55  }
56 }
57 
58 
60 {
61  ElemsClear();
62 }
63 
64 
65 void PROJECT::SetProjectFullName( const wxString& aFullPathAndName )
66 {
67  // Compare paths, rather than inodes, to be less surprising to the user.
68  // Create a temporary wxFileName to normalize the path
69  wxFileName candidate_path( aFullPathAndName );
70 
71  // Edge transitions only. This is what clears the project
72  // data using the Clear() function.
73  if( m_project_name.GetFullPath() != candidate_path.GetFullPath() )
74  {
75  Clear(); // clear the data when the project changes.
76 
77  DBG(printf( "%s: old:'%s' new:'%s'\n", __func__, 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::FootprintLibTblName() const
115 {
116  wxFileName fn = GetProjectFullName();
117  wxString path = fn.GetPath();
118 
119  // DBG(printf( "path:'%s' fn:'%s'\n", TO_UTF8(path), TO_UTF8(fn.GetFullPath()) );)
120 
121  // if there's no path to the project name, or the name as a whole is bogus or its not
122  // write-able then use a template file.
123  if( !fn.GetDirCount() || !fn.IsOk() || !wxFileName::IsDirWritable( path ) )
124  {
125  // return a template filename now.
126 
127  // this next line is likely a problem now, since it relies on an
128  // application title which is no longer constant or known. This next line needs
129  // to be re-thought out.
130 
131 #ifndef __WXMAC__
132  fn.AssignDir( wxStandardPaths::Get().GetUserConfigDir() );
133 #else
134  // don't pollute home folder, temp folder seems to be more appropriate
135  fn.AssignDir( wxStandardPaths::Get().GetTempDir() );
136 #endif
137 
138 #if defined( __WINDOWS__ )
139  fn.AppendDir( wxT( "kicad" ) );
140 #endif
141 
142  /*
143  The footprint library table name used when no project file is passed
144  to Pcbnew or CvPcb. This is used temporarily to store the project
145  specific library table until the project file being edited is saved.
146  It is then moved to the file fp-lib-table in the folder where the
147  project file is saved.
148  */
149  fn.SetName( wxT( "prj-fp-lib-table" ) );
150  }
151  else // normal path.
152  {
153  fn.SetName( wxT( "fp-lib-table" ) );
154  }
155 
156  fn.ClearExt();
157 
158  return fn.GetFullPath();
159 }
160 
161 
162 void PROJECT::SetRString( RSTRING_T aIndex, const wxString& aString )
163 {
164  unsigned ndx = unsigned( aIndex );
165 
166  if( ndx < DIM( m_rstrings ) )
167  {
168  m_rstrings[ndx] = aString;
169  }
170  else
171  {
172  wxASSERT( 0 ); // bad index
173  }
174 }
175 
176 
177 const wxString& PROJECT::GetRString( RSTRING_T aIndex )
178 {
179  unsigned ndx = unsigned( aIndex );
180 
181  if( ndx < DIM( m_rstrings ) )
182  {
183  return m_rstrings[ndx];
184  }
185  else
186  {
187  static wxString no_cookie_for_you;
188 
189  wxASSERT( 0 ); // bad index
190 
191  return no_cookie_for_you;
192  }
193 }
194 
195 
197 {
198  // This is virtual, so implement it out of line
199 
200  if( unsigned( aIndex ) < DIM( m_elems ) )
201  {
202  return m_elems[aIndex];
203  }
204  return NULL;
205 }
206 
207 
208 void PROJECT::SetElem( ELEM_T aIndex, _ELEM* aElem )
209 {
210  // This is virtual, so implement it out of line
211 
212  if( unsigned( aIndex ) < DIM( m_elems ) )
213  {
214 #if defined(DEBUG) && 0
215  if( aIndex == ELEM_SCH_PART_LIBS )
216  {
217  printf( "%s: &m_elems[%i]:%p aElem:%p\n", __func__, aIndex, &m_elems[aIndex], aElem );
218  }
219 #endif
220  delete m_elems[aIndex];
221  m_elems[aIndex] = aElem;
222  }
223 }
224 
225 
226 static bool copy_pro_file_template( const SEARCH_STACK& aSearchS, const wxString& aDestination )
227 {
228  if( aDestination.IsEmpty() )
229  {
230  DBG( printf( "%s: destination is empty.\n", __func__ );)
231  return false;
232  }
233 
234  wxString templateFile = wxT( "kicad." ) + ProjectFileExtension;
235 
236  wxString kicad_pro_template = aSearchS.FindValidPath( templateFile );
237 
238  if( !kicad_pro_template )
239  {
240  DBG( printf( "%s: template file '%s' not found using search paths.\n", __func__, TO_UTF8( templateFile ) );)
241 
242  wxFileName templ( wxStandardPaths::Get().GetDocumentsDir(),
243  wxT( "kicad" ), ProjectFileExtension );
244 
245  if( !templ.IsFileReadable() )
246  {
247  wxString msg = wxString::Format( _(
248  "Unable to find '%s' template config file." ),
249  GetChars( templateFile ) );
250 
251  DisplayErrorMessage( nullptr, _( "Error copying project file template" ), msg );
252 
253  return false;
254  }
255 
256  kicad_pro_template = templ.GetFullPath();
257  }
258 
259  DBG( printf( "%s: using template file '%s' as project file.\n", __func__, TO_UTF8( kicad_pro_template ) );)
260 
261  // Verify aDestination can be created. if this is not the case, wxCopyFile
262  // will generate a crappy log error message, and we *do not want* this kind
263  // of stupid message
264  wxFileName fn( aDestination );
265  bool success = true;
266 
267  if( fn.IsOk() && fn.IsDirWritable() )
268  success = wxCopyFile( kicad_pro_template, aDestination );
269  else
270  {
271  wxLogMessage( _( "Cannot create prj file '%s' (Directory not writable)" ),
272  GetChars( aDestination) );
273  success = false;
274  }
275 
276  return success;
277 }
278 
279 
280 wxConfigBase* PROJECT::configCreate( const SEARCH_STACK& aSList,
281  const wxString& aGroupName, const wxString& aProjectFileName )
282 {
283  wxConfigBase* cfg = 0;
284  wxString cur_pro_fn = !aProjectFileName ? GetProjectFullName() : aProjectFileName;
285 
286  if( wxFileName( cur_pro_fn ).IsFileReadable() )
287  {
288  // Note: currently, aGroupName is not used.
289  // Previoulsy, the version of aGroupName was tested, but it
290  // was useless, and if the version is important,
291  // this is not the right place here, because configCreate does know anything
292  // about info stored in this config file.
293  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
294  return cfg;
295  }
296 
297  // No suitable pro file was found, either does not exist, or not readable.
298  // Use the template kicad.pro file. Find it by using caller's SEARCH_STACK.
299  copy_pro_file_template( aSList, cur_pro_fn );
300 
301  cfg = new wxFileConfig( wxEmptyString, wxEmptyString, cur_pro_fn, wxEmptyString );
302 
303  return cfg;
304 }
305 
306 
307 void PROJECT::ConfigSave( const SEARCH_STACK& aSList, const wxString& aGroupName,
308  const PARAM_CFG_ARRAY& aParams, const wxString& aFileName )
309 {
310  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aFileName ) );
311 
312  if( !cfg.get() )
313  {
314  // could not find template
315  return;
316  }
317 
318  cfg->SetPath( wxT( "/" ) );
319 
320  cfg->Write( wxT( "update" ), DateAndTime() );
321 
322  // @todo: pass in aLastClient wxString:
323  cfg->Write( wxT( "last_client" ), Pgm().App().GetAppName() );
324 
325  // Save parameters
326  cfg->DeleteGroup( aGroupName ); // Erase all data
327  cfg->Flush();
328 
329  cfg->SetPath( aGroupName );
330  cfg->Write( wxT( "version" ), CONFIG_VERSION );
331 
332  cfg->SetPath( wxT( "/" ) );
333 
334  wxConfigSaveParams( cfg.get(), aParams, aGroupName );
335 
336  cfg->SetPath( wxT( "/" ) );
337 
338  // cfg is deleted here by std::unique_ptr, that saves the *.pro file to disk
339 }
340 
341 
342 bool PROJECT::ConfigLoad( const SEARCH_STACK& aSList, const wxString& aGroupName,
343  const PARAM_CFG_ARRAY& aParams, const wxString& aForeignProjectFileName )
344 {
345  std::unique_ptr<wxConfigBase> cfg( configCreate( aSList, aGroupName, aForeignProjectFileName ) );
346 
347  if( !cfg.get() )
348  {
349  // could not find template
350  return false;
351  }
352 
353  cfg->SetPath( wxCONFIG_PATH_SEPARATOR );
354 
355  wxString timestamp = cfg->Read( wxT( "update" ) );
356 
357  m_pro_date_and_time = timestamp;
358 
359  // We do not want expansion of env var values when reading our project config file
360  bool state = cfg.get()->IsExpandingEnvVars();
361  cfg.get()->SetExpandEnvVars( false );
362 
363  wxConfigLoadParams( cfg.get(), aParams, aGroupName );
364 
365  cfg.get()->SetExpandEnvVars( state );
366 
367  return true;
368 }
369 
370 
371 const wxString PROJECT::AbsolutePath( const wxString& aFileName ) const
372 {
373  wxFileName fn = aFileName;
374 
375  if( !fn.IsAbsolute() )
376  {
377  wxString pro_dir = wxPathOnly( GetProjectFullName() );
378  fn.Normalize( wxPATH_NORM_ALL, pro_dir );
379  }
380 
381  return fn.GetFullPath();
382 }
383 
384 
386 {
387  // This is a lazy loading function, it loads the project specific table when
388  // that table is asked for, not before.
389 
391 
392  // its gotta be NULL or a FP_LIB_TABLE, or a bug.
393  wxASSERT( !tbl || dynamic_cast<FP_LIB_TABLE*>( tbl ) );
394 
395  if( !tbl )
396  {
397  // Stack the project specific FP_LIB_TABLE overlay on top of the global table.
398  // ~FP_LIB_TABLE() will not touch the fallback table, so multiple projects may
399  // stack this way, all using the same global fallback table.
400  KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB );
401  if( kiface )
403 
404  wxASSERT( tbl );
405  SetElem( ELEM_FPTBL, tbl );
406 
407  wxString projectFpLibTableFileName = FootprintLibTblName();
408 
409  try
410  {
411  tbl->Load( projectFpLibTableFileName );
412  }
413  catch( const IO_ERROR& ioe )
414  {
415  DisplayErrorMessage( NULL, _( "Error loading project footprint library table" ), ioe.What() );
416  }
417  }
418 
419  return tbl;
420 }
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:280
_ELEM * m_elems[ELEM_COUNT]
Definition: project.h:314
#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:228
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:307
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:96
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:196
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:208
#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
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:85
wxFileName m_project_name
/.pro
Definition: project.h:307
VTBL_ENTRY const wxString AbsolutePath(const wxString &aFileName) const
Function AbsolutePath fixes up aFileName if it is relative to the project's directory to be an absolu...
Definition: project.cpp:371
The common library.
Class KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within...
Definition: kiway.h:257
pcbnew DSO
Definition: kiway.h:266
VTBL_ENTRY KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Function KiFACE returns the KIFACE* given a FACE_T.
Definition: kiway.cpp:150
VTBL_ENTRY const wxString FootprintLibTblName() const
Function FootprintLibTblName returns the path and filename of this project's fp-lib-table, i.e.
Definition: project.cpp:114
void Load(const wxString &aFileName)
Function Load.
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:65
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:226
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:162
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:177
ELEM_T
Enum ELEM_T is the set of _ELEMs that a PROJECT can hold.
Definition: project.h:194
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:155
wxString m_rstrings[RSTRING_COUNT]
Definition: project.h:311
Class KIFACE is used by a participant in the KIWAY alchemy.
Definition: kiway.h:150
#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:66
return & kiface
Definition: pcbnew.cpp:207
#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:342
wxString m_pro_date_and_time
Definition: project.h:308
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:71
~PROJECT()
Definition: project.cpp:59
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:108