* This file is part of the KDE Baloo Project
* Copyright (C) 2014 Vishesh Handa <me@vhanda.in>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "folderselectionwidget.h"
#include <Solid/Device>
#include <Solid/StorageAccess>
#include <Solid/StorageDrive>
#include <QIcon>
#include <QFileDialog>
#include <QDir>
#include <QTimer>
#include <QUrl>
#include <KLocalizedString>
#include <QSpacerItem>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
FolderSelectionWidget::FolderSelectionWidget(QWidget* parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
m_listWidget = new QListWidget(this);
m_listWidget->setAlternatingRowColors(true);
m_messageWidget = new KMessageWidget(this);
m_messageWidget->hide();
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(m_messageWidget);
layout->addWidget(m_listWidget);
QHBoxLayout* hLayout = new QHBoxLayout;
m_addButton = new QPushButton(this);
m_addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
connect(m_addButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotAddButtonClicked);
m_removeButton = new QPushButton(this);
m_removeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
m_removeButton->setEnabled(false);
connect(m_removeButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotRemoveButtonClicked);
connect(m_listWidget, &QListWidget::currentItemChanged, this, &FolderSelectionWidget::slotCurrentItemChanged);
hLayout->addWidget(m_addButton);
hLayout->addWidget(m_removeButton);
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
hLayout->addItem(spacer);
layout->addItem(hLayout);
}
namespace {
QStringList addTrailingSlashes(const QStringList& input) {
QStringList output;
Q_FOREACH (QString str, input) {
if (!str.endsWith(QDir::separator()))
str.append(QDir::separator());
output << str;
}
return output;
}
QString makeHomePretty(const QString& url) {
if (url.startsWith(QDir::homePath()))
return QString(url).replace(0, QDir::homePath().length(), QStringLiteral("~"));
return url;
}
}
void FolderSelectionWidget::setDirectoryList(QStringList includeDirs, QStringList exclude)
{
m_listWidget->clear();
m_mountPoints.clear();
QList<Solid::Device> devices
= Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess);
Q_FOREACH (const Solid::Device& dev, devices) {
const Solid::StorageAccess* sa = dev.as<Solid::StorageAccess>();
if (!sa->isAccessible())
continue;
QString mountPath = sa->filePath();
if (!shouldShowMountPoint(mountPath))
continue;
m_mountPoints << mountPath;
}
m_mountPoints << QDir::homePath();
m_mountPoints = addTrailingSlashes(m_mountPoints);
includeDirs = addTrailingSlashes(includeDirs);
exclude = addTrailingSlashes(exclude);
QStringList excludeList = exclude;
Q_FOREACH (const QString& mountPath, m_mountPoints) {
if (includeDirs.contains(mountPath))
continue;
if (exclude.contains(mountPath))
continue;
if (!excludeList.contains(mountPath)) {
excludeList << mountPath;
}
}
Q_FOREACH (QString url, excludeList) {
QListWidgetItem* item = new QListWidgetItem(m_listWidget);
QString display = folderDisplayName(url);
item->setData(Qt::DisplayRole, display);
item->setData(Qt::WhatsThisRole, url);
item->setData(UrlRole, url);
item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url)));
item->setToolTip(makeHomePretty(url));
m_listWidget->addItem(item);
}
if (m_listWidget->count() == 0) {
m_removeButton->setEnabled(false);
}
}
QStringList FolderSelectionWidget::includeFolders() const
{
QStringList folders;
Q_FOREACH (const QString& mountPath, m_mountPoints) {
bool inExclude = false;
for (int i=0; i<m_listWidget->count(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString url = item->data(UrlRole).toString();
if (mountPath == url) {
inExclude = true;
break;
}
}
if (!inExclude && !folders.contains(mountPath)) {
folders << mountPath;
}
}
return folders;
}
QStringList FolderSelectionWidget::excludeFolders() const
{
QStringList folders;
for (int i=0; i<m_listWidget->count(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString url = item->data(UrlRole).toString();
if (!folders.contains(url)) {
folders << url;
}
}
return folders;
}
QString FolderSelectionWidget::fetchMountPoint(const QString& url) const
{
QString mountPoint;
Q_FOREACH (const QString& mount, m_mountPoints) {
if (url.startsWith(mount) && mount.size() > mountPoint.size())
mountPoint = mount;
}
return mountPoint;
}
void FolderSelectionWidget::slotAddButtonClicked()
{
QString url = QFileDialog::getExistingDirectory(this, i18n("Select the folder which should be excluded"));
if (url.isEmpty()) {
return;
}
if (!url.endsWith(QDir::separator()))
url.append(QDir::separator());
if (url == QLatin1String("/")) {
showMessage(i18n("Not allowed to exclude root folder, please disable File Search if you do not want it"));
return;
}
QVector<QListWidgetItem*> deleteList;
for (int i=0; i<m_listWidget->count(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString existingUrl = item->data(UrlRole).toString();
if (existingUrl == url) {
QString name = QUrl::fromLocalFile(url).fileName();
showMessage(i18n("Folder %1 is already excluded", name));
deleteList << item;
continue;
}
QString existingMountPoint = fetchMountPoint(existingUrl);
QString mountPoint = fetchMountPoint(url);
if (existingMountPoint == mountPoint) {
if (existingUrl.startsWith(url)) {
deleteList << item;
}
else if (url.startsWith(existingUrl)) {
url = existingUrl;
deleteList << item;
QString name = QUrl::fromLocalFile(url).adjusted(QUrl::StripTrailingSlash).fileName();
showMessage(i18n("Folder's parent %1 is already excluded", name));
}
}
}
qDeleteAll(deleteList);
QListWidgetItem* item = new QListWidgetItem(m_listWidget);
QString displayName = folderDisplayName(url);
item->setData(Qt::DisplayRole, displayName);
item->setData(Qt::WhatsThisRole, url);
item->setData(UrlRole, url);
item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url)));
item->setToolTip(makeHomePretty(url));
m_listWidget->addItem(item);
m_listWidget->setCurrentItem(item);
Q_EMIT changed();
}
void FolderSelectionWidget::slotRemoveButtonClicked()
{
QListWidgetItem* item = m_listWidget->currentItem();
delete item;
Q_EMIT changed();
}
void FolderSelectionWidget::slotCurrentItemChanged(QListWidgetItem* current, QListWidgetItem*)
{
m_removeButton->setEnabled(current != nullptr);
}
void FolderSelectionWidget::showMessage(const QString& message)
{
m_messageWidget->setText(message);
m_messageWidget->setMessageType(KMessageWidget::Warning);
m_messageWidget->animatedShow();
QTimer::singleShot(3000, m_messageWidget, &KMessageWidget::animatedHide);
}
QString FolderSelectionWidget::folderDisplayName(const QString& url) const
{
QString name = url;
QString homePath = QDir::homePath() + QLatin1Char('/');
if (url == homePath) {
return QDir(homePath).dirName();
}
if (url.startsWith(homePath)) {
name = url.mid(homePath.size());
}
else {
Q_FOREACH (QString mountPoint, m_mountPoints) {
if (url.startsWith(mountPoint)) {
name = QLatin1Char('[') + QDir(mountPoint).dirName() + QLatin1String("]/") + url.mid(mountPoint.length());
break;
}
}
}
if (name.endsWith(QLatin1Char('/'))) {
name = name.mid(0, name.size() - 1);
}
return name;
}
bool FolderSelectionWidget::shouldShowMountPoint(const QString& mountPoint)
{
if (mountPoint == QLatin1String("/"))
return false;
if (mountPoint.startsWith(QLatin1String("/boot")))
return false;
if (mountPoint.startsWith(QLatin1String("/tmp")))
return false;
if (mountPoint.startsWith(QLatin1String("/home")) && QDir::homePath().startsWith(QLatin1String("/home")))
return false;
return true;
}
QString FolderSelectionWidget::iconName(QString path) const
{
if (!path.endsWith(QDir::separator()))
path.append(QDir::separator());
QString homePath = QDir::homePath();
if (!homePath.endsWith(QDir::separator()))
homePath.append(QDir::separator());
if (path == homePath)
return QStringLiteral("user-home");
if (m_mountPoints.contains(path))
return QStringLiteral("drive-harddisk");
return QStringLiteral("folder");
}