From ce8341ca1fd21d946e8c056430025f08d586e380 Mon Sep 17 00:00:00 2001 From: ckamm Date: Tue, 4 Jul 2017 14:08:41 +0200 Subject: [PATCH] Add a more functional error view #5516 (#5861) * Add a more functional error view #5516 * Allow filtering of ignores and warnings to see only important bits. * Navigate from the folder view to the error view by clicking on the error list with the red background. * Move the error list into its own ui file to allow easier extension. * Fix issue around tab id handling in ActivitySettings. * Rename "Action" column to "Issue". * Change mouse cursor to hand over button and new error list area Several OSX fixes provided by guruz. --- src/gui/CMakeLists.txt | 2 + src/gui/accountsettings.cpp | 49 ++++++ src/gui/accountsettings.h | 1 + src/gui/activitywidget.cpp | 48 ++--- src/gui/activitywidget.h | 5 + src/gui/folderstatusdelegate.cpp | 10 ++ src/gui/folderstatusdelegate.h | 1 + src/gui/issueswidget.cpp | 294 +++++++++++++++++++++++++++++++ src/gui/issueswidget.h | 82 +++++++++ src/gui/issueswidget.ui | 161 +++++++++++++++++ src/gui/protocolwidget.cpp | 107 ++--------- src/gui/protocolwidget.h | 17 +- src/gui/settingsdialog.cpp | 9 + src/gui/settingsdialog.h | 1 + src/gui/settingsdialogmac.cpp | 9 + src/gui/settingsdialogmac.h | 1 + 16 files changed, 665 insertions(+), 132 deletions(-) create mode 100644 src/gui/issueswidget.cpp create mode 100644 src/gui/issueswidget.h create mode 100644 src/gui/issueswidget.ui diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 643bb932cda..e40c0303467 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -21,6 +21,7 @@ set(client_UI ignorelisteditor.ui networksettings.ui protocolwidget.ui + issueswidget.ui activitywidget.ui synclogdialog.ui settingsdialog.ui @@ -64,6 +65,7 @@ set(client_SRCS owncloudgui.cpp owncloudsetupwizard.cpp protocolwidget.cpp + issueswidget.cpp activitydata.cpp activitylistmodel.cpp activitywidget.cpp diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 2f866502ad6..6da34d59048 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -69,6 +69,42 @@ static const char progressBarStyleC[] = "background-color: %1; width: 1px;" "}"; +/** + * Adjusts the mouse cursor based on the region it is on over the folder tree view. + * + * Used to show that one can click the red error list box by changing the cursor + * to the pointing hand. + */ +class MouseCursorChanger : public QObject +{ + Q_OBJECT +public: + MouseCursorChanger(QObject *parent) + : QObject(parent) + { + } + + QTreeView *folderList; + FolderStatusModel *model; + +protected: + bool eventFilter(QObject *watched, QEvent *event) override + { + if (event->type() == QEvent::HoverMove) { + Qt::CursorShape shape = Qt::ArrowCursor; + auto pos = folderList->mapFromGlobal(QCursor::pos()); + auto index = folderList->indexAt(pos); + if (model->classify(index) == FolderStatusModel::RootFolder + && (FolderStatusDelegate::errorsListRect(folderList->visualRect(index)).contains(pos) + || FolderStatusDelegate::optionsButtonRect(folderList->visualRect(index),folderList->layoutDirection()).contains(pos))) { + shape = Qt::PointingHandCursor; + } + folderList->setCursor(shape); + } + return QObject::eventFilter(watched, event); + } +}; + AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) : QWidget(parent) , ui(new Ui::AccountSettings) @@ -94,6 +130,13 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) #endif new ToolTipUpdater(ui->_folderList); + auto mouseCursorChanger = new MouseCursorChanger(this); + mouseCursorChanger->folderList = ui->_folderList; + mouseCursorChanger->model = _model; + ui->_folderList->setMouseTracking(true); + ui->_folderList->setAttribute(Qt::WA_Hover, true); + ui->_folderList->installEventFilter(mouseCursorChanger); + createAccountToolbox(); connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState *)), SLOT(slotAccountAdded(AccountState *))); @@ -301,6 +344,10 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx) slotCustomContextMenuRequested(pos); return; } + if (FolderStatusDelegate::errorsListRect(tv->visualRect(indx)).contains(pos)) { + emit showIssuesList(_model->data(indx, FolderStatusDelegate::FolderAliasRole).toString()); + return; + } // Expand root items on single click if (_accountState && _accountState->state() == AccountState::Connected) { @@ -808,3 +855,5 @@ bool AccountSettings::event(QEvent *e) } } // namespace OCC + +#include "accountsettings.moc" diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 9c4a1fc5696..3e45d7e4a7f 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -60,6 +60,7 @@ class AccountSettings : public QWidget signals: void folderChanged(); void openFolderAlias(const QString &); + void showIssuesList(const QString &folderAlias); public slots: void slotOpenOC(); diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp index 2d40ce8ee49..c4e82884b11 100644 --- a/src/gui/activitywidget.cpp +++ b/src/gui/activitywidget.cpp @@ -33,6 +33,7 @@ #include "accountmanager.h" #include "activityitemdelegate.h" #include "protocolwidget.h" +#include "issueswidget.h" #include "QProgressIndicator.h" #include "notificationwidget.h" #include "notificationconfirmjob.h" @@ -518,33 +519,23 @@ ActivitySettings::ActivitySettings(QWidget *parent) _tab = new QTabWidget(this); hbox->addWidget(_tab); _activityWidget = new ActivityWidget(this); - _activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); + _activityTabId = _tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard())); connect(_activityWidget, SIGNAL(hideActivityTab(bool)), this, SLOT(setActivityTabHidden(bool))); connect(_activityWidget, SIGNAL(guiLog(QString, QString)), this, SIGNAL(guiLog(QString, QString))); connect(_activityWidget, SIGNAL(newNotification()), SLOT(slotShowActivityTab())); _protocolWidget = new ProtocolWidget(this); - _tab->insertTab(1, _protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol")); + _protocolTabId = _tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol")); connect(_protocolWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard())); - connect(_protocolWidget, SIGNAL(issueItemCountUpdated(int)), - this, SLOT(slotShowIssueItemCount(int))); - // Add the not-synced list into the tab - QWidget *w = new QWidget; - QVBoxLayout *vbox2 = new QVBoxLayout(w); - vbox2->addWidget(new QLabel(tr("List of ignored or erroneous files"), this)); - vbox2->addWidget(_protocolWidget->issueWidget()); - QDialogButtonBox *dlgButtonBox = new QDialogButtonBox(this); - vbox2->addWidget(dlgButtonBox); - QPushButton *_copyBtn = dlgButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); - _copyBtn->setToolTip(tr("Copy the activity list to the clipboard.")); - _copyBtn->setEnabled(true); - connect(_copyBtn, SIGNAL(clicked()), this, SLOT(slotCopyToClipboard())); - - w->setLayout(vbox2); - _syncIssueTabId = _tab->insertTab(2, w, Theme::instance()->syncStateIcon(SyncResult::Problem), QString()); + _issuesWidget = new IssuesWidget(this); + _syncIssueTabId = _tab->addTab(_issuesWidget, Theme::instance()->syncStateIcon(SyncResult::Problem), QString()); slotShowIssueItemCount(0); // to display the label. + connect(_issuesWidget, SIGNAL(issueCountUpdated(int)), + this, SLOT(slotShowIssueItemCount(int))); + connect(_issuesWidget, SIGNAL(copyToClipboard()), + this, SLOT(slotCopyToClipboard())); // Add a progress indicator to spin if the acitivity list is updated. _progressIndicator = new QProgressIndicator(this); @@ -571,10 +562,14 @@ void ActivitySettings::setActivityTabHidden(bool hidden) if (hidden && _activityTabId > -1) { _tab->removeTab(_activityTabId); _activityTabId = -1; + _protocolTabId -= 1; + _syncIssueTabId -= 1; } if (!hidden && _activityTabId == -1) { _activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); + _protocolTabId += 1; + _syncIssueTabId += 1; } } @@ -595,6 +590,15 @@ void ActivitySettings::slotShowActivityTab() } } +void ActivitySettings::slotShowIssuesTab(const QString &folderAlias) +{ + if (_syncIssueTabId == -1) + return; + _tab->setCurrentIndex(_syncIssueTabId); + + _issuesWidget->showFolderErrors(folderAlias); +} + void ActivitySettings::slotCopyToClipboard() { QString text; @@ -603,18 +607,18 @@ void ActivitySettings::slotCopyToClipboard() int idx = _tab->currentIndex(); QString message; - if (idx == 0) { + if (idx == _activityTabId) { // the activity widget _activityWidget->storeActivityList(ts); message = tr("The server activity list has been copied to the clipboard."); - } else if (idx == 1) { + } else if (idx == _protocolTabId) { // the protocol widget _protocolWidget->storeSyncActivity(ts); message = tr("The sync activity list has been copied to the clipboard."); - } else if (idx == 2) { + } else if (idx == _syncIssueTabId) { // issues Widget message = tr("The list of unsynced items has been copied to the clipboard."); - _protocolWidget->storeSyncIssues(ts); + _issuesWidget->storeSyncIssues(ts); } QApplication::clipboard()->setText(text); diff --git a/src/gui/activitywidget.h b/src/gui/activitywidget.h index bae9b6ff951..e2fa314e059 100644 --- a/src/gui/activitywidget.h +++ b/src/gui/activitywidget.h @@ -35,6 +35,7 @@ namespace OCC { class Account; class AccountStatusPtr; class ProtocolWidget; +class IssuesWidget; class JsonApiJob; class NotificationWidget; class ActivityListModel; @@ -138,6 +139,8 @@ public slots: void setNotificationRefreshInterval(quint64 interval); + void slotShowIssuesTab(const QString &folderAlias); + private slots: void slotCopyToClipboard(); void setActivityTabHidden(bool hidden); @@ -153,10 +156,12 @@ private slots: QTabWidget *_tab; int _activityTabId; + int _protocolTabId; int _syncIssueTabId; ActivityWidget *_activityWidget; ProtocolWidget *_protocolWidget; + IssuesWidget *_issuesWidget; QProgressIndicator *_progressIndicator; QTimer _notificationCheckTimer; QHash _timeSinceLastCheck; diff --git a/src/gui/folderstatusdelegate.cpp b/src/gui/folderstatusdelegate.cpp index ddb50386755..1d93893747b 100644 --- a/src/gui/folderstatusdelegate.cpp +++ b/src/gui/folderstatusdelegate.cpp @@ -373,5 +373,15 @@ QRect FolderStatusDelegate::optionsButtonRect(QRect within, Qt::LayoutDirection return QStyle::visualRect(direction, within, r); } +QRect FolderStatusDelegate::errorsListRect(QRect within) +{ + QFont font = QFont(); + QFont aliasFont = makeAliasFont(font); + QFontMetrics fm(font); + QFontMetrics aliasFm(aliasFont); + within.setTop(within.top() + FolderStatusDelegate::rootFolderHeightWithoutErrors(fm, aliasFm)); + return within; +} + } // namespace OCC diff --git a/src/gui/folderstatusdelegate.h b/src/gui/folderstatusdelegate.h index b42ae134d01..a3fd5b71d27 100644 --- a/src/gui/folderstatusdelegate.h +++ b/src/gui/folderstatusdelegate.h @@ -56,6 +56,7 @@ class FolderStatusDelegate : public QStyledItemDelegate * return the position of the option button within the item */ static QRect optionsButtonRect(QRect within, Qt::LayoutDirection direction); + static QRect errorsListRect(QRect within); static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm); private: diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp new file mode 100644 index 00000000000..eff8a3b454d --- /dev/null +++ b/src/gui/issueswidget.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) by Klaas Freitag + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +#include "issueswidget.h" +#include "configfile.h" +#include "syncresult.h" +#include "logger.h" +#include "utility.h" +#include "theme.h" +#include "folderman.h" +#include "syncfileitem.h" +#include "folder.h" +#include "openfilemanager.h" +#include "activityitemdelegate.h" +#include "protocolwidget.h" +#include "accountstate.h" +#include "account.h" +#include "accountmanager.h" + +#include "ui_issueswidget.h" + +#include + +namespace OCC { + +IssuesWidget::IssuesWidget(QWidget *parent) + : QWidget(parent) + , _ui(new Ui::IssuesWidget) +{ + _ui->setupUi(this); + + connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), + this, SLOT(slotProgressInfo(QString, ProgressInfo))); + connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), + this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); + + connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int))); + connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard())); + + connect(_ui->showIgnores, SIGNAL(toggled(bool)), SLOT(slotRefreshIssues())); + connect(_ui->showWarnings, SIGNAL(toggled(bool)), SLOT(slotRefreshIssues())); + connect(_ui->filterAccount, SIGNAL(currentIndexChanged(int)), SLOT(slotRefreshIssues())); + connect(_ui->filterAccount, SIGNAL(currentIndexChanged(int)), SLOT(slotUpdateFolderFilters())); + connect(_ui->filterFolder, SIGNAL(currentIndexChanged(int)), SLOT(slotRefreshIssues())); + for (auto account : AccountManager::instance()->accounts()) { + slotAccountAdded(account.data()); + } + connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState *)), + SLOT(slotAccountAdded(AccountState *))); + connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState *)), + SLOT(slotAccountRemoved(AccountState *))); + + + // Adjust copyToClipboard() when making changes here! + QStringList header; + header << tr("Time"); + header << tr("File"); + header << tr("Folder"); + header << tr("Issue"); + + int timestampColumnExtra = 0; +#ifdef Q_OS_WIN + timestampColumnExtra = 20; // font metrics are broken on Windows, see #4721 +#endif + + _ui->_treeWidget->setHeaderLabels(header); + int timestampColumnWidth = + ActivityItemDelegate::rowHeight() // icon + + _ui->_treeWidget->fontMetrics().width(ProtocolWidget::timeString(QDateTime::currentDateTime())) + + timestampColumnExtra; + _ui->_treeWidget->setColumnWidth(0, timestampColumnWidth); + _ui->_treeWidget->setColumnWidth(1, 180); + _ui->_treeWidget->setColumnCount(4); + _ui->_treeWidget->setRootIsDecorated(false); + _ui->_treeWidget->setTextElideMode(Qt::ElideMiddle); + _ui->_treeWidget->header()->setObjectName("ActivityErrorListHeader"); +#if defined(Q_OS_MAC) + _ui->_treeWidget->setMinimumWidth(400); +#endif +} + +IssuesWidget::~IssuesWidget() +{ + delete _ui; +} + +void IssuesWidget::showEvent(QShowEvent *ev) +{ + ConfigFile cfg; + cfg.restoreGeometryHeader(_ui->_treeWidget->header()); + QWidget::showEvent(ev); +} + +void IssuesWidget::hideEvent(QHideEvent *ev) +{ + ConfigFile cfg; + cfg.saveGeometryHeader(_ui->_treeWidget->header()); + QWidget::hideEvent(ev); +} + +void IssuesWidget::cleanItems(const QString &folder) +{ + // The issue list is a state, clear it and let the next sync fill it + // with ignored files and propagation errors. + int itemCnt = _ui->_treeWidget->topLevelItemCount(); + for (int cnt = itemCnt - 1; cnt >= 0; cnt--) { + QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt); + QString itemFolder = item->data(2, Qt::UserRole).toString(); + if (itemFolder == folder) { + delete item; + } + } + // update the tabtext + emit(issueCountUpdated(_ui->_treeWidget->topLevelItemCount())); +} + +void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int) +{ + QString folderName = item->data(2, Qt::UserRole).toString(); + QString fileName = item->text(1); + + Folder *folder = FolderMan::instance()->folder(folderName); + if (folder) { + // folder->path() always comes back with trailing path + QString fullPath = folder->path() + fileName; + if (QFile(fullPath).exists()) { + showInFileManager(fullPath); + } + } +} + +void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress) +{ + if (!progress.isUpdatingEstimates()) { + // The sync is restarting, clean the old items + cleanItems(folder); + } else if (progress.completedFiles() >= progress.totalFiles()) { + //Sync completed + } +} + +void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item) +{ + if (!item->hasErrorStatus()) + return; + QTreeWidgetItem *line = ProtocolWidget::createCompletedTreewidgetItem(folder, *item); + if (!line) + return; + + _ui->_treeWidget->insertTopLevelItem(0, line); + line->setHidden(!shouldBeVisible(line, currentAccountFilter(), currentFolderFilter())); + emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); +} + +void IssuesWidget::slotRefreshIssues() +{ + auto tree = _ui->_treeWidget; + auto filterFolderAlias = currentFolderFilter(); + auto filterAccount = currentAccountFilter(); + + for (int i = 0; i < tree->topLevelItemCount(); ++i) { + auto item = tree->topLevelItem(i); + item->setHidden(!shouldBeVisible(item, filterAccount, filterFolderAlias)); + } +} + +void IssuesWidget::slotAccountAdded(AccountState *account) +{ + _ui->filterAccount->addItem(account->account()->displayName(), QVariant::fromValue(account)); +} + +void IssuesWidget::slotAccountRemoved(AccountState *account) +{ + for (int i = _ui->filterAccount->count() - 1; i >= 0; --i) { + if (account == _ui->filterAccount->itemData(i).value()) + _ui->filterAccount->removeItem(i); + } +} + +AccountState *IssuesWidget::currentAccountFilter() const +{ + return _ui->filterAccount->currentData().value(); +} + +QString IssuesWidget::currentFolderFilter() const +{ + return _ui->filterFolder->currentData().toString(); +} + +bool IssuesWidget::shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, + const QString &filterFolderAlias) const +{ + bool visible = true; + auto status = item->data(0, Qt::UserRole); + visible &= (_ui->showIgnores->isChecked() || status != SyncFileItem::FileIgnored); + visible &= (_ui->showWarnings->isChecked() + || (status != SyncFileItem::SoftError + && status != SyncFileItem::Conflict + && status != SyncFileItem::Restoration)); + + auto folderalias = item->data(2, Qt::UserRole).toString(); + if (filterAccount) { + auto folder = FolderMan::instance()->folder(folderalias); + visible &= folder && folder->accountState() == filterAccount; + } + visible &= (filterFolderAlias.isEmpty() || filterFolderAlias == folderalias); + + return visible; +} + +void IssuesWidget::slotUpdateFolderFilters() +{ + auto account = _ui->filterAccount->currentData().value(); + + if (!account) { + _ui->filterFolder->setCurrentIndex(0); + } + _ui->filterFolder->setEnabled(account != 0); + + for (int i = _ui->filterFolder->count() - 1; i >= 1; --i) { + _ui->filterFolder->removeItem(i); + } + for (auto folder : FolderMan::instance()->map().values()) { + if (folder->accountState() != account) + continue; + _ui->filterFolder->addItem(folder->shortGuiLocalPath(), folder->alias()); + } +} + +void IssuesWidget::storeSyncIssues(QTextStream &ts) +{ + int topLevelItems = _ui->_treeWidget->topLevelItemCount(); + + for (int i = 0; i < topLevelItems; i++) { + QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i); + if (child->isHidden()) + continue; + ts << right + // time stamp + << qSetFieldWidth(20) + << child->data(0, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // file name + << qSetFieldWidth(64) + << child->data(1, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // folder + << qSetFieldWidth(30) + << child->data(2, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // action + << qSetFieldWidth(15) + << child->data(3, Qt::DisplayRole).toString() + << qSetFieldWidth(0) + << endl; + } +} + +void IssuesWidget::showFolderErrors(const QString &folderAlias) +{ + auto folder = FolderMan::instance()->folder(folderAlias); + if (!folder) + return; + + _ui->filterAccount->setCurrentIndex( + qMax(0, _ui->filterAccount->findData(QVariant::fromValue(folder->accountState())))); + _ui->filterFolder->setCurrentIndex( + qMax(0, _ui->filterFolder->findData(folderAlias))); + _ui->showIgnores->setChecked(false); + _ui->showWarnings->setChecked(false); +} +} diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h new file mode 100644 index 00000000000..9d31e16e166 --- /dev/null +++ b/src/gui/issueswidget.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) by Klaas Freitag + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef ISSUESWIDGET_H +#define ISSUESWIDGET_H + +#include +#include +#include + +#include "progressdispatcher.h" +#include "owncloudgui.h" + +#include "ui_issueswidget.h" + +class QPushButton; + +namespace OCC { +class SyncResult; + +namespace Ui { + class ProtocolWidget; +} +class Application; + +/** + * @brief The ProtocolWidget class + * @ingroup gui + */ +class IssuesWidget : public QWidget +{ + Q_OBJECT +public: + explicit IssuesWidget(QWidget *parent = 0); + ~IssuesWidget(); + QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); } + + void storeSyncIssues(QTextStream &ts); + void showFolderErrors(const QString &folderAlias); + +public slots: + void slotProgressInfo(const QString &folder, const ProgressInfo &progress); + void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); + void slotOpenFile(QTreeWidgetItem *item, int); + +protected: + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); + +signals: + void copyToClipboard(); + void issueCountUpdated(int); + +private slots: + void slotRefreshIssues(); + void slotUpdateFolderFilters(); + void slotAccountAdded(AccountState *account); + void slotAccountRemoved(AccountState *account); + +private: + AccountState *currentAccountFilter() const; + QString currentFolderFilter() const; + bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, + const QString &filterFolderAlias) const; + void cleanItems(const QString &folder); + + Ui::IssuesWidget *_ui; +}; +} + +#endif diff --git a/src/gui/issueswidget.ui b/src/gui/issueswidget.ui new file mode 100644 index 00000000000..eb15d3d8c4e --- /dev/null +++ b/src/gui/issueswidget.ui @@ -0,0 +1,161 @@ + + + OCC::IssuesWidget + + + + 0 + 0 + 580 + 578 + + + + Form + + + + + + List of issues + + + Qt::PlainText + + + + + + + + + + + Account + + + + + + + + <no filter> + + + + + + + + Folder + + + + + + + false + + + + <no filter> + + + + + + + + + + + + Show warnings + + + true + + + + + + + Show ignored files + + + true + + + + + + + + + + + true + + + false + + + true + + + 4 + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy the issues list to the clipboard. + + + Copy + + + + + + + + + + diff --git a/src/gui/protocolwidget.cpp b/src/gui/protocolwidget.cpp index 9803e796bd3..2f177a1c64b 100644 --- a/src/gui/protocolwidget.cpp +++ b/src/gui/protocolwidget.cpp @@ -37,13 +37,10 @@ namespace OCC { ProtocolWidget::ProtocolWidget(QWidget *parent) : QWidget(parent) - , IgnoredIndicatorRole(Qt::UserRole + 1) , _ui(new Ui::ProtocolWidget) { _ui->setupUi(this); - connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), - this, SLOT(slotProgressInfo(QString, ProgressInfo))); connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); @@ -81,25 +78,6 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) copyBtn->setToolTip(tr("Copy the activity list to the clipboard.")); copyBtn->setEnabled(true); connect(copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard())); - - // this view is used to display all errors such as real errors, soft errors and ignored files - // it is instantiated here, but made accessible via the method issueWidget() so that it can - // be embedded into another gui element. - _issueItemView = new QTreeWidget(this); - header.removeLast(); - _issueItemView->setHeaderLabels(header); - timestampColumnWidth = - ActivityItemDelegate::rowHeight() // icon - + _issueItemView->fontMetrics().width(timeString(QDateTime::currentDateTime())) - + timestampColumnExtra; - _issueItemView->setColumnWidth(0, timestampColumnWidth); - _issueItemView->setColumnWidth(1, 180); - _issueItemView->setColumnCount(4); - _issueItemView->setRootIsDecorated(false); - _issueItemView->setTextElideMode(Qt::ElideMiddle); - _issueItemView->header()->setObjectName("ActivityErrorListHeader"); - connect(_issueItemView, SIGNAL(itemActivated(QTreeWidgetItem *, int)), - SLOT(slotOpenFile(QTreeWidgetItem *, int))); } ProtocolWidget::~ProtocolWidget() @@ -121,23 +99,8 @@ void ProtocolWidget::hideEvent(QHideEvent *ev) QWidget::hideEvent(ev); } -void ProtocolWidget::cleanItems(const QString &folder) -{ - // The issue list is a state, clear it and let the next sync fill it - // with ignored files and propagation errors. - int itemCnt = _issueItemView->topLevelItemCount(); - for (int cnt = itemCnt - 1; cnt >= 0; cnt--) { - QTreeWidgetItem *item = _issueItemView->topLevelItem(cnt); - QString itemFolder = item->data(2, Qt::UserRole).toString(); - if (itemFolder == folder) { - delete item; - } - } - // update the tabtext - emit(issueItemCountUpdated(_issueItemView->topLevelItemCount())); -} -QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) const +QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) { const QLocale loc = QLocale::system(); QString dtFormat = loc.dateTimeFormat(format); @@ -198,50 +161,32 @@ QTreeWidgetItem *ProtocolWidget::createCompletedTreewidgetItem(const QString &fo } QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); - if (item._status == SyncFileItem::FileIgnored) { - // Tell that we want to remove it on the next sync. - twitem->setData(0, IgnoredIndicatorRole, true); - } - twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight())); twitem->setIcon(0, icon); twitem->setToolTip(0, longTimeStr); twitem->setToolTip(1, item._file); twitem->setToolTip(3, message); + twitem->setData(0, Qt::UserRole, item._status); twitem->setData(2, Qt::UserRole, folder); return twitem; } -void ProtocolWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress) -{ - if (!progress.isUpdatingEstimates()) { - // The sync is restarting, clean the old items - cleanItems(folder); - } else if (progress.completedFiles() >= progress.totalFiles()) { - //Sync completed - } -} - void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item) { + if (item->hasErrorStatus()) + return; QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, *item); if (line) { - if (item->hasErrorStatus()) { - _issueItemView->insertTopLevelItem(0, line); - emit issueItemCountUpdated(_issueItemView->topLevelItemCount()); - } else { - // Limit the number of items - int itemCnt = _ui->_treeWidget->topLevelItemCount(); - while (itemCnt > 2000) { - delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1); - itemCnt--; - } - _ui->_treeWidget->insertTopLevelItem(0, line); + // Limit the number of items + int itemCnt = _ui->_treeWidget->topLevelItemCount(); + while (itemCnt > 2000) { + delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1); + itemCnt--; } + _ui->_treeWidget->insertTopLevelItem(0, line); } } - void ProtocolWidget::storeSyncActivity(QTextStream &ts) { int topLevelItems = _ui->_treeWidget->topLevelItemCount(); @@ -281,36 +226,4 @@ void ProtocolWidget::storeSyncActivity(QTextStream &ts) } } -void ProtocolWidget::storeSyncIssues(QTextStream &ts) -{ - int topLevelItems = _issueItemView->topLevelItemCount(); - - for (int i = 0; i < topLevelItems; i++) { - QTreeWidgetItem *child = _issueItemView->topLevelItem(i); - ts << right - // time stamp - << qSetFieldWidth(20) - << child->data(0, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // file name - << qSetFieldWidth(64) - << child->data(1, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // folder - << qSetFieldWidth(30) - << child->data(2, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // action - << qSetFieldWidth(15) - << child->data(3, Qt::DisplayRole).toString() - << qSetFieldWidth(0) - << endl; - } -} } diff --git a/src/gui/protocolwidget.h b/src/gui/protocolwidget.h index 927a23c4b10..7e3a154a5ce 100644 --- a/src/gui/protocolwidget.h +++ b/src/gui/protocolwidget.h @@ -46,12 +46,13 @@ class ProtocolWidget : public QWidget ~ProtocolWidget(); QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); } - QTreeWidget *issueWidget() { return _issueItemView; } void storeSyncActivity(QTextStream &ts); - void storeSyncIssues(QTextStream &ts); + + // Shared with IssueWidget + static QTreeWidgetItem *createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item); + static QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat); public slots: - void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotOpenFile(QTreeWidgetItem *item, int); @@ -61,19 +62,9 @@ public slots: signals: void copyToClipboard(); - void issueItemCountUpdated(int); private: - void setSyncResultStatus(const SyncResult &result); - void cleanItems(const QString &folder); - - QTreeWidgetItem *createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item); - - QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat) const; - - const int IgnoredIndicatorRole; Ui::ProtocolWidget *_ui; - QTreeWidget *_issueItemView; }; } #endif // PROTOCOLWIDGET_H diff --git a/src/gui/settingsdialog.cpp b/src/gui/settingsdialog.cpp index bb44a080aa8..f21ecd5f8f7 100644 --- a/src/gui/settingsdialog.cpp +++ b/src/gui/settingsdialog.cpp @@ -211,6 +211,14 @@ void SettingsDialog::showActivityPage() } } +void SettingsDialog::showIssuesList(const QString &folderAlias) +{ + if (!_activityAction) + return; + _activityAction->trigger(); + _activitySettings->slotShowIssuesTab(folderAlias); +} + void SettingsDialog::accountAdded(AccountState *s) { auto height = _toolBar->sizeHint().height(); @@ -242,6 +250,7 @@ void SettingsDialog::accountAdded(AccountState *s) connect(accountSettings, SIGNAL(folderChanged()), _gui, SLOT(slotFoldersChanged())); connect(accountSettings, SIGNAL(openFolderAlias(const QString &)), _gui, SLOT(slotFolderOpenAction(QString))); + connect(accountSettings, SIGNAL(showIssuesList(QString)), SLOT(showIssuesList(QString))); connect(s->account().data(), SIGNAL(accountChangedAvatar()), SLOT(slotAccountAvatarChanged())); slotRefreshActivity(s); diff --git a/src/gui/settingsdialog.h b/src/gui/settingsdialog.h index 9d445995994..15596a00f0b 100644 --- a/src/gui/settingsdialog.h +++ b/src/gui/settingsdialog.h @@ -56,6 +56,7 @@ class SettingsDialog : public QDialog public slots: void showFirstPage(); void showActivityPage(); + void showIssuesList(const QString &folderAlias); void slotSwitchPage(QAction *action); void slotRefreshActivity(AccountState *accountState); void slotAccountAvatarChanged(); diff --git a/src/gui/settingsdialogmac.cpp b/src/gui/settingsdialogmac.cpp index 486bc701843..db4fad95fc1 100644 --- a/src/gui/settingsdialogmac.cpp +++ b/src/gui/settingsdialogmac.cpp @@ -133,6 +133,14 @@ void SettingsDialogMac::showActivityPage() setCurrentPanelIndex(preferencePanelCount() - 1 - 2); } +void SettingsDialogMac::showIssuesList(const QString &folderAlias) +{ + // Count backwards (0-based) from the last panel (multiple accounts can be on the left) + setCurrentPanelIndex(preferencePanelCount() - 1 - 2); + _activitySettings->slotShowIssuesTab(folderAlias); +} + + void SettingsDialogMac::accountAdded(AccountState *s) { QIcon accountIcon = MacStandardIcon::icon(MacStandardIcon::UserAccounts); @@ -144,6 +152,7 @@ void SettingsDialogMac::accountAdded(AccountState *s) connect(accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged); connect(accountSettings, &AccountSettings::openFolderAlias, _gui, &ownCloudGui::slotFolderOpenAction); + connect(accountSettings, &AccountSettings::showIssuesList, this, &SettingsDialogMac::showIssuesList); connect(s->account().data(), SIGNAL(accountChangedAvatar()), this, SLOT(slotAccountAvatarChanged())); diff --git a/src/gui/settingsdialogmac.h b/src/gui/settingsdialogmac.h index f931f90095c..c69ee5f140d 100644 --- a/src/gui/settingsdialogmac.h +++ b/src/gui/settingsdialogmac.h @@ -47,6 +47,7 @@ class SettingsDialogMac : public MacPreferencesWindow public slots: void showActivityPage(); + void showIssuesList(const QString &folderAlias); void slotRefreshActivity(AccountState *accountState); private slots: