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 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "workspace.h"
24
25 #include "favoriteprojectsmodel.h"
26 #include "library/workspacelibrarydb.h"
27 #include "projecttreemodel.h"
28 #include "recentprojectsmodel.h"
29 #include "settings/workspacesettings.h"
30
31 #include <librepcb/common/application.h>
32 #include <librepcb/common/exceptions.h>
33 #include <librepcb/common/fileio/filepath.h>
34 #include <librepcb/common/fileio/fileutils.h>
35 #include <librepcb/common/fileio/versionfile.h>
36 #include <librepcb/library/library.h>
37 #include <librepcb/libraryeditor/libraryeditor.h>
38 #include <librepcb/project/project.h>
39
40 #include <QtCore>
41
42 #include <algorithm>
43
44 /*******************************************************************************
45 * Namespace
46 ******************************************************************************/
47 namespace librepcb {
48
49 using namespace library;
50 using namespace project;
51
52 namespace workspace {
53
54 /*******************************************************************************
55 * Constructors / Destructor
56 ******************************************************************************/
57
Workspace(const FilePath & wsPath,DirectoryLock::LockHandlerCallback lockCallback)58 Workspace::Workspace(const FilePath& wsPath,
59 DirectoryLock::LockHandlerCallback lockCallback)
60 : QObject(nullptr),
61 mPath(wsPath),
62 mProjectsPath(mPath.getPathTo("projects")),
63 mMetadataPath(mPath.getPathTo("v" % qApp->getFileFormatVersion().toStr())),
64 mLibrariesPath(mMetadataPath.getPathTo("libraries")),
65 mLock(mMetadataPath) {
66 // check if the workspace is valid
67 if (!isValidWorkspacePath(mPath)) {
68 throw RuntimeError(
69 __FILE__, __LINE__,
70 tr("Invalid workspace path: \"%1\"").arg(mPath.toNative()));
71 }
72 FilePath versionFp = mPath.getPathTo(".librepcb-workspace");
73 QByteArray versionRaw(FileUtils::readFile(versionFp)); // can throw
74 VersionFile wsVersionFile =
75 VersionFile::fromByteArray(versionRaw); // can throw
76 if (wsVersionFile.getVersion() != FILE_FORMAT_VERSION()) {
77 throw RuntimeError(__FILE__, __LINE__,
78 tr("The workspace version %1 is not compatible "
79 "with this application version.")
80 .arg(wsVersionFile.getVersion().toStr()));
81 }
82
83 // create directories which do not exist already
84 FileUtils::makePath(mProjectsPath); // can throw
85 FileUtils::makePath(mMetadataPath); // can throw
86 FileUtils::makePath(mLibrariesPath); // can throw
87
88 // the workspace can be opened by this application, so we will lock it
89 mLock.tryLock(lockCallback); // can throw
90
91 // all OK, let's load the workspace stuff!
92
93 // load workspace settings
94 mWorkspaceSettings.reset(
95 new WorkspaceSettings(mMetadataPath.getPathTo("settings.lp"),
96 qApp->getFileFormatVersion(), this));
97
98 // load library database
99 mLibraryDb.reset(new WorkspaceLibraryDb(*this)); // can throw
100
101 // load project models
102 mRecentProjectsModel.reset(new RecentProjectsModel(*this));
103 mFavoriteProjectsModel.reset(new FavoriteProjectsModel(*this));
104 mProjectTreeModel.reset(new ProjectTreeModel(*this));
105 }
106
~Workspace()107 Workspace::~Workspace() noexcept {
108 }
109
110 /*******************************************************************************
111 * Getters
112 ******************************************************************************/
113
getProjectTreeModel() const114 ProjectTreeModel& Workspace::getProjectTreeModel() const noexcept {
115 return *mProjectTreeModel;
116 }
117
getRecentProjectsModel() const118 RecentProjectsModel& Workspace::getRecentProjectsModel() const noexcept {
119 return *mRecentProjectsModel;
120 }
121
getFavoriteProjectsModel() const122 FavoriteProjectsModel& Workspace::getFavoriteProjectsModel() const noexcept {
123 return *mFavoriteProjectsModel;
124 }
125
126 /*******************************************************************************
127 * Project Management
128 ******************************************************************************/
129
setLastRecentlyUsedProject(const FilePath & filepath)130 void Workspace::setLastRecentlyUsedProject(const FilePath& filepath) noexcept {
131 mRecentProjectsModel->setLastRecentProject(filepath);
132 }
133
isFavoriteProject(const FilePath & filepath) const134 bool Workspace::isFavoriteProject(const FilePath& filepath) const noexcept {
135 return mFavoriteProjectsModel->isFavoriteProject(filepath);
136 }
137
addFavoriteProject(const FilePath & filepath)138 void Workspace::addFavoriteProject(const FilePath& filepath) noexcept {
139 mFavoriteProjectsModel->addFavoriteProject(filepath);
140 }
141
removeFavoriteProject(const FilePath & filepath)142 void Workspace::removeFavoriteProject(const FilePath& filepath) noexcept {
143 mFavoriteProjectsModel->removeFavoriteProject(filepath);
144 }
145
146 /*******************************************************************************
147 * Static Methods
148 ******************************************************************************/
149
isValidWorkspacePath(const FilePath & path)150 bool Workspace::isValidWorkspacePath(const FilePath& path) noexcept {
151 return path.getPathTo(".librepcb-workspace").isExistingFile();
152 }
153
getFileFormatVersionsOfWorkspace(const FilePath & path)154 QList<Version> Workspace::getFileFormatVersionsOfWorkspace(
155 const FilePath& path) noexcept {
156 QList<Version> list;
157 if (isValidWorkspacePath(path)) {
158 QDir dir(path.toStr());
159 QStringList subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
160 foreach (const QString& subdir, subdirs) {
161 if (subdir.startsWith('v')) {
162 tl::optional<Version> version =
163 Version::tryFromString(subdir.mid(1, -1));
164 if (version) {
165 list.append(*version);
166 }
167 }
168 }
169 std::sort(list.begin(), list.end());
170 }
171 return list;
172 }
173
getHighestFileFormatVersionOfWorkspace(const FilePath & path)174 tl::optional<Version> Workspace::getHighestFileFormatVersionOfWorkspace(
175 const FilePath& path) noexcept {
176 QList<Version> versions = getFileFormatVersionsOfWorkspace(path);
177 if (versions.count() > 0) {
178 return versions.last();
179 } else {
180 return tl::nullopt;
181 }
182 }
183
createNewWorkspace(const FilePath & path)184 void Workspace::createNewWorkspace(const FilePath& path) {
185 FileUtils::writeFile(
186 path.getPathTo(".librepcb-workspace"),
187 VersionFile(FILE_FORMAT_VERSION()).toByteArray()); // can throw
188 }
189
getMostRecentlyUsedWorkspacePath()190 FilePath Workspace::getMostRecentlyUsedWorkspacePath() noexcept {
191 QSettings clientSettings;
192 return FilePath(
193 clientSettings.value("workspaces/most_recently_used").toString());
194 }
195
setMostRecentlyUsedWorkspacePath(const FilePath & path)196 void Workspace::setMostRecentlyUsedWorkspacePath(
197 const FilePath& path) noexcept {
198 QSettings clientSettings;
199 clientSettings.setValue("workspaces/most_recently_used", path.toNative());
200 }
201
202 /*******************************************************************************
203 * End of File
204 ******************************************************************************/
205
206 } // namespace workspace
207 } // namespace librepcb
208