#include "fileoperator.h"
#include <QtGui>
-#include <QDir>
#include <QMessageBox>
#include <QHBoxLayout>
-#include <QChar>
#include "dialog.h"
#include "utils.h"
-#include <math.h>
-#include <errno.h>
-#include <iostream>
-
-
-#define BLOCK_SIZE 524288
-
-
-#define PAUSE() \
- if (pause) { \
- emit operationPaused(this); \
- waitOnCond(); \
- }
-
-
-#define SHOW_ERROR_PROMPT(promptString, fileName) \
- response = FileOperator::NONE; \
- if (ignoreAll[errno]) { \
- response = FileOperator::IGNORE; \
- } else { \
- char buf[255]; \
- char *realBuf = buf; \
- if (errno == 255) { \
- strcpy(buf, tr("File is sequential").toStdString().c_str()); \
- } else { \
- realBuf = strerror_r(errno, buf, 255); \
- } \
- emit showErrorPrompt(this, promptString + " " + realBuf + ".", fileName, errno); \
- waitOnCond(); \
- }
-
-
-#define ERROR_PROMPT(operation, promptString, fileName) \
-{ \
- response = FileOperator::NONE; \
- while (!abort && operation) { \
- SHOW_ERROR_PROMPT(promptString, fileName) \
- if (response == FileOperator::IGNORE) { \
- break; \
- } \
- PAUSE() \
- } \
-}
-
-
-#define SPECIAL_COPY_ERROR_PROMPT(operation, promptString, fileName) \
-{ \
- ERROR_PROMPT(operation, promptString, fileName) \
- if (abort || response == FileOperator::IGNORE) { \
- if (!abort) { \
- updateProgress(fileSizeMap[path]); \
- removeExcludeFiles.insert(path); \
- } \
- return; \
- } \
-}
-
-
-#define OVERWRITE_PROMPT(file, newFile) \
-{ \
- response = FileOperator::NONE; \
- \
- while (!abort && response == FileOperator::NONE && newFile.exists()) { \
- if (overwriteAll != FileOperator::NONE) { \
- response = overwriteAll; \
- } else { \
- emit showOverwritePrompt(this, newFile.absoluteFilePath(), \
- newFile.isDir() && file.isDir()); \
- waitOnCond(); \
- \
- PAUSE() \
- else if (response == FileOperator::NONE) { \
- emit showInputFilenamePrompt(this, newFile, file.isDir()); \
- waitOnCond(); \
- if (newNameFromDialog.size()) { \
- newFile.setFile(newNameFromDialog); \
- } \
- } \
- } \
- } \
- if (response == FileOperator::ASK) response = FileOperator::NONE; \
-}
-
FileOperator::FileOperator(QWidget *parent) : QWidget(parent) {
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(1);
setLayout(layout);
+
qRegisterMetaType<QFileInfo>("QFileInfo");
+ qRegisterMetaType<time_t>("time_t");
+
loadOperationIcons(palette(), "delete_small", deleteIcon, inverseDeleteIcon);
loadOperationIcons(palette(), "copy_small", copyIcon, inverseCopyIcon);
loadOperationIcons(palette(), "move_small", moveIcon, inverseMoveIcon);
}
-QString FileOperator::shortenPath(const QString &path) {
- QString homePath = QFSFileEngine::homePath();
-
- if (path.indexOf(homePath, 0) == 0) {
- QString result = path;
-
- result.replace(0, homePath.size(), "~");
- return result;
- }
-
- return path;
-}
-
-
-QString FileOperator::unwindPath(const QString &path) {
- QString result = path;
- // if ~ is the first character and / or nothing follows it, replace with home dir
- if (path == "~" || path.indexOf("~/", 0) == 0) {
- QString homePath = QFSFileEngine::homePath();
- result.replace(0, 1, homePath);
- // in case someone wants to enter a dir called ~ in the current dir, he can escape it with \~
- } else if (path == "\\~" || path.indexOf("\\~/", 0) == 0) {
- result.replace(0, 2, "~");
- }
-
- return result;
-}
-
-
void FileOperator::deleteFiles(const QFileInfoList &files) {
QString title, desc;
if (files.size() == 1) {
title = tr("Delete file");
desc = tr("Are you sure you want to delete %1?")
- .arg(FileOperator::shortenPath(files[0].absoluteFilePath()));
+ .arg(shortenPath(files[0].absoluteFilePath()));
} else {
title = tr("Delete files");
desc = tr("You are about to delete %1 files. Are you sure you want to continue?").arg(files.size());
);
if(confirm == QMessageBox::Yes) {
- DeleteThread *t = new DeleteThread(files);
- t->progressBar->setIcons(deleteIcon, inverseDeleteIcon);
- caterNewThread(t);
+ ProgressBar *bar = new ProgressBar(deleteIcon, inverseDeleteIcon);
+ initOperation(new DeleteThread(files), bar);
}
}
if (files.size() == 1) {
title = tr("Copy file");
desc = tr("Are you sure you want to copy %1 to %2?")
- .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
- .arg(FileOperator::shortenPath(destination.absolutePath()));
+ .arg(shortenPath(files[0].absoluteFilePath()))
+ .arg(shortenPath(destination.absolutePath()));
} else {
title = tr("Copy files");
desc = tr("You are about to copy %1 files to %2. Are you sure you want to continue?")
- .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
+ .arg(files.size()).arg(shortenPath(destination.absolutePath()));
}
int confirm = QMessageBox::warning(
);
if(confirm == QMessageBox::Yes) {
- CopyThread *t = new CopyThread(files, destination);
- t->progressBar->setIcons(copyIcon, inverseCopyIcon);
- t->progressBar->fromText = shortenPath(files[0].absolutePath());
- t->progressBar->toText = FileOperator::shortenPath(destination.absolutePath());
- caterNewThread(t);
+ ProgressBar *bar = new ProgressBar(copyIcon, inverseCopyIcon);
+ bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
+ initOperation(new CopyThread(files, destination), bar);
}
}
if (files.size() == 1) {
title = tr("Move file");
desc = tr("Are you sure you want to move %1 to %2?")
- .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
- .arg(FileOperator::shortenPath(destination.absolutePath()));
+ .arg(shortenPath(files[0].absoluteFilePath()))
+ .arg(shortenPath(destination.absolutePath()));
} else {
title = tr("Move files");
desc = tr("You are about to move %1 files to %2. Are you sure you want to continue?")
- .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
+ .arg(files.size()).arg(shortenPath(destination.absolutePath()));
}
int confirm = QMessageBox::warning(
);
if(confirm == QMessageBox::Yes) {
- MoveThread *t = new MoveThread(files, destination);
- t->progressBar->setIcons(moveIcon, inverseMoveIcon);
- t->progressBar->fromText = shortenPath(files[0].absolutePath());
- t->progressBar->toText = shortenPath(destination.absolutePath());
- caterNewThread(t);
+ ProgressBar *bar = new ProgressBar(moveIcon, inverseMoveIcon);
+ bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
+ initOperation(new MoveThread(files, destination), bar);
}
}
-void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
+void FileOperator::showErrorPrompt(OperationThread* op,
const QString &message,
const QString &fileName,
const int err)
QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole);
- msgBox.setText(message.arg(FileOperator::shortenPath(fileName)));
+ msgBox.setText(message.arg(shortenPath(fileName)));
msgBox.exec();
if (msgBox.clickedButton() == cancelButton) {
- manipulator->pause = true;
- manipulator->setResponse(RETRY);
+ op->pause = true;
+ op->setResponse(OperationThread::RETRY);
} else if (msgBox.clickedButton() == abortButton) {
- manipulator->setResponse(ABORT);
+ op->setResponse(OperationThread::ABORT);
} else if (msgBox.clickedButton() == retryButton) {
- manipulator->setResponse(RETRY);
+ op->setResponse(OperationThread::RETRY);
} else if (msgBox.clickedButton() == ignoreButton) {
- manipulator->setResponse(IGNORE);
+ op->setResponse(OperationThread::IGNORE);
} else if (msgBox.clickedButton() == ignoreAllButton) {
- manipulator->setResponse(IGNORE, true, err);
+ op->setResponse(OperationThread::IGNORE, true, err);
}
}
void FileOperator::showOverwritePrompt(
- FileManipulatorThread* manipulator,
+ OperationThread* op,
const QString &fileName,
const bool dirOverDir)
{
if (dirOverDir) {
msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
- .arg(FileOperator::shortenPath(fileName)));
+ .arg(shortenPath(fileName)));
askButton = msgBox.addButtonFirst(tr("Ask"), QDialogButtonBox::AcceptRole);
skipDirButton = msgBox.addButtonSecond(tr("Skip"), QDialogButtonBox::NoRole);
} else {
- msgBox.setText(tr("File %1 already exists. Overwrite?").arg(FileOperator::shortenPath(fileName)));
+ msgBox.setText(tr("File %1 already exists. Overwrite?").arg(shortenPath(fileName)));
}
msgBox.exec();
if (msgBox.clickedButton == 0) {
- manipulator->pause = true;
- manipulator->setResponse(NONE);
+ op->pause = true;
+ op->setResponse(OperationThread::NONE);
} else if (msgBox.clickedButton == abortButton) {
- manipulator->setResponse(ABORT);
+ op->setResponse(OperationThread::ABORT);
} else if (msgBox.clickedButton == yesButton) {
- manipulator->setResponse(OVERWRITE);
+ op->setResponse(OperationThread::OVERWRITE);
} else if (msgBox.clickedButton == yesToAllButton) {
- manipulator->setResponse(OVERWRITE, true);
+ op->setResponse(OperationThread::OVERWRITE, true);
} else if (msgBox.clickedButton == noButton) {
- manipulator->setResponse(KEEP);
+ op->setResponse(OperationThread::KEEP);
} else if (msgBox.clickedButton == noToAllButton) {
- manipulator->setResponse(KEEP, true);
+ op->setResponse(OperationThread::KEEP, true);
} else if (msgBox.clickedButton == askButton) {
- manipulator->setResponse(ASK);
+ op->setResponse(OperationThread::ASK);
} else if (msgBox.clickedButton == newNameButton) {
- manipulator->setResponse(NONE);
+ op->setResponse(OperationThread::NONE);
} else if (msgBox.clickedButton == skipDirButton) {
- manipulator->setResponse(SKIP_DIR);
+ op->setResponse(OperationThread::SKIP_DIR);
}
}
-void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
+void FileOperator::showInputFilenamePrompt(OperationThread* op,
const QFileInfo &file,
const bool dir)
{
prompt = tr("Enter the new file name.");
}
- manipulator->mutex.lock();
-
- manipulator->newNameFromDialog = "";
+ op->newNameFromDialog = "";
QString text = file.fileName();
while (true) {
error = "";
if (text.contains(QRegExp("[\"*/:<>?\\\\|]"))) {
- error = "<small><br/><font color = 'red'>" + tr("The name cannot contain any of the following characters: ") +
+ error = "<small><br/><font color = 'red'>" +
+ tr("The name cannot contain any of the following characters: ") +
"\"*/:<>?\\|</font></small>";
} else if (ok && !text.isEmpty()) {
QFileInfo info(file.path() + "/" + text);
- manipulator->newNameFromDialog = info.absoluteFilePath();
+ op->newNameFromDialog = info.absoluteFilePath();
break;
}
}
- manipulator->mutex.unlock();
- manipulator->wake();
-}
-
-
-void FileOperator::remove(FileManipulatorThread* manipulator) {
- manipulator->wait();
- layout()->removeWidget(manipulator->progressBar);
- manipulatorList.removeAll(manipulator);
- delete manipulator;
+ op->wake();
}
-void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
- manipulator->progressBar->setMinimum(0);
- manipulator->progressBar->setMaximum(size);
+void FileOperator::remove(OperationThread* op) {
+ op->wait();
+ ProgressBar *bar = get(op);
+ layout()->removeWidget(bar);
+ opList.removeAll(qMakePair(op, bar));
+ delete op;
+ delete bar;
}
-void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value) {
- manipulator->setText(value);
-}
-
-
-void FileOperator::updateMainText(FileManipulatorThread* manipulator, const QString &text) {
- manipulator->progressBar->mainText = text;
- manipulator->progressBar->mainText.remove(0, manipulator->progressBar->fromText.size() + 1);
- manipulator->progressBar->repaint();
-}
-
+void FileOperator::togglePauseOperation(ProgressBar* bar) {
+ OperationThread *op = get(bar);
-void FileOperator::showPaused(FileManipulatorThread* manipulator) {
- manipulator->progressBar->paused = true;
- manipulator->progressBar->repaint();
-}
-
-
-void FileOperator::togglePauseOperation(FileManipulatorThread* manipulator) {
- if (manipulator->pause) {
- manipulator->pause = false;
- manipulator->progressBar->paused = false;
- manipulator->progressBar->repaint();
- manipulator->wake();
+ if (op->pause) {
+ op->wake();
} else {
- manipulator->pause = true;
+ op->pause = true;
}
}
-void FileOperator::abortOperation(FileManipulatorThread* manipulator) {
+void FileOperator::abortOperation(ProgressBar* bar) {
+ OperationThread *op = get(bar);
+
int confirm = QMessageBox::warning(
0,
tr("Abort operation"),
);
if(confirm == QMessageBox::Yes) {
- manipulator->abort = true;
- manipulator->pause = false;
- manipulator->wake();
- }
-}
-
-
-void FileOperator::caterNewThread(FileManipulatorThread *thread) {
- manipulatorList.append(thread);
-
- connect(thread, SIGNAL(showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int)),
- this, SLOT(showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int)));
- connect(thread, SIGNAL(showOverwritePrompt(FileManipulatorThread*, const QString&, bool)),
- this, SLOT(showOverwritePrompt(FileManipulatorThread*, const QString&, bool)));
- connect(thread, SIGNAL(showInputFilenamePrompt(FileManipulatorThread*, const QFileInfo&, bool)),
- this, SLOT(showInputFilenamePrompt(FileManipulatorThread*, const QFileInfo&, bool)));
- connect(thread, SIGNAL(finished(FileManipulatorThread*)),
- this, SLOT(remove(FileManipulatorThread*)));
- connect(thread, SIGNAL(setBarSize(FileManipulatorThread*, unsigned int)),
- this, SLOT(setBarSize(FileManipulatorThread*, unsigned int)));
- connect(thread, SIGNAL(updateProgress(FileManipulatorThread*, int)),
- this, SLOT(updateProgress(FileManipulatorThread*, int)));
- connect(thread, SIGNAL(updateFileName(FileManipulatorThread*, QString)),
- this, SLOT(updateMainText(FileManipulatorThread*, QString)));
- connect(thread, SIGNAL(operationPaused(FileManipulatorThread*)),
- this, SLOT(showPaused(FileManipulatorThread*)));
-
- connect(thread->progressBar, SIGNAL(togglePauseOperation(FileManipulatorThread*)),
- this, SLOT(togglePauseOperation(FileManipulatorThread*)));
- connect(thread->progressBar, SIGNAL(abortOperation(FileManipulatorThread*)),
- this, SLOT(abortOperation(FileManipulatorThread*)));
-
- layout()->addWidget(thread->progressBar);
- thread->start(QThread::LowestPriority);
-}
-
-
-FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) :
- progressBar(new ProgressBar(this)),
- abort(false),
- pause(false),
- files(files),
- dest(dest),
- response(FileOperator::NONE),
- overwriteAll(FileOperator::NONE),
- lastTimeUpdate(0),
- startTime(0),
- waitTime(0),
- barSize(0),
- barValue(0),
- fileSize(0),
- fileValue(0)
-{
- memset(ignoreAll, false, sizeof(ignoreAll));
-}
-
-
-FileManipulatorThread::~FileManipulatorThread() {
- if (!abort && progressBar->value() < progressBar->maximum()) {
- std::cout << "WARNING: deleting a progressbar which's value " << progressBar->value() <<
- " has not reached maximum of " << progressBar->maximum() << std::endl;
- }
- delete progressBar;
-}
-
-
-void FileManipulatorThread::setResponse(
- const FileOperator::Response response,
- const bool applyToAll,
- const int err)
-{
- mutex.lock();
-
- this->response = response;
-
- if (applyToAll) {
- if (response == FileOperator::KEEP
- || response == FileOperator::OVERWRITE
- || response == FileOperator::NONE)
- {
- overwriteAll = response;
- }
-
- if (response == FileOperator::IGNORE) {
- ignoreAll[err] = true;
- }
- }
-
- if (response == FileOperator::ABORT) abort = true;
-
- mutex.unlock();
- wake();
-}
-
-
-void FileManipulatorThread::processFiles(const QFileInfoList &files) {
- for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
- PAUSE();
- if (abort) break;
- perform(*it);
- }
-}
-
-
-bool FileManipulatorThread::remove(QString &fileName, const bool doUpdates) {
- return remove(QFileInfo(fileName), doUpdates);
-}
-
-
-bool FileManipulatorThread::remove(const QFileInfoList &files, const bool doUpdates) {
- bool res = true;
- for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
- if (!remove(*it, doUpdates)) res = false;
- PAUSE();
- if (abort) break;
- }
- return res;
-}
-
-
-bool FileManipulatorThread::remove(const QFileInfo &file, const bool doUpdates) {
- std::cout << "DELETING " << file.absoluteFilePath().toStdString() << std::endl;
-
- QString path = file.absoluteFilePath();
-
- if (removeExcludeFiles.contains(path)) {
- if (doUpdates) updateProgress(1);
- return false;
- }
-
- QFSFileEngine engine(path);
-
- if (doUpdates) updateFile(path);
-
- if (file.isDir()) {
- if (!remove(listDirFiles(path), doUpdates)) {
- if (doUpdates) updateProgress(1);
- return false;
- }
-
- if (!listDirFiles(path).size()) {
- ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
- }
- } else {
- ERROR_PROMPT(!engine.remove(), tr("Error deleting file %1."), path)
- }
-
- if (!abort && doUpdates) updateProgress(1);
-
- PAUSE();
- if (abort || response == FileOperator::IGNORE) return false;
- return true;
-}
-
-
-void FileManipulatorThread::copy(const QFileInfo &file) {
- std::cout << "COPYING " << file.absoluteFilePath().toStdString()
- << " to " << dest.absolutePath().toStdString() << std::endl;
-
- QString path(file.absoluteFilePath());
- QFSFileEngine engine(path);
- QFileInfo newFile(dest.absolutePath() + "/" + file.fileName());
-
- updateFile(path);
-
- // hack to prevent asking about the same file if we already asked in the rename(...) function
- if (overwriteAll == FileOperator::DONT_ASK_ONCE) {
- overwriteAll = FileOperator::NONE;
- } else {
- OVERWRITE_PROMPT(file, newFile)
- }
-
- QString newPath(newFile.absoluteFilePath());
- QFSFileEngine newEngine(newPath);
-
- PAUSE();
- if (abort) return;
-
- if (file.isDir()) {
- // save the overwrite response, because the response variable will get ovewritten in remove(...)
- FileOperator::Response overwriteResponse = response;
-
- if (newFile.exists() && !newFile.isDir()) {
- // overwriting a file, so check for KEEP and handle it
- if (response == FileOperator::KEEP) {
- updateProgress(fileSizeMap[path]);
- removeExcludeFiles.insert(path);
- return;
- }
-
- // if it should not be kept, remove it and return on failure
- if(!remove(newPath)) {
- updateProgress(fileSizeMap[path]);
- return;
- }
- // create new info since we deleted the file - is it needed?
- newFile = QFileInfo(newPath);
- } else {
- // overwriting a directory - response KEEP means to keep the files inside,
- // SKIP_DIR means to skip the dir completely
- if (response == FileOperator::SKIP_DIR) {
- updateProgress(fileSizeMap[path]);
- removeExcludeFiles.insert(path);
- return;
- }
- }
-
- if (!newFile.exists()) {
- SPECIAL_COPY_ERROR_PROMPT(!engine.mkdir(newPath, false),
- tr("Error creating directory %1."), newPath)
- }
-
- // we've done the job with the dir, so update progress and recurse into the dir
- updateProgress(1);
-
- // change the dest for the recursion
- QDir destBackup = dest;
- dest = newPath;
-
- // and set overwriteAll to the response we got a while ago
- // because it applies to the files inside the dir
- FileOperator::Response tmpResp = overwriteAll;
- overwriteAll = overwriteResponse;
-
- processFiles(listDirFiles(path));
-
- overwriteAll = tmpResp;
-
- ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
- tr("Error setting permissions for directory %1."), newPath)
-
- PAUSE();
- if (abort) return;
-
- dest = destBackup;
- } else {
- if (response == FileOperator::KEEP) {
- updateProgress(fileSizeMap[path]);
- removeExcludeFiles.insert(path);
- return;
- }
-
- SPECIAL_COPY_ERROR_PROMPT(checkSequentialFile(engine), tr("Cannot copy file %1."), path)
-
- if (newFile.exists() && newFile.isDir()) {
- SPECIAL_COPY_ERROR_PROMPT(!remove(newPath),
- tr("Cannot replace directory %1 due to previous errors."), newPath)
- }
-
- SPECIAL_COPY_ERROR_PROMPT(!engine.open(QIODevice::ReadOnly), tr("Error reading file %1."), path)
-
- bool ignore = false, newFileWritten = false;
- while (!abort && !ignore) {
- engine.seek(0);
- fileValue = 0;
-
- ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
- tr("Error writing file %1."), newPath)
-
- if (abort || response == FileOperator::IGNORE) {
- if (response == FileOperator::IGNORE) {
- updateProgress(fileSizeMap[path]);
- removeExcludeFiles.insert(path);
- ignore = true;
- }
- break;
- }
-
- newFileWritten = true;
-
- bool error = false;
- char block[BLOCK_SIZE];
- qint64 bytes;
- while ((bytes = engine.read(block, sizeof(block))) > 0) {
- if (bytes == -1 || bytes != newEngine.write(block, bytes)) {
- if (bytes == -1) {
- SHOW_ERROR_PROMPT(tr("Error while reading from file %1."), path);
- } else {
- SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
- }
-
- if (!abort) {
- if (response == FileOperator::IGNORE) {
- updateProgress(fileSizeMap[path] - fileValue);
- removeExcludeFiles.insert(path);
- ignore = true;
- } else {
- updateProgress(-fileValue);
- }
- }
- error = true;
- break;
- }
-
- PAUSE();
- if (abort) break;
-
- updateProgress(1);
- }
-
- if (!error) break;
- PAUSE();
- }
-
- engine.close();
- newEngine.close();
-
- PAUSE();
- if (abort || ignore) {
- if (newFileWritten) {
- newEngine.remove();
- }
- } else {
- ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
- tr("Error setting permissions for file %1."), newPath)
- }
- }
-}
-
-
-unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files,
- const bool count,
- const bool addSize)
-{
- unsigned int res = 0;
-
- for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
- unsigned int size = 0;
-
- PAUSE();
- if (abort) break;
-
- if (it->isDir()) {
- size += calculateFileSize(listDirFiles(it->absoluteFilePath()), count, addSize);
- }
-
- if (addSize) {
- if (it->isDir()) {
- ++size;
- } else {
- size += ceil(static_cast<float>(it->size()) / BLOCK_SIZE);
- }
- fileSizeMap[it->absoluteFilePath()] = size;
- }
-
- if (count) {
- ++size;
- }
-
- res += size;
+ op->abort = true;
+ op->pause = false;
+ op->wake();
}
-
- return res;
-}
-
-
-QFileInfoList FileManipulatorThread::listDirFiles(const QString &dirPath) {
- QDir dir = dirPath;
- return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden);
}
-void FileManipulatorThread::setBarSize(unsigned int size) {
- barSize = size;
- emit setBarSize(this, size);
-}
+void FileOperator::initOperation(OperationThread *thread, ProgressBar *bar) {
+ opList.append(qMakePair(thread, bar));
+ connect(thread, SIGNAL(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)),
+ this, SLOT(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)));
+ connect(thread, SIGNAL(showOverwritePrompt(OperationThread*, const QString&, bool)),
+ this, SLOT(showOverwritePrompt(OperationThread*, const QString&, bool)));
+ connect(thread, SIGNAL(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)),
+ this, SLOT(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)));
+ connect(thread, SIGNAL(finished(OperationThread*)),
+ this, SLOT(remove(OperationThread*)));
-void FileManipulatorThread::updateProgress(int value) {
- barValue += value;
- fileValue += value;
- emit updateProgress(this, value);
-}
-
-
-void FileManipulatorThread::updateFile(const QString &name) {
- fileValue = 0;
- emit updateFileName(this, FileOperator::shortenPath(name));
-}
+ connect(thread, SIGNAL(totalSizeChanged(int)), bar, SLOT(setMaximum(int)));
+ connect(thread, SIGNAL(progressUpdate(int)), bar, SLOT(updateProgress(int)));
+ connect(thread, SIGNAL(fileNameUpdated(QString)), bar, SLOT(updateMainText(QString)));
+ connect(thread, SIGNAL(operationStarted(time_t)), bar, SLOT(setStartTime(time_t)));
+ connect(thread, SIGNAL(operationPaused()), bar, SLOT(pause()));
+ connect(thread, SIGNAL(operationResumed(time_t)), bar, SLOT(resume(time_t)));
+ connect(thread, SIGNAL(removeAfterCopy()), bar, SLOT(showRemoveNotice()));
+ connect(bar, SIGNAL(togglePauseOperation(ProgressBar*)), this, SLOT(togglePauseOperation(ProgressBar*)));
+ connect(bar, SIGNAL(abortOperation(ProgressBar*)), this, SLOT(abortOperation(ProgressBar*)));
-void FileManipulatorThread::waitOnCond() {
- waitTime = time(0);
- waitCond.wait(&mutex);
-}
-
-
-bool FileManipulatorThread::checkSequentialFile(const QFSFileEngine &engine) {
- errno = 0;
- if (engine.isSequential()) {
- if (!errno) errno = 255;
- return true;
- }
-
- return false;
-}
-
-
-void FileManipulatorThread::wake() {
- startTime += time(0) - waitTime;
- waitCond.wakeAll();
+ layout()->addWidget(bar);
+ thread->start(QThread::LowestPriority);
}
-void FileManipulatorThread::setText(int value) {
- if (progressBar->value() + value > progressBar->maximum()) {
- std::cout << "WARNING: exceeding progressbar maximum (" << progressBar->maximum()
- << ") by " << value << std::endl;
- }
-
- time_t now = time(0);
- if (lastTimeUpdate < now) {
- lastTimeUpdate = now;
-
- time_t elapsed = now - startTime;
- time_t remaining = (time_t) ((float) elapsed / barValue * (barSize - barValue));
- struct tm *ts = gmtime(&remaining);
-
- if (remaining < 60) {
- strftime(timeBuf, sizeof(timeBuf), "%Ss", ts);
- } else if (remaining < 3600) {
- strftime(timeBuf, sizeof(timeBuf), "%M:%S", ts);
- } else {
- strftime(timeBuf, sizeof(timeBuf), "%H:%M:%S", ts);
- }
+ProgressBar *FileOperator::get(OperationThread *op) const {
+ for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
+ if (it->first == op) return it->second;
}
-
-
- progressBar->setFormat(QString("%p% ") + timeBuf);
- progressBar->setValue(progressBar->value() + value);
-}
-
-
-DeleteThread::DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {
- barText = tr("deleting %1");
-}
-
-
-void DeleteThread::run() {
- mutex.lock();
-
- setBarSize(calculateFileSize(files, true));
- startTime = time(0);
-
- processFiles(files);
-
- sleep(0.5);
- emit finished(this);
-}
-
-
-void DeleteThread::perform(const QFileInfo &file) {
- remove(file, true);
-}
-
-
-CopyThread::CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
- barText = tr("copying %1");
-}
-
-
-void CopyThread::run() {
- mutex.lock();
-
- setBarSize(calculateFileSize(files, false, true));
- startTime = time(0);
-
- processFiles(files);
-
- sleep(0.5);
- emit finished(this);
-}
-
-
-void CopyThread::perform(const QFileInfo &file) {
- copy(file);
-}
-
-
-MoveThread::MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
- barText = tr("moving %1");
+ return 0;
}
-void MoveThread::run() {
- mutex.lock();
-
- rename(files, dest);
-
- sleep(0.5);
- emit finished(this);
-}
-
-
-void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
- setBarSize(barSize + files.size());
- startTime = time(0);
-
- for (int i = 0; i < files.size(); ++i) {
- QString path = files[i].absoluteFilePath();
- QFSFileEngine engine(path);
- QFileInfo newFile(dest.absolutePath() + "/" + files[i].fileName());
-
- updateFile(path);
-
- OVERWRITE_PROMPT(files[i], newFile)
-
- // if we are owerwriting dir over a dir, we will get SKIP_DIR
- // as a response from OVERWRITE_PROMT meaning we should skip it
- // (KEEP would mean to keep the files inside)
- if (files[i].isDir() && newFile.exists() && newFile.isDir()) {
- if (response == FileOperator::SKIP_DIR) {
- PAUSE();
- if (abort) break;
- updateProgress(1);
- removeExcludeFiles.insert(path);
- continue;
- }
- } else {
- if (response == FileOperator::KEEP) {
- PAUSE();
- if (abort) break;
- updateProgress(1);
- removeExcludeFiles.insert(path);
- continue;
- }
- }
-
- QString newPath(newFile.absoluteFilePath());
- QFSFileEngine newEngine(newPath);
-
- bool done = false;
-
- while (!abort && !engine.rename(newPath)) {
- // source and target are on different partitions
- // this should happen on the first file, unless some are skipped by overwrite prompt
- // we calculate the actual file sizes, because from now on copy & remove takes over
- if (errno == EXDEV) {
- overwriteAll = response;
- // hack: we already checked the first file we are sending to processFiles(...)
- // so we don't want to ask about this one again
- if (overwriteAll == FileOperator::NONE) overwriteAll = FileOperator::DONT_ASK_ONCE;
-
- QFileInfoList remainingFiles = files.mid(i);
-
- setBarSize(barValue + calculateFileSize(remainingFiles, true, true));
-
- processFiles(remainingFiles);
-
- barText = tr("deleting %1");
-
- remove(remainingFiles, true);
-
- done = true;
- break;
- // the target is nonempty dir. lets call this recursively and rename the contents one by one
- } else if (errno == ENOTEMPTY || errno == EEXIST) {
- FileOperator::Response tmpResp = overwriteAll;
- overwriteAll = response;
-
- rename(listDirFiles(path), QDir(newPath));
- PAUSE();
- if (abort) break;
-
- overwriteAll = tmpResp;
-
- remove(files[i]);
-
- break;
- // source and target are nonmatching types(file and dir)
- // remove the target and let it loop once again
- } else if (errno == ENOTDIR || errno == EISDIR) {
- if (!remove(newPath)) break;
- } else {
- SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
-
- if (response == FileOperator::IGNORE) {
- break;
- }
- }
- PAUSE();
- }
-
- if (done) break;
-
- PAUSE();
- if (abort) break;
- updateProgress(1);
+OperationThread *FileOperator::get(ProgressBar *bar) const {
+ for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
+ if (it->second == bar) return it->first;
}
-}
-
-
-void MoveThread::perform(const QFileInfo &file) {
- copy(file);
+ return 0;
}
#define FILEOPERATOR_H
#include <QWidget>
-#include <QFileInfo>
-#include <QThread>
-#include <QMutex>
-#include <QWaitCondition>
-#include <QDir>
-#include <QFSFileEngine>
-#include <QMap>
-#include <QSet>
#include "progressbar.h"
+#include "operationthread.h"
-class FileManipulatorThread;
-
+typedef QList<QPair<OperationThread*, ProgressBar*> > OperationList;
class FileOperator : public QWidget {
Q_OBJECT
public:
- // DONT_ASK_ONCE is a hackish way to avoid asking twice to overwrite the same directory when moving
- enum Response{NONE, ABORT, RETRY, IGNORE, KEEP, OVERWRITE, SKIP_DIR, ASK, DONT_ASK_ONCE};
-
FileOperator(QWidget *parent = 0);
- static QString shortenPath(const QString &path);
- static QString unwindPath(const QString &path);
-
void deleteFiles(const QFileInfoList &files);
void copyFiles(const QFileInfoList &files, QDir &destination);
void moveFiles(const QFileInfoList &files, QDir &destination);
public slots:
- void showErrorPrompt(FileManipulatorThread* manipulator,
+ void showErrorPrompt(OperationThread* op,
const QString &message,
const QString &fileName,
const int err);
- void showOverwritePrompt(FileManipulatorThread* manipulator,
+ void showOverwritePrompt(OperationThread* op,
const QString &fileName,
const bool dirOverDir);
- void showInputFilenamePrompt(FileManipulatorThread* manipulator,
+ void showInputFilenamePrompt(OperationThread* op,
const QFileInfo &fileName,
const bool dirOverDir);
- void remove(FileManipulatorThread* manipulator);
- void setBarSize(FileManipulatorThread* manipulator, unsigned int size);
- void updateProgress(FileManipulatorThread* manipulator, int value);
- void updateMainText(FileManipulatorThread* manipulator, const QString &text);
- void showPaused(FileManipulatorThread* manipulator);
+ void remove(OperationThread* op);
- void togglePauseOperation(FileManipulatorThread* manipulator);
- void abortOperation(FileManipulatorThread* manipulator);
+ void togglePauseOperation(ProgressBar* bar);
+ void abortOperation(ProgressBar* bar);
protected:
- void caterNewThread(FileManipulatorThread *thread);
+ void initOperation(OperationThread *thread, ProgressBar *bar);
+ ProgressBar *get(OperationThread *op) const;
+ OperationThread *get(ProgressBar *bar) const;
- QList<FileManipulatorThread*> manipulatorList;
+ OperationList opList;
QPixmap deleteIcon, inverseDeleteIcon, copyIcon, inverseCopyIcon, moveIcon, inverseMoveIcon;
};
-
-class FileManipulatorThread : public QThread {
- Q_OBJECT
-
-public:
- explicit FileManipulatorThread(const QFileInfoList files, QDir dest = QDir());
- ~FileManipulatorThread();
- void setResponse(const FileOperator::Response response, const bool appyToAll = false, const int err = 0);
-
- void setText(int value);
-
- void wake();
-
- ProgressBar *progressBar;
-
- QMutex mutex;
- // the new name entered from the overwrite dialog
- QString newNameFromDialog;
- // flags to abort/pause the operation
- bool abort, pause;
-
-protected:
- void processFiles(const QFileInfoList &files);
- virtual void perform(const QFileInfo &file) = 0;
-
- bool remove(QString &fileName, const bool doUpdates = false);
- bool remove(const QFileInfoList &files, const bool doUpdates = false);
- bool remove(const QFileInfo &file, const bool doUpdates = false);
-
- void copy(const QFileInfo &file);
-
- unsigned int calculateFileSize(const QFileInfoList &files,
- const bool count = false,
- const bool addSize = false);
-
- QFileInfoList listDirFiles(const QString &dirPath);
-
- void setBarSize(unsigned int size);
- void updateProgress(int value);
- void updateFile(const QString &name);
-
- void waitOnCond();
-
- bool checkSequentialFile(const QFSFileEngine &engine);
-
- QWaitCondition waitCond;
-
- // files to process by the operation
- const QFileInfoList files;
- // destination for files - changes as the operation recurses into directories
- QDir dest;
-
- // responses from the dialog prompts (error and overwrite)
- FileOperator::Response response;
- FileOperator::Response overwriteAll;
- // an array indicating whether to always ignore the error of index errno
- bool ignoreAll[256];
-
- // set of files that won't be deleted by the remove(...) functions
- // used when move(...) would not overwrite target file to ensure the source file doesn't get deleted
- QSet<QString> removeExcludeFiles;
-
- // A map of file paths to their size. Not the actual size, but what is calculated for the
- // purpose of the progressbar for the given operation. So either fileSize/BLOCK_SIZE or simply
- // 1 for a file and file count for dirs (or both for copy&delete)
- QMap<QString, qint64> fileSizeMap;
-
- // the text of the progressBar (the format)
- QString barText;
- // stamp of the last ETA recalculation - done every second
- time_t lastTimeUpdate;
- time_t startTime, waitTime;
- char timeBuf[10];
- // progress information of the bar and for the current file
- unsigned int barSize, barValue, fileSize, fileValue;
-
-signals:
- void showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int);
- void showOverwritePrompt(FileManipulatorThread*, const QString&, const bool);
- void showInputFilenamePrompt(FileManipulatorThread*, const QFileInfo&, const bool);
- void finished(FileManipulatorThread*);
- void setBarSize(FileManipulatorThread*, unsigned int);
- void updateProgress(FileManipulatorThread*, int);
- void updateFileName(FileManipulatorThread*, QString);
- void operationPaused(FileManipulatorThread*);
-};
-
-
-class DeleteThread : public FileManipulatorThread {
- Q_OBJECT
-
-public:
- explicit DeleteThread(const QFileInfoList &files);
-
-protected:
- void run();
- virtual void perform(const QFileInfo &file);
-};
-
-
-class CopyThread : public FileManipulatorThread {
- Q_OBJECT
-
-public:
- explicit CopyThread(const QFileInfoList &files, QDir &dest);
-
-protected:
- void run();
- virtual void perform(const QFileInfo &file);
-};
-
-
-class MoveThread : public FileManipulatorThread {
- Q_OBJECT
-
-public:
- explicit MoveThread(const QFileInfoList &files, QDir &dest);
-
-protected:
- void run();
- virtual void perform(const QFileInfo &file);
- void rename(const QFileInfoList &files, const QDir &dest);
-};
-
-
#endif // FILEOPERATOR_H
--- /dev/null
+// case - file manager for N900
+// Copyright (C) 2010 Lukas Hrazky <lukkash@email.cz>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+
+#include "operationthread.h"
+
+#include <errno.h>
+#include <math.h>
+
+#include "utils.h"
+
+
+#define BLOCK_SIZE (256 * 1024)
+
+
+#define PAUSE() \
+ if (pause) { \
+ emit operationPaused(); \
+ waitOnCond(); \
+ }
+
+
+#define SHOW_ERROR_PROMPT(promptString, fileName) \
+ response = NONE; \
+ if (ignoreAll[errno]) { \
+ response = IGNORE; \
+ } else { \
+ char buf[255]; \
+ char *realBuf = buf; \
+ if (errno == 255) { \
+ strcpy(buf, tr("File is sequential").toStdString().c_str()); \
+ } else { \
+ realBuf = strerror_r(errno, buf, 255); \
+ } \
+ emit showErrorPrompt(this, promptString + " " + realBuf + ".", fileName, errno); \
+ waitOnCond(); \
+ }
+
+
+#define ERROR_PROMPT(operation, promptString, fileName) \
+{ \
+ response = NONE; \
+ while (!abort && operation) { \
+ SHOW_ERROR_PROMPT(promptString, fileName) \
+ if (response == IGNORE) { \
+ break; \
+ } \
+ PAUSE() \
+ } \
+}
+
+
+#define SPECIAL_COPY_ERROR_PROMPT(operation, promptString, fileName) \
+{ \
+ ERROR_PROMPT(operation, promptString, fileName) \
+ if (abort || response == IGNORE) { \
+ if (!abort) { \
+ updateProgress(fileSizeMap[path]); \
+ removeExcludeFiles.insert(path); \
+ } \
+ return; \
+ } \
+}
+
+
+#define OVERWRITE_PROMPT(file, newFile) \
+{ \
+ response = NONE; \
+ \
+ while (!abort && response == NONE && newFile.exists()) { \
+ if (overwriteAll != NONE) { \
+ response = overwriteAll; \
+ } else { \
+ emit showOverwritePrompt(this, newFile.absoluteFilePath(), \
+ newFile.isDir() && file.isDir()); \
+ waitOnCond(); \
+ \
+ PAUSE() \
+ else if (response == NONE) { \
+ emit showInputFilenamePrompt(this, newFile, file.isDir()); \
+ waitOnCond(); \
+ if (newNameFromDialog.size()) { \
+ newFile.setFile(newNameFromDialog); \
+ } \
+ } \
+ } \
+ } \
+ if (response == ASK) response = NONE; \
+}
+
+
+OperationThread::OperationThread(const QFileInfoList files, QDir dest) :
+ abort(false),
+ pause(false),
+ files(files),
+ dest(dest),
+ response(NONE),
+ overwriteAll(NONE),
+ totalSize(0),
+ totalValue(0),
+ fileValue(0)
+{
+ memset(ignoreAll, false, sizeof(ignoreAll));
+}
+
+
+void OperationThread::setResponse(
+ const Response response,
+ const bool applyToAll,
+ const int err)
+{
+ mutex.lock();
+
+ this->response = response;
+
+ if (applyToAll) {
+ if (response == KEEP
+ || response == OVERWRITE
+ || response == NONE)
+ {
+ overwriteAll = response;
+ }
+
+ if (response == IGNORE) {
+ ignoreAll[err] = true;
+ }
+ }
+
+ if (response == ABORT) abort = true;
+
+ mutex.unlock();
+ waitCond.wakeAll();
+}
+
+
+void OperationThread::processFiles(const QFileInfoList &files) {
+ for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
+ PAUSE();
+ if (abort) break;
+ perform(*it);
+ }
+}
+
+
+bool OperationThread::remove(QString &fileName, const bool doUpdates) {
+ return remove(QFileInfo(fileName), doUpdates);
+}
+
+
+bool OperationThread::remove(const QFileInfoList &files, const bool doUpdates) {
+ bool res = true;
+ for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
+ if (!remove(*it, doUpdates)) res = false;
+ PAUSE();
+ if (abort) break;
+ }
+ return res;
+}
+
+
+bool OperationThread::remove(const QFileInfo &file, const bool doUpdates) {
+ QString path = file.absoluteFilePath();
+
+ if (removeExcludeFiles.contains(path)) {
+ if (doUpdates) updateProgress(1);
+ return false;
+ }
+
+ QFSFileEngine engine(path);
+
+ if (doUpdates) updateFile(path);
+
+ if (file.isDir()) {
+ if (!remove(listDirFiles(path), doUpdates)) {
+ if (doUpdates) updateProgress(1);
+ return false;
+ }
+
+ if (!listDirFiles(path).size()) {
+ ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
+ }
+ } else {
+ ERROR_PROMPT(!engine.remove(), tr("Error deleting file %1."), path)
+ }
+
+ if (!abort && doUpdates) updateProgress(1);
+
+ PAUSE();
+ if (abort || response == IGNORE) return false;
+ return true;
+}
+
+
+void OperationThread::copy(const QFileInfo &file) {
+ QString path(file.absoluteFilePath());
+ QFSFileEngine engine(path);
+ QFileInfo newFile(dest.absolutePath() + "/" + file.fileName());
+
+ updateFile(path);
+
+ // hack to prevent asking about the same file if we already asked in the rename(...) function
+ if (overwriteAll == DONT_ASK_ONCE) {
+ overwriteAll = NONE;
+ } else {
+ OVERWRITE_PROMPT(file, newFile)
+ }
+
+ QString newPath(newFile.absoluteFilePath());
+ QFSFileEngine newEngine(newPath);
+
+ PAUSE();
+ if (abort) return;
+
+ if (file.isDir()) {
+ // save the overwrite response, because the response variable will get ovewritten in remove(...)
+ Response overwriteResponse = response;
+
+ if (newFile.exists() && !newFile.isDir()) {
+ // overwriting a file, so check for KEEP and handle it
+ if (response == KEEP) {
+ updateProgress(fileSizeMap[path]);
+ removeExcludeFiles.insert(path);
+ return;
+ }
+
+ // if it should not be kept, remove it and return on failure
+ if(!remove(newPath)) {
+ updateProgress(fileSizeMap[path]);
+ return;
+ }
+ // create new info since we deleted the file - is it needed?
+ newFile = QFileInfo(newPath);
+ } else {
+ // overwriting a directory - response KEEP means to keep the files inside,
+ // SKIP_DIR means to skip the dir completely
+ if (response == SKIP_DIR) {
+ updateProgress(fileSizeMap[path]);
+ removeExcludeFiles.insert(path);
+ return;
+ }
+ }
+
+ if (!newFile.exists()) {
+ SPECIAL_COPY_ERROR_PROMPT(!engine.mkdir(newPath, false),
+ tr("Error creating directory %1."), newPath)
+ }
+
+ // we've done the job with the dir, so update progress and recurse into the dir
+ updateProgress(1);
+
+ // change the dest for the recursion
+ QDir destBackup = dest;
+ dest = newPath;
+
+ // and set overwriteAll to the response we got a while ago
+ // because it applies to the files inside the dir
+ Response tmpResp = overwriteAll;
+ overwriteAll = overwriteResponse;
+
+ processFiles(listDirFiles(path));
+
+ overwriteAll = tmpResp;
+
+ ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
+ tr("Error setting permissions for directory %1."), newPath)
+
+ PAUSE();
+ if (abort) return;
+
+ dest = destBackup;
+ } else {
+ if (response == KEEP) {
+ updateProgress(fileSizeMap[path]);
+ removeExcludeFiles.insert(path);
+ return;
+ }
+
+ SPECIAL_COPY_ERROR_PROMPT(checkSequentialFile(engine), tr("Cannot copy file %1."), path)
+
+ if (newFile.exists() && newFile.isDir()) {
+ SPECIAL_COPY_ERROR_PROMPT(!remove(newPath),
+ tr("Cannot replace directory %1 due to previous errors."), newPath)
+ }
+
+ SPECIAL_COPY_ERROR_PROMPT(!engine.open(QIODevice::ReadOnly), tr("Error reading file %1."), path)
+
+ bool ignore = false, newFileWritten = false;
+ while (!abort && !ignore) {
+ engine.seek(0);
+ fileValue = 0;
+
+ ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
+ tr("Error writing file %1."), newPath)
+
+ if (abort || response == IGNORE) {
+ if (response == IGNORE) {
+ updateProgress(fileSizeMap[path]);
+ removeExcludeFiles.insert(path);
+ ignore = true;
+ }
+ break;
+ }
+
+ newFileWritten = true;
+
+ bool error = false;
+ char block[BLOCK_SIZE];
+ qint64 bytes;
+
+ while ((bytes = engine.read(block, sizeof(block))) > 0) {
+ if (bytes == -1 || bytes != newEngine.write(block, bytes)) {
+ if (bytes == -1) {
+ SHOW_ERROR_PROMPT(tr("Error while reading from file %1."), path);
+ } else {
+ SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
+ }
+
+ if (!abort) {
+ if (response == IGNORE) {
+ updateProgress(fileSizeMap[path] - fileValue);
+ removeExcludeFiles.insert(path);
+ ignore = true;
+ } else {
+ updateProgress(-fileValue);
+ }
+ }
+ error = true;
+ break;
+ }
+
+ PAUSE();
+ if (abort) break;
+
+ updateProgress(1);
+ }
+
+ if (!error) break;
+ PAUSE();
+ }
+
+ engine.close();
+ newEngine.close();
+
+ PAUSE();
+ if (abort || ignore) {
+ if (newFileWritten) {
+ newEngine.remove();
+ }
+ } else {
+ ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
+ tr("Error setting permissions for file %1."), newPath)
+ }
+ }
+}
+
+
+unsigned int OperationThread::calculateFileSize(const QFileInfoList &files,
+ const bool count,
+ const bool addSize)
+{
+ unsigned int res = 0;
+
+ for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
+ unsigned int size = 0;
+
+ PAUSE();
+ if (abort) break;
+
+ if (it->isDir()) {
+ size += calculateFileSize(listDirFiles(it->absoluteFilePath()), count, addSize);
+ }
+
+ if (addSize) {
+ if (it->isDir()) {
+ ++size;
+ } else {
+ size += ceil(static_cast<float>(it->size()) / BLOCK_SIZE);
+ }
+ fileSizeMap[it->absoluteFilePath()] = size;
+ }
+
+ if (count) {
+ ++size;
+ }
+
+ res += size;
+ }
+
+ return res;
+}
+
+
+QFileInfoList OperationThread::listDirFiles(const QString &dirPath) {
+ QDir dir = dirPath;
+ return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden);
+}
+
+
+void OperationThread::setTotalSize(unsigned int size) {
+ totalSize = size;
+ emit totalSizeChanged(size);
+}
+
+
+void OperationThread::updateProgress(int value) {
+ totalValue += value;
+ fileValue += value;
+ emit progressUpdate(value);
+}
+
+
+void OperationThread::updateFile(const QString &name) {
+ fileValue = 0;
+ emit fileNameUpdated(shortenPath(name));
+}
+
+
+void OperationThread::waitOnCond() {
+ time_t waitTime = time(0);
+ waitCond.wait(&mutex);
+ emit operationResumed(time(0) - waitTime);
+}
+
+
+bool OperationThread::checkSequentialFile(const QFSFileEngine &engine) {
+ errno = 0;
+ if (engine.isSequential()) {
+ if (!errno) errno = 255;
+ return true;
+ }
+
+ return false;
+}
+
+
+void OperationThread::wake() {
+ pause = false;
+ waitCond.wakeAll();
+}
+
+
+void DeleteThread::run() {
+ mutex.lock();
+
+ setTotalSize(calculateFileSize(files, true));
+ emit operationStarted(time(0));
+
+ processFiles(files);
+
+ sleep(0.5);
+ emit finished(this);
+}
+
+
+void DeleteThread::perform(const QFileInfo &file) {
+ remove(file, true);
+}
+
+
+void CopyThread::run() {
+ mutex.lock();
+
+ setTotalSize(calculateFileSize(files, false, true));
+ emit operationStarted(time(0));
+
+ processFiles(files);
+
+ sleep(0.5);
+ emit finished(this);
+}
+
+
+void CopyThread::perform(const QFileInfo &file) {
+ copy(file);
+}
+
+
+void MoveThread::run() {
+ mutex.lock();
+
+ rename(files, dest);
+
+ sleep(0.5);
+ emit finished(this);
+}
+
+
+void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
+ setTotalSize(totalSize + files.size());
+ emit operationStarted(time(0));
+
+ for (int i = 0; i < files.size(); ++i) {
+ QString path = files[i].absoluteFilePath();
+ QFSFileEngine engine(path);
+ QFileInfo newFile(dest.absolutePath() + "/" + files[i].fileName());
+
+ updateFile(path);
+
+ OVERWRITE_PROMPT(files[i], newFile)
+
+ // if we are owerwriting dir over a dir, we will get SKIP_DIR
+ // as a response from OVERWRITE_PROMT meaning we should skip it
+ // (KEEP would mean to keep the files inside)
+ if (files[i].isDir() && newFile.exists() && newFile.isDir()) {
+ if (response == SKIP_DIR) {
+ PAUSE();
+ if (abort) break;
+ updateProgress(1);
+ removeExcludeFiles.insert(path);
+ continue;
+ }
+ } else {
+ if (response == KEEP) {
+ PAUSE();
+ if (abort) break;
+ updateProgress(1);
+ removeExcludeFiles.insert(path);
+ continue;
+ }
+ }
+
+ QString newPath(newFile.absoluteFilePath());
+ QFSFileEngine newEngine(newPath);
+
+ bool done = false;
+
+ while (!abort && !engine.rename(newPath)) {
+ // source and target are on different partitions
+ // this should happen on the first file, unless some are skipped by overwrite prompt
+ // we calculate the actual file sizes, because from now on copy & remove takes over
+ if (errno == EXDEV) {
+ overwriteAll = response;
+ // hack: we already checked the first file we are sending to processFiles(...)
+ // so we don't want to ask about this one again
+ if (overwriteAll == NONE) overwriteAll = DONT_ASK_ONCE;
+
+ QFileInfoList remainingFiles = files.mid(i);
+
+ setTotalSize(totalValue + calculateFileSize(remainingFiles, true, true));
+
+ processFiles(remainingFiles);
+
+ emit removeAfterCopy();
+
+ remove(remainingFiles, true);
+
+ done = true;
+ break;
+ // the target is nonempty dir. lets call this recursively and rename the contents one by one
+ } else if (errno == ENOTEMPTY || errno == EEXIST) {
+ Response tmpResp = overwriteAll;
+ overwriteAll = response;
+
+ rename(listDirFiles(path), QDir(newPath));
+ PAUSE();
+ if (abort) break;
+
+ overwriteAll = tmpResp;
+
+ remove(files[i]);
+
+ break;
+ // source and target are nonmatching types(file and dir)
+ // remove the target and let it loop once again
+ } else if (errno == ENOTDIR || errno == EISDIR) {
+ if (!remove(newPath)) break;
+ } else {
+ SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
+
+ if (response == IGNORE) {
+ break;
+ }
+ }
+ PAUSE();
+ }
+
+ if (done) break;
+
+ PAUSE();
+ if (abort) break;
+ updateProgress(1);
+ }
+}
+
+
+void MoveThread::perform(const QFileInfo &file) {
+ copy(file);
+}