Skip to content
Permalink
Browse files

Merge pull request #426 from LibrePCB/378-hide-empty-categories

Filter all category trees to show only relevant entries
(cherry picked from commit efa95a2)
  • Loading branch information...
ubruhin committed Mar 12, 2019
1 parent e3b34aa commit b41f7387bc4f3d6d8125902abefe36c9d25dd609
@@ -160,6 +160,21 @@ QSqlQuery SQLiteDatabase::prepareQuery(const QString& query) const {
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) {
exec(query); // can throw

@@ -77,6 +77,7 @@ class SQLiteDatabase final : public QObject {

// General Methods
QSqlQuery prepareQuery(const QString& query) const;
int count(QSqlQuery& query);
int insert(QSqlQuery& query);
void exec(QSqlQuery& query);
void exec(const QString& query);
@@ -50,8 +50,8 @@ CategoryChooserDialog<ElementType>::CategoryChooserDialog(
&CategoryChooserDialog<ElementType>::accept);

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

mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder()));
mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::COMPONENTS));
mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this,
@@ -61,7 +61,8 @@ PackageChooserDialog::PackageChooserDialog(
mUi->graphicsView->setScene(mGraphicsScene.data());

mCategoryTreeModel.reset(new workspace::PackageCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder()));
mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::PACKAGES));
mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this,
@@ -60,7 +60,8 @@ SymbolChooserDialog::SymbolChooserDialog(
mUi->graphicsView->setOriginCrossVisible(false);

mCategoryTreeModel.reset(new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder()));
mWorkspace.getLibraryDb(), localeOrder(),
workspace::CategoryTreeFilter::SYMBOLS));
mUi->treeCategories->setModel(mCategoryTreeModel.data());
connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this,
@@ -247,22 +247,42 @@ void NewElementWizardPage_CopyFrom::initializePage() noexcept {
setSelectedElement(FilePath());
mIsCategoryElement = false;
switch (mContext.mElementType) {
case NewElementWizardContext::ElementType::ComponentCategory:
case NewElementWizardContext::ElementType::ComponentCategory: {
mIsCategoryElement = true;
case NewElementWizardContext::ElementType::Symbol:
case NewElementWizardContext::ElementType::Component:
setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
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: {
setCategoryTreeModel(new workspace::ComponentCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(),
mContext.getLibLocaleOrder()));
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::DEVICES));
break;
}
case NewElementWizardContext::ElementType::PackageCategory:
case NewElementWizardContext::ElementType::PackageCategory: {
mIsCategoryElement = true;
setCategoryTreeModel(new workspace::PackageCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::ALL));
break;
}
case NewElementWizardContext::ElementType::Package: {
setCategoryTreeModel(new workspace::PackageCategoryTreeModel(
mContext.getWorkspace().getLibraryDb(),
mContext.getLibLocaleOrder()));
mContext.getWorkspace().getLibraryDb(), mContext.getLibLocaleOrder(),
workspace::CategoryTreeFilter::PACKAGES));
break;
}
default: {
@@ -105,7 +105,8 @@ AddComponentDialog::AddComponentDialog(workspace::Workspace& workspace,

const QStringList& localeOrder = mProject.getSettings().getLocaleOrder();
mCategoryTreeModel = new workspace::ComponentCategoryTreeModel(
mWorkspace.getLibraryDb(), localeOrder);
mWorkspace.getLibraryDb(), localeOrder,
workspace::CategoryTreeFilter::COMPONENTS);
mUi->treeCategories->setModel(mCategoryTreeModel);
connect(mUi->treeCategories->selectionModel(),
&QItemSelectionModel::currentChanged, this,
@@ -39,11 +39,13 @@ namespace workspace {
template <typename ElementType>
CategoryTreeItem<ElementType>::CategoryTreeItem(
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),
mUuid(uuid),
mDepth(parent ? parent->getDepth() + 1 : 0),
mExceptionMessage() {
mExceptionMessage(),
mIsVisible(false) {
try {
if (mUuid) {
FilePath fp = getLatestCategory(library);
@@ -56,9 +58,11 @@ CategoryTreeItem<ElementType>::CategoryTreeItem(
if (mUuid || (!mParent)) {
QSet<Uuid> childs = getCategoryChilds(library);
foreach (const Uuid& childUuid, childs) {
ChildType child(
new CategoryTreeItem(library, localeOrder, this, childUuid));
mChilds.append(child);
ChildType child(new CategoryTreeItem(library, localeOrder, this,
childUuid, filter));
if (child->isVisible()) {
mChilds.append(child);
}
}

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

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

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

@@ -164,6 +175,41 @@ QSet<Uuid> CategoryTreeItem<library::PackageCategory>::getCategoryChilds(
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
******************************************************************************/
@@ -41,6 +41,21 @@ namespace workspace {

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

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

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

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

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

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

#endif // LIBREPCB_LIBRARY_CATEGORYTREEITEM_H
@@ -40,10 +40,11 @@ namespace workspace {

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

template <typename ElementType>
@@ -49,7 +49,8 @@ class CategoryTreeModel final : public QAbstractItemModel {
CategoryTreeModel() = delete;
CategoryTreeModel(const CategoryTreeModel& other) = delete;
explicit CategoryTreeModel(const WorkspaceLibraryDb& library,
const QStringList& localeOrder) noexcept;
const QStringList& localeOrder,
CategoryTreeFilter::Flags filter) noexcept;
~CategoryTreeModel() noexcept;

// Getters
@@ -400,6 +400,34 @@ QList<Uuid> WorkspaceLibraryDb::getPackageCategoryParents(
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(
const tl::optional<Uuid>& category) const {
return getElementsByCategory("symbols", "symbol_id", category);
@@ -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(
const QString& tablename, const QString& idrowname,
const tl::optional<Uuid>& categoryUuid) const {

0 comments on commit b41f738

Please sign in to comment.
You can’t perform that action at this time.