mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
Selective sync: Add UI to select paths
This commit is contained in:
parent
1b4c613fa6
commit
aa85e875bd
@ -267,6 +267,7 @@ set(mirall_SRCS
|
||||
mirall/socketapi.cpp
|
||||
mirall/sslbutton.cpp
|
||||
mirall/syncrunfilelog.cpp
|
||||
mirall/selectivesyncdialog.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "mirall/ignorelisteditor.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/quotainfo.h"
|
||||
#include "selectivesyncdialog.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <math.h>
|
||||
@ -77,6 +78,7 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
|
||||
ui->_buttonRemove->setEnabled(false);
|
||||
ui->_buttonEnable->setEnabled(false);
|
||||
ui->_buttonSelectiveSync->setEnabled(false);
|
||||
ui->_buttonAdd->setEnabled(true);
|
||||
|
||||
QAction *resetFolderAction = new QAction(this);
|
||||
@ -92,6 +94,7 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
connect(ui->_buttonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_buttonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_buttonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
connect(ui->_buttonSelectiveSync, SIGNAL(clicked()), this, SLOT(slotSelectiveSync()));
|
||||
connect(ui->modifyAccountButton, SIGNAL(clicked()), SLOT(slotOpenAccountWizard()));
|
||||
connect(ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));;
|
||||
|
||||
@ -152,6 +155,7 @@ void AccountSettings::slotFolderActivated( const QModelIndex& indx )
|
||||
}
|
||||
ui->_buttonAdd->setEnabled(_account && _account->state() == Account::Connected);
|
||||
ui->_buttonEnable->setEnabled( isValid );
|
||||
ui->_buttonSelectiveSync->setEnabled( isValid );
|
||||
|
||||
if ( isValid ) {
|
||||
bool folderEnabled = _model->data( indx, FolderStatusDelegate::FolderSyncEnabled).toBool();
|
||||
@ -369,6 +373,20 @@ void AccountSettings::slotResetCurrentFolder()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotSelectiveSync()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
if( selected.isValid() ) {
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder(alias);
|
||||
if (f) {
|
||||
(new SelectiveSyncDialog(f, this))->show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AccountSettings::slotDoubleClicked( const QModelIndex& indx )
|
||||
{
|
||||
if( ! indx.isValid() ) return;
|
||||
|
||||
@ -81,6 +81,7 @@ protected slots:
|
||||
void slotFolderWizardRejected();
|
||||
void slotOpenAccountWizard();
|
||||
void slotHideProgress();
|
||||
void slotSelectiveSync();
|
||||
|
||||
private:
|
||||
QString shortenFilename( const QString& folder, const QString& file ) const;
|
||||
|
||||
@ -70,6 +70,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonSelectiveSync">
|
||||
<property name="text">
|
||||
<string>Selective Sync...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
|
||||
@ -121,6 +121,10 @@ public:
|
||||
SyncJournalDb *journalDb() { return &_journal; }
|
||||
CSYNC *csyncContext() { return _csync_ctx; }
|
||||
|
||||
QStringList selectiveSyncList() { return _selectiveSyncWhiteList; }
|
||||
void setSelectiveSyncList(const QStringList &whiteList)
|
||||
{ _selectiveSyncWhiteList = whiteList; }
|
||||
|
||||
|
||||
signals:
|
||||
void syncStateChange();
|
||||
@ -188,6 +192,7 @@ private:
|
||||
SyncResult _syncResult;
|
||||
QScopedPointer<SyncEngine> _engine;
|
||||
QStringList _errors;
|
||||
QStringList _selectiveSyncWhiteList;
|
||||
bool _csyncError;
|
||||
bool _csyncUnavail;
|
||||
bool _wipeDb;
|
||||
|
||||
@ -234,7 +234,7 @@ void FolderMan::terminateCurrentSync()
|
||||
#define PAR_O_TAG QLatin1String("__PAR_OPEN__")
|
||||
#define PAR_C_TAG QLatin1String("__PAR_CLOSE__")
|
||||
|
||||
QString FolderMan::escapeAlias( const QString& alias ) const
|
||||
QString FolderMan::escapeAlias( const QString& alias )
|
||||
{
|
||||
QString a(alias);
|
||||
|
||||
@ -312,6 +312,7 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
QString backend = settings.value(QLatin1String("backend")).toString();
|
||||
QString targetPath = settings.value( QLatin1String("targetPath")).toString();
|
||||
bool paused = settings.value( QLatin1String("paused"), false).toBool();
|
||||
QStringList whiteList = settings.value( QLatin1String("whiteList")).toStringList();
|
||||
// QString connection = settings.value( QLatin1String("connection") ).toString();
|
||||
QString alias = unescapeAlias( escapedAlias );
|
||||
|
||||
@ -326,7 +327,8 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
}
|
||||
|
||||
folder = new Folder( alias, path, targetPath, this );
|
||||
folder->setConfigFile(file);
|
||||
folder->setConfigFile(cfgFile.absoluteFilePath());
|
||||
folder->setSelectiveSyncList(whiteList);
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
_folderMap[alias] = folder;
|
||||
if (paused) {
|
||||
@ -359,7 +361,7 @@ void FolderMan::slotEnableFolder( const QString& alias, bool enable )
|
||||
slotScheduleSync(alias);
|
||||
|
||||
// FIXME: Use MirallConfigFile
|
||||
QSettings settings(_folderConfigPath + QLatin1Char('/') + f->configFile(), QSettings::IniFormat);
|
||||
QSettings settings(f->configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(escapeAlias(f->alias()));
|
||||
if (enable) {
|
||||
settings.remove("paused");
|
||||
@ -588,7 +590,7 @@ void FolderMan::removeFolder( const QString& alias )
|
||||
f->setSyncEnabled(false);
|
||||
|
||||
// remove the folder configuration
|
||||
QFile file( _folderConfigPath + QLatin1Char('/') + f->configFile() );
|
||||
QFile file(f->configFile() );
|
||||
if( file.exists() ) {
|
||||
qDebug() << "Remove folder config file " << file.fileName();
|
||||
file.remove();
|
||||
|
||||
@ -84,6 +84,10 @@ public:
|
||||
void removeMonitorPath( const QString& alias, const QString& path );
|
||||
void addMonitorPath( const QString& alias, const QString& path );
|
||||
|
||||
// Escaping of the alias which is used in QSettings AND the file
|
||||
// system, thus need to be escaped.
|
||||
static QString escapeAlias( const QString& );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* signal to indicate a folder named by alias has changed its sync state.
|
||||
@ -131,9 +135,6 @@ private:
|
||||
QString getBackupName( const QString& ) const;
|
||||
void registerFolderMonitor( Folder *folder );
|
||||
|
||||
// Escaping of the alias which is used in QSettings AND the file
|
||||
// system, thus need to be escaped.
|
||||
QString escapeAlias( const QString& ) const;
|
||||
QString unescapeAlias( const QString& ) const;
|
||||
|
||||
void removeFolder( const QString& );
|
||||
|
||||
@ -50,7 +50,7 @@ Logger *Logger::instance()
|
||||
Logger::Logger( QObject* parent) : QObject(parent),
|
||||
_showTime(true), _doLogging(false), _doFileFlush(false), _logExpire(0)
|
||||
{
|
||||
qInstallMessageHandler(mirallLogCatcher);
|
||||
// qInstallMessageHandler(mirallLogCatcher);
|
||||
}
|
||||
|
||||
Logger::~Logger() {
|
||||
|
||||
256
src/mirall/selectivesyncdialog.cpp
Normal file
256
src/mirall/selectivesyncdialog.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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 "selectivesyncdialog.h"
|
||||
#include "folder.h"
|
||||
#include "account.h"
|
||||
#include "networkjobs.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include <QDialogButtonBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeWidget>
|
||||
#include <qpushbutton.h>
|
||||
#include <QFileIconProvider>
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QScopedValueRollback>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
SelectiveSyncDialog::SelectiveSyncDialog(Folder* folder, QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f), _folder(folder)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
_treeView = new QTreeWidget;
|
||||
connect(_treeView, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
connect(_treeView, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||
layout->addWidget(_treeView);
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
||||
QPushButton *button;
|
||||
button = buttonBox->addButton(QDialogButtonBox::Ok);
|
||||
connect(button, SIGNAL(clicked()), this, SLOT(accept()));
|
||||
button = buttonBox->addButton(QDialogButtonBox::Cancel);
|
||||
connect(button, SIGNAL(clicked()), this, SLOT(reject()));
|
||||
layout->addWidget(buttonBox);
|
||||
|
||||
// Make sure we don't get crashes if the folder is destroyed while we are still open
|
||||
connect(_folder, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater()));
|
||||
|
||||
refreshFolders();
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::refreshFolders()
|
||||
{
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), _folder->remotePath(), this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
this, SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
_treeView->clear();
|
||||
|
||||
}
|
||||
|
||||
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
|
||||
{
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
QTreeWidgetItem *child = parent->child(i);
|
||||
if (child->text(0) == text) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail,
|
||||
QString path)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
if (pathTrail.size() == 0) {
|
||||
if (path.endsWith('/')) {
|
||||
path.chop(1);
|
||||
}
|
||||
parent->setToolTip(0, path);
|
||||
parent->setData(0, Qt::UserRole, path);
|
||||
} else {
|
||||
QTreeWidgetItem *item = findFirstChild(parent, pathTrail.first());
|
||||
if (!item) {
|
||||
item = new QTreeWidgetItem(parent);
|
||||
if (parent->checkState(0) == Qt::Checked) {
|
||||
item->setCheckState(0, Qt::Checked);
|
||||
} else if (parent->checkState(0) == Qt::Unchecked) {
|
||||
item->setCheckState(0, Qt::Unchecked);
|
||||
} else {
|
||||
item->setCheckState(0, Qt::Unchecked);
|
||||
foreach(const QString &str , _folder->selectiveSyncList()) {
|
||||
if (str + "/" == path) {
|
||||
item->setCheckState(0, Qt::Checked);
|
||||
break;
|
||||
} else if (str.startsWith(path)) {
|
||||
item->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
item->setIcon(0, folderIcon);
|
||||
item->setText(0, pathTrail.first());
|
||||
// item->setData(0, Qt::UserRole, pathTrail.first());
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
}
|
||||
|
||||
pathTrail.removeFirst();
|
||||
recursiveInsert(item, pathTrail, path);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::slotUpdateDirectories(const QStringList &list)
|
||||
{
|
||||
QScopedValueRollback<bool> isInserting(_inserting);
|
||||
_inserting = true;
|
||||
|
||||
QTreeWidgetItem *root = _treeView->topLevelItem(0);
|
||||
if (!root) {
|
||||
root = new QTreeWidgetItem(_treeView);
|
||||
root->setText(0, _folder->alias());
|
||||
root->setIcon(0, Theme::instance()->applicationIcon());
|
||||
root->setData(0, Qt::UserRole, _folder->remotePath());
|
||||
if (_folder->selectiveSyncList().isEmpty() || _folder->selectiveSyncList().contains(QString())) {
|
||||
root->setCheckState(0, Qt::Checked);
|
||||
} else {
|
||||
root->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
const QString folderPath = _folder->remoteUrl().path();
|
||||
foreach (QString path, list) {
|
||||
path.remove(folderPath);
|
||||
QStringList paths = path.split('/');
|
||||
if (paths.last().isEmpty()) paths.removeLast();
|
||||
recursiveInsert(root, paths, path);
|
||||
}
|
||||
root->setExpanded(true);
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
QString dir = item->data(0, Qt::UserRole).toString();
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
{
|
||||
if (col != 0 || _inserting)
|
||||
return;
|
||||
|
||||
if (item->checkState(0) == Qt::Checked) {
|
||||
// If we are checked, check that we may need to check the parent as well if
|
||||
// all the sibilings are also checked
|
||||
QTreeWidgetItem *parent = item->parent();
|
||||
if (parent && parent->checkState(0) != Qt::Checked) {
|
||||
bool hasUnchecked = false;
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
if (parent->child(i)->checkState(0) != Qt::Checked) {
|
||||
hasUnchecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasUnchecked) {
|
||||
parent->setCheckState(0, Qt::Checked);
|
||||
} else if (parent->checkState(0) == Qt::Unchecked) {
|
||||
parent->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
// also check all the childs
|
||||
for (int i = 0; i < item->childCount(); ++i) {
|
||||
if (item->child(i)->checkState(0) != Qt::Checked) {
|
||||
item->child(i)->setCheckState(0, Qt::Checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item->checkState(0) == Qt::Unchecked) {
|
||||
QTreeWidgetItem *parent = item->parent();
|
||||
if (parent && parent->checkState(0) != Qt::Unchecked) {
|
||||
bool hasChecked = false;
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
if (parent->child(i)->checkState(0) != Qt::Unchecked) {
|
||||
hasChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasChecked) {
|
||||
parent->setCheckState(0, Qt::Unchecked);
|
||||
} else if (parent->checkState(0) == Qt::Checked) {
|
||||
parent->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Uncheck all the childs
|
||||
for (int i = 0; i < item->childCount(); ++i) {
|
||||
if (item->child(i)->checkState(0) != Qt::Unchecked) {
|
||||
item->child(i)->setCheckState(0, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item->checkState(0) == Qt::PartiallyChecked) {
|
||||
QTreeWidgetItem *parent = item->parent();
|
||||
if (parent && parent->checkState(0) != Qt::PartiallyChecked) {
|
||||
parent->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncDialog::createWhiteList(QTreeWidgetItem* root) const
|
||||
{
|
||||
if (!root) {
|
||||
root = _treeView->topLevelItem(0);
|
||||
}
|
||||
if (!root) return {};
|
||||
|
||||
switch(root->checkState(0)) {
|
||||
case Qt::Checked:
|
||||
return { root->data(0, Qt::UserRole).toString() };
|
||||
case Qt::Unchecked:
|
||||
return {};
|
||||
case Qt::PartiallyChecked:
|
||||
break;
|
||||
}
|
||||
|
||||
QStringList result;
|
||||
for (int i = 0; i < root->childCount(); ++i) {
|
||||
result += createWhiteList(root->child(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::accept()
|
||||
{
|
||||
QStringList whiteList = createWhiteList();
|
||||
_folder->setSelectiveSyncList(whiteList);
|
||||
|
||||
// FIXME: Use MirallConfigFile
|
||||
QSettings settings(_folder->configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(FolderMan::escapeAlias(_folder->alias()));
|
||||
settings.setValue("whiteList", whiteList);
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
47
src/mirall/selectivesyncdialog.h
Normal file
47
src/mirall/selectivesyncdialog.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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
|
||||
#include <QDialog>
|
||||
|
||||
class QTreeWidgetItem;
|
||||
class QTreeWidget;
|
||||
namespace Mirall {
|
||||
|
||||
class Folder;
|
||||
|
||||
class SelectiveSyncDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelectiveSyncDialog(Folder *folder, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
|
||||
virtual void accept() Q_DECL_OVERRIDE;
|
||||
QStringList createWhiteList(QTreeWidgetItem* root = 0) const;
|
||||
|
||||
private slots:
|
||||
void refreshFolders();
|
||||
void slotUpdateDirectories(const QStringList &);
|
||||
void slotItemExpanded(QTreeWidgetItem *);
|
||||
void slotItemChanged(QTreeWidgetItem*,int);
|
||||
|
||||
private:
|
||||
void recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path);
|
||||
|
||||
Folder *_folder;
|
||||
QTreeWidget *_treeView;
|
||||
bool _inserting = false; // set to true when we are inserting new items on the list
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user