]> gitweb.hamatoma.de Git - reqt/commitdiff
reimgconvert works
authorhama <hama@siduction.net>
Sun, 17 Jan 2016 23:42:57 +0000 (00:42 +0100)
committerhama <hama@siduction.net>
Sun, 17 Jan 2016 23:42:57 +0000 (00:42 +0100)
18 files changed:
appl/reimgconvert/ReImgConvert.html [new file with mode: 0644]
appl/reimgconvert/converter.cpp
appl/reimgconvert/converter.hpp
appl/reimgconvert/main.cpp
appl/reimgconvert/mainwindow.cpp
appl/reimgconvert/mainwindow.hpp
appl/reimgconvert/mainwindow.ui
appl/reimgconvert/reimgconvert.pro
base/ReFileUtils.cpp
base/ReFileUtils.hpp
base/ReQStringUtils.cpp
base/ReQStringUtils.hpp
base/ReTest.cpp
cunit/allTests.cpp
cunit/cuReQStringUtils.cpp
gui/ReGuiQueue.hpp
gui/ReStateStorage.cpp
gui/ReStateStorage.hpp

diff --git a/appl/reimgconvert/ReImgConvert.html b/appl/reimgconvert/ReImgConvert.html
new file mode 100644 (file)
index 0000000..f32ef37
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<style>#doc_header { font-size: 3em; color: blue; }
+h1 { border-bottom: 1px solid red; }
+h2 { border-bottom: 1px solid blue; }
+h3 { border-bottom: 1px solid black; }
+</style>
+<body>
+<div id="doc_header">Documentation of ReImgConvert</div>
+<h1>Release Notes</h1>
+<p>
+<ul>
+<li>v1.0 (2016.01.15)<br/>
+       <ul><li>Basic version. User language is English only.</li>
+       </ul>
+</li>
+</ul>
+</p>
+
+<h1>User Manual</h1>
+<h2>Purpose:</h2>
+<p>This program allows to convert image files (pictures) into another format.</p>
+<p>Conversion is done for the dimensions (width/height) and/or the quality/file
+size.</p>
+
+<h2>Simple Mode and Detailed Mode</h2>
+<p>There are two modes of processing:
+<ul>
+<li>Simple mode: You define the maximum width and maximum height of a
+       picture. The converted file will respect these limits.</li>
+<li>Detailed mode: The maximum dimensions for the three format types
+       (portrait, landscape, square) must be defined.</li>
+</ul>
+</p>
+
+<h2>Templates</h2>
+<p>For the simple mode there are predefined limits called "templates".
+Chosing one template with the combobox the fields "max. width" and "max height"
+will be filled with these limits.
+</p>
+
+<h2>Conversion Details</h2>
+
+<p>If an image is lower than the given limits it will be copied without
+conversion.</p>
+<p>This program is only the GUI of the image conversion program "convert"
+from the package ImageMagick.</p>
+<h2>Installation</h2>
+
+<p>Copy reimgconvert into a directory inserted in the execution
+path, e.g. /usr/local/bin and give it the right "executable".</p>
+<p>Install the packages qt5 and imagemagick.</p>
+
+<h2>Requirements</h2>
+<p>
+<ul>
+<li>ImageMagick</li>
+</ul>
+</p>
+
+<h1>Program Documentation</h1>
+
+<h2>Programming Features</h2>
+
+<p>This is a example for a complete QT application with the following features:
+<ul>
+<li>C++</li>
+<li>QT</li>
+<li>Threads</li>
+<li>Using GUI wizzard</li>
+<li>Changing GUI elements from a 2nd thread</li>
+<li>Abstract class as interface</li>
+<li>Calling an external program</li>
+</ul>
+</p>
+
+<h1>(Un)License: Public Domain</h1>
+
+<p>You can use and modify this file without any restriction.<br/>
+Do what you want.<br/>
+No warranties and disclaimer of any damages.<br/>
+More info: <a href="http://unlicense.org">http://unlicense.org</a><br/>
+The latest sources: <a href="https://github.com/republib">https://github.com/republib</a>.
+</p>
+
+</body>
+</html>
index 0eb8c5fea5904c1814d4c5656d589f1744d46ae5..50bbde8b2863c8416c75bcd1a8860dfbd9f618c9 100644 (file)
  *
  * If an image is lower than the given limits it will be copied without
  * conversion
- * This program is only the GUI of an Perl script which does the conversion.
- * The script uses the
- *
- * @section install_sec Installation
- *
- * Copy reimgconvert into a directory inserted in the executable
- * path, e.g. /usr/local/bin and give them the right "executable".
- *
- * @subsection req_sec Requirements
- *
- * <ul>
- * <li>ImageMagick</li>
- * </ul>
- *
+ * This program is only the GUI of the image conversion program "convert"
+ * from the package ImageMagick
+ * *
  * @section prog_sec Programming Features
  *
  * This is a example for a complete QT application with the following features:
@@ -57,9 +46,7 @@
  * <li>Threads</li>
  * <li>Using GUI wizzard</li>
  * <li>Abstract class as interface</li>
- * <li>Calling an external script</li>
- * <li>Lists the output of the external list line by line at the moment
- *  the line is created</li>
+ * <li>Calling an external program</li>
  * </ul>
  *
  * @section version_sec Release Notes
@@ -74,7 +61,7 @@
  *
  * You can use and modify this file without any restriction.
  * There is no warranty.
- * You also can use the licence from http://www.wtfpl.net/.
+ * You also can use the licence from http://unlicense.org
  * The original sources can be found on https://github.com/republib
  */
 
