added filename and ETA information to progressbars
authorLukas Hrazky <lukkash@email.cz>
Sun, 18 Jul 2010 20:01:45 +0000 (22:01 +0200)
committerLukas Hrazky <lukkash@email.cz>
Sun, 18 Jul 2010 20:46:23 +0000 (22:46 +0200)
Signed-off-by: Lukas Hrazky <lukkash@email.cz>

src/fileoperator.cpp
src/fileoperator.h

index bbd3583..68ca019 100644 (file)
 #include <iostream>
 
 
-#define SHOW_ERROR_PROMPT(promptString)                                                 \
-    response = FileOperator::NONE;                                                      \
-    if (ignoreAll[errno]) {                                                             \
-        response = FileOperator::IGNORE;                                                \
-    } else {                                                                            \
-        char buf[255];                                                                  \
-        char *realBuf = strerror_r(errno, buf, 255);                                    \
-        emit showErrorPrompt(this, promptString + ". " + realBuf + ".", errno);         \
-        waitCond.wait(&mutex);                                                          \
+#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);                                        \
+        emit showErrorPrompt(this, promptString + " " + realBuf + ".", fileName, errno);    \
+        waitCond.wait(&mutex);                                                              \
     }
 
 
-#define ERROR_PROMPT(operation, promptString)                                           \
-{                                                                                       \
-    response = FileOperator::NONE;                                                      \
-    while (!abort && operation) {                                                       \
-        SHOW_ERROR_PROMPT(promptString)                                                 \
-        if (response == FileOperator::IGNORE) {                                         \
-            break;                                                                      \
-        }                                                                               \
-    }                                                                                   \
+#define ERROR_PROMPT(operation, promptString, fileName)                                     \
+{                                                                                           \
+    response = FileOperator::NONE;                                                          \
+    while (!abort && operation) {                                                           \
+        SHOW_ERROR_PROMPT(promptString, fileName)                                           \
+        if (response == FileOperator::IGNORE) {                                             \
+            break;                                                                          \
+        }                                                                                   \
+    }                                                                                       \
 }
 
 
-#define ERROR_PROMPT_XP(operation, promptString, onIgnore, quitCmd)                     \
-{                                                                                       \
-    ERROR_PROMPT(operation, promptString)                                               \
-    if (abort || response == FileOperator::IGNORE) {                                    \
-        if (!abort) onIgnore;                                                           \
-        quitCmd;                                                                        \
-    }                                                                                   \
+#define ERROR_PROMPT_XP(operation, promptString, fileName, onIgnore, quitCmd)               \
+{                                                                                           \
+    ERROR_PROMPT(operation, promptString, fileName)                                         \
+    if (abort || response == FileOperator::IGNORE) {                                        \
+        if (!abort) onIgnore;                                                               \
+        quitCmd;                                                                            \
+    }                                                                                       \
 }
 
 
-#define OVERWRITE_PROMPT(file, newFile)                                                 \
-{                                                                                       \
-    response = FileOperator::NONE;                                                      \
-                                                                                        \
-    if (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);                                                      \
-        }                                                                               \
-    }                                                                                   \
+#define OVERWRITE_PROMPT(file, newFile)                                                     \
+{                                                                                           \
+    response = FileOperator::NONE;                                                          \
+                                                                                            \
+    if (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);                                                          \
+        }                                                                                   \
+    }                                                                                       \
 }
 
 
