code cleanup
authorLukas Hrazky <lukkash@email.cz>
Sat, 21 Aug 2010 20:54:08 +0000 (22:54 +0200)
committerLukas Hrazky <lukkash@email.cz>
Sun, 22 Aug 2010 13:37:47 +0000 (15:37 +0200)
Signed-off-by: Lukas Hrazky <lukkash@email.cz>

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

index fe18116..5655ede 100644 (file)
--- a/case.pro
+++ b/case.pro
@@ -20,6 +20,7 @@ HEADERS += src/addressbar.h \
            src/case.h \
            src/filelist.h \
            src/fileoperator.h \
+           src/operationthread.h \
            src/pane.h \
            src/dialog.h \
            src/progressbar.h \
@@ -30,6 +31,7 @@ SOURCES += src/addressbar.cpp \
            src/case.cpp \
            src/filelist.cpp \
            src/fileoperator.cpp \
+           src/operationthread.cpp \
            src/main.cpp \
            src/pane.cpp \
            src/dialog.cpp \
index d189cf2..54db44b 100644 (file)
@@ -17,7 +17,7 @@
 
 #include "addressbar.h"
 
-#include "fileoperator.h"
+#include "utils.h"
 
 
 AddressBar::AddressBar(QWidget *parent) : QLineEdit(parent) {
@@ -29,7 +29,7 @@ AddressBar::AddressBar(QWidget *parent) : QLineEdit(parent) {
 
 
 void AddressBar::setText(const QString &text) {
-    QLineEdit::setText(FileOperator::shortenPath(text));
+    QLineEdit::setText(shortenPath(text));
 }
 
 
index a4927b8..fe281b8 100644 (file)
@@ -24,7 +24,7 @@
 #include <hildon-mime.h>
 #include <dbus/dbus.h>
 
-#include "fileoperator.h"
+#include "utils.h"
 
 
 FileList::FileList(QWidget *parent) :
@@ -62,7 +62,7 @@ const QString FileList::path() const {
 
 
 bool FileList::changePath(QString path) {
-    path = FileOperator::unwindPath(path);
+    path = unwindPath(path);
     QDir dir(fileSystemModel->rootPath());
     if (dir.cd(path)) {
         setRootIndex(fileSystemModel->setRootPath(dir.absolutePath()));
index bd4c667..b25ab3b 100644 (file)
 #include "fileoperator.h"
 
 #include <QtGui>
-#include <QDir>
 #include <QMessageBox>
 #include <QHBoxLayout>
-#include <QChar>
 
 #include "dialog.h"
 #include "utils.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 = 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);    \
-        waitOnCond();                                                                       \
-    }
-
-
-#define ERROR_PROMPT(operation, promptString, fileName)                                     \
-{                                                                                           \
-    response = FileOperator::NONE;                                                          \
-    while (!abort && operation) {                                                           \
-        SHOW_ERROR_PROMPT(promptString, fileName)                                           \
-        if (response == FileOperator::IGNORE) {                                             \
-            break;                                                                          \
-        }                                                                                   \
-        PAUSE()                                                                             \
-    }                                                                                       \
-}
-
-
-#define SPECIAL_COPY_ERROR_PROMPT(operation, promptString, fileName)                        \
-{                                                                                           \
-    ERROR_PROMPT(operation, promptString, fileName)                                         \
-    if (abort || response == FileOperator::IGNORE) {                                        \
-        if (!abort) {                                                                       \
-            updateProgress(fileSizeMap[path]);                                              \
-            removeExcludeFiles.insert(path);                                                \
-        }                                                                                   \
-        return;                                                                             \
-    }                                                                                       \
-}
-
-
-#define OVERWRITE_PROMPT(file, newFile)                                                     \
-{                                                                                           \
-    response = FileOperator::NONE;                                                          \
-                                                                                            \
-    while (!abort && response == FileOperator::NONE && newFile.exists()) {                  \
-        if (overwriteAll != FileOperator::NONE) {                                           \
-            response = overwriteAll;                                                        \
-        } else {                                                                            \
-            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;                       \
-}
-
 
 FileOperator::FileOperator(QWidget *parent) : QWidget(parent) {
     QHBoxLayout *layout = new QHBoxLayout;
     layout->setContentsMargins(0, 0, 0, 0);
     layout->setSpacing(1);
     setLayout(layout);
+
     qRegisterMetaType<QFileInfo>("QFileInfo");
+    qRegisterMetaType<time_t>("time_t");
+
     loadOperationIcons(palette(), "delete_small", deleteIcon, inverseDeleteIcon);
     loadOperationIcons(palette(), "copy_small", copyIcon, inverseCopyIcon);
     loadOperationIcons(palette(), "move_small", moveIcon, inverseMoveIcon);
 }
 
 
-QString FileOperator::shortenPath(const QString &path) {
-    QString homePath = QFSFileEngine::homePath();
-
-    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;
-}
-
-
 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(FileOperator::shortenPath(files[0].absoluteFilePath()));
+            .arg(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());
@@ -171,9 +60,8 @@ void FileOperator::deleteFiles(const QFileInfoList &files) {
     );
 
     if(confirm == QMessageBox::Yes) {
-        DeleteThread *t = new DeleteThread(files);
-        t->progressBar->setIcons(deleteIcon, inverseDeleteIcon);
-        caterNewThread(t);
+        ProgressBar *bar = new ProgressBar(deleteIcon, inverseDeleteIcon);
+        initOperation(new DeleteThread(files), bar);
     }
 }
 
@@ -183,12 +71,12 @@ void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) {
     if (files.size() == 1) {
         title = tr("Copy file");
         desc = tr("Are you sure you want to copy %1 to %2?")
-            .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
-            .arg(FileOperator::shortenPath(destination.absolutePath()));
+            .arg(shortenPath(files[0].absoluteFilePath()))
+            .arg(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(FileOperator::shortenPath(destination.absolutePath()));
+            .arg(files.size()).arg(shortenPath(destination.absolutePath()));
     }
 
     int confirm = QMessageBox::warning(
@@ -200,11 +88,9 @@ void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) {
     );
 
     if(confirm == QMessageBox::Yes) {
-        CopyThread *t = new CopyThread(files, destination);
-        t->progressBar->setIcons(copyIcon, inverseCopyIcon);
-        t->progressBar->fromText = shortenPath(files[0].absolutePath());
-        t->progressBar->toText = FileOperator::shortenPath(destination.absolutePath());
-        caterNewThread(t);
+        ProgressBar *bar = new ProgressBar(copyIcon, inverseCopyIcon);
+        bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
+        initOperation(new CopyThread(files, destination), bar);
     }
 }
 
@@ -217,12 +103,12 @@ void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
     if (files.size() == 1) {
         title = tr("Move file");
         desc = tr("Are you sure you want to move %1 to %2?")
-            .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
-            .arg(FileOperator::shortenPath(destination.absolutePath()));
+            .arg(shortenPath(files[0].absoluteFilePath()))
+            .arg(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(FileOperator::shortenPath(destination.absolutePath()));
+            .arg(files.size()).arg(shortenPath(destination.absolutePath()));
     }
 
     int confirm = QMessageBox::warning(
@@ -234,16 +120,14 @@ void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
     );
 
     if(confirm == QMessageBox::Yes) {
-        MoveThread *t = new MoveThread(files, destination);
-        t->progressBar->setIcons(moveIcon, inverseMoveIcon);
-        t->progressBar->fromText = shortenPath(files[0].absolutePath());
-        t->progressBar->toText = shortenPath(destination.absolutePath());
-        caterNewThread(t);
+        ProgressBar *bar = new ProgressBar(moveIcon, inverseMoveIcon);
+        bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
+        initOperation(new MoveThread(files, destination), bar);
     }
 }
 
 
-void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
+void FileOperator::showErrorPrompt(OperationThread* op,
     const QString &message,
     const QString &fileName,
     const int err)
@@ -254,27 +138,27 @@ void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
     QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
     QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
     QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole);
-    msgBox.setText(message.arg(FileOperator::shortenPath(fileName)));
+    msgBox.setText(message.arg(shortenPath(fileName)));
 
     msgBox.exec();
 
     if (msgBox.clickedButton() == cancelButton) {
-        manipulator->pause = true;
-        manipulator->setResponse(RETRY);
+        op->pause = true;
+        op->setResponse(OperationThread::RETRY);
     } else if (msgBox.clickedButton() == abortButton) {
-        manipulator->setResponse(ABORT);
+        op->setResponse(OperationThread::ABORT);
     } else if (msgBox.clickedButton() == retryButton) {
-        manipulator->setResponse(RETRY);
+        op->setResponse(OperationThread::RETRY);
     } else if (msgBox.clickedButton() == ignoreButton) {
-        manipulator->setResponse(IGNORE);
+        op->setResponse(OperationThread::IGNORE);
     } else if (msgBox.clickedButton() == ignoreAllButton) {
-        manipulator->setResponse(IGNORE, true, err);
+        op->setResponse(OperationThread::IGNORE, true, err);
     }
 }
 
 
 void FileOperator::showOverwritePrompt(
-    FileManipulatorThread* manipulator,
+    OperationThread* op,
     const QString &fileName,
     const bool dirOverDir)
 {
@@ -290,39 +174,39 @@ void FileOperator::showOverwritePrompt(
 
     if (dirOverDir) {
         msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
-            .arg(FileOperator::shortenPath(fileName)));
+            .arg(shortenPath(fileName)));
         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.setText(tr("File %1 already exists. Overwrite?").arg(shortenPath(fileName)));
     }
 
     msgBox.exec();
 
     if (msgBox.clickedButton == 0) {
-        manipulator->pause = true;
-        manipulator->setResponse(NONE);
+        op->pause = true;
+        op->setResponse(OperationThread::NONE);
     } else if (msgBox.clickedButton == abortButton) {
-        manipulator->setResponse(ABORT);
+        op->setResponse(OperationThread::ABORT);
     } else if (msgBox.clickedButton == yesButton) {
-        manipulator->setResponse(OVERWRITE);
+        op->setResponse(OperationThread::OVERWRITE);
     } else if (msgBox.clickedButton == yesToAllButton) {
-        manipulator->setResponse(OVERWRITE, true);
+        op->setResponse(OperationThread::OVERWRITE, true);
     } else if (msgBox.clickedButton == noButton) {
-        manipulator->setResponse(KEEP);
+        op->setResponse(OperationThread::KEEP);
     } else if (msgBox.clickedButton == noToAllButton) {
-        manipulator->setResponse(KEEP, true);
+        op->setResponse(OperationThread::KEEP, true);
     } else if (msgBox.clickedButton == askButton) {
-        manipulator->setResponse(ASK);
+        op->setResponse(OperationThread::ASK);
     } else if (msgBox.clickedButton == newNameButton) {
-        manipulator->setResponse(NONE);
+        op->setResponse(OperationThread::NONE);
     } else if (msgBox.clickedButton == skipDirButton) {
-        manipulator->setResponse(SKIP_DIR);
+        op->setResponse(OperationThread::SKIP_DIR);
     }
 }
 
 
-void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
+void FileOperator::showInputFilenamePrompt(OperationThread* op,
     const QFileInfo &file,
     const bool dir)
 {
@@ -335,9 +219,7 @@ void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
         prompt = tr("Enter the new file name.");
     }
 
-    manipulator->mutex.lock();
-
-    manipulator->newNameFromDialog = "";
+    op->newNameFromDialog = "";
     QString text = file.fileName();
 
     while (true) {
@@ -347,65 +229,44 @@ void FileOperator::showInputFilenamePrompt(FileManipulatorThread* manipulator,
 
         error = "";
         if (text.contains(QRegExp("[\"*/:<>?\\\\|]"))) {
-            error = "<small><br/><font color = 'red'>" + tr("The name cannot contain any of the following characters: ") +
+            error = "<small><br/><font color = 'red'>" +
+                tr("The name cannot contain any of the following characters: ") +
                 "\"*/:&lt;&gt;?\\|</font></small>";
         } else if (ok && !text.isEmpty()) {
             QFileInfo info(file.path() + "/" + text);
-            manipulator->newNameFromDialog = info.absoluteFilePath();
+            op->newNameFromDialog = info.absoluteFilePath();
             break;
         }
     }
 
-    manipulator->mutex.unlock();
-    manipulator->wake();
-}
-
-
-void FileOperator::remove(FileManipulatorThread* manipulator) {
-    manipulator->wait();
-    layout()->removeWidget(manipulator->progressBar);
-    manipulatorList.removeAll(manipulator);
-    delete manipulator;
+    op->wake();
 }
 
 
-void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
-    manipulator->progressBar->setMinimum(0);
-    manipulator->progressBar->setMaximum(size);
+void FileOperator::remove(OperationThread* op) {
+    op->wait();
+    ProgressBar *bar = get(op);
+    layout()->removeWidget(bar);
+    opList.removeAll(qMakePair(op, bar));
+    delete op;
+    delete bar;
 }
 
 
-void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value) {
-    manipulator->setText(value);
-}
-
-
-void FileOperator::updateMainText(FileManipulatorThread* manipulator, const QString &text) {
-    manipulator->progressBar->mainText = text;
-    manipulator->progressBar->mainText.remove(0, manipulator->progressBar->fromText.size() + 1);
-    manipulator->progressBar->repaint();
-}
-
+void FileOperator::togglePauseOperation(ProgressBar* bar) {
+    OperationThread *op = get(bar);
 
-void FileOperator::showPaused(FileManipulatorThread* manipulator) {
-    manipulator->progressBar->paused = true;
-    manipulator->progressBar->repaint();
-}
-
-
-void FileOperator::togglePauseOperation(FileManipulatorThread* manipulator) {
-    if (manipulator->pause) {
-        manipulator->pause = false;
-        manipulator->progressBar->paused = false;
-        manipulator->progressBar->repaint();
-        manipulator->wake();
+    if (op->pause) {
+        op->wake();
     } else {
-        manipulator->pause = true;
+        op->pause = true;
     }
 }
 
 
-void FileOperator::abortOperation(FileManipulatorThread* manipulator) {
+void FileOperator::abortOperation(ProgressBar* bar) {
+    OperationThread *op = get(bar);
+
     int confirm = QMessageBox::warning(
         0,
         tr("Abort operation"),
@@ -415,598 +276,52 @@ void FileOperator::abortOperation(FileManipulatorThread* manipulator) {
     );
 
     if(confirm == QMessageBox::Yes) {
-        manipulator->abort = true;
-        manipulator->pause = false;
-        manipulator->wake();
-    }
-}
-
-
-void FileOperator::caterNewThread(FileManipulatorThread *thread) {
-    manipulatorList.append(thread);
-
-    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(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(updateFileName(FileManipulatorThread*, QString)),
-        this, SLOT(updateMainText(FileManipulatorThread*, QString)));
-    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*)));
-
-    layout()->addWidget(thread->progressBar);
-    thread->start(QThread::LowestPriority);
-}
-
-
-FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) :
-    progressBar(new ProgressBar(this)),
-    abort(false),
-    pause(false),
-    files(files),
-    dest(dest),
-    response(FileOperator::NONE),
-    overwriteAll(FileOperator::NONE),
-    lastTimeUpdate(0),
-    startTime(0),
-    waitTime(0),
-    barSize(0),
-    barValue(0),
-    fileSize(0),
-    fileValue(0)
-{
-    memset(ignoreAll, false, sizeof(ignoreAll));
-}
-
-
-FileManipulatorThread::~FileManipulatorThread() {
-    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;
-    }
-    delete progressBar;
-}
-
-
-void FileManipulatorThread::setResponse(
-    const FileOperator::Response response,
-    const bool applyToAll,
-    const int err)
-{
-    mutex.lock();
-
-    this->response = response;
-
-    if (applyToAll) {
-        if (response == FileOperator::KEEP
-            || response == FileOperator::OVERWRITE
-            || response == FileOperator::NONE)
-        {
-            overwriteAll = response;
-        }
-
-        if (response == FileOperator::IGNORE) {
-            ignoreAll[err] = true;
-        }
-    }
-
-    if (response == FileOperator::ABORT) abort = true;
-
-    mutex.unlock();
-    wake();
-}
-
-
-void FileManipulatorThread::processFiles(const QFileInfoList &files) {
-    for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
-        PAUSE();
-        if (abort) break;
-        perform(*it);
-    }
-}
-
-
-bool FileManipulatorThread::remove(QString &fileName, const bool doUpdates) {
-    return remove(QFileInfo(fileName), doUpdates);
-}
-
-
-bool FileManipulatorThread::remove(const QFileInfoList &files, const bool doUpdates) {
-    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;
-}
-
-
-bool FileManipulatorThread::remove(const QFileInfo &file, const bool doUpdates) {
-    std::cout << "DELETING " << file.absoluteFilePath().toStdString() << std::endl;
-
-    QString path = file.absoluteFilePath();
-
-    if (removeExcludeFiles.contains(path)) {
-        if (doUpdates) updateProgress(1);
-        return false;
-    }
-
-    QFSFileEngine engine(path);
-
-    if (doUpdates) updateFile(path);
-
-    if (file.isDir()) {
-        if (!remove(listDirFiles(path), doUpdates)) {
-            if (doUpdates) updateProgress(1);
-            return false;
-        }
-
-        if (!listDirFiles(path).size()) {
-            ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
-        }
-    } else {
-        ERROR_PROMPT(!engine.remove(), tr("Error deleting file %1."), path)
-    }
-
-    if (!abort && doUpdates) updateProgress(1);
-
-    PAUSE();
-    if (abort || response == FileOperator::IGNORE) return false;
-    return true;
-}
-
-
-void FileManipulatorThread::copy(const QFileInfo &file) {
-    std::cout << "COPYING " << file.absoluteFilePath().toStdString()
-        << " to " << dest.absolutePath().toStdString() << std::endl;
-
-    QString path(file.absoluteFilePath());
-    QFSFileEngine engine(path);
-    QFileInfo newFile(dest.absolutePath() + "/" + file.fileName());
-
-    updateFile(path);
-
-    // hack to prevent asking about the same file if we already asked in the rename(...) function
-    if (overwriteAll == FileOperator::DONT_ASK_ONCE) {
-        overwriteAll = FileOperator::NONE;
-    } else {
-        OVERWRITE_PROMPT(file, newFile)
-    }
-
-    QString newPath(newFile.absoluteFilePath());
-    QFSFileEngine newEngine(newPath);
-
-    PAUSE();
-    if (abort) return;
-
-    if (file.isDir()) {
-        // save the overwrite response, because the response variable will get ovewritten in remove(...)
-        FileOperator::Response overwriteResponse = response;
-
-        if (newFile.exists() && !newFile.isDir()) {
-            // overwriting a file, so check for KEEP and handle it
-            if (response == FileOperator::KEEP) {
-                updateProgress(fileSizeMap[path]);
-                removeExcludeFiles.insert(path);
-                return;
-            }
-
-            // if it should not be kept, remove it and return on failure
-            if(!remove(newPath)) {
-                updateProgress(fileSizeMap[path]);
-                return;
-            }
-            // create new info since we deleted the file - is it needed?
-            newFile = QFileInfo(newPath);
-        } else {
-            // overwriting a directory - response KEEP means to keep the files inside,
-            // SKIP_DIR means to skip the dir completely
-            if (response == FileOperator::SKIP_DIR) {
-                updateProgress(fileSizeMap[path]);
-                removeExcludeFiles.insert(path);
-                return;
-            }
-        }
-
-        if (!newFile.exists()) {
-            SPECIAL_COPY_ERROR_PROMPT(!engine.mkdir(newPath, false),
-                tr("Error creating directory %1."), newPath)
-        }
-
-        // we've done the job with the dir, so update progress and recurse into the dir
-        updateProgress(1);
-        
-        // change the dest for the recursion
-        QDir destBackup = dest;
-        dest = newPath;
-
-        // and set overwriteAll to the response we got a while ago
-        // because it applies to the files inside the dir
-        FileOperator::Response tmpResp = overwriteAll;
-        overwriteAll = overwriteResponse;
-
-        processFiles(listDirFiles(path));
-
-        overwriteAll = tmpResp;
-
-        ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
-            tr("Error setting permissions for directory %1."), newPath)
-
-        PAUSE();
-        if (abort) return;
-
-        dest = destBackup;
-    } else {
-        if (response == FileOperator::KEEP) {
-            updateProgress(fileSizeMap[path]);
-            removeExcludeFiles.insert(path);
-            return;
-        }
-
-        SPECIAL_COPY_ERROR_PROMPT(checkSequentialFile(engine), tr("Cannot copy file %1."), path)
-
-        if (newFile.exists() && newFile.isDir()) {
-            SPECIAL_COPY_ERROR_PROMPT(!remove(newPath),
-                tr("Cannot replace directory %1 due to previous errors."), newPath)
-        }
-
-        SPECIAL_COPY_ERROR_PROMPT(!engine.open(QIODevice::ReadOnly), tr("Error reading file %1."), path)
-
-        bool ignore = false, newFileWritten = false;
-        while (!abort && !ignore) {
-            engine.seek(0);
-            fileValue = 0;
-
-            ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
-                tr("Error writing file %1."), newPath)
-
-            if (abort || response == FileOperator::IGNORE) {
-                if (response == FileOperator::IGNORE) {
-                    updateProgress(fileSizeMap[path]);
-                    removeExcludeFiles.insert(path);
-                    ignore = true;
-                }
-                break;
-            }
-
-            newFileWritten = true;
-
-            bool error = false;
-            char block[BLOCK_SIZE];
-            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."), path);
-                    } else {
-                        SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
-                    }
-
-                    if (!abort) {
-                        if (response == FileOperator::IGNORE) {
-                            updateProgress(fileSizeMap[path] - fileValue);
-                            removeExcludeFiles.insert(path);
-                            ignore = true;
-                        } else {
-                            updateProgress(-fileValue);
-                        }
-                    }
-                    error = true;
-                    break;
-                }
-
-                PAUSE();
-                if (abort) break;
-
-                updateProgress(1);
-            }
-
-            if (!error) break;
-            PAUSE();
-        }
-
-        engine.close();
-        newEngine.close();
-
-        PAUSE();
-        if (abort || ignore) {
-            if (newFileWritten) {
-                newEngine.remove();
-            }
-        } else {
-            ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
-                tr("Error setting permissions for file %1."), newPath)
-        }
-    }
-}
-
-
-unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files,
-    const bool count,
-    const bool addSize)
-{
-    unsigned int res = 0;
-
-    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);
-        }
-
-        if (addSize) {
-            if (it->isDir()) {
-                ++size;
-            } else {
-                size += ceil(static_cast<float>(it->size()) / BLOCK_SIZE);
-            }
-            fileSizeMap[it->absoluteFilePath()] = size;
-        }
-
-        if (count) {
-            ++size;
-        }
-
-        res += size;
+        op->abort = true;
+        op->pause = false;
+        op->wake();
     }
