#include <QHBoxLayout>
#include <QChar>
+#include "dialog.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 = strerror_r(errno, 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); \
- waitCond.wait(&mutex); \
+ waitOnCond(); \
}
if (response == FileOperator::IGNORE) { \
break; \
} \
+ PAUSE() \
} \
}
{ \
response = FileOperator::NONE; \
\
- if (newFile.exists()) { \
+ while (!abort && response == FileOperator::NONE && newFile.exists()) { \
if (overwriteAll != FileOperator::NONE) { \
response = overwriteAll; \
} else { \
- bool dirOverDir = false; \
- if (newFile.isDir() && file.isDir()) dirOverDir = true; \
- emit showOverwritePrompt(this, newFile.absoluteFilePath(), dirOverDir); \
- waitCond.wait(&mutex); \
+ 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; \
}
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
setLayout(layout);
+ qRegisterMetaType<QFileInfo>("QFileInfo");
}
QString FileOperator::shortenPath(const QString &path) {
QString homePath = QFSFileEngine::homePath();
- QString result = path;
+
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;
const int err)
{
QMessageBox msgBox;
- msgBox.addButton(QMessageBox::Cancel);
+ QAbstractButton *cancelButton = msgBox.addButton(QMessageBox::Cancel);
QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
msgBox.exec();
- if (msgBox.clickedButton() == abortButton) {
+ if (msgBox.clickedButton() == cancelButton) {
+ manipulator->pause = true;
+ manipulator->setResponse(RETRY);
+ } else if (msgBox.clickedButton() == abortButton) {
manipulator->setResponse(ABORT);
} else if (msgBox.clickedButton() == retryButton) {
manipulator->setResponse(RETRY);
const QString &fileName,
const bool dirOverDir)
{
- QMessageBox msgBox;
- msgBox.addButton(QMessageBox::Cancel);
- QAbstractButton *yesButton = msgBox.addButton(QMessageBox::Yes);
- QAbstractButton *yesToAllButton = msgBox.addButton(QMessageBox::YesToAll);
- QAbstractButton *noButton = msgBox.addButton(QMessageBox::No);
- QAbstractButton *noToAllButton = msgBox.addButton(QMessageBox::NoToAll);
- QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
+ Dialog msgBox;
+ QAbstractButton *yesButton = msgBox.addButtonFirst(QDialogButtonBox::Yes);
+ QAbstractButton *yesToAllButton = msgBox.addButtonFirst(QDialogButtonBox::YesToAll);
+ QAbstractButton *noButton = msgBox.addButtonSecond(QDialogButtonBox::No);
+ QAbstractButton *noToAllButton = msgBox.addButtonSecond(QDialogButtonBox::NoToAll);
+ QAbstractButton *abortButton = msgBox.addButtonSecond(tr("Abort"), QDialogButtonBox::DestructiveRole);
+ QAbstractButton *newNameButton = msgBox.addButtonFirst(tr("New Name"), QDialogButtonBox::AcceptRole);
QAbstractButton *askButton = 0;
QAbstractButton *skipDirButton = 0;
if (dirOverDir) {
msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
.arg(FileOperator::shortenPath(fileName)));
- askButton = msgBox.addButton(tr("Ask"), QMessageBox::AcceptRole);
- skipDirButton = msgBox.addButton(tr("Skip"), QMessageBox::NoRole);
+ 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.exec();
- if (msgBox.clickedButton() == abortButton) {
+ if (msgBox.clickedButton == 0) {
+ manipulator->pause = true;
+ manipulator->setResponse(NONE);
+ } else if (msgBox.clickedButton == abortButton) {
manipulator->setResponse(ABORT);
- } else if (msgBox.clickedButton() == yesButton) {
+ } else if (msgBox.clickedButton == yesButton) {
manipulator->setResponse(OVERWRITE);
- } else if (msgBox.clickedButton() == yesToAllButton) {
+ } else if (msgBox.clickedButton == yesToAllButton) {
manipulator->setResponse(OVERWRITE, true);
- } else if (msgBox.clickedButton() == noButton) {
+ } else if (msgBox.clickedButton == noButton) {
manipulator->setResponse(KEEP);
- } else if (msgBox.clickedButton() == noToAllButton) {
+ } else if (msgBox.clickedButton == noToAllButton) {
manipulator->setResponse(KEEP, true);
- } else if (msgBox.clickedButton() == askButton) {
- manipulator->setResponse(NONE, true);
- } else if (msgBox.clickedButton() == skipDirButton) {
+ } else if (msgBox.clickedButton == askButton) {
+ manipulator->setResponse(ASK);
+ } else if (msgBox.clickedButton == newNameButton) {
+ manipulator->setResponse(NONE);
+ } else if (msgBox.clickedButton == skipDirButton) {
manipulator->setResponse(SKIP_DIR);
}
}
+void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
+ const QFileInfo &file,
+ const bool dir)
+{
+ bool ok;
+ QString prompt, error;
+
+ if (dir) {
+ prompt = tr("Enter the new directory name.");
+ } else {
+ prompt = tr("Enter the new file name.");
+ }
+
+ manipulator->mutex.lock();
+
+ manipulator->newNameFromDialog = "";
+ QString text = file.fileName();
+
+ while (true) {
+ text = QInputDialog::getText(this, QString(), prompt + error, QLineEdit::Normal, text, &ok);
+
+ if (!ok) break;
+
+ error = "";
+ if (text.contains(QRegExp("[\"*/:<>?\\\\|]"))) {
+ 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();
+ break;
+ }
+ }
+
+ manipulator->mutex.unlock();
+ manipulator->wake();
+}
+
+
void FileOperator::remove(FileManipulatorThread* manipulator) {
manipulator->wait();
layout()->removeWidget(manipulator->progressBar);
void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
- if (!manipulator->progressBar->maximum()) {
- manipulator->startTime = time(0);
- }
manipulator->progressBar->setMinimum(0);
manipulator->progressBar->setMaximum(size);
}
}
+void FileOperator::showPaused(FileManipulatorThread* manipulator) {
+ manipulator->setText(0);
+}
+
+
+void FileOperator::togglePauseOperation(FileManipulatorThread* manipulator) {
+ if (manipulator->pause) {
+ manipulator->pause = false;
+ manipulator->wake();
+ } else {
+ manipulator->pause = true;
+ }
+}
+
+
+void FileOperator::abortOperation(FileManipulatorThread* manipulator) {
+ int confirm = QMessageBox::warning(
+ 0,
+ tr("Abort operation"),
+ tr("Are you sure you want to abort the operation?"),
+ QMessageBox::Yes,
+ QMessageBox::No
+ );
+
+ if(confirm == QMessageBox::Yes) {
+ manipulator->abort = true;
+ manipulator->pause = false;
+ manipulator->setText(0);
+ manipulator->wake();
+ }
+}
+
+
void FileOperator::caterNewThread(FileManipulatorThread *thread) {
manipulatorList.append(thread);
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(operationPaused(FileManipulatorThread*)),
+ this, SLOT(showPaused(FileManipulatorThread*)));
- thread->progressBar->setValue(0);
+ connect(thread->progressBar, SIGNAL(togglePauseOperation(FileManipulatorThread*)),
+ this, SLOT(togglePauseOperation(FileManipulatorThread*)));
+ connect(thread->progressBar, SIGNAL(abortOperation(FileManipulatorThread*)),
+ this, SLOT(abortOperation(FileManipulatorThread*)));
+ thread->setText(0);
layout()->addWidget(thread->progressBar);
thread->start(QThread::LowestPriority);
}
FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) :
- progressBar(new QProgressBar()),
- startTime(0),
+ progressBar(new ProgressBar(this)),
+ abort(false),
+ pause(false),
files(files),
dest(dest),
response(FileOperator::NONE),
overwriteAll(FileOperator::NONE),
- abort(false),
lastTimeUpdate(0),
+ startTime(0),
+ waitTime(0),
barSize(0),
barValue(0),
fileSize(0),
fileValue(0)
{
memset(ignoreAll, false, sizeof(ignoreAll));
- progressBar->setMaximum(0);
- QFont barFont = progressBar->font();
- barFont.setPointSize(12);
- progressBar->setFont(barFont);
- progressBar->setFormat(tr("Gathering information..."));
- progressBar->setMinimumHeight(44);
- progressBar->setStyle(new QPlastiqueStyle);
- //progressBar->setStyle(new QMotifStyle);
}
FileManipulatorThread::~FileManipulatorThread() {
- if (progressBar->value() < progressBar->maximum()) {
+ 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;
}
if (response == FileOperator::ABORT) abort = true;
mutex.unlock();
- waitCond.wakeAll();
+ wake();
}
void FileManipulatorThread::processFiles(const QFileInfoList &files) {
for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
- perform(*it);
+ PAUSE();
if (abort) break;
+ perform(*it);
}
}
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;
if (!abort && doUpdates) updateProgress(1);
+ PAUSE();
if (abort || response == FileOperator::IGNORE) return false;
return true;
}
<< " to " << dest.absolutePath().toStdString() << std::endl;
QString path(file.absoluteFilePath());
- QString newPath(dest.absolutePath() + "/" + file.fileName());
QFSFileEngine engine(path);
- QFSFileEngine newEngine(newPath);
- QFileInfo newFile(newPath);
+ QFileInfo newFile(dest.absolutePath() + "/" + file.fileName());
updateFile(path);
OVERWRITE_PROMPT(file, newFile)
}
+ QString newPath(newFile.absoluteFilePath());
+ QFSFileEngine newEngine(newPath);
+
+ PAUSE();
if (abort) return;
if (file.isDir()) {
ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
tr("Error setting permissions for directory %1."), newPath)
+ PAUSE();
if (abort) return;
dest = destBackup;
return;
}
- SPECIAL_COPY_ERROR_PROMPT(engine.isSequential(), tr("Cannot copy sequential file %1."), path)
+ SPECIAL_COPY_ERROR_PROMPT(checkSequentialFile(engine), tr("Cannot copy file %1."), path)
if (newFile.exists() && newFile.isDir()) {
SPECIAL_COPY_ERROR_PROMPT(!remove(newPath),
SPECIAL_COPY_ERROR_PROMPT(!engine.open(QIODevice::ReadOnly), tr("Error reading file %1."), path)
- bool ignore = false;
+ bool ignore = false, newFileWritten = false;
while (!abort && !ignore) {
engine.seek(0);
fileValue = 0;
break;
}
+ newFileWritten = true;
+
bool error = false;
char block[BLOCK_SIZE];
qint64 bytes;
break;
}
+ PAUSE();
+ if (abort) break;
+
updateProgress(1);
}
if (!error) break;
+ PAUSE();
}
engine.close();
newEngine.close();
+ PAUSE();
if (abort || ignore) {
- newEngine.remove();
+ if (newFileWritten) {
+ newEngine.remove();
+ }
} else {
ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
tr("Error setting permissions for file %1."), newPath)
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);
}
}
+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();
+}
+
+
void FileManipulatorThread::setText(int value) {
if (progressBar->value() + value > progressBar->maximum()) {
std::cout << "WARNING: exceeding progressbar maximum (" << progressBar->maximum()
<< ") by " << value << std::endl;
}
+
+ if (!fileName.size()) {
+ if (pause) {
+ progressBar->setFormat(tr("Gathering information...") + " (" + tr("paused") + ")");
+ } else {
+ progressBar->setFormat(tr("Gathering information..."));
+ }
+ return;
+ }
time_t now = time(0);
if (lastTimeUpdate < now) {
}
}
- progressBar->setFormat(barText.arg(fileName) + "\n%p% ETA " + timeBuf);
+ QString newFormat = barText.arg(fileName) + "\n%p% ETA " + timeBuf;
+ if (pause) newFormat += " (" + tr("paused") + ")";
+
+ progressBar->setFormat(newFormat);
progressBar->setValue(progressBar->value() + value);
}
mutex.lock();
setBarSize(calculateFileSize(files, true));
+ startTime = time(0);
processFiles(files);
mutex.lock();
setBarSize(calculateFileSize(files, false, true));
+ startTime = time(0);
processFiles(files);
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);
- QString newPath = dest.absolutePath() + "/" + files[i].fileName();
- QFileInfo newFile(newPath);
+ 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);
}
} else {
if (response == FileOperator::KEEP) {
+ PAUSE();
if (abort) break;
updateProgress(1);
removeExcludeFiles.insert(path);
}
}
+ 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
remove(remainingFiles, true);
- // just to quit the loops, we are done
- abort = 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;
break;
}
}
+ PAUSE();
}
-
+
+ if (done) break;
+
+ PAUSE();
if (abort) break;
updateProgress(1);
}