@@ -87,11 +87,23 @@ 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) {
+        result.replace(0, homePath.size(), "~");
+    }
+
+    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(files[0].absoluteFilePath());
+        desc = tr("Are you sure you want to delete %1?")
+            .arg(FileOperator::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());
@@ -115,12 +127,13 @@ void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) {
     QString title, desc;
     if (files.size() == 1) {
         title = tr("Copy file");
-        desc = tr("Are you sure you want to copy %1 to %2?").arg(files[0].absoluteFilePath())
-            .arg(destination.absolutePath());
+        desc = tr("Are you sure you want to copy %1 to %2?")
+            .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
+            .arg(FileOperator::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(destination.absolutePath());
+            .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
     }
 
     int confirm = QMessageBox::warning(
@@ -144,12 +157,13 @@ void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
     QString title, desc;
     if (files.size() == 1) {
         title = tr("Move file");
-        desc = tr("Are you sure you want to move %1 to %2?").arg(files[0].absoluteFilePath())
-            .arg(destination.absolutePath());
+        desc = tr("Are you sure you want to move %1 to %2?")
+            .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
+            .arg(FileOperator::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(destination.absolutePath());
+            .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
     }
 
     int confirm = QMessageBox::warning(
@@ -166,14 +180,18 @@ void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
 }
 
 
-void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator, const QString &message, const int err) {
+void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
+    const QString &message,
+    const QString &fileName,
+    const int err)
+{
     QMessageBox msgBox;
     msgBox.addButton(QMessageBox::Cancel);
     QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
     QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
     QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
     QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole);
-    msgBox.setText(message);
+    msgBox.setText(message.arg(FileOperator::shortenPath(fileName)));
 
     msgBox.exec();
 
@@ -204,10 +222,11 @@ void FileOperator::showOverwritePrompt(
     QAbstractButton *askButton = 0;
 
     if (dirOverDir) {
-        msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?").arg(fileName));
+        msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
+            .arg(FileOperator::shortenPath(fileName)));
         askButton = msgBox.addButton(tr("Ask"), QMessageBox::AcceptRole);
     } else {
-        msgBox.setText(tr("File %1 already exists. Overwrite?").arg(fileName));
+        msgBox.setText(tr("File %1 already exists. Overwrite?").arg(FileOperator::shortenPath(fileName)));
     }
 
     msgBox.exec();
@@ -229,31 +248,32 @@ void FileOperator::showOverwritePrompt(
 
 
 void FileOperator::remove(FileManipulatorThread* manipulator) {
-    layout()->removeWidget(manipulator->widget);
+    manipulator->wait();
+    layout()->removeWidget(manipulator->progressBar);
     manipulatorList.removeAll(manipulator);
     delete manipulator;
 }
 
 
 void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
-    manipulator->widget->setMinimum(0);
-    manipulator->widget->setMaximum(size);
+    if (!manipulator->progressBar->maximum()) {
+        manipulator->startTime = time(0);
+    }
+    manipulator->progressBar->setMinimum(0);
+    manipulator->progressBar->setMaximum(size);
 }
 
 
 void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value) {
-    if (manipulator->widget->value() + value > manipulator->widget->maximum()) {
-        std::cout << "WARNING, EXCEEDING MAXIMUM BY " << value << std::endl;
-    }
-    manipulator->widget->setValue(manipulator->widget->value() + value);
+    manipulator->setText(value);
 }
 
 
 void FileOperator::caterNewThread(FileManipulatorThread *thread) {
     manipulatorList.append(thread);
 
-    connect(thread, SIGNAL(showErrorPrompt(FileManipulatorThread*, const QString&, const int)),
-        this, SLOT(showErrorPrompt(FileManipulatorThread*, const QString&, const int)));
+    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(finished(FileManipulatorThread*)),
@@ -263,27 +283,41 @@ void FileOperator::caterNewThread(FileManipulatorThread *thread) {
     connect(thread, SIGNAL(updateProgress(FileManipulatorThread*, int)),
         this, SLOT(updateProgress(FileManipulatorThread*, int)));
 
-    thread->widget->setValue(0);
+    thread->progressBar->setValue(0);
 
-    layout()->addWidget(thread->widget);
-    thread->start();
+    layout()->addWidget(thread->progressBar);
+    thread->start(QThread::LowestPriority);
 }
 
 
 FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) :
-    widget(new QProgressBar()),
+    progressBar(new QProgressBar()),
+    startTime(0),
     files(files),
     dest(dest),
     response(FileOperator::NONE),
     overwriteAll(FileOperator::NONE),
     abort(false),
+    lastTimeUpdate(0),
     barSize(0),
     barValue(0),
     fileSize(0),
     fileValue(0)
 {
     memset(ignoreAll, false, sizeof(ignoreAll));
-    widget->setStyle(new QPlastiqueStyle);
+    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() {
+    delete progressBar;
 }
 
 
@@ -351,10 +385,10 @@ bool FileManipulatorThread::remove(const QFileInfo &file, const bool ignoreDirNo
         if (!remove(list, ignoreDirNotEmpty)) return false;
 
         ERROR_PROMPT(!engine.rmdir(path, false),
-            tr("Error deleting directory %1.").arg(path))
+            tr("Error deleting directory %1."), path)
     } else {
         ERROR_PROMPT(!engine.remove(),
-            tr("Error deleting file %1.").arg(path))
+            tr("Error deleting file %1."), path)
     }
 
     if (abort || response == FileOperator::IGNORE) return false;
@@ -403,7 +437,7 @@ void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCo
 
             if (!newFile.exists()) {
                 ERROR_PROMPT_XP(!engine.mkdir(newPath, false),
-                    tr("Error creating directory %1.").arg(newPath),
+                    tr("Error creating directory %1."), newPath,
                     updateProgress(fileSizeMap[path]),
                     break)
             }
@@ -421,26 +455,26 @@ void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCo
             overwriteAll = tmpResp;
 
             ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
-                tr("Error setting permissions for directory %1.").arg(newPath))
+                tr("Error setting permissions for directory %1."), newPath)
 
             if (abort) return;
 
             dest = destBackup;
         } else {
             ERROR_PROMPT_XP(engine.isSequential(),
-                tr("Cannot copy sequential file %1.").arg(path),
+                tr("Cannot copy sequential file %1."), path,
                 updateProgress(fileSizeMap[path]),
                 break)
 
             if (newFile.exists() && newFile.isDir()) {
                 ERROR_PROMPT_XP(!remove(newPath),
-                    tr("Cannot replace directory %1 due to previous errors.").arg(newPath),
+                    tr("Cannot replace directory %1 due to previous errors."), newPath,
                     updateProgress(fileSizeMap[path]),
                     break)
             }
 
             ERROR_PROMPT_XP(!engine.open(QIODevice::ReadOnly),
-                tr("Error reading file %1.").arg(path),
+                tr("Error reading file %1."), path,
                 updateProgress(fileSizeMap[path]),
                 break)
 
@@ -449,7 +483,7 @@ void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCo
                 engine.seek(0);
 
                 ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
-                    tr("Error writing file %1.").arg(newPath))
+                    tr("Error writing file %1."), newPath)
 
                 if (abort || response == FileOperator::IGNORE) {
                     if (response == FileOperator::IGNORE) {
@@ -460,14 +494,14 @@ void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCo
                 }
 
                 bool error = false;
-                char block[4096];
+                char block[524288];
                 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.").arg(path));
+                            SHOW_ERROR_PROMPT(tr("Error while reading from file %1."), path);
                         } else {
-                            SHOW_ERROR_PROMPT(tr("Error while writing to file %1.").arg(newPath));
+                            SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
                         }
 
                         if (!abort) {
@@ -495,7 +529,7 @@ void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCo
                 newEngine.remove();
             } else {
                 ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
-                    tr("Error setting permissions for file %1.").arg(newPath))
+                    tr("Error setting permissions for file %1."), newPath)
             }
         }
 
