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 <project.h>
34
35#include <wx/filename.h>
36#include <wx/string.h>
37
38
55{
56public:
57 SCRATCH_PROJECT() = default;
58
59 SCRATCH_PROJECT( SETTINGS_MANAGER& aMgr, const wxString& aDocOrProjectPath,
60 bool aRequireProjectFile = false ) :
61 m_mgr( &aMgr )
62 {
63 wxFileName pro( aDocOrProjectPath );
65 pro.MakeAbsolute();
66
67 const wxString projectPath = pro.GetFullPath();
68 const bool fileMissing = !pro.FileExists();
69
70 // Even if a defaults-only slot was loaded earlier by someone else,
71 // refuse when the caller demands a real .kicad_pro that doesn't exist.
72 // Otherwise the import dialog accepts garbage from a stale slot.
73 if( aRequireProjectFile && fileMissing )
74 return;
75
76 if( PROJECT* existing = aMgr.GetProject( projectPath ) )
77 {
78 // Someone else already loaded this slot; don't UnloadProject in
79 // our dtor or we'd pull the rug out from under them.
80 m_project = existing;
81 m_owned = false;
82 return;
83 }
84
85 const bool loadOk = aMgr.LoadProject( projectPath, /*aSetActive=*/false );
86 m_project = aMgr.GetProject( projectPath );
87
88 if( !m_project )
89 return;
90
91 m_owned = true;
92
93 // Reject a malformed .kicad_pro when the file is present but failed
94 // to parse — a defaults-only slot is not a substitute.
95 if( !loadOk && !fileMissing )
96 {
97 release();
98 m_project = nullptr;
99 }
100 }
101
103
106
107 SCRATCH_PROJECT( SCRATCH_PROJECT&& aOther ) noexcept { *this = std::move( aOther ); }
108
110 {
111 if( this != &aOther )
112 {
113 release();
114
115 m_mgr = std::exchange( aOther.m_mgr, nullptr );
116 m_project = std::exchange( aOther.m_project, nullptr );
117 m_owned = std::exchange( aOther.m_owned, false );
118 }
119
120 return *this;
121 }
122
123 PROJECT* GetProject() const { return m_project; }
124
127 bool IsValid() const { return m_project != nullptr; }
128
129private:
130 void release()
131 {
132 if( m_owned && m_mgr && m_project && m_project != &m_mgr->Prj() )
133 m_mgr->UnloadProject( m_project, false );
134
135 m_owned = false;
136 }
137
139 PROJECT* m_project = nullptr;
140 bool m_owned = false;
141};
142
143
163template<typename DOC>
165{
166 using CLEAR_FN = std::function<void( DOC* )>;
167
169 std::unique_ptr<DOC> doc;
170 PROJECT* project = nullptr;
172
179 bool ownsProject = false;
180
181 SCRATCH_DOC() = default;
182
183 SCRATCH_DOC( const SCRATCH_DOC& ) = delete;
184 SCRATCH_DOC& operator=( const SCRATCH_DOC& ) = delete;
185
186 SCRATCH_DOC( SCRATCH_DOC&& aOther ) noexcept { *this = std::move( aOther ); }
187
188 SCRATCH_DOC& operator=( SCRATCH_DOC&& aOther ) noexcept
189 {
190 if( this != &aOther )
191 {
192 release();
193
194 mgr = std::exchange( aOther.mgr, nullptr );
195 doc = std::move( aOther.doc );
196 project = std::exchange( aOther.project, nullptr );
197 ownsProject = std::exchange( aOther.ownsProject, false );
198 clearProject = std::move( aOther.clearProject );
199 }
200
201 return *this;
202 }
203
205
206private:
207 void release()
208 {
209 if( doc && clearProject )
210 clearProject( doc.get() );
211
212 doc.reset();
213
214 if( mgr && project && ownsProject && project != &mgr->Prj() )
215 mgr->UnloadProject( project, false );
216
217 mgr = nullptr;
218 project = nullptr;
219 ownsProject = false;
220 }
221};
222
223
230template<typename DOC, typename Loader, typename ClearFn>
231SCRATCH_DOC<DOC> LoadScratchDoc( SETTINGS_MANAGER& aMgr, const wxString& aDocPath,
232 Loader aLoader, ClearFn aClearFn )
233{
235 out.mgr = &aMgr;
236 out.clearProject = aClearFn;
237
238 if( aDocPath.IsEmpty() )
239 return out;
240
241 wxFileName pro( aDocPath );
242 pro.SetExt( FILEEXT::ProjectFileExtension );
243 pro.MakeAbsolute();
244
245 // LoadProject returns true both when newly loaded AND when already
246 // present in the manager (see SETTINGS_MANAGER::LoadProject line 970).
247 // We need to know which case we hit so SCRATCH_DOC::release() doesn't
248 // double-unload a project shared with another SCRATCH_DOC — query the
249 // manager BEFORE LoadProject so we can compare.
250 const bool projectAlreadyLoaded = ( aMgr.GetProject( pro.GetFullPath() ) != nullptr );
251
252 // LoadProject returns false but still inserts a defaults-only PROJECT
253 // when no .kicad_pro exists alongside the document. A present-but-
254 // malformed .kicad_pro should be a hard failure though, otherwise the
255 // diff/merge runs against garbage settings.
256 const bool projectLoadOk = aMgr.LoadProject( pro.GetFullPath(), /*aSetActive=*/false );
257 out.project = aMgr.GetProject( pro.GetFullPath() );
258
259 if( !out.project )
260 return out;
261
262 out.ownsProject = !projectAlreadyLoaded;
263
264 if( !projectLoadOk && pro.FileExists() )
265 {
266 if( out.ownsProject && out.project != &aMgr.Prj() )
267 aMgr.UnloadProject( out.project, false );
268
269 out.project = nullptr;
270 out.ownsProject = false;
271 return out;
272 }
273
274 out.doc = aLoader( out.project );
275
276 return out;
277}
278
279#endif // KICAD_SCRATCH_DOC_H
Container for project specific data.
Definition project.h:62
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:59
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.