added possibility to pause/abort the operations
authorLukas Hrazky <lukkash@email.cz>
Sun, 15 Aug 2010 10:47:57 +0000 (12:47 +0200)
committerLukas Hrazky <lukkash@email.cz>
Sun, 15 Aug 2010 10:47:57 +0000 (12:47 +0200)
Clicking the progressbar will pause the operation, long press will abort
it. Clicking the empty space (ie. canceling) of error and overwrite
dialogs will also pause the operation (postponing the decision, giving
the chance to fix things).

Signed-off-by: Lukas Hrazky <lukkash@email.cz>

case.pro
src/addressbar.cpp
src/dialog.cpp
src/fileoperator.cpp
src/fileoperator.h
src/progressbar.cpp [new file with mode: 0644]
src/progressbar.h [new file with mode: 0644]

index 2a3833a..95431d3 100644 (file)
--- a/case.pro
+++ b/case.pro
@@ -21,7 +21,8 @@ HEADERS += src/addressbar.h \
            src/filelist.h \
            src/fileoperator.h \
            src/pane.h \
-           src/dialog.h
+           src/dialog.h \
+           src/progressbar.h
 SOURCES += src/addressbar.cpp \
            src/button.cpp \
            src/case.cpp \
@@ -29,4 +30,5 @@ SOURCES += src/addressbar.cpp \
            src/fileoperator.cpp \
            src/main.cpp \
            src/pane.cpp \
-           src/dialog.cpp
+           src/dialog.cpp \
+           src/progressbar.cpp
index 0bc1038..d189cf2 100644 (file)
@@ -16,6 +16,7 @@
 
 
 #include "addressbar.h"
+
 #include "fileoperator.h"
 
 
index 8af4fba..483f493 100644 (file)
@@ -22,6 +22,7 @@
 
 Dialog::Dialog(QWidget *parent) :
     QDialog(parent),
+    clickedButton(0),
     text(new QLabel()),
     buttons1(new QDialogButtonBox(Qt::Vertical)),
     buttons2(new QDialogButtonBox(Qt::Vertical))
index 1dcdae8..8e6335a 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]) {                                                                 \
@@ -41,7 +48,7 @@
         char buf[255];                                                                      \
         char *realBuf = strerror_r(errno, buf, 255);                                        \
         emit showErrorPrompt(this, promptString + " " + realBuf + ".", fileName, errno);    \
-        waitCond.wait(&mutex);                                                              \
+        waitOnCond();                                                                       \
     }
 
 
@@ -53,6 +60,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);                                     \
                 }                                                                           \
@@ -221,7 +230,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);
@@ -230,7 +239,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);
@@ -248,7 +260,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);
@@ -269,7 +280,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);
@@ -307,7 +321,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);
 
@@ -325,7 +338,7 @@ void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
     }
 
     manipulator->mutex.unlock();
-    manipulator->waitCond.wakeAll();
+    manipulator->wake();
 }
 
 
@@ -338,9 +351,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);
 }
@@ -351,6 +361,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);
 
@@ -366,37 +409,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);
 }
 
 
@@ -434,14 +477,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);
     }
 }
 
@@ -455,6 +499,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;
@@ -490,6 +535,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;
 }
@@ -515,6 +561,7 @@ void FileManipulatorThread::copy(const QFileInfo &file) {
     QString newPath(newFile.absoluteFilePath());
     QFSFileEngine newEngine(newPath);
 
+    PAUSE();
     if (abort) return;
 
     if (file.isDir()) {
@@ -570,6 +617,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;
@@ -632,15 +680,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();
@@ -662,6 +715,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);
         }
@@ -712,11 +768,32 @@ void FileManipulatorThread::updateFile(const QString &name) {
 }
 
 
+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) {
@@ -735,7 +812,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);
 }
 
