Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to filter issue table by issue type #9023

Merged
merged 1 commit into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/gui/activitywidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ ActivityWidget::ActivityWidget(QWidget *parent)
header->setSectionResizeMode(QHeaderView::Interactive);
header->setSortIndicator(static_cast<int>(ActivityListModel::ActivityRole::PointInTime), Qt::DescendingOrder);

connect(_ui->_filterButton, &QAbstractButton::clicked, this, [this] {
TheOneRing marked this conversation as resolved.
Show resolved Hide resolved
ProtocolWidget::showFilterMenu(_ui->_filterButton, _sortModel);
});

_ui->_notifyLabel->hide();
_ui->_notifyScroll->hide();

Expand Down Expand Up @@ -102,9 +106,8 @@ ActivityWidget::ActivityWidget(QWidget *parent)
connect(_ui->_activityList, &QListView::customContextMenuRequested, this, &ActivityWidget::slotItemContextMenu);
header->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header, &QListView::customContextMenuRequested, header, [header, this] {
auto menu = Models::displayFilterDialog(AccountManager::instance()->accountNames(), _sortModel, static_cast<int>(ActivityListModel::ActivityRole::Account), Qt::DisplayRole, this);
menu->addSeparator();
menu->addAction(tr("Reset column sizes"), this, [header] { header->resizeColumns(true); });
auto menu = ProtocolWidget::showFilterMenu(header, _sortModel);
header->addResetActionToMenu(menu);
});

connect(&_removeTimer, &QTimer::timeout, this, &ActivityWidget::slotCheckToCleanWidgets);
Expand Down
52 changes: 38 additions & 14 deletions src/gui/activitywidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,44 @@
</widget>
</item>
<item>
<widget class="QLabel" name="_headerLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="_headerLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="_filterButton">
<property name="text">
<string>Filter</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="_activityList">
Expand Down
158 changes: 154 additions & 4 deletions src/gui/issueswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include "folderman.h"
#include "syncfileitem.h"
#include "folder.h"
#include "models/expandingheaderview.h"
#include "models/models.h"
#include "openfilemanager.h"
#include "protocolwidget.h"
Expand All @@ -50,6 +49,60 @@ bool persistsUntilLocalDiscovery(const OCC::ProtocolItem &data)
}
namespace OCC {

class SyncFileItemStatusSetSortFilterProxyModel : public QSortFilterProxyModel
{
public:
using StatusSet = std::array<bool, SyncFileItem::StatusCount>;

explicit SyncFileItemStatusSetSortFilterProxyModel(QObject *parent = nullptr)
: QSortFilterProxyModel(parent)
{
resetFilter();
}

~SyncFileItemStatusSetSortFilterProxyModel() override
{
}

StatusSet filter() const
{
return _filter;
}

void setFilter(const StatusSet &newFilter)
{
if (_filter != newFilter) {
_filter = newFilter;
invalidateFilter();
}
}

void resetFilter()
{
StatusSet defaultFilter;
defaultFilter.fill(true);
defaultFilter[SyncFileItem::NoStatus] = false;
defaultFilter[SyncFileItem::Success] = false;
setFilter(defaultFilter);
}

bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
{
QModelIndex idx = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent);

bool ok = false;
int sourceData = sourceModel()->data(idx, filterRole()).toInt(&ok);
if (!ok) {
return false;
}

return _filter[static_cast<SyncFileItem::Status>(sourceData)];
}

private:
StatusSet _filter;
};