@@ -130,10 +117,7 @@ Converter::Converter(const QString& directory, const QString& targetDirectory,
    int landscapeY, int portraitX, int portraitY, int squareX, int quality,
    MainWindow* mainWindow) :
                        m_dir(directory),
-                       m_targetDir(
-                          targetDirectory.indexOf(QDir::separator()) >= 0 ?
-                                 targetDirectory :
-                                 directory + QDir::separator() + targetDirectory),
+                       m_targetDir(targetDirectory),
                        m_sourcePattern(sourcePattern),
                        m_targetType(targetType),
                        m_landscapeWidth(landscapeX),
@@ -151,7 +135,7 @@ Converter::Converter(const QString& directory, const QString& targetDirectory,
                        m_simpleMode(simpleConversion),
                        m_maxWidth(maxWidth),
                        m_maxHeight(maxHeight)
-       {
+{
 }
 
 /**
@@ -181,7 +165,7 @@ void addArg(QStringList& args, const char* prefix, int value){
  * @param info      an info about the change
  */
 void Converter::changeState(Converter::State state, const QString& info){
-   m_mainWindows->on_threadStateChanged(state, info);
+   m_mainWindows->onThreadStateChanged(state, info);
 }
 
 /**
@@ -291,12 +275,13 @@ bool Converter::handleUserDefined(int width, int height, int& widthNew, int& hei
 /**
  * @brief Converts one file.
  *
- * @param source    the file's name with path
- * @param target    the new filename with path
- * @param size  the size of the file (in byte)
+ * @param source               the file's name with path
+ * @param target               the new filename with path
+ * @param size                 the size of the file (in byte)
+ * @param sizeTarget   OUT: the file size of the target
  */
 void Converter::convertOneFile(const QString& source, const QString& target,
-   qint64 size){
+   qint64 size, qint64& sizeTarget){
    int width = 0;
    int height = 0;
    QString info;
@@ -304,22 +289,23 @@ void Converter::convertOneFile(const QString& source, const QString& target,
    if (readProperties(source, width, height, info)){
           int widthNew = width;
           int heightNew = height;
-          bool doConvert;
           if (m_simpleMode)
-                  doConvert = handleSimple(width, height, widthNew, heightNew);
+                  handleSimple(width, height, widthNew, heightNew);
           else
-                  doConvert = handleUserDefined(width, height, widthNew, heightNew);
+                  handleUserDefined(width, height, widthNew, heightNew);
+          QString node = ReFileUtils::nodeOf(source);
          log(
-                source + " " + info + " " + sizeToString(size)
+                node + " " + info + " " + ReQStringUtils::readableSize(size)
                        + QString(" -> %1x%2 ").arg(widthNew).arg(heightNew));
          convert(source, target, width, height, widthNew, heightNew, m_quality);
          struct stat info;
-         if (stat(I18N::s2b(target).constData(), &info) == 0)
-                m_mainWindows->logAppendLast(sizeToString(info.st_size) + " ");
+         if (stat(I18N::s2b(target).constData(), &info) == 0){
+                 sizeTarget = info.st_size;
+                m_mainWindows->logAppendLast(ReQStringUtils::readableSize(sizeTarget)
+                                                                         + " " + ReQStringUtils::readableDuration(
+                                                                                 clock() - start));
+         }
    }
-   m_mainWindows->logAppendLast(
-         QString("").sprintf("%.3f sec",
-                                                 double(clock() - start) / CLOCKS_PER_SEC));
 }
 
 /**
@@ -421,6 +407,10 @@ bool Converter::readPropertiesByIdentify(const QString& name, int& width, int& h
  */
 void Converter::run(){
    QString msg;
+   clock_t start = clock();
+   uint64_t preSize = 0;
+   uint64_t postSize = 0;
+   uint64_t lengthTarget = 0;
    int no = 0;
    try{
          if (!m_dir.exists())
@@ -458,7 +448,16 @@ void Converter::run(){
                        QString nodeTarget = ReFileUtils::replaceExtension(node,
                           "." + m_targetType);
                        QString target = m_targetDir.absoluteFilePath(nodeTarget);
-                       convertOneFile(path, target, length);
+                       convertOneFile(path, target, length, lengthTarget);
+                       preSize += length;
+                       postSize += lengthTarget;
+                       m_mainWindows->setStatusMessage(LOG_INFO,
+                                                                                       tr("%1 file(s) %2 -> %3 (%4 %)")
+                                                          .arg(no)
+                                                               .arg(ReQStringUtils::readableSize(preSize))
+                                                               .arg(ReQStringUtils::readableSize(postSize))
+                                                          .arg(postSize * 100.0 / max(1, preSize), 0,
+                                                                       'f', 2));
                 }
          }
          changeState(Converter::STATE_SUB_TASK_STOPPED, msg);
@@ -467,7 +466,12 @@ void Converter::run(){
                 QObject::tr("Execution stopped because of error(s): ")
                        + exc.message());
    }
-   msg = QObject::tr("%1 file(s) converted").arg(no);
+   preSize = max(1, preSize);
+   msg = QObject::tr("%1 file(s) converted, %2 -> %3 (%4 %), duration: %5").arg(no)
+                  .arg(ReQStringUtils::readableSize(preSize))
+                       .arg(ReQStringUtils::readableSize(postSize))
+                  .arg(postSize * 100.0 / preSize, 0, 'f', 2)
+                  .arg(ReQStringUtils::readableDuration(clock() - start));
    changeState(Converter::STATE_READY, msg);
 }
 
@@ -494,22 +498,3 @@ QString findScript(const QString& node){
    }
    return rc;
 }
-/**
- * @brief Converts the size into a human readable string.
- *
- * @param size  the size in bytes
- * @return      the size as human readable string, e.g. "2MiByte"
- */
-QString sizeToString(qint64 size){
-   QString rc;
-   if (size < 10 * 1024)
-         rc.sprintf("%d Bytes", (int) size);
-   else if (size < qint64(10 * 1024 * 1024))
-         rc.sprintf("%d KiBytes", (int) (size / 1024));
-   else if (size < qint64(10 * 1024 * 1024) * 1024)
-         rc.sprintf("%d MiBytes", (int) (size / 1024 / 1024));
-   else
-         rc.sprintf("%d GiBytes", (int) (size / 1024 / 1024 / 1024));
-   return rc;
-}
-
index c0585d8c7d677a0e4d8b24baec1cc169a585b1f0..0ba19a1f0a70d27a0a8e9b43b4c22daa2533ad30 100644 (file)
@@ -65,7 +65,7 @@ protected:
    void convert(const QString& source, const QString& target, int width,
          int height, int widthNew, int heightNew, int quality);
    void convertOneFile(const QString& source, const QString& target,
-         qint64 size);
+         qint64 size, qint64& sizeTarget);
    bool handleSimple(int width, int height, int& widthNew, int& heightNew);
    bool handleUserDefined(int width, int height, int& widthNew, int& heightNew);
    bool readProperties(const QString& name, int &width, int &height,
index 051c685209390f5cb8fd9feb1112a88612b61fdc..0d45da37c32ca7f5859ce165dcbf3e544f18f72a 100644 (file)
@@ -19,7 +19,6 @@ int main(int argc, char *argv[]){
    QString homeDir = argc > 1 ? argv[1] : "";
    QApplication a(argc, argv);
    MainWindow w(homeDir);
-       QObject::connect(&a, SIGNAL(aboutToQuit()), &w, SLOT(closing()));
        w.show();
        return a.exec();
 }
index c258ddaf5da6669e6d37dfd9a909dd2ceee1ed99..5f8006d041873d941a39899a15c722d92053cb29 100644 (file)
@@ -32,24 +32,25 @@ const QString VERSION("2016.01.15");
  * @param parent        NULL or the parent (who destroys the objects at the end)
  */
 MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
-                       QMainWindow(parent),
-                       m_homeDir(homeDir),
-                       m_storageFile(),
+                       ReHomeApplication("reimgconvert", homeDir, 2, 100100100, parent),
                        ui(new Ui::MainWindow),
                        m_converter(NULL),
                        m_logger(),
                        m_guiQueue(),
                        m_guiTimer(new QTimer(this)){
        ui->setupUi(this);
-       initializeHome();
-       m_logger.buildStandardAppender(I18N::s2b(m_homeDir + OS_SEPARATOR_STR
-                                                                                        + "reimgconvert").constData());
    startStop(false);
    m_statusMessage = new QLabel(tr("Welcome at reimgconvert"));
    statusBar()->addWidget(m_statusMessage);
+   connect(ui->actionStart, SIGNAL(triggered()), this,
+         SLOT(onConvert()));
+   connect(ui->actionStop, SIGNAL(triggered()), this,
+         SLOT(onStop()));
    connect(ui->actionSelectTarget, SIGNAL(triggered()), this,
          SLOT(selectTarget()));
    connect(ui->actionClear, SIGNAL(triggered()), this, SLOT(clear()));
+   connect(ui->pushButtonConvert, SIGNAL(clicked()), this, SLOT(onConvert()));
+   connect(ui->pushButtonStop, SIGNAL(clicked()), this, SLOT(onStop()));
    connect(ui->pushButtonClear, SIGNAL(clicked()), this, SLOT(clear()));
    connect(ui->actionSelectTarget, SIGNAL(triggered()), this,
          SLOT(selectTarget()));
@@ -61,8 +62,7 @@ MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
          SLOT(selectSource()));
    connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));
    connect(ui->comboBoxTemplate, SIGNAL(currentIndexChanged(const QString&)),
-                       this, SLOT(on_templateChangeIndex(const QString&)));
-   connect(ui->pushButtonActivate, SIGNAL(clicked()), this, SLOT(activate()));
+                       this, SLOT(onTemplateChangeIndex(const QString&)));
    connect(m_guiTimer, SIGNAL(timeout()), this, SLOT(guiTimerUpdate()));
    restoreState();
    m_guiTimer->start(100);
