KiCad PCB EDA Suite
Loading...
Searching...
No Matches
scratch_doc.h
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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * 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/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 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#ifndef KICAD_SCRATCH_DOC_H
25#define KICAD_SCRATCH_DOC_H
26
27#include <functional>
28#include <memory>
29#include <utility>
30
31#include <kiid.h>
32#include <project.h>
35
36#include <wx/filename.h>
37#include <wx/string.h>
38
39
56{
57public:
58 SCRATCH_PROJECT() = default;
59
60 SCRATCH_PROJECT( SETTINGS_MANAGER& aMgr, const wxString& aDocOrProjectPath,
61 bool aRequireProjectFile = false ) :
62 m_mgr( &aMgr )
63 {
64 wxFileName pro( aDocOrProjectPath );
66 pro.MakeAbsolute();
67
68 const wxString projectPath = pro.GetFullPath();
69 const bool fileMissing = !pro.FileExists();
70
71 // Even if a defaults-only slot was loaded earlier by someone else,
72 // refuse when the caller demands a real .kicad_pro that doesn't exist.
73 // Otherwise the import dialog accepts garbage from a stale slot.
74 if( aRequireProjectFile && fileMissing )
75 return;
76
77 if( PROJECT* existing = aMgr.GetProject( projectPath ) )
78 {
79 // Someone else already loaded this slot; don't UnloadProject in
80 // our dtor or we'd pull the rug out from under them.
81 m_project = existing;
82 m_owned = false;
83 return;
84 }
85
86 const bool loadOk = aMgr.LoadProject( projectPath, /*aSetActive=*/false );
87 m_project = aMgr.GetProject( projectPath );
88
89 if( !m_project )
90 return;
91
92 m_owned = true;
93
94 // Reject a malformed .kicad_pro when the file is present but failed
95 // to parse — a defaults-only slot is not a substitute.
96 if( !loadOk && !fileMissing )
97 {
98 release();
99 m_project = nullptr;
100 }
101 }
102
104
107
108 SCRATCH_PROJECT( SCRATCH_PROJECT&& aOther ) noexcept { *this = std::move( aOther ); }
109
111 {
112 if( this != &aOther )
113 {
114 release();
115
116 m_mgr = std::exchange( aOther.m_mgr, nullptr );
117 m_project = std::exchange( aOther.m_project, nullptr );
118 m_owned = std::exchange( aOther.m_owned, false );
119 }
120
121 return *this;
122 }
123
124 PROJECT* GetProject() const { return m_project; }
125
128 bool IsValid() const { return m_project != nullptr; }
129
130private:
131 void release()
132 {
133 if( m_owned && m_mgr && m_project && m_project != &m_mgr->Prj() )
134 m_mgr->UnloadProject( m_project, false );
135
136 m_owned = false;
137 }
138
140 PROJECT* m_project = nullptr;
141 bool m_owned = false;
142};
143
144
164template<typename DOC>
166{
167 using CLEAR_FN = std::function<void( DOC* )>;
168
170 std::unique_ptr<DOC> doc;
171 PROJECT* project = nullptr;
173
180 bool ownsProject = false;
181
182 SCRATCH_DOC() = default;
183
184 SCRATCH_DOC( const SCRATCH_DOC& ) = delete;
185 SCRATCH_DOC& operator=( const SCRATCH_DOC& ) = delete;
186
187 SCRATCH_DOC( SCRATCH_DOC&& aOther ) noexcept { *this = std::move( aOther ); }
188
189 SCRATCH_DOC& operator=( SCRATCH_DOC&& aOther ) noexcept
190 {
191 if( this != &aOther )
192 {
193 release();
194
195 mgr = std::exchange( aOther.mgr, nullptr );
196 doc = std::move( aOther.doc );
197 project = std::exchange( aOther.project, nullptr );
198 ownsProject = std::exchange( aOther.ownsProject, false );
199 clearProject = std::move( aOther.clearProject );
200 }
201
202 return *this;
203 }
204
206
207private:
208 void release()
209 {
210 if( doc && clearProject )
211 clearProject( doc.get() );
212
213 doc.reset();
214
215 if( mgr && project && ownsProject && project != &mgr->Prj() )
216 mgr->UnloadProject( project, false );
217
218 mgr = nullptr;
219 project = nullptr;
220 ownsProject = false;
221 }
222};
223
224
231template<typename DOC, typename Loader, typename ClearFn>
232SCRATCH_DOC<DOC> LoadScratchDoc( SETTINGS_MANAGER& aMgr, const wxString& aDocPath,
233 Loader aLoader, ClearFn aClearFn )
234{
236 out.mgr = &aMgr;
237 out.clearProject = aClearFn;
238
239 if( aDocPath.IsEmpty() )
240 return out;
241
242 wxFileName pro( aDocPath );
243 pro.SetExt( FILEEXT::ProjectFileExtension );
244 pro.MakeAbsolute();
245
246 // A document bound to the editor's live project would share and then tear down its
247 // settings, so a live path gets a scratch project of its own.
248 if( PROJECT* active = aMgr.GetProject( pro.GetFullPath() ); active && active == &aMgr.Prj() )
249 {
250 pro = wxFileName( wxFileName::GetTempDir(), wxS( "kicad_scratch_" ) + KIID().AsString() );
251 pro.SetExt( FILEEXT::ProjectFileExtension );
252 }
253
254 // LoadProject returns true both when newly loaded AND when already
255 // present in the manager (see SETTINGS_MANAGER::LoadProject line 970).
256 // We need to know which case we hit so SCRATCH_DOC::release() doesn't
257 // double-unload a project shared with another SCRATCH_DOC — query the
258 // manager BEFORE LoadProject so we can compare.
259 const bool projectAlreadyLoaded = ( aMgr.GetProject( pro.GetFullPath() ) != nullptr );
260
261 // LoadProject returns false but still inserts a defaults-only PROJECT
262 // when no .kicad_pro exists alongside the document. A present-but-
263 // malformed .kicad_pro should be a hard failure though, otherwise the
264 // diff/merge runs against garbage settings.
265 const bool projectLoadOk = aMgr.LoadProject( pro.GetFullPath(), /*aSetActive=*/false );
266 out.project = aMgr.GetProject( pro.GetFullPath() );
267
268 if( !out.project )
269 return out;
270
271 out.ownsProject = !projectAlreadyLoaded;
272
273 if( !projectLoadOk && pro.FileExists() )
274 {
275 if( out.ownsProject && out.project != &aMgr.Prj() )
276 aMgr.UnloadProject( out.project, false );
277
278 out.project = nullptr;
279 out.ownsProject = false;
280 return out;
281 }
282
283 out.doc = aLoader( out.project );
284
285 return out;
286}
287
288#endif // KICAD_SCRATCH_DOC_H
Definition kiid.h:46
Container for project specific data.
Definition project.h:63
SCRATCH_PROJECT(SCRATCH_PROJECT &&aOther) noexcept
SCRATCH_PROJECT()=default
bool IsValid() const
True iff the project loaded (or was already loaded).
SCRATCH_PROJECT(SETTINGS_MANAGER &aMgr, const wxString &aDocOrProjectPath, bool aRequireProjectFile=false)
Definition scratch_doc.h:60
PROJECT * GetProject() const
SETTINGS_MANAGER * m_mgr
SCRATCH_PROJECT(const SCRATCH_PROJECT &)=delete
PROJECT * m_project
SCRATCH_PROJECT & operator=(const SCRATCH_PROJECT &)=delete
SCRATCH_PROJECT & operator=(SCRATCH_PROJECT &&aOther) noexcept
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
static const std::string ProjectFileExtension
SCRATCH_DOC< DOC > LoadScratchDoc(SETTINGS_MANAGER &aMgr, const wxString &aDocPath, Loader aLoader, ClearFn aClearFn)
Construct a SCRATCH_DOC by loading a project non-active and then handing it to the caller's document ...
Move-only RAII wrapper for "load a KiCad document into a non-active scratch PROJECT and clean up afte...
SCRATCH_DOC(SCRATCH_DOC &&aOther) noexcept
PROJECT * project
bool ownsProject
True when this load created the project (LoadScratchDoc found the path not yet in the SettingsManager...
SCRATCH_DOC()=default
std::function< void(DOC *)> CLEAR_FN
SCRATCH_DOC & operator=(SCRATCH_DOC &&aOther) noexcept
CLEAR_FN clearProject
SCRATCH_DOC(const SCRATCH_DOC &)=delete
SCRATCH_DOC & operator=(const SCRATCH_DOC &)=delete
std::unique_ptr< DOC > doc
void release()
SETTINGS_MANAGER * mgr
Definition of file extensions used in Kicad.