@@ -749,6 +829,7 @@ void DeleteThread::run() {
     mutex.lock();
 
     setBarSize(calculateFileSize(files, true));
+    startTime = time(0);
 
     processFiles(files);
 
@@ -771,6 +852,7 @@ void CopyThread::run() {
     mutex.lock();
 
     setBarSize(calculateFileSize(files, false, true));
+    startTime = time(0);
 
     processFiles(files);
 
@@ -801,6 +883,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();
@@ -816,6 +899,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);
@@ -823,6 +907,7 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
             }
         } else {
             if (response == FileOperator::KEEP) {
+                PAUSE();
                 if (abort) break;
                 updateProgress(1);
                 removeExcludeFiles.insert(path);
@@ -833,6 +918,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
@@ -853,14 +940,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;
@@ -879,8 +967,12 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
                     break;
                 }
             }
+            PAUSE();
         }
-            
+
+        if (done) break;
+
+        PAUSE();
         if (abort) break;
         updateProgress(1);
     }
index 76f5535..b73e45a 100644 (file)
@@ -19,7 +19,6 @@
 #define FILEOPERATOR_H
 
 #include <QWidget>
-#include <QProgressBar>
 #include <QFileInfo>
 #include <QThread>
 #include <QMutex>
@@ -28,6 +27,8 @@
 #include <QMap>
 #include <QSet>
 
+#include "progressbar.h"
+
 
 class FileManipulatorThread;
 
@@ -63,6 +64,10 @@ public slots:
     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);
@@ -81,13 +86,15 @@ public:
 
     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);
@@ -109,6 +116,10 @@ protected:
     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
@@ -117,8 +128,6 @@ protected:
     // 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];
 
@@ -135,6 +144,7 @@ protected:
     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;
@@ -146,6 +156,7 @@ signals:
     void finished(FileManipulatorThread*);
     void setBarSize(FileManipulatorThread*, unsigned int);
     void updateProgress(FileManipulatorThread*, int);
+    void operationPaused(FileManipulatorThread*);
 };
 
 
diff --git a/src/progressbar.cpp b/src/progressbar.cpp
new file mode 100644 (file)
index 0000000..29ad8da
--- /dev/null
@@ -0,0 +1,52 @@
+// 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 "progressbar.h"
+
+#include <QtGui>
+
+#include "fileoperator.h"
+
+#include <iostream>
+
+ProgressBar::ProgressBar(FileManipulatorThread *thread, QWidget *parent) :
+    QProgressBar(parent),
+    contextEvent(false),
+    thread(thread)
+{
+    setMaximum(1);
+    setValue(0);
+    setMinimum(0);
+    QFont barFont = font();
+    barFont.setPointSize(12);
+    setFont(barFont);
+    setMinimumHeight(44);
+    setStyle(new QPlastiqueStyle);
+    //progressBar->setStyle(new QMotifStyle);
+}
+
+
+void ProgressBar::mouseReleaseEvent(QMouseEvent *) {
+    if (!contextEvent) emit togglePauseOperation(thread);
+    contextEvent = false;
+}
+
+
+void ProgressBar::contextMenuEvent(QContextMenuEvent *) {
+    contextEvent = true;
+    emit abortOperation(thread);
+}
diff --git a/src/progressbar.h b/src/progressbar.h
new file mode 100644 (file)
index 0000000..90ba48b
--- /dev/null
@@ -0,0 +1,44 @@
+// 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/>.
+
+
+#ifndef PROGRESSBAR_H
+#define PROGRESSBAR_H
+
+#include <QProgressBar>
+
+
+class FileManipulatorThread;
+
+class ProgressBar : public QProgressBar {
+    Q_OBJECT;
+
+signals:
+    void abortOperation(FileManipulatorThread*);
+    void togglePauseOperation(FileManipulatorThread*);
+
+public:
+    explicit ProgressBar(FileManipulatorThread *thread, QWidget *parent = 0);
+
+protected:
+    void mouseReleaseEvent(QMouseEvent *);
+    void contextMenuEvent(QContextMenuEvent *);
+
+    bool contextEvent;
+    FileManipulatorThread *thread;
+};
+
+#endif // PROGRESSBAR_H