From 118c8d853d8b7dad28341fc2bc0d326c0ca10548 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Thu, 27 Dec 2018 19:33:57 +0100 Subject: [PATCH 1/6] Update version of workspace library database to v2 - Store PNG icon of libraries in database. Used to get library icons without loading the libraries (useful for the library manager). - Use "ON DELETE CASCADE" for foreign keys. Required to allow removing single libraries from the database without generating a foreign key error. Used to speed-up scanning of installed libraries. --- .../workspace/library/workspacelibrarydb.cpp | 36 ++++++++++++------- .../workspace/library/workspacelibrarydb.h | 2 +- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.cpp b/libs/librepcb/workspace/library/workspacelibrarydb.cpp index 745071e36d..5d7493a6c0 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.cpp +++ b/libs/librepcb/workspace/library/workspacelibrarydb.cpp @@ -564,12 +564,14 @@ void WorkspaceLibraryDb::createAllTables() { "`id` INTEGER PRIMARY KEY NOT NULL, " "`filepath` TEXT UNIQUE NOT NULL, " "`uuid` TEXT NOT NULL, " - "`version` TEXT NOT NULL " + "`version` TEXT NOT NULL, " + "`icon_png` BLOB " ")"); queries << QString( "CREATE TABLE IF NOT EXISTS libraries_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`lib_id` INTEGER REFERENCES libraries(id) NOT NULL, " + "`lib_id` INTEGER " + "REFERENCES libraries(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -590,7 +592,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS component_categories_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`cat_id` INTEGER REFERENCES component_categories(id) NOT NULL, " + "`cat_id` INTEGER " + "REFERENCES component_categories(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -611,7 +614,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS package_categories_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`cat_id` INTEGER REFERENCES package_categories(id) NOT NULL, " + "`cat_id` INTEGER " + "REFERENCES package_categories(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -631,7 +635,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS symbols_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`symbol_id` INTEGER REFERENCES symbols(id) NOT NULL, " + "`symbol_id` INTEGER " + "REFERENCES symbols(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -641,7 +646,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS symbols_cat (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`symbol_id` INTEGER REFERENCES symbols(id) NOT NULL, " + "`symbol_id` INTEGER " + "REFERENCES symbols(id) ON DELETE CASCADE NOT NULL, " "`category_uuid` TEXT NOT NULL, " "UNIQUE(symbol_id, category_uuid)" ")"); @@ -658,7 +664,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS packages_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`package_id` INTEGER REFERENCES packages(id) NOT NULL, " + "`package_id` INTEGER " + "REFERENCES packages(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -668,7 +675,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS packages_cat (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`package_id` INTEGER REFERENCES packages(id) NOT NULL, " + "`package_id` INTEGER " + "REFERENCES packages(id) ON DELETE CASCADE NOT NULL, " "`category_uuid` TEXT NOT NULL, " "UNIQUE(package_id, category_uuid)" ")"); @@ -685,7 +693,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS components_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`component_id` INTEGER REFERENCES components(id) NOT NULL, " + "`component_id` INTEGER " + "REFERENCES components(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -695,7 +704,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS components_cat (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`component_id` INTEGER REFERENCES components(id) NOT NULL, " + "`component_id` INTEGER " + "REFERENCES components(id) ON DELETE CASCADE NOT NULL, " "`category_uuid` TEXT NOT NULL, " "UNIQUE(component_id, category_uuid)" ")"); @@ -714,7 +724,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS devices_tr (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`device_id` INTEGER REFERENCES devices(id) NOT NULL, " + "`device_id` INTEGER " + "REFERENCES devices(id) ON DELETE CASCADE NOT NULL, " "`locale` TEXT NOT NULL, " "`name` TEXT, " "`description` TEXT, " @@ -724,7 +735,8 @@ void WorkspaceLibraryDb::createAllTables() { queries << QString( "CREATE TABLE IF NOT EXISTS devices_cat (" "`id` INTEGER PRIMARY KEY NOT NULL, " - "`device_id` INTEGER REFERENCES devices(id) NOT NULL, " + "`device_id` INTEGER " + "REFERENCES devices(id) ON DELETE CASCADE NOT NULL, " "`category_uuid` TEXT NOT NULL, " "UNIQUE(device_id, category_uuid)" ")"); diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.h b/libs/librepcb/workspace/library/workspacelibrarydb.h index 896bd01e9e..74a87cfdee 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.h +++ b/libs/librepcb/workspace/library/workspacelibrarydb.h @@ -161,7 +161,7 @@ class WorkspaceLibraryDb final : public QObject { QScopedPointer mLibraryScanner; // Constants - static const int sCurrentDbVersion = 1; + static const int sCurrentDbVersion = 2; }; /******************************************************************************* From ac429c45a12c453994e038a73d870242e0666297 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Thu, 27 Dec 2018 19:33:57 +0100 Subject: [PATCH 2/6] WorkspaceLibraryDb: Add more methods to query data Add support for reading more data from the database, especially library metadata. --- .../workspace/library/workspacelibrarydb.cpp | 127 +++++++++++++++++- .../workspace/library/workspacelibrarydb.h | 11 ++ 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.cpp b/libs/librepcb/workspace/library/workspacelibrarydb.cpp index 5d7493a6c0..483a003fb9 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.cpp +++ b/libs/librepcb/workspace/library/workspacelibrarydb.cpp @@ -91,10 +91,39 @@ WorkspaceLibraryDb::WorkspaceLibraryDb(Workspace& ws) WorkspaceLibraryDb::~WorkspaceLibraryDb() noexcept { } +/******************************************************************************* + * Getters: Libraries + ******************************************************************************/ + +QMultiMap WorkspaceLibraryDb::getLibraries() const { + QSqlQuery query = + mDb->prepareQuery("SELECT version, filepath FROM libraries"); + mDb->exec(query); + + QMultiMap libraries; + while (query.next()) { + Version version = + Version::fromString(query.value(0).toString()); // can throw + FilePath filepath(FilePath::fromRelative(mWorkspace.getLibrariesPath(), + query.value(1).toString())); + if (filepath.isValid()) { + libraries.insert(version, filepath); + } else { + throw LogicError(__FILE__, __LINE__); + } + } + return libraries; +} + /******************************************************************************* * Getters: Library Elements by their UUID ******************************************************************************/ +QMultiMap WorkspaceLibraryDb::getLibraries( + const Uuid& uuid) const { + return getElementFilePathsFromDb("libraries", uuid); +} + QMultiMap WorkspaceLibraryDb::getComponentCategories( const Uuid& uuid) const { return getElementFilePathsFromDb("component_categories", uuid); @@ -129,6 +158,10 @@ QMultiMap WorkspaceLibraryDb::getDevices( * Getters: Best Match Library Elements by their UUID ******************************************************************************/ +FilePath WorkspaceLibraryDb::getLatestLibrary(const Uuid& uuid) const { + return getLatestVersionFilePath(getLibraries(uuid)); +} + FilePath WorkspaceLibraryDb::getLatestComponentCategory( const Uuid& uuid) const { return getLatestVersionFilePath(getComponentCategories(uuid)); @@ -198,6 +231,14 @@ QList WorkspaceLibraryDb::getLibraryElements( * Getters: Element Metadata ******************************************************************************/ +template <> +void WorkspaceLibraryDb::getElementTranslations( + const FilePath& elemDir, const QStringList& localeOrder, QString* name, + QString* desc, QString* keywords) const { + getElementTranslations("libraries", "lib_id", elemDir, localeOrder, name, + desc, keywords); +} + template <> void WorkspaceLibraryDb::getElementTranslations( const FilePath& elemDir, const QStringList& localeOrder, QString* name, @@ -246,6 +287,72 @@ void WorkspaceLibraryDb::getElementTranslations( desc, keywords); } +template <> +void WorkspaceLibraryDb::getElementMetadata(const FilePath elemDir, + Uuid* uuid, + Version* version) const { + return getElementMetadata("libraries", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata( + const FilePath elemDir, Uuid* uuid, Version* version) const { + return getElementMetadata("component_categories", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata( + const FilePath elemDir, Uuid* uuid, Version* version) const { + return getElementMetadata("package_categories", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata(const FilePath elemDir, + Uuid* uuid, + Version* version) const { + return getElementMetadata("symbols", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata(const FilePath elemDir, + Uuid* uuid, + Version* version) const { + return getElementMetadata("packages", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata(const FilePath elemDir, + Uuid* uuid, + Version* version) const { + return getElementMetadata("components", elemDir, uuid, version); +} + +template <> +void WorkspaceLibraryDb::getElementMetadata(const FilePath elemDir, + Uuid* uuid, + Version* version) const { + return getElementMetadata("devices", elemDir, uuid, version); +} + +void WorkspaceLibraryDb::getLibraryMetadata(const FilePath libDir, + QPixmap* icon) const { + QSqlQuery query = mDb->prepareQuery( + "SELECT icon_png FROM libraries WHERE filepath = :filepath"); + query.bindValue(":filepath", + libDir.toRelative(mWorkspace.getLibrariesPath())); + mDb->exec(query); + + if (query.first()) { + QByteArray blob = query.value(0).toByteArray(); + if (icon) icon->loadFromData(blob, "png"); + } else { + throw RuntimeError( + __FILE__, __LINE__, + QString(tr("Library not found in workspace library: \"%1\"")) + .arg(libDir.toNative())); + } +} + void WorkspaceLibraryDb::getDeviceMetadata(const FilePath& devDir, Uuid* pkgUuid) const { QSqlQuery query = mDb->prepareQuery( @@ -382,8 +489,7 @@ void WorkspaceLibraryDb::getElementTranslations(const QString& table, QString locale = query.value(0).toString(); QString name = query.value(1).toString(); QString description = query.value(2).toString(); - ; - QString keywords = query.value(3).toString(); + QString keywords = query.value(3).toString(); if (!name.isNull()) nameMap.insert(locale, ElementName(name)); // can throw if (!description.isNull()) descriptionMap.insert(locale, description); if (!keywords.isNull()) keywordsMap.insert(locale, keywords); @@ -394,6 +500,23 @@ void WorkspaceLibraryDb::getElementTranslations(const QString& table, if (keywords) *keywords = keywordsMap.value(localeOrder); } +void WorkspaceLibraryDb::getElementMetadata(const QString& table, + const FilePath elemDir, Uuid* uuid, + Version* version) const { + QSqlQuery query = mDb->prepareQuery("SELECT uuid, version FROM " % table % + " WHERE filepath = :filepath"); + query.bindValue(":filepath", + elemDir.toRelative(mWorkspace.getLibrariesPath())); + mDb->exec(query); + + while (query.next()) { + QString uuidStr = query.value(0).toString(); + QString versionStr = query.value(1).toString(); + if (uuid) *uuid = Uuid::fromString(uuidStr); // can throw + if (version) *version = Version::fromString(versionStr); // can throw + } +} + QMultiMap WorkspaceLibraryDb::getElementFilePathsFromDb( const QString& tablename, const Uuid& uuid) const { QSqlQuery query = mDb->prepareQuery("SELECT version, filepath FROM " % diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.h b/libs/librepcb/workspace/library/workspacelibrarydb.h index 74a87cfdee..cfc752dd67 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.h +++ b/libs/librepcb/workspace/library/workspacelibrarydb.h @@ -71,7 +71,11 @@ class WorkspaceLibraryDb final : public QObject { // Getters: Attributes const FilePath& getFilePath() const noexcept { return mFilePath; } + // Getters: Libraries + QMultiMap getLibraries() const; + // Getters: Library Elements by their UUID + QMultiMap getLibraries(const Uuid& uuid) const; QMultiMap getComponentCategories(const Uuid& uuid) const; QMultiMap getPackageCategories(const Uuid& uuid) const; QMultiMap getSymbols(const Uuid& uuid) const; @@ -80,6 +84,7 @@ class WorkspaceLibraryDb final : public QObject { QMultiMap getDevices(const Uuid& uuid) const; // Getters: Best Match Library Elements by their UUID + FilePath getLatestLibrary(const Uuid& uuid) const; FilePath getLatestComponentCategory(const Uuid& uuid) const; FilePath getLatestPackageCategory(const Uuid& uuid) const; FilePath getLatestSymbol(const Uuid& uuid) const; @@ -97,6 +102,10 @@ class WorkspaceLibraryDb final : public QObject { const QStringList& localeOrder, QString* name = nullptr, QString* desc = nullptr, QString* keywords = nullptr) const; + template + void getElementMetadata(const FilePath elemDir, Uuid* uuid = nullptr, + Version* version = nullptr) const; + void getLibraryMetadata(const FilePath libDir, QPixmap* icon = nullptr) const; void getDeviceMetadata(const FilePath& devDir, Uuid* pkgUuid = nullptr) const; // Getters: Special @@ -134,6 +143,8 @@ class WorkspaceLibraryDb final : public QObject { const FilePath& elemDir, const QStringList& localeOrder, QString* name, QString* desc, QString* keywords) const; + void getElementMetadata(const QString& table, const FilePath elemDir, + Uuid* uuid, Version* version) const; QMultiMap getElementFilePathsFromDb( const QString& tablename, const Uuid& uuid) const; FilePath getLatestVersionFilePath( From 9cc3f30c91193253f3e2c8422beea0db400be07c Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Fri, 28 Dec 2018 13:22:21 +0100 Subject: [PATCH 3/6] Refactor and improve scanning of workspace libraries WorkspaceLibraryScanner now also scans which libraries are installed instead of using the libraries loaded by the Workspace object. This allows to add/remove/update workspace libraries without notifying the Workspace object about the exact changes. To get faster feedback of the changes (e.g. to update the widgets of the library manager), the scanner now emits a signal after the library list is updated. In addition, the scanner now emits a new signal scanFinished() which is used in many classes to get notified even if the scan fails or was aborted. Also the debug output is now a bit more verbose to see more information about the library scan. --- apps/librepcb/controlpanel/controlpanel.cpp | 2 +- .../lib/libraryoverviewwidget.cpp | 2 +- libs/librepcb/libraryeditor/libraryeditor.cpp | 2 +- .../projecteditor/boardeditor/boardeditor.cpp | 2 +- .../schematiceditor/schematiceditor.cpp | 2 +- .../workspace/library/workspacelibrarydb.cpp | 13 +- .../workspace/library/workspacelibrarydb.h | 2 + .../library/workspacelibraryscanner.cpp | 188 +++++++++++++----- .../library/workspacelibraryscanner.h | 31 ++- libs/librepcb/workspace/workspace.cpp | 4 +- libs/librepcb/workspace/workspace.h | 14 ++ 11 files changed, 188 insertions(+), 74 deletions(-) diff --git a/apps/librepcb/controlpanel/controlpanel.cpp b/apps/librepcb/controlpanel/controlpanel.cpp index 8362bda376..52960e4160 100644 --- a/apps/librepcb/controlpanel/controlpanel.cpp +++ b/apps/librepcb/controlpanel/controlpanel.cpp @@ -81,7 +81,7 @@ ControlPanel::ControlPanel(Workspace& workspace) mUi->statusBar->setProgressBarTextFormat(tr("Scanning libraries (%p%)")); connect(&mWorkspace.getLibraryDb(), &WorkspaceLibraryDb::scanStarted, mUi->statusBar, &StatusBar::showProgressBar, Qt::QueuedConnection); - connect(&mWorkspace.getLibraryDb(), &WorkspaceLibraryDb::scanSucceeded, + connect(&mWorkspace.getLibraryDb(), &WorkspaceLibraryDb::scanFinished, mUi->statusBar, &StatusBar::hideProgressBar, Qt::QueuedConnection); connect(&mWorkspace.getLibraryDb(), &WorkspaceLibraryDb::scanProgressUpdate, mUi->statusBar, &StatusBar::setProgressBarPercent, diff --git a/libs/librepcb/libraryeditor/lib/libraryoverviewwidget.cpp b/libs/librepcb/libraryeditor/lib/libraryoverviewwidget.cpp index debee816f0..ec363c82e4 100644 --- a/libs/librepcb/libraryeditor/lib/libraryoverviewwidget.cpp +++ b/libs/librepcb/libraryeditor/lib/libraryoverviewwidget.cpp @@ -123,7 +123,7 @@ LibraryOverviewWidget::LibraryOverviewWidget(const Context& context, // Load all library elements. updateElementLists(); connect(&mContext.workspace.getLibraryDb(), - &workspace::WorkspaceLibraryDb::scanSucceeded, this, + &workspace::WorkspaceLibraryDb::scanFinished, this, &LibraryOverviewWidget::updateElementLists); } diff --git a/libs/librepcb/libraryeditor/libraryeditor.cpp b/libs/librepcb/libraryeditor/libraryeditor.cpp index 4f7e187eb1..a82c7b9b45 100644 --- a/libs/librepcb/libraryeditor/libraryeditor.cpp +++ b/libs/librepcb/libraryeditor/libraryeditor.cpp @@ -123,7 +123,7 @@ LibraryEditor::LibraryEditor(workspace::Workspace& ws, &workspace::WorkspaceLibraryDb::scanStarted, mUi->statusBar, &StatusBar::showProgressBar, Qt::QueuedConnection); connect(&mWorkspace.getLibraryDb(), - &workspace::WorkspaceLibraryDb::scanSucceeded, mUi->statusBar, + &workspace::WorkspaceLibraryDb::scanFinished, mUi->statusBar, &StatusBar::hideProgressBar, Qt::QueuedConnection); connect(&mWorkspace.getLibraryDb(), &workspace::WorkspaceLibraryDb::scanProgressUpdate, mUi->statusBar, diff --git a/libs/librepcb/projecteditor/boardeditor/boardeditor.cpp b/libs/librepcb/projecteditor/boardeditor/boardeditor.cpp index 683f75b073..fcf2497a79 100644 --- a/libs/librepcb/projecteditor/boardeditor/boardeditor.cpp +++ b/libs/librepcb/projecteditor/boardeditor/boardeditor.cpp @@ -224,7 +224,7 @@ BoardEditor::BoardEditor(ProjectEditor& projectEditor, Project& project) &workspace::WorkspaceLibraryDb::scanStarted, mUi->statusbar, &StatusBar::showProgressBar, Qt::QueuedConnection); connect(&mProjectEditor.getWorkspace().getLibraryDb(), - &workspace::WorkspaceLibraryDb::scanSucceeded, mUi->statusbar, + &workspace::WorkspaceLibraryDb::scanFinished, mUi->statusbar, &StatusBar::hideProgressBar, Qt::QueuedConnection); connect(&mProjectEditor.getWorkspace().getLibraryDb(), &workspace::WorkspaceLibraryDb::scanProgressUpdate, mUi->statusbar, diff --git a/libs/librepcb/projecteditor/schematiceditor/schematiceditor.cpp b/libs/librepcb/projecteditor/schematiceditor/schematiceditor.cpp index 0eab476950..4dec8577c6 100644 --- a/libs/librepcb/projecteditor/schematiceditor/schematiceditor.cpp +++ b/libs/librepcb/projecteditor/schematiceditor/schematiceditor.cpp @@ -190,7 +190,7 @@ SchematicEditor::SchematicEditor(ProjectEditor& projectEditor, Project& project) &workspace::WorkspaceLibraryDb::scanStarted, mUi->statusbar, &StatusBar::showProgressBar, Qt::QueuedConnection); connect(&mProjectEditor.getWorkspace().getLibraryDb(), - &workspace::WorkspaceLibraryDb::scanSucceeded, mUi->statusbar, + &workspace::WorkspaceLibraryDb::scanFinished, mUi->statusbar, &StatusBar::hideProgressBar, Qt::QueuedConnection); connect(&mProjectEditor.getWorkspace().getLibraryDb(), &workspace::WorkspaceLibraryDb::scanProgressUpdate, mUi->statusbar, diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.cpp b/libs/librepcb/workspace/library/workspacelibrarydb.cpp index 483a003fb9..d018822a74 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.cpp +++ b/libs/librepcb/workspace/library/workspacelibrarydb.cpp @@ -76,14 +76,19 @@ WorkspaceLibraryDb::WorkspaceLibraryDb(Workspace& ws) // create library scanner object mLibraryScanner.reset(new WorkspaceLibraryScanner(mWorkspace, mFilePath)); - connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::started, this, + connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::scanStarted, this, &WorkspaceLibraryDb::scanStarted, Qt::QueuedConnection); - connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::progressUpdate, + connect(mLibraryScanner.data(), + &WorkspaceLibraryScanner::scanLibraryListUpdated, this, + &WorkspaceLibraryDb::scanLibraryListUpdated, Qt::QueuedConnection); + connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::scanProgressUpdate, this, &WorkspaceLibraryDb::scanProgressUpdate, Qt::QueuedConnection); - connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::succeeded, this, + connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::scanSucceeded, this, &WorkspaceLibraryDb::scanSucceeded, Qt::QueuedConnection); - connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::failed, this, + connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::scanFailed, this, &WorkspaceLibraryDb::scanFailed, Qt::QueuedConnection); + connect(mLibraryScanner.data(), &WorkspaceLibraryScanner::scanFinished, this, + &WorkspaceLibraryDb::scanFinished, Qt::QueuedConnection); qDebug("Workspace library database successfully loaded!"); } diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.h b/libs/librepcb/workspace/library/workspacelibrarydb.h index cfc752dd67..6bd25bcda7 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.h +++ b/libs/librepcb/workspace/library/workspacelibrarydb.h @@ -133,9 +133,11 @@ class WorkspaceLibraryDb final : public QObject { signals: void scanStarted(); + void scanLibraryListUpdated(int libraryCount); void scanProgressUpdate(int percent); void scanSucceeded(int elementCount); void scanFailed(QString errorMsg); + void scanFinished(); private: // Private Methods diff --git a/libs/librepcb/workspace/library/workspacelibraryscanner.cpp b/libs/librepcb/workspace/library/workspacelibraryscanner.cpp index 7c9df820e6..35c711e870 100644 --- a/libs/librepcb/workspace/library/workspacelibraryscanner.cpp +++ b/libs/librepcb/workspace/library/workspacelibraryscanner.cpp @@ -75,17 +75,25 @@ QVariant WorkspaceLibraryScanner::optionalToVariant( void WorkspaceLibraryScanner::run() noexcept { try { + QElapsedTimer timer; + timer.start(); mAbort = false; - emit started(); - - // get a list of all available libraries - QList> libraries; - libraries.append(mWorkspace.getLocalLibraries().values()); - libraries.append(mWorkspace.getRemoteLibraries().values()); + emit scanStarted(); + emit scanProgressUpdate(0); + qDebug() << "Workspace library scan started."; // open SQLite database SQLiteDatabase db(mDbFilePath); // can throw + // update list of libraries + QHash> libraries; + getLibrariesOfDirectory(mWorkspace.getLocalLibrariesPath(), libraries); + getLibrariesOfDirectory(mWorkspace.getRemoteLibrariesPath(), libraries); + QHash libIds = updateLibraries(db, libraries); // can throw + emit scanLibraryListUpdated(libIds.count()); + qDebug() << "Workspace libraries indexed:" << libIds.count() + << "libraries in" << timer.elapsed() << "ms"; + // begin database transaction SQLiteDatabase::TransactionScopeGuard transactionGuard(db); // can throw @@ -95,52 +103,166 @@ void WorkspaceLibraryScanner::run() noexcept { // scan all libraries int count = 0; qreal percent = 0; - foreach (const QSharedPointer& lib, libraries) { - int libId = addLibraryToDb(db, lib); + foreach (const FilePath& fp, libraries.keys()) { + Q_ASSERT(libIds.contains(fp)); + int libId = libIds[fp]; + const std::shared_ptr& lib = libraries[fp]; + Q_ASSERT(lib); if (mAbort) break; count += addCategoriesToDb( db, lib->searchForElements(), "component_categories", "cat_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); if (mAbort) break; count += addCategoriesToDb( db, lib->searchForElements(), "package_categories", "cat_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); if (mAbort) break; count += addElementsToDb(db, lib->searchForElements(), "symbols", "symbol_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); if (mAbort) break; count += addElementsToDb(db, lib->searchForElements(), "packages", "package_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); if (mAbort) break; count += addElementsToDb(db, lib->searchForElements(), "components", "component_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); if (mAbort) break; count += addDevicesToDb(db, lib->searchForElements(), "devices", "device_id", libId); - emit progressUpdate(percent += qreal(100) / (libraries.count() * 6)); + emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); } // commit transaction if (!mAbort) { transactionGuard.commit(); // can throw - emit succeeded(count); + qDebug() << "Workspace library scan succeeded:" << count << "elements in" + << timer.elapsed() << "ms"; + emit scanSucceeded(count); + } else { + qDebug() << "Workspace library scan aborted after" << timer.elapsed() + << "ms."; } } catch (const Exception& e) { - emit failed(e.getMsg()); + qDebug() << "Workspace library scan failed:" << e.getMsg(); + emit scanFailed(e.getMsg()); } + emit scanFinished(); } -void WorkspaceLibraryScanner::clearAllTables(SQLiteDatabase& db) { - // libraries +void WorkspaceLibraryScanner::getLibrariesOfDirectory( + const FilePath& dir, + QHash>& libs) noexcept { + foreach (const QString& name, + QDir(dir.toStr()).entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { + FilePath libDirPath = dir.getPathTo(name); + if (Library::isValidElementDirectory(libDirPath)) { + try { + libs.insert(libDirPath, std::make_shared(libDirPath, true)); + } catch (Exception& e) { + qCritical() << "Could not open workspace library!"; + qCritical() << "Library:" << libDirPath.toNative(); + qCritical() << "Error:" << e.getMsg(); + } + } else { + qWarning() << "Directory is not a valid libary:" << libDirPath.toNative(); + } + } +} + +QHash WorkspaceLibraryScanner::updateLibraries( + SQLiteDatabase& db, const QHash>& libs) { + SQLiteDatabase::TransactionScopeGuard transactionGuard(db); // can throw + + // get IDs of libraries in DB + QHash dbLibIds; + QSqlQuery query = db.prepareQuery("SELECT id, filepath FROM libraries"); + db.exec(query); + while (query.next()) { + int id = query.value(0).toInt(); + FilePath fp(FilePath::fromRelative(mWorkspace.getLibrariesPath(), + query.value(1).toString())); + if (!fp.isValid()) throw LogicError(__FILE__, __LINE__); + dbLibIds[fp] = id; + } + + // update existing libraries in DB + foreach (const FilePath& fp, libs.keys().toSet() & dbLibIds.keys().toSet()) { + Q_ASSERT(dbLibIds.contains(fp)); + std::shared_ptr lib = libs[fp]; + Q_ASSERT(lib); + query = db.prepareQuery( + "UPDATE libraries SET " + "filepath = :filepath, " + "uuid = :uuid, " + "version = :version, " + "icon_png = :icon_png " + "WHERE id = :id"); + query.bindValue(":filepath", fp.toRelative(mWorkspace.getLibrariesPath())); + query.bindValue(":uuid", lib->getUuid().toStr()); + query.bindValue(":version", lib->getVersion().toStr()); + query.bindValue(":icon_png", lib->getIcon()); + query.bindValue(":id", dbLibIds[fp]); + db.exec(query); + } + + // add new libraries to DB + foreach (const FilePath& fp, libs.keys().toSet() - dbLibIds.keys().toSet()) { + Q_ASSERT(!dbLibIds.contains(fp)); + std::shared_ptr lib = libs[fp]; + Q_ASSERT(lib); + query = db.prepareQuery( + "INSERT INTO libraries " + "(filepath, uuid, version, icon_png) VALUES " + "(:filepath, :uuid, :version, :icon_png)"); + query.bindValue(":filepath", fp.toRelative(mWorkspace.getLibrariesPath())); + query.bindValue(":uuid", lib->getUuid().toStr()); + query.bindValue(":version", lib->getVersion().toStr()); + query.bindValue(":icon_png", lib->getIcon()); + dbLibIds[fp] = db.insert(query); + } + + // remove no longer existing libraries from DB + foreach (const FilePath& fp, dbLibIds.keys().toSet() - libs.keys().toSet()) { + Q_ASSERT(dbLibIds.contains(fp)); + query = db.prepareQuery("DELETE FROM libraries WHERE id = :id"); + query.bindValue(":id", dbLibIds[fp]); + db.exec(query); + dbLibIds.remove(fp); + } + + // update all library translations db.clearTable("libraries_tr"); - db.clearTable("libraries"); + foreach (const FilePath& fp, libs.keys()) { + Q_ASSERT(dbLibIds.contains(fp)); + std::shared_ptr lib = libs[fp]; + Q_ASSERT(lib); + foreach (const QString& locale, lib->getAllAvailableLocales()) { + query = db.prepareQuery( + "INSERT INTO libraries_tr " + "(lib_id, locale, name, description, keywords) VALUES " + "(:lib_id, :locale, :name, :description, :keywords)"); + query.bindValue(":lib_id", dbLibIds[fp]); + query.bindValue(":locale", locale); + query.bindValue(":name", + optionalToVariant(lib->getNames().tryGet(locale))); + query.bindValue(":description", + optionalToVariant(lib->getDescriptions().tryGet(locale))); + query.bindValue(":keywords", + optionalToVariant(lib->getKeywords().tryGet(locale))); + db.insert(query); + } + } + transactionGuard.commit(); // can throw + return dbLibIds; +} + +void WorkspaceLibraryScanner::clearAllTables(SQLiteDatabase& db) { // component categories db.clearTable("component_categories_tr"); db.clearTable("component_categories"); @@ -170,34 +292,6 @@ void WorkspaceLibraryScanner::clearAllTables(SQLiteDatabase& db) { db.clearTable("devices"); } -int WorkspaceLibraryScanner::addLibraryToDb( - SQLiteDatabase& db, const QSharedPointer& lib) { - QSqlQuery query = db.prepareQuery( - "INSERT INTO libraries " - "(filepath, uuid, version) VALUES " - "(:filepath, :uuid, :version)"); - query.bindValue(":filepath", - lib->getFilePath().toRelative(mWorkspace.getLibrariesPath())); - query.bindValue(":uuid", lib->getUuid().toStr()); - query.bindValue(":version", lib->getVersion().toStr()); - int id = db.insert(query); - foreach (const QString& locale, lib->getAllAvailableLocales()) { - QSqlQuery query = db.prepareQuery( - "INSERT INTO libraries_tr " - "(lib_id, locale, name, description, keywords) VALUES " - "(:element_id, :locale, :name, :description, :keywords)"); - query.bindValue(":element_id", id); - query.bindValue(":locale", locale); - query.bindValue(":name", optionalToVariant(lib->getNames().tryGet(locale))); - query.bindValue(":description", - optionalToVariant(lib->getDescriptions().tryGet(locale))); - query.bindValue(":keywords", - optionalToVariant(lib->getKeywords().tryGet(locale))); - db.insert(query); - } - return id; -} - template int WorkspaceLibraryScanner::addCategoriesToDb(SQLiteDatabase& db, const QList& dirs, diff --git a/libs/librepcb/workspace/library/workspacelibraryscanner.h b/libs/librepcb/workspace/library/workspacelibraryscanner.h index 9ecccbbe01..4834906171 100644 --- a/libs/librepcb/workspace/library/workspacelibraryscanner.h +++ b/libs/librepcb/workspace/library/workspacelibraryscanner.h @@ -27,6 +27,8 @@ #include +#include + /******************************************************************************* * Namespace / Forward Declarations ******************************************************************************/ @@ -53,14 +55,6 @@ class Workspace; * @warning Be very careful with dependencies to other objects as the #run() * method is executed in a separate thread! Keep the number of dependencies as * small as possible and consider thread synchronization and object lifetimes. - * - * @todo Don't really sure that the #run() method is 100% thread save ;) - * Maybe it would be better to put the whole library scanning code into - * this class instead of having references to objects from the library and - * workspace namespaces. This way it would be easier to guarantee thread safety. - * - * @author ubruhin - * @date 2016-09-06 */ class WorkspaceLibraryScanner final : public QThread { Q_OBJECT @@ -76,17 +70,22 @@ class WorkspaceLibraryScanner final : public QThread { delete; signals: - - void started(); - void progressUpdate(int percent); - void succeeded(int elementCount); - void failed(QString errorMsg); + void scanStarted(); + void scanLibraryListUpdated(int libraryCount); + void scanProgressUpdate(int percent); + void scanSucceeded(int elementCount); + void scanFailed(QString errorMsg); + void scanFinished(); private: // Methods - void run() noexcept override; + void run() noexcept override; + QHash updateLibraries( + SQLiteDatabase& db, + const QHash>& libs); void clearAllTables(SQLiteDatabase& db); - int addLibraryToDb(SQLiteDatabase& db, - const QSharedPointer& lib); + void getLibrariesOfDirectory( + const FilePath& dir, + QHash>& libs) noexcept; template int addCategoriesToDb(SQLiteDatabase& db, const QList& dirs, const QString& table, const QString& idColumn, diff --git a/libs/librepcb/workspace/workspace.cpp b/libs/librepcb/workspace/workspace.cpp index c9b301efcb..effd994b0b 100644 --- a/libs/librepcb/workspace/workspace.cpp +++ b/libs/librepcb/workspace/workspace.cpp @@ -113,7 +113,7 @@ Workspace::Workspace(const FilePath& wsPath) mWorkspaceSettings.reset(new WorkspaceSettings(*this)); // load local libraries - FilePath localLibsDirPath = mLibrariesPath.getPathTo("local"); + FilePath localLibsDirPath = getLocalLibrariesPath(); QDir localLibsDir(localLibsDirPath.toStr()); foreach (const QString& dir, localLibsDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { @@ -136,7 +136,7 @@ Workspace::Workspace(const FilePath& wsPath) } // load remote libraries - FilePath remoteLibsDirPath = mLibrariesPath.getPathTo("remote"); + FilePath remoteLibsDirPath = getRemoteLibrariesPath(); QDir remoteLibsDir(remoteLibsDirPath.toStr()); foreach (const QString& dir, remoteLibsDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { diff --git a/libs/librepcb/workspace/workspace.h b/libs/librepcb/workspace/workspace.h index becee141da..97e823294d 100644 --- a/libs/librepcb/workspace/workspace.h +++ b/libs/librepcb/workspace/workspace.h @@ -109,6 +109,20 @@ class Workspace final : public QObject { */ const FilePath& getLibrariesPath() const { return mLibrariesPath; } + /** + * @brief Get the filepath to the "v#/libraries/local" directory + */ + FilePath getLocalLibrariesPath() const { + return mLibrariesPath.getPathTo("local"); + } + + /** + * @brief Get the filepath to the "v#/libraries/remote" directory + */ + FilePath getRemoteLibrariesPath() const { + return mLibrariesPath.getPathTo("remote"); + } + ProjectTreeModel& getProjectTreeModel() const noexcept; RecentProjectsModel& getRecentProjectsModel() const noexcept; FavoriteProjectsModel& getFavoriteProjectsModel() const noexcept; From 1ce1d1b708205b8e17bdb8514f4392f3a5abda44 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Thu, 27 Dec 2018 19:33:57 +0100 Subject: [PATCH 4/6] Restart workspace library scanner when triggered Until now, the workspace library scanner was not restarted on every trigger, thus triggers while the scan was running were lost, which lead to outdated information in the database. Now the scan is immediately restarted if it is triggered while already running. This avoids outdated information, and is important to keep the list of libraries (especially in the library manager) always up to date when libraries are added or removed. --- .../workspace/library/workspacelibrarydb.cpp | 2 +- .../library/workspacelibraryscanner.cpp | 52 ++++++++++++++----- .../library/workspacelibraryscanner.h | 5 ++ 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/libs/librepcb/workspace/library/workspacelibrarydb.cpp b/libs/librepcb/workspace/library/workspacelibrarydb.cpp index d018822a74..987dffeea2 100644 --- a/libs/librepcb/workspace/library/workspacelibrarydb.cpp +++ b/libs/librepcb/workspace/library/workspacelibrarydb.cpp @@ -462,7 +462,7 @@ QSet WorkspaceLibraryDb::getComponentsBySearchKeyword( ******************************************************************************/ void WorkspaceLibraryDb::startLibraryRescan() noexcept { - mLibraryScanner->start(); + mLibraryScanner->startScan(); } /******************************************************************************* diff --git a/libs/librepcb/workspace/library/workspacelibraryscanner.cpp b/libs/librepcb/workspace/library/workspacelibraryscanner.cpp index 35c711e870..117fea080e 100644 --- a/libs/librepcb/workspace/library/workspacelibraryscanner.cpp +++ b/libs/librepcb/workspace/library/workspacelibraryscanner.cpp @@ -43,11 +43,17 @@ using namespace library; WorkspaceLibraryScanner::WorkspaceLibraryScanner( Workspace& ws, const FilePath& dbFilePath) noexcept - : QThread(nullptr), mWorkspace(ws), mDbFilePath(dbFilePath), mAbort(false) { + : QThread(nullptr), + mWorkspace(ws), + mDbFilePath(dbFilePath), + mSemaphore(0), + mAbort(false) { + start(); } WorkspaceLibraryScanner::~WorkspaceLibraryScanner() noexcept { mAbort = true; + mSemaphore.release(); if (!wait(2000)) { qWarning() << "Could not abort the library scanner worker thread!"; terminate(); @@ -57,6 +63,14 @@ WorkspaceLibraryScanner::~WorkspaceLibraryScanner() noexcept { } } +/******************************************************************************* + * General Methods + ******************************************************************************/ + +void WorkspaceLibraryScanner::startScan() noexcept { + mSemaphore.release(); +} + /******************************************************************************* * Private Methods ******************************************************************************/ @@ -74,10 +88,24 @@ QVariant WorkspaceLibraryScanner::optionalToVariant( } void WorkspaceLibraryScanner::run() noexcept { + qDebug() << "Workspace library scanner thread started."; + + while (true) { + mSemaphore.acquire(); + if (mAbort) { + break; + } else { + scan(); + } + } + + qDebug() << "Workspace library scanner thread stopped."; +} + +void WorkspaceLibraryScanner::scan() noexcept { try { QElapsedTimer timer; timer.start(); - mAbort = false; emit scanStarted(); emit scanProgressUpdate(0); qDebug() << "Workspace library scan started."; @@ -108,37 +136,37 @@ void WorkspaceLibraryScanner::run() noexcept { int libId = libIds[fp]; const std::shared_ptr& lib = libraries[fp]; Q_ASSERT(lib); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addCategoriesToDb( db, lib->searchForElements(), "component_categories", "cat_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addCategoriesToDb( db, lib->searchForElements(), "package_categories", "cat_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addElementsToDb(db, lib->searchForElements(), "symbols", "symbol_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addElementsToDb(db, lib->searchForElements(), "packages", "package_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addElementsToDb(db, lib->searchForElements(), "components", "component_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; count += addDevicesToDb(db, lib->searchForElements(), "devices", "device_id", libId); emit scanProgressUpdate(percent += qreal(100) / (libraries.count() * 6)); } // commit transaction - if (!mAbort) { + if ((!mAbort) && (mSemaphore.available() == 0)) { transactionGuard.commit(); // can throw qDebug() << "Workspace library scan succeeded:" << count << "elements in" << timer.elapsed() << "ms"; @@ -300,7 +328,7 @@ int WorkspaceLibraryScanner::addCategoriesToDb(SQLiteDatabase& db, int libId) { int count = 0; foreach (const FilePath& filepath, dirs) { - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; try { ElementType element(filepath, true); // can throw QSqlQuery query = db.prepareQuery( @@ -352,7 +380,7 @@ int WorkspaceLibraryScanner::addElementsToDb(SQLiteDatabase& db, int libId) { int count = 0; foreach (const FilePath& filepath, dirs) { - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; try { ElementType element(filepath, true); // can throw QSqlQuery query = @@ -411,7 +439,7 @@ int WorkspaceLibraryScanner::addDevicesToDb(SQLiteDatabase& db, int libId) { int count = 0; foreach (const FilePath& filepath, dirs) { - if (mAbort) break; + if (mAbort || (mSemaphore.available() > 0)) break; try { Device element(filepath, true); // can throw QSqlQuery query = db.prepareQuery("INSERT INTO " % table % diff --git a/libs/librepcb/workspace/library/workspacelibraryscanner.h b/libs/librepcb/workspace/library/workspacelibraryscanner.h index 4834906171..d4312f596a 100644 --- a/libs/librepcb/workspace/library/workspacelibraryscanner.h +++ b/libs/librepcb/workspace/library/workspacelibraryscanner.h @@ -65,6 +65,9 @@ class WorkspaceLibraryScanner final : public QThread { WorkspaceLibraryScanner(const WorkspaceLibraryScanner& other) = delete; ~WorkspaceLibraryScanner() noexcept; + // General Methods + void startScan() noexcept; + // Operator Overloadings WorkspaceLibraryScanner& operator=(const WorkspaceLibraryScanner& rhs) = delete; @@ -79,6 +82,7 @@ class WorkspaceLibraryScanner final : public QThread { private: // Methods void run() noexcept override; + void scan() noexcept; QHash updateLibraries( SQLiteDatabase& db, const QHash>& libs); @@ -101,6 +105,7 @@ class WorkspaceLibraryScanner final : public QThread { private: // Data Workspace& mWorkspace; FilePath mDbFilePath; + QSemaphore mSemaphore; volatile bool mAbort; }; From af56e1bc501ea2470fe79ff56b2a58b6a8943810 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Tue, 1 Jan 2019 21:34:10 +0100 Subject: [PATCH 5/6] LibraryManager: Use library metadata from database --- .../lib/librarylisteditorwidget.cpp | 45 ++++--- .../lib/librarylisteditorwidget.h | 1 + .../librarymanager/addlibrarywidget.cpp | 30 +---- .../librarymanager/addlibrarywidget.h | 4 +- .../librarymanager/libraryinfowidget.cpp | 68 +++++----- .../librarymanager/libraryinfowidget.h | 14 +-- .../librarymanager/librarylistwidgetitem.cpp | 37 +++--- .../librarymanager/librarylistwidgetitem.h | 23 ++-- .../librarymanager/librarymanager.cpp | 116 ++++++++++-------- libs/librepcb/librarymanager/librarymanager.h | 10 +- .../repositorylibrarylistwidgetitem.cpp | 106 ++++++++-------- .../repositorylibrarylistwidgetitem.h | 4 +- .../test_install_remote_libraries.py | 10 +- 13 files changed, 217 insertions(+), 251 deletions(-) diff --git a/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.cpp b/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.cpp index ea0610db24..5f47809e65 100644 --- a/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.cpp +++ b/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.cpp @@ -25,6 +25,7 @@ #include "ui_librarylisteditorwidget.h" #include +#include #include #include @@ -46,21 +47,30 @@ LibraryListEditorWidget::LibraryListEditorWidget(const workspace::Workspace& ws, QWidget* parent) noexcept : QWidget(parent), mWorkspace(ws), mUi(new Ui::LibraryListEditorWidget) { mUi->setupUi(this); + mUi->comboBox->addItem(tr("Choose library...")); connect(mUi->btnAdd, &QPushButton::clicked, this, &LibraryListEditorWidget::btnAddClicked); connect(mUi->btnRemove, &QPushButton::clicked, this, &LibraryListEditorWidget::btnRemoveClicked); - const QStringList& localeOrder = - mWorkspace.getSettings().getLibLocaleOrder().getLocaleOrder(); - QList> libs; - libs.append(mWorkspace.getLocalLibraries().values()); - libs.append(mWorkspace.getRemoteLibraries().values()); - mUi->comboBox->addItem(tr("Choose library...")); - foreach (const QSharedPointer& lib, libs) { - mUi->comboBox->addItem(lib->getIconAsPixmap(), - *lib->getNames().value(localeOrder), - lib->getUuid().toStr()); + try { + QMultiMap libs = + mWorkspace.getLibraryDb().getLibraries(); // can throw + foreach (const FilePath& fp, libs) { + Uuid uuid = Uuid::createRandom(); + mWorkspace.getLibraryDb().getElementMetadata( + fp, &uuid); // can throw + QString name; + mWorkspace.getLibraryDb().getElementTranslations( + fp, mWorkspace.getSettings().getLibLocaleOrder().getLocaleOrder(), + &name); // can throw + QPixmap icon; + mWorkspace.getLibraryDb().getLibraryMetadata(fp, &icon); // can throw + mUi->comboBox->addItem(icon, name, uuid.toStr()); + mLibNames[uuid] = name; + } + } catch (const Exception& e) { + qCritical() << "Could not load library list."; } } @@ -116,20 +126,7 @@ void LibraryListEditorWidget::btnRemoveClicked() noexcept { } void LibraryListEditorWidget::addItem(const Uuid& library) noexcept { - QString name = library.toStr(); - - const QStringList& localeOrder = - mWorkspace.getSettings().getLibLocaleOrder().getLocaleOrder(); - QList> libs; - libs.append(mWorkspace.getLocalLibraries().values()); - libs.append(mWorkspace.getRemoteLibraries().values()); - foreach (const QSharedPointer& lib, libs) { - if (lib->getUuid() == library) { - name = *lib->getNames().value(localeOrder); - break; - } - } - + QString name = mLibNames.value(library, library.toStr()); QListWidgetItem* item = new QListWidgetItem(name, mUi->listWidget); item->setData(Qt::UserRole, library.toStr()); } diff --git a/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.h b/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.h index a38f060833..4a5e77e9df 100644 --- a/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.h +++ b/libs/librepcb/libraryeditor/lib/librarylisteditorwidget.h @@ -89,6 +89,7 @@ class LibraryListEditorWidget final : public QWidget { const workspace::Workspace& mWorkspace; QScopedPointer mUi; QSet mUuids; + QHash mLibNames; }; /******************************************************************************* diff --git a/libs/librepcb/librarymanager/addlibrarywidget.cpp b/libs/librepcb/librarymanager/addlibrarywidget.cpp index ff7ae987c6..d11d7a25b2 100644 --- a/libs/librepcb/librarymanager/addlibrarywidget.cpp +++ b/libs/librepcb/librarymanager/addlibrarywidget.cpp @@ -107,16 +107,6 @@ void AddLibraryWidget::updateRepositoryLibraryList() noexcept { } } -void AddLibraryWidget::updateInstalledStatusOfRepositoryLibraries() noexcept { - for (int i = 0; i < mUi->lstRepoLibs->count(); i++) { - QListWidgetItem* item = mUi->lstRepoLibs->item(i); - Q_ASSERT(item); - auto* widget = dynamic_cast( - mUi->lstRepoLibs->itemWidget(item)); - if (widget) widget->updateInstalledStatus(); - } -} - /******************************************************************************* * Private Methods ******************************************************************************/ @@ -266,9 +256,6 @@ void AddLibraryWidget::createLocalLibraryButtonClicked() noexcept { qCritical() << "Could not copy the .gitattributes file:" << e.getMsg(); } - // add the new library to the workspace - mWorkspace.addLocalLibrary(directory.getFilename()); // can throw - // library successfully added! reset input fields and emit signal mUi->edtLocalName->clear(); mUi->edtLocalDescription->clear(); @@ -277,7 +264,7 @@ void AddLibraryWidget::createLocalLibraryButtonClicked() noexcept { mUi->edtLocalUrl->clear(); mUi->cbxLocalCc0License->setChecked(false); mUi->edtLocalDirectory->clear(); - emit libraryAdded(directory, true); + emit libraryAdded(directory); } catch (Exception& e) { QMessageBox::critical(this, tr("Error"), e.getMsg()); } @@ -344,17 +331,8 @@ void AddLibraryWidget::downloadZipFinished(bool success, Q_ASSERT(mManualLibraryDownload); if (success) { - try { - // add library to workspace - mWorkspace.addLocalLibrary( - mManualLibraryDownload->getDestinationDir().getFilename()); - - // finish - mUi->lblDownloadZipStatusMsg->setText(""); - emit libraryAdded(mManualLibraryDownload->getDestinationDir(), true); - } catch (const Exception& e) { - mUi->lblDownloadZipStatusMsg->setText(e.getMsg()); - } + mUi->lblDownloadZipStatusMsg->setText(""); + emit libraryAdded(mManualLibraryDownload->getDestinationDir()); } else { mUi->lblDownloadZipStatusMsg->setText(errMsg); } @@ -376,8 +354,6 @@ void AddLibraryWidget::repositoryLibraryListReceived( new RepositoryLibraryListWidgetItem(mWorkspace, libVal.toObject()); connect(widget, &RepositoryLibraryListWidgetItem::checkedChanged, this, &AddLibraryWidget::repoLibraryDownloadCheckedChanged); - connect(widget, &RepositoryLibraryListWidgetItem::libraryAdded, this, - &AddLibraryWidget::libraryAdded); QListWidgetItem* item = new QListWidgetItem(mUi->lstRepoLibs); item->setSizeHint(widget->sizeHint()); mUi->lstRepoLibs->setItemWidget(item, widget); diff --git a/libs/librepcb/librarymanager/addlibrarywidget.h b/libs/librepcb/librarymanager/addlibrarywidget.h index ee5f380b8d..e8519ea727 100644 --- a/libs/librepcb/librarymanager/addlibrarywidget.h +++ b/libs/librepcb/librarymanager/addlibrarywidget.h @@ -69,14 +69,12 @@ class AddLibraryWidget final : public QWidget { // General Methods void updateRepositoryLibraryList() noexcept; - void updateInstalledStatusOfRepositoryLibraries() noexcept; // Operator Overloadings AddLibraryWidget& operator=(const AddLibraryWidget& rhs) = delete; signals: - - void libraryAdded(const FilePath& libDir, bool select); + void libraryAdded(const FilePath& libDir); private: // Methods void localLibraryNameLineEditTextChanged(QString name) noexcept; diff --git a/libs/librepcb/librarymanager/libraryinfowidget.cpp b/libs/librepcb/librarymanager/libraryinfowidget.cpp index 1f18a51321..5155879dbb 100644 --- a/libs/librepcb/librarymanager/libraryinfowidget.cpp +++ b/libs/librepcb/librarymanager/libraryinfowidget.cpp @@ -24,7 +24,9 @@ #include "ui_libraryinfowidget.h" +#include #include +#include #include #include @@ -42,24 +44,27 @@ namespace manager { * Constructors / Destructor ******************************************************************************/ -LibraryInfoWidget::LibraryInfoWidget(workspace::Workspace& ws, - QSharedPointer lib) noexcept +LibraryInfoWidget::LibraryInfoWidget(workspace::Workspace& ws, + const FilePath& libDir) : QWidget(nullptr), mUi(new Ui::LibraryInfoWidget), mWorkspace(ws), - mLib(lib) { + mLibDir(libDir) { mUi->setupUi(this); connect(mUi->btnOpenLibraryEditor, &QPushButton::clicked, this, &LibraryInfoWidget::btnOpenLibraryEditorClicked); connect(mUi->btnRemove, &QPushButton::clicked, this, &LibraryInfoWidget::btnRemoveLibraryClicked); + // try to load the library + Library lib(mLibDir, true); // can throw + const QStringList& localeOrder = ws.getSettings().getLibLocaleOrder().getLocaleOrder(); // image - if (!lib->getIconAsPixmap().isNull()) { - mUi->lblIcon->setPixmap(lib->getIconAsPixmap().scaled( + if (!lib.getIconAsPixmap().isNull()) { + mUi->lblIcon->setPixmap(lib.getIconAsPixmap().scaled( mUi->lblIcon->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { mUi->lblIcon->setVisible(false); @@ -67,28 +72,29 @@ LibraryInfoWidget::LibraryInfoWidget(workspace::Workspace& ws, } // general attributes - mUi->lblName->setText(*lib->getNames().value(localeOrder)); - mUi->lblDescription->setText(lib->getDescriptions().value(localeOrder)); - mUi->lblVersion->setText(lib->getVersion().toStr()); - mUi->lblAuthor->setText(lib->getAuthor()); + mUi->lblName->setText(*lib.getNames().value(localeOrder)); + mUi->lblDescription->setText(lib.getDescriptions().value(localeOrder)); + mUi->lblVersion->setText(lib.getVersion().toStr()); + mUi->lblAuthor->setText(lib.getAuthor()); mUi->lblUrl->setText( QString("%2") - .arg(lib->getUrl().toEncoded(), lib->getUrl().toDisplayString())); - mUi->lblCreated->setText(lib->getCreated().toString(Qt::TextDate)); + .arg(lib.getUrl().toEncoded(), lib.getUrl().toDisplayString())); + mUi->lblCreated->setText(lib.getCreated().toString(Qt::TextDate)); mUi->lblDeprecated->setText( - lib->isDeprecated() ? tr("Yes - Consider switching to another library.") - : tr("No")); + lib.isDeprecated() ? tr("Yes - Consider switching to another library.") + : tr("No")); // extended attributes mUi->lblLibType->setText(isRemoteLibrary() ? tr("Remote") : tr("Local")); QString dependencies; - foreach (const Uuid& uuid, lib->getDependencies()) { - QSharedPointer installedLib = - mWorkspace.getLibrary(uuid, true, true); - QString line = dependencies.isEmpty() ? "" : "
"; - if (installedLib) { - line += QString(" %1 ✔") - .arg(*installedLib->getNames().value(localeOrder)); + foreach (const Uuid& uuid, lib.getDependencies()) { + QString line = dependencies.isEmpty() ? "" : "
"; + FilePath fp = ws.getLibraryDb().getLatestLibrary(uuid); // can throw + if (fp.isValid()) { + QString name; + ws.getLibraryDb().getElementTranslations(fp, localeOrder, + &name); // can throw + line += QString(" %1 ✔").arg(name); } else { line += QString(" %1 ✖").arg(uuid.toStr()); } @@ -97,9 +103,9 @@ LibraryInfoWidget::LibraryInfoWidget(workspace::Workspace& ws, mUi->lblDependencies->setText(dependencies); mUi->lblDirectory->setText( QString("%2") - .arg(lib->getFilePath().toQUrl().toLocalFile(), - lib->getFilePath().toRelative(ws.getLibrariesPath()))); - mUi->lblDirectory->setToolTip(lib->getFilePath().toNative()); + .arg(lib.getFilePath().toQUrl().toLocalFile(), + lib.getFilePath().toRelative(ws.getLibrariesPath()))); + mUi->lblDirectory->setToolTip(lib.getFilePath().toNative()); } LibraryInfoWidget::~LibraryInfoWidget() noexcept { @@ -110,7 +116,7 @@ LibraryInfoWidget::~LibraryInfoWidget() noexcept { ******************************************************************************/ void LibraryInfoWidget::btnOpenLibraryEditorClicked() noexcept { - emit openLibraryEditorTriggered(mLib); + emit openLibraryEditorTriggered(mLibDir); } void LibraryInfoWidget::btnRemoveLibraryClicked() noexcept { @@ -118,29 +124,23 @@ void LibraryInfoWidget::btnRemoveLibraryClicked() noexcept { QString text = QString(tr("Attention! This will remove the whole library directory:" "\n\n%1\n\nAre you really sure to remove \"%2\"?")) - .arg(mLib->getFilePath().toNative(), mUi->lblName->text()); + .arg(mLibDir.toNative(), mUi->lblName->text()); int res = QMessageBox::question(this, title, text, QMessageBox::Yes | QMessageBox::No); if (res == QMessageBox::Yes) { try { - if (isRemoteLibrary()) { - mWorkspace.removeRemoteLibrary( - mLib->getFilePath().getFilename()); // can throw - } else { - mWorkspace.removeLocalLibrary( - mLib->getFilePath().getFilename()); // can throw - } - emit libraryRemoved(mLib->getFilePath()); + FileUtils::removeDirRecursively(mLibDir); // can throw } catch (const Exception& e) { QMessageBox::critical(this, tr("Error"), e.getMsg()); } + mWorkspace.getLibraryDb().startLibraryRescan(); } } bool LibraryInfoWidget::isRemoteLibrary() const noexcept { - return mLib->isOpenedReadOnly(); + return mLibDir.isLocatedInDir(mWorkspace.getRemoteLibrariesPath()); } /******************************************************************************* diff --git a/libs/librepcb/librarymanager/libraryinfowidget.h b/libs/librepcb/librarymanager/libraryinfowidget.h index 688328fc58..6e3a861b51 100644 --- a/libs/librepcb/librarymanager/libraryinfowidget.h +++ b/libs/librepcb/librarymanager/libraryinfowidget.h @@ -23,7 +23,7 @@ /******************************************************************************* * Includes ******************************************************************************/ -#include +#include #include #include @@ -53,9 +53,6 @@ class LibraryInfoWidget; /** * @brief The LibraryInfoWidget class - * - * @author ubruhin - * @date 2016-08-03 */ class LibraryInfoWidget final : public QWidget { Q_OBJECT @@ -64,8 +61,7 @@ class LibraryInfoWidget final : public QWidget { // Constructors / Destructor LibraryInfoWidget() noexcept; LibraryInfoWidget(const LibraryInfoWidget& other) = delete; - LibraryInfoWidget(workspace::Workspace& ws, - QSharedPointer lib) noexcept; + LibraryInfoWidget(workspace::Workspace& ws, const FilePath& libDir); ~LibraryInfoWidget() noexcept; // Getters @@ -75,9 +71,7 @@ class LibraryInfoWidget final : public QWidget { LibraryInfoWidget& operator=(const LibraryInfoWidget& rhs) = delete; signals: - - void libraryRemoved(const FilePath& libDir); - void openLibraryEditorTriggered(QSharedPointer lib); + void openLibraryEditorTriggered(const FilePath& libDir); private: // Methods void btnOpenLibraryEditorClicked() noexcept; @@ -87,7 +81,7 @@ class LibraryInfoWidget final : public QWidget { private: // Data QScopedPointer mUi; workspace::Workspace& mWorkspace; - QSharedPointer mLib; + FilePath mLibDir; }; /******************************************************************************* diff --git a/libs/librepcb/librarymanager/librarylistwidgetitem.cpp b/libs/librepcb/librarymanager/librarylistwidgetitem.cpp index 2cfed706f4..fc2b0226bf 100644 --- a/libs/librepcb/librarymanager/librarylistwidgetitem.cpp +++ b/libs/librepcb/librarymanager/librarylistwidgetitem.cpp @@ -24,8 +24,6 @@ #include "ui_librarylistwidgetitem.h" -#include -#include #include #include @@ -42,19 +40,20 @@ namespace manager { * Constructors / Destructor ******************************************************************************/ -LibraryListWidgetItem::LibraryListWidgetItem( - workspace::Workspace& ws, QSharedPointer lib) noexcept +LibraryListWidgetItem::LibraryListWidgetItem(workspace::Workspace& ws, + const FilePath& libDir, + const QString& name, + const QString& description, + const QPixmap& icon) noexcept : QWidget(nullptr), mUi(new Ui::LibraryListWidgetItem), - mWorkspace(ws), - mLib(lib) { + mLibDir(libDir), + mIsRemoteLibrary(libDir.isLocatedInDir(ws.getRemoteLibrariesPath())) { mUi->setupUi(this); - if (lib) { - const QStringList& localeOrder = - ws.getSettings().getLibLocaleOrder().getLocaleOrder(); - if (!lib->getIconAsPixmap().isNull()) { - mUi->lblIcon->setPixmap(lib->getIconAsPixmap()); + if (mLibDir.isValid()) { + if (!icon.isNull()) { + mUi->lblIcon->setPixmap(icon); } if (isRemoteLibrary()) { mUi->lblLibraryType->setText(tr("(remote)")); @@ -63,11 +62,9 @@ LibraryListWidgetItem::LibraryListWidgetItem( mUi->lblLibraryType->setText(tr("(local)")); mUi->lblLibraryType->setStyleSheet("QLabel { color: blue; }"); } - mUi->lblLibraryName->setText(*lib->getNames().value(localeOrder)); - mUi->lblLibraryDescription->setText( - lib->getDescriptions().value(localeOrder)); - mUi->lblLibraryUrl->setText( - lib->getFilePath().toRelative(ws.getLibrariesPath())); + mUi->lblLibraryName->setText(name); + mUi->lblLibraryDescription->setText(description); + mUi->lblLibraryUrl->setText(libDir.toRelative(ws.getLibrariesPath())); } else { QPixmap image(":/img/actions/add.png"); mUi->lblIcon->setPixmap(image.scaled( @@ -94,17 +91,13 @@ QString LibraryListWidgetItem::getName() const noexcept { return mUi->lblLibraryName->text(); } -bool LibraryListWidgetItem::isRemoteLibrary() const noexcept { - return mLib ? mLib->isOpenedReadOnly() : false; -} - /******************************************************************************* * Inherited from QWidget ******************************************************************************/ void LibraryListWidgetItem::mouseDoubleClickEvent(QMouseEvent* e) noexcept { - if (mLib) { - emit openLibraryEditorTriggered(mLib); + if (mLibDir.isValid()) { + emit openLibraryEditorTriggered(mLibDir); e->accept(); } else { QWidget::mouseDoubleClickEvent(e); diff --git a/libs/librepcb/librarymanager/librarylistwidgetitem.h b/libs/librepcb/librarymanager/librarylistwidgetitem.h index d0f5193018..b859ee853b 100644 --- a/libs/librepcb/librarymanager/librarylistwidgetitem.h +++ b/libs/librepcb/librarymanager/librarylistwidgetitem.h @@ -23,7 +23,7 @@ /******************************************************************************* * Includes ******************************************************************************/ -#include +#include #include #include @@ -38,9 +38,6 @@ class Workspace; } namespace library { - -class Library; - namespace manager { namespace Ui { @@ -64,14 +61,16 @@ class LibraryListWidgetItem final : public QWidget { // Constructors / Destructor LibraryListWidgetItem() noexcept; LibraryListWidgetItem(const LibraryListWidgetItem& other) = delete; - LibraryListWidgetItem(workspace::Workspace& ws, - QSharedPointer lib) noexcept; + LibraryListWidgetItem(workspace::Workspace& ws, const FilePath& libDir, + const QString& name = "", + const QString& description = "", + const QPixmap& icon = QPixmap()) noexcept; ~LibraryListWidgetItem() noexcept; // Getters - QSharedPointer getLibrary() const noexcept { return mLib; } - QString getName() const noexcept; - bool isRemoteLibrary() const noexcept; + const FilePath& getLibraryFilePath() const noexcept { return mLibDir; } + QString getName() const noexcept; + bool isRemoteLibrary() const noexcept { return mIsRemoteLibrary; } // Operator Overloadings LibraryListWidgetItem& operator=(const LibraryListWidgetItem& rhs) = delete; @@ -80,12 +79,12 @@ class LibraryListWidgetItem final : public QWidget { void mouseDoubleClickEvent(QMouseEvent* e) noexcept override; signals: - void openLibraryEditorTriggered(QSharedPointer lib); + void openLibraryEditorTriggered(const FilePath& libDir); private: // Data QScopedPointer mUi; - workspace::Workspace& mWorkspace; - QSharedPointer mLib; + FilePath mLibDir; + bool mIsRemoteLibrary; }; /******************************************************************************* diff --git a/libs/librepcb/librarymanager/librarymanager.cpp b/libs/librepcb/librarymanager/librarymanager.cpp index a63b01898d..9d605fba7b 100644 --- a/libs/librepcb/librarymanager/librarymanager.cpp +++ b/libs/librepcb/librarymanager/librarymanager.cpp @@ -51,7 +51,8 @@ LibraryManager::LibraryManager(workspace::Workspace& ws, : QMainWindow(parent), mWorkspace(ws), mUi(new Ui::LibraryManager), - mCurrentWidget(nullptr) { + mCurrentWidget(nullptr), + mSelectedLibrary() { mUi->setupUi(this); connect(mUi->btnClose, &QPushButton::clicked, this, &QMainWindow::close); connect(mUi->lstLibraries, &QListWidget::currentItemChanged, this, @@ -62,7 +63,10 @@ LibraryManager::LibraryManager(workspace::Workspace& ws, connect(mAddLibraryWidget.data(), &AddLibraryWidget::libraryAdded, this, &LibraryManager::libraryAddedSlot); - loadLibraryList(); + updateLibraryList(); + connect(&mWorkspace.getLibraryDb(), + &workspace::WorkspaceLibraryDb::scanLibraryListUpdated, this, + &LibraryManager::updateLibraryList); } LibraryManager::~LibraryManager() noexcept { @@ -98,37 +102,57 @@ void LibraryManager::clearLibraryList() noexcept { Q_ASSERT(mUi->lstLibraries->count() == 0); } -void LibraryManager::loadLibraryList() noexcept { +void LibraryManager::updateLibraryList() noexcept { + FilePath selectedLibrary = mSelectedLibrary; + + clearLibraryList(); + QList widgets; // add the "Add new library" item - widgets.append(new LibraryListWidgetItem(mWorkspace, - QSharedPointer())); + widgets.append(new LibraryListWidgetItem(mWorkspace, FilePath())); // add all existing libraries - QList> libraries; - libraries.append(mWorkspace.getLocalLibraries().values()); - libraries.append(mWorkspace.getRemoteLibraries().values()); - foreach (const QSharedPointer& lib, libraries) { - LibraryListWidgetItem* widget = new LibraryListWidgetItem(mWorkspace, lib); - connect(widget, &LibraryListWidgetItem::openLibraryEditorTriggered, this, - &LibraryManager::openLibraryEditorTriggered); - widgets.append(widget); + try { + QMultiMap libraries = + mWorkspace.getLibraryDb().getLibraries(); // can throw + + foreach (const FilePath& libDir, libraries) { + QString name, description, keywords; + mWorkspace.getLibraryDb().getElementTranslations( + libDir, mWorkspace.getSettings().getLibLocaleOrder().getLocaleOrder(), + &name, &description, &keywords); // can throw + QPixmap icon; + mWorkspace.getLibraryDb().getLibraryMetadata(libDir, &icon); // can throw + + LibraryListWidgetItem* widget = new LibraryListWidgetItem( + mWorkspace, libDir, name, description, icon); + connect(widget, &LibraryListWidgetItem::openLibraryEditorTriggered, this, + &LibraryManager::openLibraryEditorTriggered); + widgets.append(widget); + } + } catch (const Exception& e) { + QMessageBox::critical(this, tr("Could not load library list"), e.getMsg()); } // sort all list widget items qSort(widgets.begin(), widgets.end(), widgetsLessThan); // populate the list widget - foreach (LibraryListWidgetItem* widget, widgets) { + int selectedLibraryIndex = 0; + for (int i = 0; i < widgets.count(); ++i) { + LibraryListWidgetItem* widget = widgets.at(i); Q_ASSERT(widget); QListWidgetItem* item = new QListWidgetItem(mUi->lstLibraries); item->setSizeHint(widget->sizeHint()); mUi->lstLibraries->setItemWidget(item, widget); + if (widget->getLibraryFilePath() == selectedLibrary) { + selectedLibraryIndex = i; + } } - // select the first item in the list - mUi->lstLibraries->setCurrentRow(0); + // select the previously selected library + mUi->lstLibraries->setCurrentRow(selectedLibraryIndex); } void LibraryManager::currentListItemChanged( @@ -140,18 +164,23 @@ void LibraryManager::currentListItemChanged( mCurrentWidget = nullptr; } + mSelectedLibrary = FilePath(); + if (current) { LibraryListWidgetItem* item = dynamic_cast( mUi->lstLibraries->itemWidget(current)); - if (item && (!item->getLibrary().isNull())) { - QSharedPointer lib = item->getLibrary(); - LibraryInfoWidget* widget = new LibraryInfoWidget(mWorkspace, lib); - connect(widget, &LibraryInfoWidget::openLibraryEditorTriggered, this, - &LibraryManager::openLibraryEditorTriggered); - connect(widget, &LibraryInfoWidget::libraryRemoved, this, - &LibraryManager::libraryRemovedSlot); - mUi->verticalLayout->insertWidget(0, widget); - mCurrentWidget = widget; + if (item && item->getLibraryFilePath().isValid()) { + try { + LibraryInfoWidget* widget = new LibraryInfoWidget( + mWorkspace, item->getLibraryFilePath()); // can throw + connect(widget, &LibraryInfoWidget::openLibraryEditorTriggered, this, + &LibraryManager::openLibraryEditorTriggered); + mUi->verticalLayout->insertWidget(0, widget); + mCurrentWidget = widget; + mSelectedLibrary = item->getLibraryFilePath(); + } catch (const Exception& e) { + QMessageBox::critical(this, tr("Error"), e.getMsg()); + } } } else { mCurrentWidget = new QWidget(); @@ -161,33 +190,12 @@ void LibraryManager::currentListItemChanged( mAddLibraryWidget->setVisible(mCurrentWidget ? false : true); } -void LibraryManager::libraryAddedSlot(const FilePath& libDir, - bool select) noexcept { - clearLibraryList(); - loadLibraryList(); - mAddLibraryWidget->updateInstalledStatusOfRepositoryLibraries(); - - if (select) { - // select the new item - for (int i = 0; i < mUi->lstLibraries->count(); i++) { - QListWidgetItem* item = mUi->lstLibraries->item(i); - Q_ASSERT(item); - LibraryListWidgetItem* widget = dynamic_cast( - mUi->lstLibraries->itemWidget(item)); - if (widget && widget->getLibrary() && - widget->getLibrary()->getFilePath() == libDir) { - mUi->lstLibraries->setCurrentItem(item); - break; - } - } - } -} - -void LibraryManager::libraryRemovedSlot(const FilePath& libDir) noexcept { - Q_UNUSED(libDir); - clearLibraryList(); - loadLibraryList(); - mAddLibraryWidget->updateInstalledStatusOfRepositoryLibraries(); +void LibraryManager::libraryAddedSlot(const FilePath& libDir) noexcept { + // Update the selected library and start library scan - the library list will + // be updated soon (triggered by the workspace library scanner), then the new + // library will be selected as soon as it appears in the list. + mSelectedLibrary = libDir; + mWorkspace.getLibraryDb().startLibraryRescan(); } /******************************************************************************* @@ -202,9 +210,9 @@ bool LibraryManager::widgetsLessThan(const LibraryListWidgetItem* a, } else if (a->isRemoteLibrary() && !b->isRemoteLibrary()) { return false; } else { - if (a->getLibrary().isNull()) { + if (!a->getLibraryFilePath().isValid()) { return true; - } else if (b->getLibrary().isNull()) { + } else if (!b->getLibraryFilePath().isValid()) { return false; } else { return a->getName().toLower() < b->getName().toLower(); diff --git a/libs/librepcb/librarymanager/librarymanager.h b/libs/librepcb/librarymanager/librarymanager.h index 26cbd965cb..1f0a03f50a 100644 --- a/libs/librepcb/librarymanager/librarymanager.h +++ b/libs/librepcb/librarymanager/librarymanager.h @@ -23,7 +23,7 @@ /******************************************************************************* * Includes ******************************************************************************/ -#include +#include #include #include @@ -79,23 +79,23 @@ class LibraryManager final : public QMainWindow { private: // Methods void closeEvent(QCloseEvent* event) noexcept override; void clearLibraryList() noexcept; - void loadLibraryList() noexcept; + void updateLibraryList() noexcept; void currentListItemChanged(QListWidgetItem* current, QListWidgetItem* previous) noexcept; - void libraryAddedSlot(const FilePath& libDir, bool select) noexcept; - void libraryRemovedSlot(const FilePath& libDir) noexcept; + void libraryAddedSlot(const FilePath& libDir) noexcept; static bool widgetsLessThan(const LibraryListWidgetItem* a, const LibraryListWidgetItem* b) noexcept; signals: - void openLibraryEditorTriggered(QSharedPointer lib); + void openLibraryEditorTriggered(const FilePath& libDir); private: // Data workspace::Workspace& mWorkspace; QScopedPointer mUi; QScopedPointer mAddLibraryWidget; QWidget* mCurrentWidget; + FilePath mSelectedLibrary; }; /******************************************************************************* diff --git a/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.cpp b/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.cpp index e6a034bc3d..3dba238d77 100644 --- a/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.cpp +++ b/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -86,6 +87,9 @@ RepositoryLibraryListWidgetItem::RepositoryLibraryListWidgetItem( // check if this library is already installed updateInstalledStatus(); + connect(&mWorkspace.getLibraryDb(), + &workspace::WorkspaceLibraryDb::scanLibraryListUpdated, this, + &RepositoryLibraryListWidgetItem::updateInstalledStatus); } RepositoryLibraryListWidgetItem::~RepositoryLibraryListWidgetItem() noexcept { @@ -111,41 +115,6 @@ void RepositoryLibraryListWidgetItem::setChecked(bool checked) noexcept { * General Methods ******************************************************************************/ -void RepositoryLibraryListWidgetItem::updateInstalledStatus() noexcept { - if (mUuid) { - QSharedPointer lib = - mWorkspace.getLibrary(*mUuid, true, true); - if (lib) { - mUi->lblInstalledVersion->setText( - QString(tr("Installed: v%1")).arg(lib->getVersion().toStr())); - mUi->lblInstalledVersion->setVisible(true); - if (lib->getVersion() < mVersion) { - mUi->lblInstalledVersion->setStyleSheet("QLabel {color: red;}"); - mUi->cbxDownload->setText(tr("Update")); - mUi->cbxDownload->setVisible(true); - } else { - mUi->lblInstalledVersion->setStyleSheet("QLabel {color: green;}"); - mUi->cbxDownload->setVisible(false); - } - } else { - if (mIsRecommended) { - mUi->lblInstalledVersion->setText(tr("Recommended")); - mUi->lblInstalledVersion->setStyleSheet("QLabel {color: blue;}"); - mUi->lblInstalledVersion->setVisible(true); - } else { - mUi->lblInstalledVersion->setVisible(false); - } - mUi->cbxDownload->setText(tr("Install")); - mUi->cbxDownload->setVisible(true); - } - } else { - mUi->lblInstalledVersion->setText(tr("Error: Invalid UUID")); - mUi->lblInstalledVersion->setStyleSheet("QLabel {color: red;}"); - mUi->lblInstalledVersion->setVisible(true); - mUi->cbxDownload->setVisible(false); - } -} - void RepositoryLibraryListWidgetItem::startDownloadIfSelected() noexcept { if (mUuid && mUi->cbxDownload->isVisible() && mUi->cbxDownload->isChecked() && (!mLibraryDownload)) { @@ -189,23 +158,7 @@ void RepositoryLibraryListWidgetItem::downloadFinished( bool success, const QString& errMsg) noexcept { Q_ASSERT(mLibraryDownload); - if (success) { - try { - // if the library exists already in the workspace, remove it first - QString libDirName = mLibraryDownload->getDestinationDir().getFilename(); - if (mWorkspace.getRemoteLibraries().contains(libDirName)) { - mWorkspace.removeRemoteLibrary(libDirName, false); // can throw - } - - // add downloaded library to workspace - mWorkspace.addRemoteLibrary(libDirName); // can throw - - // finish - emit libraryAdded(mLibraryDownload->getDestinationDir(), false); - } catch (const Exception& e) { - QMessageBox::critical(this, tr("Download failed"), e.getMsg()); - } - } else if (!errMsg.isEmpty()) { + if ((!success) && (!errMsg.isEmpty())) { QMessageBox::critical(this, tr("Download failed"), errMsg); } @@ -217,6 +170,9 @@ void RepositoryLibraryListWidgetItem::downloadFinished( // delete download helper mLibraryDownload.reset(); + + // start library scanner to index the new library + mWorkspace.getLibraryDb().startLibraryRescan(); } void RepositoryLibraryListWidgetItem::iconReceived( @@ -226,6 +182,52 @@ void RepositoryLibraryListWidgetItem::iconReceived( mUi->lblIcon->setPixmap(pixmap); } +void RepositoryLibraryListWidgetItem::updateInstalledStatus() noexcept { + if (mUuid) { + tl::optional installedVersion; + try { + FilePath fp = + mWorkspace.getLibraryDb().getLatestLibrary(*mUuid); // can throw + if (fp.isValid()) { + Version v = Version::fromString("0.1"); // only for initialization + mWorkspace.getLibraryDb().getElementMetadata(fp, nullptr, + &v); // can throw + installedVersion = v; + } + } catch (const Exception& e) { + qCritical() << "Could not determine if library is installed."; + } + if (installedVersion) { + mUi->lblInstalledVersion->setText( + QString(tr("Installed: v%1")).arg(installedVersion->toStr())); + mUi->lblInstalledVersion->setVisible(true); + if (installedVersion < mVersion) { + mUi->lblInstalledVersion->setStyleSheet("QLabel {color: red;}"); + mUi->cbxDownload->setText(tr("Update")); + mUi->cbxDownload->setVisible(true); + } else { + mUi->lblInstalledVersion->setStyleSheet("QLabel {color: green;}"); + mUi->cbxDownload->setVisible(false); + } + } else { + if (mIsRecommended) { + mUi->lblInstalledVersion->setText(tr("Recommended")); + mUi->lblInstalledVersion->setStyleSheet("QLabel {color: blue;}"); + mUi->lblInstalledVersion->setVisible(true); + } else { + mUi->lblInstalledVersion->setVisible(false); + } + mUi->cbxDownload->setText(tr("Install")); + mUi->cbxDownload->setVisible(true); + } + } else { + mUi->lblInstalledVersion->setText(tr("Error: Invalid UUID")); + mUi->lblInstalledVersion->setStyleSheet("QLabel {color: red;}"); + mUi->lblInstalledVersion->setVisible(true); + mUi->cbxDownload->setVisible(false); + } +} + /******************************************************************************* * End of File ******************************************************************************/ diff --git a/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.h b/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.h index 0dd6888dde..9aa7fa5c6f 100644 --- a/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.h +++ b/libs/librepcb/librarymanager/repositorylibrarylistwidgetitem.h @@ -80,7 +80,6 @@ class RepositoryLibraryListWidgetItem final : public QWidget { void setChecked(bool checked) noexcept; // General Methods - void updateInstalledStatus() noexcept; void startDownloadIfSelected() noexcept; // Operator Overloadings @@ -88,13 +87,12 @@ class RepositoryLibraryListWidgetItem final : public QWidget { const RepositoryLibraryListWidgetItem& rhs) = delete; signals: - void checkedChanged(bool checked); - void libraryAdded(const FilePath& libDir, bool select); private: // Methods void downloadFinished(bool success, const QString& errMsg) noexcept; void iconReceived(const QByteArray& data) noexcept; + void updateInstalledStatus() noexcept; private: // Data workspace::Workspace& mWorkspace; diff --git a/tests/funq/librarymanager/test_install_remote_libraries.py b/tests/funq/librarymanager/test_install_remote_libraries.py index 055ba61b04..181715b320 100644 --- a/tests/funq/librarymanager/test_install_remote_libraries.py +++ b/tests/funq/librarymanager/test_install_remote_libraries.py @@ -57,6 +57,11 @@ def test(librepcb, helpers): # Install selected libraries app.widget('libraryManagerDownloadFromRepoDownloadButton').click() + # Check if two libraries were added + library_count_after = library_count_before + 2 + helpers.wait_for_model_items_count(library_list, library_count_after, + library_count_after) + # Wait until progress bars are at 100% and hidden (i.e. installation finished) for i in range(0, remote_library_count): props = {'value': 100 if i <= 1 else 0, 'visible': False} @@ -68,8 +73,3 @@ def test(librepcb, helpers): assert statuslabels[i].properties()['text'].startswith('Installed') else: assert statuslabels[i].properties()['text'] == 'Recommended' - - # Check if two libraries were added - library_count_after = library_count_before + 2 - helpers.wait_for_model_items_count(library_list, library_count_after, - library_count_after) From 79c8426b3a10f1b5b8a4b7aa6cf35e5c07d410d2 Mon Sep 17 00:00:00 2001 From: "U. Bruhin" Date: Thu, 27 Dec 2018 19:33:57 +0100 Subject: [PATCH 6/6] Workspace: Don't load libraries at startup --- apps/librepcb/controlpanel/controlpanel.cpp | 43 ++++--- apps/librepcb/controlpanel/controlpanel.h | 15 +-- libs/librepcb/workspace/workspace.cpp | 124 ------------------- libs/librepcb/workspace/workspace.h | 127 +++++--------------- 4 files changed, 62 insertions(+), 247 deletions(-) diff --git a/apps/librepcb/controlpanel/controlpanel.cpp b/apps/librepcb/controlpanel/controlpanel.cpp index 52960e4160..e9d5f47751 100644 --- a/apps/librepcb/controlpanel/controlpanel.cpp +++ b/apps/librepcb/controlpanel/controlpanel.cpp @@ -94,17 +94,14 @@ ControlPanel::ControlPanel(Workspace& workspace) Workspace::getHighestFileFormatVersionOfWorkspace(workspace.getPath()); mUi->lblWarnForNewerAppVersions->setVisible(highestVersion > actualVersion); - // decide if we have to show the warning about missing workspace libraries - if (mWorkspace.getLocalLibraries().isEmpty() && - mWorkspace.getRemoteLibraries().isEmpty()) { - mUi->lblWarnForNoLibraries->setVisible(true); - connect(mUi->lblWarnForNoLibraries, &QLabel::linkActivated, this, - &ControlPanel::on_actionOpen_Library_Manager_triggered); - connect(&mWorkspace, &Workspace::libraryAdded, mUi->lblWarnForNoLibraries, - &QLabel::hide); - } else { - mUi->lblWarnForNoLibraries->setVisible(false); - } + // hide warning about missing libraries, but update visibility each time the + // workspace library was scanned + mUi->lblWarnForNoLibraries->setVisible(false); + connect(mUi->lblWarnForNoLibraries, &QLabel::linkActivated, this, + &ControlPanel::on_actionOpen_Library_Manager_triggered); + connect(&mWorkspace.getLibraryDb(), + &WorkspaceLibraryDb::scanLibraryListUpdated, this, + &ControlPanel::updateNoLibrariesWarningVisibility); // connect some actions which are created with the Qt Designer connect(mUi->actionQuit, &QAction::triggered, this, &ControlPanel::close); @@ -248,6 +245,16 @@ void ControlPanel::loadSettings() { clientSettings.endGroup(); } +void ControlPanel::updateNoLibrariesWarningVisibility() noexcept { + bool showWarning = false; + try { + showWarning = mWorkspace.getLibraryDb().getLibraries().isEmpty(); + } catch (const Exception& e) { + qCritical() << "Could not get library list:" << e.getMsg(); + } + mUi->lblWarnForNoLibraries->setVisible(showWarning); +} + void ControlPanel::showProjectReadmeInBrowser( const FilePath& projectFilePath) noexcept { if (projectFilePath.isValid()) { @@ -371,16 +378,18 @@ ProjectEditor* ControlPanel::getOpenProject(const FilePath& filepath) const * Library Management ******************************************************************************/ -void ControlPanel::openLibraryEditor( - QSharedPointer lib) noexcept { +void ControlPanel::openLibraryEditor(const FilePath& libDir) noexcept { + using library::Library; using library::editor::LibraryEditor; - LibraryEditor* editor = mOpenLibraryEditors.value(lib.data()); + LibraryEditor* editor = mOpenLibraryEditors.value(libDir); if (!editor) { try { + bool remote = libDir.isLocatedInDir(mWorkspace.getRemoteLibrariesPath()); + QSharedPointer lib(new Library(libDir, remote)); editor = new LibraryEditor(mWorkspace, lib); connect(editor, &LibraryEditor::destroyed, this, &ControlPanel::libraryEditorDestroyed); - mOpenLibraryEditors.insert(lib.data(), editor); + mOpenLibraryEditors.insert(libDir, editor); } catch (const Exception& e) { QMessageBox::critical(this, tr("Error"), e.getMsg()); } @@ -398,8 +407,8 @@ void ControlPanel::libraryEditorDestroyed() noexcept { // static_cast is used instead ;) LibraryEditor* editor = static_cast(QObject::sender()); Q_ASSERT(editor); - library::Library* library = mOpenLibraryEditors.key(editor); - Q_ASSERT(library); + FilePath library = mOpenLibraryEditors.key(editor); + Q_ASSERT(library.isValid()); mOpenLibraryEditors.remove(library); } diff --git a/apps/librepcb/controlpanel/controlpanel.h b/apps/librepcb/controlpanel/controlpanel.h index de2af537ce..4778a00440 100644 --- a/apps/librepcb/controlpanel/controlpanel.h +++ b/apps/librepcb/controlpanel/controlpanel.h @@ -130,6 +130,7 @@ private slots: // General private methods void saveSettings(); void loadSettings(); + void updateNoLibrariesWarningVisibility() noexcept; void showProjectReadmeInBrowser(const FilePath& projectFilePath) noexcept; // Project Management @@ -206,7 +207,7 @@ private slots: noexcept; // Library Management - void openLibraryEditor(QSharedPointer lib) noexcept; + void openLibraryEditor(const FilePath& libDir) noexcept; void libraryEditorDestroyed() noexcept; /** @@ -220,12 +221,12 @@ private slots: bool closeAllLibraryEditors(bool askForSave) noexcept; // Attributes - workspace::Workspace& mWorkspace; - QScopedPointer mUi; - QScopedPointer mLibraryManager; - QHash mOpenProjectEditors; - QHash mOpenLibraryEditors; - QScopedPointer mProjectLibraryUpdater; + workspace::Workspace& mWorkspace; + QScopedPointer mUi; + QScopedPointer mLibraryManager; + QHash mOpenProjectEditors; + QHash mOpenLibraryEditors; + QScopedPointer mProjectLibraryUpdater; }; /******************************************************************************* diff --git a/libs/librepcb/workspace/workspace.cpp b/libs/librepcb/workspace/workspace.cpp index effd994b0b..6fe8f7d0e5 100644 --- a/libs/librepcb/workspace/workspace.cpp +++ b/libs/librepcb/workspace/workspace.cpp @@ -112,59 +112,8 @@ Workspace::Workspace(const FilePath& wsPath) // load workspace settings mWorkspaceSettings.reset(new WorkspaceSettings(*this)); - // load local libraries - FilePath localLibsDirPath = getLocalLibrariesPath(); - QDir localLibsDir(localLibsDirPath.toStr()); - foreach (const QString& dir, - localLibsDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { - FilePath libDirPath = localLibsDirPath.getPathTo(dir); - if (Library::isValidElementDirectory(libDirPath)) { - qDebug() << "Load local workspace library:" << dir; - try { - addLocalLibrary(dir); // can throw - } catch (Exception& e) { - // @todo do not show a message box here, better use something like a - // getLastError() method which is used by the ControlPanel to show - // errors - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("Could not open local library %1: %2")) - .arg(dir, e.getMsg())); - } - } else { - qWarning() << "Directory is not a valid libary:" << libDirPath.toNative(); - } - } - - // load remote libraries - FilePath remoteLibsDirPath = getRemoteLibrariesPath(); - QDir remoteLibsDir(remoteLibsDirPath.toStr()); - foreach (const QString& dir, - remoteLibsDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { - FilePath libDirPath = remoteLibsDirPath.getPathTo(dir); - if (Library::isValidElementDirectory(libDirPath)) { - qDebug() << "Load remote workspace library:" << dir; - try { - addRemoteLibrary(dir); // can throw - } catch (Exception& e) { - // @todo do not show a message box here, better use something like a - // getLastError() method which is used by the ControlPanel to show - // errors - QMessageBox::critical( - nullptr, tr("Error"), - QString(tr("Could not open remote library %1: %2")) - .arg(dir, e.getMsg())); - } - } else { - qWarning() << "Directory is not a valid libary:" << libDirPath.toNative(); - } - } - // load library database mLibraryDb.reset(new WorkspaceLibraryDb(*this)); // can throw - connect(this, &Workspace::libraryAdded, mLibraryDb.data(), - &WorkspaceLibraryDb::startLibraryRescan); - connect(this, &Workspace::libraryRemoved, mLibraryDb.data(), - &WorkspaceLibraryDb::startLibraryRescan); // load project models mRecentProjectsModel.reset(new RecentProjectsModel(*this)); @@ -191,79 +140,6 @@ FavoriteProjectsModel& Workspace::getFavoriteProjectsModel() const noexcept { return *mFavoriteProjectsModel; } -/******************************************************************************* - * Library Management - ******************************************************************************/ - -QSharedPointer Workspace::getLibrary(const Uuid& uuid, - bool local, - bool remote) const - noexcept { - QSharedPointer library; - if (local) { - foreach (const auto& lib, mLocalLibraries) { - Q_ASSERT(lib); - if ((lib->getUuid() == uuid) && - ((!library) || (library->getVersion() < lib->getVersion()))) { - library = lib; - } - } - } - if (remote) { - foreach (const auto& lib, mRemoteLibraries) { - Q_ASSERT(lib); - if ((lib->getUuid() == uuid) && - ((!library) || (library->getVersion() < lib->getVersion()))) { - library = lib; - } - } - } - return library; -} - -void Workspace::addLocalLibrary(const QString& libDirName) { - if (!mLocalLibraries.contains(libDirName)) { - FilePath libDirPath = - mLibrariesPath.getPathTo("local").getPathTo(libDirName); - QSharedPointer library( - new Library(libDirPath, false)); // can throw - mLocalLibraries.insert(libDirName, library); - emit libraryAdded(libDirPath); - } -} - -void Workspace::addRemoteLibrary(const QString& libDirName) { - if (!mRemoteLibraries.contains(libDirName)) { - // remote libraries are always opened read-only! - FilePath libDirPath = - mLibrariesPath.getPathTo("remote").getPathTo(libDirName); - QSharedPointer library( - new Library(libDirPath, true)); // can throw - mRemoteLibraries.insert(libDirName, library); - emit libraryAdded(libDirPath); - } -} - -void Workspace::removeLocalLibrary(const QString& libDirName, bool rmDir) { - Library* library = mLocalLibraries.value(libDirName).data(); - if (library) { - FilePath libDirPath = library->getFilePath(); - mLocalLibraries.remove(libDirName); - emit libraryRemoved(libDirPath); - if (rmDir) FileUtils::removeDirRecursively(libDirPath); // can throw - } -} - -void Workspace::removeRemoteLibrary(const QString& libDirName, bool rmDir) { - Library* library = mRemoteLibraries.value(libDirName).data(); - if (library) { - FilePath libDirPath = library->getFilePath(); - mRemoteLibraries.remove(libDirName); - emit libraryRemoved(libDirPath); - if (rmDir) FileUtils::removeDirRecursively(libDirPath); // can throw - } -} - /******************************************************************************* * Project Management ******************************************************************************/ diff --git a/libs/librepcb/workspace/workspace.h b/libs/librepcb/workspace/workspace.h index 97e823294d..5ebdd0cf72 100644 --- a/libs/librepcb/workspace/workspace.h +++ b/libs/librepcb/workspace/workspace.h @@ -23,9 +23,7 @@ /******************************************************************************* * Includes ******************************************************************************/ -#include #include -#include #include #include @@ -134,79 +132,6 @@ class Workspace final : public QObject { // Library Management - /** - * @brief Get the (highest version) library of a given UUID - * - * @param uuid The uuid of the library - * @param local If true, local libraries are searched - * @param remote If true, remote libraries are searched - * - * @return The library with the highest version (nullptr if not installed) - */ - QSharedPointer getLibrary(const Uuid& uuid, - bool local = true, - bool remote = true) const - noexcept; - - /** - * @brief Get all local libraries (located in "workspace/v#/libraries/local") - * - * @return A list of all local libraries - */ - const QMap> getLocalLibraries() - const noexcept { - return mLocalLibraries; - } - - /** - * @brief Get all remote libraries (located in - * "workspace/v#/libraries/remote") - * - * @return A list of all remote libraries - */ - const QMap> getRemoteLibraries() - const noexcept { - return mRemoteLibraries; - } - - /** - * @brief Add a new local library - * - * @param libDirName The name of the (existing) local library directory - * - * @throws Exception on error - */ - void addLocalLibrary(const QString& libDirName); - - /** - * @brief Add a new remote library - * - * @param libDirName The name of the (existing) remote library directory - * - * @throws Exception on error - */ - void addRemoteLibrary(const QString& libDirName); - - /** - * @brief Remove a local library - * - * @param libDirName The name of the (existing) local library directory - * @param rmDir It true, the library's directory will be removed - * - * @throws Exception on error - */ - void removeLocalLibrary(const QString& libDirName, bool rmDir = true); - - /** - * @brief Remove a remote library - * - * @param libDirName The name of the (existing) remote library directory - * @param rmDir It true, the library's directory will be removed - * - * @throws Exception on error - */ - void removeRemoteLibrary(const QString& libDirName, bool rmDir = true); - /** * @brief Get the workspace library database */ @@ -317,32 +242,36 @@ class Workspace final : public QObject { return Version::fromString("0.1"); } -signals: +private: // Data + /// a FilePath object which represents the workspace directory + FilePath mPath; + + /// the directory "projects" + FilePath mProjectsPath; - void libraryAdded(const FilePath& libDir); - void libraryRemoved(const FilePath& libDir); + /// the subdirectory of the current file format version + FilePath mMetadataPath; -private: // Data - FilePath - mPath; ///< a FilePath object which represents the workspace directory - FilePath mProjectsPath; ///< the directory "projects" - FilePath - mMetadataPath; ///< the subdirectory of the current file format version - FilePath mLibrariesPath; ///< the directory "v#/libraries" - DirectoryLock mLock; ///< to lock the version directory (#mVersionPath) - QScopedPointer - mWorkspaceSettings; ///< the WorkspaceSettings object - QMap> - mLocalLibraries; ///< all local libraries - QMap> - mRemoteLibraries; ///< all remote libraries - QScopedPointer mLibraryDb; ///< the library database - QScopedPointer - mProjectTreeModel; ///< a tree model for the whole projects directory - QScopedPointer - mRecentProjectsModel; ///< a list model of all recent projects - QScopedPointer - mFavoriteProjectsModel; ///< a list model of all favorite projects + /// the directory "v#/libraries" + FilePath mLibrariesPath; + + /// to lock the version directory (#mVersionPath) + DirectoryLock mLock; + + /// the WorkspaceSettings object + QScopedPointer mWorkspaceSettings; + + /// the library database + QScopedPointer mLibraryDb; + + /// a tree model for the whole projects directory + QScopedPointer mProjectTreeModel; + + /// a list model of all recent projects + QScopedPointer mRecentProjectsModel; + + /// a list model of all favorite projects + QScopedPointer mFavoriteProjectsModel; }; /*******************************************************************************