mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-24 05:55:59 +03:00
Fix merge conflict
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
This commit is contained in:
commit
7653c5fa60
111 changed files with 12962 additions and 14000 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
|||
[submodule "src/3rdparty/qtmacgoodies"]
|
||||
path = src/3rdparty/qtmacgoodies
|
||||
url = https://github.com/camilasan/qtmacgoodies.git
|
||||
[submodule "binary"]
|
||||
path = binary
|
||||
url = git://github.com/owncloud/owncloud-client-binary.git
|
||||
|
|
|
@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
|
|||
|
||||
|
||||
# Translations
|
||||
Icon[de]=@APPLICATION_ICON_NAME@
|
||||
Name[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
Comment[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
GenericName[de]=Synchronisationsordner
|
||||
Icon[de_DE]=@APPLICATION_ICON_NAME@
|
||||
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
GenericName[de_DE]=Synchronisationsordner
|
||||
|
|
|
@ -198,4 +198,7 @@ X-GNOME-Autostart-Delay=3
|
|||
|
||||
|
||||
# Translations
|
||||
Icon[el]=@APPLICATION_ICON_NAME@
|
||||
Name[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
|
||||
Comment[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
|
||||
GenericName[el]=Συγχρονισμός φακέλου
|
||||
|
|
1
src/3rdparty/qtmacgoodies
vendored
1
src/3rdparty/qtmacgoodies
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit b59d091b3e6b98e7219cf636f7d21fb267242c27
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
// 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());
|
||||
if(!_pollTimer.isActive()) {
|
||||
_pollTimer.start();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
QString loginName, appPassword;
|
||||
|
||||
QUrl serverUrl = json["server"].toString();
|
||||
QString loginName = json["loginName"].toString();
|
||||
QString appPassword = json["appPassword"].toString();
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
221
src/gui/creds/keychainchunk.cpp
Normal file
221
src/gui/creds/keychainchunk.cpp
Normal file
|
@ -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
|
120
src/gui/creds/keychainchunk.h
Normal file
120
src/gui/creds/keychainchunk.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
auto *job = new KeychainChunk::WriteJob(_account,
|
||||
_user + clientKeyPEMC,
|
||||
_clientSslKey.toPem());
|
||||
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
|
||||
job->start();
|
||||
} 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);
|
||||
job->start();
|
||||
|
||||
chunk.clear();
|
||||
} else {
|
||||
slotWriteClientKeyPEMJobDone();
|
||||
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
|
|||
}
|
||||
}
|
||||
|
||||
readJob->deleteLater();
|
||||
|
||||
// Load key too
|
||||
_clientSslKeyChunkCount = 0;
|
||||
_clientSslKeyChunkBufferPEM.clear();
|
||||
|
||||
const QString kck = keychainKey(
|
||||
_account->url().toString(),
|
||||
_user + clientKeyPEMC,
|
||||
_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);
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
|
||||
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);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
146
src/gui/headerbanner.cpp
Normal file
146
src/gui/headerbanner.cpp
Normal file
|
@ -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
|
93
src/gui/headerbanner.h
Normal file
93
src/gui/headerbanner.h
Normal file
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
;
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,6 +68,10 @@ private slots:
|
|||
|
||||
signals:
|
||||
void toggleAnimation(bool);
|
||||
void styleChanged();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *) override;
|
||||
|
||||
private:
|
||||
void showSharingUi();
|
||||
|
|
|
@ -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 ¬e)
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
@ -26,22 +26,6 @@
|
|||
<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">
|
||||
|
@ -73,7 +57,6 @@
|
|||
<widget class="QCommandLinkButton" name="copyLinkButton">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI</family>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
|
@ -83,6 +66,32 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
@ -91,21 +100,11 @@
|
|||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>127</height>
|
||||
<height>107</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/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -565,4 +565,81 @@ QString Theme::versionSwitchOutput() const
|
|||
return helpText;
|
||||
}
|
||||
|
||||
bool Theme::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;
|
||||
}
|
||||
|
||||
QColor Theme::getBackgroundAwareLinkColor(const QColor &backgroundColor)
|
||||
{
|
||||
return QColor((isDarkColor(backgroundColor) ? QColor("#6193dc") : QGuiApplication::palette().color(QPalette::Link)));
|
||||
}
|
||||
|
||||
QColor Theme::getBackgroundAwareLinkColor()
|
||||
{
|
||||
return getBackgroundAwareLinkColor(QGuiApplication::palette().base().color());
|
||||
}
|
||||
|
||||
void Theme::replaceLinkColorStringBackgroundAware(QString &linkString, const QColor &backgroundColor)
|
||||
{
|
||||
replaceLinkColorString(linkString, getBackgroundAwareLinkColor(backgroundColor));
|
||||
}
|
||||
|
||||
void Theme::replaceLinkColorStringBackgroundAware(QString &linkString)
|
||||
{
|
||||
replaceLinkColorStringBackgroundAware(linkString, QGuiApplication::palette().color(QPalette::Base));
|
||||
}
|
||||
|
||||
void Theme::replaceLinkColorString(QString &linkString, const QColor &newColor)
|
||||
{
|
||||
linkString.replace(QRegularExpression("(<a href|<a style='color:#([a-zA-Z0-9]{6});' href)"), QString::fromLatin1("<a style='color:%1;' href").arg(newColor.name()));
|
||||
}
|
||||
|
||||
QIcon Theme::createColorAwareIcon(const QString &name, const QPalette &palette)
|
||||
{
|
||||
QImage img(name);
|
||||
QImage inverted(img);
|
||||
inverted.invertPixels(QImage::InvertRgb);
|
||||
|
||||
QIcon icon;
|
||||
if (Theme::isDarkColor(palette.color(QPalette::Base))) {
|
||||
icon.addPixmap(QPixmap::fromImage(inverted));
|
||||
} else {
|
||||
icon.addPixmap(QPixmap::fromImage(img));
|
||||
}
|
||||
if (Theme::isDarkColor(palette.color(QPalette::HighlightedText))) {
|
||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal, QIcon::On);
|
||||
} else {
|
||||
icon.addPixmap(QPixmap::fromImage(inverted), QIcon::Normal, QIcon::On);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
QIcon Theme::createColorAwareIcon(const QString &name)
|
||||
{
|
||||
return createColorAwareIcon(name, QGuiApplication::palette());
|
||||
}
|
||||
|
||||
QPixmap Theme::createColorAwarePixmap(const QString &name, const QPalette &palette)
|
||||
{
|
||||
QImage img(name);
|
||||
QImage inverted(img);
|
||||
inverted.invertPixels(QImage::InvertRgb);
|
||||
|
||||
QPixmap pixmap;
|
||||
if (Theme::isDarkColor(palette.color(QPalette::Base))) {
|
||||
pixmap = QPixmap::fromImage(inverted);
|
||||
} else {
|
||||
pixmap = QPixmap::fromImage(img);
|
||||
}
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
QPixmap Theme::createColorAwarePixmap(const QString &name)
|
||||
{
|
||||
return createColorAwarePixmap(name, QGuiApplication::palette());
|
||||
}
|
||||
|
||||
} // end namespace client
|
||||
|
|
|
@ -24,6 +24,7 @@ class QObject;
|
|||
class QPixmap;
|
||||
class QColor;
|
||||
class QPaintDevice;
|
||||
class QPalette;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
|
@ -355,6 +356,96 @@ public:
|
|||
* (actually 2019/09/13 only systray theming).
|
||||
*/
|
||||
virtual QIcon uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const;
|
||||
|
||||
/**
|
||||
* @brief Perform a calculation to check if a colour is dark or light and accounts for different sensitivity of the human eye.
|
||||
*
|
||||
* @return True if the specified colour is dark.
|
||||
*
|
||||
* 2019/12/08: Moved here from SettingsDialog.
|
||||
*/
|
||||
static bool isDarkColor(const QColor &color);
|
||||
|
||||
/**
|
||||
* @brief Return the colour to be used for HTML links (e.g. used in QLabel), based on the current app palette or given colour (Dark-/Light-Mode switching).
|
||||
*
|
||||
* @return Background-aware colour for HTML links, based on the current app palette or given colour.
|
||||
*
|
||||
* 2019/12/08: Implemented for the Dark Mode on macOS, because the app palette can not account for that (Qt 5.12.5).
|
||||
*/
|
||||
static QColor getBackgroundAwareLinkColor(const QColor &backgroundColor);
|
||||
|
||||
/**
|
||||
* @brief Return the colour to be used for HTML links (e.g. used in QLabel), based on the current app palette (Dark-/Light-Mode switching).
|
||||
*
|
||||
* @return Background-aware colour for HTML links, based on the current app palette.
|
||||
*
|
||||
* 2019/12/08: Implemented for the Dark Mode on macOS, because the app palette can not account for that (Qt 5.12.5).
|
||||
*/
|
||||
static QColor getBackgroundAwareLinkColor();
|
||||
|
||||
/**
|
||||
* @brief Appends a CSS-style colour value to all HTML link tags in a given string, based on the current app palette or given colour (Dark-/Light-Mode switching).
|
||||
*
|
||||
* 2019/12/08: Implemented for the Dark Mode on macOS, because the app palette can not account for that (Qt 5.12.5).
|
||||
*
|
||||
* This way we also avoid having certain strings re-translated on Transifex.
|
||||
*/
|
||||
static void replaceLinkColorStringBackgroundAware(QString &linkString, const QColor &backgroundColor);
|
||||
|
||||
/**
|
||||
* @brief Appends a CSS-style colour value to all HTML link tags in a given string, based on the current app palette (Dark-/Light-Mode switching).
|
||||
*
|
||||
* 2019/12/08: Implemented for the Dark Mode on macOS, because the app palette can not account for that (Qt 5.12.5).
|
||||
*
|
||||
* This way we also avoid having certain strings re-translated on Transifex.
|
||||
*/
|
||||
static void replaceLinkColorStringBackgroundAware(QString &linkString);
|
||||
|
||||
/**
|
||||
* @brief Appends a CSS-style colour value to all HTML link tags in a given string, as specified by newColor.
|
||||
*
|
||||
* 2019/12/19: Implemented for the Dark Mode on macOS, because the app palette can not account for that (Qt 5.12.5).
|
||||
*
|
||||
* This way we also avoid having certain strings re-translated on Transifex.
|
||||
*/
|
||||
static void replaceLinkColorString(QString &linkString, const QColor &newColor);
|
||||
|
||||
/**
|
||||
* @brief Creates a colour-aware icon based on the specified palette's base colour.
|
||||
*
|
||||
* @return QIcon, colour-aware (inverted on dark backgrounds).
|
||||
*
|
||||
* 2019/12/09: Moved here from SettingsDialog.
|
||||
*/
|
||||
static QIcon createColorAwareIcon(const QString &name, const QPalette &palette);
|
||||
|
||||
/**
|
||||
* @brief Creates a colour-aware icon based on the app palette's base colour (Dark-/Light-Mode switching).
|
||||
*
|
||||
* @return QIcon, colour-aware (inverted on dark backgrounds).
|
||||
*
|
||||
* 2019/12/09: Moved here from SettingsDialog.
|
||||
*/
|
||||
static QIcon createColorAwareIcon(const QString &name);
|
||||
|
||||
/**
|
||||
* @brief Creates a colour-aware pixmap based on the specified palette's base colour.
|
||||
*
|
||||
* @return QPixmap, colour-aware (inverted on dark backgrounds).
|
||||
*
|
||||
* 2019/12/09: Adapted from createColorAwareIcon.
|
||||
*/
|
||||
static QPixmap createColorAwarePixmap(const QString &name, const QPalette &palette);
|
||||
|
||||
/**
|
||||
* @brief Creates a colour-aware pixmap based on the app palette's base colour (Dark-/Light-Mode switching).
|
||||
*
|
||||
* @return QPixmap, colour-aware (inverted on dark backgrounds).
|
||||
*
|
||||
* 2019/12/09: Adapted from createColorAwareIcon.
|
||||
*/
|
||||
static QPixmap createColorAwarePixmap(const QString &name);
|
||||
|
||||
protected:
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue