Selective sync: Add UI to select paths

This commit is contained in:
Olivier Goffart 2014-08-11 15:09:17 +02:00
parent 1b4c613fa6
commit aa85e875bd
10 changed files with 346 additions and 8 deletions

View File

@ -267,6 +267,7 @@ set(mirall_SRCS
mirall/socketapi.cpp
mirall/sslbutton.cpp
mirall/syncrunfilelog.cpp
mirall/selectivesyncdialog.cpp
)

View File

@ -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;

View File

@ -81,6 +81,7 @@ protected slots:
void slotFolderWizardRejected();
void slotOpenAccountWizard();
void slotHideProgress();
void slotSelectiveSync();
private:
QString shortenFilename( const QString& folder, const QString& file ) const;

View File

@ -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">

View File

@ -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;

View File

@ -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();

View File

@ -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& );

View File

@ -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() {

View 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();
}
}

View 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
};
}