mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
290 lines
9.6 KiB
C++
290 lines
9.6 KiB
C++
/*
|
|
* 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 <QHeaderView>
|
|
#include <QDebug>
|
|
#include <QSettings>
|
|
#include <QScopedValueRollback>
|
|
#include <QLabel>
|
|
|
|
namespace Mirall {
|
|
|
|
SelectiveSyncTreeView::SelectiveSyncTreeView(QWidget* parent)
|
|
: QTreeWidget(parent)
|
|
{
|
|
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
|
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
|
header()->hide();
|
|
}
|
|
|
|
void SelectiveSyncTreeView::refreshFolders()
|
|
{
|
|
LsColJob *job = new LsColJob(AccountManager::instance()->account(), _folderPath, this);
|
|
connect(job, SIGNAL(directoryListing(QStringList)),
|
|
this, SLOT(slotUpdateDirectories(QStringList)));
|
|
job->start();
|
|
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 SelectiveSyncTreeView::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::Checked);
|
|
foreach(const QString &str , _oldBlackList) {
|
|
if (str + "/" == path) {
|
|
item->setCheckState(0, Qt::Unchecked);
|
|
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 SelectiveSyncTreeView::slotUpdateDirectories(const QStringList&list)
|
|
{
|
|
QScopedValueRollback<bool> isInserting(_inserting);
|
|
_inserting = true;
|
|
|
|
QTreeWidgetItem *root = topLevelItem(0);
|
|
if (!root) {
|
|
root = new QTreeWidgetItem(this);
|
|
root->setText(0, _rootName);
|
|
root->setIcon(0, Theme::instance()->applicationIcon());
|
|
root->setData(0, Qt::UserRole, QString());
|
|
if (_oldBlackList.isEmpty()) {
|
|
root->setCheckState(0, Qt::Checked);
|
|
} else {
|
|
root->setCheckState(0, Qt::PartiallyChecked);
|
|
}
|
|
}
|
|
|
|
Account *account = AccountManager::instance()->account();
|
|
QUrl url = account->davUrl();
|
|
QString pathToRemove = url.path();
|
|
if (!pathToRemove.endsWith('/')) {
|
|
pathToRemove.append('/');
|
|
}
|
|
pathToRemove.append(_folderPath);
|
|
if (!_folderPath.isEmpty())
|
|
pathToRemove.append('/');
|
|
|
|
foreach (QString path, list) {
|
|
path.remove(pathToRemove);
|
|
QStringList paths = path.split('/');
|
|
if (paths.last().isEmpty()) paths.removeLast();
|
|
if (paths.isEmpty())
|
|
continue;
|
|
recursiveInsert(root, paths, path);
|
|
}
|
|
root->setExpanded(true);
|
|
}
|
|
|
|
void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
|
|
{
|
|
QString dir = item->data(0, Qt::UserRole).toString();
|
|
if (dir.isEmpty()) return;
|
|
QString prefix;
|
|
if (!_folderPath.isEmpty()) {
|
|
prefix = _folderPath + QLatin1Char('/');
|
|
}
|
|
LsColJob *job = new LsColJob(AccountManager::instance()->account(), prefix + dir, this);
|
|
connect(job, SIGNAL(directoryListing(QStringList)),
|
|
SLOT(slotUpdateDirectories(QStringList)));
|
|
job->start();
|
|
}
|
|
|
|
void SelectiveSyncTreeView::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 SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
|
|
{
|
|
if (!root) {
|
|
root = topLevelItem(0);
|
|
}
|
|
if (!root) return QStringList();
|
|
|
|
switch(root->checkState(0)) {
|
|
case Qt::Unchecked:
|
|
return QStringList(root->data(0, Qt::UserRole).toString());
|
|
case Qt::Checked:
|
|
return QStringList();
|
|
case Qt::PartiallyChecked:
|
|
break;
|
|
}
|
|
|
|
QStringList result;
|
|
if (root->childCount()) {
|
|
for (int i = 0; i < root->childCount(); ++i) {
|
|
result += createBlackList(root->child(i));
|
|
}
|
|
} else {
|
|
// We did not load from the server so we re-use the one from the old black list
|
|
QString path = root->data(0, Qt::UserRole).toString();
|
|
foreach (const QString & it, _oldBlackList) {
|
|
if (it.startsWith(path))
|
|
result += it;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
SelectiveSyncDialog::SelectiveSyncDialog(Folder* folder, QWidget* parent, Qt::WindowFlags f)
|
|
: QDialog(parent, f), _folder(folder)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
_treeView = new SelectiveSyncTreeView(parent);
|
|
layout->addWidget(new QLabel(tr("Only checked folders will sync to this computer")));
|
|
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()));
|
|
|
|
_treeView->setFolderInfo(_folder->remotePath(), _folder->alias(), _folder->selectiveSyncBlackList());
|
|
}
|
|
|
|
void SelectiveSyncDialog::accept()
|
|
{
|
|
QStringList blackList = _treeView->createBlackList();
|
|
_folder->setSelectiveSyncBlackList(blackList);
|
|
|
|
// FIXME: Use MirallConfigFile
|
|
QSettings settings(_folder->configFile(), QSettings::IniFormat);
|
|
settings.beginGroup(FolderMan::escapeAlias(_folder->alias()));
|
|
settings.setValue("blackList", blackList);
|
|
|
|
QDialog::accept();
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|