Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorDominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>2020-01-02 10:43:34 +0300
committerDominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>2020-01-02 10:43:34 +0300
commit7653c5fa605718618cd769526b40e5a6164dd969 (patch)
tree37071fdf1a6867ca71f8736793193aa705e74c38 /src/gui
parent44bfc79caac1bbd8e1187fc84435219ea2af2a9b (diff)
parentbd61cd3142fcc7bad365c4d96616bafb1e88feda (diff)
Fix merge conflict
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt19
-rw-r--r--src/gui/accountsettings.cpp218
-rw-r--r--src/gui/accountsettings.h5
-rw-r--r--src/gui/activityitemdelegate.cpp95
-rw-r--r--src/gui/activityitemdelegate.h24
-rw-r--r--src/gui/activitylistmodel.cpp81
-rw-r--r--src/gui/activitylistmodel.h17
-rw-r--r--src/gui/activitywidget.cpp24
-rw-r--r--src/gui/activitywidget.h5
-rw-r--r--src/gui/application.cpp7
-rw-r--r--src/gui/application.h2
-rw-r--r--src/gui/creds/flow2auth.cpp135
-rw-r--r--src/gui/creds/flow2auth.h34
-rw-r--r--src/gui/creds/keychainchunk.cpp221
-rw-r--r--src/gui/creds/keychainchunk.h120
-rw-r--r--src/gui/creds/webflowcredentials.cpp282
-rw-r--r--src/gui/creds/webflowcredentials.h33
-rw-r--r--src/gui/creds/webflowcredentialsdialog.cpp107
-rw-r--r--src/gui/creds/webflowcredentialsdialog.h16
-rw-r--r--src/gui/folderstatusdelegate.cpp23
-rw-r--r--src/gui/folderstatusdelegate.h8
-rw-r--r--src/gui/generalsettings.cpp34
-rw-r--r--src/gui/generalsettings.h5
-rw-r--r--src/gui/headerbanner.cpp146
-rw-r--r--src/gui/headerbanner.h93
-rw-r--r--src/gui/owncloudgui.cpp25
-rw-r--r--src/gui/owncloudgui.h7
-rw-r--r--src/gui/owncloudsetupwizard.cpp3
-rw-r--r--src/gui/settingsdialog.cpp56
-rw-r--r--src/gui/settingsdialog.h5
-rw-r--r--src/gui/settingsdialogmac.cpp239
-rw-r--r--src/gui/settingsdialogmac.h73
-rw-r--r--src/gui/sharedialog.cpp24
-rw-r--r--src/gui/sharedialog.h4
-rw-r--r--src/gui/sharelinkwidget.cpp28
-rw-r--r--src/gui/sharelinkwidget.h3
-rw-r--r--src/gui/shareusergroupwidget.cpp50
-rw-r--r--src/gui/shareusergroupwidget.h8
-rw-r--r--src/gui/sslbutton.cpp2
-rw-r--r--src/gui/wizard/flow2authcredspage.cpp68
-rw-r--r--src/gui/wizard/flow2authcredspage.h19
-rw-r--r--src/gui/wizard/flow2authcredspage.ui101
-rw-r--r--src/gui/wizard/flow2authwidget.cpp152
-rw-r--r--src/gui/wizard/flow2authwidget.h26
-rw-r--r--src/gui/wizard/flow2authwidget.ui47
-rw-r--r--src/gui/wizard/owncloudadvancedsetuppage.cpp11
-rw-r--r--src/gui/wizard/owncloudadvancedsetuppage.h2
-rw-r--r--src/gui/wizard/owncloudhttpcredspage.cpp10
-rw-r--r--src/gui/wizard/owncloudhttpcredspage.h4
-rw-r--r--src/gui/wizard/owncloudsetuppage.cpp45
-rw-r--r--src/gui/wizard/owncloudsetuppage.h2
-rw-r--r--src/gui/wizard/owncloudwizard.cpp45
-rw-r--r--src/gui/wizard/owncloudwizard.h9
53 files changed, 1814 insertions, 1008 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 35102e0cc..9b0455a6b 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -37,12 +37,11 @@ set(client_UI_SRCS
mnemonicdialog.ui
tray/window.qml
tray/UserLine.qml
+ wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui
wizard/owncloudoauthcredspage.ui
- wizard/flow2authcredspage.ui
- wizard/flow2authwidget.ui
wizard/owncloudsetupnocredspage.ui
wizard/owncloudwizardresultpage.ui
wizard/webview.ui
@@ -105,6 +104,7 @@ set(client_SRCS
servernotificationhandler.cpp
guiutility.cpp
elidedlabel.cpp
+ headerbanner.cpp
iconjob.cpp
remotewipe.cpp
tray/UserModel.cpp
@@ -112,6 +112,7 @@ set(client_SRCS
creds/httpcredentialsgui.cpp
creds/oauth.cpp
creds/flow2auth.cpp
+ creds/keychainchunk.cpp
creds/webflowcredentials.cpp
creds/webflowcredentialsdialog.cpp
wizard/postfixlineedit.cpp
@@ -148,8 +149,6 @@ set(updater_SRCS
IF( APPLE )
list(APPEND client_SRCS cocoainitializer_mac.mm)
- list(APPEND client_SRCS settingsdialogmac.cpp)
- list(REMOVE_ITEM client_SRCS settingsdialog.cpp)
list(APPEND client_SRCS socketapisocket_mac.mm)
list(APPEND client_SRCS systray.mm)
@@ -179,17 +178,6 @@ set(3rdparty_SRC
../3rdparty/kmessagewidget/kmessagewidget.cpp
)
-if (APPLE)
- list(APPEND 3rdparty_SRC
- ../3rdparty/qtmacgoodies/src/macpreferenceswindow.mm
- ../3rdparty/qtmacgoodies/src/macstandardicon.mm
- ../3rdparty/qtmacgoodies/src/macwindow.mm
- )
- # We want to access Cocoa specific structures in the code above
- # and need the platform plugin interface for that - which is private.
- include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
-endif()
-
if(NOT WIN32)
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
else()
@@ -345,7 +333,6 @@ ENDIF()
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
- ${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR}
diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp
index 1bbf7ee10..a7fd0f5a1 100644
--- a/src/gui/accountsettings.cpp
+++ b/src/gui/accountsettings.cpp
@@ -57,10 +57,6 @@
#include "account.h"
-#ifdef Q_OS_MAC
-#include "settingsdialogmac.h"
-#endif
-
namespace OCC {
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
@@ -113,13 +109,13 @@ protected:
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
: QWidget(parent)
- , ui(new Ui::AccountSettings)
+ , _ui(new Ui::AccountSettings)
, _wasDisabledBefore(false)
, _accountState(accountState)
, _quotaInfo(accountState)
, _menuShown(false)
{
- ui->setupUi(this);
+ _ui->setupUi(this);
_model = new FolderStatusModel;
_model->setAccountState(_accountState);
@@ -127,37 +123,40 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
FolderStatusDelegate *delegate = new FolderStatusDelegate;
delegate->setParent(this);
- ui->_folderList->header()->hide();
- ui->_folderList->setItemDelegate(delegate);
- ui->_folderList->setModel(_model);
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &AccountSettings::styleChanged, delegate, &FolderStatusDelegate::slotStyleChanged);
+
+ _ui->_folderList->header()->hide();
+ _ui->_folderList->setItemDelegate(delegate);
+ _ui->_folderList->setModel(_model);
#if defined(Q_OS_MAC)
- ui->_folderList->setMinimumWidth(400);
+ _ui->_folderList->setMinimumWidth(400);
#else
- ui->_folderList->setMinimumWidth(300);
+ _ui->_folderList->setMinimumWidth(300);
#endif
- new ToolTipUpdater(ui->_folderList);
+ new ToolTipUpdater(_ui->_folderList);
auto mouseCursorChanger = new MouseCursorChanger(this);
- mouseCursorChanger->folderList = ui->_folderList;
+ mouseCursorChanger->folderList = _ui->_folderList;
mouseCursorChanger->model = _model;
- ui->_folderList->setMouseTracking(true);
- ui->_folderList->setAttribute(Qt::WA_Hover, true);
- ui->_folderList->installEventFilter(mouseCursorChanger);
+ _ui->_folderList->setMouseTracking(true);
+ _ui->_folderList->setAttribute(Qt::WA_Hover, true);
+ _ui->_folderList->installEventFilter(mouseCursorChanger);
createAccountToolbox();
connect(AccountManager::instance(), &AccountManager::accountAdded,
this, &AccountSettings::slotAccountAdded);
connect(this, &AccountSettings::removeAccountFolders,
AccountManager::instance(), &AccountManager::removeAccountFolders);
- connect(ui->_folderList, &QWidget::customContextMenuRequested,
+ connect(_ui->_folderList, &QWidget::customContextMenuRequested,
this, &AccountSettings::slotCustomContextMenuRequested);
- connect(ui->_folderList, &QAbstractItemView::clicked,
+ connect(_ui->_folderList, &QAbstractItemView::clicked,
this, &AccountSettings::slotFolderListClicked);
- connect(ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
- connect(ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
- connect(ui->selectiveSyncNotification, &QLabel::linkActivated,
+ connect(_ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
+ connect(_ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
+ connect(_ui->selectiveSyncNotification, &QLabel::linkActivated,
this, &AccountSettings::slotLinkActivated);
- connect(_model, &FolderStatusModel::suggestExpand, ui->_folderList, &QTreeView::expand);
+ connect(_model, &FolderStatusModel::suggestExpand, _ui->_folderList, &QTreeView::expand);
connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus);
refreshSelectiveSyncStatus();
connect(_model, &QAbstractItemModel::rowsInserted,
@@ -174,20 +173,21 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
addAction(syncNowWithRemoteDiscovery);
- connect(ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
- connect(ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
- connect(ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
- connect(ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
- connect(ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
+ connect(_ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
+ connect(_ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
+ connect(_ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
+ connect(_ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
+ connect(_ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
- QColor color = palette().highlight().color();
- ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
+ // quotaProgressBar style now set in customizeStyle()
+ /*QColor color = palette().highlight().color();
+ _ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/
- ui->connectLabel->setText(tr("No account configured."));
+ _ui->connectLabel->setText(tr("No account configured."));
connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
slotAccountStateChanged();
@@ -204,8 +204,10 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
{
slotNewMnemonicGenerated();
} else {
- ui->encryptionMessage->hide();
+ _ui->encryptionMessage->hide();
}
+
+ customizeStyle();
}
@@ -233,9 +235,9 @@ void AccountSettings::createAccountToolbox()
connect(UserModel::instance(), &UserModel::removeAccount,
this, &AccountSettings::slotOpenAccountWizard);
- ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
- ui->_accountToolbox->setMenu(menu);
- ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
+ _ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
+ _ui->_accountToolbox->setMenu(menu);
+ _ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
slotAccountAdded(_accountState);
}
@@ -243,14 +245,14 @@ void AccountSettings::createAccountToolbox()
void AccountSettings::slotNewMnemonicGenerated()
{
- ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
+ _ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
QAction *mnemonic = new QAction(tr("Enable encryption"), this);
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
- connect(mnemonic, &QAction::triggered, ui->encryptionMessage, &KMessageWidget::hide);
+ connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide);
- ui->encryptionMessage->addAction(mnemonic);
- ui->encryptionMessage->show();
+ _ui->encryptionMessage->addAction(mnemonic);
+ _ui->encryptionMessage->show();
}
void AccountSettings::slotMenuBeforeShow() {
@@ -258,7 +260,7 @@ void AccountSettings::slotMenuBeforeShow() {
return;
}
- auto menu = ui->_accountToolbox->menu();
+ auto menu = _ui->_accountToolbox->menu();
// We can't check this during the initial creation as there is no account yet then
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
@@ -273,7 +275,7 @@ void AccountSettings::slotMenuBeforeShow() {
QString AccountSettings::selectedFolderAlias() const
{
- QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
+ QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid())
return "";
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
@@ -286,16 +288,7 @@ void AccountSettings::slotOpenAccountWizard()
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
topLevelWidget()->close();
}
-#ifdef Q_OS_MAC
- qCDebug(lcAccountSettings) << parent() << topLevelWidget();
- SettingsDialogMac *sd = qobject_cast<SettingsDialogMac *>(topLevelWidget());
- if (sd) {
- sd->showActivityPage();
- } else {
- qFatal("nope");
- }
-#endif
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr);
}
@@ -311,7 +304,7 @@ void AccountSettings::slotToggleSignInState()
void AccountSettings::doExpand()
{
- ui->_folderList->expandToDepth(0);
+ _ui->_folderList->expandToDepth(0);
}
void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
@@ -559,7 +552,7 @@ void AccountSettings::slotEditCurrentIgnoredFiles()
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
{
- QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
+ QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -631,7 +624,7 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
{
- QTreeView *tv = ui->_folderList;
+ QTreeView *tv = _ui->_folderList;
QModelIndex index = tv->indexAt(pos);
if (!index.isValid()) {
return;
@@ -662,7 +655,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
ac = menu->addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
- if (!ui->_folderList->isExpanded(index)) {
+ if (!_ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
@@ -701,7 +694,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
}
if (_model->classify(indx) == FolderStatusModel::RootFolder) {
// tries to find if we clicked on the '...' button.
- QTreeView *tv = ui->_folderList;
+ QTreeView *tv = _ui->_folderList;
auto pos = tv->mapFromGlobal(QCursor::pos());
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
slotCustomContextMenuRequested(pos);
@@ -714,8 +707,8 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
// Expand root items on single click
if (_accountState && _accountState->state() == AccountState::Connected) {
- bool expanded = !(ui->_folderList->isExpanded(indx));
- ui->_folderList->setExpanded(indx, expanded);
+ bool expanded = !(_ui->_folderList->isExpanded(indx));
+ _ui->_folderList->setExpanded(indx, expanded);
}
}
}
@@ -797,7 +790,7 @@ void AccountSettings::slotRemoveCurrentFolder()
{
FolderMan *folderMan = FolderMan::instance();
auto folder = folderMan->folder(selectedFolderAlias());
- QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
+ QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
if (selected.isValid() && folder) {
int row = selected.row();
@@ -839,7 +832,7 @@ void AccountSettings::slotOpenCurrentFolder()
void AccountSettings::slotOpenCurrentLocalSubFolder()
{
- QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
+ QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -853,18 +846,21 @@ void AccountSettings::showConnectionLabel(const QString &message, QStringList er
"border-width: 1px; border-style: solid; border-color: #aaaaaa;"
"border-radius:5px;");
if (errors.isEmpty()) {
- ui->connectLabel->setText(message);
- ui->connectLabel->setToolTip(QString());
- ui->connectLabel->setStyleSheet(QString());
+ QString msg = message;
+ Theme::replaceLinkColorStringBackgroundAware(msg);
+ _ui->connectLabel->setText(msg);
+ _ui->connectLabel->setToolTip(QString());
+ _ui->connectLabel->setStyleSheet(QString());
} else {
errors.prepend(message);
- const QString msg = errors.join(QLatin1String("\n"));
+ QString msg = errors.join(QLatin1String("\n"));
qCDebug(lcAccountSettings) << msg;
- ui->connectLabel->setText(msg);
- ui->connectLabel->setToolTip(QString());
- ui->connectLabel->setStyleSheet(errStyle);
+ Theme::replaceLinkColorString(msg, QColor("#c1c8e6"));
+ _ui->connectLabel->setText(msg);
+ _ui->connectLabel->setToolTip(QString());
+ _ui->connectLabel->setStyleSheet(errStyle);
}
- ui->accountStatus->setVisible(!message.isEmpty());
+ _ui->accountStatus->setVisible(!message.isEmpty());
}
void AccountSettings::slotEnableCurrentFolder()
@@ -962,29 +958,29 @@ void AccountSettings::slotOpenOC()
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
{
if (total > 0) {
- ui->quotaProgressBar->setVisible(true);
- ui->quotaProgressBar->setEnabled(true);
+ _ui->quotaProgressBar->setVisible(true);
+ _ui->quotaProgressBar->setEnabled(true);
// workaround the label only accepting ints (which may be only 32 bit wide)
const double percent = used / (double)total * 100;
const int percentInt = qMin(qRound(percent), 100);
- ui->quotaProgressBar->setValue(percentInt);
+ _ui->quotaProgressBar->setValue(percentInt);
QString usedStr = Utility::octetsToString(used);
QString totalStr = Utility::octetsToString(total);
QString percentStr = Utility::compactFormatDouble(percent, 1);
QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr);
- ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
- ui->quotaInfoLabel->setToolTip(toolTip);
- ui->quotaProgressBar->setToolTip(toolTip);
+ _ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
+ _ui->quotaInfoLabel->setToolTip(toolTip);
+ _ui->quotaProgressBar->setToolTip(toolTip);
} else {
- ui->quotaProgressBar->setVisible(false);
- ui->quotaInfoLabel->setToolTip(QString());
+ _ui->quotaProgressBar->setVisible(false);
+ _ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) {
- ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
+ _ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else {
QString usedStr = Utility::octetsToString(used);
- ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
+ _ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
}
}
}
@@ -993,7 +989,7 @@ void AccountSettings::slotAccountStateChanged()
{
int state = _accountState ? _accountState->state() : AccountState::Disconnected;
if (_accountState) {
- ui->sslButton->updateAccountState(_accountState);
+ _ui->sslButton->updateAccountState(_accountState);
AccountPtr account = _accountState->account();
QUrl safeUrl(account->url());
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
@@ -1052,14 +1048,14 @@ void AccountSettings::slotAccountStateChanged()
}
/* Allow to expand the item if the account is connected. */
- ui->_folderList->setItemsExpandable(state == AccountState::Connected);
+ _ui->_folderList->setItemsExpandable(state == AccountState::Connected);
if (state != AccountState::Connected) {
/* check if there are expanded root items, if so, close them */
int i;
for (i = 0; i < _model->rowCount(); ++i) {
- if (ui->_folderList->isExpanded(_model->index(i)))
- ui->_folderList->setExpanded(_model->index(i), false);
+ if (_ui->_folderList->isExpanded(_model->index(i)))
+ _ui->_folderList->setExpanded(_model->index(i), false);
}
} else if (_model->isDirty()) {
// If we connect and have pending changes, show the list.
@@ -1103,21 +1099,21 @@ void AccountSettings::slotLinkActivated(const QString &link)
// Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString());
- if (!ui->_folderList->isExpanded(folderIndx)) {
- ui->_folderList->setExpanded(folderIndx, true);
+ if (!_ui->_folderList->isExpanded(folderIndx)) {
+ _ui->_folderList->setExpanded(folderIndx, true);
}
QModelIndex indx = _model->indexForPath(f, myFolder);
if (indx.isValid()) {
// make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
- if (!ui->_folderList->isExpanded(i)) {
- ui->_folderList->setExpanded(i, true);
+ if (!_ui->_folderList->isExpanded(i)) {
+ _ui->_folderList->setExpanded(i, true);
}
}
- ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
- ui->_folderList->setCurrentIndex(indx);
- ui->_folderList->scrollTo(indx);
+ _ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
+ _ui->_folderList->setCurrentIndex(indx);
+ _ui->_folderList->scrollTo(indx);
} else {
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
}
@@ -1126,7 +1122,7 @@ void AccountSettings::slotLinkActivated(const QString &link)
AccountSettings::~AccountSettings()
{
- delete ui;
+ delete _ui;
}
void AccountSettings::refreshSelectiveSyncStatus()
@@ -1164,8 +1160,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
if (msg.isEmpty()) {
- ui->selectiveSyncButtons->setVisible(true);
- ui->bigFolderUi->setVisible(false);
+ _ui->selectiveSyncButtons->setVisible(true);
+ _ui->bigFolderUi->setVisible(false);
} else {
ConfigFile cfg;
QString info = !cfg.confirmExternalStorage()
@@ -1174,27 +1170,27 @@ void AccountSettings::refreshSelectiveSyncStatus()
? tr("There are folders that were not synchronized because they are external storages: ")
: tr("There are folders that were not synchronized because they are too big or external storages: ");
- ui->selectiveSyncNotification->setText(info + msg);
- ui->selectiveSyncButtons->setVisible(false);
- ui->bigFolderUi->setVisible(true);
+ _ui->selectiveSyncNotification->setText(info + msg);
+ _ui->selectiveSyncButtons->setVisible(false);
+ _ui->bigFolderUi->setVisible(true);
shouldBeVisible = true;
}
- ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
- bool wasVisible = !ui->selectiveSyncStatus->isHidden();
+ _ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
+ bool wasVisible = !_ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) {
- QSize hint = ui->selectiveSyncStatus->sizeHint();
+ QSize hint = _ui->selectiveSyncStatus->sizeHint();
if (shouldBeVisible) {
- ui->selectiveSyncStatus->setMaximumHeight(0);
- ui->selectiveSyncStatus->setVisible(true);
+ _ui->selectiveSyncStatus->setMaximumHeight(0);
+ _ui->selectiveSyncStatus->setVisible(true);
}
- auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus);
+ auto anim = new QPropertyAnimation(_ui->selectiveSyncStatus, "maximumHeight", _ui->selectiveSyncStatus);
anim->setEndValue(shouldBeVisible ? hint.height() : 0);
anim->start(QAbstractAnimation::DeleteWhenStopped);
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
- ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
+ _ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
if (!shouldBeVisible) {
- ui->selectiveSyncStatus->hide();
+ _ui->selectiveSyncStatus->hide();
}
});
}
@@ -1254,12 +1250,30 @@ bool AccountSettings::event(QEvent *e)
// Expand the folder automatically only if there's only one, see #4283
// The 2 is 1 folder + 1 'add folder' button
if (_model->rowCount() <= 2) {
- ui->_folderList->setExpanded(_model->index(0, 0), true);
+ _ui->_folderList->setExpanded(_model->index(0, 0), true);
}
}
return QWidget::event(e);
}
+void AccountSettings::slotStyleChanged()
+{
+ customizeStyle();
+
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+}
+
+void AccountSettings::customizeStyle()
+{
+ QString msg = _ui->connectLabel->text();
+ Theme::replaceLinkColorStringBackgroundAware(msg);
+ _ui->connectLabel->setText(msg);
+
+ QColor color = palette().highlight().color();
+ _ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
+}
+
} // namespace OCC
#include "accountsettings.moc"
diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h
index e283df26f..886ebed23 100644
--- a/src/gui/accountsettings.h
+++ b/src/gui/accountsettings.h
@@ -64,11 +64,13 @@ signals:
void showIssuesList(AccountState *account);
void requesetMnemonic();
void removeAccountFolders(AccountState *account);
+ void styleChanged();
public slots:
void slotOpenOC();
void slotUpdateQuota(qint64, qint64);
void slotAccountStateChanged();
+ void slotStyleChanged();
AccountState *accountsState() { return _accountState; }
@@ -129,11 +131,12 @@ private:
bool event(QEvent *) override;
void createAccountToolbox();
void openIgnoredFilesDialog(const QString & absFolderPath);
+ void customizeStyle();
/// Returns the alias of the selected folder, empty string if none
QString selectedFolderAlias() const;
- Ui::AccountSettings *ui;
+ Ui::AccountSettings *_ui;
FolderStatusModel *_model;
QUrl _OCUrl;
diff --git a/src/gui/activityitemdelegate.cpp b/src/gui/activityitemdelegate.cpp
index 1d49af7d7..2b4231e07 100644
--- a/src/gui/activityitemdelegate.cpp
+++ b/src/gui/activityitemdelegate.cpp
@@ -15,6 +15,7 @@
*/
#include "activityitemdelegate.h"
+#include "activitylistmodel.h"
#include "folderstatusmodel.h"
#include "folderman.h"
#include "accountstate.h"
@@ -26,6 +27,12 @@
#include <QPainter>
#include <QApplication>
+#define FIXME_USE_HIGH_DPI_RATIO
+#ifdef FIXME_USE_HIGH_DPI_RATIO
+ // FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
+ #include <QDesktopWidget>
+#endif
+
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
namespace OCC {
@@ -40,6 +47,12 @@ int ActivityItemDelegate::_buttonHeight = 0;
const QString ActivityItemDelegate::_remote_share("remote_share");
const QString ActivityItemDelegate::_call("call");
+ActivityItemDelegate::ActivityItemDelegate()
+ : QStyledItemDelegate()
+{
+ customizeStyle();
+}
+
int ActivityItemDelegate::iconHeight()
{
if (_iconHeight == 0) {
@@ -89,10 +102,26 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
int iconSize = 16;
int iconOffset = qRound(fm.height() / 4.0 * 7.0);
int offset = 4;
+ const bool isSelected = (option.state & QStyle::State_Selected);
+#ifdef FIXME_USE_HIGH_DPI_RATIO
+ // FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
+ const int device_pixel_ration = QApplication::desktop()->devicePixelRatio();
+ int pixel_ratio = (device_pixel_ration > 1 ? device_pixel_ration : 1);
+#endif
// get the data
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
- QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
+ QIcon actionIcon;
+ const ActivityListModel::ActionIcon icn = qvariant_cast<ActivityListModel::ActionIcon>(index.data(ActionIconRole));
+ switch(icn.iconType) {
+ case ActivityListModel::ActivityIconType::iconUseCached: actionIcon = icn.cachedIcon; break;
+ case ActivityListModel::ActivityIconType::iconActivity: actionIcon = (isSelected ? _iconActivity_sel : _iconActivity); break;
+ case ActivityListModel::ActivityIconType::iconBell: actionIcon = (isSelected ? _iconBell_sel : _iconBell); break;
+ case ActivityListModel::ActivityIconType::iconStateError: actionIcon = _iconStateError; break;
+ case ActivityListModel::ActivityIconType::iconStateWarning: actionIcon = _iconStateWarning; break;
+ case ActivityListModel::ActivityIconType::iconStateInfo: actionIcon = _iconStateInfo; break;
+ case ActivityListModel::ActivityIconType::iconStateSync: actionIcon = _iconStateSync; break;
+ }
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString messageText = qvariant_cast<QString>(index.data(MessageRole));
@@ -116,6 +145,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
actionTextBox.setTop(option.rect.top() + margin + offset/2);
actionTextBox.setHeight(fm.height());
actionTextBox.setLeft(actionIconRect.right() + margin);
+#ifdef FIXME_USE_HIGH_DPI_RATIO
+ // FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
+ actionTextBoxWidth *= pixel_ratio;
+#endif
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
// message text rect
@@ -138,11 +171,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// time box rect
QRect timeBox = messageTextBox;
- QString timeStr = tr("%1").arg(timeText);
#if (HASQT5_11)
- int timeTextWidth = fm.horizontalAdvance(timeStr);
+ int timeTextWidth = fm.horizontalAdvance(timeText);
#else
- int timeTextWidth = fm.width(timeStr);
+ int timeTextWidth = fm.width(timeText);
#endif
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
if(messageText.isEmpty() || actionText.isEmpty())
@@ -150,6 +182,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
timeBox.setTop(timeTop);
timeBox.setHeight(fm.height());
timeBox.setBottom(timeBox.top() + fm.height());
+#ifdef FIXME_USE_HIGH_DPI_RATIO
+ // FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
+ timeTextWidth *= pixel_ratio;
+#endif
timeBox.setRight(timeBox.left() + timeTextWidth + margin);
// buttons - default values
@@ -184,9 +220,9 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if(activityType == Activity::Type::NotificationType){
// Secondary will be 'Dismiss' or '...' multiple options button
- secondaryButton.icon = QIcon(QLatin1String(":/client/resources/close.svg"));
+ secondaryButton.icon = (isSelected ? _iconClose_sel : _iconClose);
if(customList.size() > 1)
- secondaryButton.icon = QIcon(QLatin1String(":/client/resources/more.svg"));
+ secondaryButton.icon = (isSelected ? _iconMore_sel : _iconMore);
secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'More Information' or 'Accept'
@@ -209,7 +245,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncResultType){
// Secondary will be 'open file manager' with the folder icon
- secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
+ secondaryButton.icon = _iconFolder;
secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'open browser'
@@ -230,7 +266,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncFileItemType){
// Secondary will be 'open file manager' with the folder icon
- secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
+ secondaryButton.icon = _iconFolder;
secondaryButton.iconSize = QSize(iconSize, iconSize);
// No primary button on this case
@@ -255,7 +291,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
p.setCurrentColorGroup(QPalette::Disabled);
// change pen color if the line is selected
- if (option.state & QStyle::State_Selected)
+ if (isSelected)
painter->setPen(p.color(QPalette::HighlightedText));
else
painter->setPen(p.color(QPalette::Text));
@@ -269,8 +305,15 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
painter->drawText(actionTextBox, elidedAction);
// draw the buttons
- if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType)
+ if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType) {
+ primaryButton.palette = p;
+ if (isSelected)
+ primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::HighlightedText));
+ else
+ primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::Text));
+
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
+ }
// Since they are errors on local syncing, there is nothing to do in the server
if(activityType != Activity::Type::ActivityType)
@@ -284,13 +327,13 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
}
// change pen color for the time
- if (option.state & QStyle::State_Selected)
+ if (isSelected)
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
else
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
// draw the time
- const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, spaceLeftForText);
+ const QString elidedTime = fm.elidedText(timeText, Qt::ElideRight, spaceLeftForText);
painter->drawText(timeBox, elidedTime);
painter->restore();
@@ -333,4 +376,32 @@ bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
+void ActivityItemDelegate::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void ActivityItemDelegate::customizeStyle()
+{
+ QPalette pal;
+ pal.setColor(QPalette::Base, QColor(0,0,0)); // use dark background colour to invert icons
+
+ _iconClose = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"));
+ _iconClose_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"), pal);
+ _iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
+ _iconMore_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"), pal);
+
+ _iconFolder = QIcon(QLatin1String(":/client/resources/folder.svg"));
+
+ _iconActivity = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"));
+ _iconActivity_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"), pal);
+ _iconBell = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"));
+ _iconBell_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"), pal);
+
+ _iconStateError = QIcon(QLatin1String(":/client/resources/state-error.svg"));
+ _iconStateWarning = QIcon(QLatin1String(":/client/resources/state-warning.svg"));
+ _iconStateInfo = QIcon(QLatin1String(":/client/resources/state-info.svg"));
+ _iconStateSync = QIcon(QLatin1String(":/client/resources/state-sync.svg"));
+}
+
} // namespace OCC
diff --git a/src/gui/activityitemdelegate.h b/src/gui/activityitemdelegate.h
index 49ffca592..908889eee 100644
--- a/src/gui/activityitemdelegate.h
+++ b/src/gui/activityitemdelegate.h
@@ -43,6 +43,8 @@ public:
AccountConnectedRole,
SyncFileStatusRole };
+ ActivityItemDelegate();
+
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
@@ -51,11 +53,16 @@ public:
static int rowHeight();
static int iconHeight();
+public slots:
+ void slotStyleChanged();
+
signals:
void primaryButtonClickedOnItemView(const QModelIndex &index);
void secondaryButtonClickedOnItemView(const QModelIndex &index);
private:
+ void customizeStyle();
+
static int _margin;
static int _iconHeight;
static int _primaryButtonWidth;
@@ -65,6 +72,23 @@ private:
static int _buttonHeight;
static const QString _remote_share;
static const QString _call;
+
+ QIcon _iconClose;
+ QIcon _iconClose_sel;
+ QIcon _iconMore;
+ QIcon _iconMore_sel;
+
+ QIcon _iconFolder;
+
+ QIcon _iconActivity;
+ QIcon _iconActivity_sel;
+ QIcon _iconBell;
+ QIcon _iconBell_sel;
+
+ QIcon _iconStateError;
+ QIcon _iconStateWarning;
+ QIcon _iconStateInfo;
+ QIcon _iconStateSync;
};
} // namespace OCC
diff --git a/src/gui/activitylistmodel.cpp b/src/gui/activitylistmodel.cpp
index eab049af7..eeb0cc836 100644
--- a/src/gui/activitylistmodel.cpp
+++ b/src/gui/activitylistmodel.cpp
@@ -64,10 +64,19 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
case ActivityItemDelegate::PathRole:
if(!a._file.isEmpty()){
auto folder = FolderMan::instance()->folder(a._folder);
- list = FolderMan::instance()->findFileInLocalFolders(folder->remotePath(), ast->account());
+ QString relPath(a._file);
+ if(folder) relPath.prepend(folder->remotePath());
+ list = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());
if (list.count() > 0) {
return QVariant(list.at(0));
}
+ // File does not exist anymore? Let's try to open its path
+ if(QFileInfo(relPath).exists()) {
+ list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
+ if (list.count() > 0) {
+ return QVariant(list.at(0));
+ }
+ }
}
return QVariant();
case ActivityItemDelegate::ActionsLinksRole:{
@@ -79,59 +88,60 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
}
return customList;
}
- case ActivityItemDelegate::ActionIconRole:
+ case ActivityItemDelegate::ActionIconRole:{
+ ActionIcon actionIcon;
if(a._type == Activity::NotificationType){
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
- if(!cachedIcon.isNull())
- return cachedIcon;
- else return QIcon(QLatin1String(":/client/resources/bell.svg"));
+ if(!cachedIcon.isNull()) {
+ actionIcon.iconType = ActivityIconType::iconUseCached;
+ actionIcon.cachedIcon = cachedIcon;
+ } else {
+ actionIcon.iconType = ActivityIconType::iconBell;
+ }
} else if(a._type == Activity::SyncResultType){
- return QIcon(QLatin1String(":/client/resources/state-error.svg"));
+ actionIcon.iconType = ActivityIconType::iconStateError;
} else if(a._type == Activity::SyncFileItemType){
if(a._status == SyncFileItem::NormalError
|| a._status == SyncFileItem::FatalError
|| a._status == SyncFileItem::DetailError
|| a._status == SyncFileItem::BlacklistedError) {
- return QIcon(QLatin1String(":/client/resources/state-error.svg"));
+ actionIcon.iconType = ActivityIconType::iconStateError;
} else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration
|| a._status == SyncFileItem::FileLocked){
- return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
+ actionIcon.iconType = ActivityIconType::iconStateWarning;
} else if(a._status == SyncFileItem::FileIgnored){
- return QIcon(QLatin1String(":/client/resources/state-info.svg"));
+ actionIcon.iconType = ActivityIconType::iconStateInfo;
+ } else {
+ actionIcon.iconType = ActivityIconType::iconStateSync;
}
- return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
+ } else {
+ actionIcon.iconType = ActivityIconType::iconActivity;
}
- return QIcon(QLatin1String(":/client/resources/activity.png"));
- break;
+ QVariant icn;
+ icn.setValue(actionIcon);
+ return icn;
+ }
case ActivityItemDelegate::ObjectTypeRole:
return a._objectType;
- break;
case ActivityItemDelegate::ActionRole:{
QVariant type;
type.setValue(a._type);
return type;
- break;
}
case ActivityItemDelegate::ActionTextRole:
return a._subject;
- break;
case ActivityItemDelegate::MessageRole:
return a._message;
- break;
case ActivityItemDelegate::LinkRole:
return a._link;
- break;
case ActivityItemDelegate::AccountRole:
return a._accName;
- break;
case ActivityItemDelegate::PointInTimeRole:
- return Utility::timeAgoInWords(a._dateTime);
- break;
+ return QString("%1 (%2)").arg(a._dateTime.toLocalTime().toString(Qt::DefaultLocaleShortDate), Utility::timeAgoInWords(a._dateTime.toLocalTime()));
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
- break;
default:
return QVariant();
}
@@ -256,7 +266,7 @@ void ActivityListModel::clearNotifications() {
}
void ActivityListModel::removeActivityFromActivityList(int row) {
- Activity activity = _finalList.at(row);
+ Activity activity = _finalList.at(row);
removeActivityFromActivityList(activity);
combineActivityLists();
}
@@ -294,18 +304,27 @@ void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
- std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
- resultList.append(_notificationErrorsLists);
- resultList.append(_notificationIgnoredFiles);
+ if(_notificationErrorsLists.count() > 0) {
+ std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
+ resultList.append(_notificationErrorsLists);
+ }
+ if(_listOfIgnoredFiles.size() > 0)
+ resultList.append(_notificationIgnoredFiles);
- std::sort(_notificationLists.begin(), _notificationLists.end());
- resultList.append(_notificationLists);
+ if(_notificationLists.count() > 0) {
+ std::sort(_notificationLists.begin(), _notificationLists.end());
+ resultList.append(_notificationLists);
+ }
- std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
- resultList.append(_syncFileItemLists);
+ if(_syncFileItemLists.count() > 0) {
+ std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
+ resultList.append(_syncFileItemLists);
+ }
- std::sort(_activityLists.begin(), _activityLists.end());
- resultList.append(_activityLists);
+ if(_activityLists.count() > 0) {
+ std::sort(_activityLists.begin(), _activityLists.end());
+ resultList.append(_activityLists);
+ }
beginResetModel();
_finalList.clear();
diff --git a/src/gui/activitylistmodel.h b/src/gui/activitylistmodel.h
index cbb823f1f..1ff352beb 100644
--- a/src/gui/activitylistmodel.h
+++ b/src/gui/activitylistmodel.h
@@ -38,6 +38,20 @@ class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
+ enum ActivityIconType {
+ iconUseCached = 0,
+ iconActivity,
+ iconBell,
+ iconStateError,
+ iconStateWarning,
+ iconStateInfo,
+ iconStateSync
+ };
+ struct ActionIcon {
+ ActivityIconType iconType;
+ QIcon cachedIcon;
+ };
+
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
@@ -84,4 +98,7 @@ private:
int _currentItem = 0;
};
}
+
+Q_DECLARE_METATYPE(OCC::ActivityListModel::ActionIcon)
+
#endif // ACTIVITYLISTMODEL_H
diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp
index 7ce4471d3..469b0d14e 100644
--- a/src/gui/activitywidget.cpp
+++ b/src/gui/activitywidget.cpp
@@ -89,6 +89,9 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
this, &ActivityWidget::addError);
_removeTimer.setInterval(1000);
+
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &ActivityWidget::styleChanged, delegate, &ActivityItemDelegate::slotStyleChanged);
}
ActivityWidget::~ActivityWidget()
@@ -176,7 +179,7 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
Activity activity;
activity._type = Activity::SyncFileItemType; //client activity
activity._status = item->_status;
- activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
+ activity._dateTime = QDateTime::currentDateTime();
activity._message = item->_originalFile;
activity._link = folderInstance->accountState()->account()->url();
activity._accName = folderInstance->accountState()->account()->displayName();
@@ -548,6 +551,12 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
}
+void ActivityWidget::slotStyleChanged()
+{
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+}
+
/* ==================================================================== */
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
@@ -570,6 +579,9 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
// connect a model signal to stop the animation
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
+
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &ActivitySettings::styleChanged, _activityWidget, &ActivityWidget::slotStyleChanged);
}
void ActivitySettings::slotDisplayActivities(){
@@ -628,4 +640,14 @@ bool ActivitySettings::event(QEvent *e)
ActivitySettings::~ActivitySettings()
{
}
+
+void ActivitySettings::slotStyleChanged()
+{
+ if(_progressIndicator)
+ _progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
+
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+}
+
}
diff --git a/src/gui/activitywidget.h b/src/gui/activitywidget.h
index a51ed9346..ee271c62a 100644
--- a/src/gui/activitywidget.h
+++ b/src/gui/activitywidget.h
@@ -78,12 +78,14 @@ public slots:
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
+ void slotStyleChanged();
signals:
void guiLog(const QString &, const QString &);
void rowsInserted();
void hideActivityTab(bool);
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
+ void styleChanged();
private slots:
void slotBuildNotificationDisplay(const ActivityList &list);
@@ -96,6 +98,7 @@ private slots:
void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
private:
+ void customizeStyle();
void showLabels();
QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui;
@@ -137,6 +140,7 @@ public slots:
void slotRefresh();
void slotRemoveAccount();
void setNotificationRefreshInterval(std::chrono::milliseconds interval);
+ void slotStyleChanged();
private slots:
void slotRegularNotificationCheck();
@@ -144,6 +148,7 @@ private slots:
signals:
void guiLog(const QString &, const QString &);
+ void styleChanged();
private:
bool event(QEvent *e) override;
diff --git a/src/gui/application.cpp b/src/gui/application.cpp
index 189141595..51af86f92 100644
--- a/src/gui/application.cpp
+++ b/src/gui/application.cpp
@@ -263,6 +263,9 @@ Application::Application(int &argc, char **argv)
// Cleanup at Quit.
connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup);
+
+ // Allow other classes to hook into isShowingSettingsDialog() signals (re-auth widgets, for example)
+ connect(_gui.data(), &ownCloudGui::isShowingSettingsDialog, this, &Application::slotGuiIsShowingSettings);
}
Application::~Application()
@@ -655,5 +658,9 @@ void Application::showSettingsDialog()
_gui->slotShowSettings();
}
+void Application::slotGuiIsShowingSettings()
+{
+ emit isShowingSettingsDialog();
+}
} // namespace OCC
diff --git a/src/gui/application.h b/src/gui/application.h
index 77abd7a97..ac303cb4c 100644
--- a/src/gui/application.h
+++ b/src/gui/application.h
@@ -82,6 +82,7 @@ protected:
signals:
void folderRemoved();
void folderStateChanged(Folder *);
+ void isShowingSettingsDialog();
protected slots:
void slotParseMessage(const QString &, QObject *);
@@ -91,6 +92,7 @@ protected slots:
void slotAccountStateAdded(AccountState *accountState);
void slotAccountStateRemoved(AccountState *accountState);
void slotSystemOnlineConfigurationChanged(QNetworkConfiguration);
+ void slotGuiIsShowingSettings();
private:
void setHelp();
diff --git a/src/gui/creds/flow2auth.cpp b/src/gui/creds/flow2auth.cpp
index 3a9db471a..e75a8c9bf 100644
--- a/src/gui/creds/flow2auth.cpp
+++ b/src/gui/creds/flow2auth.cpp
@@ -14,10 +14,12 @@
*/
#include <QDesktopServices>
+#include <QApplication>
+#include <QClipboard>
#include <QTimer>
#include <QBuffer>
#include "account.h"
-#include "creds/flow2auth.h"
+#include "flow2auth.h"
#include <QJsonObject>
#include <QJsonDocument>
#include "theme.h"
@@ -28,6 +30,17 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
+
+Flow2Auth::Flow2Auth(Account *account, QObject *parent)
+ : QObject(parent)
+ , _account(account)
+ , _isBusy(false)
+ , _hasToken(false)
+{
+ _pollTimer.setInterval(1000);
+ QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
+}
+
Flow2Auth::~Flow2Auth()
{
}
@@ -47,7 +60,23 @@ QUrl Flow2Auth::authorisationLink() const
void Flow2Auth::openBrowser()
{
- _pollTimer.stop();
+ fetchNewToken(TokenAction::actionOpenBrowser);
+}
+
+void Flow2Auth::copyLinkToClipboard()
+{
+ fetchNewToken(TokenAction::actionCopyLinkToClipboard);
+}
+
+void Flow2Auth::fetchNewToken(const TokenAction action)
+{
+ if(_isBusy)
+ return;
+
+ _isBusy = true;
+ _hasToken = false;
+
+ emit statusChanged(PollStatus::statusFetchToken, 0);
// Step 1: Initiate a login, do an anonymous POST request
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
@@ -59,14 +88,18 @@ void Flow2Auth::openBrowser()
auto job = _account->sendRequest("POST", url, req);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
- QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
+ QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) {
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
+ QString pollToken, pollEndpoint, loginUrl;
- QString pollToken = json.value("poll").toObject().value("token").toString();
- QString pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
- QUrl loginUrl = json["login"].toString();
+ if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
+ && !json.isEmpty()) {
+ pollToken = json.value("poll").toObject().value("token").toString();
+ pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
+ loginUrl = json["login"].toString();
+ }
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
@@ -85,7 +118,9 @@ void Flow2Auth::openBrowser()
errorReason = tr("The reply from the server did not contain all expected fields");
}
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
- emit result(Error);
+ emit result(Error, errorReason);
+ _pollTimer.stop();
+ _isBusy = false;
return;
}
@@ -99,23 +134,50 @@ void Flow2Auth::openBrowser()
ConfigFile cfg;
std::chrono::milliseconds polltime = cfg.remotePollInterval();
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
- _pollTimer.setInterval(polltime.count());
- QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
- _pollTimer.start();
+ _secondsInterval = (polltime.count() / 1000);
+ _secondsLeft = _secondsInterval;
+ emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
+
+ if(!_pollTimer.isActive()) {
+ _pollTimer.start();
+ }
- // Try to open Browser
- if (!QDesktopServices::openUrl(authorisationLink())) {
- // We cannot open the browser, then we claim we don't support Flow2Auth.
- // Our UI callee should ask the user to copy and open the link.
- emit result(NotSupported, QString());
+ switch(action)
+ {
+ case actionOpenBrowser:
+ // Try to open Browser
+ if (!QDesktopServices::openUrl(authorisationLink())) {
+ // We cannot open the browser, then we claim we don't support Flow2Auth.
+ // Our UI callee will ask the user to copy and open the link.
+ emit result(NotSupported);
+ }
+ break;
+ case actionCopyLinkToClipboard:
+ QApplication::clipboard()->setText(authorisationLink().toString(QUrl::FullyEncoded));
+ emit statusChanged(PollStatus::statusCopyLinkToClipboard, 0);
+ break;
}
+
+ _isBusy = false;
+ _hasToken = true;
});
}
void Flow2Auth::slotPollTimerTimeout()
{
- _pollTimer.stop();
+ if(_isBusy || !_hasToken)
+ return;
+
+ _isBusy = true;
+
+ _secondsLeft--;
+ if(_secondsLeft > 0) {
+ emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
+ _isBusy = false;
+ return;
+ }
+ emit statusChanged(PollStatus::statusPollNow, 0);
// Step 2: Poll
QNetworkRequest req;
@@ -132,10 +194,15 @@ void Flow2Auth::slotPollTimerTimeout()
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
-
- QUrl serverUrl = json["server"].toString();
- QString loginName = json["loginName"].toString();
- QString appPassword = json["appPassword"].toString();
+ QUrl serverUrl;
+ QString loginName, appPassword;
+
+ if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
+ && !json.isEmpty()) {
+ serverUrl = json["server"].toString();
+ loginName = json["loginName"].toString();
+ appPassword = json["appPassword"].toString();
+ }
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
@@ -155,26 +222,50 @@ void Flow2Auth::slotPollTimerTimeout()
}
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
+ // We get a 404 until authentication is done, so don't show this error in the GUI.
+ if(reply->error() != QNetworkReply::ContentNotFoundError)
+ emit result(Error, errorReason);
+
// Forget sensitive data
appPassword.clear();
loginName.clear();
// Failed: poll again
- _pollTimer.start();
+ _secondsLeft = _secondsInterval;
+ _isBusy = false;
return;
}
+ _pollTimer.stop();
+
// Success
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
_account->setUrl(serverUrl);
- emit result(LoggedIn, loginName, appPassword);
+ emit result(LoggedIn, QString(), loginName, appPassword);
// Forget sensitive data
appPassword.clear();
loginName.clear();
+
+ _loginUrl.clear();
+ _pollToken.clear();
+ _pollEndpoint.clear();
+
+ _isBusy = false;
+ _hasToken = false;
});
}
+void Flow2Auth::slotPollNow()
+{
+ // poll now if we're not already doing so
+ if(_isBusy || !_hasToken)
+ return;
+
+ _secondsLeft = 1;
+ slotPollTimerTimeout();
+}
+
} // namespace OCC
diff --git a/src/gui/creds/flow2auth.h b/src/gui/creds/flow2auth.h
index b53834a11..e4deb203e 100644
--- a/src/gui/creds/flow2auth.h
+++ b/src/gui/creds/flow2auth.h
@@ -25,17 +25,23 @@ namespace OCC {
* Job that does the authorization, grants and fetches the access token via Login Flow v2
*
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
- *
*/
class Flow2Auth : public QObject
{
Q_OBJECT
public:
- Flow2Auth(Account *account, QObject *parent)
- : QObject(parent)
- , _account(account)
- {
- }
+ enum TokenAction {
+ actionOpenBrowser = 1,
+ actionCopyLinkToClipboard
+ };
+ enum PollStatus {
+ statusPollCountdown = 1,
+ statusPollNow,
+ statusFetchToken,
+ statusCopyLinkToClipboard
+ };
+
+ Flow2Auth(Account *account, QObject *parent);
~Flow2Auth();
enum Result { NotSupported,
@@ -44,6 +50,7 @@ public:
Q_ENUM(Result);
void start();
void openBrowser();
+ void copyLinkToClipboard();
QUrl authorisationLink() const;
signals:
@@ -51,18 +58,29 @@ signals:
* The state has changed.
* when logged in, appPassword has the value of the app password.
*/
- void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
+ void result(Flow2Auth::Result result, const QString &errorString = QString(),
+ const QString &user = QString(), const QString &appPassword = QString());
+
+ void statusChanged(const PollStatus status, int secondsLeft);
+
+public slots:
+ void slotPollNow();
private slots:
void slotPollTimerTimeout();
private:
+ void fetchNewToken(const TokenAction action);
+
Account *_account;
QUrl _loginUrl;
QString _pollToken;
QString _pollEndpoint;
QTimer _pollTimer;
+ int _secondsLeft;
+ int _secondsInterval;
+ bool _isBusy;
+ bool _hasToken;
};
-
} // namespace OCC
diff --git a/src/gui/creds/keychainchunk.cpp b/src/gui/creds/keychainchunk.cpp
new file mode 100644
index 000000000..ceacd27d7
--- /dev/null
+++ b/src/gui/creds/keychainchunk.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) by Michael Schuster <michael@nextcloud.com>
+ *
+ * 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 "account.h"
+#include "keychainchunk.h"
+#include "theme.h"
+#include "networkjobs.h"
+#include "configfile.h"
+#include "creds/abstractcredentials.h"
+
+using namespace QKeychain;
+
+namespace OCC {
+
+Q_LOGGING_CATEGORY(lcKeychainChunk, "nextcloud.sync.credentials.keychainchunk", QtInfoMsg)
+
+namespace KeychainChunk {
+
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
+static void addSettingsToJob(Account *account, QKeychain::Job *job)
+{
+ Q_UNUSED(account)
+ auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
+ settings->setParent(job); // make the job parent to make setting deleted properly
+ job->setSettings(settings.release());
+}
+#endif
+
+/*
+* Job
+*/
+Job::Job(QObject *parent)
+ : QObject(parent)
+{
+ _serviceName = Theme::instance()->appName();
+}
+
+/*
+* WriteJob
+*/
+WriteJob::WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent)
+ : Job(parent)
+{
+ _account = account;
+ _key = key;
+
+ // Windows workaround: Split the private key into chunks of 2048 bytes,
+ // to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
+ _chunkBuffer = data;
+ _chunkCount = 0;
+}
+
+void WriteJob::start()
+{
+ slotWriteJobDone(nullptr);
+}
+
+void WriteJob::slotWriteJobDone(QKeychain::Job *incomingJob)
+{
+ QKeychain::WritePasswordJob *writeJob = static_cast<QKeychain::WritePasswordJob *>(incomingJob);
+
+ // errors?
+ if (writeJob) {
+ _error = writeJob->error();
+ _errorString = writeJob->errorString();
+
+ if (writeJob->error() != NoError) {
+ qCWarning(lcKeychainChunk) << "Error while writing" << writeJob->key() << "chunk" << writeJob->errorString();
+ _chunkBuffer.clear();
+ }
+ }
+
+ // write a chunk if there is any in the buffer
+ if (!_chunkBuffer.isEmpty()) {
+#if defined(Q_OS_WIN)
+ // Windows workaround: Split the data into chunks of 2048 bytes,
+ // to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
+ auto chunk = _chunkBuffer.left(KeychainChunk::ChunkSize);
+
+ _chunkBuffer = _chunkBuffer.right(_chunkBuffer.size() - chunk.size());
+#else
+ // write full data in one chunk on non-Windows, as usual
+ auto chunk = _chunkBuffer;
+
+ _chunkBuffer.clear();
+#endif
+ auto index = (_chunkCount++);
+
+ // keep the limit
+ if (_chunkCount > KeychainChunk::MaxChunks) {
+ qCWarning(lcKeychainChunk) << "Maximum chunk count exceeded while writing" << writeJob->key() << "chunk" << QString::number(index) << "cutting off after" << QString::number(KeychainChunk::MaxChunks) << "chunks";
+
+ writeJob->deleteLater();
+
+ _chunkBuffer.clear();
+
+ emit finished(this);
+ return;
+ }
+
+ const QString kck = AbstractCredentials::keychainKey(
+ _account->url().toString(),
+ _key + (index > 0 ? (QString(".") + QString::number(index)) : QString()),
+ _account->id());
+
+ QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(_serviceName);
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
+ addSettingsToJob(_account, job);
+#endif
+ job->setInsecureFallback(_insecureFallback);
+ connect(job, &QKeychain::Job::finished, this, &KeychainChunk::WriteJob::slotWriteJobDone);
+ // only add the key's (sub)"index" after the first element, to stay compatible with older versions and non-Windows
+ job->setKey(kck);
+ job->setBinaryData(chunk);
+ job->start();
+
+ chunk.clear();
+ } else {
+ emit finished(this);
+ }
+
+ writeJob->deleteLater();
+}
+
+/*
+* ReadJob
+*/
+ReadJob::ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent)
+ : Job(parent)
+{
+ _account = account;
+ _key = key;
+
+ _keychainMigration = keychainMigration;
+
+ _chunkCount = 0;
+ _chunkBuffer.clear();
+}
+
+void ReadJob::start()
+{
+ _chunkCount = 0;
+ _chunkBuffer.clear();
+
+ const QString kck = AbstractCredentials::keychainKey(
+ _account->url().toString(),
+ _key,
+ _keychainMigration ? QString() : _account->id());
+
+ QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
+ addSettingsToJob(_account, job);
+#endif
+ job->setInsecureFallback(_insecureFallback);
+ job->setKey(kck);
+ connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
+ job->start();
+}
+
+void ReadJob::slotReadJobDone(QKeychain::Job *incomingJob)
+{
+ // Errors or next chunk?
+ QKeychain::ReadPasswordJob *readJob = static_cast<QKeychain::ReadPasswordJob *>(incomingJob);
+
+ if (readJob) {
+ if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
+ _chunkBuffer.append(readJob->binaryData());
+ _chunkCount++;
+
+#if defined(Q_OS_WIN)
+ // try to fetch next chunk
+ if (_chunkCount < KeychainChunk::MaxChunks) {
+ const QString kck = AbstractCredentials::keychainKey(
+ _account->url().toString(),
+ _key + QString(".") + QString::number(_chunkCount),
+ _keychainMigration ? QString() : _account->id());
+
+ QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
+ addSettingsToJob(_account, job);
+#endif
+ job->setInsecureFallback(_insecureFallback);
+ job->setKey(kck);
+ connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
+ job->start();
+
+ readJob->deleteLater();
+ return;
+ } else {
+ qCWarning(lcKeychainChunk) << "Maximum chunk count for" << readJob->key() << "reached, ignoring after" << KeychainChunk::MaxChunks;
+ }
+#endif
+ } else {
+ if (readJob->error() != QKeychain::Error::EntryNotFound ||
+ ((readJob->error() == QKeychain::Error::EntryNotFound) && _chunkCount == 0)) {
+ _error = readJob->error();
+ _errorString = readJob->errorString();
+ qCWarning(lcKeychainChunk) << "Unable to read" << readJob->key() << "chunk" << QString::number(_chunkCount) << readJob->errorString();
+ }
+ }
+
+ readJob->deleteLater();
+ }
+
+ emit finished(this);
+}
+
+} // namespace KeychainChunk
+
+} // namespace OCC
diff --git a/src/gui/creds/keychainchunk.h b/src/gui/creds/keychainchunk.h
new file mode 100644
index 000000000..875ab5037
--- /dev/null
+++ b/src/gui/creds/keychainchunk.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) by Michael Schuster <michael@nextcloud.com>
+ *
+ * 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.
+ */
+
+#pragma once
+#ifndef KEYCHAINCHUNK_H
+#define KEYCHAINCHUNK_H
+
+#include <QObject>
+#include <keychain.h>
+#include "accountfwd.h"
+
+// We don't support insecure fallback
+// #define KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK
+
+namespace OCC {
+
+namespace KeychainChunk {
+
+/*
+* Workaround for Windows:
+*
+* Split the keychain entry's data into chunks of 2048 bytes,
+* to allow 4k (4096 bit) keys / large certs to be saved (see limits in webflowcredentials.h)
+*/
+static constexpr int ChunkSize = 2048;
+static constexpr int MaxChunks = 10;
+
+/*
+ * @brief: Abstract base class for KeychainChunk jobs.
+ */
+class Job : public QObject {
+ Q_OBJECT
+public:
+ Job(QObject *parent = nullptr);
+
+ const QKeychain::Error error() const {
+ return _error;
+ }
+ const QString errorString() const {
+ return _errorString;
+ }
+
+ QByteArray binaryData() const {
+ return _chunkBuffer;
+ }
+
+ const bool insecureFallback() const {
+ return _insecureFallback;
+ }
+
+// If we use it but don't support insecure fallback, give us nice compilation errors ;p
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
+ void setInsecureFallback(const bool &insecureFallback)
+ {
+ _insecureFallback = insecureFallback;
+ }
+#endif
+
+protected:
+ QString _serviceName;
+ Account *_account;
+ QString _key;
+ bool _insecureFallback = false;
+ bool _keychainMigration = false;
+
+ QKeychain::Error _error = QKeychain::NoError;
+ QString _errorString;
+
+ int _chunkCount = 0;
+ QByteArray _chunkBuffer;
+}; // class Job
+
+/*
+* @brief: Simple wrapper class for QKeychain::WritePasswordJob, splits too large keychain entry's data into chunks on Windows
+*/
+class WriteJob : public KeychainChunk::Job {
+ Q_OBJECT
+public:
+ WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent = nullptr);
+ void start();
+
+signals:
+ void finished(KeychainChunk::WriteJob *incomingJob);
+
+private slots:
+ void slotWriteJobDone(QKeychain::Job *incomingJob);
+}; // class WriteJob
+
+/*
+* @brief: Simple wrapper class for QKeychain::ReadPasswordJob, splits too large keychain entry's data into chunks on Windows
+*/
+class ReadJob : public KeychainChunk::Job {
+ Q_OBJECT
+public:
+ ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent = nullptr);
+ void start();
+
+signals:
+ void finished(KeychainChunk::ReadJob *incomingJob);
+
+private slots:
+ void slotReadJobDone(QKeychain::Job *incomingJob);
+}; // class ReadJob
+
+} // namespace KeychainChunk
+
+} // namespace OCC
+
+#endif // KEYCHAINCHUNK_H
diff --git a/src/gui/creds/webflowcredentials.cpp b/src/gui/creds/webflowcredentials.cpp
index 008e119f3..4993a0dd0 100644
--- a/src/gui/creds/webflowcredentials.cpp
+++ b/src/gui/creds/webflowcredentials.cpp
@@ -18,6 +18,7 @@
#include "theme.h"
#include "wizard/webview.h"
#include "webflowcredentialsdialog.h"
+#include "keychainchunk.h"
using namespace QKeychain;
@@ -75,6 +76,7 @@ private:
QPointer<const WebFlowCredentials> _cred;
};
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job)
{
Q_UNUSED(account)
@@ -82,6 +84,7 @@ static void addSettingsToJob(Account *account, QKeychain::Job *job)
settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release());
}
+#endif
WebFlowCredentials::WebFlowCredentials()
: _ready(false)
@@ -170,6 +173,7 @@ void WebFlowCredentials::askFromUser() {
_askDialog->show();
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
+ connect(_askDialog, &WebFlowCredentialsDialog::onClose, this, &WebFlowCredentials::slotAskFromUserCancelled);
});
job->start();
@@ -205,10 +209,18 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
emit asked();
_askDialog->close();
- delete _askDialog;
+ _askDialog->deleteLater();
_askDialog = nullptr;
}
+void WebFlowCredentials::slotAskFromUserCancelled() {
+ qCDebug(lcWebFlowCredentials()) << "User cancelled reauth!";
+
+ emit asked();
+
+ _askDialog->deleteLater();
+ _askDialog = nullptr;
+}
bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
@@ -229,86 +241,32 @@ void WebFlowCredentials::persist() {
// write cert if there is one
if (!_clientSslCertificate.isNull()) {
- WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
- job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC, _account->id()));
- job->setBinaryData(_clientSslCertificate.toPem());
+ auto *job = new KeychainChunk::WriteJob(_account,
+ _user + clientCertificatePEMC,
+ _clientSslCertificate.toPem());
+ connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
job->start();
} else {
// no cert, just write credentials
- slotWriteClientCertPEMJobDone();
+ slotWriteClientCertPEMJobDone(nullptr);
}
}
-void WebFlowCredentials::slotWriteClientCertPEMJobDone()
+void WebFlowCredentials::slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
+ if(writeJob)
+ writeJob->deleteLater();
+
// write ssl key if there is one
if (!_clientSslKey.isNull()) {
- // Windows workaround: Split the private key into chunks of 2048 bytes,
- // to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
- _clientSslKeyChunkBufferPEM = _clientSslKey.toPem();
- _clientSslKeyChunkCount = 0;
-
- writeSingleClientKeyChunkPEM(nullptr);
- } else {
- // no key, just write credentials
- slotWriteClientKeyPEMJobDone();
- }
-}
-
-void WebFlowCredentials::writeSingleClientKeyChunkPEM(QKeychain::Job *incomingJob)
-{
- // errors?
- if (incomingJob) {
- WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
-
- if (writeJob->error() != NoError) {
- qCWarning(lcWebFlowCredentials) << "Error while writing client CA key chunk" << writeJob->errorString();
-
- _clientSslKeyChunkBufferPEM.clear();
- }
- }
-
- // write a key chunk if there is any in the buffer
- if (!_clientSslKeyChunkBufferPEM.isEmpty()) {
-#if defined(Q_OS_WIN)
- // Windows workaround: Split the private key into chunks of 2048 bytes,
- // to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
- auto chunk = _clientSslKeyChunkBufferPEM.left(_clientSslKeyChunkSize);
-
- _clientSslKeyChunkBufferPEM = _clientSslKeyChunkBufferPEM.right(_clientSslKeyChunkBufferPEM.size() - chunk.size());
-#else
- // write full key in one slot on non-Windows, as usual
- auto chunk = _clientSslKeyChunkBufferPEM;
-
- _clientSslKeyChunkBufferPEM.clear();
-#endif
- auto index = (_clientSslKeyChunkCount++);
-
- // keep the limit
- if (_clientSslKeyChunkCount > _clientSslKeyMaxChunks) {
- qCWarning(lcWebFlowCredentials) << "Maximum client key chunk count exceeded while writing slot" << QString::number(index) << "cutting off after" << QString::number(_clientSslKeyMaxChunks) << "chunks";
-
- _clientSslKeyChunkBufferPEM.clear();
-
- slotWriteClientKeyPEMJobDone();
- return;
- }
-
- WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- connect(job, &Job::finished, this, &WebFlowCredentials::writeSingleClientKeyChunkPEM);
- // only add the key's (sub)"index" after the first element, to stay compatible with older versions and non-Windows
- job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC + (index > 0 ? (QString(".") + QString::number(index)) : QString()), _account->id()));
- job->setBinaryData(chunk);
+ auto *job = new KeychainChunk::WriteJob(_account,
+ _user + clientKeyPEMC,
+ _clientSslKey.toPem());
+ connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
job->start();
-
- chunk.clear();
} else {
- slotWriteClientKeyPEMJobDone();
+ // no key, just write credentials
+ slotWriteClientKeyPEMJobDone(nullptr);
}
}
@@ -331,20 +289,21 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
return;
}
- WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
- job->setKey(keychainKey(_account->url().toString(), _user + clientCaCertificatePEMC + QString::number(index), _account->id()));
- job->setBinaryData(cert.toPem());
+ auto *job = new KeychainChunk::WriteJob(_account,
+ _user + clientCaCertificatePEMC + QString::number(index),
+ cert.toPem());
+ connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
job->start();
} else {
slotWriteClientCaCertsPEMJobDone(nullptr);
}
}
-void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
+void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
+ if(writeJob)
+ writeJob->deleteLater();
+
_clientSslCaCertificatesWriteQueue.clear();
// write ca certs if there are any
@@ -359,16 +318,16 @@ void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
}
}
-void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob)
+void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
// errors / next ca cert?
- if (incomingJob && !_clientSslCaCertificates.isEmpty()) {
- WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
-
+ if (writeJob && !_clientSslCaCertificates.isEmpty()) {
if (writeJob->error() != NoError) {
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
}
+ writeJob->deleteLater();
+
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// next ca cert
writeSingleClientCaCertPEM();
@@ -378,7 +337,9 @@ void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomi
// done storing ca certs, time for the password
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
+#endif
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
@@ -428,6 +389,10 @@ void WebFlowCredentials::forgetSensitiveData() {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setInsecureFallback(false);
job->setKey(kck);
+ connect(job, &Job::finished, this, [](QKeychain::Job *job) {
+ DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
+ djob->deleteLater();
+ });
job->start();
invalidateToken();
@@ -478,29 +443,23 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
void WebFlowCredentials::fetchFromKeychainHelper() {
// Read client cert from keychain
- const QString kck = keychainKey(
- _account->url().toString(),
- _user + clientCertificatePEMC,
- _keychainMigration ? QString() : _account->id());
-
- ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
+ auto *job = new KeychainChunk::ReadJob(_account,
+ _user + clientCertificatePEMC,
+ _keychainMigration);
+ connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
job->start();
}
-void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob)
+void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
- Q_ASSERT(!incomingJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
- if (_retryOnKeyChainError && (incomingJob->error() == QKeychain::NoBackendAvailable
- || incomingJob->error() == QKeychain::OtherError)) {
+ Q_ASSERT(!readJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
+ if (_retryOnKeyChainError && (readJob->error() == QKeychain::NoBackendAvailable
+ || readJob->error() == QKeychain::OtherError)) {
// Could be that the backend was not yet available. Wait some extra seconds.
// (Issues #4274 and #6522)
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
- qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << incomingJob->errorString();
+ qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << readJob->errorString();
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
_retryOnKeyChainError = false;
return;
@@ -509,7 +468,6 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJo
#endif
// Store PEM in memory
- ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
if (sslCertificateList.length() >= 1) {
@@ -517,79 +475,40 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJo
}
}
- // Load key too
- _clientSslKeyChunkCount = 0;
- _clientSslKeyChunkBufferPEM.clear();
-
- const QString kck = keychainKey(
- _account->url().toString(),
- _user + clientKeyPEMC,
- _keychainMigration ? QString() : _account->id());
+ readJob->deleteLater();
- ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
+ // Load key too
+ auto *job = new KeychainChunk::ReadJob(_account,
+ _user + clientKeyPEMC,
+ _keychainMigration);
+ connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
job->start();
}
-void WebFlowCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob)
+void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob)
{
- // Errors or next key chunk?
- ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
-
- if (readJob) {
- if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
- _clientSslKeyChunkBufferPEM.append(readJob->binaryData());
- _clientSslKeyChunkCount++;
-
-#if defined(Q_OS_WIN)
- // try to fetch next chunk
- if (_clientSslKeyChunkCount < _clientSslKeyMaxChunks) {
- const QString kck = keychainKey(
- _account->url().toString(),
- _user + clientKeyPEMC + QString(".") + QString::number(_clientSslKeyChunkCount),
- _keychainMigration ? QString() : _account->id());
-
- ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
- job->start();
-
- return;
- } else {
- qCWarning(lcWebFlowCredentials) << "Maximum client key chunk count reached, ignoring after" << _clientSslKeyMaxChunks;
- }
-#endif
- } else {
- if (readJob->error() != QKeychain::Error::EntryNotFound ||
- ((readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslKeyChunkCount == 0)) {
- qCWarning(lcWebFlowCredentials) << "Unable to read client key chunk slot" << QString::number(_clientSslKeyChunkCount) << readJob->errorString();
- }
- }
- }
-
// Store key in memory
- if (_clientSslKeyChunkBufferPEM.size() > 0) {
+ if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
+ QByteArray clientKeyPEM = readJob->binaryData();
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
// load whatever we have. So we try until it works.
- _clientSslKey = QSslKey(_clientSslKeyChunkBufferPEM, QSsl::Rsa);
+ _clientSslKey = QSslKey(clientKeyPEM, QSsl::Rsa);
if (_clientSslKey.isNull()) {
- _clientSslKey = QSslKey(_clientSslKeyChunkBufferPEM, QSsl::Dsa);
+ _clientSslKey = QSslKey(clientKeyPEM, QSsl::Dsa);
}
if (_clientSslKey.isNull()) {
- _clientSslKey = QSslKey(_clientSslKeyChunkBufferPEM, QSsl::Ec);
+ _clientSslKey = QSslKey(clientKeyPEM, QSsl::Ec);
}
if (_clientSslKey.isNull()) {
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
}
- // clear key chunk buffer, but don't set _clientSslKeyChunkCount to zero because we need it for deleteKeychainEntries
- _clientSslKeyChunkBufferPEM.clear();
+ clientKeyPEM.clear();
+ } else {
+ qCWarning(lcWebFlowCredentials) << "Unable to read client key" << readJob->errorString();
}
+ readJob->deleteLater();
+
// Start fetching client CA certs
_clientSslCaCertificates.clear();
@@ -600,16 +519,10 @@ void WebFlowCredentials::readSingleClientCaCertPEM()
{
// try to fetch a client ca cert
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
- const QString kck = keychainKey(
- _account->url().toString(),
- _user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
- _keychainMigration ? QString() : _account->id());
-
- ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
- addSettingsToJob(_account, job);
- job->setInsecureFallback(false);
- job->setKey(kck);
- connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
+ auto *job = new KeychainChunk::ReadJob(_account,
+ _user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
+ _keychainMigration);
+ connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
job->start();
} else {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after" << _clientSslCaCertificatesMaxCount;
@@ -618,10 +531,8 @@ void WebFlowCredentials::readSingleClientCaCertPEM()
}
}
-void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomingJob) {
- // Store key in memory
- ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
-
+void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob) {
+ // Store cert in memory
if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
@@ -629,6 +540,8 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
_clientSslCaCertificates.append(sslCertificateList.at(0));
}
+ readJob->deleteLater();
+
// try next cert
readSingleClientCaCertPEM();
return;
@@ -638,6 +551,8 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot" << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
}
}
+
+ readJob->deleteLater();
}
// Now fetch the actual server password
@@ -647,7 +562,9 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
_keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
+#endif
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
@@ -655,7 +572,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
}
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
- QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
+ QKeychain::ReadPasswordJob *job = qobject_cast<ReadPasswordJob *>(incomingJob);
QKeychain::Error error = job->error();
// If we could not find the entry try the old entries
@@ -678,6 +595,8 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
}
emit fetched();
+ job->deleteLater();
+
// If keychain data was read from legacy location, wipe these entries and store new ones
if (_keychainMigration && _ready) {
_keychainMigration = false;
@@ -688,13 +607,20 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
}
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
- auto startDeleteJob = [this, oldKeychainEntries](QString user) {
+ auto startDeleteJob = [this, oldKeychainEntries](QString key) {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
+#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
+#endif
job->setInsecureFallback(false);
job->setKey(keychainKey(_account->url().toString(),
- user,
+ key,
oldKeychainEntries ? QString() : _account->id()));
+
+ connect(job, &Job::finished, this, [](QKeychain::Job *job) {
+ DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
+ djob->deleteLater();
+ });
job->start();
};
@@ -719,9 +645,17 @@ void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
}
#if defined(Q_OS_WIN)
- // also delete key sub-chunks (Windows workaround)
- for (auto i = 1; i < _clientSslKeyChunkCount; i++) {
- startDeleteJob(_user + clientKeyPEMC + QString(".") + QString::number(i));
+ // Also delete key / cert sub-chunks (Windows workaround)
+ // The first chunk (0) has no suffix, to stay compatible with older versions and non-Windows
+ for (auto chunk = 1; chunk < KeychainChunk::MaxChunks; chunk++) {
+ const QString strChunkSuffix = QString(".") + QString::number(chunk);
+
+ startDeleteJob(_user + clientKeyPEMC + strChunkSuffix);
+ startDeleteJob(_user + clientCertificatePEMC + strChunkSuffix);
+
+ for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
+ startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
+ }
}
#endif
// FIXME MS@2019-12-07 -->
@@ -729,4 +663,4 @@ void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
// <-- FIXME MS@2019-12-07
}
-}
+} // namespace OCC
diff --git a/src/gui/creds/webflowcredentials.h b/src/gui/creds/webflowcredentials.h
index bb09bf57c..4b2414b01 100644
--- a/src/gui/creds/webflowcredentials.h
+++ b/src/gui/creds/webflowcredentials.h
@@ -19,6 +19,11 @@ namespace QKeychain {
namespace OCC {
+namespace KeychainChunk {
+ class ReadJob;
+ class WriteJob;
+}
+
class WebFlowCredentialsDialog;
class WebFlowCredentials : public AbstractCredentials
@@ -61,15 +66,16 @@ private slots:
void slotFinished(QNetworkReply *reply);
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
+ void slotAskFromUserCancelled();
- void slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob);
- void slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob);
- void slotReadClientCaCertsPEMJobDone(QKeychain::Job *incommingJob);
+ void slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob);
+ void slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob);
+ void slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob);
void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
- void slotWriteClientCertPEMJobDone();
- void slotWriteClientKeyPEMJobDone();
- void slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob);
+ void slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob);
+ void slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob);
+ void slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteJobDone(QKeychain::Job *);
private:
@@ -91,19 +97,6 @@ private:
static constexpr int _clientSslCaCertificatesMaxCount = 10;
QQueue<QSslCertificate> _clientSslCaCertificatesWriteQueue;
- /*
- * Workaround: ...and this time only on Windows:
- *
- * Split the private key into chunks of 2048 bytes,
- * to allow 4k (4096 bit) keys to be saved (see limits above)
- */
- void writeSingleClientKeyChunkPEM(QKeychain::Job *incomingJob);
-
- static constexpr int _clientSslKeyChunkSize = 2048;
- static constexpr int _clientSslKeyMaxChunks = 10;
- int _clientSslKeyChunkCount = 0;
- QByteArray _clientSslKeyChunkBufferPEM;
-
protected:
/** Reads data from keychain locations
*
@@ -134,6 +127,6 @@ protected:
WebFlowCredentialsDialog *_askDialog;
};
-}
+} // namespace OCC
#endif // WEBFLOWCREDENTIALS_H
diff --git a/src/gui/creds/webflowcredentialsdialog.cpp b/src/gui/creds/webflowcredentialsdialog.cpp
index 9971f8f34..ea8d29e79 100644
--- a/src/gui/creds/webflowcredentialsdialog.cpp
+++ b/src/gui/creds/webflowcredentialsdialog.cpp
@@ -4,6 +4,9 @@
#include <QLabel>
#include "theme.h"
+#include "application.h"
+#include "owncloudgui.h"
+#include "headerbanner.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/webview.h"
#include "wizard/flow2authwidget.h"
@@ -19,31 +22,59 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlo
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_layout = new QVBoxLayout(this);
+ int spacing = _layout->spacing();
+ int margin = _layout->margin();
+ _layout->setSpacing(0);
+ _layout->setMargin(0);
+
+ if(_useFlow2) {
+ _headerBanner = new HeaderBanner(this);
+ _layout->addWidget(_headerBanner);
+ Theme *theme = Theme::instance();
+ _headerBanner->setup(tr("Log in"), theme->wizardHeaderLogo(), theme->wizardHeaderBanner(),
+ Qt::AutoText, QString::fromLatin1("color:#fff;"));
+ }
+
+ _containerLayout = new QVBoxLayout(this);
+ _containerLayout->setSpacing(spacing);
+ _containerLayout->setMargin(margin);
- //QString msg = tr("You have been logged out of %1 as user %2, please login again")
- // .arg(_account->displayName(), _user);
_infoLabel = new QLabel();
- _layout->addWidget(_infoLabel);
+ _containerLayout->addWidget(_infoLabel);
if (_useFlow2) {
- _flow2AuthWidget = new Flow2AuthWidget(account);
- _layout->addWidget(_flow2AuthWidget);
+ _flow2AuthWidget = new Flow2AuthWidget();
+ _containerLayout->addWidget(_flow2AuthWidget);
+
+ connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &WebFlowCredentialsDialog::slotFlow2AuthResult);
+
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &WebFlowCredentialsDialog::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
- connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
+ // allow Flow2 page to poll on window activation
+ connect(this, &WebFlowCredentialsDialog::onActivate, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
+
+ _flow2AuthWidget->startAuth(account);
} else {
_webView = new WebView();
- _layout->addWidget(_webView);
+ _containerLayout->addWidget(_webView);
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
}
+ auto app = static_cast<Application *>(qApp);
+ connect(app, &Application::isShowingSettingsDialog, this, &WebFlowCredentialsDialog::slotShowSettingsDialog);
+
_errorLabel = new QLabel();
_errorLabel->hide();
- _layout->addWidget(_errorLabel);
+ _containerLayout->addWidget(_errorLabel);
WizardCommon::initErrorLabel(_errorLabel);
+ _layout->addLayout(_containerLayout);
setLayout(_layout);
+
+ customizeStyle();
}
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
@@ -52,11 +83,17 @@ void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
if (_webView) {
// Force calling WebView::~WebView() earlier so that _profile and _page are
// deleted in the correct order.
- delete _webView;
+ _webView->deleteLater();
+ _webView = nullptr;
}
- if (_flow2AuthWidget)
- delete _flow2AuthWidget;
+ if (_flow2AuthWidget) {
+ _flow2AuthWidget->resetAuth();
+ _flow2AuthWidget->deleteLater();
+ _flow2AuthWidget = nullptr;
+ }
+
+ emit onClose();
}
void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
@@ -69,6 +106,9 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
}
void WebFlowCredentialsDialog::setError(const QString &error) {
+ // bring window to top
+ slotShowSettingsDialog();
+
if (_useFlow2 && _flow2AuthWidget) {
_flow2AuthWidget->setError(error);
return;
@@ -82,4 +122,49 @@ void WebFlowCredentialsDialog::setError(const QString &error) {
}
}
+void WebFlowCredentialsDialog::changeEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::ThemeChange:
+ customizeStyle();
+
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+ break;
+ case QEvent::ActivationChange:
+ if(isActiveWindow())
+ emit onActivate();
+ break;
+ default:
+ break;
+ }
+
+ QDialog::changeEvent(e);
}
+
+void WebFlowCredentialsDialog::customizeStyle()
+{
+ // HINT: Customize dialog's own style here, if necessary in the future (Dark-/Light-Mode switching)
+}
+
+void WebFlowCredentialsDialog::slotShowSettingsDialog()
+{
+ // bring window to top but slightly delay, to avoid being hidden behind the SettingsDialog
+ QTimer::singleShot(100, this, [this] {
+ ownCloudGui::raiseDialog(this);
+ });
+}
+
+void WebFlowCredentialsDialog::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
+{
+ if(r == Flow2Auth::LoggedIn) {
+ emit urlCatched(user, appPassword, QString());
+ } else {
+ // bring window to top
+ slotShowSettingsDialog();
+ }
+}
+
+} // namespace OCC
diff --git a/src/gui/creds/webflowcredentialsdialog.h b/src/gui/creds/webflowcredentialsdialog.h
index a540f0edb..50ed7088d 100644
--- a/src/gui/creds/webflowcredentialsdialog.h
+++ b/src/gui/creds/webflowcredentialsdialog.h
@@ -5,12 +5,14 @@
#include <QUrl>
#include "accountfwd.h"
+#include "creds/flow2auth.h"
class QLabel;
class QVBoxLayout;
namespace OCC {
+class HeaderBanner;
class WebView;
class Flow2AuthWidget;
@@ -30,11 +32,21 @@ public:
protected:
void closeEvent(QCloseEvent * e) override;
+ void changeEvent(QEvent *) override;
+
+public slots:
+ void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
+ void slotShowSettingsDialog();
signals:
void urlCatched(const QString user, const QString pass, const QString host);
+ void styleChanged();
+ void onActivate();
+ void onClose();
private:
+ void customizeStyle();
+
bool _useFlow2;
Flow2AuthWidget *_flow2AuthWidget;
@@ -43,8 +55,10 @@ private:
QLabel *_errorLabel;
QLabel *_infoLabel;
QVBoxLayout *_layout;
+ QVBoxLayout *_containerLayout;
+ HeaderBanner *_headerBanner;
};
-}
+} // namespace OCC
#endif // WEBFLOWCREDENTIALSDIALOG_H
diff --git a/src/gui/folderstatusdelegate.cpp b/src/gui/folderstatusdelegate.cpp
index 337530da9..4b8899a05 100644
--- a/src/gui/folderstatusdelegate.cpp
+++ b/src/gui/folderstatusdelegate.cpp
@@ -40,7 +40,7 @@ namespace OCC {
FolderStatusDelegate::FolderStatusDelegate()
: QStyledItemDelegate()
{
- m_moreIcon = QIcon(QLatin1String(":/client/resources/more.svg"));
+ customizeStyle();
}
QString FolderStatusDelegate::addFolderText()
@@ -273,6 +273,11 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
rect.setHeight(texts.count() * subFm.height() + 2 * margin);
rect.setRight(option.rect.right() - margin);
+ // save previous state to not mess up colours with the background (fixes issue: https://github.com/nextcloud/desktop/issues/1237)
+ auto oldBrush = painter->brush();
+ auto oldPen = painter->pen();
+ auto oldFont = painter->font();
+
painter->setBrush(color);
painter->setPen(QColor(0xaa, 0xaa, 0xaa));
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
@@ -290,6 +295,11 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
textRect.translate(0, textRect.height());
}
+ // restore previous state
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+ painter->setFont(oldFont);
+
h = rect.bottom() + margin;
};
@@ -349,7 +359,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonVisualRect;
- btnOpt.icon = m_moreIcon;
+ btnOpt.icon = _iconMore;
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
btnOpt.iconSize = QSize(e,e);
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
@@ -423,5 +433,14 @@ QRect FolderStatusDelegate::errorsListRect(QRect within)
return within;
}
+void FolderStatusDelegate::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void FolderStatusDelegate::customizeStyle()
+{
+ _iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
+}
} // namespace OCC
diff --git a/src/gui/folderstatusdelegate.h b/src/gui/folderstatusdelegate.h
index b4902abf0..342be330a 100644
--- a/src/gui/folderstatusdelegate.h
+++ b/src/gui/folderstatusdelegate.h
@@ -26,7 +26,6 @@ class FolderStatusDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
- QIcon m_moreIcon;
FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100,
@@ -62,9 +61,16 @@ public:
static QRect errorsListRect(QRect within);
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
+public slots:
+ void slotStyleChanged();
+
private:
+ void customizeStyle();
+
static QString addFolderText();
QPersistentModelIndex _pressedIndex;
+
+ QIcon _iconMore;
};
} // namespace OCC
diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp
index 62381d36d..632ae43f8 100644
--- a/src/gui/generalsettings.cpp
+++ b/src/gui/generalsettings.cpp
@@ -57,6 +57,13 @@ GeneralSettings::GeneralSettings(QWidget *parent)
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
+ // Rename 'Explorer' appropriately on non-Windows
+#ifdef Q_OS_MAC
+ QString txt = _ui->showInExplorerNavigationPaneCheckBox->text();
+ txt.replace(QString::fromLatin1("Explorer"), QString::fromLatin1("Finder"));
+ _ui->showInExplorerNavigationPaneCheckBox->setText(txt);
+#endif
+
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
@@ -70,7 +77,8 @@ GeneralSettings::GeneralSettings(QWidget *parent)
connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice);
loadMiscSettings();
- slotUpdateInfo();
+ // updater info now set in: customizeStyle
+ //slotUpdateInfo();
// misc
connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings);
@@ -109,6 +117,8 @@ GeneralSettings::GeneralSettings(QWidget *parent)
// accountAdded means the wizard was finished and the wizard might change some options.
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
+
+ customizeStyle();
}
GeneralSettings::~GeneralSettings()
@@ -149,7 +159,11 @@ void GeneralSettings::slotUpdateInfo()
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
- _ui->updateStateLabel->setText(updater->statusString());
+
+ QString status = updater->statusString();
+ Theme::replaceLinkColorStringBackgroundAware(status);
+ _ui->updateStateLabel->setText(status);
+
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
} else {
// can't have those infos from sparkle currently
@@ -211,4 +225,20 @@ void GeneralSettings::slotShowLegalNotice()
delete notice;
}
+void GeneralSettings::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void GeneralSettings::customizeStyle()
+{
+ // setup about section
+ QString about = Theme::instance()->about();
+ Theme::replaceLinkColorStringBackgroundAware(about);
+ _ui->aboutLabel->setText(about);
+
+ // updater info
+ slotUpdateInfo();
+}
+
} // namespace OCC
diff --git a/src/gui/generalsettings.h b/src/gui/generalsettings.h
index 12abf3660..f7a759161 100644
--- a/src/gui/generalsettings.h
+++ b/src/gui/generalsettings.h
@@ -39,6 +39,9 @@ public:
~GeneralSettings();
QSize sizeHint() const override;
+public slots:
+ void slotStyleChanged();
+
private slots:
void saveMiscSettings();
void slotToggleLaunchOnStartup(bool);
@@ -50,6 +53,8 @@ private slots:
void slotShowLegalNotice();
private:
+ void customizeStyle();
+
Ui::GeneralSettings *_ui;
QPointer<IgnoreListEditor> _ignoreEditor;
QPointer<SyncLogDialog> _syncLogDialog;
diff --git a/src/gui/headerbanner.cpp b/src/gui/headerbanner.cpp
new file mode 100644
index 000000000..4c425750f
--- /dev/null
+++ b/src/gui/headerbanner.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) by Michael Schuster <michael@nextcloud.com>
+ *
+ * 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.
+ */
+
+/****************************************************************************
+**
+** Based on Qt sourcecode:
+** qt5/qtbase/src/widgets/dialogs/qwizard.cpp
+**
+** https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/dialogs/qwizard.cpp?h=v5.13.0
+**
+** Original license:
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "headerbanner.h"
+
+#include <QVBoxLayout>
+#include <QLabel>
+
+#include <QPainter>
+#include <QStyle>
+#include <QGuiApplication>
+
+namespace OCC {
+
+// These fudge terms were needed a few places to obtain pixel-perfect results
+const int GapBetweenLogoAndRightEdge = 5;
+const int ModernHeaderTopMargin = 2;
+
+HeaderBanner::HeaderBanner(QWidget *parent)
+ : QWidget(parent)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ setBackgroundRole(QPalette::Base);
+ titleLabel = new QLabel(this);
+ titleLabel->setBackgroundRole(QPalette::Base);
+ logoLabel = new QLabel(this);
+ QFont font = titleLabel->font();
+ font.setBold(true);
+ titleLabel->setFont(font);
+ layout = new QGridLayout(this);
+ layout->setContentsMargins(QMargins());
+ layout->setSpacing(0);
+ layout->setRowMinimumHeight(3, 1);
+ layout->setRowStretch(4, 1);
+ layout->setColumnStretch(2, 1);
+ layout->setColumnMinimumWidth(4, 2 * GapBetweenLogoAndRightEdge);
+ layout->setColumnMinimumWidth(6, GapBetweenLogoAndRightEdge);
+ layout->addWidget(titleLabel, 1, 1, 5, 1);
+ layout->addWidget(logoLabel, 1, 5, 5, 1);
+}
+
+void HeaderBanner::setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
+ const Qt::TextFormat titleFormat, const QString &styleSheet)
+{
+ QStyle *style = parentWidget()->style();
+ //const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+ int topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, parentWidget());
+ int topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, parentWidget());
+ int topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, parentWidget());
+ //int topLevelMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, parentWidget());
+
+ layout->setRowMinimumHeight(0, ModernHeaderTopMargin);
+ layout->setRowMinimumHeight(1, topLevelMarginTop - ModernHeaderTopMargin - 1);
+ layout->setRowMinimumHeight(6, 3);
+ int minColumnWidth0 = topLevelMarginLeft + topLevelMarginRight;
+ int minColumnWidth1 = topLevelMarginLeft + topLevelMarginRight + 1;
+ layout->setColumnMinimumWidth(0, minColumnWidth0);
+ layout->setColumnMinimumWidth(1, minColumnWidth1);
+ titleLabel->setTextFormat(titleFormat);
+ titleLabel->setText(title);
+ if(!styleSheet.isEmpty())
+ titleLabel->setStyleSheet(styleSheet);
+ logoLabel->setPixmap(logo);
+ bannerPixmap = banner;
+ if (bannerPixmap.isNull()) {
+ QSize size = layout->totalMinimumSize();
+ setMinimumSize(size);
+ setMaximumSize(QWIDGETSIZE_MAX, size.height());
+ } else {
+ setFixedHeight(banner.height() + 2);
+ }
+ updateGeometry();
+}
+
+void HeaderBanner::paintEvent(QPaintEvent * /* event */)
+{
+ QPainter painter(this);
+ painter.drawPixmap(0, 0, width(), bannerPixmap.height(), bannerPixmap);
+ int x = width() - 2;
+ int y = height() - 2;
+ const QPalette &pal = QGuiApplication::palette();
+ painter.setPen(pal.mid().color());
+ painter.drawLine(0, y, x, y);
+ painter.setPen(pal.base().color());
+ painter.drawPoint(x + 1, y);
+ painter.drawLine(0, y + 1, x + 1, y + 1);
+}
+
+} // namespace OCC
diff --git a/src/gui/headerbanner.h b/src/gui/headerbanner.h
new file mode 100644
index 000000000..c52d73c1f
--- /dev/null
+++ b/src/gui/headerbanner.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) by Michael Schuster <michael@nextcloud.com>
+ *
+ * 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.
+ */
+
+/****************************************************************************
+**
+** Based on Qt sourcecode:
+** qt5/qtbase/src/widgets/dialogs/qwizard.cpp
+**
+** https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/dialogs/qwizard.cpp?h=v5.13.0
+**
+** Original license:
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HEADERBANNER_H
+#define HEADERBANNER_H
+
+#include <QWidget>
+
+class QLabel;
+class QGridLayout;
+class QPixmap;
+
+namespace OCC {
+
+class HeaderBanner : public QWidget
+{
+ Q_OBJECT
+public:
+ HeaderBanner(QWidget *parent = 0);
+
+ void setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
+ const Qt::TextFormat titleFormat, const QString &styleSheet);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ QLabel *titleLabel;
+ QLabel *logoLabel;
+ QGridLayout *layout;
+ QPixmap bannerPixmap;
+};
+
+} // namespace OCC
+
+#endif // HEADERBANNER_H
diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp
index c975a162e..27662f715 100644
--- a/src/gui/owncloudgui.cpp
+++ b/src/gui/owncloudgui.cpp
@@ -20,12 +20,7 @@
#include "progressdispatcher.h"
#include "owncloudsetupwizard.h"
#include "sharedialog.h"
-#if defined(Q_OS_MAC)
-#include "settingsdialogmac.h"
-#include "macwindow.h" // qtmacgoodies
-#else
#include "settingsdialog.h"
-#endif
#include "logger.h"
#include "logbrowser.h"
#include "account.h"
@@ -68,11 +63,7 @@ const char propertyAccountC[] = "oc_account";
ownCloudGui::ownCloudGui(Application *parent)
: QObject(parent)
, _tray(nullptr)
-#if defined(Q_OS_MAC)
- , _settingsDialog(new SettingsDialogMac(this))
-#else
, _settingsDialog(new SettingsDialog(this))
-#endif
, _logBrowser(nullptr)
#ifdef WITH_LIBCLOUDPROVIDERS
, _bus(QDBusConnection::sessionBus())
@@ -1090,18 +1081,18 @@ void ownCloudGui::slotShowGuiMessage(const QString &title, const QString &messag
void ownCloudGui::slotShowSettings()
{
if (_settingsDialog.isNull()) {
- _settingsDialog =
-#if defined(Q_OS_MAC)
- new SettingsDialogMac(this);
-#else
- new SettingsDialog(this);
-#endif
+ _settingsDialog = new SettingsDialog(this);
_settingsDialog->setAttribute(Qt::WA_DeleteOnClose, true);
_settingsDialog->show();
}
raiseDialog(_settingsDialog.data());
}
+void ownCloudGui::slotSettingsDialogActivated()
+{
+ emit isShowingSettingsDialog();
+}
+
void ownCloudGui::slotShowSyncProtocol()
{
slotShowSettings();
@@ -1156,10 +1147,6 @@ void ownCloudGui::raiseDialog(QWidget *raiseWidget)
raiseWidget->raise();
raiseWidget->activateWindow();
-#if defined(Q_OS_MAC)
- // viel hilft viel ;-)
- MacWindow::bringToFront(raiseWidget);
-#endif
#if defined(Q_OS_X11)
WId wid = widget->winId();
NETWM::init();
diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h
index 95d078bc6..98af71bb7 100644
--- a/src/gui/owncloudgui.h
+++ b/src/gui/owncloudgui.h
@@ -34,7 +34,6 @@ namespace OCC {
class Folder;
class SettingsDialog;
-class SettingsDialogMac;
class ShareDialog;
class Application;
class LogBrowser;
@@ -71,6 +70,7 @@ public:
signals:
void setupProxy();
void serverError(int code, const QString &message);
+ void isShowingSettingsDialog();
public slots:
void setupContextMenu();
@@ -94,6 +94,7 @@ public slots:
void slotToggleLogBrowser();
void slotOpenOwnCloud();
void slotOpenSettingsDialog();
+ void slotSettingsDialogActivated();
void slotHelp();
void slotOpenPath(const QString &path);
void slotAccountStateChanged();
@@ -131,11 +132,7 @@ private:
void buildNavigationAppsMenu(AccountStatePtr account, QMenu *accountMenu);
QPointer<Systray> _tray;
-#if defined(Q_OS_MAC)
- QPointer<SettingsDialogMac> _settingsDialog;
-#else
QPointer<SettingsDialog> _settingsDialog;
-#endif
QPointer<LogBrowser> _logBrowser;
// tray's menu
QScopedPointer<QMenu> _contextMenu;
diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp
index ec2512f85..875dacf55 100644
--- a/src/gui/owncloudsetupwizard.cpp
+++ b/src/gui/owncloudsetupwizard.cpp
@@ -407,7 +407,8 @@ void OwncloudSetupWizard::slotAuthError()
errorMsg = tr("There was an invalid response to an authenticated webdav request");
}
- _ocWizard->show();
+ // bring wizard to top
+ _ocWizard->bringToTop();
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds || _ocWizard->currentId() == WizardCommon::Page_OAuthCreds || _ocWizard->currentId() == WizardCommon::Page_Flow2AuthCreds) {
_ocWizard->back();
}
diff --git a/src/gui/settingsdialog.cpp b/src/gui/settingsdialog.cpp
index da40ffff1..a80de4da5 100644
--- a/src/gui/settingsdialog.cpp
+++ b/src/gui/settingsdialog.cpp
@@ -56,10 +56,6 @@ namespace OCC {
#include "settingsdialogcommon.cpp"
-//
-// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
-//
-
SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
: QDialog(parent)
, _ui(new Ui::SettingsDialog)
@@ -108,6 +104,9 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
GeneralSettings *generalSettings = new GeneralSettings;
_ui->stack->addWidget(generalSettings);
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &SettingsDialog::styleChanged, generalSettings, &GeneralSettings::slotStyleChanged);
+
QAction *networkAction = createColorAwareAction(QLatin1String(":/client/resources/network.png"), tr("Network"));
_actionGroup->addAction(networkAction);
_toolBar->addAction(networkAction);
@@ -128,6 +127,8 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
addAction(showLogWindow);
+ connect(this, &SettingsDialog::onActivate, gui, &ownCloudGui::slotSettingsDialogActivated);
+
customizeStyle();
cfg.restoreGeometry(this);
@@ -160,6 +161,13 @@ void SettingsDialog::changeEvent(QEvent *e)
case QEvent::PaletteChange:
case QEvent::ThemeChange:
customizeStyle();
+
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+ break;
+ case QEvent::ActivationChange:
+ if(isActiveWindow())
+ emit onActivate();
break;
default:
break;
@@ -265,6 +273,10 @@ void SettingsDialog::accountAdded(AccountState *s)
// Refresh immediatly when getting online
connect(s, &AccountState::isConnectedChanged, this, &SettingsDialog::slotRefreshActivityAccountStateSender);
+ // Connect styleChanged event, to adapt (Dark-/Light-Mode switching)
+ connect(this, &SettingsDialog::styleChanged, accountSettings, &AccountSettings::slotStyleChanged);
+ connect(this, &SettingsDialog::styleChanged, _activitySettings[s], &ActivitySettings::slotStyleChanged);
+
activityAdded(s);
slotRefreshActivity(s);
}
@@ -346,13 +358,13 @@ void SettingsDialog::accountRemoved(AccountState *s)
void SettingsDialog::customizeStyle()
{
QString highlightColor(palette().highlight().color().name());
- QString altBase(palette().alternateBase().color().name());
+ QString highlightTextColor(palette().highlightedText().color().name());
QString dark(palette().dark().color().name());
QString background(palette().base().color().name());
- _toolBar->setStyleSheet(QString::fromLatin1(TOOLBAR_CSS).arg(background, dark, highlightColor, altBase));
+ _toolBar->setStyleSheet(QString::fromLatin1(TOOLBAR_CSS).arg(background, dark, highlightColor, highlightTextColor));
Q_FOREACH (QAction *a, _actionGroup->actions()) {
- QIcon icon = createColorAwareIcon(a->property("iconPath").toString());
+ QIcon icon = Theme::createColorAwareIcon(a->property("iconPath").toString(), palette());
a->setIcon(icon);
QToolButton *btn = qobject_cast<QToolButton *>(_toolBar->widgetForAction(a));
if (btn)
@@ -360,34 +372,6 @@ void SettingsDialog::customizeStyle()
}
}
-static bool isDarkColor(const QColor &color)
-{
- // account for different sensitivity of the human eye to certain colors
- double treshold = 1.0 - (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255.0;
- return treshold > 0.5;
-}
-
-QIcon SettingsDialog::createColorAwareIcon(const QString &name)
-{
- QPalette pal = palette();
- QImage img(name);
- QImage inverted(img);
- inverted.invertPixels(QImage::InvertRgb);
-
- QIcon icon;
- if (isDarkColor(pal.color(QPalette::Base))) {
- icon.addPixmap(QPixmap::fromImage(inverted));
- } else {
- icon.addPixmap(QPixmap::fromImage(img));
- }
- if (isDarkColor(pal.color(QPalette::HighlightedText))) {
- icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal, QIcon::On);
- } else {
- icon.addPixmap(QPixmap::fromImage(inverted), QIcon::Normal, QIcon::On);
- }
- return icon;
-}
-
class ToolButtonAction : public QWidgetAction
{
public:
@@ -428,7 +412,7 @@ QAction *SettingsDialog::createActionWithIcon(const QIcon &icon, const QString &
QAction *SettingsDialog::createColorAwareAction(const QString &iconPath, const QString &text)
{
// all buttons must have the same size in order to keep a good layout
- QIcon coloredIcon = createColorAwareIcon(iconPath);
+ QIcon coloredIcon = Theme::createColorAwareIcon(iconPath, palette());
return createActionWithIcon(coloredIcon, text, iconPath);
}
diff --git a/src/gui/settingsdialog.h b/src/gui/settingsdialog.h
index 97d15c03c..f5ec654d9 100644
--- a/src/gui/settingsdialog.h
+++ b/src/gui/settingsdialog.h
@@ -63,6 +63,10 @@ public slots:
void slotAccountAvatarChanged();
void slotAccountDisplayNameChanged();
+signals:
+ void styleChanged();
+ void onActivate();
+
protected:
void reject() override;
void accept() override;
@@ -76,7 +80,6 @@ private:
void customizeStyle();
void activityAdded(AccountState *);
- QIcon createColorAwareIcon(const QString &name);
QAction *createColorAwareAction(const QString &iconName, const QString &fileName);
QAction *createActionWithIcon(const QIcon &icon, const QString &text, const QString &iconPath = QString());
diff --git a/src/gui/settingsdialogmac.cpp b/src/gui/settingsdialogmac.cpp
deleted file mode 100644
index 938dd6b45..000000000
--- a/src/gui/settingsdialogmac.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) by Denis Dzyubenko
- *
- * 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 "settingsdialogmac.h"
-
-#include "macstandardicon.h"
-
-#include "folderman.h"
-#include "theme.h"
-#include "generalsettings.h"
-#include "networksettings.h"
-#include "accountsettings.h"
-#include "accountstate.h"
-#include "creds/abstractcredentials.h"
-#include "configfile.h"
-#include "progressdispatcher.h"
-#include "owncloudgui.h"
-#include "activitywidget.h"
-#include "accountmanager.h"
-
-#include <QLabel>
-#include <QStandardItemModel>
-#include <QPushButton>
-#include <QSettings>
-#include <QPainter>
-#include <QPainterPath>
-
-namespace OCC {
-
-#include "settingsdialogcommon.cpp"
-
-
-// Duplicate in settingsdialog.cpp
-static QIcon circleMask(const QImage &avatar)
-{
- int dim = avatar.width();
-
- QPixmap fixedImage(dim, dim);
- fixedImage.fill(Qt::transparent);
-
- QPainter imgPainter(&fixedImage);
- QPainterPath clip;
- clip.addEllipse(0, 0, dim, dim);
- imgPainter.setClipPath(clip);
- imgPainter.drawImage(0, 0, avatar);
- imgPainter.end();
-
- return QIcon(fixedImage);
-}
-
-
-//
-// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
-//
-SettingsDialogMac::SettingsDialogMac(ownCloudGui *gui, QWidget *parent)
- : MacPreferencesWindow(parent)
- , _gui(gui)
-{
- // do not show minimize button. There is no use, and restoring the
- // dialog from minimize is broken in MacPreferencesWindow
- setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint);
-
- // Emulate dialog behavior: Escape means close
- QAction *closeDialogAction = new QAction(this);
- closeDialogAction->setShortcut(QKeySequence(Qt::Key_Escape));
- connect(closeDialogAction, &QAction::triggered, this, &SettingsDialogMac::close);
- addAction(closeDialogAction);
- // People perceive this as a Window, so also make Ctrl+W work
- QAction *closeWindowAction = new QAction(this);
- closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
- connect(closeWindowAction, &QAction::triggered, this, &SettingsDialogMac::close);
- addAction(closeWindowAction);
- // People perceive this as a Window, so also make Ctrl+H work
- QAction *hideWindowAction = new QAction(this);
- hideWindowAction->setShortcut(QKeySequence("Ctrl+H"));
- connect(hideWindowAction, &QAction::triggered, this, &SettingsDialogMac::hide);
- addAction(hideWindowAction);
-
- setObjectName("SettingsMac"); // required as group for saveGeometry call
-
- setWindowTitle(tr("%1").arg(Theme::instance()->appNameGUI()));
-
- connect(AccountManager::instance(), &AccountManager::accountAdded,
- this, &SettingsDialogMac::accountAdded);
- connect(AccountManager::instance(), &AccountManager::accountRemoved,
- this, &SettingsDialogMac::accountRemoved);
-
- _actionsIdx = -1;
- foreach (auto ai, AccountManager::instance()->accounts()) {
- accountAdded(ai.data());
- }
-
- QIcon generalIcon = MacStandardIcon::icon(MacStandardIcon::PreferencesGeneral);
- GeneralSettings *generalSettings = new GeneralSettings;
- addPreferencesPanel(generalIcon, tr("General"), generalSettings);
-
- QIcon networkIcon = MacStandardIcon::icon(MacStandardIcon::Network);
- NetworkSettings *networkSettings = new NetworkSettings;
- addPreferencesPanel(networkIcon, tr("Network"), networkSettings);
-
- QAction *showLogWindow = new QAction(this);
- showLogWindow->setShortcut(QKeySequence("F12"));
- connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
- addAction(showLogWindow);
-
- ConfigFile cfg;
- cfg.restoreGeometry(this);
-}
-
-void SettingsDialogMac::closeEvent(QCloseEvent *event)
-{
- ConfigFile cfg;
- cfg.saveGeometry(this);
- MacPreferencesWindow::closeEvent(event);
-}
-
-void SettingsDialogMac::showActivityPage()
-{
- // Count backwards (0-based) from the last panel (multiple accounts can be on the left)
- setCurrentPanelIndex(preferencePanelCount() - 1 - 2);
-}
-
-void SettingsDialogMac::accountAdded(AccountState *s)
-{
- QIcon accountIcon = MacStandardIcon::icon(MacStandardIcon::UserAccounts);
- auto accountSettings = new AccountSettings(s, this);
- QString displayName = Theme::instance()->multiAccount() ? SettingsDialogCommon::shortDisplayNameForSettings(s->account().data(), 0) : tr("Account");
-
- // this adds the panel - nothing to add here just to fix the order
- insertPreferencesPanel(++_actionsIdx, accountIcon, displayName, accountSettings);
-
- connect(accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged);
- connect(accountSettings, &AccountSettings::openFolderAlias, _gui, &ownCloudGui::slotFolderOpenAction);
-
- connect(s->account().data(), &Account::accountChangedAvatar, this, &SettingsDialogMac::slotAccountAvatarChanged);
- connect(s->account().data(), &Account::accountChangedDisplayName, this, &SettingsDialogMac::slotAccountDisplayNameChanged);
-
- // Refresh immediatly when getting online
- connect(s, &AccountState::isConnectedChanged, this, &SettingsDialogMac::slotRefreshActivityAccountStateSender);
-
- // Add activity panel
- QIcon activityIcon(QLatin1String(":/client/resources/activity.png"));
- _activitySettings[s] = new ActivitySettings(s, this);
- insertPreferencesPanel(++_actionsIdx, activityIcon, tr("Activity"), _activitySettings[s]);
- connect(_activitySettings[s], SIGNAL(guiLog(QString, QString)), _gui,
- SLOT(slotShowOptionalTrayMessage(QString, QString)));
-
- // if this is not the first account, add separator 2 positions before int the toolbar
- if(AccountManager::instance()->accounts().first().data() != s &&
- AccountManager::instance()->accounts().size() >= 1){
- _separators[s] = insertSeparator(_actionsIdx - 1);
- ++_actionsIdx; //we have one more item in the toolbar
- }
-
- ConfigFile cfg;
- _activitySettings[s]->setNotificationRefreshInterval(cfg.notificationRefreshInterval());
-
- slotRefreshActivity(s);
- setCurrentPanelIndex(0);
-}
-
-void SettingsDialogMac::accountRemoved(AccountState *s)
-{
- auto list = findChildren<AccountSettings *>(QString());
- foreach (auto p, list) {
- if (p->accountsState() == s) {
- removePreferencesPanel(p);
-
- // remove settings panel
- if(_activitySettings.contains(s))
- removePreferencesPanel(_activitySettings[s]);
-
- // remove separator if there is any
- if(_separators.contains(s)){
- removeSeparator(_separators[s]);
- _separators.remove(s);
- }
- }
- }
-}
-
-void SettingsDialogMac::slotRefreshActivityAccountStateSender()
-{
- slotRefreshActivity(qobject_cast<AccountState*>(sender()));
-}
-
-void SettingsDialogMac::slotRefreshActivity(AccountState *accountState)
-{
- if (accountState) {
- _activitySettings[accountState]->slotRefresh();
- }
-}
-
-void SettingsDialogMac::slotAccountAvatarChanged()
-{
- Account *account = static_cast<Account *>(sender());
- auto list = findChildren<AccountSettings *>(QString());
- foreach (auto p, list) {
- if (p->accountsState()->account() == account) {
- int idx = indexForPanel(p);
- QImage pix = account->avatar();
- if (!pix.isNull()) {
- setPreferencesPanelIcon(idx, circleMask(pix));
- }
- }
- }
-}
-
-void SettingsDialogMac::slotAccountDisplayNameChanged()
-{
- Account *account = static_cast<Account *>(sender());
- auto list = findChildren<AccountSettings *>(QString());
- foreach (auto p, list) {
- if (p->accountsState()->account() == account) {
- int idx = indexForPanel(p);
- QString displayName = account->displayName();
- if (!displayName.isNull()) {
- displayName = Theme::instance()->multiAccount()
- ? SettingsDialogCommon::shortDisplayNameForSettings(account, 0)
- : tr("Account");
- setPreferencesPanelTitle(idx, displayName);
- }
- }
- }
-}
-
-}
-
diff --git a/src/gui/settingsdialogmac.h b/src/gui/settingsdialogmac.h
deleted file mode 100644
index ef0995b53..000000000
--- a/src/gui/settingsdialogmac.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) by Denis Dzyubenko
- *
- * 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 SETTINGSDIALOGMAC_H
-#define SETTINGSDIALOGMAC_H
-
-#include "progressdispatcher.h"
-#include "macpreferenceswindow.h"
-#include "owncloudgui.h"
-
-class QStandardItemModel;
-class QListWidgetItem;
-
-namespace OCC {
-
-class AccountSettings;
-class Application;
-class FolderMan;
-class ownCloudGui;
-class Folder;
-class AccountState;
-class ActivitySettings;
-
-/**
- * @brief The SettingsDialogMac class
- * @ingroup gui
- */
-class SettingsDialogMac : public MacPreferencesWindow
-{
- Q_OBJECT
-
-public:
- explicit SettingsDialogMac(ownCloudGui *gui, QWidget *parent = 0);
-
-public slots:
- void showActivityPage();
- void slotRefreshActivity(AccountState *accountState);
- void slotRefreshActivityAccountStateSender();
-
-private slots:
- void accountAdded(AccountState *);
- void accountRemoved(AccountState *);
- void slotAccountAvatarChanged();
- void slotAccountDisplayNameChanged();
-
-private:
- void closeEvent(QCloseEvent *event);
-
- QAction *_actionBefore;
- int _actionsIdx;
- QMap<AccountState *, QAction *> _separators;
-
- QMap<AccountState *, ActivitySettings *> _activitySettings;
- ownCloudGui *_gui;
-
- int _protocolIdx;
-};
-}
-
-#endif // SETTINGSDIALOGMAC_H
-;
diff --git a/src/gui/sharedialog.cpp b/src/gui/sharedialog.cpp
index 435de4a0c..544e0e723 100644
--- a/src/gui/sharedialog.cpp
+++ b/src/gui/sharedialog.cpp
@@ -156,6 +156,9 @@ void ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare)
connect(_linkWidgetList.at(index), &ShareLinkWidget::deleteLinkShare, this, &ShareDialog::slotDeleteShare);
//connect(_linkWidgetList.at(index), &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
+ // Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
+ connect(this, &ShareDialog::styleChanged, _linkWidgetList.at(index), &ShareLinkWidget::slotStyleChanged);
+
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _linkWidgetList.at(index));
_linkWidgetList.at(index)->setupUiOptions();
}
@@ -281,6 +284,10 @@ void ShareDialog::showSharingUi()
if (userGroupSharing) {
_userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, this);
+
+ // Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
+ connect(this, &ShareDialog::styleChanged, _userGroupWidget, &ShareUserGroupWidget::slotStyleChanged);
+
_ui->verticalLayout->insertWidget(1, _userGroupWidget);
_userGroupWidget->getShares();
}
@@ -334,4 +341,21 @@ void ShareDialog::slotAccountStateChanged(int state)
}
}
}
+
+void ShareDialog::changeEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::ThemeChange:
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+ break;
+ default:
+ break;
+ }
+
+ QDialog::changeEvent(e);
+}
+
}
diff --git a/src/gui/sharedialog.h b/src/gui/sharedialog.h
index dbd0ca775..17d156437 100644
--- a/src/gui/sharedialog.h
+++ b/src/gui/sharedialog.h
@@ -68,6 +68,10 @@ private slots:
signals:
void toggleAnimation(bool);
+ void styleChanged();
+
+protected:
+ void changeEvent(QEvent *) override;
private:
void showSharingUi();
diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp
index b201871dc..62ed18da8 100644
--- a/src/gui/sharelinkwidget.cpp
+++ b/src/gui/sharelinkwidget.cpp
@@ -19,6 +19,7 @@
#include "capabilities.h"
#include "guiutility.h"
#include "sharemanager.h"
+#include "theme.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@@ -256,6 +257,8 @@ void ShareLinkWidget::setupUiOptions()
//TO DO
//startAnimation(0, height());
+
+ customizeStyle();
}
void ShareLinkWidget::setNote(const QString &note)
@@ -549,4 +552,29 @@ void ShareLinkWidget::displayError(const QString &errMsg)
_ui->errorLabel->setText(errMsg);
_ui->errorLabel->show();
}
+
+void ShareLinkWidget::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void ShareLinkWidget::customizeStyle()
+{
+ _unshareLinkAction->setIcon(Theme::createColorAwareIcon(":/client/resources/delete.png"));
+
+ _addAnotherLinkAction->setIcon(Theme::createColorAwareIcon(":/client/resources/add.png"));
+
+ _ui->enableShareLink->setIcon(Theme::createColorAwareIcon(":/client/resources/copy.svg"));
+
+ _ui->shareLinkIconLabel->setPixmap(Theme::createColorAwarePixmap(":/client/resources/public.svg"));
+
+ _ui->shareLinkToolButton->setIcon(Theme::createColorAwareIcon(":/client/resources/more.svg"));
+
+ _ui->confirmNote->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
+ _ui->confirmPassword->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
+ _ui->confirmExpirationDate->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
+
+ _ui->progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
+}
+
}
diff --git a/src/gui/sharelinkwidget.h b/src/gui/sharelinkwidget.h
index 34b2b3c3f..451687879 100644
--- a/src/gui/sharelinkwidget.h
+++ b/src/gui/sharelinkwidget.h
@@ -65,6 +65,7 @@ public slots:
void slotToggleAnimation(bool start);
void slotServerError(int code, const QString &message);
void slotCreateShareRequiresPassword(const QString &message);
+ void slotStyleChanged();
private slots:
void slotCreateShareLink(bool clicked);
@@ -114,6 +115,8 @@ private:
void startAnimation(const int start, const int end);
+ void customizeStyle();
+
Ui::ShareLinkWidget *_ui;
AccountPtr _account;
QString _sharePath;
diff --git a/src/gui/shareusergroupwidget.cpp b/src/gui/shareusergroupwidget.cpp
index 82a3425f1..0a58d61a5 100644
--- a/src/gui/shareusergroupwidget.cpp
+++ b/src/gui/shareusergroupwidget.cpp
@@ -26,6 +26,7 @@
#include "thumbnailjob.h"
#include "sharee.h"
#include "sharemanager.h"
+#include "theme.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@@ -114,6 +115,8 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
//_ui->shareeHorizontalLayout->addWidget(&_pi_sharee);
_parentScrollArea = parentWidget()->findChild<QScrollArea*>("scrollArea");
+
+ customizeStyle();
}
ShareUserGroupWidget::~ShareUserGroupWidget()
@@ -214,6 +217,10 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
s->setBackgroundRole(layout->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase);
+
+ // Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
+ connect(this, &ShareUserGroupWidget::styleChanged, s, &ShareUserLine::slotStyleChanged);
+
layout->addWidget(s);
x++;
@@ -258,7 +265,8 @@ void ShareUserGroupWidget::slotPrivateLinkShare()
auto menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
- menu->addAction(QIcon(":/client/resources/copy.svg"),
+ // this icon is not handled by slotStyleChanged() -> customizeStyle but we can live with that
+ menu->addAction(Theme::createColorAwareIcon(":/client/resources/copy.svg"),
tr("Copy link"),
this, SLOT(slotPrivateLinkCopy()));
@@ -361,6 +369,25 @@ void ShareUserGroupWidget::slotPrivateLinkEmail()
this);
}
+void ShareUserGroupWidget::slotStyleChanged()
+{
+ customizeStyle();
+
+ // Notify the other widgets (ShareUserLine in this case, Dark-/Light-Mode switching)
+ emit styleChanged();
+}
+
+void ShareUserGroupWidget::customizeStyle()
+{
+ _ui->confirmShare->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
+
+ _pi_sharee.setColor(QGuiApplication::palette().color(QPalette::Text));
+
+ foreach (auto pi, _parentScrollArea->findChildren<QProgressIndicator *>()) {
+ pi->setColor(QGuiApplication::palette().color(QPalette::Text));;
+ }
+}
+
ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
SharePermissions maxSharingPermissions,
bool isFile,
@@ -426,8 +453,9 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
_ui->permissionToolButton->setMenu(menu);
_ui->permissionToolButton->setPopupMode(QToolButton::InstantPopup);
- QIcon icon(QLatin1String(":/client/resources/more.svg"));
- _ui->permissionToolButton->setIcon(icon);
+ // icon now set in: customizeStyle
+ /*QIcon icon(QLatin1String(":/client/resources/more.svg"));
+ _ui->permissionToolButton->setIcon(icon);*/
// Set the permissions checkboxes
displayPermissions();
@@ -454,6 +482,8 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
}
loadAvatar();
+
+ customizeStyle();
}
void ShareUserLine::loadAvatar()
@@ -648,4 +678,18 @@ void ShareUserLine::displayPermissions()
_permissionDelete->setChecked(perm & SharePermissionDelete);
}
}
+
+void ShareUserLine::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void ShareUserLine::customizeStyle()
+{
+ _ui->permissionToolButton->setIcon(Theme::createColorAwareIcon(":/client/resources/more.svg"));
+
+ QIcon deleteicon = QIcon::fromTheme(QLatin1String("user-trash"),Theme::createColorAwareIcon(QLatin1String(":/client/resources/delete.png")));
+ _deleteShareButton->setIcon(deleteicon);
+}
+
}
diff --git a/src/gui/shareusergroupwidget.h b/src/gui/shareusergroupwidget.h
index a1f537f05..f60edaedb 100644
--- a/src/gui/shareusergroupwidget.h
+++ b/src/gui/shareusergroupwidget.h
@@ -64,9 +64,11 @@ public:
signals:
void togglePublicLinkShare(bool);
+ void styleChanged();
public slots:
void getShares();
+ void slotStyleChanged();
private slots:
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
@@ -88,6 +90,8 @@ private slots:
void slotPrivateLinkEmail();
private:
+ void customizeStyle();
+
Ui::ShareUserGroupWidget *_ui;
QScrollArea *_parentScrollArea;
AccountPtr _account;
@@ -127,6 +131,9 @@ signals:
void visualDeletionDone();
void resizeRequested();
+public slots:
+ void slotStyleChanged();
+
private slots:
void on_deleteShareButton_clicked();
void slotPermissionsChanged();
@@ -141,6 +148,7 @@ private slots:
private:
void displayPermissions();
void loadAvatar();
+ void customizeStyle();
Ui::ShareUserLine *_ui;
QSharedPointer<Share> _share;
diff --git a/src/gui/sslbutton.cpp b/src/gui/sslbutton.cpp
index 98db319e6..0efdc6f7c 100644
--- a/src/gui/sslbutton.cpp
+++ b/src/gui/sslbutton.cpp
@@ -142,7 +142,7 @@ QMenu *SslButton::buildCertMenu(QMenu *parent, const QSslCertificate &cert,
// create label first
QLabel *label = new QLabel(parent);
- label->setStyleSheet(QLatin1String("QLabel { padding: 8px; background-color: #fff; }"));
+ label->setStyleSheet(QLatin1String("QLabel { padding: 8px; }"));
label->setTextFormat(Qt::RichText);
label->setText(details);
diff --git a/src/gui/wizard/flow2authcredspage.cpp b/src/gui/wizard/flow2authcredspage.cpp
index 85bcb89dc..d04d9741e 100644
--- a/src/gui/wizard/flow2authcredspage.cpp
+++ b/src/gui/wizard/flow2authcredspage.cpp
@@ -14,40 +14,39 @@
*/
#include <QVariant>
-#include <QMenu>
-#include <QClipboard>
+#include <QVBoxLayout>
-#include "wizard/flow2authcredspage.h"
+#include "flow2authcredspage.h"
#include "theme.h"
#include "account.h"
#include "cookiejar.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/owncloudwizard.h"
+#include "wizard/flow2authwidget.h"
#include "creds/credentialsfactory.h"
#include "creds/webflowcredentials.h"
namespace OCC {
Flow2AuthCredsPage::Flow2AuthCredsPage()
- : AbstractCredentialsWizardPage()
+ : AbstractCredentialsWizardPage(),
+ _flow2AuthWidget(nullptr)
{
- _ui.setupUi(this);
-
- Theme *theme = Theme::instance();
- _ui.topLabel->hide();
- _ui.bottomLabel->hide();
- QVariant variant = theme->customMedia(Theme::oCSetupTop);
- WizardCommon::setupCustomMedia(variant, _ui.topLabel);
- variant = theme->customMedia(Theme::oCSetupBottom);
- WizardCommon::setupCustomMedia(variant, _ui.bottomLabel);
-
- WizardCommon::initErrorLabel(_ui.errorLabel);
+ _layout = new QVBoxLayout(this);
setTitle(WizardCommon::titleTemplate().arg(tr("Connect to %1").arg(Theme::instance()->appNameGUI())));
setSubTitle(WizardCommon::subTitleTemplate().arg(tr("Login in your browser (Login Flow v2)")));
- connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotOpenBrowser);
- connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotCopyLinkToClipboard);
+ _flow2AuthWidget = new Flow2AuthWidget();
+ _layout->addWidget(_flow2AuthWidget);
+
+ connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &Flow2AuthCredsPage::slotFlow2AuthResult);
+
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &Flow2AuthCredsPage::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
+
+ // allow Flow2 page to poll on window activation
+ connect(this, &Flow2AuthCredsPage::pollNow, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
}
void Flow2AuthCredsPage::initializePage()
@@ -55,9 +54,9 @@ void Flow2AuthCredsPage::initializePage()
OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
Q_ASSERT(ocWizard);
ocWizard->account()->setCredentials(CredentialsFactory::create("http"));
- _asyncAuth.reset(new Flow2Auth(ocWizard->account().data(), this));
- connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthCredsPage::asyncAuthResult, Qt::QueuedConnection);
- _asyncAuth->start();
+
+ if(_flow2AuthWidget)
+ _flow2AuthWidget->startAuth(ocWizard->account().data());
// Don't hide the wizard (avoid user confusion)!
//wizard()->hide();
@@ -67,21 +66,20 @@ void OCC::Flow2AuthCredsPage::cleanupPage()
{
// The next or back button was activated, show the wizard again
wizard()->show();
- _asyncAuth.reset();
+ if(_flow2AuthWidget)
+ _flow2AuthWidget->resetAuth();
// Forget sensitive data
_appPassword.clear();
_user.clear();
}
-void Flow2AuthCredsPage::asyncAuthResult(Flow2Auth::Result r, const QString &user,
- const QString &appPassword)
+void Flow2AuthCredsPage::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
{
switch (r) {
case Flow2Auth::NotSupported: {
/* Flow2Auth not supported (can't open browser) */
- _ui.errorLabel->setText(tr("Unable to open the Browser, please copy the link to your Browser."));
- _ui.errorLabel->show();
+ wizard()->show();
/* Don't fallback to HTTP credentials */
/*OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
@@ -91,7 +89,6 @@ void Flow2AuthCredsPage::asyncAuthResult(Flow2Auth::Result r, const QString &use
}
case Flow2Auth::Error:
/* Error while getting the access token. (Timeout, or the server did not accept our client credentials */
- _ui.errorLabel->show();
wizard()->show();
break;
case Flow2Auth::LoggedIn: {
@@ -113,7 +110,11 @@ int Flow2AuthCredsPage::nextId() const
void Flow2AuthCredsPage::setConnected()
{
- wizard()->show();
+ OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
+ Q_ASSERT(ocWizard);
+
+ // bring wizard to top
+ ocWizard->bringToTop();
}
AbstractCredentials *Flow2AuthCredsPage::getCredentials() const
@@ -134,19 +135,14 @@ bool Flow2AuthCredsPage::isComplete() const
return false; /* We can never go forward manually */
}
-void Flow2AuthCredsPage::slotOpenBrowser()
+void Flow2AuthCredsPage::slotPollNow()
{
- if (_ui.errorLabel)
- _ui.errorLabel->hide();
-
- if (_asyncAuth)
- _asyncAuth->openBrowser();
+ emit pollNow();
}
-void Flow2AuthCredsPage::slotCopyLinkToClipboard()
+void Flow2AuthCredsPage::slotStyleChanged()
{
- if (_asyncAuth)
- QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
+ emit styleChanged();
}
} // namespace OCC
diff --git a/src/gui/wizard/flow2authcredspage.h b/src/gui/wizard/flow2authcredspage.h
index f51d7d7f3..8a59605e0 100644
--- a/src/gui/wizard/flow2authcredspage.h
+++ b/src/gui/wizard/flow2authcredspage.h
@@ -25,11 +25,12 @@
#include "accountfwd.h"
#include "creds/flow2auth.h"
-#include "ui_flow2authcredspage.h"
-
+class QVBoxLayout;
+class QProgressIndicator;
namespace OCC {
+class Flow2AuthWidget;
class Flow2AuthCredsPage : public AbstractCredentialsWizardPage
{
@@ -46,20 +47,22 @@ public:
bool isComplete() const override;
public Q_SLOTS:
- void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
+ void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
+ void slotPollNow();
+ void slotStyleChanged();
signals:
void connectToOCUrl(const QString &);
+ void pollNow();
+ void styleChanged();
public:
QString _user;
QString _appPassword;
- QScopedPointer<Flow2Auth> _asyncAuth;
- Ui_Flow2AuthCredsPage _ui;
-protected slots:
- void slotOpenBrowser();
- void slotCopyLinkToClipboard();
+private:
+ Flow2AuthWidget *_flow2AuthWidget;
+ QVBoxLayout *_layout;
};
} // namespace OCC
diff --git a/src/gui/wizard/flow2authcredspage.ui b/src/gui/wizard/flow2authcredspage.ui
deleted file mode 100644
index 73b72b516..000000000
--- a/src/gui/wizard/flow2authcredspage.ui
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Flow2AuthCredsPage</class>
- <widget class="QWidget" name="Flow2AuthCredsPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>424</width>
- <height>373</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Browser Authentication</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QLabel" name="topLabel">
- <property name="text">
- <string notr="true">TextLabel</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Please switch to your browser to proceed.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="errorLabel">
- <property name="text">
- <string>An error occurred while connecting. Please try again.</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCommandLinkButton" name="openLinkButton">
- <property name="text">
- <string>Re-open Browser</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCommandLinkButton" name="copyLinkButton">
- <property name="font">
- <font>
- <family>Segoe UI</family>
- <weight>50</weight>
- <bold>false</bold>
- </font>
- </property>
- <property name="text">
- <string>Copy link</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>127</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="bottomLabel">
- <property name="text">
- <string notr="true">TextLabel</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/gui/wizard/flow2authwidget.cpp b/src/gui/wizard/flow2authwidget.cpp
index c050a54d0..498a9803e 100644
--- a/src/gui/wizard/flow2authwidget.cpp
+++ b/src/gui/wizard/flow2authwidget.cpp
@@ -14,53 +14,66 @@
#include "flow2authwidget.h"
-#include <QDesktopServices>
-#include <QProgressBar>
-#include <QLoggingCategory>
-#include <QLocale>
-#include <QMessageBox>
-
-#include <QMenu>
-#include <QClipboard>
-
#include "common/utility.h"
#include "account.h"
-#include "theme.h"
#include "wizard/owncloudwizardcommon.h"
+#include "QProgressIndicator.h"
+
namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2AuthWidget, "gui.wizard.flow2authwidget", QtInfoMsg)
-Flow2AuthWidget::Flow2AuthWidget(Account *account, QWidget *parent)
- : QWidget(parent),
- _account(account),
- _ui()
+Flow2AuthWidget::Flow2AuthWidget(QWidget *parent)
+ : QWidget(parent)
+ , _account(nullptr)
+ , _ui()
+ , _progressIndi(new QProgressIndicator(this))
+ , _statusUpdateSkipCount(0)
{
_ui.setupUi(this);
- Theme *theme = Theme::instance();
- _ui.topLabel->hide();
- _ui.bottomLabel->hide();
- QVariant variant = theme->customMedia(Theme::oCSetupTop);
- WizardCommon::setupCustomMedia(variant, _ui.topLabel);
- variant = theme->customMedia(Theme::oCSetupBottom);
- WizardCommon::setupCustomMedia(variant, _ui.bottomLabel);
-
WizardCommon::initErrorLabel(_ui.errorLabel);
+ _ui.errorLabel->setTextFormat(Qt::RichText);
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotOpenBrowser);
connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotCopyLinkToClipboard);
- _asyncAuth.reset(new Flow2Auth(_account, this));
- connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthWidget::asyncAuthResult, Qt::QueuedConnection);
- _asyncAuth->start();
+ _ui.horizontalLayout->addWidget(_progressIndi);
+ stopSpinner(false);
+
+ customizeStyle();
}
-void Flow2AuthWidget::asyncAuthResult(Flow2Auth::Result r, const QString &user,
- const QString &appPassword)
+void Flow2AuthWidget::startAuth(Account *account)
{
+ Flow2Auth *oldAuth = _asyncAuth.take();
+ if(oldAuth)
+ oldAuth->deleteLater();
+
+ _statusUpdateSkipCount = 0;
+
+ if(account) {
+ _account = account;
+
+ _asyncAuth.reset(new Flow2Auth(_account, this));
+ connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthWidget::slotAuthResult, Qt::QueuedConnection);
+ connect(_asyncAuth.data(), &Flow2Auth::statusChanged, this, &Flow2AuthWidget::slotStatusChanged);
+ connect(this, &Flow2AuthWidget::pollNow, _asyncAuth.data(), &Flow2Auth::slotPollNow);
+ _asyncAuth->start();
+ }
+}
+
+void Flow2AuthWidget::resetAuth(Account *account)
+{
+ startAuth(account);
+}
+
+void Flow2AuthWidget::slotAuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
+{
+ stopSpinner(false);
+
switch (r) {
case Flow2Auth::NotSupported:
/* Flow2Auth can't open browser */
@@ -69,15 +82,16 @@ void Flow2AuthWidget::asyncAuthResult(Flow2Auth::Result r, const QString &user,
break;
case Flow2Auth::Error:
/* Error while getting the access token. (Timeout, or the server did not accept our client credentials */
+ _ui.errorLabel->setText(errorString);
_ui.errorLabel->show();
break;
case Flow2Auth::LoggedIn: {
- _user = user;
- _appPassword = appPassword;
- emit urlCatched(_user, _appPassword, QString());
+ _ui.errorLabel->hide();
break;
}
}
+
+ emit authResult(r, errorString, user, appPassword);
}
void Flow2AuthWidget::setError(const QString &error) {
@@ -90,11 +104,8 @@ void Flow2AuthWidget::setError(const QString &error) {
}
Flow2AuthWidget::~Flow2AuthWidget() {
- _asyncAuth.reset();
-
// Forget sensitive data
- _appPassword.clear();
- _user.clear();
+ _asyncAuth.reset();
}
void Flow2AuthWidget::slotOpenBrowser()
@@ -108,8 +119,79 @@ void Flow2AuthWidget::slotOpenBrowser()
void Flow2AuthWidget::slotCopyLinkToClipboard()
{
+ if (_ui.errorLabel)
+ _ui.errorLabel->hide();
+
if (_asyncAuth)
- QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
+ _asyncAuth->copyLinkToClipboard();
+}
+
+void Flow2AuthWidget::slotPollNow()
+{
+ emit pollNow();
+}
+
+void Flow2AuthWidget::slotStatusChanged(Flow2Auth::PollStatus status, int secondsLeft)
+{
+ switch(status)
+ {
+ case Flow2Auth::statusPollCountdown:
+ if(_statusUpdateSkipCount > 0) {
+ _statusUpdateSkipCount--;
+ break;
+ }
+ _ui.statusLabel->setText(tr("Waiting for authorization") + QString("… (%1)").arg(secondsLeft));
+ stopSpinner(true);
+ break;
+ case Flow2Auth::statusPollNow:
+ _statusUpdateSkipCount = 0;
+ _ui.statusLabel->setText(tr("Polling for authorization") + "…");
+ startSpinner();
+ break;
+ case Flow2Auth::statusFetchToken:
+ _statusUpdateSkipCount = 0;
+ _ui.statusLabel->setText(tr("Starting authorization") + "…");
+ startSpinner();
+ break;
+ case Flow2Auth::statusCopyLinkToClipboard:
+ _ui.statusLabel->setText(tr("Link copied to clipboard."));
+ _statusUpdateSkipCount = 3;
+ stopSpinner(true);
+ break;
+ }
+}
+
+void Flow2AuthWidget::startSpinner()
+{
+ _ui.horizontalLayout->setEnabled(true);
+ _ui.statusLabel->setVisible(true);
+ _progressIndi->setVisible(true);
+ _progressIndi->startAnimation();
+
+ _ui.openLinkButton->setEnabled(false);
+ _ui.copyLinkButton->setEnabled(false);
+}
+
+void Flow2AuthWidget::stopSpinner(bool showStatusLabel)
+{
+ _ui.horizontalLayout->setEnabled(false);
+ _ui.statusLabel->setVisible(showStatusLabel);
+ _progressIndi->setVisible(false);
+ _progressIndi->stopAnimation();
+
+ _ui.openLinkButton->setEnabled(_statusUpdateSkipCount == 0);
+ _ui.copyLinkButton->setEnabled(_statusUpdateSkipCount == 0);
+}
+
+void Flow2AuthWidget::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void Flow2AuthWidget::customizeStyle()
+{
+ if(_progressIndi)
+ _progressIndi->setColor(QGuiApplication::palette().color(QPalette::Text));
}
} // namespace OCC
diff --git a/src/gui/wizard/flow2authwidget.h b/src/gui/wizard/flow2authwidget.h
index 7fe1844c1..609791d9b 100644
--- a/src/gui/wizard/flow2authwidget.h
+++ b/src/gui/wizard/flow2authwidget.h
@@ -22,35 +22,49 @@
#include "ui_flow2authwidget.h"
+class QProgressIndicator;
+
namespace OCC {
class Flow2AuthWidget : public QWidget
{
Q_OBJECT
public:
- Flow2AuthWidget(Account *account, QWidget *parent = nullptr);
+ Flow2AuthWidget(QWidget *parent = nullptr);
virtual ~Flow2AuthWidget();
+ void startAuth(Account *account);
+ void resetAuth(Account *account = nullptr);
void setError(const QString &error);
public Q_SLOTS:
- void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
+ void slotAuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
+ void slotPollNow();
+ void slotStatusChanged(Flow2Auth::PollStatus status, int secondsLeft);
+ void slotStyleChanged();
signals:
- void urlCatched(const QString user, const QString pass, const QString host);
+ void authResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
+ void pollNow();
private:
Account *_account;
- QString _user;
- QString _appPassword;
QScopedPointer<Flow2Auth> _asyncAuth;
Ui_Flow2AuthWidget _ui;
protected slots:
void slotOpenBrowser();
void slotCopyLinkToClipboard();
+
+private:
+ void startSpinner();
+ void stopSpinner(bool showStatusLabel);
+ void customizeStyle();
+
+ QProgressIndicator *_progressIndi;
+ int _statusUpdateSkipCount;
};
-}
+} // namespace OCC
#endif // FLOW2AUTHWIDGET_H
diff --git a/src/gui/wizard/flow2authwidget.ui b/src/gui/wizard/flow2authwidget.ui
index 2e4eb3b48..57f23186d 100644
--- a/src/gui/wizard/flow2authwidget.ui
+++ b/src/gui/wizard/flow2authwidget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>500</width>
- <height>280</height>
+ <width>580</width>
+ <height>330</height>
</rect>
</property>
<property name="sizePolicy">
@@ -27,22 +27,6 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QLabel" name="topLabel">
- <property name="text">
- <string notr="true">TextLabel</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please switch to your browser to proceed.</string>
@@ -73,7 +57,6 @@
<widget class="QCommandLinkButton" name="copyLinkButton">
<property name="font">
<font>
- <family>Segoe UI</family>
<weight>50</weight>
<bold>false</bold>
</font>
@@ -84,28 +67,44 @@
</widget>
</item>
<item>
- <spacer name="verticalSpacer">
+ <spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
- <height>127</height>
+ <height>20</height>
</size>
</property>
</spacer>
</item>
<item>
- <widget class="QLabel" name="bottomLabel">
+ <widget class="QLabel" name="statusLabel">
<property name="text">
<string notr="true">TextLabel</string>
</property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
</property>
</widget>
</item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout"/>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>107</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<resources/>
diff --git a/src/gui/wizard/owncloudadvancedsetuppage.cpp b/src/gui/wizard/owncloudadvancedsetuppage.cpp
index 7cfb79644..03cfb0661 100644
--- a/src/gui/wizard/owncloudadvancedsetuppage.cpp
+++ b/src/gui/wizard/owncloudadvancedsetuppage.cpp
@@ -389,4 +389,15 @@ QString OwncloudAdvancedSetupPage::checkLocalSpace(qint64 remoteSize) const
return (availableLocalSpace()>remoteSize) ? QString() : tr("There isn't enough free space in the local folder!");
}
+void OwncloudAdvancedSetupPage::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void OwncloudAdvancedSetupPage::customizeStyle()
+{
+ if(_progressIndi)
+ _progressIndi->setColor(QGuiApplication::palette().color(QPalette::Text));
+}
+
} // namespace OCC
diff --git a/src/gui/wizard/owncloudadvancedsetuppage.h b/src/gui/wizard/owncloudadvancedsetuppage.h
index 9ebf9fc70..e3d328a18 100644
--- a/src/gui/wizard/owncloudadvancedsetuppage.h
+++ b/src/gui/wizard/owncloudadvancedsetuppage.h
@@ -51,6 +51,7 @@ signals:
public slots:
void setErrorString(const QString &);
+ void slotStyleChanged();
private slots:
void slotSelectFolder();
@@ -67,6 +68,7 @@ private:
QUrl serverUrl() const;
qint64 availableLocalSpace() const;
QString checkLocalSpace(qint64 remoteSize) const;
+ void customizeStyle();
Ui_OwncloudAdvancedSetupPage _ui;
bool _checking;
diff --git a/src/gui/wizard/owncloudhttpcredspage.cpp b/src/gui/wizard/owncloudhttpcredspage.cpp
index 265d3bf3c..092bf789d 100644
--- a/src/gui/wizard/owncloudhttpcredspage.cpp
+++ b/src/gui/wizard/owncloudhttpcredspage.cpp
@@ -194,5 +194,15 @@ AbstractCredentials *OwncloudHttpCredsPage::getCredentials() const
return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text(), _ocWizard->_clientSslCertificate, _ocWizard->_clientSslKey);
}
+void OwncloudHttpCredsPage::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void OwncloudHttpCredsPage::customizeStyle()
+{
+ if(_progressIndi)
+ _progressIndi->setColor(QGuiApplication::palette().color(QPalette::Text));
+}
} // namespace OCC
diff --git a/src/gui/wizard/owncloudhttpcredspage.h b/src/gui/wizard/owncloudhttpcredspage.h
index 83fc55ad4..adc46117d 100644
--- a/src/gui/wizard/owncloudhttpcredspage.h
+++ b/src/gui/wizard/owncloudhttpcredspage.h
@@ -46,10 +46,14 @@ public:
Q_SIGNALS:
void connectToOCUrl(const QString &);
+public slots:
+ void slotStyleChanged();
+
private:
void startSpinner();
void stopSpinner();
void setupCustomization();
+ void customizeStyle();
Ui_OwncloudHttpCredsPage _ui;
bool _connected;
diff --git a/src/gui/wizard/owncloudsetuppage.cpp b/src/gui/wizard/owncloudsetuppage.cpp
index fb2499c86..84f160aaf 100644
--- a/src/gui/wizard/owncloudsetuppage.cpp
+++ b/src/gui/wizard/owncloudsetuppage.cpp
@@ -89,29 +89,15 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
connect(_ui.nextButton, &QPushButton::clicked, _ui.slideShow, &SlideShow::nextSlide);
connect(_ui.prevButton, &QPushButton::clicked, _ui.slideShow, &SlideShow::prevSlide);
- auto widgetBgLightness = OwncloudSetupPage::palette().color(OwncloudSetupPage::backgroundRole()).lightness();
- bool widgetHasDarkBg =
- (widgetBgLightness >= 125)
- ? false
- : true;
- _ui.nextButton->setIcon(theme->uiThemeIcon(QString("control-next.svg"), widgetHasDarkBg));
- _ui.prevButton->setIcon(theme->uiThemeIcon(QString("control-prev.svg"), widgetHasDarkBg));
-
- // QPushButtons are a mess when it comes to consistent background coloring without stylesheets,
- // so we do it here even though this is an exceptional styling method here
- _ui.createAccountButton->setStyleSheet("QPushButton {background-color: #0082C9; color: white}");
-
_ui.slideShow->startShow();
-
- QPalette pal = _ui.slideShow->palette();
- pal.setColor(QPalette::WindowText, theme->wizardHeaderBackgroundColor());
- _ui.slideShow->setPalette(pal);
#else
_ui.createAccountButton->hide();
_ui.loginButton->hide();
_ui.installLink->hide();
_ui.slideShow->hide();
#endif
+
+ customizeStyle();
}
void OwncloudSetupPage::setServerUrl(const QString &newUrl)
@@ -434,4 +420,31 @@ OwncloudSetupPage::~OwncloudSetupPage()
{
}
+void OwncloudSetupPage::slotStyleChanged()
+{
+ customizeStyle();
+}
+
+void OwncloudSetupPage::customizeStyle()
+{
+#ifdef WITH_PROVIDERS
+ Theme *theme = Theme::instance();
+
+ bool widgetHasDarkBg = Theme::isDarkColor(QGuiApplication::palette().base().color());
+ _ui.nextButton->setIcon(theme->uiThemeIcon(QString("control-next.svg"), widgetHasDarkBg));
+ _ui.prevButton->setIcon(theme->uiThemeIcon(QString("control-prev.svg"), widgetHasDarkBg));
+
+ // QPushButtons are a mess when it comes to consistent background coloring without stylesheets,
+ // so we do it here even though this is an exceptional styling method here
+ _ui.createAccountButton->setStyleSheet("QPushButton {background-color: #0082C9; color: white}");
+
+ QPalette pal = _ui.slideShow->palette();
+ pal.setColor(QPalette::WindowText, theme->wizardHeaderBackgroundColor());
+ _ui.slideShow->setPalette(pal);
+#endif
+
+ if(_progressIndi)
+ _progressIndi->setColor(QGuiApplication::palette().color(QPalette::Text));
+}
+
} // namespace OCC
diff --git a/src/gui/wizard/owncloudsetuppage.h b/src/gui/wizard/owncloudsetuppage.h
index 0bcea0450..6e7ed93b0 100644
--- a/src/gui/wizard/owncloudsetuppage.h
+++ b/src/gui/wizard/owncloudsetuppage.h
@@ -62,6 +62,7 @@ public slots:
void startSpinner();
void stopSpinner();
void slotCertificateAccepted();
+ void slotStyleChanged();
protected slots:
void slotUrlChanged(const QString &);
@@ -78,6 +79,7 @@ signals:
private:
bool urlHasChanged();
+ void customizeStyle();
Ui_OwncloudSetupPage _ui;
diff --git a/src/gui/wizard/owncloudwizard.cpp b/src/gui/wizard/owncloudwizard.cpp
index e4dda0fbd..2076136ff 100644
--- a/src/gui/wizard/owncloudwizard.cpp
+++ b/src/gui/wizard/owncloudwizard.cpp
@@ -16,6 +16,7 @@
#include "account.h"
#include "configfile.h"
#include "theme.h"
+#include "owncloudgui.h"
#include "wizard/owncloudwizard.h"
#include "wizard/owncloudsetuppage.h"
@@ -100,6 +101,17 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
setTitleFormat(Qt::RichText);
setSubTitleFormat(Qt::RichText);
setButtonText(QWizard::CustomButton1, tr("Skip folders configuration"));
+
+
+ // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
+ connect(this, &OwncloudWizard::styleChanged, _setupPage, &OwncloudSetupPage::slotStyleChanged);
+ connect(this, &OwncloudWizard::styleChanged, _advancedSetupPage, &OwncloudAdvancedSetupPage::slotStyleChanged);
+ connect(this, &OwncloudWizard::styleChanged, _flow2CredsPage, &Flow2AuthCredsPage::slotStyleChanged);
+
+ customizeStyle();
+
+ // allow Flow2 page to poll on window activation
+ connect(this, &OwncloudWizard::onActivate, _flow2CredsPage, &Flow2AuthCredsPage::slotPollNow);
}
void OwncloudWizard::setAccount(AccountPtr account)
@@ -277,4 +289,37 @@ AbstractCredentials *OwncloudWizard::getCredentials() const
return nullptr;
}
+void OwncloudWizard::changeEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::StyleChange:
+ case QEvent::PaletteChange:
+ case QEvent::ThemeChange:
+ customizeStyle();
+
+ // Notify the other widgets (Dark-/Light-Mode switching)
+ emit styleChanged();
+ break;
+ case QEvent::ActivationChange:
+ if(isActiveWindow())
+ emit onActivate();
+ break;
+ default:
+ break;
+ }
+
+ QWizard::changeEvent(e);
+}
+
+void OwncloudWizard::customizeStyle()
+{
+ // HINT: Customize wizard's own style here, if necessary in the future (Dark-/Light-Mode switching)
+}
+
+void OwncloudWizard::bringToTop()
+{
+ // bring wizard to top
+ ownCloudGui::raiseDialog(this);
+}
+
} // end namespace
diff --git a/src/gui/wizard/owncloudwizard.h b/src/gui/wizard/owncloudwizard.h
index c1d290d4b..3cbf89f71 100644
--- a/src/gui/wizard/owncloudwizard.h
+++ b/src/gui/wizard/owncloudwizard.h
@@ -74,6 +74,8 @@ public:
void displayError(const QString &, bool retryHTTPonly);
AbstractCredentials *getCredentials() const;
+ void bringToTop();
+
// FIXME: Can those be local variables?
// Set from the OwncloudSetupPage, later used from OwncloudHttpCredsPage
QSslKey _clientSslKey;
@@ -96,8 +98,15 @@ signals:
void basicSetupFinished(int);
void skipFolderConfiguration();
void needCertificate();
+ void styleChanged();
+ void onActivate();
+
+protected:
+ void changeEvent(QEvent *) override;
private:
+ void customizeStyle();
+
AccountPtr _account;
OwncloudSetupPage *_setupPage;
OwncloudHttpCredsPage *_httpCredsPage;