Skip to content

Commit

Permalink
Merge pull request #426 from LibrePCB/378-hide-empty-categories
Browse files Browse the repository at this point in the history
Filter all category trees to show only relevant entries
(cherry picked from commit efa95a2)
  • Loading branch information
ubruhin committed Mar 17, 2019
1 parent e3b34aa commit b41f738
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 34 deletions.
15 changes: 15 additions & 0 deletions libs/librepcb/common/sqlitedatabase.cpp
Expand Up @@ -160,6 +160,21 @@ QSqlQuery SQLiteDatabase::prepareQuery(const QString& query) const {
return q; return q;
} }


int SQLiteDatabase::count(QSqlQuery& query) {
exec(query); // can throw

int count = 0;
bool success = query.next() && query.value(0).isValid();
if (success) {
count = query.value(0).toInt(&success);
}
if (success) {
return count;
} else {
throw LogicError(__FILE__, __LINE__);
}
}

int SQLiteDatabase::insert(QSqlQuery& query) { int SQLiteDatabase::insert(QSqlQuery& query) {
exec(query); // can throw exec(query); // can throw


Expand Down
1 change: 1 addition & 0 deletions libs/librepcb/common/sqlitedatabase.h
Expand Up @@ -77,6 +77,7 @@ class SQLiteDatabase final : public QObject {


// General Methods // General Methods
QSqlQuery prepareQuery(const QString& query) const; QSqlQuery prepareQuery(const QString& query) const;
int count(QSqlQuery& query);
int insert(QSqlQuery& query); int insert(QSqlQuery& query);
void exec(QSqlQuery& query); void exec(QSqlQuery& query);
void exec(const QString& query); void exec(const QString& query);
Expand Down
4 changes: 2 additions & 2 deletions libs/librepcb/libraryeditor/common/categorychooserdialog.cpp
Expand Up @@ -50,8 +50,8 @@ CategoryChooserDialog<ElementType>::CategoryChooserDialog(
&CategoryChooserDialog<ElementType>::accept); &CategoryChooserDialog<ElementType>::accept);


mModel.reset(new workspace::CategoryTreeModel<ElementType>( mModel.reset(new workspace::CategoryTreeModel<ElementType>(
ws.getLibraryDb(), ws.getLibraryDb(), ws.getSettings().getLibLocaleOrder().getLocaleOrder(),
ws.getSettings().getLibLocaleOrder().getLocaleOrder())); workspace::CategoryTreeFilter::ALL));
mUi->treeView->setModel(mModel.data()); mUi->treeView->setModel(mModel.data());
mUi->treeView->setRootIndex(QModelIndex()); mUi->treeView->setRootIndex(QModelIndex());
} }
Expand Down
Expand Up @@ -60,7 +60,8 @@ ComponentChooserDialog::ComponentChooserDialog(
mUi->graphicsView->setScene(mGraphicsScene.data()); mUi->graphicsView->setScene(mGraphicsScene.data());


mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel( mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder())); mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::COMPONENTS));
mUi->treeCategories->setModel(mCategoryTreeModel.data()); mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(), connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this, &QItemSelectionModel::currentChanged, this,
Expand Down
3 changes: 2 additions & 1 deletion libs/librepcb/libraryeditor/common/packagechooserdialog.cpp
Expand Up @@ -61,7 +61,8 @@ PackageChooserDialog::PackageChooserDialog(
mUi->graphicsView->setScene(mGraphicsScene.data()); mUi->graphicsView->setScene(mGraphicsScene.data());


mCategoryTreeModel.reset(new workspace::PackageCategoryTreeModel( mCategoryTreeModel.reset(new workspace::PackageCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder())); mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::PACKAGES));
mUi->treeCategories->setModel(mCategoryTreeModel.data()); mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(), connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this, &QItemSelectionModel::currentChanged, this,
Expand Down
3 changes: 2 additions & 1 deletion libs/librepcb/libraryeditor/common/symbolchooserdialog.cpp
Expand Up @@ -60,7 +60,8 @@ SymbolChooserDialog::SymbolChooserDialog(
mUi->graphicsView->setOriginCrossVisible(false); mUi->graphicsView->setOriginCrossVisible(false);


mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel( mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder())); mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::SYMBOLS));
mUi->treeCategories->setModel(mCategoryTreeModel.data()); mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(), connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this, &QItemSelectionModel::currentChanged, this,
Expand Down
Expand Up @@ -247,22 +247,42 @@ void NewElementWizardPage_CopyFrom::initializePage() noexcept {
setSelectedElement(FilePath()); setSelectedElement(FilePath());
mIsCategoryElement = false; mIsCategoryElement = false;
switch (mContext.mElementType) { switch (mContext.mElementType) {
case NewElementWizardContext::ElementType::ComponentCategory: case NewElementWizardContext::ElementType::ComponentCategory: {
mIsCategoryElement = true; mIsCategoryElement = true;
case NewElementWizardContext::ElementType::Symbol: setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
case NewElementWizardContext::ElementType::Component: mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::ALL));
break;
}
case NewElementWizardContext::ElementType::Symbol: {
setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::SYMBOLS));
break;
}
case NewElementWizardContext::ElementType::Component: {
setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::COMPONENTS));
break;
}
case NewElementWizardContext::ElementType::Device: { case NewElementWizardContext::ElementType::Device: {
setCategoryTreeModel(new workspace::ComponentCategoryTreeModel( setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
mContext.getLibLocaleOrder())); workspace::CategoryTreeFilter::DEVICES));
break; break;
} }
case NewElementWizardContext::ElementType::PackageCategory: case NewElementWizardContext::ElementType::PackageCategory: {
mIsCategoryElement = true; mIsCategoryElement = true;
setCategoryTreeModel(new workspace::PackageCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::ALL));
break;
}
case NewElementWizardContext::ElementType::Package: { case NewElementWizardContext::ElementType::Package: {
setCategoryTreeModel(new workspace::PackageCategoryTreeModel( setCategoryTreeModel(new workspace::PackageCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
mContext.getLibLocaleOrder())); workspace::CategoryTreeFilter::PACKAGES));
break; break;
} }
default: { default: {
Expand Down
3 changes: 2 additions & 1 deletion libs/librepcb/projecteditor/dialogs/addcomponentdialog.cpp
Expand Up @@ -105,7 +105,8 @@ AddComponentDialog::AddComponentDialog(workspace::Workspace& workspace,


const QStringList& localeOrder = mProject.getSettings().getLocaleOrder(); const QStringList& localeOrder = mProject.getSettings().getLocaleOrder();
mCategoryTreeModel = new workspace::ComponentCategoryTreeModel( mCategoryTreeModel = new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder); mWorkspace.getLibraryDb(), localeOrder,
workspace::CategoryTreeFilter::COMPONENTS);
mUi->treeCategories->setModel(mCategoryTreeModel); mUi->treeCategories->setModel(mCategoryTreeModel);
connect(mUi->treeCategories->selectionModel(), connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this, &QItemSelectionModel::currentChanged, this,
Expand Down
62 changes: 54 additions & 8 deletions libs/librepcb/workspace/library/cat/categorytreeitem.cpp
Expand Up @@ -39,11 +39,13 @@ namespace workspace {
template <typename ElementType> template <typename ElementType>
CategoryTreeItem<ElementType>::CategoryTreeItem( CategoryTreeItem<ElementType>::CategoryTreeItem(
const WorkspaceLibraryDb& library, const QStringList localeOrder, const WorkspaceLibraryDb& library, const QStringList localeOrder,
CategoryTreeItem* parent, const tl::optional<Uuid>& uuid) noexcept CategoryTreeItem* parent, const tl::optional<Uuid>& uuid,
CategoryTreeFilter::Flags filter) noexcept
: mParent(parent), : mParent(parent),
mUuid(uuid), mUuid(uuid),
mDepth(parent ? parent->getDepth() + 1 : 0), mDepth(parent ? parent->getDepth() + 1 : 0),
mExceptionMessage() { mExceptionMessage(),
mIsVisible(false) {
try { try {
if (mUuid) { if (mUuid) {
FilePath fp = getLatestCategory(library); FilePath fp = getLatestCategory(library);
Expand All @@ -56,9 +58,11 @@ CategoryTreeItem<ElementType>::CategoryTreeItem(
if (mUuid || (!mParent)) { if (mUuid || (!mParent)) {
QSet<Uuid> childs = getCategoryChilds(library); QSet<Uuid> childs = getCategoryChilds(library);
foreach (const Uuid& childUuid, childs) { foreach (const Uuid& childUuid, childs) {
ChildType child( ChildType child(new CategoryTreeItem(library, localeOrder, this,
new CategoryTreeItem(library, localeOrder, this, childUuid)); childUuid, filter));
mChilds.append(child); if (child->isVisible()) {
mChilds.append(child);
}
} }


// sort childs // sort childs
Expand All @@ -70,12 +74,19 @@ CategoryTreeItem<ElementType>::CategoryTreeItem(


if (!mParent) { if (!mParent) {
// add category for elements without category // add category for elements without category
ChildType child( ChildType child(new CategoryTreeItem(library, localeOrder, this,
new CategoryTreeItem(library, localeOrder, this, tl::nullopt)); tl::nullopt, filter));
mChilds.append(child); if (child->isVisible()) {
mChilds.append(child);
}
}

if ((!mChilds.isEmpty()) || matchesFilter(library, filter)) {
mIsVisible = true;
} }
} catch (const Exception& e) { } catch (const Exception& e) {
mExceptionMessage = e.getMsg(); mExceptionMessage = e.getMsg();
mIsVisible = true; // make sure errors are visible
} }
} }


Expand Down Expand Up @@ -164,6 +175,41 @@ QSet<Uuid> CategoryTreeItem<library::PackageCategory>::getCategoryChilds(
return lib.getPackageCategoryChilds(mUuid); return lib.getPackageCategoryChilds(mUuid);
} }


template <>
bool CategoryTreeItem<library::ComponentCategory>::matchesFilter(
const WorkspaceLibraryDb& lib, CategoryTreeFilter::Flags filter) const {
if (filter.testFlag(CategoryTreeFilter::ALL)) {
return true;
}
int categories = 0, symbols = 0, components = 0, devices = 0;
lib.getComponentCategoryElementCount(mUuid, &categories, &symbols,
&components, &devices);
if (filter.testFlag(CategoryTreeFilter::SYMBOLS) && (symbols > 0)) {
return true;
}
if (filter.testFlag(CategoryTreeFilter::COMPONENTS) && (components > 0)) {
return true;
}
if (filter.testFlag(CategoryTreeFilter::DEVICES) && (devices > 0)) {
return true;
}
return false;
}

template <>
bool CategoryTreeItem<library::PackageCategory>::matchesFilter(
const WorkspaceLibraryDb& lib, CategoryTreeFilter::Flags filter) const {
if (filter.testFlag(CategoryTreeFilter::ALL)) {
return true;
}
int categories = 0, packages = 0;
lib.getPackageCategoryElementCount(mUuid, &categories, &packages);
if (filter.testFlag(CategoryTreeFilter::PACKAGES) && (packages > 0)) {
return true;
}
return false;
}

/******************************************************************************* /*******************************************************************************
* Explicit template instantiations * Explicit template instantiations
******************************************************************************/ ******************************************************************************/
Expand Down
24 changes: 23 additions & 1 deletion libs/librepcb/workspace/library/cat/categorytreeitem.h
Expand Up @@ -41,6 +41,21 @@ namespace workspace {


class WorkspaceLibraryDb; class WorkspaceLibraryDb;


/*******************************************************************************
* Class CategoryTreeFilter
******************************************************************************/

struct CategoryTreeFilter {
enum Flag {
SYMBOLS = 1 << 0, ///< Show items containing symbols
PACKAGES = 1 << 1, ///< Show items containing packages
COMPONENTS = 1 << 2, ///< Show items containing components
DEVICES = 1 << 3, ///< Show items containing devices
ALL = 1 << 4, ///< Show all items, even empty ones
};
Q_DECLARE_FLAGS(Flags, Flag)
};

/******************************************************************************* /*******************************************************************************
* Class CategoryTreeItem * Class CategoryTreeItem
******************************************************************************/ ******************************************************************************/
Expand All @@ -56,7 +71,8 @@ class CategoryTreeItem final {
CategoryTreeItem(const CategoryTreeItem& other) = delete; CategoryTreeItem(const CategoryTreeItem& other) = delete;
CategoryTreeItem(const WorkspaceLibraryDb& library, CategoryTreeItem(const WorkspaceLibraryDb& library,
const QStringList localeOrder, CategoryTreeItem* parent, const QStringList localeOrder, CategoryTreeItem* parent,
const tl::optional<Uuid>& uuid) noexcept; const tl::optional<Uuid>& uuid,
CategoryTreeFilter::Flags filter) noexcept;
~CategoryTreeItem() noexcept; ~CategoryTreeItem() noexcept;


// Getters // Getters
Expand All @@ -70,6 +86,7 @@ class CategoryTreeItem final {
int getChildCount() const noexcept { return mChilds.count(); } int getChildCount() const noexcept { return mChilds.count(); }
int getChildNumber() const noexcept; int getChildNumber() const noexcept;
QVariant data(int role) const noexcept; QVariant data(int role) const noexcept;
bool isVisible() const noexcept { return mIsVisible; }


// Operator Overloadings // Operator Overloadings
CategoryTreeItem& operator=(const CategoryTreeItem& rhs) = delete; CategoryTreeItem& operator=(const CategoryTreeItem& rhs) = delete;
Expand All @@ -81,6 +98,8 @@ class CategoryTreeItem final {
// Methods // Methods
FilePath getLatestCategory(const WorkspaceLibraryDb& lib) const; FilePath getLatestCategory(const WorkspaceLibraryDb& lib) const;
QSet<Uuid> getCategoryChilds(const WorkspaceLibraryDb& lib) const; QSet<Uuid> getCategoryChilds(const WorkspaceLibraryDb& lib) const;
bool matchesFilter(const WorkspaceLibraryDb& lib,
CategoryTreeFilter::Flags filter) const;


// Attributes // Attributes
CategoryTreeItem* mParent; CategoryTreeItem* mParent;
Expand All @@ -90,6 +109,7 @@ class CategoryTreeItem final {
unsigned int mDepth; ///< this is to avoid endless recursion in the unsigned int mDepth; ///< this is to avoid endless recursion in the
///< parent-child relationship ///< parent-child relationship
QString mExceptionMessage; QString mExceptionMessage;
bool mIsVisible;
QList<ChildType> mChilds; QList<ChildType> mChilds;
}; };


Expand All @@ -103,4 +123,6 @@ typedef CategoryTreeItem<library::PackageCategory> PackageCategoryTreeItem;
} // namespace workspace } // namespace workspace
} // namespace librepcb } // namespace librepcb


Q_DECLARE_OPERATORS_FOR_FLAGS(librepcb::workspace::CategoryTreeFilter::Flags)

#endif // LIBREPCB_LIBRARY_CATEGORYTREEITEM_H #endif // LIBREPCB_LIBRARY_CATEGORYTREEITEM_H
7 changes: 4 additions & 3 deletions libs/librepcb/workspace/library/cat/categorytreemodel.cpp
Expand Up @@ -40,10 +40,11 @@ namespace workspace {


template <typename ElementType> template <typename ElementType>
CategoryTreeModel<ElementType>::CategoryTreeModel( CategoryTreeModel<ElementType>::CategoryTreeModel(
const WorkspaceLibraryDb& library, const QStringList& localeOrder) noexcept const WorkspaceLibraryDb& library, const QStringList& localeOrder,
CategoryTreeFilter::Flags filter) noexcept
: QAbstractItemModel(nullptr) { : QAbstractItemModel(nullptr) {
mRootItem.reset(new CategoryTreeItem<ElementType>(library, localeOrder, mRootItem.reset(new CategoryTreeItem<ElementType>(
nullptr, tl::nullopt)); library, localeOrder, nullptr, tl::nullopt, filter));
} }


template <typename ElementType> template <typename ElementType>
Expand Down
3 changes: 2 additions & 1 deletion libs/librepcb/workspace/library/cat/categorytreemodel.h
Expand Up @@ -49,7 +49,8 @@ class CategoryTreeModel final : public QAbstractItemModel {
CategoryTreeModel() = delete; CategoryTreeModel() = delete;
CategoryTreeModel(const CategoryTreeModel& other) = delete; CategoryTreeModel(const CategoryTreeModel& other) = delete;
explicit CategoryTreeModel(const WorkspaceLibraryDb& library, explicit CategoryTreeModel(const WorkspaceLibraryDb& library,
const QStringList& localeOrder) noexcept; const QStringList& localeOrder,
CategoryTreeFilter::Flags filter) noexcept;
~CategoryTreeModel() noexcept; ~CategoryTreeModel() noexcept;


// Getters // Getters
Expand Down
47 changes: 47 additions & 0 deletions libs/librepcb/workspace/library/workspacelibrarydb.cpp
Expand Up @@ -400,6 +400,34 @@ QList<Uuid> WorkspaceLibraryDb::getPackageCategoryParents(
return getCategoryParents("package_categories", category); return getCategoryParents("package_categories", category);
} }


void WorkspaceLibraryDb::getComponentCategoryElementCount(
const tl::optional<Uuid>& category, int* categories, int* symbols,
int* components, int* devices) const {
if (categories) {
*categories = getCategoryChildCount("component_categories", category);
}
if (symbols) {
*symbols = getCategoryElementCount("symbols", "symbol_id", category);
}
if (components) {
*components =
getCategoryElementCount("components", "component_id", category);
}
if (devices) {
*devices = getCategoryElementCount("devices", "device_id", category);
}
}

void WorkspaceLibraryDb::getPackageCategoryElementCount(
const tl::optional<Uuid>& category, int* categories, int* packages) const {
if (categories) {
*categories = getCategoryChildCount("package_categories", category);
}
if (packages) {
*packages = getCategoryElementCount("packages", "package_id", category);
}
}

QSet<Uuid> WorkspaceLibraryDb::getSymbolsByCategory( QSet<Uuid> WorkspaceLibraryDb::getSymbolsByCategory(
const tl::optional<Uuid>& category) const { const tl::optional<Uuid>& category) const {
return getElementsByCategory("symbols", "symbol_id", category); return getElementsByCategory("symbols", "symbol_id", category);
Expand Down Expand Up @@ -606,6 +634,25 @@ tl::optional<Uuid> WorkspaceLibraryDb::getCategoryParent(
} }
} }


int WorkspaceLibraryDb::getCategoryChildCount(
const QString& tablename, const tl::optional<Uuid>& category) const {
QSqlQuery query = mDb->prepareQuery(
"SELECT COUNT(*) FROM " % tablename % " WHERE parent_uuid " %
(category ? "= '" % category->toStr() % "'" : QString("IS NULL")));
return mDb->count(query);
}

int WorkspaceLibraryDb::getCategoryElementCount(
const QString& tablename, const QString& idrowname,
const tl::optional<Uuid>& category) const {
QSqlQuery query = mDb->prepareQuery(
"SELECT COUNT(*) FROM " % tablename % " LEFT JOIN " % tablename % "_cat" %
" ON " % tablename % ".id=" % tablename % "_cat." % idrowname %
" WHERE category_uuid " %
(category ? "= '" % category->toStr() % "'" : QString("IS NULL")));
return mDb->count(query);
}

QSet<Uuid> WorkspaceLibraryDb::getElementsByCategory( QSet<Uuid> WorkspaceLibraryDb::getElementsByCategory(
const QString& tablename, const QString& idrowname, const QString& tablename, const QString& idrowname,
const tl::optional<Uuid>& categoryUuid) const { const tl::optional<Uuid>& categoryUuid) const {
Expand Down

0 comments on commit b41f738

Please sign in to comment.