-
-    return res;
-}
-
-
-QFileInfoList FileManipulatorThread::listDirFiles(const QString &dirPath) {
-    QDir dir = dirPath;
-    return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden);
 }
 
 
-void FileManipulatorThread::setBarSize(unsigned int size) {
-    barSize = size;
-    emit setBarSize(this, size);
-}
+void FileOperator::initOperation(OperationThread *thread, ProgressBar *bar) {
+    opList.append(qMakePair(thread, bar));
 
+    connect(thread, SIGNAL(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)),
+        this, SLOT(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)));
+    connect(thread, SIGNAL(showOverwritePrompt(OperationThread*, const QString&, bool)),
+        this, SLOT(showOverwritePrompt(OperationThread*, const QString&, bool)));
+    connect(thread, SIGNAL(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)),
+        this, SLOT(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)));
+    connect(thread, SIGNAL(finished(OperationThread*)),
+        this, SLOT(remove(OperationThread*)));
 
-void FileManipulatorThread::updateProgress(int value) {
-    barValue += value;
-    fileValue += value;
-    emit updateProgress(this, value);
-}
-
-
-void FileManipulatorThread::updateFile(const QString &name) {
-    fileValue = 0;
-    emit updateFileName(this, FileOperator::shortenPath(name));
-}
+    connect(thread, SIGNAL(totalSizeChanged(int)), bar, SLOT(setMaximum(int)));
+    connect(thread, SIGNAL(progressUpdate(int)), bar, SLOT(updateProgress(int)));
+    connect(thread, SIGNAL(fileNameUpdated(QString)), bar, SLOT(updateMainText(QString)));
+    connect(thread, SIGNAL(operationStarted(time_t)), bar, SLOT(setStartTime(time_t)));
+    connect(thread, SIGNAL(operationPaused()), bar, SLOT(pause()));
+    connect(thread, SIGNAL(operationResumed(time_t)), bar, SLOT(resume(time_t)));
+    connect(thread, SIGNAL(removeAfterCopy()), bar, SLOT(showRemoveNotice()));
 