/**
* If more issues are reported than this they will not show up
* to avoid performance issues around sorting this many issues.
Expand Down Expand Up @@ -90,7 +143,12 @@ IssuesWidget::IssuesWidget(QWidget *parent)
_model = new ProtocolItemModel(20000, true, this);
_sortModel = new QSortFilterProxyModel(this);
_sortModel->setSourceModel(_model);
_ui->_tableView->setModel(_sortModel);
_statusSortModel = new SyncFileItemStatusSetSortFilterProxyModel(this);
_statusSortModel->setSourceModel(_sortModel);
_statusSortModel->setSortRole(Qt::DisplayRole); // Sorting should be done based on the text in the column cells, but...
_statusSortModel->setFilterRole(Models::UnderlyingDataRole); // ... filtering should be done on the underlying enum value.
_statusSortModel->setFilterKeyColumn(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Status));
_ui->_tableView->setModel(_statusSortModel);

auto header = new ExpandingHeaderView(QStringLiteral("ActivityErrorListHeaderV2"), _ui->_tableView);
_ui->_tableView->setHorizontalHeader(header);
Expand All @@ -100,8 +158,13 @@ IssuesWidget::IssuesWidget(QWidget *parent)

connect(_ui->_tableView, &QTreeView::customContextMenuRequested, this, &IssuesWidget::slotItemContextMenu);
_ui->_tableView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header, &QHeaderView::customContextMenuRequested, header, [header, this] {
ProtocolWidget::showHeaderContextMenu(header, _sortModel);
connect(header, &QHeaderView::customContextMenuRequested, [this, header]() {
auto menu = showFilterMenu(header);
menu->addAction(tr("Reset column sizes"), header, [header] { header->resizeColumns(true); });
});

connect(_ui->_filterButton, &QAbstractButton::clicked, this, [this] {
showFilterMenu(_ui->_filterButton);
});

_ui->_tooManyIssuesWarning->hide();
Expand Down Expand Up @@ -131,6 +194,33 @@ IssuesWidget::~IssuesWidget()
delete _ui;
}

QMenu *IssuesWidget::showFilterMenu(QWidget *parent)
{
auto menu = new QMenu(parent);
menu->setAttribute(Qt::WA_DeleteOnClose);

auto accountFilterReset = Models::addFilterMenuItems(menu, AccountManager::instance()->accountNames(), _sortModel, static_cast<int>(ProtocolItemModel::ProtocolItemRole::Account), tr("Account"), Qt::DisplayRole);
menu->addSeparator();
auto statusFilterReset = addStatusFilter(menu);
menu->addSeparator();
addResetFiltersAction(menu, { accountFilterReset, statusFilterReset });

QTimer::singleShot(0, menu, [menu] {
menu->popup(QCursor::pos());
});

return menu;
}

void IssuesWidget::addResetFiltersAction(QMenu *menu, const QList<std::function<void()>> &resetFunctions)
{
menu->addAction(QCoreApplication::translate("OCC::Models", "Reset Filters"), [resetFunctions]() {
for (const auto &reset : resetFunctions) {
reset();
}
});
}

void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
{
if (progress.status() == ProgressInfo::Reconcile) {
Expand Down Expand Up @@ -195,9 +285,69 @@ void IssuesWidget::slotItemContextMenu()
{
auto rows = _ui->_tableView->selectionModel()->selectedRows();
for (int i = 0; i < rows.size(); ++i) {
rows[i] = _statusSortModel->mapToSource(rows[i]);
rows[i] = _sortModel->mapToSource(rows[i]);
}
ProtocolWidget::showContextMenu(this, _model, rows);
TheOneRing marked this conversation as resolved.
Show resolved Hide resolved
}

std::function<void(void)> IssuesWidget::addStatusFilter(QMenu *menu)
{
menu->addAction(QCoreApplication::translate("OCC::Models", "Status Filter:"))->setEnabled(false);

// Use a QActionGroup to contain all status filter items, so we can find them back easily to reset.
auto statusFilterGroup = new QActionGroup(menu);
statusFilterGroup->setExclusive(false);

const auto initialFilter = _statusSortModel->filter();

{ // Add all errors under 1 action:
const std::array<SyncFileItem::Status, 5> ErrorStatusItems = {
SyncFileItem::Status::FatalError,
SyncFileItem::Status::NormalError,
SyncFileItem::Status::SoftError,
SyncFileItem::Status::DetailError
};

auto action = menu->addAction(SyncFileItem::statusEnumDisplayName(SyncFileItem::NormalError), [this, ErrorStatusItems](bool checked) {
auto currentFilter = _statusSortModel->filter();
for (const auto &item : ErrorStatusItems) {
currentFilter[item] = checked;
}
_statusSortModel->setFilter(currentFilter);
});
action->setCheckable(true);
action->setChecked(initialFilter[ErrorStatusItems[0]]);
statusFilterGroup->addAction(action);
}

// Add the other non-error items:
const std::array<SyncFileItem::Status, 5> OtherStatusItems = {
SyncFileItem::Status::Conflict,
SyncFileItem::Status::FileIgnored,
SyncFileItem::Status::Restoration,
SyncFileItem::Status::BlacklistedError,
SyncFileItem::Status::Excluded
};
for (const auto &item : OtherStatusItems) {
auto action = menu->addAction(SyncFileItem::statusEnumDisplayName(item), [this, item](bool checked) {
auto currentFilter = _statusSortModel->filter();
currentFilter[item] = checked;
_statusSortModel->setFilter(currentFilter);
});
action->setCheckable(true);
action->setChecked(initialFilter[item]);
statusFilterGroup->addAction(action);
}

menu->addSeparator();

// Add action to reset all filters at once:
return [statusFilterGroup, this]() {
for (QAction *action : statusFilterGroup->actions()) {
action->setChecked(true);
}
_statusSortModel->resetFilter();
};
}
}
9 changes: 8 additions & 1 deletion src/gui/issueswidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
#include <QLocale>
#include <QTimer>

#include "models/expandingheaderview.h"
#include "models/protocolitemmodel.h"
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "progressdispatcher.h"

#include "ui_issueswidget.h"

class QSortFilterProxyModel;

namespace OCC {
class SyncResult;
class SyncFileItemStatusSetSortFilterProxyModel;

namespace Ui {
class ProtocolWidget;
Expand All @@ -55,11 +57,16 @@ public slots:
void issueCountUpdated(int);

private slots:
QMenu *showFilterMenu(QWidget *parent);
void slotItemContextMenu();

private:
static void addResetFiltersAction(QMenu *menu, const QList<std::function<void()>> &resetFunctions);
std::function<void()> addStatusFilter(QMenu *menu);

ProtocolItemModel *_model;
QSortFilterProxyModel *_sortModel;
SyncFileItemStatusSetSortFilterProxyModel *_statusSortModel;

Ui::IssuesWidget *_ui;
};
Expand Down
40 changes: 32 additions & 8 deletions src/gui/issueswidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,38 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>List of issues</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>List of issues</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="_filterButton">
<property name="text">
<string>Filter</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="_tableView">
Expand Down
8 changes: 7 additions & 1 deletion src/gui/models/expandingheaderview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

#include "configfile.h"

#include <QScopedValueRollback>
#include <QApplication>
#include <QDebug>
#include <QMenu>
#include <QScopedValueRollback>

using namespace OCC;

Expand Down Expand Up @@ -71,3 +72,8 @@ void ExpandingHeaderView::resizeColumns(bool reset)
}
resizeSection(_expandingColumn, availableWidth);
}

void ExpandingHeaderView::addResetActionToMenu(QMenu *menu)
{
menu->addAction(tr("Reset column sizes"), this, [this] { resizeColumns(true); });
}
1 change: 1 addition & 0 deletions src/gui/models/expandingheaderview.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ExpandingHeaderView : public QHeaderView
void setExpandingColumn(int newExpandingColumn);

void resizeColumns(bool reset = false);
void addResetActionToMenu(QMenu *menu);

protected:
void resizeEvent(QResizeEvent *event) override;
Expand Down
Loading