@@ -85,17 +85,10 @@ void MainWindow::about(){
 }
 
 /**
- * Slot when the pushbutton "activate" is clicked.
+ * Actions while closing the application.
  */
-void MainWindow::activate(){
-       QString theTemplate = comboText(ui->comboBoxTemplate);
-       QRegularExpression rexpr("(\\d+)x(\\d+)");
-       QRegularExpressionMatch matcher = rexpr.match(theTemplate);
-       if (matcher.hasMatch()){
-               int width = atoi(matcher.captured(1).toLatin1());
-               int height = atoi(matcher.captured(2).toLatin1());
-               setMaxDimensions(width, height);
-       }
+void MainWindow::aboutToQuit(){
+       saveState();
 }
 
 /**
@@ -106,13 +99,6 @@ void MainWindow::clear()
        ui->listWidget->clear();
 }
 
-/**
- * Actions while closing the application.
- */
-void MainWindow::closing(){
-       saveState();
-}
-
 /**
  * Issues an error message.
  *
@@ -146,6 +132,16 @@ void MainWindow::guiTimerUpdate()
                        case ReGuiQueueItem::LogMessage:
                                say(LOG_INFO, item.m_value);
                                break;
+                       case ReGuiQueueItem::StatusLine:
+                               setStatusMessage(false, item.m_value);
+                               break;
+                       case ReGuiQueueItem::ListAppendToCurrent:
+                       {
+                               QListWidgetItem* item2 = reinterpret_cast<QListWidget*>(
+                                                       item.m_widget)->currentItem();
+                               item2->setText(item2->text() + " " + item.m_value);
+                               break;
+                       }
                        default:
                                say(LOG_ERROR, "unknown item type: " + QString::number(item.m_type)
                                        + " " + item.m_value);
@@ -155,26 +151,6 @@ void MainWindow::guiTimerUpdate()
                }
        }
 }
-/**
- * initializeHomeializes the program home directory.
- */
-void MainWindow::initializeHome(){
-   if (m_homeDir.isEmpty()){
-         m_homeDir = QDir::home().absoluteFilePath(".reimgconvert");
-   }
-
-   QDir home(m_homeDir);
-   if (!home.exists()){
-         if (!home.mkpath(m_homeDir)){
-                m_homeDir = home.tempPath() + "/.reimgconvert";
-                home.mkpath(m_homeDir);
-         }
-   }
-   if (!m_homeDir.endsWith("/"))
-         m_homeDir += "/";
-   m_storageFile = m_homeDir + "state.conf";
-}
-
 
 /**
  * @brief Logs a message
@@ -197,17 +173,41 @@ bool MainWindow::log(const QString& message){
  * @return          <code>true</code>
  */
 bool MainWindow::logAppendLast(const QString& message){
-       m_guiQueue.pushBack(ReGuiQueueItem(ReGuiQueueItem::ListEnd, ui->listWidget, message));
-   QListWidgetItem* item = ui->listWidget->item(0);
-   item->setText(item->text() + " " + message);
+       m_guiQueue.pushBack(ReGuiQueueItem(ReGuiQueueItem::ListAppendToCurrent,
+                                                                          ui->listWidget, message));
    return true;
 }
 
 /**
- * @brief Handles the click on the button "stop".
+ * @brief Handles the button click on "convert".
  */
-void MainWindow::on_pushButtonStop_clicked(){
-   m_converter->stop();
+void MainWindow::onConvert(){
+   startStop(true);
+   delete m_converter;
+   m_errors = 0;
+   int landscapeX = comboInt(ui->comboBoxLandscapeX, 0, "*", 0);
+   int landscapeY = comboInt(ui->comboBoxLandscapeY, 0, "*", 0);
+   int portraitX = comboInt(ui->comboBoxPortraitX, 0, "*", 0);
+   int portraitY = comboInt(ui->comboBoxPortraitY, 0, "*", 0);
+   int squareX = comboInt(ui->comboBoxSquareX, 0);
+   int quality = comboInt(ui->comboBoxQuality, 75);
+   int maxWidth = comboInt(ui->comboBoxMaxWidth, 0);
+   int maxHeight = comboInt(ui->comboBoxMaxHeight, 0);
+   QRegularExpressionMatch match = QRegularExpression("\\dx\\d").match(
+                          ui->comboBoxTemplate->currentText());
+   bool simpleConversion = match.hasMatch();
+   if (m_errors == 0){
+          saveState();
+         m_converter = new Converter(ui->comboBoxSourceDir->currentText(),
+                ui->comboBoxTargetDir->currentText(),
+                ui->comboBoxSourcePattern->currentText(),
+                ui->comboBoxDestType->currentText(),
+               simpleConversion, maxWidth, maxHeight,
+               landscapeX, landscapeY, portraitX,
+                portraitY, squareX, quality, this);
+         // start the thread:
+         m_converter->start();
+   }
 }
 
 /**
@@ -216,7 +216,7 @@ void MainWindow::on_pushButtonStop_clicked(){
  * Shows a selection dialog for directories and sets the source directory
  * onto the selected directory.
  */
-void MainWindow::on_pushButtonFileSelect_clicked(){
+void MainWindow::onSourceFileSelect(){
    QFileDialog selection;
    selection.setFileMode(QFileDialog::DirectoryOnly);
    QString dir = ui->comboBoxSourceDir->currentText();
@@ -226,12 +226,19 @@ void MainWindow::on_pushButtonFileSelect_clicked(){
          ui->comboBoxSourceDir->setCurrentText(selection.selectedFiles().at(0));
 }
 
+/**
+ * @brief Handles the click on the button "stop".
+ */
+void MainWindow::onStop(){
+   m_converter->stop();
+}
+
 /**
  * Slot when the value of the template combobox has been changed.
  *
  * @param text  the new selected combobox text
  */
-void MainWindow::on_templateChangeIndex(const QString & text){
+void MainWindow::onTemplateChangeIndex(const QString & text){
    QRegularExpression rexpr("(\\d+)x(\\d+)");
    QRegularExpressionMatch match = rexpr.match(text);
    int width = match.captured(1).toInt();
@@ -239,38 +246,6 @@ void MainWindow::on_templateChangeIndex(const QString & text){
    setMaxDimensions(width, height);
 }
 
-/**
- * @brief Handles the button click on "convert".
- */
-void MainWindow::on_pushButtonConvert_clicked(){
-   startStop(true);
-   delete m_converter;
-   m_errors = 0;
-   int landscapeX = comboInt(ui->comboBoxLandscapeX, 0, "*", 0);
-   int landscapeY = comboInt(ui->comboBoxLandscapeY, 0, "*", 0);
-   int portraitX = comboInt(ui->comboBoxPortraitX, 0, "*", 0);
-   int portraitY = comboInt(ui->comboBoxPortraitY, 0, "*", 0);
-   int squareX = comboInt(ui->comboBoxSquareX, 0);
-   int quality = comboInt(ui->comboBoxQuality, 75);
-   int maxWidth = comboInt(ui->comboBoxMaxWidth, 0);
-   int maxHeight = comboInt(ui->comboBoxMaxHeight, 0);
-   QRegularExpressionMatch match = QRegularExpression("\\dx\\d").match(
-                          ui->comboBoxTemplate->currentText());
-   bool simpleConversion = match.hasMatch();
-   if (m_errors == 0){
-         m_converter = new Converter(ui->comboBoxSourceDir->currentText(),
-                ui->comboBoxLandscapeX->currentText(),
-                ui->comboBoxSourcePattern->currentText(),
-                ui->comboBoxDestType->currentText(),
-               simpleConversion, maxWidth, maxHeight,
-               landscapeX, landscapeY, portraitX,
-                portraitY, squareX, quality, this);
-         // start the thread:
-         m_converter->start();
-   }
-
-}
-
 /**
  * @brief Handles the event "thread changed".
  *
@@ -279,7 +254,7 @@ void MainWindow::on_pushButtonConvert_clicked(){
  * @param state     the new state of the thread
  * @param info      info about the new state. Not used
  */
-void MainWindow::on_threadStateChanged(Converter::State state, const QString& info){
+void MainWindow::onThreadStateChanged(Converter::State state, const QString& info){
    switch (state) {
    case Converter::STATE_READY:
          m_guiQueue.pushBack(ReGuiQueueItem(ReGuiQueueItem::ReadyMessage, NULL, info));
@@ -401,6 +376,18 @@ void MainWindow::selectTarget(){
          ui->comboBoxTargetDir->setCurrentText(dir);
 }
 
+/**
+ * Writes a text to the status line.
+ *
+ * Note: this method is called from a non-main thread
+ *
+ * @param message   the text to set
+ */
+void MainWindow::setRemoteStatus(const QString& message){
+       m_guiQueue.pushBack(ReGuiQueueItem(ReGuiQueueItem::StatusLine,
+                                                                          NULL, message));
+}
+
 /**
  * Writes a text to the status line.
  *
index cef44ba8152961d892f498d1f6f34127054854c6..05ececf4468ca0b06dde87be7c1a60a8272cccdf 100644 (file)
@@ -23,7 +23,7 @@ namespace Ui {
 class MainWindow;
 }
 
-class MainWindow: public QMainWindow,
+class MainWindow: public ReHomeApplication,
    public ReGuiValidator,
    public ConvertLogger {
    Q_OBJECT
@@ -32,36 +32,32 @@ public:
    explicit MainWindow(const QString& homeDir, QWidget *parent = 0);
    ~MainWindow();
 
-public slots:
-       void closing();
 public:
+       virtual void aboutToQuit();
    bool error(const QString& message);
    bool log(const QString& message);
    bool logAppendLast(const QString& message);
-   void on_threadStateChanged(Converter::State state, const QString& info);
+   void onThreadStateChanged(Converter::State state, const QString& info);
+   void setRemoteStatus(const QString& message);
    void setStatusMessage(bool error, const QString& message);
 public:
    virtual bool say(ReLoggerLevel level, const QString& message);
 private:
-   void initializeHome();
    void restoreState();
    void saveState();
    void setMaxDimensions(int maxWidth, int maxHeight);public slots:
    void startStop(bool isStart);
 private slots:
-   void activate();
    void about();
    void clear();
    void guiTimerUpdate();
-   void on_pushButtonFileSelect_clicked();
-   void on_pushButtonStop_clicked();
-   void on_pushButtonConvert_clicked();
-   void on_templateChangeIndex(const QString& text);
+   void onConvert();
+   void onSourceFileSelect();
+   void onStop();
+   void onTemplateChangeIndex(const QString& text);
    void selectSource();
    void selectTarget();
 private:
-   QString m_homeDir;
-   QString m_storageFile;
    Ui::MainWindow *ui;
    Converter* m_converter;
    QLabel* m_statusMessage;
index 5ed2f0c8f3abe0156a8eb243b46e0d25a115cc90..21bcb38cb3c3e4fe11cf1c10d1699660712bf9a8 100644 (file)
@@ -71,7 +71,7 @@
               <widget class="QComboBox" name="comboBoxTemplate">
                <property name="minimumSize">
                 <size>
-                 <width>315</width>
+                 <width>316</width>
                  <height>0</height>
                 </size>
                </property>
                </item>
               </layout>
              </item>
-             <item>
-              <widget class="QPushButton" name="pushButtonActivate">
-               <property name="text">
-                <string>&amp;Activate template</string>
-               </property>
-              </widget>
-             </item>
              <item>
               <spacer name="horizontalSpacer_5">
                <property name="orientation">
index d2d7863da9a490b704058e1cb2250e5ea8de008c..4aa94bfeaf0ccef463477042f5bd6050044bd381 100644 (file)
@@ -45,3 +45,6 @@ TRANSLATIONS = reimgconvert_de.ts
 CODECFORSRC = UTF-8
 
 OTHER_FILES +=
+
+DISTFILES += \
+    ReImgConvert.html
index 2c49d40e50d2a7c9a2a5ba6fa30e0fec57cdd5ee..8c3c37524159a8988df4d254040b48654b1ed32c 100644 (file)
@@ -100,7 +100,7 @@ bool ReFileUtils::deleteTree(const QString& path, bool withBase,
                        if (info.isDir()) {
                                if (!deleteTree(full, false, logger))
                                        rc = false;
-                else if (_rmdir(I18N::s2b(full)) != 0) {
+                               else if (_rmdir(I18N::s2b(full)) != 0) {
                                        rc = false;
                                        if (logger != NULL)
                                                logger->logv(LOG_ERROR, LOC_DELETE_TREE_1,
@@ -118,7 +118,7 @@ bool ReFileUtils::deleteTree(const QString& path, bool withBase,
                        }
                }
        }
-    if (withBase && (_rmdir(I18N::s2b(path))) != 0) {
+       if (withBase && (_rmdir(I18N::s2b(path))) != 0) {
                rc = false;
                logger->logv(LOG_ERROR, LOC_DELETE_TREE_3,
                        "cannot delete directory (%d): %s", errno, I18N::s2b(path).constData());
@@ -178,7 +178,7 @@ QByteArray ReFileUtils::extensionOf(const char* filename) {
                                if ((cc = filename[ix - 1]) != '/' && cc != '\\')
                                        rc.append(filename + ix);
                                break;
-            } else if (cc == '\\' || cc == '/')
+                       } else if (cc == '\\' || cc == '/')
                                break;
                        ix--;
                }
@@ -222,6 +222,21 @@ bool ReFileUtils::isAbsolutPath(const char* path) {
        return rc;
 }
 
+/**
+ * Checks whether path is a root directory.
+ *
+ * @param path the path to inspect
+ * @return             <code>true</code>: path is a root directory, e.g. "/" or "c:\\"
+ */
+bool ReFileUtils::isRootDir(const char* path)
+{
+#if defined __linux__
+       return path[0] == OS_SEPARATOR && path[1] == '\0';
+#elif defined WIN32
+       return isalpha(path[0]) &&  path[1] == ':' && path[2] == '\\' && path[3] = '\0';
+#endif
+}
+
 /**
  * Extracts the node of a filename.
  *
@@ -659,7 +674,7 @@ QByteArray ReFileUtils::tempFile(const char* node, const char* parent,
        rc += node;
        struct stat info;
        if (deleteIfExists && stat(rc.constData(), &info) == 0)
-        _unlink(rc.constData());
+               _unlink(rc.constData());
        return rc;
 }
 
index a378ce86a66014aa6d1bdca8b25c8a9ff2c2ef36..23b021e3aa85d9a2c9809e228673b1fc3ea35d50 100644 (file)
@@ -36,6 +36,7 @@ public:
        static QByteArray extensionOf(const char* filename);
        static bool isAbsolutPath(const QString& path);
        static bool isAbsolutPath(const char* path);
+       static bool isRootDir(const char* path);
        /** Returns a path with native separators.
         * QT under windows can operator with 2 separators: '\\' and '/'.
         * '\\' is the native separator.
index 785a0727adcb1d2153fb735bd271ccfb18c430c6..2458f1f547bf78f77585ea08f95e053bcc20ce00 100644 (file)
@@ -473,6 +473,63 @@ void ReQStringUtils::skipExpected(const ReString& text, QChar expected,
        }
 }
 
+/**
+ * Returns a readable string for a duration given by <code>clock_t</code>.
+ *
+ * @param duration     a duration measured by <code>clock()</code>
+ *
+ * @return     a string describing the duration.
+ */
+QString ReQStringUtils::readableDuration(clock_t duration){
+       QString rc;
+       char buffer[128];
+       int cl_per_sec = CLOCKS_PER_SEC;
+       double duration2 = (double) duration / CLOCKS_PER_SEC;
+       if (duration2 < 60.0){
+               rc = QString::number(duration2, 'f', 3) + " sec";
+       } else if (duration2 < 3600.0){
+               int duration3 = int(duration2);
+               snprintf(buffer, sizeof buffer, "%d:%02d",
+                                duration3 / 60, duration3 % 60);
+               rc = buffer;
+       } else if (duration2 < 3600.0 * 24){
+               int duration3 = int(duration2);
+               snprintf(buffer, sizeof buffer, "%d:%02d:%02d",
+                                duration3 / 3600, duration3 % 3600 / 60,
+                                duration3 % 60);
+               rc = buffer;
+       } else {
+               int duration3 = int(duration2);
+               snprintf(buffer, sizeof buffer, "%d:%02d:%02d:%02d",
+                                int(duration3 / (3600*24)),
+                                int(duration3 % (3600*24) / 3600), int(duration3 % 3600 / 60),
+                                int(duration3 % 60));
+               rc = buffer;
+       }
+       return rc;
+}
+
+/**
+ * Returns a readable string for a filesize.
+ *
+ * @param filesize     the filesize to convert
+ * @return     a filesize expression (number and unit) with at least 3 significant digits.
+ */
+QString ReQStringUtils::readableSize(int64_t filesize){
+       QString rc;
+       if (filesize < 1000){
+               rc = QString::number(filesize) + " B";
+       } else if (filesize < 1000*1000){
+               rc = QString::number(filesize / 1e3, 'f', 3) + " kB";
+       } else if (filesize < 1000*1000*1000){
+               rc = QString::number(filesize / 1e6, 'f', 3) + " MB";
+       } else if (filesize < int64_t(1000)*1000*1000*1000){
+               rc = QString::number(filesize / 1e9, 'f', 3) + " GB";
+       } else {
+               rc = QString::number(filesize / 1e12, 'f', 3) + " TB";
+       }
+       return rc;
+}
 /**
  * @brief Converts a ReString into an utf-8 string
  *
index f90372a389866af1c7301f5cf1f2a927663fa32a..eca88b86cf058566407a216c4fba38bcf40e444b 100644 (file)
@@ -71,6 +71,8 @@ public:
                return path;
 #endif
        }
+       static QString readableDuration(clock_t duration);
+       static QString readableSize(int64_t filesize);
        static bool replacePlaceholders(QString& text,
                const QMap<QString, QString>& placeholders, QString* error);
        static void skipExpected(const ReString& text, QChar expected, int& index,
index 831279cf2754483eca9ce633bb7074fd616e563b..860914b85987a29e47e3f3f979c3a2ff2254dd94 100644 (file)
@@ -168,8 +168,6 @@ bool ReTest::assertEquals(const ReString& expected, const ReString& current,
  */
 bool ReTest::assertEquals(const char* expected, const char* current,
        const char* file, int lineNo) {
-       int len1 = strlen(expected);
-       int len2 = strlen(current);
        bool equal = strcmp(expected, current) == 0;
        if (!equal) {
                if (strchr(expected, '\n') != NULL || strchr(current, '\n')) {
index 998ec7b4d6597793c297fea08420a3d2ab095c6c..0bc2b41667a9a2c21a6e40d7057563d8e0959d02 100644 (file)
@@ -44,12 +44,12 @@ static void testBase() {
        void testReWriter();
        void testReFile();
        void testReMatcher();
+       testReQStringUtil();
        testReProgArgs();
        testReProcess();
        testReRandomizer();
        testReFileUtils();
        testReMatcher();
-       testReQStringUtil();
        testReFile();
        if (s_allTest) {
                testReProcess();
@@ -101,9 +101,9 @@ static void testOs() {
        testReCryptFileSystem();
 }
 void allTests() {
+       testBase();
        testOs();
        testExpr();
-       testBase();
        testGui();
        if (s_allTest) {
                testBase();
index f32ae4beaa68c7634c8262310fae96a4bbb9b5d9..35e523df3ad870999b6c245f5d558577ebf1ed64 100644 (file)
@@ -205,8 +205,37 @@ public:
                list.clear();
                checkEqu("", ReQStringUtils::longestPrefix(list));
        }
+       void testReadableSize(){
+               checkEqu("0 B", ReQStringUtils::readableSize(0));
+               checkEqu("10 B", ReQStringUtils::readableSize(10));
+               checkEqu("999 B", ReQStringUtils::readableSize(999));
+               checkEqu("1.000 kB", ReQStringUtils::readableSize(1000));
+               checkEqu("1.001 kB", ReQStringUtils::readableSize(1001));
+               checkEqu("999.999 kB", ReQStringUtils::readableSize(999999));
+               checkEqu("1.000 MB", ReQStringUtils::readableSize(1000000));
+               checkEqu("1.001 MB", ReQStringUtils::readableSize(1001000));
+               checkEqu("999.999 MB", ReQStringUtils::readableSize(999999000));
+               checkEqu("1.000 GB", ReQStringUtils::readableSize(1000000000));
+               checkEqu("1.001 GB", ReQStringUtils::readableSize(1001000000));
+               checkEqu("999.999 GB", ReQStringUtils::readableSize(999999000000L));
+               checkEqu("1.000 TB", ReQStringUtils::readableSize(1000000000000L));
+               checkEqu("1.001 TB", ReQStringUtils::readableSize(1001000000000l));
+       }
+       void testReadableDuration(){
+#define clf(sec) clock_t(clock_t((sec) * CLOCKS_PER_SEC))
+               checkEqu("0.000 sec", ReQStringUtils::readableDuration(clf(0.0)));
+               checkEqu("1.234 sec", ReQStringUtils::readableDuration(clf(1.234)));
+               checkEqu("59.250 sec", ReQStringUtils::readableDuration(clf(59.250)));
+               checkEqu("1:01", ReQStringUtils::readableDuration(clf(61.251)));
+               checkEqu("59:59", ReQStringUtils::readableDuration(clf(59*60+59.499)));
+               checkEqu("1:00:01", ReQStringUtils::readableDuration(clf(1*3600+1.99)));
+               checkEqu("23:59:59", ReQStringUtils::readableDuration(clf(24*3600-0.1)));
+               checkEqu("1:02:03:04", ReQStringUtils::readableDuration(clf(24*3600+2*3600+3*60+4.0)));
+       }
 
        virtual void runTests(void) {
+               testReadableSize();
+               testReadableDuration();
                testLongestPrefix();
                testLengtOfTime();
                testLengtOfDate();
index 9186a17ac68cc3eea2b590bdfd8f41a7a705a691..23fa19d84c6fda4cbe5f7197ae9410cbd888aa75 100644 (file)
@@ -15,8 +15,8 @@
 class ReGuiQueueItem {
 public:
        enum WidgetType {
-               Undef, LabelText, NewTableRow, ListEnd,
-               LogMessage, ReadyMessage,
+               Undef, LabelText, NewTableRow, ListEnd, ListAppendToCurrent,
+               LogMessage, ReadyMessage, StatusLine,
                UserDefined1, UserDefined2
        };
 
index 50a68f33a0bd88ae41ecab2a67b3c4d5e7f2592f..e21b7e80672cac9daf9179e64b7b585c2741c487 100644 (file)
@@ -12,6 +12,7 @@
 #include "base/rebase.hpp"
 #include "gui/regui.hpp"
 #include <QDesktopWidget>
+#include <QCoreApplication>
 enum {
        LOC_INIT_FOR_WRITE_1 = LOC_FIRST_OF(LOC_STATESTORAGE), // 12001
        LOC_INIT_FOR_READ_1,    // 12002
@@ -199,22 +200,31 @@ bool ReStateStorage::initForWrite() {
 void ReStateStorage::restore(QComboBox* combo, const char* name,
        bool withCurrentText) {
        if (initForRead()) {
-               QByteArray keyPrefix = fullname(name) + ".item";
-               int ix = 0;
-               QByteArray key;
-               while (true) {
-                       key = keyPrefix + QByteArray::number(ix);
-                       if (!m_map.contains(key))
-                               break;
-                       combo->addItem(m_map.value(key));
-                       ix++;
-               }
-               key = fullname(name) + ".text";
-               if (!withCurrentText)
-                       combo->setCurrentText("");
-               else {
-                       if (m_map.contains(key))
-                               combo->setCurrentText(m_map.value(key));
+               if (! combo->isEditable()){
+                       QByteArray key = fullname(name) + ".index";
+                       if (m_map.contains(key)){
+                               QString value = m_map[key];
+                               int index = atoi(value.toLatin1().constData());
+                               combo->setCurrentIndex(index);
+                       }
+               } else {
+                       QByteArray keyPrefix = fullname(name) + ".item";
+                       int ix = 0;
+                       QByteArray key;
+                       while (true) {
+                               key = keyPrefix + QByteArray::number(ix);
+                               if (!m_map.contains(key))
+                                       break;
+                               combo->addItem(m_map.value(key));
+                               ix++;
+                       }
+                       key = fullname(name) + ".text";
+                       if (!withCurrentText)
+                               combo->setCurrentText("");
+                       else {
+                               if (m_map.contains(key))
+                                       combo->setCurrentText(m_map.value(key));
+                       }
                }
        }
 }
@@ -300,15 +310,19 @@ void ReStateStorage::store(const QComboBox* combo, const char* name,
        bool withCurrentText) {
        if (initForWrite()) {
                QByteArray key(fullname(name));
-               for (int ii = 0; ii < combo->count(); ii++) {
-                       *m_stream << key << ".item" << ii << "=" << combo->itemText(ii)
-                               << endl;
-               }
-               if (withCurrentText) {
-                       *m_stream << key << ".text=" << combo->currentText() << endl;
+               if (! combo->isEditable()){
+                       *m_stream << key << ".index=" << combo->currentIndex()  << endl;
+               } else {
+                       for (int ii = 0; ii < combo->count(); ii++) {
+                               *m_stream << key << ".item" << ii << "=" << combo->itemText(ii)
+                                                 << endl;
+                       }
+                       if (withCurrentText) {
+                               *m_stream << key << ".text=" << combo->currentText() << endl;
+                       }
                }
-               m_stream->flush();
        }
+       m_stream->flush();
 }
 
 /**
@@ -340,3 +354,65 @@ void ReStateStorage::store(const QMainWindow* window) {
                m_stream->flush();
        }
 }
+
+/**
+ * Constructor.
+ *
+ * @param applicationName      the name of the application. Defines the node
+ *                                                     of the home directory
+ * @param homeDirBase          "": the user's home directory will be used<br>
+ *                                                     otherwise: the home directory will created in this
+ *                                                     directory
+ * @param maxLogFiles          max. count of (rotating) log files
+ * @param maxLogSize           max. size of the (rotating) log files
+ */
+ReHomeApplication::ReHomeApplication(const char* applicationName,
+                                                                                  const QString& homeDirBase,
+                                                                                  int maxLogFiles, int maxLogSize,
+                                                                        QWidget *parent = NULL) :
+       QMainWindow(parent),
+       m_applicationName(applicationName),
+       m_homeDir(buildHomeDir(homeDirBase, "." + m_applicationName)),
+       m_logger(),
+       m_storageFile()
+{
+       m_logger.buildStandardAppender(I18N::s2b(m_homeDir) + "logger.",
+                                                                  maxLogSize, maxLogFiles);
+       m_storageFile = m_homeDir + OS_SEPARATOR_STR + "status.conf";
+       m_storageFile = m_homeDir + "state.conf";
+       QCoreApplication* app = QCoreApplication::instance();
+       QObject::connect(app, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
+}
+
+void ReHomeApplication::aboutToQuit()
+{
+       m_logger.log(LOG_INFO, 999, "exit");
+}
+
+/**
+ * Build the home directory path name.
+ *
+ * @param homeDirBase          "": the user's home directory will be used<br>
+ *                                                     otherwise: the home directory will created in this
+ *                                                     directory
+ * @param node
+ * @return
+ */
+QString ReHomeApplication::buildHomeDir(QString homeDirBase, const QString& node){
+       QString homeDir;
+       if (homeDirBase.isEmpty()){
+                         homeDir = QDir::home().absoluteFilePath(node);
+       } else if (ReFileUtils::isRootDir(homeDirBase.toLatin1().constData())){
+               homeDir = homeDirBase + node;
+       }
+       ReQStringUtils::chomp(homeDir, OS_SEPARATOR);
+       QDir home(homeDir);
+       if (!home.exists()){
+        if (!home.mkpath(homeDir)){
+               homeDir = home.tempPath() + node;
+               home.mkpath(homeDir);
+        }
+       }
+       ReQStringUtils::ensureLastChar(homeDir, OS_SEPARATOR);
+       return homeDir;
+}
index b44de8b338de2300592267071e91b8bfa9c9fe4d..075fec20868f21614ee57b4c436e9949cc043b96 100644 (file)
@@ -55,4 +55,26 @@ private:
        ReLogger* m_logger;
 };
 
+/**
+ * An abstract base class for applications which stores their data in a home directory.
+ */
+class ReHomeApplication : public QMainWindow{
+public:
+       ReHomeApplication(const char* applicationName,
+                                                const QString& homeDir, int maxLogFiles, int maxLogSize,
+                                         QWidget *parent);
+public slots:
+       /**
+        * Callback method called when the application is closed.
+        */
+       void aboutToQuit();
+public:
+       static QString buildHomeDir(QString homeDirBase, const QString& node);
+protected:
+       QByteArray m_applicationName;
+       QString m_homeDir;
+       ReLogger m_logger;
+       QString m_storageFile;
+};
+
 #endif /* GUI_RESTATESTORAGE_HPP_ */