#define BLOCK_SIZE 524288
+#define PAUSE() \
+ if (pause) { \
+ emit operationPaused(this); \
+ waitOnCond(); \
+ }
+
+
#define SHOW_ERROR_PROMPT(promptString, fileName) \
response = FileOperator::NONE; \
if (ignoreAll[errno]) { \
char buf[255]; \
char *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; \
\
- while (response == FileOperator::NONE && newFile.exists()) { \
+ while (!abort && response == FileOperator::NONE && newFile.exists()) { \
if (overwriteAll != FileOperator::NONE) { \
response = overwriteAll; \
} else { \
emit showOverwritePrompt(this, newFile.absoluteFilePath(), \
newFile.isDir() && file.isDir()); \
- waitCond.wait(&mutex); \
+ waitOnCond(); \
\
- if (response == FileOperator::NONE) { \
+ PAUSE() \
+ else if (response == FileOperator::NONE) { \
emit showInputFilenamePrompt(this, newFile, file.isDir()); \
- waitCond.wait(&mutex); \
+ waitOnCond(); \
if (newNameFromDialog.size()) { \
newFile.setFile(newNameFromDialog); \
} \
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 bool dirOverDir)
{
Dialog msgBox;
- msgBox.addButtonFirst(QDialogButtonBox::Cancel);
QAbstractButton *yesButton = msgBox.addButtonFirst(QDialogButtonBox::Yes);
QAbstractButton *yesToAllButton = msgBox.addButtonFirst(QDialogButtonBox::YesToAll);
QAbstractButton *noButton = msgBox.addButtonSecond(QDialogButtonBox::No);
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) {
manipulator->setResponse(OVERWRITE);
manipulator->newNameFromDialog = "";
QString text = file.fileName();
-
while (true) {
text = QInputDialog::getText(this, QString(), prompt + error, QLineEdit::Normal, text, &ok);
}
manipulator->mutex.unlock();
- manipulator->waitCond.wakeAll();
+ manipulator->wake();
}
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(setBarSize(FileManipulatorThread*, unsigned int)));
connect(thread, SIGNAL(updateProgress(FileManipulatorThread*, int)),
this, SLOT(updateProgress(FileManipulatorThread*, int)));
+ 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*)));
+ 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(1);
- progressBar->setValue(0);
- progressBar->setMinimum(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);
}
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;
}
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;
break;
}
+ PAUSE();
+ if (abort) break;
+
updateProgress(1);
}
if (!error) break;
+ PAUSE();
}
engine.close();
newEngine.close();
+ PAUSE();
if (abort || ignore) {
if (newFileWritten) {
newEngine.remove();
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);
+}
+
+
+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();
// (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);
}
#define FILEOPERATOR_H
#include <QWidget>
-#include <QProgressBar>
#include <QFileInfo>
#include <QThread>
#include <QMutex>
#include <QMap>
#include <QSet>
+#include "progressbar.h"
+
class FileManipulatorThread;
void remove(FileManipulatorThread* manipulator);
void setBarSize(FileManipulatorThread* manipulator, unsigned int size);
void updateProgress(FileManipulatorThread* manipulator, int value);
+ void showPaused(FileManipulatorThread* manipulator);
+
+ void togglePauseOperation(FileManipulatorThread* manipulator);
+ void abortOperation(FileManipulatorThread* manipulator);
protected:
void caterNewThread(FileManipulatorThread *thread);
void setText(int value);
- QProgressBar *progressBar;
+ void wake();
+
+ ProgressBar *progressBar;
- time_t startTime;
QMutex mutex;
- QWaitCondition waitCond;
// 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);
void updateProgress(int value);
void updateFile(const QString &name);
+ void waitOnCond();
+
+ QWaitCondition waitCond;
+
// files to process by the operation
const QFileInfoList files;
// destination for files - changes as the operation recurses into directories
// responses from the dialog prompts (error and overwrite)
FileOperator::Response response;
FileOperator::Response overwriteAll;
- // a flag to abort the operation
- bool abort;
// an array indicating whether to always ignore the error of index errno
bool ignoreAll[256];
QString fileName, 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;
void finished(FileManipulatorThread*);
void setBarSize(FileManipulatorThread*, unsigned int);
void updateProgress(FileManipulatorThread*, int);
+ void operationPaused(FileManipulatorThread*);
};