+    connect(bar, SIGNAL(togglePauseOperation(ProgressBar*)), this, SLOT(togglePauseOperation(ProgressBar*)));
+    connect(bar, SIGNAL(abortOperation(ProgressBar*)), this, SLOT(abortOperation(ProgressBar*)));
 
-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();
+    layout()->addWidget(bar);
+    thread->start(QThread::LowestPriority);
 }
 
 
-void FileManipulatorThread::setText(int value) {
-    if (progressBar->value() + value > progressBar->maximum()) {
-        std::cout << "WARNING: exceeding progressbar maximum (" << progressBar->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 *FileOperator::get(OperationThread *op) const {
+    for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
+        if (it->first == op) return it->second;
     }
-
-
-    progressBar->setFormat(QString("%p%  ") + timeBuf);
-    progressBar->setValue(progressBar->value() + value);
-}
-
-
-DeleteThread::DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {
-    barText = tr("deleting %1");
-}
-
-
-void DeleteThread::run() {
-    mutex.lock();
-
-    setBarSize(calculateFileSize(files, true));
-    startTime = time(0);
-
-    processFiles(files);
-
-    sleep(0.5);
-    emit finished(this);
-}
-
-
-void DeleteThread::perform(const QFileInfo &file) {
-    remove(file, true);
-}
-
-
-CopyThread::CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
-    barText = tr("copying %1");
-}
-
-
-void CopyThread::run() {
-    mutex.lock();
-
-    setBarSize(calculateFileSize(files, false, true));
-    startTime = time(0);
-
-    processFiles(files);
-
-    sleep(0.5);
-    emit finished(this);
-}
-
-
-void CopyThread::perform(const QFileInfo &file) {
-    copy(file);
-}
-
-
-MoveThread::MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
-    barText = tr("moving %1");
+    return 0;
 }
 
 
