Working copy/move operation
This commit is contained in:
+319
-2
@@ -5,8 +5,213 @@
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QDesktopServices>
|
||||
#include <QMimeData>
|
||||
#include <QClipboard>
|
||||
#include <QThread>
|
||||
#include "loadimage.h"
|
||||
|
||||
class FileTimes
|
||||
{
|
||||
public:
|
||||
explicit FileTimes(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
#ifndef Q_OS_WIN
|
||||
birthTime = file.fileTime(QFileDevice::FileBirthTime);
|
||||
#endif
|
||||
modificationTime = file.fileTime(QFileDevice::FileModificationTime);
|
||||
accessTime = file.fileTime(QFileDevice::FileAccessTime);
|
||||
}
|
||||
void apply(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
if(file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
|
||||
{
|
||||
#ifndef Q_OS_WIN // Only windows allow changing birth time
|
||||
file.setFileTime(birthTime, QFileDevice::FileBirthTime);
|
||||
#endif
|
||||
file.setFileTime(accessTime, QFileDevice::FileAccessTime);
|
||||
file.setFileTime(modificationTime, QFileDevice::FileModificationTime);
|
||||
}
|
||||
}
|
||||
private:
|
||||
QDateTime birthTime;
|
||||
QDateTime modificationTime;
|
||||
QDateTime accessTime;
|
||||
};
|
||||
|
||||
FileTransfer::FileTransfer(FileManager *fm) :
|
||||
_fm(fm)
|
||||
{
|
||||
}
|
||||
|
||||
FileTransfer::~FileTransfer()
|
||||
{
|
||||
_run = false;
|
||||
}
|
||||
|
||||
void FileTransfer::copy(const QStringList &src, const QString &dst)
|
||||
{
|
||||
_run = true;
|
||||
perform(src, dst, true);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void FileTransfer::move(const QStringList &src, const QString &dst)
|
||||
{
|
||||
_run = true;
|
||||
perform(src, dst, false);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void FileTransfer::cancel()
|
||||
{
|
||||
_run = false;
|
||||
}
|
||||
|
||||
void FileTransfer::perform(const QStringList &src, const QString &dst, bool copy)
|
||||
{
|
||||
QDir dstDir(dst);
|
||||
if(!dstDir.exists())
|
||||
{
|
||||
emit error(tr("Error"), tr("Destination directory %1 doesn't exists").arg(dstDir.absolutePath()));
|
||||
return;
|
||||
}
|
||||
|
||||
QList<Action> actions;
|
||||
QStringList dirs;
|
||||
|
||||
emit progress(0);
|
||||
|
||||
for(const QString &i : src)
|
||||
{
|
||||
QFileInfo srcInfo(i);
|
||||
if(srcInfo.absolutePath() == dst || dst.startsWith(srcInfo.absoluteFilePath()))
|
||||
return;
|
||||
|
||||
if(srcInfo.isDir())
|
||||
{
|
||||
QDir srcDir(i);
|
||||
//qDebug() << "dir" << srcInfo.absoluteFilePath() << srcInfo.fileName();
|
||||
if(!copy && !dstDir.exists(srcInfo.fileName()))
|
||||
{
|
||||
if(QFile::rename(srcInfo.absoluteFilePath(), dstDir.absoluteFilePath(srcInfo.fileName())))
|
||||
continue;
|
||||
}
|
||||
actions.append({srcInfo.absoluteFilePath(), srcInfo.fileName(), true});
|
||||
if(!copy)dirs.prepend(srcInfo.absoluteFilePath());
|
||||
QDirIterator it(i, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
while(it.hasNext())
|
||||
{
|
||||
QFileInfo info = it.nextFileInfo();
|
||||
if(info.fileName() == "." || info.fileName() == "..")
|
||||
continue;
|
||||
|
||||
QString relativePath = srcDir.dirName() + "/" + srcDir.relativeFilePath(info.absoluteFilePath());
|
||||
if(info.isDir())
|
||||
{
|
||||
actions.append({"", relativePath, true});
|
||||
if(!copy)dirs.prepend(info.absoluteFilePath());
|
||||
//qDebug() << "dir" << info.absoluteFilePath() << relativePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
actions.append({info.absoluteFilePath(), dstDir.absoluteFilePath(relativePath), false});
|
||||
//qDebug() << "file" << info.absoluteFilePath() << dstDir.absoluteFilePath(relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actions.append({srcInfo.absoluteFilePath(), dstDir.absoluteFilePath(srcInfo.fileName())});
|
||||
//qDebug() << "file" << srcInfo.absoluteFilePath() << dstDir.absoluteFilePath(srcInfo.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
bool overwriteAll = false;
|
||||
bool skipAll = false;
|
||||
int total = actions.size();
|
||||
int i = 0;
|
||||
for(auto &a : actions)
|
||||
{
|
||||
if(!_run)
|
||||
return;
|
||||
|
||||
if(a.dir)
|
||||
{
|
||||
dstDir.mkpath(a.dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
QFileInfo dstInfo(a.dst);
|
||||
if(dstInfo.exists())
|
||||
{
|
||||
if(overwriteAll)
|
||||
{
|
||||
QFile::remove(dstInfo.absoluteFilePath());
|
||||
}
|
||||
else if(skipAll)
|
||||
{
|
||||
emit progress(i++ * 100 / total);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::StandardButton ret;
|
||||
QMetaObject::invokeMethod(_fm, "overwrite", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QMessageBox::StandardButton, ret), Q_ARG(QString, dstInfo.fileName()));
|
||||
switch(ret)
|
||||
{
|
||||
case QMessageBox::YesToAll:
|
||||
overwriteAll = true;//break; is intentionally missing
|
||||
case QMessageBox::Yes:
|
||||
QFile::remove(dstInfo.absoluteFilePath());
|
||||
break;
|
||||
case QMessageBox::NoToAll:
|
||||
skipAll = true;//break; is intentionally missing
|
||||
case QMessageBox::No:
|
||||
emit progress(i++ * 100 / total);
|
||||
continue;
|
||||
break;
|
||||
case QMessageBox::Cancel:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FileTimes t(a.src);
|
||||
if(copy)
|
||||
{
|
||||
if(!QFile::copy(a.src, a.dst))
|
||||
{
|
||||
emit error(tr("Copy failed"), tr("Failed to copy file %1 to %2").arg(a.src).arg(a.dst));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!QFile::rename(a.src, a.dst))
|
||||
{
|
||||
emit error(tr("Move failed"), tr("Failed to move file %1 to %2").arg(a.src).arg(a.dst));
|
||||
return;
|
||||
}
|
||||
}
|
||||
t.apply(a.dst);
|
||||
}
|
||||
emit progress(i++ * 100 / total);
|
||||
}
|
||||
|
||||
if(!copy)
|
||||
{
|
||||
for(const QString &d : dirs)
|
||||
{
|
||||
QDir dir(d);
|
||||
if(dir.isEmpty())
|
||||
dir.removeRecursively();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathTabBar::PathTabBar(const QStringList &tabs) :
|
||||
_tabs(tabs)
|
||||
{
|
||||
@@ -147,6 +352,8 @@ FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMa
|
||||
connect(ui->rightTab, &DirView::dirChanged, _rightTabBar, &PathTabBar::pathChanged);
|
||||
connect(ui->leftTab, &DirView::openFile, this, &FileManager::openFile);
|
||||
connect(ui->rightTab, &DirView::openFile, this, &FileManager::openFile);
|
||||
connect(ui->leftTab, &DirView::filesAction, this, &FileManager::copyMoveFiles, Qt::QueuedConnection);
|
||||
connect(ui->rightTab, &DirView::filesAction, this, &FileManager::copyMoveFiles, Qt::QueuedConnection);
|
||||
connect(ui->actionLoad_FITS_keywordsLeft, &QAction::toggled, ui->leftTab, &DirView::loadFitsKeywords);
|
||||
connect(ui->actionLoad_FITS_keywordsRight, &QAction::toggled, ui->rightTab, &DirView::loadFitsKeywords);
|
||||
|
||||
@@ -165,6 +372,8 @@ FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMa
|
||||
|
||||
connect(ui->actionSelect_columnsLeft, &QAction::triggered, this, &FileManager::selectFITSKeywords);
|
||||
connect(ui->actionSelect_columnsRight, &QAction::triggered, this, &FileManager::selectFITSKeywords);
|
||||
connect(ui->actionCopySelectedFilesPathsLeft, &QAction::triggered, this, &FileManager::copySelectedFilesPaths);
|
||||
connect(ui->actionCopySelectedFilesPathsRight, &QAction::triggered, this, &FileManager::copySelectedFilesPaths);
|
||||
connect(ui->leftPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
|
||||
connect(ui->rightPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
|
||||
|
||||
@@ -175,6 +384,25 @@ FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMa
|
||||
ui->menuLeft_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->leftTab->setDir(path); });
|
||||
ui->menuRight_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->rightTab->setDir(path); });
|
||||
}
|
||||
|
||||
ui->progressBar->hide();
|
||||
ui->cancelButton->hide();
|
||||
|
||||
_thread = new QThread(this);
|
||||
_thread->start();
|
||||
_fileTransfer = new FileTransfer(this);
|
||||
_fileTransfer->moveToThread(_thread);
|
||||
connect(_fileTransfer, &FileTransfer::progress, ui->progressBar, &QProgressBar::setValue);
|
||||
connect(_fileTransfer, &FileTransfer::error, this, &FileManager::errorMessage);
|
||||
connect(_fileTransfer, &FileTransfer::finished, [this](){
|
||||
ui->leftTab->setDragEnabled(true);
|
||||
ui->rightTab->setDragEnabled(true);
|
||||
ui->progressBar->hide();
|
||||
ui->cancelButton->hide();
|
||||
});
|
||||
connect(this, &FileManager::copy, _fileTransfer, &FileTransfer::copy);
|
||||
connect(this, &FileManager::move, _fileTransfer, &FileTransfer::move);
|
||||
connect(ui->cancelButton, &QPushButton::clicked, [this](){ _fileTransfer->cancel(); });
|
||||
}
|
||||
|
||||
FileManager::~FileManager()
|
||||
@@ -190,6 +418,12 @@ FileManager::~FileManager()
|
||||
settings.setValue("filemanager/rightLoadFitsKeywords", ui->actionLoad_FITS_keywordsRight->isChecked());
|
||||
settings.setValue("filemanager/geometry", saveGeometry());
|
||||
delete ui;
|
||||
|
||||
_fileTransfer->cancel();
|
||||
_thread->quit();
|
||||
_thread->wait();
|
||||
|
||||
delete _fileTransfer;
|
||||
}
|
||||
|
||||
void FileManager::selectFITSKeywords()
|
||||
@@ -213,6 +447,14 @@ void FileManager::selectFITSKeywords()
|
||||
}
|
||||
}
|
||||
|
||||
void FileManager::copySelectedFilesPaths()
|
||||
{
|
||||
if(sender() == ui->actionCopySelectedFilesPathsLeft)
|
||||
ui->leftTab->copySelectedFilesPathsToClipboard();
|
||||
if(sender() == ui->actionCopySelectedFilesPathsRight)
|
||||
ui->rightTab->copySelectedFilesPathsToClipboard();
|
||||
}
|
||||
|
||||
void FileManager::pathEdited()
|
||||
{
|
||||
if(sender() == ui->leftPath)
|
||||
@@ -229,6 +471,40 @@ void FileManager::pathEdited()
|
||||
}
|
||||
}
|
||||
|
||||
void FileManager::copyMoveFiles(Qt::DropAction action, const QStringList &src, const QString &dst)
|
||||
{
|
||||
ui->leftTab->setDragEnabled(false);
|
||||
ui->rightTab->setDragEnabled(false);
|
||||
ui->progressBar->show();
|
||||
ui->cancelButton->show();
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case Qt::CopyAction:
|
||||
emit copy(src, dst);
|
||||
break;
|
||||
case Qt::MoveAction:
|
||||
emit move(src, dst);
|
||||
break;
|
||||
case Qt::LinkAction:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton FileManager::overwrite(const QString &dst)
|
||||
{
|
||||
QMessageBox::StandardButton button = QMessageBox::question(this, tr("Overwrite file?"), tr("Destination file %1 already exists. Overwrite?").arg(dst),
|
||||
QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
void FileManager::errorMessage(const QString &title, const QString &text)
|
||||
{
|
||||
QMessageBox::critical(this, title, text);
|
||||
}
|
||||
|
||||
QCache<QString, ImageInfoData>* DirFileSystemModel::getCacheInstance()
|
||||
{
|
||||
static bool init = true;
|
||||
@@ -241,7 +517,8 @@ QCache<QString, ImageInfoData>* DirFileSystemModel::getCacheInstance()
|
||||
return &cache;
|
||||
}
|
||||
|
||||
DirFileSystemModel::DirFileSystemModel(QObject *parent) : QFileSystemModel(parent)
|
||||
DirFileSystemModel::DirFileSystemModel(QWidget *parentWidget) : QFileSystemModel(parentWidget)
|
||||
,_parentWidget(parentWidget)
|
||||
{
|
||||
_cache = getCacheInstance();
|
||||
setFilter(QDir::AllEntries | QDir::NoDot);
|
||||
@@ -272,7 +549,9 @@ const QStringList &DirFileSystemModel::FITSKeywords() const
|
||||
|
||||
Qt::ItemFlags DirFileSystemModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
return QFileSystemModel::flags(index) & ~Qt::ItemIsEditable;
|
||||
Qt::ItemFlags ret = QFileSystemModel::flags(index) & ~Qt::ItemIsEditable;
|
||||
if(index.row() == 0)ret &= ~Qt::ItemIsDragEnabled;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DirFileSystemModel::columnCount(const QModelIndex &parent) const
|
||||
@@ -335,6 +614,21 @@ bool DirFileSystemModel::hasChildren(const QModelIndex &parent) const
|
||||
return QFileSystemModel::hasChildren(parent);
|
||||
}
|
||||
|
||||
bool DirFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(row);
|
||||
Q_UNUSED(column);
|
||||
if(data->hasUrls())
|
||||
{
|
||||
QStringList srcPaths;
|
||||
for(auto &url : data->urls())
|
||||
srcPaths.append(url.toLocalFile());
|
||||
emit filesAction(action, srcPaths, filePath(parent));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DirFileSystemModel::loadFitsKeywords(bool enable)
|
||||
{
|
||||
_loadFitsKeywords = enable;
|
||||
@@ -348,6 +642,8 @@ DirView::DirView(QWidget *parent) : QTreeView(parent)
|
||||
setDragEnabled(true);
|
||||
setAcceptDrops(true);
|
||||
|
||||
connect(_dirFileSystemModel, &DirFileSystemModel::filesAction, this, &DirView::filesAction);
|
||||
|
||||
setModel(_dirFileSystemModel);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
@@ -435,3 +731,24 @@ void DirView::loadFitsKeywords(bool enable)
|
||||
{
|
||||
_dirFileSystemModel->loadFitsKeywords(enable);
|
||||
}
|
||||
|
||||
void DirView::copySelectedFilesPathsToClipboard() const
|
||||
{
|
||||
QList<QUrl> urls;
|
||||
QString text;
|
||||
auto selected = selectionModel()->selectedRows();
|
||||
for(auto &item : selected)
|
||||
{
|
||||
if(item.column() == 0)
|
||||
{
|
||||
QString path = _dirFileSystemModel->filePath(item);
|
||||
text.append(path); text.append('\n');
|
||||
urls.append(QUrl::fromLocalFile(path));
|
||||
}
|
||||
}
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
mimeData->setUrls(urls);
|
||||
mimeData->setText(text);
|
||||
clipboard->setMimeData(mimeData);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user