filemanager/model/foldermodel.cpp

1936 lines
54 KiB
C++
Raw Normal View History

2021-04-21 10:51:17 -07:00
/***************************************************************************
* Copyright (C) 2006 David Faure <faure@kde.org> *
* Copyright (C) 2008 Fredrik Höglund <fredrik@kde.org> *
* Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org> *
* Copyright (C) 2011 Marco Martin <mart@kde.org> *
* Copyright (C) 2014 by Eike Hein <hein@kde.org> *
* Copyright (C) 2021 Reven Martin <revenmartin@gmail.com> *
2021-09-12 04:30:01 -07:00
* Copyright (C) 2021 Reion Wong <reionwong@gmail.com> *
2021-04-21 10:51:17 -07:00
* *
* 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. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
2021-03-29 01:51:34 -07:00
#include "foldermodel.h"
#include "dirlister.h"
#include "../dialogs/filepropertiesdialog.h"
#include "../dialogs/createfolderdialog.h"
2021-07-29 02:38:47 -07:00
#include "../dialogs/openwithdialog.h"
2021-03-29 01:51:34 -07:00
2021-04-21 19:24:10 -07:00
#include "../helper/datehelper.h"
2021-07-28 12:30:17 -07:00
#include "../helper/filelauncher.h"
2021-04-21 19:24:10 -07:00
2021-09-05 14:39:56 -07:00
#include "../cio/cfilesizejob.h"
2021-03-29 01:51:34 -07:00
// Qt
2021-09-01 06:13:47 -07:00
#include <QSet>
2021-03-29 01:51:34 -07:00
#include <QDir>
#include <QMenu>
#include <QAction>
#include <QCollator>
2021-04-01 01:05:15 -07:00
#include <QDBusInterface>
2021-03-29 01:51:34 -07:00
#include <QStandardPaths>
#include <QApplication>
#include <QDesktopWidget>
#include <QMimeDatabase>
2021-11-03 10:35:09 -07:00
#include <QMimeData>
2021-03-29 01:51:34 -07:00
#include <QClipboard>
#include <QPainter>
#include <QDrag>
2021-04-01 01:05:15 -07:00
#include <QDir>
2021-05-21 00:55:58 -07:00
#include <QProcess>
2021-09-12 04:30:01 -07:00
#include <QSettings>
2021-07-28 12:30:17 -07:00
#include <QDesktopServices>
2021-10-15 03:57:18 -07:00
#include <QPixmapCache>
2021-03-29 01:51:34 -07:00
// Qt Quick
#include <QQuickItem>
2021-09-03 11:26:01 -07:00
#include <QQmlContext>
2021-03-29 01:51:34 -07:00
// KIO
#include <KIO/CopyJob>
#include <KIO/Job>
#include <KIO/PreviewJob>
#include <KIO/DeleteJob>
#include <KIO/DropJob>
#include <KIO/FileUndoManager>
#include <KIO/JobUiDelegate>
#include <KIO/Paste>
#include <KIO/PasteJob>
#include <KIO/RestoreJob>
#include <KUrlMimeData>
#include <KFileItemListProperties>
#include <KDesktopFile>
2021-11-13 16:01:00 -08:00
static bool isDropBetweenSharedViews(const QList<QUrl> &urls, const QUrl &folderUrl)
{
for (const auto &url : urls) {
if (folderUrl.adjusted(QUrl::StripTrailingSlash) != url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)) {
return false;
}
}
return true;
}
2021-03-29 01:51:34 -07:00
FolderModel::FolderModel(QObject *parent)
: QSortFilterProxyModel(parent)
, m_dirWatch(nullptr)
2021-12-26 05:18:02 -08:00
, m_status(None)
2021-03-29 01:51:34 -07:00
, m_sortMode(0)
, m_sortDesc(false)
, m_sortDirsFirst(true)
2021-09-12 04:30:01 -07:00
, m_showHiddenFiles(false)
2021-08-31 14:08:00 -07:00
, m_filterMode(NoFilter)
, m_filterPatternMatchAll(true)
2021-03-29 01:51:34 -07:00
, m_complete(false)
2021-03-29 07:53:21 -07:00
, m_isDesktop(false)
2021-09-05 14:39:56 -07:00
, m_selectedItemSize("")
2021-03-29 01:51:34 -07:00
, m_actionCollection(this)
, m_dragInProgress(false)
2021-11-13 16:01:00 -08:00
, m_dropTargetPositionsCleanup(new QTimer(this))
2021-03-29 01:51:34 -07:00
, m_viewAdapter(nullptr)
2021-07-28 12:30:17 -07:00
, m_mimeAppManager(MimeAppManager::self())
2021-09-05 14:39:56 -07:00
, m_sizeJob(nullptr)
, m_currentIndex(-1)
2021-03-29 01:51:34 -07:00
{
2021-09-12 04:30:01 -07:00
QSettings settings("cutefishos", qApp->applicationName());
m_showHiddenFiles = settings.value("showHiddenFiles", false).toBool();
m_dirLister = new DirLister(this);
m_dirLister->setDelayedMimeTypes(true);
m_dirLister->setAutoErrorHandlingEnabled(false, nullptr);
2021-12-26 05:18:02 -08:00
m_dirLister->setAutoUpdate(true);
2021-09-12 04:30:01 -07:00
m_dirLister->setShowingDotFiles(m_showHiddenFiles);
2021-08-31 14:31:38 -07:00
// connect(dirLister, &DirLister::error, this, &FolderModel::notification);
2021-03-29 01:51:34 -07:00
2021-11-13 16:01:00 -08:00
connect(m_dirLister, &KCoreDirLister::started, this, std::bind(&FolderModel::setStatus, this, Status::Listing));
void (KCoreDirLister::*myCompletedSignal)() = &KCoreDirLister::completed;
QObject::connect(m_dirLister, myCompletedSignal, this, [this] {
setStatus(Status::Ready);
emit listingCompleted();
});
void (KCoreDirLister::*myCanceledSignal)() = &KCoreDirLister::canceled;
QObject::connect(m_dirLister, myCanceledSignal, this, [this] {
setStatus(Status::Canceled);
emit listingCanceled();
});
2021-03-29 01:51:34 -07:00
m_dirModel = new KDirModel(this);
2021-09-12 04:30:01 -07:00
m_dirModel->setDirLister(m_dirLister);
2021-03-29 01:51:34 -07:00
m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory | KDirModel::DropOnLocalExecutable);
2021-04-23 10:46:27 -07:00
m_dirModel->moveToThread(qApp->thread());
2021-03-29 01:51:34 -07:00
2021-11-13 16:01:00 -08:00
// If we have dropped items queued for moving, go unsorted now.
connect(this, &QAbstractItemModel::rowsAboutToBeInserted, this, [this]() {
if (!m_dropTargetPositions.isEmpty()) {
setSortMode(-1);
}
});
// Position dropped items at the desired target position.
connect(this, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
QModelIndex changeIdx;
2021-11-13 16:01:00 -08:00
for (int i = first; i <= last; ++i) {
const auto idx = index(i, 0, parent);
const auto url = itemForIndex(idx).url();
auto it = m_dropTargetPositions.find(url.fileName());
if (it != m_dropTargetPositions.end()) {
const auto pos = it.value();
m_dropTargetPositions.erase(it);
Q_EMIT move(pos.x(), pos.y(), {url});
}
if (url == m_newDocumentUrl) {
changeIdx = idx;
m_newDocumentUrl.clear();
}
2021-11-13 16:01:00 -08:00
}
QTimer::singleShot(0, this, [=] {
if (changeIdx.isValid()) {
setSelected(changeIdx.row());
emit requestRename();
}
});
2021-11-13 16:01:00 -08:00
});
/*
* Dropped files may not actually show up as new files, e.g. when we overwrite
* an existing file. Or files that fail to be listed by the dirLister, or...
* To ensure we don't grow the map indefinitely, clean it up periodically.
* The cleanup timer is (re)started whenever we modify the map. We use a quite
* high interval of 10s. This should ensure, that we don't accidentally wipe
* the mapping when we actually still want to use it. Since the time between
* adding an entry in the map and it showing up in the model should be
* small, this should rarely, if ever happen.
*/
m_dropTargetPositionsCleanup->setInterval(10000);
m_dropTargetPositionsCleanup->setSingleShot(true);
connect(m_dropTargetPositionsCleanup, &QTimer::timeout, this, [this]() {
if (!m_dropTargetPositions.isEmpty()) {
qDebug() << "clearing drop target positions after timeout:" << m_dropTargetPositions;
m_dropTargetPositions.clear();
}
});
2021-03-29 01:51:34 -07:00
m_selectionModel = new QItemSelectionModel(this, this);
connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &FolderModel::selectionChanged);
setSourceModel(m_dirModel);
setSortLocaleAware(true);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setDynamicSortFilter(true);
sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
createActions();
2021-04-01 01:05:15 -07:00
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), SIGNAL(countChanged()));
connect(this, SIGNAL(rowsRemoved(QModelIndex, int, int)), SIGNAL(countChanged()));
connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
2021-03-29 01:51:34 -07:00
}
FolderModel::~FolderModel()
{
}
void FolderModel::classBegin()
{
}
void FolderModel::componentComplete()
{
m_complete = true;
invalidate();
}
QHash<int, QByteArray> FolderModel::roleNames() const
{
return staticRoleNames();
}
QHash<int, QByteArray> FolderModel::staticRoleNames()
{
QHash<int, QByteArray> roleNames;
roleNames[Qt::DisplayRole] = "display";
roleNames[Qt::DecorationRole] = "decoration";
roleNames[BlankRole] = "blank";
roleNames[SelectedRole] = "selected";
roleNames[IsDirRole] = "isDir";
2021-09-12 04:30:01 -07:00
roleNames[IsHiddenRole] = "isHidden";
2021-10-22 02:31:30 -07:00
roleNames[IsLinkRole] = "isLink";
2021-03-29 01:51:34 -07:00
roleNames[UrlRole] = "url";
2021-08-16 18:53:52 -07:00
roleNames[DisplayNameRole] = "displayName";
2021-03-29 01:51:34 -07:00
roleNames[FileNameRole] = "fileName";
2021-04-01 01:05:15 -07:00
roleNames[FileSizeRole] = "fileSize";
2021-03-29 01:51:34 -07:00
roleNames[IconNameRole] = "iconName";
roleNames[ThumbnailRole] = "thumbnail";
2021-04-01 01:05:15 -07:00
roleNames[ModifiedRole] = "modified";
2021-08-16 18:53:52 -07:00
roleNames[IsDesktopFileRole] = "desktopFile";
2021-03-29 01:51:34 -07:00
return roleNames;
}
QVariant FolderModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
KFileItem item = itemForIndex(index);
switch (role) {
case BlankRole:
return m_dragIndexes.contains(index);
case SelectedRole:
return m_selectionModel->isSelected(index);
case UrlRole:
return item.url();
2021-08-16 18:53:52 -07:00
case DisplayNameRole: {
if (item.isDesktopFile()) {
KDesktopFile dfile(item.localPath());
2021-12-14 11:52:44 -08:00
if (!dfile.readName().isEmpty())
return dfile.readName();
}
2021-08-16 18:53:52 -07:00
return item.url().fileName();
}
2021-03-30 18:22:32 -07:00
case FileNameRole: {
2021-03-29 01:51:34 -07:00
return item.url().fileName();
2021-03-30 18:22:32 -07:00
}
2021-08-16 18:53:52 -07:00
case IsDesktopFileRole: {
return item.isDesktopFile();
}
2021-09-12 04:30:01 -07:00
case IsDirRole: {
return item.isDir();
}
case IsHiddenRole: {
return item.isHidden();
}
2021-10-22 02:31:30 -07:00
case IsLinkRole: {
return item.isLink();
}
2021-04-01 01:05:15 -07:00
case FileSizeRole: {
if (item.isDir()) {
2021-04-01 07:16:42 -07:00
QDir dir(item.url().toLocalFile());
dir.setFilter(QDir::Dirs | QDir::AllEntries | QDir::NoDotAndDotDot);
uint count = dir.count();
2021-04-01 01:05:15 -07:00
return count == 1 ? tr("%1 item").arg(count) : tr("%1 items").arg(count);
}
return KIO::convertSize(item.size());
}
2021-03-29 01:51:34 -07:00
case IconNameRole:
return item.iconName();
case ThumbnailRole: {
if (item.isLocalFile()) {
// Svg Image
if (item.mimetype() == "image/svg" ||
item.mimetype() == "image/svg+xml") {
return item.url();
}
// Support
if (isSupportThumbnails(item.mimetype())) {
return "image://thumbnailer/" + item.url().toString();
}
2021-03-29 01:51:34 -07:00
}
return QVariant();
}
2021-04-01 01:05:15 -07:00
case ModifiedRole: {
2021-04-21 19:24:10 -07:00
return DateHelper::friendlyTime(item.time(KFileItem::ModificationTime));
2021-04-01 01:05:15 -07:00
}
2021-03-29 01:51:34 -07:00
default:
break;
}
return QSortFilterProxyModel::data(index, role);
}
2021-09-19 20:04:33 -07:00
int FolderModel::indexForKeyboardSearch(const QString &text, int startFromIndex) const
{
startFromIndex = qMax(0, startFromIndex);
for (int i = startFromIndex; i < rowCount(); ++i) {
if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
return i;
}
}
for (int i = 0; i < startFromIndex; ++i) {
if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
return i;
}
}
return -1;
}
2021-03-29 01:51:34 -07:00
KFileItem FolderModel::itemForIndex(const QModelIndex &index) const
{
return m_dirModel->itemForIndex(mapToSource(index));
}
QModelIndex FolderModel::indexForUrl(const QUrl &url) const
{
return m_dirModel->indexForUrl(url);
}
2021-09-19 20:04:33 -07:00
KFileItem FolderModel::fileItem(int index) const
{
if (index >= 0 && index < count()) {
return itemForIndex(FolderModel::index(index, 0));
}
return KFileItem();
}
2021-03-29 01:51:34 -07:00
QList<QUrl> FolderModel::selectedUrls() const
{
const auto indexes = m_selectionModel->selectedIndexes();
QList<QUrl> urls;
urls.reserve(indexes.count());
for (const QModelIndex &index : indexes) {
urls.append(itemForIndex(index).url());
}
return urls;
}
int FolderModel::currentIndex() const
{
return m_currentIndex;
}
2021-03-29 01:51:34 -07:00
QString FolderModel::url() const
{
return m_url;
}
void FolderModel::setUrl(const QString &url)
{
2021-03-29 18:48:59 -07:00
if (url.isEmpty())
return;
2021-09-26 21:24:42 -07:00
bool isTrash = url.startsWith("trash:/");
2021-09-26 07:01:24 -07:00
QUrl resolvedNewUrl = resolve(url);
QFileInfo info(resolvedNewUrl.toLocalFile());
2021-03-29 01:51:34 -07:00
2021-09-26 21:24:42 -07:00
if (!QFile::exists(resolvedNewUrl.toLocalFile()) && !isTrash) {
2021-08-31 14:31:38 -07:00
emit notification(tr("The file or folder %1 does not exist.").arg(url));
return;
}
2021-09-26 07:01:24 -07:00
// TODO: selected ?
2021-09-26 21:24:42 -07:00
if (info.isFile() && !isTrash) {
2021-09-26 07:01:24 -07:00
resolvedNewUrl = QUrl::fromLocalFile(info.dir().path());
}
2021-03-29 01:51:34 -07:00
// Refresh this directory.
if (url == m_url) {
2021-12-26 05:18:02 -08:00
refresh();
2021-03-29 01:51:34 -07:00
return;
}
2021-12-26 05:18:02 -08:00
setStatus(Status::Listing);
2021-03-29 01:51:34 -07:00
m_pathHistory.append(resolvedNewUrl);
beginResetModel();
2021-09-26 23:56:10 -07:00
m_url = resolvedNewUrl.toString(QUrl::PreferLocalFile);
2021-12-26 05:18:02 -08:00
m_dirModel->dirLister()->openUrl(isTrash ? QUrl(QStringLiteral("trash:/")) : resolvedNewUrl);
2021-03-29 01:51:34 -07:00
clearDragImages();
m_dragIndexes.clear();
endResetModel();
2021-12-26 05:18:02 -08:00
if (isTrash) {
refresh();
}
2021-03-29 01:51:34 -07:00
emit urlChanged();
emit resolvedUrlChanged();
}
QUrl FolderModel::resolvedUrl() const
{
return m_dirModel->dirLister()->url();
}
QUrl FolderModel::resolve(const QString &url)
{
QUrl resolvedUrl;
if (url.startsWith(QLatin1Char('~'))) {
resolvedUrl = QUrl::fromLocalFile(QDir::homePath());
} else {
resolvedUrl = QUrl::fromUserInput(url);
}
return resolvedUrl;
}
FolderModel::Status FolderModel::status() const
{
return m_status;
}
void FolderModel::setStatus(FolderModel::Status status)
{
if (m_status != status) {
m_status = status;
emit statusChanged();
}
}
int FolderModel::sortMode() const
{
return m_sortMode;
}
void FolderModel::setSortMode(int mode)
{
if (m_sortMode != mode) {
m_sortMode = mode;
if (mode == -1 /* Unsorted */) {
setDynamicSortFilter(false);
} else {
invalidateIfComplete();
sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
setDynamicSortFilter(true);
}
emit sortModeChanged();
}
}
bool FolderModel::sortDirsFirst() const
{
return m_sortDirsFirst;
}
void FolderModel::setSortDirsFirst(bool enable)
{
if (m_sortDirsFirst != enable) {
m_sortDirsFirst = enable;
if (m_sortMode != -1 /* Unsorted */) {
invalidateIfComplete();
sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
}
emit sortDirsFirstChanged();
}
}
2021-08-31 14:08:00 -07:00
int FolderModel::filterMode() const
{
return m_filterMode;
}
void FolderModel::setFilterMode(int filterMode)
{
if (m_filterMode != (FilterMode)filterMode) {
m_filterMode = (FilterMode)filterMode;
invalidateFilterIfComplete();
emit filterModeChanged();
}
}
QStringList FolderModel::filterMimeTypes() const
{
return m_mimeSet.values();
}
void FolderModel::setFilterMimeTypes(const QStringList &mimeList)
{
2021-09-01 06:56:26 -07:00
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
const QSet<QString> &set = QSet<QString>::fromList(mimeList);
#else
const QSet<QString> set(mimeList.constBegin(), mimeList.constEnd());
#endif
2021-08-31 14:08:00 -07:00
if (m_mimeSet != set) {
m_mimeSet = set;
invalidateFilterIfComplete();
emit filterMimeTypesChanged();
}
}
QString FolderModel::filterPattern() const
{
return m_filterPattern;
}
void FolderModel::setFilterPattern(const QString &pattern)
{
if (m_filterPattern == pattern) {
return;
}
m_filterPattern = pattern;
m_filterPatternMatchAll = (pattern == QLatin1String("*"));
const QStringList patterns = pattern.split(QLatin1Char(' '));
m_regExps.clear();
m_regExps.reserve(patterns.count());
foreach (const QString &pattern, patterns) {
QRegExp rx(pattern);
rx.setPatternSyntax(QRegExp::Wildcard);
rx.setCaseSensitivity(Qt::CaseInsensitive);
m_regExps.append(rx);
}
invalidateFilterIfComplete();
emit filterPatternChanged();
}
2021-03-29 01:51:34 -07:00
QObject *FolderModel::viewAdapter() const
{
return m_viewAdapter;
}
void FolderModel::setViewAdapter(QObject *adapter)
{
if (m_viewAdapter != adapter) {
ItemViewAdapter *viewAdapter = dynamic_cast<ItemViewAdapter *>(adapter);
m_viewAdapter = viewAdapter;
emit viewAdapterChanged();
}
}
bool FolderModel::dragging() const
{
return m_dragInProgress;
}
bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const
{
KFileItem item = dirModel->itemForIndex(index);
if (item.isDir()) {
return true;
}
return false;
}
bool FolderModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
const KDirModel *dirModel = static_cast<KDirModel *>(sourceModel());
if (m_sortDirsFirst || left.column() == KDirModel::Size) {
bool leftIsDir = isDir(left, dirModel);
bool rightIsDir = isDir(right, dirModel);
if (leftIsDir && !rightIsDir) {
return (sortOrder() == Qt::AscendingOrder);
}
if (!leftIsDir && rightIsDir) {
return (sortOrder() == Qt::DescendingOrder);
}
}
const KFileItem leftItem = dirModel->data(left, KDirModel::FileItemRole).value<KFileItem>();
const KFileItem rightItem = dirModel->data(right, KDirModel::FileItemRole).value<KFileItem>();
const int column = left.column();
int result = 0;
switch (column) {
case KDirModel::Size: {
if (isDir(left, dirModel) && isDir(right, dirModel)) {
const int leftChildCount = dirModel->data(left, KDirModel::ChildCountRole).toInt();
const int rightChildCount = dirModel->data(right, KDirModel::ChildCountRole).toInt();
if (leftChildCount < rightChildCount)
result = -1;
else if (leftChildCount > rightChildCount)
result = +1;
} else {
const KIO::filesize_t leftSize = leftItem.size();
const KIO::filesize_t rightSize = rightItem.size();
if (leftSize < rightSize)
result = -1;
else if (leftSize > rightSize)
result = +1;
}
break;
}
case KDirModel::ModifiedTime: {
const long long leftTime = leftItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
const long long rightTime = rightItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
if (leftTime < rightTime)
result = -1;
else if (leftTime > rightTime)
result = +1;
break;
}
case KDirModel::Type:
result = QString::compare(dirModel->data(left, Qt::DisplayRole).toString(), dirModel->data(right, Qt::DisplayRole).toString());
break;
default:
break;
}
if (result != 0)
return result < 0;
QCollator collator;
result = collator.compare(leftItem.text(), rightItem.text());
if (result != 0)
return result < 0;
result = collator.compare(leftItem.name(), rightItem.name());
if (result != 0)
return result < 0;
return QString::compare(leftItem.url().url(), rightItem.url().url(), Qt::CaseSensitive);
}
Qt::DropActions FolderModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}
Qt::DropActions FolderModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}
KFileItem FolderModel::rootItem() const
{
return m_dirModel->dirLister()->rootItem();
}
2021-04-01 01:05:15 -07:00
int FolderModel::count() const
2021-03-29 01:51:34 -07:00
{
2021-04-01 01:05:15 -07:00
return rowCount();
2021-03-29 01:51:34 -07:00
}
2021-05-30 00:33:45 -07:00
int FolderModel::selectionCount() const
2021-03-31 19:25:20 -07:00
{
return m_selectionModel->selectedIndexes().size();
}
2021-03-29 01:51:34 -07:00
QString FolderModel::homePath() const
{
return QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
}
QString FolderModel::desktopPath() const
{
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
QAction *FolderModel::action(const QString &name) const
{
return m_actionCollection.action(name);
}
void FolderModel::up()
{
const QUrl &url = KIO::upUrl(resolvedUrl());
if (url.isValid()) {
setUrl(url.toString());
}
}
void FolderModel::goBack()
{
QUrl url = m_pathHistory.previousPath();
if (url.isEmpty())
url = resolvedUrl();
setUrl(url.toString());
}
void FolderModel::goForward()
{
QUrl url = m_pathHistory.posteriorPath();
if (url.isEmpty())
url = resolvedUrl();
setUrl(url.toString());
}
2021-08-31 13:39:44 -07:00
void FolderModel::refresh()
{
m_dirModel->dirLister()->updateDirectory(m_dirModel->dirLister()->url());
}
2021-12-26 01:14:45 -08:00
void FolderModel::undo()
{
if (KIO::FileUndoManager::self()->undoAvailable()) {
KIO::FileUndoManager::self()->undo();
}
}
2021-03-29 01:51:34 -07:00
bool FolderModel::supportSetAsWallpaper(const QString &mimeType)
{
if (mimeType == "image/jpeg" || mimeType == "image/png")
return true;
return false;
}
int FolderModel::fileExtensionBoundary(int row)
{
const QModelIndex idx = index(row, 0);
const QString &name = data(idx, Qt::DisplayRole).toString();
int boundary = name.length();
if (data(idx, IsDirRole).toBool()) {
return boundary;
}
QMimeDatabase db;
const QString &ext = db.suffixForFileName(name);
if (ext.isEmpty()) {
boundary = name.lastIndexOf(QLatin1Char('.'));
if (boundary < 1) {
boundary = name.length();
}
} else {
boundary -= ext.length() + 1;
}
return boundary;
}
bool FolderModel::hasSelection() const
{
return m_selectionModel->hasSelection();
}
bool FolderModel::isSelected(int row) const
{
if (row < 0)
return false;
return m_selectionModel->isSelected(index(row, 0));
}
bool FolderModel::isBlank(int row) const
{
if (row < 0) {
return true;
}
return data(index(row, 0), BlankRole).toBool();
}
void FolderModel::setSelected(int row)
{
if (row < 0)
return;
m_selectionModel->select(index(row, 0), QItemSelectionModel::Select);
m_currentIndex = row;
emit currentIndexChanged();
2021-03-29 01:51:34 -07:00
}
void FolderModel::selectAll()
{
setRangeSelected(0, rowCount() - 1);
}
void FolderModel::toggleSelected(int row)
{
if (row < 0)
return;
m_selectionModel->select(index(row, 0), QItemSelectionModel::Toggle);
}
void FolderModel::setRangeSelected(int anchor, int to)
{
if (anchor < 0 || to < 0) {
return;
}
QItemSelection selection(index(anchor, 0), index(to, 0));
m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect);
}
void FolderModel::updateSelection(const QVariantList &rows, bool toggle)
{
QItemSelection newSelection;
int iRow = -1;
foreach (const QVariant &row, rows) {
iRow = row.toInt();
if (iRow < 0) {
return;
}
const QModelIndex &idx = index(iRow, 0);
newSelection.select(idx, idx);
}
if (toggle) {
QItemSelection pinnedSelection = m_pinnedSelection;
pinnedSelection.merge(newSelection, QItemSelectionModel::Toggle);
m_selectionModel->select(pinnedSelection, QItemSelectionModel::ClearAndSelect);
} else {
m_selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect);
}
}
void FolderModel::clearSelection()
{
if (m_selectionModel->hasSelection())
m_selectionModel->clear();
}
void FolderModel::pinSelection()
{
m_pinnedSelection = m_selectionModel->selection();
}
void FolderModel::unpinSelection()
{
m_pinnedSelection = QItemSelection();
}
void FolderModel::newFolder()
{
QString rootPath = rootItem().url().toString();
QString baseName = tr("New Folder");
QString newName = baseName;
int i = 0;
while (true) {
if (QFile::exists(rootItem().url().toLocalFile() + "/" + newName)) {
++i;
newName = QString("%1%2").arg(baseName).arg(QString::number(i));
} else {
break;
}
}
m_newDocumentUrl = QUrl(rootItem().url().toString() + "/" + newName);
auto job = KIO::mkdir(QUrl(rootItem().url().toString() + "/" + newName));
job->start();
}
2021-12-26 09:32:21 -08:00
void FolderModel::newTextFile()
{
QString rootPath = rootItem().url().toString();
QString baseName = tr("New Text");
QString newName = baseName;
int i = 0;
while (true) {
if (QFile::exists(rootItem().url().toLocalFile() + "/" + newName)) {
++i;
newName = QString("%1%2").arg(baseName).arg(QString::number(i));
} else {
break;
}
}
m_newDocumentUrl = QUrl(rootItem().url().toString() + "/" + newName);
QFile srcFile(":/templates/TextFile.txt");
if (!srcFile.open(QIODevice::ReadOnly)) {
return;
}
srcFile.copy(m_newDocumentUrl.toLocalFile());
}
2021-03-29 01:51:34 -07:00
void FolderModel::rename(int row, const QString &name)
{
if (row < 0)
return;
QModelIndex idx = index(row, 0);
m_dirModel->setData(mapToSource(idx), name, Qt::EditRole);
}
void FolderModel::copy()
{
if (!m_selectionModel->hasSelection())
return;
if (QAction *action = m_actionCollection.action("copy"))
if (!action->isEnabled())
return;
QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes());
QApplication::clipboard()->setMimeData(mimeData);
}
void FolderModel::paste()
{
2021-06-15 21:43:43 -07:00
const QMimeData *mimeData = QApplication::clipboard()->mimeData();
bool enable = false;
// Update paste action
if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) {
QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
if (!urls.isEmpty()) {
if (!rootItem().isNull()) {
enable = rootItem().isWritable();
}
}
paste->setEnabled(enable);
}
2021-03-29 01:51:34 -07:00
2021-06-15 21:43:43 -07:00
if (enable) {
KIO::paste(mimeData, m_dirModel->dirLister()->url());
}
2021-03-29 01:51:34 -07:00
}
void FolderModel::cut()
{
if (!m_selectionModel->hasSelection())
return;
if (QAction *action = m_actionCollection.action("cut"))
if (!action->isEnabled())
return;
QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes());
KIO::setClipboardDataCut(mimeData, true);
QApplication::clipboard()->setMimeData(mimeData);
}
void FolderModel::openSelected()
{
if (!m_selectionModel->hasSelection())
return;
const QList<QUrl> urls = selectedUrls();
2021-03-29 07:53:21 -07:00
if (!m_isDesktop) {
if (urls.size() == 1 && KFileItem(urls.first()).isDir()) {
setUrl(urls.first().toLocalFile());
return;
}
2021-03-29 01:51:34 -07:00
}
for (const QUrl &url : urls) {
2021-07-28 12:30:17 -07:00
KFileItem item(url);
2021-07-28 12:44:29 -07:00
QString mimeType = item.mimetype();
// Desktop file.
if (mimeType == "application/x-desktop") {
FileLauncher::self()->launchApp(url.toLocalFile(), "");
continue;
}
2021-07-28 12:44:29 -07:00
// runnable
if (mimeType == "application/x-executable" ||
mimeType == "application/x-sharedlib" ||
mimeType == "application/x-iso9660-appimage" ||
mimeType == "application/vnd.appimage") {
QFileInfo fileInfo(url.toLocalFile());
if (!fileInfo.isExecutable()) {
QFile file(url.toLocalFile());
file.setPermissions(file.permissions() | QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
}
FileLauncher::self()->launchExecutable(url.toLocalFile());
continue;
}
2021-07-28 12:30:17 -07:00
QString defaultAppDesktopFile = m_mimeAppManager->getDefaultAppByMimeType(item.currentMimeType());
// If no default application is found,
// look for the first one of the frequently used applications.
if (defaultAppDesktopFile.isEmpty()) {
QStringList recommendApps = m_mimeAppManager->getRecommendedAppsByMimeType(item.currentMimeType());
if (recommendApps.count() > 0) {
defaultAppDesktopFile = recommendApps.first();
}
}
if (!defaultAppDesktopFile.isEmpty()) {
FileLauncher::self()->launchApp(defaultAppDesktopFile, url.toLocalFile());
continue;
}
QDesktopServices::openUrl(url);
2021-03-29 01:51:34 -07:00
}
}
2021-07-29 02:38:47 -07:00
void FolderModel::showOpenWithDialog()
{
if (!m_selectionModel->hasSelection())
return;
const QList<QUrl> urls = selectedUrls();
OpenWithDialog *dlg = new OpenWithDialog(urls.first());
dlg->show();
}
2021-03-29 01:51:34 -07:00
void FolderModel::deleteSelected()
{
if (!m_selectionModel->hasSelection()) {
return;
}
if (QAction *action = m_actionCollection.action(QStringLiteral("del"))) {
if (!action->isEnabled()) {
return;
}
}
2021-09-03 11:26:01 -07:00
KIO::DeleteJob *job = KIO::del(selectedUrls());
job->start();
2021-03-29 01:51:34 -07:00
}
void FolderModel::moveSelectedToTrash()
{
if (!m_selectionModel->hasSelection()) {
return;
}
if (QAction *action = m_actionCollection.action(QStringLiteral("trash"))) {
if (!action->isEnabled()) {
return;
}
}
const QList<QUrl> urls = selectedUrls();
KIO::JobUiDelegate uiDelegate;
if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
KIO::Job *job = KIO::trash(urls);
job->uiDelegate()->setAutoErrorHandlingEnabled(true);
KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl(QStringLiteral("trash:/")), job);
}
}
void FolderModel::emptyTrash()
{
2021-05-21 00:55:58 -07:00
QProcess::startDetached("cutefish-filemanager", QStringList() << "-e");
2021-03-29 01:51:34 -07:00
}
2021-03-31 19:25:20 -07:00
void FolderModel::keyDeletePress()
{
if (!m_selectionModel->hasSelection())
return;
2021-09-03 11:26:01 -07:00
resolvedUrl().scheme() == "trash" ? openDeleteDialog() : moveSelectedToTrash();
2021-03-31 19:25:20 -07:00
}
2021-03-29 01:51:34 -07:00
void FolderModel::setDragHotSpotScrollOffset(int x, int y)
{
m_dragHotSpotScrollOffset.setX(x);
m_dragHotSpotScrollOffset.setY(y);
}
void FolderModel::addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image)
{
if (row < 0)
return;
delete m_dragImages.take(row);
DragImage *dragImage = new DragImage();
dragImage->row = row;
dragImage->rect = QRect(x, y, width, height);
dragImage->image = image.value<QImage>();
dragImage->blank = false;
m_dragImages.insert(row, dragImage);
}
void FolderModel::clearDragImages()
{
qDeleteAll(m_dragImages);
m_dragImages.clear();
}
void FolderModel::dragSelected(int x, int y)
{
if (m_dragInProgress)
return;
m_dragInProgress = true;
emit draggingChanged();
QMetaObject::invokeMethod(this, "dragSelectedInternal", Qt::QueuedConnection, Q_ARG(int, x), Q_ARG(int, y));
}
2021-11-03 10:35:09 -07:00
void FolderModel::drop(QQuickItem *target, QObject *dropEvent, int row)
{
QMimeData *mimeData = qobject_cast<QMimeData *>(dropEvent->property("mimeData").value<QObject *>());
if (!mimeData) {
return;
}
QModelIndex idx;
KFileItem item;
if (row > -1 && row < rowCount()) {
idx = index(row, 0);
item = itemForIndex(idx);
}
QUrl dropTargetUrl;
// So we get to run mostLocalUrl() over the current URL.
if (item.isNull()) {
item = rootItem();
}
if (item.isNull()) {
dropTargetUrl = m_dirModel->dirLister()->url();
} else {
dropTargetUrl = item.mostLocalUrl();
}
2021-11-13 16:01:00 -08:00
auto dropTargetFolderUrl = dropTargetUrl;
if (dropTargetFolderUrl.fileName() == QLatin1Char('.')) {
// the target URL for desktop:/ is e.g. 'file://home/user/Desktop/.'
dropTargetFolderUrl = dropTargetFolderUrl.adjusted(QUrl::RemoveFilename);
2021-11-03 10:35:09 -07:00
}
2021-11-13 16:01:00 -08:00
const int x = dropEvent->property("x").toInt();
const int y = dropEvent->property("y").toInt();
const QPoint dropPos = {x, y};
if (m_dragInProgress && row == -1) {
if (mimeData->urls().isEmpty())
return;
2021-11-03 10:35:09 -07:00
2021-11-13 16:01:00 -08:00
setSortMode(-1);
for (const auto &url : mimeData->urls()) {
m_dropTargetPositions.insert(url.fileName(), dropPos);
2021-11-03 10:35:09 -07:00
}
2021-11-13 16:01:00 -08:00
emit move(x, y, mimeData->urls());
return;
}
if (idx.isValid() && !(flags(idx) & Qt::ItemIsDropEnabled)) {
return;
2021-11-03 10:35:09 -07:00
}
2021-11-13 16:01:00 -08:00
if (!isDropBetweenSharedViews(mimeData->urls(), dropTargetFolderUrl)) {
KIO::Job *job = KIO::move(mimeData->urls(), dropTargetUrl, KIO::HideProgressInfo);
2021-11-03 10:35:09 -07:00
job->start();
}
}
2021-03-29 01:51:34 -07:00
void FolderModel::setWallpaperSelected()
{
if (!m_selectionModel)
return;
QUrl url = selectedUrls().first();
if (!url.isLocalFile())
return;
2021-09-15 00:46:38 -07:00
QDBusInterface iface("com.cutefish.Settings", "/Theme",
"com.cutefish.Theme",
2021-03-29 01:51:34 -07:00
QDBusConnection::sessionBus(), nullptr);
if (iface.isValid())
iface.call("setWallpaper", url.toLocalFile());
}
void FolderModel::openContextMenu(QQuickItem *visualParent, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(modifiers);
updateActions();
const QModelIndexList indexes = m_selectionModel->selectedIndexes();
QMenu *menu = new QMenu;
// Open folder menu.
2021-03-29 01:51:34 -07:00
if (indexes.isEmpty()) {
QAction *selectAll = new QAction(tr("Select All"), this);
connect(selectAll, &QAction::triggered, this, &FolderModel::selectAll);
2021-12-26 09:32:21 -08:00
QMenu *newMenu = new QMenu(tr("New Documents"));
newMenu->addAction(m_actionCollection.action("newTextFile"));
2021-03-29 01:51:34 -07:00
menu->addAction(m_actionCollection.action("newFolder"));
2021-12-26 09:32:21 -08:00
menu->addMenu(newMenu);
2021-03-29 01:51:34 -07:00
menu->addSeparator();
menu->addAction(m_actionCollection.action("paste"));
menu->addAction(selectAll);
if (m_actionCollection.action("terminal")->isVisible()) {
menu->addSeparator();
menu->addAction(m_actionCollection.action("terminal"));
}
if (m_isDesktop) {
menu->addAction(m_actionCollection.action("changeBackground"));
}
2021-09-16 00:01:07 -07:00
menu->addSeparator();
2021-09-12 04:30:01 -07:00
menu->addAction(m_actionCollection.action("showHidden"));
2021-03-29 01:51:34 -07:00
menu->addSeparator();
menu->addAction(m_actionCollection.action("emptyTrash"));
menu->addAction(m_actionCollection.action("properties"));
} else {
// Open the items menu.
2021-09-03 10:47:54 -07:00
// Trash items
menu->addAction(m_actionCollection.action("restore"));
2021-03-29 01:51:34 -07:00
menu->addAction(m_actionCollection.action("open"));
2021-10-04 05:50:59 -07:00
menu->addAction(m_actionCollection.action("openInNewWindow"));
2021-07-29 02:38:47 -07:00
menu->addAction(m_actionCollection.action("openWith"));
2021-10-04 05:50:59 -07:00
menu->addSeparator();
2021-03-29 01:51:34 -07:00
menu->addAction(m_actionCollection.action("cut"));
menu->addAction(m_actionCollection.action("copy"));
menu->addAction(m_actionCollection.action("trash"));
menu->addAction(m_actionCollection.action("del"));
menu->addAction(m_actionCollection.action("rename"));
if (m_actionCollection.action("terminal")->isVisible()) {
menu->addSeparator();
menu->addAction(m_actionCollection.action("terminal"));
}
menu->addAction(m_actionCollection.action("wallpaper"));
menu->addSeparator();
menu->addAction(m_actionCollection.action("properties"));
}
QPoint position;
if (visualParent) {
position = visualParent->mapToGlobal(QPointF(0, visualParent->height())).toPoint();
} else {
position = QCursor::pos();
}
menu->installEventFilter(this);
menu->setAttribute(Qt::WA_TranslucentBackground);
menu->winId();
menu->popup(position);
connect(menu, &QMenu::aboutToHide, [menu]() {
menu->deleteLater();
});
}
void FolderModel::openPropertiesDialog()
{
const QModelIndexList indexes = m_selectionModel->selectedIndexes();
if (indexes.isEmpty()) {
FilePropertiesDialog *dlg = new FilePropertiesDialog(QUrl::fromLocalFile(url()));
dlg->show();
2021-03-29 01:51:34 -07:00
return;
}
KFileItemList items;
items.reserve(indexes.count());
for (const QModelIndex &index : indexes) {
KFileItem item = itemForIndex(index);
if (!item.isNull()) {
items.append(item);
}
}
FilePropertiesDialog *dlg = new FilePropertiesDialog(items);
dlg->show();
2021-03-29 01:51:34 -07:00
}
void FolderModel::openInTerminal()
{
QString url;
if (m_selectionModel->hasSelection()) {
KFileItem item = itemForIndex(m_selectionModel->selectedIndexes().first());
if (item.isDir()) {
url = item.url().toLocalFile();
}
} else {
url = rootItem().url().toLocalFile();
}
2021-07-28 12:30:17 -07:00
m_mimeAppManager->launchTerminal(url);
2021-03-29 01:51:34 -07:00
}
void FolderModel::openChangeWallpaperDialog()
{
QProcess::startDetached("cutefish-settings", QStringList() << "-m" << "background");
}
2021-09-03 11:26:01 -07:00
void FolderModel::openDeleteDialog()
{
QQuickView *view = new QQuickView;
view->setModality(Qt::ApplicationModal);
view->setFlags(Qt::Dialog);
view->setTitle(tr("File Manager"));
view->setResizeMode(QQuickView::SizeRootObjectToView);
view->setSource(QUrl("qrc:/qml/Dialogs/DeleteDialog.qml"));
view->rootContext()->setContextProperty("model", this);
view->rootContext()->setContextProperty("view", view);
connect(view, &QQuickView::visibleChanged, this, [=] {
if (!view->isVisible()) {
view->deleteLater();
}
});
view->show();
}
2021-12-02 11:58:55 -08:00
void FolderModel::openInNewWindow(const QString &url)
2021-10-04 05:50:59 -07:00
{
2021-12-02 11:58:55 -08:00
if (!url.isEmpty()) {
QProcess::startDetached("cutefish-filemanager", QStringList() << url);
return;
}
// url 为空则打开已选择的 items.
2021-10-04 05:50:59 -07:00
if (!m_selectionModel->hasSelection())
return;
for (const QModelIndex &index : m_selectionModel->selectedIndexes()) {
KFileItem item = itemForIndex(index);
if (item.isDir()) {
QProcess::startDetached("cutefish-filemanager", QStringList() << item.url().toLocalFile());
}
}
}
2021-09-05 14:39:56 -07:00
void FolderModel::updateSelectedItemsSize()
{
}
2021-09-27 21:00:19 -07:00
void FolderModel::keyboardSearch(const QString &text)
{
if (rowCount() == 0)
return;
int index;
int currentIndex = -1;
if (m_selectionModel->hasSelection()) {
currentIndex = m_selectionModel->selectedIndexes().first().row();
}
index = indexForKeyboardSearch(text, (currentIndex + 1) % rowCount());
if (index < 0 || currentIndex == index)
return;
if (index >= 0) {
clearSelection();
setSelected(index);
2021-12-04 23:15:35 -08:00
emit scrollToItem(index);
2021-09-27 21:00:19 -07:00
}
}
2021-10-15 03:57:18 -07:00
void FolderModel::clearPixmapCache()
{
QPixmapCache::clear();
}
2021-09-03 10:47:54 -07:00
void FolderModel::restoreFromTrash()
{
if (!m_selectionModel->hasSelection())
return;
if (QAction *action = m_actionCollection.action("restore"))
if (!action->isVisible())
return;
KIO::RestoreJob *job = KIO::restoreFromTrash(selectedUrls());
job->start();
}
2021-03-29 01:51:34 -07:00
void FolderModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
QModelIndexList indices = selected.indexes();
indices.append(deselected.indexes());
QVector<int> roles;
roles.append(SelectedRole);
foreach (const QModelIndex &index, indices) {
emit dataChanged(index, index, roles);
}
if (!m_selectionModel->hasSelection()) {
clearDragImages();
} else {
foreach (const QModelIndex &idx, deselected.indexes()) {
delete m_dragImages.take(idx.row());
}
}
updateActions();
emit selectionCountChanged();
// The desktop does not need to calculate the selected file size.
if (m_isDesktop)
return;
// Start calculating file size.
2021-09-05 14:39:56 -07:00
if (m_sizeJob == nullptr) {
m_sizeJob = new CFileSizeJob;
connect(m_sizeJob, &CFileSizeJob::sizeChanged, this, [=] {
m_selectedItemSize = KIO::convertSize(m_sizeJob->totalSize());
if (!m_selectionModel->hasSelection())
m_selectedItemSize = "";
emit selectedItemSizeChanged();
});
connect(m_sizeJob, &CFileSizeJob::result, this, [=] {
m_selectedItemSize = KIO::convertSize(m_sizeJob->totalSize());
if (!m_selectionModel->hasSelection())
m_selectedItemSize = "";
emit selectedItemSizeChanged();
});
}
m_sizeJob->stop();
if (!m_selectionModel->hasSelection()) {
m_sizeJob->blockSignals(true);
m_selectedItemSize = "";
emit selectedItemSizeChanged();
} else {
bool fileExists = false;
2021-09-05 14:39:56 -07:00
for (const QModelIndex &index : m_selectionModel->selectedIndexes()) {
if (itemForIndex(index).isFile()) {
fileExists = true;
break;
}
}
2021-03-29 01:51:34 -07:00
// Reion: The size label at the bottom needs to be updated
// only if you select the include file.
if (fileExists) {
m_sizeJob->blockSignals(false);
m_sizeJob->start(selectedUrls());
}
}
2021-03-29 01:51:34 -07:00
}
void FolderModel::dragSelectedInternal(int x, int y)
{
if (!m_viewAdapter || !m_selectionModel->hasSelection()) {
m_dragInProgress = false;
emit draggingChanged();
return;
}
QQuickItem *item = qobject_cast<QQuickItem *>(m_viewAdapter->adapterView());
QDrag *drag = new QDrag(item);
addDragImage(drag, x, y);
m_dragIndexes = m_selectionModel->selectedIndexes();
std::sort(m_dragIndexes.begin(), m_dragIndexes.end());
// TODO: Optimize to emit contiguous groups.
emit dataChanged(m_dragIndexes.first(), m_dragIndexes.last(), QVector<int>() << BlankRole);
QModelIndexList sourceDragIndexes;
sourceDragIndexes.reserve(m_dragIndexes.count());
foreach (const QModelIndex &index, m_dragIndexes) {
sourceDragIndexes.append(mapToSource(index));
}
drag->setMimeData(m_dirModel->mimeData(sourceDragIndexes));
// Due to spring-loading (aka auto-expand), the URL might change
// while the drag is in-flight - in that case we don't want to
// unnecessarily emit dataChanged() for (possibly invalid) indices
// after it ends.
const QUrl currentUrl(m_dirModel->dirLister()->url());
item->grabMouse();
drag->exec(supportedDragActions());
item->ungrabMouse();
m_dragInProgress = false;
emit draggingChanged();
if (m_dirModel->dirLister()->url() == currentUrl) {
const QModelIndex first(m_dragIndexes.first());
const QModelIndex last(m_dragIndexes.last());
m_dragIndexes.clear();
// TODO: Optimize to emit contiguous groups.
emit dataChanged(first, last, QVector<int>() << BlankRole);
}
}
bool FolderModel::isSupportThumbnails(const QString &mimeType) const
{
const QStringList supportsMimetypes = {"image/bmp", "image/png", "image/gif", "image/jpeg", "image/web",
2021-06-01 22:25:04 -07:00
/*"application/pdf", "application/rtf", "application/doc", "application/odf",
"audio/mpeg", "video/mp4"*/};
2021-03-29 01:51:34 -07:00
if (supportsMimetypes.contains(mimeType))
return true;
return false;
}
2021-08-31 14:08:00 -07:00
bool FolderModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
const KDirModel *dirModel = static_cast<KDirModel *>(sourceModel());
const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent));
if (m_filterMode == NoFilter) {
return true;
}
if (m_filterMode == FilterShowMatches) {
return (matchPattern(item) && matchMimeType(item));
} else {
return !(matchPattern(item) && matchMimeType(item));
}
}
bool FolderModel::matchMimeType(const KFileItem &item) const
{
if (m_mimeSet.isEmpty()) {
return false;
}
if (m_mimeSet.contains(QLatin1String("all/all")) || m_mimeSet.contains(QLatin1String("all/allfiles"))) {
return true;
}
const QString mimeType = item.determineMimeType().name();
return m_mimeSet.contains(mimeType);
}
bool FolderModel::matchPattern(const KFileItem &item) const
{
if (m_filterPatternMatchAll) {
return true;
}
const QString name = item.name();
QListIterator<QRegExp> i(m_regExps);
while (i.hasNext()) {
if (i.next().exactMatch(name)) {
return true;
}
}
return false;
}
2021-09-12 04:30:01 -07:00
bool FolderModel::showHiddenFiles() const
{
return m_showHiddenFiles;
}
void FolderModel::setShowHiddenFiles(bool showHiddenFiles)
{
if (m_showHiddenFiles != showHiddenFiles) {
m_showHiddenFiles = showHiddenFiles;
m_dirLister->setShowingDotFiles(m_showHiddenFiles);
m_dirLister->emitChanges();
QSettings settings("cutefishos", qApp->applicationName());
settings.setValue("showHiddenFiles", m_showHiddenFiles);
emit showHiddenFilesChanged();
}
}
2021-09-05 14:39:56 -07:00
QString FolderModel::selectedItemSize() const
{
return m_selectedItemSize;
}
2021-03-29 07:53:21 -07:00
bool FolderModel::isDesktop() const
{
return m_isDesktop;
}
void FolderModel::setIsDesktop(bool isDesktop)
{
if (m_isDesktop == isDesktop)
return;
m_isDesktop = isDesktop;
emit isDesktopChanged();
}
2021-03-29 01:51:34 -07:00
void FolderModel::invalidateIfComplete()
{
if (!m_complete)
return;
invalidate();
}
void FolderModel::invalidateFilterIfComplete()
{
if (!m_complete)
return;
invalidateFilter();
}
void FolderModel::createActions()
{
QAction *open = new QAction(tr("Open"), this);
connect(open, &QAction::triggered, this, &FolderModel::openSelected);
2021-07-29 02:38:47 -07:00
QAction *openWith = new QAction(tr("Open with"), this);
connect(openWith, &QAction::triggered, this, &FolderModel::showOpenWithDialog);
2021-03-29 01:51:34 -07:00
QAction *cut = new QAction(tr("Cut"), this);
connect(cut, &QAction::triggered, this, &FolderModel::cut);
QAction *copy = new QAction(tr("Copy"), this);
connect(copy, &QAction::triggered, this, &FolderModel::copy);
QAction *paste = new QAction(tr("Paste"), this);
connect(paste, &QAction::triggered, this, &FolderModel::paste);
QAction *newFolder = new QAction(tr("New Folder"), this);
connect(newFolder, &QAction::triggered, this, &FolderModel::newFolder);
2021-03-29 01:51:34 -07:00
2021-12-26 09:32:21 -08:00
QMenu *newDocuments = new QMenu(tr("New Documents"));
QAction *newTextFile = new QAction(tr("New Text"), this);
connect(newTextFile, &QAction::triggered, this, &FolderModel::newTextFile);
newDocuments->addAction(newTextFile);
2021-03-29 01:51:34 -07:00
QAction *trash = new QAction(tr("Move To Trash"), this);
connect(trash, &QAction::triggered, this, &FolderModel::moveSelectedToTrash);
QAction *emptyTrash = new QAction(tr("Empty Trash"), this);
connect(emptyTrash, &QAction::triggered, this, &FolderModel::emptyTrash);
QAction *del = new QAction(tr("Delete"), this);
2021-09-03 11:26:01 -07:00
connect(del, &QAction::triggered, this, &FolderModel::openDeleteDialog);
2021-03-29 01:51:34 -07:00
QAction *rename = new QAction(tr("Rename"), this);
connect(rename, &QAction::triggered, this, &FolderModel::requestRename);
QAction *terminal = new QAction(tr("Open in Terminal"), this);
connect(terminal, &QAction::triggered, this, &FolderModel::openInTerminal);
QAction *wallpaper = new QAction(tr("Set as Wallpaper"), this);
connect(wallpaper, &QAction::triggered, this, &FolderModel::setWallpaperSelected);
QAction *properties = new QAction(tr("Properties"), this);
QObject::connect(properties, &QAction::triggered, this, &FolderModel::openPropertiesDialog);
QAction *changeBackground = new QAction(tr("Change background"), this);
QObject::connect(changeBackground, &QAction::triggered, this, &FolderModel::openChangeWallpaperDialog);
2021-09-03 10:47:54 -07:00
QAction *restore = new QAction(tr("Restore"), this);
QObject::connect(restore, &QAction::triggered, this, &FolderModel::restoreFromTrash);
2021-09-12 04:30:01 -07:00
QAction *showHidden = new QAction(tr("Show hidden files"), this);
QObject::connect(showHidden, &QAction::triggered, this, [=] {
setShowHiddenFiles(!m_showHiddenFiles);
});
2021-10-04 05:50:59 -07:00
QAction *openInNewWindow = new QAction(tr("Open in new window"), this);
2021-12-02 11:58:55 -08:00
QObject::connect(openInNewWindow, &QAction::triggered, this, [=] { this->openInNewWindow(); });
2021-10-04 05:50:59 -07:00
2021-03-29 01:51:34 -07:00
m_actionCollection.addAction(QStringLiteral("open"), open);
2021-07-29 02:38:47 -07:00
m_actionCollection.addAction(QStringLiteral("openWith"), openWith);
2021-03-29 01:51:34 -07:00
m_actionCollection.addAction(QStringLiteral("cut"), cut);
m_actionCollection.addAction(QStringLiteral("copy"), copy);
m_actionCollection.addAction(QStringLiteral("paste"), paste);
m_actionCollection.addAction(QStringLiteral("newFolder"), newFolder);
2021-12-26 09:32:21 -08:00
m_actionCollection.addAction(QStringLiteral("newTextFile"), newTextFile);
2021-03-29 01:51:34 -07:00
m_actionCollection.addAction(QStringLiteral("trash"), trash);
m_actionCollection.addAction(QStringLiteral("emptyTrash"), emptyTrash);
m_actionCollection.addAction(QStringLiteral("del"), del);
m_actionCollection.addAction(QStringLiteral("rename"), rename);
m_actionCollection.addAction(QStringLiteral("terminal"), terminal);
m_actionCollection.addAction(QStringLiteral("wallpaper"), wallpaper);
m_actionCollection.addAction(QStringLiteral("properties"), properties);
m_actionCollection.addAction(QStringLiteral("changeBackground"), changeBackground);
2021-09-03 10:47:54 -07:00
m_actionCollection.addAction(QStringLiteral("restore"), restore);
2021-09-12 04:30:01 -07:00
m_actionCollection.addAction(QStringLiteral("showHidden"), showHidden);
2021-10-04 05:50:59 -07:00
m_actionCollection.addAction(QStringLiteral("openInNewWindow"), openInNewWindow);
2021-03-29 01:51:34 -07:00
}
void FolderModel::updateActions()
{
const QModelIndexList indexes = m_selectionModel->selectedIndexes();
KFileItemList items;
QList<QUrl> urls;
bool hasRemoteFiles = false;
bool isTrashLink = false;
2021-10-04 05:50:59 -07:00
bool hasDir = false;
2021-03-29 01:51:34 -07:00
const bool isTrash = (resolvedUrl().scheme() == QLatin1String("trash"));
if (indexes.isEmpty()) {
items << rootItem();
} else {
items.reserve(indexes.count());
urls.reserve(indexes.count());
for (const QModelIndex &index : indexes) {
KFileItem item = itemForIndex(index);
if (!item.isNull()) {
hasRemoteFiles |= item.localPath().isEmpty();
items.append(item);
urls.append(item.url());
}
2021-10-04 05:50:59 -07:00
if (item.isDir())
hasDir = true;
2021-03-29 01:51:34 -07:00
}
}
KFileItemListProperties itemProperties(items);
// Check if we're showing the menu for the trash link
if (items.count() == 1 && items.at(0).isDesktopFile()) {
KDesktopFile file(items.at(0).localPath());
if (file.hasLinkType() && file.readUrl() == QLatin1String("trash:/")) {
isTrashLink = true;
}
}
2021-09-03 10:47:54 -07:00
if (QAction *openAction = m_actionCollection.action(QStringLiteral("open"))) {
openAction->setVisible(!isTrash);
}
if (QAction *copyAction = m_actionCollection.action(QStringLiteral("copy"))) {
copyAction->setVisible(!isTrash);
}
if (QAction *cutAction = m_actionCollection.action(QStringLiteral("cut"))) {
cutAction->setVisible(!isTrash);
}
if (QAction *restoreAction = m_actionCollection.action(QStringLiteral("restore"))) {
restoreAction->setVisible(items.count() >= 1 && isTrash);
}
2021-07-29 02:38:47 -07:00
if (QAction *openWith = m_actionCollection.action(QStringLiteral("openWith"))) {
2021-09-03 10:47:54 -07:00
openWith->setVisible(items.count() == 1 && !isTrash);
2021-07-29 02:38:47 -07:00
}
2021-03-29 01:51:34 -07:00
if (QAction *newFolder = m_actionCollection.action(QStringLiteral("newFolder"))) {
newFolder->setVisible(!isTrash);
newFolder->setEnabled(rootItem().isWritable());
}
2021-12-26 09:32:21 -08:00
if (QAction *newTextFile = m_actionCollection.action(QStringLiteral("newTextFile"))) {
newTextFile->setVisible(!isTrash);
newTextFile->setEnabled(rootItem().isWritable());
}
2021-03-29 01:51:34 -07:00
if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) {
bool enable = false;
2021-06-15 21:43:43 -07:00
const QMimeData *mimeData = QApplication::clipboard()->mimeData();
QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
2021-03-29 01:51:34 -07:00
2021-06-15 21:43:43 -07:00
if (!urls.isEmpty()) {
if (!rootItem().isNull()) {
enable = rootItem().isWritable();
}
2021-03-29 01:51:34 -07:00
}
paste->setEnabled(enable);
}
if (QAction *rename = m_actionCollection.action(QStringLiteral("rename"))) {
rename->setEnabled(itemProperties.supportsMoving());
rename->setVisible(!isTrash);
}
if (QAction *trash = m_actionCollection.action("trash")) {
trash->setEnabled(itemProperties.supportsMoving());
trash->setVisible(!isTrash);
}
if (QAction *emptyTrash = m_actionCollection.action("emptyTrash")) {
emptyTrash->setVisible(isTrash);
}
if (QAction *del = m_actionCollection.action(QStringLiteral("del"))) {
del->setVisible(isTrash && itemProperties.supportsDeleting());
}
if (QAction *terminal = m_actionCollection.action("terminal")) {
2021-09-03 10:47:54 -07:00
terminal->setVisible(items.size() == 1 && items.first().isDir() && !isTrash);
2021-03-29 01:51:34 -07:00
}
if (QAction *terminal = m_actionCollection.action("wallpaper")) {
2021-12-26 05:23:58 -08:00
terminal->setVisible(items.size() == 1 &&
!isTrash &&
supportSetAsWallpaper(items.first().mimetype()));
2021-03-29 01:51:34 -07:00
}
if (QAction *properties = m_actionCollection.action("properties")) {
properties->setVisible(!isTrash);
}
2021-09-12 04:30:01 -07:00
if (QAction *showHidden = m_actionCollection.action("showHidden")) {
2021-12-26 05:23:58 -08:00
showHidden->setVisible(!isTrash);
2021-09-12 04:30:01 -07:00
showHidden->setCheckable(true);
showHidden->setChecked(m_showHiddenFiles);
}
2021-10-04 05:50:59 -07:00
if (QAction *openInNewWindow = m_actionCollection.action("openInNewWindow")) {
openInNewWindow->setVisible(hasDir);
}
2021-03-29 01:51:34 -07:00
}
void FolderModel::addDragImage(QDrag *drag, int x, int y)
{
if (!drag || m_dragImages.isEmpty())
return;
QRegion region;
foreach (DragImage *image, m_dragImages) {
image->blank = isBlank(image->row);
image->rect.translate(-m_dragHotSpotScrollOffset.x(), -m_dragHotSpotScrollOffset.y());
if (!image->blank && !image->image.isNull()) {
region = region.united(image->rect);
}
}
QRect rect = region.boundingRect();
QPoint offset = rect.topLeft();
rect.translate(-offset.x(), -offset.y());
QImage dragImage(rect.size(), QImage::Format_RGBA8888);
dragImage.fill(Qt::transparent);
QPainter painter(&dragImage);
QPoint pos;
foreach (DragImage *image, m_dragImages) {
if (!image->blank && !image->image.isNull()) {
pos = image->rect.translated(-offset.x(), -offset.y()).topLeft();
image->cursorOffset.setX(pos.x() - (x - offset.x()));
image->cursorOffset.setY(pos.y() - (y - offset.y()));
painter.drawImage(pos, image->image);
}
// FIXME HACK: Operate on copy.
image->rect.translate(m_dragHotSpotScrollOffset.x(), m_dragHotSpotScrollOffset.y());
}
drag->setPixmap(QPixmap::fromImage(dragImage));
drag->setHotSpot(QPoint(x - offset.x(), y - offset.y()));
}