-void MoveThread::run() {
-    mutex.lock();
-
-    rename(files, dest);
-
-    sleep(0.5);
-    emit finished(this);
-}
-
-
-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);
-        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);
-                continue;
-            }
-        } else {
-            if (response == FileOperator::KEEP) {
-                PAUSE();
-                if (abort) break;
-                updateProgress(1);
-                removeExcludeFiles.insert(path);
-                continue;
-            }
-        }
-
-        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
-            // we calculate the actual file sizes, because from now on copy & remove takes over
-            if (errno == EXDEV) {
-                overwriteAll = response;
-                // hack: we already checked the first file we are sending to processFiles(...)
-                // so we don't want to ask about this one again
-                if (overwriteAll == FileOperator::NONE) overwriteAll = FileOperator::DONT_ASK_ONCE;
-
-                QFileInfoList remainingFiles = files.mid(i);
-
-                setBarSize(barValue + calculateFileSize(remainingFiles, true, true));
-
-                processFiles(remainingFiles);
-
-                barText = tr("deleting %1");
-
-                remove(remainingFiles, 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;
-
-                remove(files[i]);
-
-                break;
-            // source and target are nonmatching types(file and dir)
-            // remove the target and let it loop once again
-            } else if (errno == ENOTDIR || errno == EISDIR) {
-                if (!remove(newPath)) break;
-            } else {
-                SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
-
-                if (response == FileOperator::IGNORE) {
-                    break;
-                }
-            }
-            PAUSE();
-        }
-
-        if (done) break;
-
-        PAUSE();
-        if (abort) break;
-        updateProgress(1);
+OperationThread *FileOperator::get(ProgressBar *bar) const {
+    for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
+        if (it->second == bar) return it->first;
     }
-}
-
-
-void MoveThread::perform(const QFileInfo &file) {
-    copy(file);
+    return 0;
 }
index 076250d..c0a47ee 100644 (file)
 #define FILEOPERATOR_H
 
 #include <QWidget>
-#include <QFileInfo>
-#include <QThread>
-#include <QMutex>
-#include <QWaitCondition>
-#include <QDir>
-#include <QFSFileEngine>
-#include <QMap>
-#include <QSet>
 
 #include "progressbar.h"
