1 /*
2  * LibrePCB - Professional EDA for everyone!
3  * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4  * https://librepcb.org/
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef LIBREPCB_PROJECT_PROJECT_H
21 #define LIBREPCB_PROJECT_PROJECT_H
22 
23 /*******************************************************************************
24  *  Includes
25  ******************************************************************************/
26 #include <librepcb/common/attributes/attribute.h>
27 #include <librepcb/common/attributes/attributeprovider.h>
28 #include <librepcb/common/elementname.h>
29 #include <librepcb/common/exceptions.h>
30 #include <librepcb/common/fileio/directorylock.h>
31 #include <librepcb/common/fileio/transactionaldirectory.h>
32 #include <librepcb/common/uuid.h>
33 #include <librepcb/common/version.h>
34 
35 #include <QtCore>
36 
37 /*******************************************************************************
38  *  Namespace / Forward Declarations
39  ******************************************************************************/
40 class QPrinter;
41 
42 namespace librepcb {
43 
44 class StrokeFontPool;
45 
46 namespace project {
47 
48 class ProjectMetadata;
49 class ProjectSettings;
50 class ProjectLibrary;
51 class Circuit;
52 class Schematic;
53 class SchematicLayerProvider;
54 class ErcMsgList;
55 class Board;
56 
57 /*******************************************************************************
58  *  Class Project
59  ******************************************************************************/
60 
61 /**
62  * @brief The Project class represents a whole (opened) project with all its
63  * content
64  *
65  * This class represents a whole project with all the content of its directory:
66  *  - circuit, schematics and boards
67  *  - the project's library
68  *  - project settings
69  *  - and much more...
70  *
71  * The constructor of the ::librepcb::project::Project class needs the filepath
72  * to a project file. Then the project will be opened. A new project can be
73  * created with the static method #create(). The destructor will close the
74  * project (without saving). Use the method #save() to write the whole project
75  * to the harddisc.
76  *
77  * @note !! A detailed description about projects is available here: @ref
78  * doc_project !!
79  */
80 class Project final : public QObject, public AttributeProvider {
81   Q_OBJECT
82 
83 public:
84   // Constructors / Destructor
85   Project() = delete;
86   Project(const Project& other) = delete;
87 
88   /**
89    * @brief The constructor to open an existing project with all its content
90    *
91    * @param directory     The directory which contains the project.
92    * @param filename      The filename of the *.lpp project file.
93    *
94    * @throw Exception     If the project could not be opened successfully
95    */
Project(std::unique_ptr<TransactionalDirectory> directory,const QString & filename)96   Project(std::unique_ptr<TransactionalDirectory> directory,
97           const QString& filename)
98     : Project(std::move(directory), filename, false) {}
99 
100   /**
101    * @brief The destructor will close the whole project (without saving!)
102    */
103   ~Project() noexcept;
104 
105   // Getters: General
106 
107   /**
108    * @brief Get the filepath of the project file (*.lpp)
109    *
110    * @return The absolute filepath
111    */
getFilepath()112   FilePath getFilepath() const noexcept {
113     return mDirectory->getAbsPath(mFilename);
114   }
115 
116   /**
117    * @brief Get the path to the project directory
118    *
119    * @return The filepath to the project directory
120    */
getPath()121   FilePath getPath() const noexcept { return mDirectory->getAbsPath(); }
122 
getDirectory()123   const TransactionalDirectory& getDirectory() const noexcept {
124     return *mDirectory;
125   }
126 
getDirectory()127   TransactionalDirectory& getDirectory() noexcept { return *mDirectory; }
128 
129   /**
130    * @brief Get the StrokeFontPool which contains all stroke fonts of the
131    * project
132    *
133    * @return A reference to the librepcb::StrokeFontPool object
134    */
getStrokeFonts()135   StrokeFontPool& getStrokeFonts() const noexcept { return *mStrokeFontPool; }
136 
137   /**
138    * @brief Get the ProjectMetadata object which contains all project metadata
139    *
140    * @return A reference to the ProjectMetadata object
141    */
getMetadata()142   ProjectMetadata& getMetadata() const noexcept { return *mProjectMetadata; }
143 
144   /**
145    * @brief Get the ProjectSettings object which contains all project settings
146    *
147    * @return A reference to the ProjectSettings object
148    */
getSettings()149   ProjectSettings& getSettings() const noexcept { return *mProjectSettings; }
150 
151   /**
152    * @brief Get the ProjectLibrary object which contains all library elements
153    * used in this project
154    *
155    * @return A reference to the ProjectLibrary object
156    */
getLibrary()157   ProjectLibrary& getLibrary() const noexcept { return *mProjectLibrary; }
158 
159   /**
160    * @brief Get the ERC messages list
161    *
162    * @return A reference to the ErcMsgList object
163    */
getErcMsgList()164   ErcMsgList& getErcMsgList() const noexcept { return *mErcMsgList; }
165 
166   /**
167    * @brief Get the Circuit object
168    *
169    * @return A reference to the Circuit object
170    */
getCircuit()171   Circuit& getCircuit() const noexcept { return *mCircuit; }
172 
173   // Schematic Methods
174 
getLayers()175   SchematicLayerProvider& getLayers() noexcept {
176     return *mSchematicLayerProvider;
177   }
getLayers()178   const SchematicLayerProvider& getLayers() const noexcept {
179     return *mSchematicLayerProvider;
180   }
181 
182   /**
183    * @brief Get the page index of a specific schematic
184    *
185    * @return the schematic index (-1 if the schematic does not exist)
186    */
187   int getSchematicIndex(const Schematic& schematic) const noexcept;
188 
189   /**
190    * @brief Get all schematics
191    *
192    * @return A QList with all schematics
193    */
getSchematics()194   const QList<Schematic*>& getSchematics() const noexcept {
195     return mSchematics;
196   }
197 
198   /**
199    * @brief Get the schematic page at a specific index
200    *
201    * @param index     The page index (zero is the first)
202    *
203    * @return A pointer to the specified schematic, or nullptr if index is
204    * invalid
205    */
getSchematicByIndex(int index)206   Schematic* getSchematicByIndex(int index) const noexcept {
207     return mSchematics.value(index, nullptr);
208   }
209 
210   /**
211    * @brief Get the schematic page with a specific UUID
212    *
213    * @param uuid      The schematic UUID
214    *
215    * @return A pointer to the specified schematic, or nullptr if uuid is invalid
216    */
217   Schematic* getSchematicByUuid(const Uuid& uuid) const noexcept;
218 
219   /**
220    * @brief Get the schematic page with a specific name
221    *
222    * @param name      The schematic name
223    *
224    * @return A pointer to the specified schematic, or nullptr if name is invalid
225    */
226   Schematic* getSchematicByName(const QString& name) const noexcept;
227 
228   /**
229    * @brief Create a new schematic (page)
230    *
231    * @param name  The schematic page name
232    *
233    * @return A pointer to the new schematic
234    *
235    * @throw Exception This method throws an exception on error.
236    */
237   Schematic* createSchematic(const ElementName& name);
238 
239   /**
240    * @brief Add an existing schematic to this project
241    *
242    * @param schematic     The schematic to add
243    * @param newIndex      The desired index in the list (after inserting it)
244    *
245    * @throw Exception     On error
246    *
247    * @undocmd{::librepcb::project::CmdSchematicAdd}
248    */
249   void addSchematic(Schematic& schematic, int newIndex = -1);
250 
251   /**
252    * @brief Remove a schematic from this project
253    *
254    * @param schematic         The schematic to remove
255    * @param deleteSchematic   If true, the schematic object will be deleted
256    *                          (Set this to true only when called from ctor or
257    * dtor!!)
258    *
259    * @throw Exception     On error
260    *
261    * @undocmd{::librepcb::project::CmdSchematicRemove}
262    */
263   void removeSchematic(Schematic& schematic, bool deleteSchematic = false);
264 
265   /**
266    * @brief Export the schematic pages as a PDF
267    *
268    * @param filepath  The filepath where the PDF should be saved. If the file
269    * exists already, it will be overwritten.
270    *
271    * @throw Exception     On error
272    */
273   void exportSchematicsAsPdf(const FilePath& filepath);
274 
275   /**
276    * @brief Print some schematics to a QPrinter (printer or file)
277    *
278    * @param printer   The QPrinter where to print the schematic pages
279    * @param pages     A list with all schematic page indexes which should be
280    * printed
281    *
282    * @throw Exception     On error
283    */
284   void printSchematicPages(QPrinter& printer, QList<int>& pages);
285 
286   // Board Methods
287 
288   /**
289    * @brief Get the index of a specific board
290    *
291    * @return the board index (-1 if the board does not exist)
292    */
293   int getBoardIndex(const Board& board) const noexcept;
294 
295   /**
296    * @brief Get all boards
297    *
298    * @return A QList with all boards
299    */
getBoards()300   const QList<Board*>& getBoards() const noexcept { return mBoards; }
301 
302   /**
303    * @brief Get the board at a specific index
304    *
305    * @param index     The board index (zero is the first)
306    *
307    * @return A pointer to the specified board, or nullptr if index is invalid
308    */
getBoardByIndex(int index)309   Board* getBoardByIndex(int index) const noexcept {
310     return mBoards.value(index, nullptr);
311   }
312 
313   /**
314    * @brief Get the board with a specific UUID
315    *
316    * @param uuid      The board UUID
317    *
318    * @return A pointer to the specified board, or nullptr if uuid is invalid
319    */
320   Board* getBoardByUuid(const Uuid& uuid) const noexcept;
321 
322   /**
323    * @brief Get the board with a specific name
324    *
325    * @param name      The board name
326    *
327    * @return A pointer to the specified board, or nullptr if name is invalid
328    */
329   Board* getBoardByName(const QString& name) const noexcept;
330 
331   /**
332    * @brief Create a new board
333    *
334    * @param name  The board name
335    *
336    * @return A pointer to the new board
337    *
338    * @throw Exception This method throws an exception on error.
339    */
340   Board* createBoard(const ElementName& name);
341 
342   /**
343    * @brief Create a new board as a copy of an existing board
344    *
345    * @param other The board to copy
346    * @param name  The board name
347    *
348    * @return A pointer to the new board
349    *
350    * @throw Exception This method throws an exception on error.
351    */
352   Board* createBoard(const Board& other, const ElementName& name);
353 
354   /**
355    * @brief Add an existing board to this project
356    *
357    * @param board         The board to add
358    * @param newIndex      The desired index in the list (after inserting it)
359    *
360    * @throw Exception     On error
361    *
362    * @undocmd{::librepcb::project::CmdBoardAdd}
363    */
364   void addBoard(Board& board, int newIndex = -1);
365 
366   /**
367    * @brief Remove a board from this project
368    *
369    * @param board             The board to remove
370    * @param deleteBoard       If true, the board object will be deleted
371    *                          (Set this to true only when called from ctor or
372    * dtor!!)
373    *
374    * @throw Exception     On error
375    *
376    * @undocmd{::librepcb::project::CmdBoardRemove}
377    */
378   void removeBoard(Board& board, bool deleteBoard = false);
379 
380   // General Methods
381 
382   /**
383    * @brief Save the project to the transactional file system
384    *
385    * @throw Exception     If an error occurred.
386    */
387   void save();
388 
389   // Inherited from AttributeProvider
390   /// @copydoc librepcb::AttributeProvider::getUserDefinedAttributeValue()
391   QString getUserDefinedAttributeValue(const QString& key) const
392       noexcept override;
393   /// @copydoc librepcb::AttributeProvider::getBuiltInAttributeValue()
394   QString getBuiltInAttributeValue(const QString& key) const noexcept override;
395 
396   // Operator Overloadings
397   bool operator==(const Project& rhs) noexcept { return (this == &rhs); }
398   bool operator!=(const Project& rhs) noexcept { return (this != &rhs); }
399 
400   // Static Methods
401 
create(std::unique_ptr<TransactionalDirectory> directory,const QString & filename)402   static Project* create(std::unique_ptr<TransactionalDirectory> directory,
403                          const QString& filename) {
404     return new Project(std::move(directory), filename, true);
405   }
406 
407   static bool isFilePathInsideProjectDirectory(const FilePath& fp) noexcept;
408   static bool isProjectFile(const FilePath& file) noexcept;
409   static bool isProjectDirectory(const FilePath& dir) noexcept;
410   static Version getProjectFileFormatVersion(const FilePath& dir);
411 
412 signals:
413 
414   /// @copydoc AttributeProvider::attributesChanged()
415   void attributesChanged() override;
416 
417   /**
418    * @brief This signal is emitted after a schematic was added to the project
419    *
420    * @param newIndex  The index of the added schematic
421    */
422   void schematicAdded(int newIndex);
423 
424   /**
425    * @brief This signal is emitted after a schematic was removed from the
426    * project
427    *
428    * @param oldIndex  The index of the removed schematic
429    */
430   void schematicRemoved(int oldIndex);
431 
432   /**
433    * @brief This signal is emitted after a board was added to the project
434    *
435    * @param newIndex  The index of the added board
436    */
437   void boardAdded(int newIndex);
438 
439   /**
440    * @brief This signal is emitted after a board was removed from the project
441    *
442    * @param oldIndex  The index of the removed board
443    */
444   void boardRemoved(int oldIndex);
445 
446 private:
447   // Private Methods
448 
449   /**
450    * @brief The constructor to create or open a project with all its content
451    *
452    * @param directory     The directory which contains the project.
453    * @param filename      The filename of the *.lpp project file.
454    * @param create        True if the specified project does not exist already
455    *                      and must be created.
456    *
457    * @throw Exception     If the project could not be created/opened
458    * successfully
459    *
460    * @todo Remove interactive message boxes, should be done at a higher layer!
461    */
462   explicit Project(std::unique_ptr<TransactionalDirectory> directory,
463                    const QString& filename, bool create);
464 
465   std::unique_ptr<TransactionalDirectory> mDirectory;
466   QString mFilename;  ///< the name of the *.lpp project file
467 
468   // General
469   QScopedPointer<StrokeFontPool>
470       mStrokeFontPool;  ///< all fonts from ./resources/fontobene/
471   QScopedPointer<ProjectMetadata>
472       mProjectMetadata;  ///< e.g. project name, author, ...
473   QScopedPointer<ProjectSettings>
474       mProjectSettings;  ///< all project specific settings
475   QScopedPointer<ProjectLibrary>
476       mProjectLibrary;  ///< the library which contains all elements needed in
477                         ///< this project
478   QScopedPointer<ErcMsgList>
479       mErcMsgList;  ///< A list which contains all electrical rule check (ERC)
480                     ///< messages
481   QScopedPointer<Circuit>
482       mCircuit;  ///< The whole circuit of this project (contains all
483                  ///< netclasses, netsignals, component instances, ...)
484   QList<Schematic*> mSchematics;  ///< All schematics of this project
485   QList<Schematic*>
486       mRemovedSchematics;  ///< All removed schematics of this project
487   QScopedPointer<SchematicLayerProvider>
488       mSchematicLayerProvider;  ///< All schematic layers of this project
489   QList<Board*> mBoards;  ///< All boards of this project
490   QList<Board*> mRemovedBoards;  ///< All removed boards of this project
491   QScopedPointer<AttributeList>
492       mAttributes;  ///< all attributes in a specific order
493 };
494 
495 /*******************************************************************************
496  *  End of File
497  ******************************************************************************/
498 
499 }  // namespace project
500 }  // namespace librepcb
501 
502 #endif  // LIBREPCB_PROJECT_PROJECT_H
503