@@ -533,7 +567,7 @@ unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files
         if (it->isDir()) {
             size += calculateFileSize(listDirFiles(it->absoluteFilePath()));
         } else {
-            size = ceil(static_cast<float>(it->size()) / 4096);
+            size = ceil(static_cast<float>(it->size()) / 524288);
         }
 
         res += size;
@@ -563,9 +597,42 @@ void FileManipulatorThread::updateProgress(int value) {
 }
 
 
-void FileManipulatorThread::updateFile(const QString &fileName) {
+void FileManipulatorThread::updateFile(const QString &name) {
     fileValue = 0;
-    emit updateFile(this, fileName);
+    fileName = FileOperator::shortenPath(name);
+    emit updateProgress(this, 0);
+}
+
+
+void FileManipulatorThread::setText(int value) {
+    if (progressBar->value() + value > progressBar->maximum()) {
+        std::cout << "WARNING, EXCEEDING 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->setFormat(barText.arg(fileName) + "\n%p%   ETA " + timeBuf);
+    progressBar->setValue(progressBar->value() + value);
+}
+
+
+DeleteThread::DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {
+    barText = tr("deleting %1");
 }
 
 
@@ -587,22 +654,29 @@ void DeleteThread::perform(const QFileInfo &file) {
     QString path = file.absoluteFilePath();
     QFSFileEngine engine(path);
 
+    updateFile(path);
+
     if (file.isDir()) {
         processFiles(listDirFiles(path));
 
         if (!listDirFiles(path).size()) {
             ERROR_PROMPT(!engine.rmdir(path, false),
-                tr("Error deleting directory %1.").arg(path))
+                tr("Error deleting directory %1."), path)
         }
     } else {
         ERROR_PROMPT(!engine.remove(),
-            tr("Error deleting file %1.").arg(path))
+            tr("Error deleting file %1."), path)
     }
 
     if (!abort) updateProgress(1);
 }
 
 