+#include "operationthread.h"
 
 
-class FileManipulatorThread;
-
+typedef QList<QPair<OperationThread*, ProgressBar*> > OperationList;
 
 class FileOperator : public QWidget {
     Q_OBJECT
 
 public:
-    // DONT_ASK_ONCE is a hackish way to avoid asking twice to overwrite the same directory when moving
-    enum Response{NONE, ABORT, RETRY, IGNORE, KEEP, OVERWRITE, SKIP_DIR, ASK, DONT_ASK_ONCE};
-
     FileOperator(QWidget *parent = 0);
 
-    static QString shortenPath(const QString &path);
-    static QString unwindPath(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,
+    void showErrorPrompt(OperationThread* op,
         const QString &message,
         const QString &fileName,
         const int err);
-    void showOverwritePrompt(FileManipulatorThread* manipulator,
+    void showOverwritePrompt(OperationThread* op,
         const QString &fileName,
         const bool dirOverDir);
-    void showInputFilenamePrompt(FileManipulatorThread* manipulator,
+    void showInputFilenamePrompt(OperationThread* op,
         const QFileInfo &fileName,
         const bool dirOverDir);
 
-    void remove(FileManipulatorThread* manipulator);
-    void setBarSize(FileManipulatorThread* manipulator, unsigned int size);
-    void updateProgress(FileManipulatorThread* manipulator, int value);
-    void updateMainText(FileManipulatorThread* manipulator, const QString &text);
-    void showPaused(FileManipulatorThread* manipulator);
+    void remove(OperationThread* op);
 
-    void togglePauseOperation(FileManipulatorThread* manipulator);
-    void abortOperation(FileManipulatorThread* manipulator);
+    void togglePauseOperation(ProgressBar* bar);
+    void abortOperation(ProgressBar* bar);
 
 protected:
-    void caterNewThread(FileManipulatorThread *thread);
+    void initOperation(OperationThread *thread, ProgressBar *bar);
+    ProgressBar *get(OperationThread *op) const;
+    OperationThread *get(ProgressBar *bar) const;
 
-    QList<FileManipulatorThread*> manipulatorList;
+    OperationList opList;
     QPixmap deleteIcon, inverseDeleteIcon, copyIcon, inverseCopyIcon, moveIcon, inverseMoveIcon;
 };
 
-
-class FileManipulatorThread : public QThread {
-    Q_OBJECT
-
-public:
-    explicit FileManipulatorThread(const QFileInfoList files, QDir dest = QDir());
-    ~FileManipulatorThread();
-    void setResponse(const FileOperator::Response response, const bool appyToAll = false, const int err = 0);
-
-    void setText(int value);
-
-    void wake();
-
-    ProgressBar *progressBar;
-
-    QMutex mutex;
-    // 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);
-    virtual void perform(const QFileInfo &file) = 0;
-
-    bool remove(QString &fileName, const bool doUpdates = false);
-    bool remove(const QFileInfoList &files, const bool doUpdates = false);
-    bool remove(const QFileInfo &file, const bool doUpdates = false);
-
-    void copy(const QFileInfo &file);
-
-    unsigned int calculateFileSize(const QFileInfoList &files,
-        const bool count = false,
-        const bool addSize = false);
-
-    QFileInfoList listDirFiles(const QString &dirPath);
-
-    void setBarSize(unsigned int size);
-    void updateProgress(int value);
-    void updateFile(const QString &name);
-
-    void waitOnCond();
-
-    bool checkSequentialFile(const QFSFileEngine &engine);
-
-    QWaitCondition waitCond;
-
-    // files to process by the operation
-    const QFileInfoList files;
-    // destination for files - changes as the operation recurses into directories
-    QDir dest;
-
-    // responses from the dialog prompts (error and overwrite)
-    FileOperator::Response response;
-    FileOperator::Response overwriteAll;
-    // an array indicating whether to always ignore the error of index errno
-    bool ignoreAll[256];
-
-    // set of files that won't be deleted by the remove(...) functions
-    // used when move(...) would not overwrite target file to ensure the source file doesn't get deleted
-    QSet<QString> removeExcludeFiles;
-
-    // A map of file paths to their size. Not the actual size, but what is calculated for the
-    // purpose of the progressbar for the given operation. So either fileSize/BLOCK_SIZE or simply
-    // 1 for a file and file count for dirs (or both for copy&delete)
-    QMap<QString, qint64> fileSizeMap;
-
-    // the text of the progressBar (the format)
-    QString 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;
-
-signals:
-    void showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int);
-    void showOverwritePrompt(FileManipulatorThread*, const QString&, const bool);
-    void showInputFilenamePrompt(FileManipulatorThread*, const QFileInfo&, const bool);
-    void finished(FileManipulatorThread*);
-    void setBarSize(FileManipulatorThread*, unsigned int);
-    void updateProgress(FileManipulatorThread*, int);
-    void updateFileName(FileManipulatorThread*, QString);
-    void operationPaused(FileManipulatorThread*);
-};
-
-
-class DeleteThread : public FileManipulatorThread {
-    Q_OBJECT
-
-public:
-    explicit DeleteThread(const QFileInfoList &files);
-
-protected:
-    void run();
-    virtual void perform(const QFileInfo &file);
-};
-
-
-class CopyThread : public FileManipulatorThread {
-    Q_OBJECT
-
-public:
-    explicit CopyThread(const QFileInfoList &files, QDir &dest);
-
-protected:
-    void run();
-    virtual void perform(const QFileInfo &file);
-};
-
-
-class MoveThread : public FileManipulatorThread {
-    Q_OBJECT
-
-public:
-    explicit MoveThread(const QFileInfoList &files, QDir &dest);
-
-protected:
-    void run();
-    virtual void perform(const QFileInfo &file);
-    void rename(const QFileInfoList &files, const QDir &dest);
-};
-
-
 #endif // FILEOPERATOR_H
diff --git a/src/operationthread.cpp b/src/operationthread.cpp
new file mode 100644 (file)
index 0000000..d0cf7e1
--- /dev/null
@@ -0,0 +1,601 @@
+// 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 "operationthread.h"
+
+#include <errno.h>
+#include <math.h>
+
+#include "utils.h"
+
+
+#define BLOCK_SIZE (256 * 1024)
+
+
+#define PAUSE()                                                                             \
+    if (pause) {                                                                            \
+        emit operationPaused();                                                             \
+        waitOnCond();                                                                       \
+    }
+
+
+#define SHOW_ERROR_PROMPT(promptString, fileName)                                           \
+    response = NONE;                                                                        \
+    if (ignoreAll[errno]) {                                                                 \
+        response = IGNORE;                                                                  \
+    } else {                                                                                \
+        char 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);    \
+        waitOnCond();                                                                       \
+    }
+
+
+#define ERROR_PROMPT(operation, promptString, fileName)                                     \
+{                                                                                           \
+    response = NONE;                                                                        \
+    while (!abort && operation) {                                                           \
+        SHOW_ERROR_PROMPT(promptString, fileName)                                           \
+        if (response == IGNORE) {                                                           \
+            break;                                                                          \
+        }                                                                                   \
+        PAUSE()                                                                             \
+    }                                                                                       \
+}
+
+
+#define SPECIAL_COPY_ERROR_PROMPT(operation, promptString, fileName)                        \
+{                                                                                           \
+    ERROR_PROMPT(operation, promptString, fileName)                                         \
+    if (abort || response == IGNORE) {                                                      \
+        if (!abort) {                                                                       \
+            updateProgress(fileSizeMap[path]);                                              \
+            removeExcludeFiles.insert(path);                                                \
+        }                                                                                   \
+        return;                                                                             \
+    }                                                                                       \
+}
+
+
+#define OVERWRITE_PROMPT(file, newFile)                                                     \
+{                                                                                           \
+    response = NONE;                                                                        \
+                                                                                            \
+    while (!abort && response == NONE && newFile.exists()) {                                \
+        if (overwriteAll != NONE) {                                                         \
+            response = overwriteAll;                                                        \
+        } else {                                                                            \
+            emit showOverwritePrompt(this, newFile.absoluteFilePath(),                      \
+                newFile.isDir() && file.isDir());                                           \
+            waitOnCond();                                                                   \
+                                                                                            \
+            PAUSE()                                                                         \
+            else if (response == NONE) {                                                    \
+                emit showInputFilenamePrompt(this, newFile, file.isDir());                  \
+                waitOnCond();                                                               \
+                if (newNameFromDialog.size()) {                                             \
+                    newFile.setFile(newNameFromDialog);                                     \
+                }                                                                           \
+            }                                                                               \
+        }                                                                                   \
+    }                                                                                       \
+    if (response == ASK) response = NONE;                                                   \
+}
+
+
+OperationThread::OperationThread(const QFileInfoList files, QDir dest) :
+    abort(false),
+    pause(false),
+    files(files),
+    dest(dest),
+    response(NONE),
+    overwriteAll(NONE),
+    totalSize(0),
+    totalValue(0),
+    fileValue(0)
+{
+    memset(ignoreAll, false, sizeof(ignoreAll));
+}
+
+
+void OperationThread::setResponse(
+    const Response response,
+    const bool applyToAll,
+    const int err)
+{
+    mutex.lock();
+
+    this->response = response;
+
+    if (applyToAll) {
+        if (response == KEEP
+            || response == OVERWRITE
+            || response == NONE)
+        {
+            overwriteAll = response;
+        }
+
+        if (response == IGNORE) {
+            ignoreAll[err] = true;
+        }
+    }
+
+    if (response == ABORT) abort = true;
+
+    mutex.unlock();
+    waitCond.wakeAll();
+}
+
+
+void OperationThread::processFiles(const QFileInfoList &files) {
+    for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
+        PAUSE();
+        if (abort) break;
+        perform(*it);
+    }
+}
+
+
+bool OperationThread::remove(QString &fileName, const bool doUpdates) {
+    return remove(QFileInfo(fileName), doUpdates);
+}
+
+
+bool OperationThread::remove(const QFileInfoList &files, const bool doUpdates) {
+    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;
+}
+
+
+bool OperationThread::remove(const QFileInfo &file, const bool doUpdates) {
+    QString path = file.absoluteFilePath();
+
+    if (removeExcludeFiles.contains(path)) {
+        if (doUpdates) updateProgress(1);
+        return false;
+    }
+
+    QFSFileEngine engine(path);
+
+    if (doUpdates) updateFile(path);
+
+    if (file.isDir()) {
+        if (!remove(listDirFiles(path), doUpdates)) {
+            if (doUpdates) updateProgress(1);
+            return false;
+        }
+
+        if (!listDirFiles(path).size()) {
+            ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
+        }
+    } else {
+        ERROR_PROMPT(!engine.remove(), tr("Error deleting file %1."), path)
+    }
+
+    if (!abort && doUpdates) updateProgress(1);
+
+    PAUSE();
+    if (abort || response == IGNORE) return false;
+    return true;
+}
+
+
+void OperationThread::copy(const QFileInfo &file) {
+    QString path(file.absoluteFilePath());
+    QFSFileEngine engine(path);
+    QFileInfo newFile(dest.absolutePath() + "/" + file.fileName());
+
+    updateFile(path);
+
+    // hack to prevent asking about the same file if we already asked in the rename(...) function
+    if (overwriteAll == DONT_ASK_ONCE) {
+        overwriteAll = NONE;
+    } else {
+        OVERWRITE_PROMPT(file, newFile)
+    }
+
+    QString newPath(newFile.absoluteFilePath());
+    QFSFileEngine newEngine(newPath);
+
+    PAUSE();
+    if (abort) return;
+
+    if (file.isDir()) {
+        // save the overwrite response, because the response variable will get ovewritten in remove(...)
+        Response overwriteResponse = response;
+
+        if (newFile.exists() && !newFile.isDir()) {
+            // overwriting a file, so check for KEEP and handle it
+            if (response == KEEP) {
+                updateProgress(fileSizeMap[path]);
+                removeExcludeFiles.insert(path);
+                return;
+            }
+
+            // if it should not be kept, remove it and return on failure
+            if(!remove(newPath)) {
+                updateProgress(fileSizeMap[path]);
+                return;
+            }
+            // create new info since we deleted the file - is it needed?
+            newFile = QFileInfo(newPath);
+        } else {
+            // overwriting a directory - response KEEP means to keep the files inside,
+            // SKIP_DIR means to skip the dir completely
+            if (response == SKIP_DIR) {
+                updateProgress(fileSizeMap[path]);
+                removeExcludeFiles.insert(path);
+                return;
+            }
+        }
+
+        if (!newFile.exists()) {
+            SPECIAL_COPY_ERROR_PROMPT(!engine.mkdir(newPath, false),
+                tr("Error creating directory %1."), newPath)
+        }
+
+        // we've done the job with the dir, so update progress and recurse into the dir
+        updateProgress(1);
+        
+        // change the dest for the recursion
+        QDir destBackup = dest;
+        dest = newPath;
+
+        // and set overwriteAll to the response we got a while ago
+        // because it applies to the files inside the dir
+        Response tmpResp = overwriteAll;
+        overwriteAll = overwriteResponse;
+
+        processFiles(listDirFiles(path));
+
+        overwriteAll = tmpResp;
+
+        ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
+            tr("Error setting permissions for directory %1."), newPath)
+
+        PAUSE();
+        if (abort) return;
+
+        dest = destBackup;
+    } else {
+        if (response == KEEP) {
+            updateProgress(fileSizeMap[path]);
+            removeExcludeFiles.insert(path);
+            return;
+        }
+
+        SPECIAL_COPY_ERROR_PROMPT(checkSequentialFile(engine), tr("Cannot copy file %1."), path)
+
+        if (newFile.exists() && newFile.isDir()) {
+            SPECIAL_COPY_ERROR_PROMPT(!remove(newPath),
+                tr("Cannot replace directory %1 due to previous errors."), newPath)
+        }
+
+        SPECIAL_COPY_ERROR_PROMPT(!engine.open(QIODevice::ReadOnly), tr("Error reading file %1."), path)
+
+        bool ignore = false, newFileWritten = false;
+        while (!abort && !ignore) {
+            engine.seek(0);
+            fileValue = 0;
+
+            ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
+                tr("Error writing file %1."), newPath)
+
+            if (abort || response == IGNORE) {
+                if (response == IGNORE) {
+                    updateProgress(fileSizeMap[path]);
+                    removeExcludeFiles.insert(path);
+                    ignore = true;
+                }
+                break;
+            }
+
+            newFileWritten = true;
+
+            bool error = false;
+            char block[BLOCK_SIZE];
+            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."), path);
+                    } else {
+                        SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
+                    }
+
+                    if (!abort) {
+                        if (response == IGNORE) {
+                            updateProgress(fileSizeMap[path] - fileValue);
+                            removeExcludeFiles.insert(path);
+                            ignore = true;
+                        } else {
+                            updateProgress(-fileValue);
+                        }
+                    }
+                    error = true;
+                    break;
+                }
+
+                PAUSE();
+                if (abort) break;
+
+                updateProgress(1);
+            }
+
+            if (!error) break;
+            PAUSE();
+        }
+
+        engine.close();
+        newEngine.close();
+
+        PAUSE();
+        if (abort || ignore) {
+            if (newFileWritten) {
+                newEngine.remove();
+            }
+        } else {
+            ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
+                tr("Error setting permissions for file %1."), newPath)
+        }
+    }
+}
+
+
+unsigned int OperationThread::calculateFileSize(const QFileInfoList &files,
+    const bool count,
+    const bool addSize)
+{
+    unsigned int res = 0;
+
+    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);
+        }
+
+        if (addSize) {
+            if (it->isDir()) {
+                ++size;
+            } else {
+                size += ceil(static_cast<float>(it->size()) / BLOCK_SIZE);
+            }
+            fileSizeMap[it->absoluteFilePath()] = size;
+        }
+
+        if (count) {
+            ++size;
+        }
+
+        res += size;
+    }
+
+    return res;
+}
+
+
+QFileInfoList OperationThread::listDirFiles(const QString &dirPath) {
+    QDir dir = dirPath;
+    return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden);
+}
+
+
+void OperationThread::setTotalSize(unsigned int size) {
+    totalSize = size;
+    emit totalSizeChanged(size);
+}
+
+
+void OperationThread::updateProgress(int value) {
+    totalValue += value;
+    fileValue += value;
+    emit progressUpdate(value);
+}
+
+
+void OperationThread::updateFile(const QString &name) {
+    fileValue = 0;
+    emit fileNameUpdated(shortenPath(name));
+}
+
+
+void OperationThread::waitOnCond() {
+    time_t waitTime = time(0);
+    waitCond.wait(&mutex);
+    emit operationResumed(time(0) - waitTime);
+}
+
+
+bool OperationThread::checkSequentialFile(const QFSFileEngine &engine) {
+    errno = 0;
+    if (engine.isSequential()) {
+        if (!errno) errno = 255;
+        return true;
+    }
+
+    return false;
+}
+
+
+void OperationThread::wake() {
+    pause = false;
+    waitCond.wakeAll();
+}
+
+
+void DeleteThread::run() {
+    mutex.lock();
+
+    setTotalSize(calculateFileSize(files, true));
+    emit operationStarted(time(0));
+
+    processFiles(files);
+
+    sleep(0.5);
+    emit finished(this);
+}
+
+
+void DeleteThread::perform(const QFileInfo &file) {
+    remove(file, true);
+}
+
+
+void CopyThread::run() {
+    mutex.lock();
+
+    setTotalSize(calculateFileSize(files, false, true));
+    emit operationStarted(time(0));
+
+    processFiles(files);
+
+    sleep(0.5);
+    emit finished(this);
+}
+
+
+void CopyThread::perform(const QFileInfo &file) {
+    copy(file);
+}
+
+
+void MoveThread::run() {
+    mutex.lock();
+
+    rename(files, dest);
+
+    sleep(0.5);
+    emit finished(this);
+}
+
+
+void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
+    setTotalSize(totalSize + files.size());
+    emit operationStarted(time(0));
+
+    for (int i = 0; i < files.size(); ++i) {
+        QString path = files[i].absoluteFilePath();
+        QFSFileEngine engine(path);
+        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 == SKIP_DIR) {
+                PAUSE();
+                if (abort) break;
+                updateProgress(1);
+                removeExcludeFiles.insert(path);
+                continue;
+            }
+        } else {
+            if (response == KEEP) {
+                PAUSE();
+                if (abort) break;
+                updateProgress(1);
+                removeExcludeFiles.insert(path);
+                continue;
+            }
+        }
+
+        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
+            // we calculate the actual file sizes, because from now on copy & remove takes over
+            if (errno == EXDEV) {
+                overwriteAll = response;
+                // hack: we already checked the first file we are sending to processFiles(...)
+                // so we don't want to ask about this one again
+                if (overwriteAll == NONE) overwriteAll = DONT_ASK_ONCE;
+
+                QFileInfoList remainingFiles = files.mid(i);
+
+                setTotalSize(totalValue + calculateFileSize(remainingFiles, true, true));
+
+                processFiles(remainingFiles);
+
+                emit removeAfterCopy();
+
+                remove(remainingFiles, 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) {
+                Response tmpResp = overwriteAll;
+                overwriteAll = response;
+
+                rename(listDirFiles(path), QDir(newPath));
+                PAUSE();
+                if (abort) break;
+
+                overwriteAll = tmpResp;
+
+                remove(files[i]);
+
+                break;
+            // source and target are nonmatching types(file and dir)
+            // remove the target and let it loop once again
+            } else if (errno == ENOTDIR || errno == EISDIR) {
+                if (!remove(newPath)) break;
+            } else {
+                SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
+
+                if (response == IGNORE) {
+                    break;
+                }
+            }
+            PAUSE();
+        }
+
+        if (done) break;
+
+        PAUSE();
+        if (abort) break;
+        updateProgress(1);
+    }
+}
+
+
+void MoveThread::perform(const QFileInfo &file) {
+    copy(file);
+}
diff --git a/src/operationthread.h b/src/operationthread.h
new file mode 100644 (file)
index 0000000..bb9d95a
--- /dev/null
@@ -0,0 +1,152 @@
+// 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 OPERATIONTHREAD_H
+#define OPERATIONTHREAD_H
+
+#include <QThread>
+#include <QFileInfo>
+#include <QMap>
+#include <QSet>
+#include <QFSFileEngine>
+#include <QDir>
+#include <QMutex>
+#include <QWaitCondition>
+
+
+class OperationThread : public QThread {
+    Q_OBJECT
+
+public:
+    // DONT_ASK_ONCE is a hackish way to avoid asking twice to overwrite the same directory when moving
+    enum Response{NONE, ABORT, RETRY, IGNORE, KEEP, OVERWRITE, SKIP_DIR, ASK, DONT_ASK_ONCE};
+
+    explicit OperationThread(const QFileInfoList files, QDir dest = QDir());
+    void setResponse(const Response response, const bool appyToAll = false, const int err = 0);
+
+    void wake();
+
+    // 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);
+    virtual void perform(const QFileInfo &file) = 0;
+
+    bool remove(QString &fileName, const bool doUpdates = false);
+    bool remove(const QFileInfoList &files, const bool doUpdates = false);
+    bool remove(const QFileInfo &file, const bool doUpdates = false);
+
+    void copy(const QFileInfo &file);
+
+    unsigned int calculateFileSize(const QFileInfoList &files,
+        const bool count = false,
+        const bool addSize = false);
+
+    QFileInfoList listDirFiles(const QString &dirPath);
+
+    void setTotalSize(unsigned int size);
+    void updateProgress(int value);
+    void updateFile(const QString &name);
+
+    void waitOnCond();
+
+    bool checkSequentialFile(const QFSFileEngine &engine);
+
+    QMutex mutex;
+    QWaitCondition waitCond;
+
+    // files to process by the operation
+    const QFileInfoList files;
+    // destination for files - changes as the operation recurses into directories
+    QDir dest;
+
+    // responses from the dialog prompts (error and overwrite)
+    Response response;
+    Response overwriteAll;
+    // an array indicating whether to always ignore the error of index errno
+    bool ignoreAll[256];
+
+    // set of files that won't be deleted by the remove(...) functions
+    // used when move(...) would not overwrite target file to ensure the source file doesn't get deleted
+    QSet<QString> removeExcludeFiles;
+
+    // A map of file paths to their size. Not the actual size, but what is calculated for the
+    // purpose of the progressbar for the given operation. So either fileSize/BLOCK_SIZE or simply
+    // 1 for a file and file count for dirs (or both for copy&delete)
+    QMap<QString, qint64> fileSizeMap;
+
+    // progress information of the bar and for the current file
+    unsigned int totalSize, totalValue, fileValue;
+
+signals:
+    void showErrorPrompt(OperationThread*, const QString&, const QString&, const int);
+    void showOverwritePrompt(OperationThread*, const QString&, const bool);
+    void showInputFilenamePrompt(OperationThread*, const QFileInfo&, const bool);
+    void finished(OperationThread*);
+
+    void totalSizeChanged(int);
+    void progressUpdate(int);
+    void fileNameUpdated(QString);
+    void operationStarted(time_t);
+    void operationPaused();
+    void operationResumed(time_t);
+    // special signal emitted when operation has to copy files between partitions
+    // to notify the user it is deleting files after succesful copy (which can take some time)
+    void removeAfterCopy();
+};
+
+
+class DeleteThread : public OperationThread {
+    Q_OBJECT
+
+public:
+    explicit DeleteThread(const QFileInfoList &files) : OperationThread(files) {}
+
+protected:
+    void run();
+    virtual void perform(const QFileInfo &file);
+};
+
+
+class CopyThread : public OperationThread {
+    Q_OBJECT
+
+public:
+    explicit CopyThread(const QFileInfoList &files, QDir &dest) : OperationThread(files, dest) {}
+
+protected:
+    void run();
+    virtual void perform(const QFileInfo &file);
+};
+
+
+class MoveThread : public OperationThread {
+    Q_OBJECT
+
+public:
+    explicit MoveThread(const QFileInfoList &files, QDir &dest) : OperationThread(files, dest) {}
+
+protected:
+    void run();
+    virtual void perform(const QFileInfo &file);
+    void rename(const QFileInfoList &files, const QDir &dest);
+};
+
+#endif // OPERATIONTHREAD_H
index 29d68a6..7780b04 100644 (file)
 
 #include <iostream>
 
