From: hama
Date: Sun, 17 Jan 2016 23:42:57 +0000 (+0100)
Subject: reimgconvert works
X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=f0c7d97223adfb31b64b36477dff3795d65f2b20;p=reqt
reimgconvert works
---
diff --git a/appl/reimgconvert/ReImgConvert.html b/appl/reimgconvert/ReImgConvert.html
new file mode 100644
index 0000000..f32ef37
--- /dev/null
+++ b/appl/reimgconvert/ReImgConvert.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+Release Notes
+
+
+- v1.0 (2016.01.15)
+ - Basic version. User language is English only.
+
+
+
+
+
+User Manual
+Purpose:
+This program allows to convert image files (pictures) into another format.
+Conversion is done for the dimensions (width/height) and/or the quality/file
+size.
+
+Simple Mode and Detailed Mode
+There are two modes of processing:
+
+- Simple mode: You define the maximum width and maximum height of a
+ picture. The converted file will respect these limits.
+- Detailed mode: The maximum dimensions for the three format types
+ (portrait, landscape, square) must be defined.
+
+
+
+Templates
+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.
+
+
+Conversion Details
+
+If an image is lower than the given limits it will be copied without
+conversion.
+This program is only the GUI of the image conversion program "convert"
+from the package ImageMagick.
+Installation
+
+Copy reimgconvert into a directory inserted in the execution
+path, e.g. /usr/local/bin and give it the right "executable".
+Install the packages qt5 and imagemagick.
+
+Requirements
+
+
+
+
+Program Documentation
+
+Programming Features
+
+This is a example for a complete QT application with the following features:
+
+- C++
+- QT
+- Threads
+- Using GUI wizzard
+- Changing GUI elements from a 2nd thread
+- Abstract class as interface
+- Calling an external program
+
+
+
+(Un)License: Public Domain
+
+You can use and modify this file without any restriction.
+Do what you want.
+No warranties and disclaimer of any damages.
+More info: http://unlicense.org
+The latest sources: https://github.com/republib.
+
+
+
+
diff --git a/appl/reimgconvert/converter.cpp b/appl/reimgconvert/converter.cpp
index 0eb8c5f..50bbde8 100644
--- a/appl/reimgconvert/converter.cpp
+++ b/appl/reimgconvert/converter.cpp
@@ -34,20 +34,9 @@
*
* 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
- *
- *
- *
+ * 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 @@
* Threads
* Using GUI wizzard
* Abstract class as interface
- * Calling an external script
- * Lists the output of the external list line by line at the moment
- * the line is created
+ * Calling an external program
*
*
* @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;
-}
-
diff --git a/appl/reimgconvert/converter.hpp b/appl/reimgconvert/converter.hpp
index c0585d8..0ba19a1 100644
--- a/appl/reimgconvert/converter.hpp
+++ b/appl/reimgconvert/converter.hpp
@@ -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,
diff --git a/appl/reimgconvert/main.cpp b/appl/reimgconvert/main.cpp
index 051c685..0d45da3 100644
--- a/appl/reimgconvert/main.cpp
+++ b/appl/reimgconvert/main.cpp
@@ -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();
}
diff --git a/appl/reimgconvert/mainwindow.cpp b/appl/reimgconvert/mainwindow.cpp
index c258dda..5f8006d 100644
--- a/appl/reimgconvert/mainwindow.cpp
+++ b/appl/reimgconvert/mainwindow.cpp
@@ -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(
+ 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 true
*/
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.
*
diff --git a/appl/reimgconvert/mainwindow.hpp b/appl/reimgconvert/mainwindow.hpp
index cef44ba..05ececf 100644
--- a/appl/reimgconvert/mainwindow.hpp
+++ b/appl/reimgconvert/mainwindow.hpp
@@ -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;
diff --git a/appl/reimgconvert/mainwindow.ui b/appl/reimgconvert/mainwindow.ui
index 5ed2f0c..21bcb38 100644
--- a/appl/reimgconvert/mainwindow.ui
+++ b/appl/reimgconvert/mainwindow.ui
@@ -71,7 +71,7 @@
- 315
+ 316
0
@@ -213,13 +213,6 @@
- -
-
-
- &Activate template
-
-
-
-
diff --git a/appl/reimgconvert/reimgconvert.pro b/appl/reimgconvert/reimgconvert.pro
index d2d7863..4aa94bf 100644
--- a/appl/reimgconvert/reimgconvert.pro
+++ b/appl/reimgconvert/reimgconvert.pro
@@ -45,3 +45,6 @@ TRANSLATIONS = reimgconvert_de.ts
CODECFORSRC = UTF-8
OTHER_FILES +=
+
+DISTFILES += \
+ ReImgConvert.html
diff --git a/base/ReFileUtils.cpp b/base/ReFileUtils.cpp
index 2c49d40..8c3c375 100644
--- a/base/ReFileUtils.cpp
+++ b/base/ReFileUtils.cpp
@@ -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
true
: 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;
}
diff --git a/base/ReFileUtils.hpp b/base/ReFileUtils.hpp
index a378ce8..23b021e 100644
--- a/base/ReFileUtils.hpp
+++ b/base/ReFileUtils.hpp
@@ -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.
diff --git a/base/ReQStringUtils.cpp b/base/ReQStringUtils.cpp
index 785a072..2458f1f 100644
--- a/base/ReQStringUtils.cpp
+++ b/base/ReQStringUtils.cpp
@@ -473,6 +473,63 @@ void ReQStringUtils::skipExpected(const ReString& text, QChar expected,
}
}
+/**
+ * Returns a readable string for a duration given by clock_t
.
+ *
+ * @param duration a duration measured by clock()
+ *
+ * @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
*
diff --git a/base/ReQStringUtils.hpp b/base/ReQStringUtils.hpp
index f90372a..eca88b8 100644
--- a/base/ReQStringUtils.hpp
+++ b/base/ReQStringUtils.hpp
@@ -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& placeholders, QString* error);
static void skipExpected(const ReString& text, QChar expected, int& index,
diff --git a/base/ReTest.cpp b/base/ReTest.cpp
index 831279c..860914b 100644
--- a/base/ReTest.cpp
+++ b/base/ReTest.cpp
@@ -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')) {
diff --git a/cunit/allTests.cpp b/cunit/allTests.cpp
index 998ec7b..0bc2b41 100644
--- a/cunit/allTests.cpp
+++ b/cunit/allTests.cpp
@@ -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();
diff --git a/cunit/cuReQStringUtils.cpp b/cunit/cuReQStringUtils.cpp
index f32ae4b..35e523d 100644
--- a/cunit/cuReQStringUtils.cpp
+++ b/cunit/cuReQStringUtils.cpp
@@ -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();
diff --git a/gui/ReGuiQueue.hpp b/gui/ReGuiQueue.hpp
index 9186a17..23fa19d 100644
--- a/gui/ReGuiQueue.hpp
+++ b/gui/ReGuiQueue.hpp
@@ -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
};
diff --git a/gui/ReStateStorage.cpp b/gui/ReStateStorage.cpp
index 50a68f3..e21b7e8 100644
--- a/gui/ReStateStorage.cpp
+++ b/gui/ReStateStorage.cpp
@@ -12,6 +12,7 @@
#include "base/rebase.hpp"
#include "gui/regui.hpp"
#include
+#include
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
+ * 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
+ * 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;
+}
diff --git a/gui/ReStateStorage.hpp b/gui/ReStateStorage.hpp
index b44de8b..075fec2 100644
--- a/gui/ReStateStorage.hpp
+++ b/gui/ReStateStorage.hpp
@@ -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_ */