+CopyThread::CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
+    barText = tr("copying %1");
+}
+
+
 void CopyThread::run() {
     mutex.lock();
 
@@ -620,6 +694,11 @@ void CopyThread::perform(const QFileInfo &file) {
 }
 
 
+MoveThread::MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
+    barText = tr("moving %1");
+}
+
+
 void MoveThread::run() {
     mutex.lock();
 
@@ -678,7 +757,7 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
 
                 overwriteAll = tmpResp;
 
-                ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1.").arg(path))
+                ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
 
                 break;
             // source and target are nonmatching types(file and dir)
@@ -686,7 +765,7 @@ void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
             } else if (errno == ENOTDIR || errno == EISDIR) {
                 if (!remove(newPath)) break;
             } else {
-                SHOW_ERROR_PROMPT(tr("Error moving %1.").arg(path))
+                SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
 
                 if (response == FileOperator::IGNORE) {
                     break;
index 7672de1..9e2dcfb 100644 (file)
@@ -40,13 +40,21 @@ public:
 
     FileOperator(QWidget *parent = 0);
 
+    static QString shortenPath(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, const QString &message, const int err);
-    void showOverwritePrompt(FileManipulatorThread* manipulator, const QString &fileName, const bool dirOverDir);
+    void showErrorPrompt(FileManipulatorThread* manipulator,
+        const QString &message,
+        const QString &fileName,
+        const int err);
+    void showOverwritePrompt(FileManipulatorThread* manipulator,
+        const QString &fileName,
+        const bool dirOverDir);
+
     void remove(FileManipulatorThread* manipulator);
     void setBarSize(FileManipulatorThread* manipulator, unsigned int size);
     void updateProgress(FileManipulatorThread* manipulator, int value);
@@ -63,15 +71,20 @@ class FileManipulatorThread : public QThread {
 
 public:
     explicit FileManipulatorThread(const QFileInfoList files, QDir dest = QDir());
+    ~FileManipulatorThread();
     void setResponse(const FileOperator::Response response, const bool appyToAll = false, const int err = 0);
 
-    QProgressBar *widget;
+    void setText(int value);
+
+    QProgressBar *progressBar;
+
+    time_t startTime;
 
 protected:
     void processFiles(const QFileInfoList &files);
     virtual void perform(const QFileInfo &file) = 0;
 
-    bool remove(QString &filename, const bool ignoreDirNotEmpty = false);
+    bool remove(QString &fileName, const bool ignoreDirNotEmpty = false);
     bool remove(const QFileInfoList &files, const bool ignoreDirNotEmpty = false);
     bool remove(const QFileInfo &file, const bool ignoreDirNotEmpty = false);
 
@@ -84,7 +97,7 @@ protected:
 
     void setBarSize(unsigned int size);
     void updateProgress(int value);
-    void updateFile(const QString &fileName);
+    void updateFile(const QString &name);
 
     const QFileInfoList files;
     QDir dest;
@@ -99,15 +112,17 @@ protected:
     QMutex mutex;
     QWaitCondition waitCond;
 
+    QString fileName, barText;
+    time_t lastTimeUpdate;
+    char timeBuf[10];
     unsigned int barSize, barValue, fileSize, fileValue;
 
 signals:
-    void showErrorPrompt(FileManipulatorThread*, const QString&, const int);
+    void showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int);
     void showOverwritePrompt(FileManipulatorThread*, const QString&, const bool);
     void finished(FileManipulatorThread*);
     void setBarSize(FileManipulatorThread*, unsigned int);
     void updateProgress(FileManipulatorThread*, int);
-    void updateFile(FileManipulatorThread*, const QString&);
 };
 
 
@@ -115,7 +130,7 @@ class DeleteThread : public FileManipulatorThread {
     Q_OBJECT
 
 public:
-    explicit DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {}
+    explicit DeleteThread(const QFileInfoList &files);
 
 protected:
     void run();
@@ -127,7 +142,7 @@ class CopyThread : public FileManipulatorThread {
     Q_OBJECT
 
 public:
-    explicit CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {}
+    explicit CopyThread(const QFileInfoList &files, QDir &dest);
 
 protected:
     void run();
@@ -139,7 +154,7 @@ class MoveThread : public FileManipulatorThread {
     Q_OBJECT
 
 public:
-    explicit MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {}
+    explicit MoveThread(const QFileInfoList &files, QDir &dest);
 
 protected:
     void run();