-ProgressBar::ProgressBar(FileManipulatorThread *thread, QWidget *parent) :
-    QProgressBar(parent),
+
+ProgressBar::ProgressBar(const QPixmap &icon, const QPixmap &inverseIcon) :
+    bgIcon(icon),
+    fgIcon(inverseIcon),
     paused(false),
     contextEvent(false),
-    thread(thread)
+    lastTimeUpdate(0),
+    startTime(0)
 {
+    timeBuf[0] = 0;
     setMaximum(1);
     setValue(0);
     setMinimum(0);
@@ -41,19 +45,84 @@ ProgressBar::ProgressBar(FileManipulatorThread *thread, QWidget *parent) :
 }
 
 
-void ProgressBar::setIcons(const QPixmap &icon, const QPixmap &inverseIcon) {
-    bgIcon = icon;
-    fgIcon = inverseIcon;
+void ProgressBar::updateProgress(int val) {
+    if (value() + val > maximum()) {
+        std::cout << "WARNING: exceeding progressbar maximum (" << maximum() << ") by " << val << std::endl;
+    }
+
+    setValue(value() + val);
+
+    time_t now = time(0);
+    if (lastTimeUpdate < now) {
+        lastTimeUpdate = now;
+
+        time_t elapsed = now - startTime;
+        time_t remaining = (time_t) ((float) elapsed / value() * (maximum() - value()));
+        struct tm *ts = gmtime(&remaining);
+        
+        if (remaining == 0) {
+            timeBuf[0] = 0;
+        } else 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);
+        }
+    }
+
+    setFormat(QString("%p%") + timeBuf);
+}
+
+
+void ProgressBar::updateMainText(const QString &text) {
+    mainText = text;
+    if (fromText.size()) {
+        mainText.remove(0, fromText.size() + 1);
+    }
+    repaint();
+}
+
+
+void ProgressBar::setBottomTexts(const QString &left, const QString &right) {
+    fromText = left;
+    toText = right;
+}
+
+
+void ProgressBar::setStartTime(time_t t) {
+    startTime = t;
+    lastTimeUpdate = time(0);
+    updateProgress(0);
+}
+
+
+void ProgressBar::pause() {
+    paused = true;
+    repaint();
+}
+
+
+void ProgressBar::resume(time_t stallTime) {
+    startTime += stallTime;
+    paused = false;
+    repaint();
+}
+
+
+void ProgressBar::showRemoveNotice() {
+    toText = "<" + tr("deleting") + ">";
+    repaint();
 }
 
 
 void ProgressBar::mouseReleaseEvent(QMouseEvent *) {
-    if (!contextEvent) emit togglePauseOperation(thread);
+    if (!contextEvent) emit togglePauseOperation(this);
     contextEvent = false;
 }
 
 
 void ProgressBar::contextMenuEvent(QContextMenuEvent *) {
     contextEvent = true;
-    emit abortOperation(thread);
+    emit abortOperation(this);
 }
