fixed error handling on sequential file check
[case] / src / fileoperator.cpp
index a779a87..422f90b 100644 (file)
 #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();                                                                       \
     }
 
 
@@ -53,6 +65,7 @@
         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);                                     \
                 }                                                                           \
@@ -106,9 +120,27 @@ FileOperator::FileOperator(QWidget *parent) : QWidget(parent) {
 
 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;
@@ -203,7 +235,7 @@ void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
     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);
@@ -212,7 +244,10 @@ void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
 
     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);
@@ -230,7 +265,6 @@ void FileOperator::showOverwritePrompt(
     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);
@@ -251,7 +285,10 @@ void FileOperator::showOverwritePrompt(
 
     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);
@@ -289,7 +326,6 @@ void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
     manipulator->newNameFromDialog = "";
     QString text = file.fileName();
 
-    
     while (true) {
         text = QInputDialog::getText(this, QString(), prompt + error, QLineEdit::Normal, text, &ok);
 
@@ -307,7 +343,7 @@ void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
     }
 
     manipulator->mutex.unlock();
-    manipulator->waitCond.wakeAll();
+    manipulator->wake();
 }
 
 
@@ -320,9 +356,6 @@ void FileOperator::remove(FileManipulatorThread* manipulator) {
 
 
 void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
-    if (!manipulator->progressBar->maximum()) {
-        manipulator->startTime = time(0);
-    }
     manipulator->progressBar->setMinimum(0);
     manipulator->progressBar->setMaximum(size);
 }
@@ -333,6 +366,39 @@ void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value)
 }
 
 
+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);
 
@@ -348,37 +414,37 @@ void FileOperator::caterNewThread(FileManipulatorThread *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);
 }
 
 
@@ -416,14 +482,15 @@ void FileManipulatorThread::setResponse(
     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);
     }
 }
 
@@ -437,6 +504,7 @@ bool FileManipulatorThread::remove(const QFileInfoList &files, const bool doUpda
     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;
@@ -472,6 +540,7 @@ bool FileManipulatorThread::remove(const QFileInfo &file, const bool doUpdates)
 
     if (!abort && doUpdates) updateProgress(1);
 
+    PAUSE();
     if (abort || response == FileOperator::IGNORE) return false;
     return true;
 }
@@ -497,6 +566,7 @@ void FileManipulatorThread::copy(const QFileInfo &file) {
     QString newPath(newFile.absoluteFilePath());
     QFSFileEngine newEngine(newPath);
 
+    PAUSE();
     if (abort) return;
 
     if (file.isDir()) {
@@ -552,6 +622,7 @@ void FileManipulatorThread::copy(const QFileInfo &file) {
         ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
             tr("Error setting permissions for directory %1."), newPath)
 
+        PAUSE();
         if (abort) return;
 
         dest = destBackup;
@@ -562,7 +633,7 @@ void FileManipulatorThread::copy(const QFileInfo &file) {
             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),
@@ -614,15 +685,20 @@ void FileManipulatorThread::copy(const QFileInfo &file) {
                     break;
                 }
 
+                PAUSE();
+                if (abort) break;
+
                 updateProgress(1);
             }
 
             if (!error) break;
+            PAUSE();
         }
 
         engine.close();
         newEngine.close();
 
+        PAUSE();
         if (abort || ignore) {
             if (newFileWritten) {
                 newEngine.remove();
@@ -644,6 +720,9 @@ unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files
     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);
         }
@@ -694,11 +773,43 @@ void FileManipulatorThread::updateFile(const QString &name) {
 }
 
 
+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) {
@@ -717,7 +828,10 @@ void FileManipulatorThread::setText(int value) {
         }
     }
 
-    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);
 }
 
@@ -731,6 +845,7 @@ void DeleteThread::run() {
     mutex.lock();
 
     setBarSize(calculateFileSize(files, true));
+    startTime = time(0);
 
     processFiles(files);
 
@@ -753,6 +868,7 @@ void CopyThread::run() {
     mutex.lock();
 
     setBarSize(calculateFileSize(files, false, true));
+    startTime = time(0);
 
     processFiles(files);
 
@@ -783,6 +899,7 @@ void MoveThread::run() {
 
 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();
@@ -798,6 +915,7 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
         // (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);
@@ -805,6 +923,7 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
             }
         } else {
             if (response == FileOperator::KEEP) {
+                PAUSE();
                 if (abort) break;
                 updateProgress(1);
                 removeExcludeFiles.insert(path);
@@ -815,6 +934,8 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
         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
@@ -835,14 +956,15 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
 
                 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;
@@ -861,8 +983,12 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
                     break;
                 }
             }
+            PAUSE();
         }
-            
+
+        if (done) break;
+
+        PAUSE();
         if (abort) break;
         updateProgress(1);
     }