peony/peony-qt-desktop/desktop-item-model.cpp

973 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Peony-Qt
*
* Copyright (C) 2020, KylinSoft Co., Ltd.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*
* Authors: Yue Lan <lanyue@kylinos.cn>
*
*/
#include "desktop-item-model.h"
#include "file-enumerator.h"
#include "file-info.h"
#include "file-info-job.h"
#include "file-info-manager.h"
#include "file-watcher.h"
#include "file-operation-manager.h"
#include "file-move-operation.h"
#include "file-trash-operation.h"
#include "file-copy-operation.h"
#include "file-operation-utils.h"
#include "thumbnail-manager.h"
#include "file-meta-info.h"
#include "peony-desktop-application.h"
#include "desktop-icon-view.h"
#include "global-settings.h"
#include "sound-effect.h"
#include <QStandardPaths>
#include <QIcon>
#include <QMimeData>
#include <QUrl>
#include <QTimer>
#include <QMessageBox>
#include <QDebug>
using namespace Peony;
DesktopItemModel::DesktopItemModel(QObject *parent)
: QAbstractListModel(parent)
{
// do not redo layout new items while we start an operation with peony's api.
connect(FileOperationManager::getInstance(), &FileOperationManager::operationStarted, this, [=](){
m_items_need_relayout.clear();
});
m_thumbnail_watcher = std::make_shared<FileWatcher>("thumbnail:///, this");
connect(m_thumbnail_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri) {
for (auto info : m_files) {
if (info->uri() == uri) {
auto index = indexFromUri(uri);
Q_EMIT this->dataChanged(index, index);
}
}
});
m_trash_watcher = std::make_shared<FileWatcher>("trash:///", this);
this->connect(m_trash_watcher.get(), &FileWatcher::fileCreated, [=]() {
//qDebug()<<"trash changed";
auto trash = FileInfo::fromUri("trash:///");
auto job = new FileInfoJob(trash);
job->setAutoDelete();
connect(job, &FileInfoJob::infoUpdated, [=]() {
auto trashIndex = this->indexFromUri("trash:///");
this->dataChanged(trashIndex, trashIndex);
Q_EMIT this->requestClearIndexWidget();
});
job->queryAsync();
});
this->connect(m_trash_watcher.get(), &FileWatcher::fileDeleted, [=]() {
//qDebug()<<"trash changed";
auto trash = FileInfo::fromUri("trash:///");
auto job = new FileInfoJob(trash);
job->setAutoDelete();
connect(job, &FileInfoJob::infoUpdated, [=]() {
auto trashIndex = this->indexFromUri("trash:///");
this->dataChanged(trashIndex, trashIndex);
Q_EMIT this->requestClearIndexWidget();
});
job->queryAsync();
});
// monitor desktop
QString desktopFile = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
qWarning() << "desktopfile:" << desktopFile;
m_desktop_watcher = std::make_shared<FileWatcher>("file://" + desktopFile, this);
m_desktop_watcher->setMonitorChildrenChange(true);
m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileCreated, [=](const QString &uri) {
qDebug()<<"desktop file created"<<uri;
auto info = FileInfo::fromUri(uri);
bool exsited = false;
for (auto file : m_files) {
if (file->uri() == info->uri()) {
exsited = true;
break;
}
}
if (!exsited) {
if (!m_renaming_file_pos.first.isEmpty() && uri != m_renaming_file_pos.first && uri.contains(m_renaming_file_pos.first)) {
return;
}
m_items_need_relayout.append(uri);
m_items_need_relayout.removeOne(m_renaming_file_pos.first);
m_items_need_relayout.removeOne(m_renaming_file_pos.first + ".desktop");
if (m_renaming_operation_info.get()) {
m_items_need_relayout.removeOne(m_renaming_operation_info.get()->target());
}
m_items_need_relayout.removeDuplicates();
auto job = new FileInfoJob(info);
job->setAutoDelete();
job->querySync();
// locate new item =====
//task#74174 扩展模式下支持拖拽图标放置到扩展屏, 创建文件获取当前view
auto view = ((PeonyDesktopApplication*)qApp)->getIconView(QCursor::pos());
//校验图标是否已经满了如果满了寻找没有满的view
if (view && view->isFull()) {
Peony::DesktopIconView *notFullView = ((PeonyDesktopApplication*)qApp)->getNotFullView();
if (notFullView) {
view = notFullView;
}
}
auto itemRectHash = view->getCurrentItemRects();
auto grid = view->gridSize();
auto viewRect = view->viewport()->rect();
if (!view->m_show_hidden && info.get()->displayName().startsWith("."))
return;
QRegion notEmptyRegion;
for (auto rect : itemRectHash.values()) {
notEmptyRegion += rect;
}
if (!view->isRenaming()) {
view->setFileMetaInfoPos(uri, QPoint(-1, -1));
} else {
m_items_need_relayout.removeOne(uri);
view->setRenaming(false);
}
auto metaInfoPos = view->getFileMetaInfoPos(uri);
if (metaInfoPos.x() >= 0) {
// check if overlapped, it might happend whild drag out and in desktop view.
auto indexRect = QRect(metaInfoPos, itemRectHash.isEmpty()? QSize(): itemRectHash.values().first().size());
if (notEmptyRegion.intersects(indexRect)) {
// move index to closest empty grid.
auto next = indexRect;
bool isEmptyPos = false;
while (!isEmptyPos) {
next.translate(0, grid.height());
if (next.bottom() > viewRect.bottom()) {
int top = next.y();
while (true) {
if (top < grid.height()) {
break;
}
top-=grid.height();
}
//put item to next column first row
next.moveTo(next.x() + grid.width(), top);
}
if (notEmptyRegion.intersects(next))
continue;
isEmptyPos = true;
itemRectHash.insert(info->uri(), next);
notEmptyRegion += next;
// handle position locate in DesktopIconView::itemInserted().
view->setFileMetaInfoPos(info->uri(), next.topLeft());
}
}
this->beginInsertRows(QModelIndex(), m_files.count(), m_files.count());
//file changed, force create thubnail, link tobug#83108
ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher, true);
m_files<<info;
//this->insertRows(m_files.indexOf(info), 1);
this->endInsertRows();
// end locate new item=======
//this->endResetModel();
Q_EMIT this->requestUpdateItemPositions();
Q_EMIT this->requestLayoutNewItem(info->uri());
//task#74174 在当前view中创建文件
view->fileCreated(uri);
return;
}
// aligin exsited rect
int marginTop = notEmptyRegion.boundingRect().top();
while (marginTop - grid.height() >= 0) {
marginTop -= grid.height();
}
int marginLeft = notEmptyRegion.boundingRect().left();
while (marginLeft - grid.width() >= 0) {
marginLeft -= grid.width();
}
auto indexRect = QRect(QPoint(marginLeft, marginTop), itemRectHash.isEmpty()? QSize(): itemRectHash.values().first().size());
if (notEmptyRegion.intersects(indexRect)) {
// move index to closest empty grid.
auto next = indexRect;
bool isEmptyPos = false;
while (!isEmptyPos) {
next.translate(0, grid.height());
if (next.bottom() > viewRect.bottom()) {
int top = next.y();
while (true) {
if (top < grid.height()) {
break;
}
top-=grid.height();
}
//put item to next column first row
next.moveTo(next.x() + grid.width(), top);
}
if (notEmptyRegion.intersects(next))
continue;
isEmptyPos = true;
itemRectHash.insert(info->uri(), next);
notEmptyRegion += next;
view->setFileMetaInfoPos(info->uri(), next.topLeft());
}
} else {
view->setFileMetaInfoPos(info->uri(), indexRect.topLeft());
}
//this->beginResetModel();
this->beginInsertRows(QModelIndex(), m_files.count(), m_files.count());
//file changed, force create thubnail, link tobug#83108
ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher, true);
m_files<<info;
//this->insertRows(m_files.indexOf(info), 1);
this->endInsertRows();
// end locate new item=======
//this->endResetModel();
Q_EMIT this->requestUpdateItemPositions();
Q_EMIT this->requestLayoutNewItem(info->uri());
//task#74174 在当前view中创建文件
view->fileCreated(uri);
}
else{
//file content changed, need update fileinfo, fix bug#76908
auto job = new FileInfoJob(info);
job->setAutoDelete();
job->querySync();
}
});
m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileDeleted, [=](const QString &uri) {
m_items_need_relayout.removeOne(uri);
auto view = getIconView(uri);
view->removeItemRect(uri);
auto itemRectHash = view->getCurrentItemRects();
for (auto info : m_files) {
if (info->uri() == uri) {
//this->beginResetModel();
// continue fix #18155、#52228、#52231、#49442
// 注意有时不会走到view的aboutToRemoveRows中所以需要在此调用relayoutAddedItem
this->beginRemoveRows(QModelIndex(), m_files.indexOf(info), m_files.indexOf(info));
view->relayoutExsitingItems(m_items_need_relayout);
m_files.removeOne(info);
this->endRemoveRows();
//this->endResetModel();
Q_EMIT this->requestClearIndexWidget(QStringList()<<uri);
Q_EMIT this->requestUpdateItemPositions();
}
}
});
m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileChanged, [=](const QString &uri) {
for (auto info : m_files) {
if (info->uri() == uri) {
auto job = new FileInfoJob(info);
job->setAutoDelete();
connect(job, &FileInfoJob::infoUpdated, this, [=]() {
//file changed, force create thubnail, link tobug#83108
ThumbnailManager::getInstance()->createThumbnail(uri, m_thumbnail_watcher, true);
this->dataChanged(indexFromUri(uri), indexFromUri(uri));
Q_EMIT this->requestClearIndexWidget(QStringList()<<uri);
});
job->queryAsync();
this->dataChanged(indexFromUri(uri), indexFromUri(uri));
return;
}
}
});
//when system app uninstalled, delete link in desktop if exist
QString system_app_path = "file:///usr/share/applications/";
m_system_app_watcher = std::make_shared<FileWatcher>(system_app_path, this);
m_system_app_watcher->setMonitorChildrenChange(true);
auto mInfo = FileInfo::fromUri(system_app_path);
qDebug() <<"system_app_path:" <<mInfo->isDir();
this->connect(m_system_app_watcher.get(), &FileWatcher::fileDeleted, [=](const QString &uri) {
qDebug() << "m_system_app_watcher:" <<uri;
if (uri.endsWith(".desktop"))
{
QString fileName = uri;
fileName = fileName.replace(system_app_path, "");
qDebug() << "m_system_app_watcher:" <<fileName <<uri;
for (auto info : m_files) {
if (info->uri().endsWith(fileName)) {
//fix bug#136661, desktop file may be auto deleted wrong
//desktop file be deleted and then created
QString absPath = uri;
absPath = absPath.replace("file://", "");
QTimer::singleShot(100, this, [=](){
if (! QFile::exists(absPath)){
//this->beginResetModel();
this->beginRemoveRows(QModelIndex(), m_files.indexOf(info), m_files.indexOf(info));
m_files.removeOne(info);
this->endRemoveRows();
//this->endResetModel();
Q_EMIT this->requestClearIndexWidget();
Q_EMIT this->requestUpdateItemPositions();
QStringList list;
list.append(info->uri());
//auto remove, link to task#10131
FileOperationUtils::remove(list);
}
});
}
}
}
});
//when andriod app uninstalled, delete link in desktop if exist
QString homePath = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
auto andriod_app_path = homePath + "/.local/share/applications/";
auto app_info = FileInfo::fromUri(andriod_app_path);
qDebug() <<"andriod_app_path:" <<app_info->isDir();
m_andriod_app_watcher = std::make_shared<FileWatcher>(andriod_app_path, this);
m_andriod_app_watcher->setMonitorChildrenChange(true);
this->connect(m_andriod_app_watcher.get(), &FileWatcher::fileDeleted, [=](const QString &uri) {
if (uri.endsWith(".desktop"))
{
QString fileName = uri;
fileName = fileName.replace(andriod_app_path, "");
qDebug() << "andriod_app_path:" <<fileName <<uri;
for (auto info : m_files) {
if (info->uri().endsWith(fileName)) {
//fix bug#136661,android desktop file be auto deleted wrong
//desktop file be deleted and then created
QString absPath = uri;
absPath = absPath.replace("file://", "");
QTimer::singleShot(100, this, [=](){
if (! QFile::exists(absPath)){
//this->beginResetModel();
this->beginRemoveRows(QModelIndex(), m_files.indexOf(info), m_files.indexOf(info));
m_files.removeOne(info);
this->endRemoveRows();
//this->endResetModel();
Q_EMIT this->requestClearIndexWidget();
Q_EMIT this->requestUpdateItemPositions();
QStringList list;
list.append(info->uri());
//auto remove, link to task#10131
FileOperationUtils::remove(list);
}
});
}
}
}
});
//handle standard dir changing.
m_dir_manager =new UserdirManager(this);
//refresh after standard dir changed.
connect(m_dir_manager,&UserdirManager::desktopDirChanged,[=](){
refresh();
});
connect(m_dir_manager,&UserdirManager::thumbnailSetingChange,[=](){
refresh();
});
connect(FileOperationManager::getInstance(), &FileOperationManager::operationStarted, this, [=](std::shared_ptr<FileOperationInfo> info){
if (info.get()->m_type == FileOperationInfo::Rename) {
m_renaming_operation_info = info;
auto renamingUri = info.get()->m_src_uris.first();
if (!renamingUri.endsWith(".desktop")) {
m_renaming_operation_info = nullptr;
return;
}
m_renaming_file_pos.first = renamingUri;
m_renaming_file_pos.second = getIconView(renamingUri)->getFileMetaInfoPos(renamingUri);
} else {
m_renaming_file_pos.first = nullptr;
m_renaming_file_pos.second = QPoint();
m_renaming_operation_info = nullptr;
}
});
connect(FileOperationManager::getInstance(), &FileOperationManager::operationFinished, this, [=](std::shared_ptr<FileOperationInfo> info){
if (info.get()->m_type == FileOperationInfo::Rename) {
if (!info.get()->m_has_error) {
auto renamingUri = info.get()->target();
if (!renamingUri.endsWith(".desktop")) {
m_renaming_operation_info = nullptr;
m_renaming_file_pos.first = nullptr;
m_renaming_file_pos.second = QPoint();
return;
}
auto view = getIconView(renamingUri);
QPoint target_pos = view->getCurrentItemRects().value(renamingUri).topLeft();
//desktop文件重命名时如果存在相同文件则不会重命名成功。由于该文件uri不会变所以pos不变无需更新pos
if (target_pos.isNull() || (m_renaming_file_pos.second == target_pos)) {
//desktop文件重命名成功
m_renaming_file_pos.first = renamingUri;
m_items_need_relayout.removeOne(renamingUri);
m_items_need_relayout.removeOne(renamingUri + ".desktop");
view->updateItemPosByUri(renamingUri, m_renaming_file_pos.second);
view->setFileMetaInfoPos(renamingUri, m_renaming_file_pos.second);
} else {
//desktop文件(uri)重命名失败
QString &src_uri = info->m_src_uris.first();
QTimer::singleShot(100, view, [=]() {
view->setSelections(QStringList() << src_uri);
view->scrollToSelection(src_uri);
view->setFocus();
});
}
} else {
// restore/relayout?
}
m_renaming_operation_info = nullptr;
QTimer::singleShot(100, this, [=]{
m_renaming_file_pos.first = nullptr;
m_renaming_file_pos.second = QPoint();
});
} else {
m_renaming_file_pos.first = nullptr;
m_renaming_file_pos.second = QPoint();
m_renaming_operation_info = nullptr;
}
});
auto settings = GlobalSettings::getInstance();
m_showFileExtension = settings->isExist(SHOW_FILE_EXTENSION)? settings->getValue(SHOW_FILE_EXTENSION).toBool(): true;
connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) {
if (SHOW_FILE_EXTENSION == key) {
m_showFileExtension= GlobalSettings::getInstance()->getValue(key).toBool();
beginResetModel();
endResetModel();
}
});
}
DesktopItemModel::~DesktopItemModel()
{
}
bool findProgram(const QString &program)
{
QFileInfo fi(program);
if (!program.isEmpty() && fi.isExecutable()) {
return true;
}
const QStringList paths = QFile::decodeName(qgetenv("PATH")).split(':');
for(const QString &dir : paths) {
QFileInfo fi= QFileInfo(dir + QDir::separator() + program);
if (fi.isExecutable()) {
return true;
}
}
return false;
}
void DesktopItemModel::refreshInternal()
{
m_items_need_relayout.clear();
ThumbnailManager::getInstance()->syncThumbnailPreferences();
beginResetModel();
//removeRows(0, m_files.count());
//m_trash_watcher->stopMonitor();
//m_desktop_watcher->stopMonitor();
for (auto info : m_files) {
ThumbnailManager::getInstance()->releaseThumbnail(info->uri());
}
m_files.clear();
auto desktopUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
//FIXME: replace BLOCKING api in ui thread.
if (!FileUtils::isFileExsit(desktopUri)) {
// try get correct desktop path delay.
//FIXME: replace BLOCKING api in ui thread.
if (findProgram("xdg-user-dirs-update")) {
do {
QProcess p;
p.setProgram("xdg-user-dirs-update");
p.start();
p.waitForFinished();
desktopUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
} while (!FileUtils::isFileExsit(desktopUri));
}
QTimer::singleShot(1000, this, [=](){
if (!FileUtils::isFileExsit(desktopUri)) {
endResetModel();
Q_EMIT refreshed();
refresh();
} else {
m_enumerator = new FileEnumerator(this);
m_enumerator->setAutoDelete();
m_enumerator->setEnumerateWithInfoJob();
m_enumerator->setEnumerateDirectory(desktopUri);
m_enumerator->connect(m_enumerator, &FileEnumerator::enumerateFinished, this, &DesktopItemModel::onEnumerateFinished);
m_enumerator->enumerateAsync();
endResetModel();
}
});
return;
}
m_enumerator = new FileEnumerator(this);
m_enumerator->setAutoDelete();
m_enumerator->setEnumerateDirectory(desktopUri);
m_enumerator->connect(m_enumerator, &FileEnumerator::enumerateFinished, this, &DesktopItemModel::onEnumerateFinished);
m_enumerator->enumerateAsync();
endResetModel();
}
int DesktopItemModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
return m_files.count();
}
QVariant DesktopItemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
//qDebug()<<"data"<<m_files.at(index.row())->uri();
auto info = m_files.at(index.row());
switch (role) {
case Qt::DisplayRole:{
QString displayName = info->displayName();
if (info->isDesktopFile())
{
displayName = FileUtils::handleDesktopFileName(info->uri(), info->displayName());
return QVariant(displayName);
}
/* story#8359 【文件管理器】手动开启关闭文件拓展名 */
if(!m_showFileExtension){
if (info->isDir()) {
return QVariant(displayName);
}
return QVariant(FileUtils::getBaseNameOfFile(displayName));
}else
return QVariant(displayName);
}
case Qt::ToolTipRole: {
// fix #80257
switch (index.row()) {
case 0:
return tr("Computer");
case 1:
return tr("Trash");
default:
break;
}
//fix bug#53504, desktop files not show same name issue
if (info->isDesktopFile())
{
auto displayName = FileUtils::handleDesktopFileName(info->uri(), info->displayName());
return displayName;
}
return info->displayName();
}
case Qt::DecorationRole: {
auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(info->uri());
if (!thumbnail.isNull()) {
return thumbnail;
}
return QIcon::fromTheme(info->iconName(), QIcon::fromTheme("text-x-generic"));
}
case UriRole:
return info->uri();
case IsLinkRole:
return info->isSymbolLink();
}
return QVariant();
}
void DesktopItemModel::onEnumerateFinished()
{
//beginResetModel();
beginRemoveRows(QModelIndex(), 0, m_files.count() - 1);
m_files.clear();
endRemoveRows();
auto computer = FileInfo::fromUri("computer:///");
auto personal = FileInfo::fromPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
auto trash = FileInfo::fromUri("trash:///");
QList<std::shared_ptr<FileInfo>> infos;
infos<<computer;
infos<<trash;
infos<<personal;
infos<<m_enumerator->getChildren();
m_querying_files = infos;
m_files = infos;
//qDebug()<<m_files.count();
//this->endResetModel();
for (auto info : infos) {
auto asyncJob = new FileInfoJob(info);
connect(asyncJob, &FileInfoJob::queryAsyncFinished, this, [=](){
m_querying_files.removeOne(info);
if (m_querying_files.isEmpty()) {
beginInsertRows(QModelIndex(), 0, m_files.count() - 1);
endInsertRows();
for (auto info : m_files) {
auto uri = info->uri();
auto view = getIconView(uri);
auto pos = view->getFileMetaInfoPos(info->uri());
if (pos.x() >= 0) {
view->updateItemPosByUri(info->uri(), pos);
} else {
view->ensureItemPosByUri(uri);
}
if (info->isDesktopFile()) {
ThumbnailManager::getInstance()->updateDesktopFileThumbnail(info->uri(), m_thumbnail_watcher);
} else {
ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher);
}
}
//qDebug()<<"startMornitor";
m_trash_watcher->startMonitor();
qWarning() << "desktopfile:" << m_desktop_watcher->currentUri() << " >>>> " << QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
if (m_desktop_watcher->currentUri() != "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) {
m_desktop_watcher->stopMonitor();
m_desktop_watcher->forceChangeMonitorDirectory("file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
m_desktop_watcher->setMonitorChildrenChange(true);
}
m_desktop_watcher->startMonitor();
m_system_app_watcher->startMonitor();
m_andriod_app_watcher->startMonitor();
Q_EMIT refreshed();
asyncJob->deleteLater();
}
});
asyncJob->queryAsync();
}
}
void DesktopItemModel::clearFloatItems()
{
m_items_need_relayout.clear();
}
bool DesktopItemModel::acceptDropAction() const
{
return m_accept_drop_action;
}
void DesktopItemModel::setAcceptDropAction(bool acceptDropAction)
{
m_accept_drop_action = acceptDropAction;
}
const QModelIndex DesktopItemModel::indexFromUri(const QString &uri)
{
for (auto info : m_files) {
if (info->uri() == uri) {
return index(m_files.indexOf(info));
}
}
return QModelIndex();
}
const QString DesktopItemModel::indexUri(const QModelIndex &index)
{
if (index.row() < 0 || index.row() >= m_files.count()) {
return nullptr;
}
return m_files.at(index.row())->uri();
}
bool DesktopItemModel::insertRows(int row, int count, const QModelIndex &parent)
{
beginInsertRows(parent, row, row + count);
endInsertRows();
return true;
}
bool DesktopItemModel::insertRow(int row, const QModelIndex &parent)
{
beginInsertRows(parent, row, row);
endInsertRows();
return true;
}
bool DesktopItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
beginRemoveRows(parent, row, row + count);
endRemoveRows();
return true;
}
bool DesktopItemModel::removeRow(int row, const QModelIndex &parent)
{
beginRemoveRows(parent, row, row);
endRemoveRows();
return true;
}
Qt::ItemFlags DesktopItemModel::flags(const QModelIndex &index) const
{
auto uri = index.data(UriRole).toString();
auto info = FileInfo::fromUri(uri);
if (index.isValid()) {
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
flags |= Qt::ItemIsDragEnabled;
flags |= Qt::ItemIsEditable;
if (info->isDir()) {
flags |= Qt::ItemIsDropEnabled;
}
return flags;
} else {
return Qt::ItemIsDropEnabled;
}
}
QMimeData *DesktopItemModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData* data = QAbstractItemModel::mimeData(indexes);
//set urls data URLs correspond to the MIME type text/uri-list.
QList<QUrl> urls;
QStringList uris;
for (auto index : indexes) {
QUrl url = index.data(UriRole).toString();
if (!urls.contains(url))
urls<<url;
uris<<index.data(UriRole).toString();
}
data->setUrls(urls);
auto string = uris.join(" ");
data->setData("peony-qt/encoded-uris", string.toUtf8());
data->setText(string);
return data;
}
bool DesktopItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!acceptDropAction())
return false;
//qDebug()<<row<<column;
//qDebug()<<"drop mime data"<<parent.data()<<index(row, column, parent).data();
//judge the drop dest uri.
QString destDirUri = nullptr;
if (parent.isValid()) {
destDirUri = parent.data(UriRole).toString();
} else {
destDirUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
//if destDirUri was not set, do not execute a drop.
if (destDirUri.isNull()) {
return false;
}
auto info = FileInfo::fromUri(destDirUri);
if (info.get()->isEmptyInfo()) {
// note that this case nearly won't happend.
// but there is a bug reported due to this.
// link to task #48798.
FileInfoJob j(info);
j.querySync();
}
if (!info->isDir() && ! destDirUri.startsWith("trash:///")) {
return false;
}
//NOTE:
//do not allow drop on it self.
auto urls = data->urls();
QStringList srcUris;
if (data->hasFormat("peony-qt/encoded-uris")) {
srcUris = QString(data->data("peony-qt/encoded-uris")).split(" ");
for (QString uri : srcUris) {
if (uri.startsWith("recent://"))
srcUris.removeOne(uri);
}
} else {
for (auto url : urls) {
//can not drag file from recent
if (url.url().startsWith("recent://"))
return false;
srcUris<<url.url();
}
}
srcUris.removeDuplicates();
if (srcUris.isEmpty()) {
return false;
}
//can not drag file to recent
if (destDirUri.startsWith("recent://"))
return false;
//not allow drag file to itself
if (srcUris.contains(destDirUri)) {
return false;
}
//can not move StandardPath to any dir
if (action == Qt::MoveAction && FileUtils::containsStandardPath(srcUris)) {
return false;
}
bool b_trash_item = false;
for(auto path : srcUris)
{
if (path.contains("trash:///"))
{
b_trash_item = true;
break;
}
}
//drag from trash to another place, return false
//comment to fix can not drag to copy trash file,link to bug#117741
// if (b_trash_item && destDirUri != "trash:///")
// return false;
auto fileOpMgr = FileOperationManager::getInstance();
bool addHistory = true;
bool canNotTrash = false;
for (auto uri : srcUris) {
if (uri.startsWith("filesafe:///")) {
canNotTrash = true;
break;
}
}
if (destDirUri.startsWith("trash:///")) {
// 如果是保护箱删除时,不会反馈删除弹窗,保护箱文件不影响
if(!(srcUris.first().startsWith("filesafe:///") &&
(QString(srcUris.first()).remove("filesafe:///").indexOf("/") == -1))) {
//fix bug#91525, can trash file in U disk issue
FileOperationUtils::trash(srcUris, true);
// if(canNotTrash){
// FileOperationUtils::trash(srcUris, false);
// }else {
// FileTrashOperation *trashOp = new FileTrashOperation(srcUris);
// fileOpMgr->startOperation(trashOp, addHistory);
// }
}
} else {
qDebug() << "DesktopItemModel dropMimeData:" <<action;
//krme files can not move to other place, default set as copy action
if (srcUris.first().startsWith("kmre:///") || srcUris.first().startsWith("kydroid:///"))
action = Qt::CopyAction;
//filesafe files can not move to other place, default set as copy action
if (srcUris.first().startsWith("filesafe:///"))
action = Qt::CopyAction;
//fix drag trash file to other path is copy issue,link to bug#117741
if (srcUris.first().startsWith("trash:///") && action == Qt::MoveAction){
//not copy move, do target move to delete file in trash
action = Qt::TargetMoveAction;
}
auto op = FileOperationUtils::moveWithAction(srcUris, destDirUri, true, action);
connect(op, &FileOperation::operationFinished, this, [=](){
Peony::SoundEffect::getInstance()->copyOrMoveSucceedMusic();
});
}
//NOTE:
//we have to handle the dnd with file operation, so do not
//use QAbstractModel::dropMimeData() here;
return false;
}
Qt::DropActions DesktopItemModel::supportedDropActions() const
{
return Qt::MoveAction|Qt::CopyAction;
return QAbstractItemModel::supportedDropActions();
}
Qt::DropActions DesktopItemModel::supportedDragActions() const
{
return Qt::MoveAction;
}
void DesktopItemModel::refresh()
{
m_desktop_info = FileInfo::fromPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
auto infoJob = new FileInfoJob(m_desktop_info);
infoJob->setAutoDelete();
connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){
refreshInternal();
});
infoJob->queryAsync();
}
Peony::DesktopIconView *DesktopItemModel::getIconView(const QString &uri)
{
//获取当前屏幕的view
auto metaInfo = FileMetaInfo::fromUri(uri);
if (metaInfo) {
int id = metaInfo->getMetaInfoInt("peony-qt-desktop-id");
return ((PeonyDesktopApplication*)qApp)->getIconView(id);
}
return ((PeonyDesktopApplication*)qApp)->getIconView(0);
}