index fe67dfd..1c63b0d 100644 (file)
 #include <QIcon>
 
 
-class FileManipulatorThread;
-
 class ProgressBar : public QProgressBar {
     Q_OBJECT;
 
-signals:
-    void abortOperation(FileManipulatorThread*);
-    void togglePauseOperation(FileManipulatorThread*);
-
 public:
-    explicit ProgressBar(FileManipulatorThread *thread, QWidget *parent = 0);
-
-    void setIcons(const QPixmap &icon, const QPixmap &inverseIcon);
+    explicit ProgressBar(const QPixmap &icon, const QPixmap &inverseIcon);
 
     QString mainText, fromText, toText;
     QPixmap bgIcon, fgIcon;
     bool paused;
 
+public slots:
+    void updateProgress(int val);
+    void updateMainText(const QString &text);
+    void setBottomTexts(const QString &left, const QString &right);
+    void setStartTime(time_t t);
+    void pause();
+    void resume(time_t stallTime);
+    void showRemoveNotice();
 
 protected:
     void mouseReleaseEvent(QMouseEvent *);
     void contextMenuEvent(QContextMenuEvent *);
 
     bool contextEvent;
-    FileManipulatorThread *thread;
+
+    // stamp of the last ETA recalculation - done every second
+    time_t lastTimeUpdate;
+    time_t startTime;
+    char timeBuf[10];
+
+signals:
+    void abortOperation(ProgressBar*);
+    void togglePauseOperation(ProgressBar*);
 };
 
 #endif // PROGRESSBAR_H
index bf082e0..8a50e16 100644 (file)
 
 #include "utils.h"
 
+#include <QFSFileEngine>
+
+
+QString shortenPath(const QString &path) {
+    QString homePath = QFSFileEngine::homePath();
+
+    if (path.indexOf(homePath, 0) == 0) {
+        QString result = path;
+
+        result.replace(0, homePath.size(), "~");
+        return result;
+    }
+
+    return path;
+}
+
+
+QString 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;
+}
+
 
 void themeImage(const QPalette &p, QImage &image, const bool inverse) {
     image = image.convertToFormat(QImage::Format_Indexed8);
index b3e1654..c1a5671 100644 (file)
@@ -27,6 +27,9 @@
 #define ICON_SET "default"
 
 
+QString shortenPath(const QString &path);
+QString unwindPath(const QString &path);
+
 bool loadOperationIcons(const QPalette &p, const QString &name, QPixmap &normal, QPixmap &inverse);
 bool loadMiddleButtonIcons(const QPalette &p, const QString &name, QIcon &normal, QIcon &mirrored);