diff --git a/.appveyor.yml b/.appveyor.yml index 66a4c85..40d0245 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,6 +27,7 @@ install: - set PATH=%PATH%;%QTDIR%/bin build_script: + - git checkout master # re-attach to master to satisfy auto version tooling - mkdir build - cd build - cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_PREFIX_PATH=%QTDIR% .. diff --git a/.gitignore b/.gitignore index f512be1..168c78a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ core *.sav* .directory TEST-DATA +SAV* +OLD* + diff --git a/.travis.yml b/.travis.yml index 45fec74..a6fd501 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,12 @@ before_install: install: - sudo apt-get -y install qt59base qt59svg qt59tools qt59translations zlib1g-dev xvfb - sudo apt-get -y install barcode libqrencode-dev + - wget https://downloads.sourceforge.net/project/zint/zint/2.6.3/zint-2.6.3_final.tar.gz && tar xzf zint-2.6.3_final.tar.gz && cd zint-2.6.3.src && mkdir build && cd build && cmake .. && make && sudo make install && cd ../.. - source /opt/qt5*/bin/qt5*-env.sh script: + - git fetch --unshallow # restore repository depth to properly count commits in auto versioning + - git checkout master # re-attach to master to satisfy auto versioning - mkdir build - cd build - cmake .. -DCMAKE_INSTALL_PREFIX=/usr @@ -24,7 +27,7 @@ after_success: - wget -c "https://github.com/jimevins/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - chmod a+x linuxdeployqt*.AppImage - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH - - export LD_LIBRARY_PATH=/opt/qt*/lib/:$LD_LIBRARY_PATH + - export LD_LIBRARY_PATH=/opt/qt*/lib/:/usr/local/lib:$LD_LIBRARY_PATH - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage - find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq diff --git a/.version.in b/.version.in new file mode 100644 index 0000000..f3e763d --- /dev/null +++ b/.version.in @@ -0,0 +1 @@ +@VERSION_STRING@ diff --git a/CMakeLists.txt b/CMakeLists.txt index 885788f..e03fcb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,26 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/" set (WEBSITE "glabels.org") set (BUG_WEBSITE "https://github.com/jimevins/glabels-qt/issues") +execute_process( + COMMAND git symbolic-ref --short HEAD + RESULT_VARIABLE BRANCH_VALID + OUTPUT_VARIABLE BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${BRANCH_VALID} STREQUAL "0") + set (BRANCH "Unkonwn") +endif () + +execute_process( + COMMAND git rev-list --count ${BRANCH} + RESULT_VARIABLE COMMIT_COUNT_VALID + OUTPUT_VARIABLE COMMIT_COUNT + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${COMMIT_COUNT_VALID} STREQUAL "0") + set (COMMIT_COUNT "?") +endif () + execute_process( COMMAND git log -1 --format=%h RESULT_VARIABLE COMMIT_HASH_VALID @@ -38,11 +58,16 @@ if (NOT ${COMMIT_DATE_VALID} STREQUAL "0") endif () # Uncomment for snapshots, comment for releases -set(VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}-snapshot (${COMMIT_HASH} ${COMMIT_DATE})") +set(VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}-${BRANCH}${COMMIT_COUNT}") # Uncomment for releases, comment for snapshots #set(VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +set(LONG_VERSION_STRING "${VERSION_STRING} (${COMMIT_HASH} ${COMMIT_DATE})") + +# Auto-generate version file +configure_file (.version.in VERSION @ONLY) + #======================================= # Packaging Information @@ -147,7 +172,7 @@ add_subdirectory (data) #======================================= message (STATUS "") message (STATUS "Project name ............ " ${CMAKE_PROJECT_NAME}) -message (STATUS "Project version ......... " ${VERSION_STRING}) +message (STATUS "Project version ......... " ${LONG_VERSION_STRING}) message (STATUS "Installation prefix ..... " ${CMAKE_INSTALL_PREFIX}) message (STATUS "Source code location .... " ${glabels_SOURCE_DIR}) message (STATUS "CMake version ........... " ${CMAKE_VERSION}) diff --git a/backends/barcode/Backends.cpp b/backends/barcode/Backends.cpp index 040e81e..96fc83b 100644 --- a/backends/barcode/Backends.cpp +++ b/backends/barcode/Backends.cpp @@ -205,7 +205,7 @@ namespace glabels glbarcode::Factory::registerType( "zint::kix", Zint::Kix::create ); glbarcode::Factory::registerType( "zint::ean", Zint::Ean::create ); glbarcode::Factory::registerType( "zint::gmtx", Zint::Gmtx::create ); - glbarcode::Factory::registerType( "zint::gs1128", Zint::Gs1128::create ); + glbarcode::Factory::registerType( "zint::gs1-128", Zint::Gs1128::create ); glbarcode::Factory::registerType( "zint::rss14", Zint::Rss14::create ); glbarcode::Factory::registerType( "zint::rssltd", Zint::Rssltd::create ); glbarcode::Factory::registerType( "zint::rssexp", Zint::Rssexp::create ); diff --git a/backends/barcode/QrEncode.cpp b/backends/barcode/QrEncode.cpp index 2772660..2fd8bcf 100644 --- a/backends/barcode/QrEncode.cpp +++ b/backends/barcode/QrEncode.cpp @@ -80,7 +80,6 @@ namespace glabels QRcode_free( qrcode ); - QRcode_clearCache(); return true; } diff --git a/backends/merge/Merge.cpp b/backends/merge/Merge.cpp index 993c0d1..8277ac5 100644 --- a/backends/merge/Merge.cpp +++ b/backends/merge/Merge.cpp @@ -31,7 +31,7 @@ namespace glabels /// /// Constructor /// - Merge::Merge( const Merge* merge ) : mSource(merge->mSource) + Merge::Merge( const Merge* merge ) : mId(merge->mId), mSource(merge->mSource) { foreach ( Record* record, merge->mRecordList ) { diff --git a/backends/merge/Text.cpp b/backends/merge/Text.cpp index 9d40cc3..93d5350 100644 --- a/backends/merge/Text.cpp +++ b/backends/merge/Text.cpp @@ -45,7 +45,7 @@ namespace glabels Text::Text( const Text* merge ) : Merge( merge ), mDelimeter(merge->mDelimeter), mLine1HasKeys(merge->mLine1HasKeys), - mNFieldsMax(merge->mNFieldsMax) + mKeys(merge->mKeys), mNFieldsMax(merge->mNFieldsMax) { } diff --git a/cmake/Modules/FindLibZint.cmake b/cmake/Modules/FindLibZint.cmake index b44d80a..9f1a754 100644 --- a/cmake/Modules/FindLibZint.cmake +++ b/cmake/Modules/FindLibZint.cmake @@ -11,7 +11,7 @@ set (LIBZINT_DEFINITIONS "") find_path (LIBZINT_INCLUDE_DIR NAMES zint.h) -find_library (LIBZINT_LIBRARIES NAMES zint ) +find_library (LIBZINT_LIBRARY NAMES zint ) if (LIBZINT_LIBRARY AND LIBZINT_INCLUDE_DIR) diff --git a/docs/BUILD-INSTRUCTIONS-LINUX.md b/docs/BUILD-INSTRUCTIONS-LINUX.md index 6571fee..8f19722 100644 --- a/docs/BUILD-INSTRUCTIONS-LINUX.md +++ b/docs/BUILD-INSTRUCTIONS-LINUX.md @@ -1,7 +1,8 @@ gLabels Linux Build Instructions ================================ -## Prerequisites +## General +### Prerequisites - g++ - CMake 2.8.12+ @@ -12,7 +13,7 @@ gLabels Linux Build Instructions > may also need to be installed. Development packages are usually named something like > libraryName-dev or libraryName-devel. -## Compile and Install +### Compile and Install gLabels uses the CMake meta build system. Use the following commands to build and install gLabels: @@ -24,3 +25,45 @@ $ cmake .. $ make $ sudo make install + +## Example: Ubuntu 19.04 + +### Installing Prerequisites +``` +sudo apt install cmake +sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev zlib1g-dev +``` +_QREncode (Optional)_ +``` +sudo apt install pkgconf libqrencode-dev +``` +_Zint (Optional)_ + +Install zint from source: +``` +wget https://downloads.sourceforge.net/project/zint/zint/2.6.3/zint-2.6.3_final.tar.gz +tar xzf zint-2.6.3_final.tar.gz +cd zint-2.6.3.src/ +mkdir build && cd build && cmake .. && make +sudo make install +``` +_GNU Barcode (Optional)_ + +As of version 0.99, GNU Barcode no longer installs its library. So install 0.98 from source: +``` +wget https://ftp.gnu.org/gnu/barcode/barcode-0.98.tar.gz +tar xzf barcode-0.98.tar.gz +cd barcode-0.98/ +./configure && make +sudo make install +``` +### Compile and Install gLabels + +``` +$ cd glabels-qt +$ mkdir build +$ cd build +$ cmake .. +$ make +$ sudo make install +``` diff --git a/docs/BUILD-INSTRUCTIONS-MACOS.md b/docs/BUILD-INSTRUCTIONS-MACOS.md new file mode 100644 index 0000000..5f0b440 --- /dev/null +++ b/docs/BUILD-INSTRUCTIONS-MACOS.md @@ -0,0 +1,20 @@ +gLabels MacOS Build Instructions +================================ + +## Prerequisites + +``` +brew install cmake +brew install qt +``` + +## Compile and Install + +
+cd glabels_source_directory
+mkdir build
+cd build
+cmake -D CMAKE_PREFIX_PATH=/usr/local/opt/qt  ..
+make
+sudo make install
+
diff --git a/docs/SUBSTITUTION-FIELD-SPEC.md b/docs/SUBSTITUTION-FIELD-SPEC.md index b69ff38..ddfc2ef 100644 --- a/docs/SUBSTITUTION-FIELD-SPEC.md +++ b/docs/SUBSTITUTION-FIELD-SPEC.md @@ -17,6 +17,28 @@ modifiers = modifier [ ":" modifiers ] ; modifier = format-modifier | default-value-modifier | new-line-modifier; ``` + +Field Names +----------- +Field names can refer to either [Document Merge Fields](#document-merge-fields) or [User Variables](#user-variables). If a document merge field and a user variable share the same name, the document merge field takes precidence. Its syntax is simply: + +```ebnf +field-name = merge-field-name | user-variable-name ; +``` + +### Document Merge Fields +Document merge fields are the primary source of substitution fields. A document merge field represents a field from an external data source, such as a CSV file. The valid syntax for a document merge field name is determined by the merge source, with the following exception. Merge field names cannot contain either a colon (":") or closing curly bracket ("}"). + +### User Variables +Substitution fields can also refer to user variables. The syntax for valid user variable names is + +```ebnf +letter = "a" | "b" | ... | "z" | "A" | ... | "Z"; +digit = "0" | "1" | "2" | ... | "9"; +user-variable-name = ( letter | "_" ) , { letter | digit | "_" } ; +``` + + Modifiers --------- ### Format-Modifier (`%`) @@ -89,20 +111,3 @@ ${CITY} ${STATE} ${ZIP} `${ADDR2}` would be printed on its own line, only if it is set and non-empty. -Document Merge Fields ---------------------- -Document merge fields are the primary source of substitution fields. A document merge field represents a field from an external data source, such as a CSV file. - -User Defined Variables ----------------------- -Alternatively, merge fields can refer to user defined variables. - -Built-In Variables ------------------- -Potentially, merge fields may also refer to built-in variables. Candidates include: - - LABEL_NUMBER - - PAGE_NUMBER - - DATE - - TIME - - FILE_NAME - diff --git a/docs/TODO.md b/docs/TODO.md index 6eb3cb8..039f682 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -29,3 +29,15 @@ Add support for "Continuous Roll" labels Write help documentation ------------------------ + + +To Do List for gLabels 4.1 -- 2019-03-17 +======================================== + +Create a "built-in" merge source +-------------------------------- +As an alternative to external merge sources, let the user edit the merge source +in situ. The user can add fields. The user can add records using those fields. +The user created database will become part of the glabels project file. +For simple databases, such as a small address list, this would be much easier +to deal with than creating it externally. diff --git a/glabels-batch/main.cpp b/glabels-batch/main.cpp index e857f44..8240d02 100644 --- a/glabels-batch/main.cpp +++ b/glabels-batch/main.cpp @@ -60,7 +60,7 @@ int main( int argc, char **argv ) QCoreApplication::setOrganizationName( "glabels.org" ); QCoreApplication::setOrganizationDomain( "glabels.org" ); QCoreApplication::setApplicationName( "glabels-batch-qt" ); - QCoreApplication::setApplicationVersion( glabels::model::Version::STRING ); + QCoreApplication::setApplicationVersion( glabels::model::Version::LONG_STRING ); // // Setup translators @@ -175,7 +175,7 @@ int main( int argc, char **argv ) } glabels::model::PageRenderer renderer( model ); - renderer.setNCopies( 1 ); + renderer.setNCopies( parser.value( "copies" ).toInt() ); renderer.setStartLabel( parser.value( "first" ).toInt() - 1 ); renderer.setPrintOutlines( parser.isSet( "outlines" ) ); renderer.setPrintCropMarks( parser.isSet( "crop-marks" ) ); diff --git a/glabels/AboutDialog.cpp b/glabels/AboutDialog.cpp index ef9ff99..acfb423 100644 --- a/glabels/AboutDialog.cpp +++ b/glabels/AboutDialog.cpp @@ -38,7 +38,7 @@ namespace glabels { setupUi( this ); - QString version = tr("Version") + " " + model::Version::STRING; + QString version = tr("Version") + " " + model::Version::LONG_STRING; QString description = tr("A program to create labels and business cards."); diff --git a/glabels/CMakeLists.txt b/glabels/CMakeLists.txt index 6679ac9..f8ba83f 100644 --- a/glabels/CMakeLists.txt +++ b/glabels/CMakeLists.txt @@ -13,9 +13,9 @@ set (glabels_sources ColorHistory.cpp ColorPaletteDialog.cpp ColorPaletteItem.cpp - ColorPaletteButtonItem.cpp ColorSwatch.cpp Cursors.cpp + EditVariableDialog.cpp FieldButton.cpp File.cpp Help.cpp @@ -40,6 +40,7 @@ set (glabels_sources TemplatePicker.cpp TemplatePickerItem.cpp UndoRedoModel.cpp + VariablesView.cpp ) set (glabels_qobject_headers @@ -51,7 +52,7 @@ set (glabels_qobject_headers ColorHistory.h ColorPaletteDialog.h ColorPaletteItem.h - ColorPaletteButtonItem.h + EditVariableDialog.h FieldButton.h File.h LabelEditor.h @@ -69,10 +70,12 @@ set (glabels_qobject_headers TemplateDesigner.h TemplatePicker.h UndoRedoModel.h + VariablesView.h ) set (glabels_forms ui/AboutDialog.ui + ui/EditVariableDialog.ui ui/MergeView.ui ui/ObjectEditor.ui ui/PreferencesDialog.ui @@ -95,6 +98,7 @@ set (glabels_forms ui/TemplateDesignerOneLayoutPage.ui ui/TemplateDesignerTwoLayoutPage.ui ui/TemplateDesignerApplyPage.ui + ui/VariablesView.ui ) set (glabels_resource_files diff --git a/glabels/ColorButton.cpp b/glabels/ColorButton.cpp index 1abb9a0..28ea4c8 100644 --- a/glabels/ColorButton.cpp +++ b/glabels/ColorButton.cpp @@ -48,8 +48,9 @@ namespace glabels void ColorButton::init( const QString& defaultLabel, - const QColor& defaultColor, - const QColor& color ) + const QColor& defaultColor, + const QColor& color, + bool showUseFieldButton ) { mDefaultColor = defaultColor; mColorNode = model::ColorNode( color ); @@ -61,7 +62,10 @@ namespace glabels setText( "" ); setCheckable( true ); - mDialog = new ColorPaletteDialog( defaultLabel, defaultColor, color ); + mDialog = new ColorPaletteDialog( defaultLabel, + defaultColor, + color, + showUseFieldButton ); connect( this, SIGNAL(toggled(bool)), this, SLOT(onButtonToggled(bool)) ); connect( mDialog, SIGNAL(colorChanged(model::ColorNode,bool)), @@ -124,15 +128,10 @@ namespace glabels } - void ColorButton::setKeys( const QList keyList ) + void ColorButton::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mDialog->setKeys( keyList ); - } - - - void ColorButton::clearKeys() - { - mDialog->clearKeys(); + mDialog->setKeys( merge, variables ); } diff --git a/glabels/ColorButton.h b/glabels/ColorButton.h index eb16611..81ba63d 100644 --- a/glabels/ColorButton.h +++ b/glabels/ColorButton.h @@ -58,13 +58,18 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void init( const QString& defaultLabel, const QColor& defaultColor, const QColor& color ); + void init( const QString& defaultLabel, + const QColor& defaultColor, + const QColor& color, + bool showUseFieldButton = true ); + void setColorNode( model::ColorNode colorNode ); void setColor( QColor color ); void setToDefault(); model::ColorNode colorNode(); - void setKeys( const QList keyList ); - void clearKeys(); + + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// diff --git a/glabels/ColorHistory.cpp b/glabels/ColorHistory.cpp index 60ff08f..ba564de 100644 --- a/glabels/ColorHistory.cpp +++ b/glabels/ColorHistory.cpp @@ -46,23 +46,25 @@ namespace glabels } - void ColorHistory::addColor( const QColor &color ) + void ColorHistory::addColor( const QColor &color, const QString& name ) { - QList colorList = readColorList(); + QString nameColor = name + ":" + color.name(); + + QStringList nameColorList = readNameColorList(); // Remove any occurrences of this color already in list - colorList.removeAll( color ); + nameColorList.removeAll( nameColor ); // Now add to list - colorList.append( color ); + nameColorList.append( nameColor ); // Remove oldest colors, if size exceeds current max - while ( colorList.size() > MAX_COLORS ) + while ( nameColorList.size() > MAX_COLORS ) { - colorList.removeFirst(); + nameColorList.removeFirst(); } - writeColorList( colorList ); + writeNameColorList( nameColorList ); emit changed(); } @@ -70,55 +72,83 @@ namespace glabels QList ColorHistory::getColors() { - return readColorList(); - } - - - QColor ColorHistory::getColor( int id ) - { - QList colors = readColorList(); - return colors[id]; - } - - - QList ColorHistory::readColorList() - { - QStringList defaultList; - QSettings settings; - - settings.beginGroup( "ColorHistory" ); - QStringList colorNameList = settings.value( "colors", defaultList ).toStringList(); - settings.endGroup(); - QList colorList; - foreach ( QString colorName, colorNameList ) + + for ( QString& nameColor : readNameColorList() ) { - colorList << QColor( colorName ); - } - - // Remove oldest colors, if size exceeds current max - while ( colorList.size() > MAX_COLORS ) - { - colorList.removeFirst(); + QStringList v = nameColor.split( ':' ); + if ( v.size() == 2 ) + { + colorList << QColor( v[1] ); + } + else if ( v.size() == 1 ) + { + // Old-style, no name + colorList << QColor( v[0] ); + } + else + { + // Should not happen + qWarning() << "Invalid color history."; + } } return colorList; } - void ColorHistory::writeColorList( const QList& colorList ) + QStringList ColorHistory::getNames() { - // Build name list - QStringList colorNameList; - foreach ( QColor color, colorList ) + QStringList nameList; + + for ( QString& nameColor : readNameColorList() ) { - colorNameList << color.name(); + QStringList v = nameColor.split( ':' ); + if ( v.size() == 2 ) + { + nameList << v[0]; + } + else if ( v.size() == 1 ) + { + // Old-style, no name + nameList << QString(tr("color %1")).arg( v[0] ); + } + else + { + // Should not happen + qWarning() << "Invalid color history."; + } } + return nameList; + } + + + QStringList ColorHistory::readNameColorList() + { + QStringList defaultList; + QSettings settings; + + settings.beginGroup( "ColorHistory" ); + QStringList nameColorList = settings.value( "colors", defaultList ).toStringList(); + settings.endGroup(); + + // Remove oldest colors, if size exceeds current max + while ( nameColorList.size() > MAX_COLORS ) + { + nameColorList.removeFirst(); + } + + return nameColorList; + } + + + void ColorHistory::writeNameColorList( const QStringList& nameColorList ) + { // Save QSettings settings; settings.beginGroup( "ColorHistory" ); - settings.setValue( "colors", colorNameList ); + settings.setValue( "colors", nameColorList ); settings.endGroup(); } diff --git a/glabels/ColorHistory.h b/glabels/ColorHistory.h index b74b3b4..eb14812 100644 --- a/glabels/ColorHistory.h +++ b/glabels/ColorHistory.h @@ -60,17 +60,17 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void addColor( const QColor &color ); + void addColor( const QColor& color, const QString& name ); QList getColors(); - QColor getColor( int id ); + QStringList getNames(); ///////////////////////////////// // Private Methods ///////////////////////////////// private: - QList readColorList(); - void writeColorList( const QList& colorList ); + QStringList readNameColorList(); + void writeNameColorList( const QStringList& nameColorList ); ///////////////////////////////// diff --git a/glabels/ColorPaletteButtonItem.cpp b/glabels/ColorPaletteButtonItem.cpp deleted file mode 100644 index ce8d5bf..0000000 --- a/glabels/ColorPaletteButtonItem.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* ColorPaletteButtonItem.cpp - * - * Copyright (C) 2014 Jim Evins - * - * This file is part of gLabels-qt. - * - * gLabels-qt 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. - * - * gLabels-qt 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 gLabels-qt. If not, see . - */ - -#include "ColorPaletteButtonItem.h" - -#include -#include - - -namespace glabels -{ - - // - // Private - // - namespace - { - const int border = 4; - const int hBox = 25; - const int outlineWidthPixels = 1; - } - - - /// - /// Constructor From Data - /// - ColorPaletteButtonItem::ColorPaletteButtonItem( const QString& text, QWidget* parent ) - : QWidget(parent), mText(text), mHover(false) - { - setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); - setMinimumSize( hBox+2*border+1, hBox+2*border+1 ); - } - - - /// - /// Paint Event - /// - void ColorPaletteButtonItem::paintEvent( QPaintEvent* event ) - { - QPainter painter(this); - - // - // Draw background - // - if ( isEnabled() && mHover ) - { - QLinearGradient gradient( 0, 0, 0, height() ); - gradient.setColorAt( 0, palette().color( QPalette::Highlight ).lighter() ); - gradient.setColorAt( 1, palette().color( QPalette::Highlight ) ); - painter.setBrush( QBrush( gradient ) ); - - QPen pen( palette().color( QPalette::Text ) ); - pen.setWidth( outlineWidthPixels ); - painter.setPen( pen ); - - painter.drawRect( 0, 0, width()-1, height()-1 ); - } - - // - // Draw text - // - painter.setBrush( QBrush( Qt::NoBrush ) ); - - if ( isEnabled() && mHover ) - { - painter.setPen( QPen( palette().color( QPalette::HighlightedText ) ) ); - } - else - { - painter.setPen( QPen( palette().color( QPalette::Text ) ) ); - } - - QRect textRect( border, border, width()-2*border, hBox ); - - painter.drawText( textRect, Qt::AlignLeft|Qt::AlignVCenter, mText ); - } - - - /// - /// Enter Event - /// - void ColorPaletteButtonItem::enterEvent( QEvent* event ) - { - mHover = true; - update(); - } - - - /// - /// Leave Event - /// - void ColorPaletteButtonItem::leaveEvent( QEvent* event ) - { - mHover = false; - update(); - } - - - /// - /// Mouse Press Event - /// - void ColorPaletteButtonItem::mousePressEvent( QMouseEvent* event ) - { - emit activated(); - } - -} // namespace glabels diff --git a/glabels/ColorPaletteDialog.cpp b/glabels/ColorPaletteDialog.cpp index 71feb32..9ad496a 100644 --- a/glabels/ColorPaletteDialog.cpp +++ b/glabels/ColorPaletteDialog.cpp @@ -18,14 +18,16 @@ * along with gLabels-qt. If not, see . */ + #include "ColorPaletteDialog.h" #include -#include -#include -#include #include +#include +#include +#include #include +#include #include @@ -83,6 +85,7 @@ namespace glabels ColorPaletteDialog::ColorPaletteDialog( const QString& defaultLabel, const QColor& defaultColor, const QColor& color, + bool showUseFieldButton, QWidget* parent ) : QDialog( parent ) { @@ -99,14 +102,12 @@ namespace glabels vLayout->setContentsMargins( 0, 0, 0, 0 ); vLayout->setSpacing( 0 ); - auto* defaultButton = new ColorPaletteButtonItem( defaultLabel ); - connect( defaultButton, SIGNAL(activated()), this, SLOT(onDefaultItemActivated()) ); - vLayout->addWidget( defaultButton ); - - QFrame* hline1 = new QFrame; - hline1->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline1->setLineWidth( 1 ); - vLayout->addWidget( hline1 ); + // + // Construct Standard Colors Grid + // + auto* standardColorsGroup = new QGroupBox( tr("Standard Colors") ); + standardColorsGroup->setAlignment( Qt::AlignHCenter ); + vLayout->addWidget( standardColorsGroup ); auto* mainPaletteLayout = new QGridLayout(); mainPaletteLayout->setSpacing( 0 ); @@ -119,17 +120,20 @@ namespace glabels ColorPaletteItem* item = new ColorPaletteItem( i, QColor( mColorTable[i].colorSpec ), tr(mColorTable[i].trname) ); - connect( item, SIGNAL(activated(int)), this, SLOT(onPaletteItemActivated(int)) ); + connect( item, SIGNAL(activated(int)), + this, SLOT(onPaletteItemActivated(int)) ); mainPaletteLayout->addWidget( item, iRow, iCol ); } } - vLayout->addLayout( mainPaletteLayout ); + standardColorsGroup->setLayout( mainPaletteLayout ); - QFrame* hline2 = new QFrame; - hline2->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline2->setLineWidth( 1 ); - vLayout->addWidget( hline2 ); + // + // Construct Recent Colors Grid + // + auto* recentColorsGroup = new QGroupBox( tr("Recent Colors") ); + recentColorsGroup->setAlignment( Qt::AlignHCenter ); + vLayout->addWidget( recentColorsGroup ); auto* customPaletteLayout = new QHBoxLayout(); customPaletteLayout->setSpacing( 0 ); @@ -137,40 +141,49 @@ namespace glabels { mHistoryItem[iCol] = new ColorPaletteItem( iCol, QColor(0,0,0,0), "" ); mHistoryItem[iCol]->setEnabled( false ); - connect( mHistoryItem[iCol], SIGNAL(activated(int)), this, SLOT(onHistoryItemActivated(int)) ); + connect( mHistoryItem[iCol], SIGNAL(activated(int)), + this, SLOT(onHistoryItemActivated(int)) ); customPaletteLayout->addWidget( mHistoryItem[iCol] ); } - vLayout->addLayout( customPaletteLayout ); + recentColorsGroup->setLayout( customPaletteLayout ); - QFrame* hline3 = new QFrame; - hline3->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline3->setLineWidth( 1 ); - vLayout->addWidget( hline3 ); - - ColorPaletteButtonItem* customColorButton = new ColorPaletteButtonItem( tr("Custom color...") ); - connect( customColorButton, SIGNAL(activated()), this, SLOT(onCustomColorItemActivated()) ); + // + // Construct Default (e.g. "No Fill") Button + // + auto* defaultColorButton = new QPushButton( defaultLabel ); + defaultColorButton->setAutoDefault( false ); + defaultColorButton->setDefault( false ); + connect( defaultColorButton, SIGNAL(clicked()), this, SLOT(onDefaultButtonClicked()) ); + vLayout->addWidget( defaultColorButton ); + + // + // Construct Custom Color Button + // + auto* customColorButton = new QPushButton( tr("Custom color...") ); + customColorButton->setAutoDefault( false ); + customColorButton->setDefault( false ); + connect( customColorButton, SIGNAL(clicked()), this, SLOT(onCustomColorButtonClicked()) ); vLayout->addWidget( customColorButton ); - QFrame* hline4 = new QFrame; - hline4->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline4->setLineWidth( 1 ); - vLayout->addWidget( hline4 ); + // + // Construct "Use field" Button + // + if ( showUseFieldButton ) + { + mFieldButton = new FieldButton(); + mFieldButton->setText( tr("Use substitution field") ); + mFieldButton->setAutoDefault( false ); + mFieldButton->setDefault( false ); + connect( mFieldButton, SIGNAL(keySelected(QString)), this, SLOT(onKeySelected(QString)) ); + vLayout->addWidget( mFieldButton ); + } + else + { + mFieldButton = nullptr; + } - mMergeFieldCombo = new QComboBox(); - mMergeFieldCombo->addItem( tr("Merge key...") ); - mMergeFieldCombo->setMinimumSize( 34, 34 ); - mMergeFieldCombo->setFrame( false ); - mMergeFieldCombo->setEnabled( false ); - connect( mMergeFieldCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboIndexChanged(int)) ); - vLayout->addWidget( mMergeFieldCombo ); - - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* model = qobject_cast(mMergeFieldCombo->model()); - QStandardItem* item = model->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - setLayout( vLayout ); loadCustomColorHistory(); @@ -183,41 +196,47 @@ namespace glabels } - void ColorPaletteDialog::setKeys( const QStringList& keyList ) + void ColorPaletteDialog::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mKeys = keyList; - - // Clear old keys, (all entries, except item 0) - for ( int index = mMergeFieldCombo->count()-1; index > 0; index-- ) + if (mFieldButton) { - mMergeFieldCombo->removeItem( index ); - } - - // Add new keys - if ( keyList.size() > 0 ) - { - mMergeFieldCombo->addItems( keyList ); - mMergeFieldCombo->setEnabled( true ); - } - else - { - mMergeFieldCombo->setEnabled( false ); + mFieldButton->setKeys( merge, variables ); } } - void ColorPaletteDialog::clearKeys() + void ColorPaletteDialog::onPaletteItemActivated( int id ) { - - for ( int index = mMergeFieldCombo->count()-1; index > 0; index-- ) + model::ColorNode newColorNode; + newColorNode.setField( false ); + newColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); + newColorNode.setKey( "" ); + + if ( newColorNode != mColorNode ) { - mMergeFieldCombo->removeItem( index ); + mColorNode = newColorNode; + + mColorHistory->addColor( mColorNode.color(), mColorTable[id].trname ); + + emit colorChanged( mColorNode, false ); + accept(); } - mMergeFieldCombo->setEnabled( false ); } - void ColorPaletteDialog::onDefaultItemActivated() + void ColorPaletteDialog::onHistoryItemActivated( int id ) + { + mColorNode.setField( false ); + mColorNode.setColor( mColorHistory->getColors()[id] ); + mColorNode.setKey( "" ); + + emit colorChanged( mColorNode, false ); + accept(); + } + + + void ColorPaletteDialog::onDefaultButtonClicked() { mColorNode.setField( false ); mColorNode.setColor( mDefaultColor ); @@ -228,29 +247,7 @@ namespace glabels } - void ColorPaletteDialog::onPaletteItemActivated( int id ) - { - mColorNode.setField( false ); - mColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); - mColorNode.setKey( "" ); - - emit colorChanged( mColorNode, false ); - accept(); - } - - - void ColorPaletteDialog::onHistoryItemActivated( int id ) - { - mColorNode.setField( false ); - mColorNode.setColor( mColorHistory->getColor(id) ); - mColorNode.setKey( "" ); - - emit colorChanged( mColorNode, false ); - accept(); - } - - - void ColorPaletteDialog::onCustomColorItemActivated() + void ColorPaletteDialog::onCustomColorButtonClicked() { QColorDialog dlg( mColorNode.color(), this ); dlg.setWindowTitle( tr("Custom Color") ); @@ -267,7 +264,10 @@ namespace glabels { mColorNode = newColorNode; - mColorHistory->addColor( mColorNode.color() ); + // TRANSLATORS + //: %1 = color specification in hex. String must not contain a colon (:). + mColorHistory->addColor( mColorNode.color(), + QString(tr("Custom Color %1")).arg(mColorNode.color().name()) ); emit colorChanged( mColorNode, false ); accept(); @@ -284,12 +284,13 @@ namespace glabels void ColorPaletteDialog::loadCustomColorHistory() { + QStringList nameList = mColorHistory->getNames(); QList colorList = mColorHistory->getColors(); int id = 0; foreach ( QColor color, colorList ) { - mHistoryItem[id]->setColor( id, color, QString(tr("Custom color #%1").arg(id+1) ) ); + mHistoryItem[id]->setColor( id, color, nameList[id] ); mHistoryItem[id]->setEnabled( true ); id++; } @@ -302,25 +303,14 @@ namespace glabels } - void ColorPaletteDialog::onComboIndexChanged( int index ) + void ColorPaletteDialog::onKeySelected( QString key ) { - if ( index != 0 ) - { - mColorNode.setField( true ); - mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); - mColorNode.setKey( mKeys[index-1] ); + mColorNode.setField( true ); + mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); + mColorNode.setKey( key ); - emit colorChanged( mColorNode, false ); - accept(); - } - } - - - void ColorPaletteDialog::showEvent( QShowEvent* event ) - { - mMergeFieldCombo->setCurrentIndex( 0 ); - - QDialog::showEvent( event ); + emit colorChanged( mColorNode, false ); + accept(); } } // namespace glabels diff --git a/glabels/ColorPaletteDialog.h b/glabels/ColorPaletteDialog.h index db5f7e1..6e1a22c 100644 --- a/glabels/ColorPaletteDialog.h +++ b/glabels/ColorPaletteDialog.h @@ -24,11 +24,10 @@ #include "ColorHistory.h" #include "ColorPaletteItem.h" -#include "ColorPaletteButtonItem.h" +#include "FieldButton.h" #include "model/ColorNode.h" -#include #include @@ -50,6 +49,7 @@ namespace glabels ColorPaletteDialog( const QString& defaultLabel, const QColor& defaultColor, const QColor& color, + bool showUseFieldButton = true, QWidget* parent = nullptr ); @@ -64,25 +64,23 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void setColorNode( const model::ColorNode& colorNode ); - void setKeys( const QStringList& keyList ); - void clearKeys(); + void setColorNode( const model::ColorNode& colorNode ); + + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// // Slots ///////////////////////////////// private slots: - void onDefaultItemActivated(); void onPaletteItemActivated( int id ); void onHistoryItemActivated( int id ); - void onCustomColorItemActivated(); + void onDefaultButtonClicked(); + void onCustomColorButtonClicked(); + void onKeySelected( QString key ); void onColorHistoryChanged(); - void onComboIndexChanged( int index ); - protected: - void showEvent( QShowEvent* event ) override; - ///////////////////////////////// // Private Methods @@ -111,8 +109,7 @@ namespace glabels ColorHistory* mColorHistory; ColorPaletteItem* mHistoryItem[PALETTE_COLS]; - QComboBox* mMergeFieldCombo; - QStringList mKeys; + FieldButton* mFieldButton; }; diff --git a/glabels/EditVariableDialog.cpp b/glabels/EditVariableDialog.cpp new file mode 100644 index 0000000..a685e73 --- /dev/null +++ b/glabels/EditVariableDialog.cpp @@ -0,0 +1,220 @@ +/* EditVariableDialog.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "EditVariableDialog.h" + +#include "model/Settings.h" + +#include + + +namespace +{ + // All variable types. (must be in sorted order) + const QVector allTypes = { + glabels::model::Variable::Type::STRING, + glabels::model::Variable::Type::INTEGER, + glabels::model::Variable::Type::FLOATING_POINT, + glabels::model::Variable::Type::COLOR + }; + + // All variable increments. (must be in sorted order) + const QVector allIncrements = { + glabels::model::Variable::Increment::NEVER, + glabels::model::Variable::Increment::PER_ITEM, + glabels::model::Variable::Increment::PER_COPY, + glabels::model::Variable::Increment::PER_PAGE + }; +} + + +namespace glabels +{ + + /// + /// Constructor + /// + EditVariableDialog::EditVariableDialog( QWidget *parent ) + : QDialog(parent) + { + setupUi( this ); + + QRegularExpression reIdentifier( "[a-zA-Z_][a-zA-Z_0-9]*" ); + nameEdit->setValidator( new QRegularExpressionValidator( reIdentifier ) ); + + colorValueButton->init( tr("Default"), + QColor(0,0,0,255), + QColor(0,0,0,255), + false ); + + for ( auto type : allTypes ) + { + typeCombo->addItem( model::Variable::typeToI18nString( type ) ); + } + + for ( auto type : allIncrements ) + { + incrementCombo->addItem( model::Variable::incrementToI18nString( type ) ); + } + + stepSizeEdit->setText( "1" ); + } + + + /// + /// Set variable + /// + void EditVariableDialog::setVariable( const model::Variable& variable ) + { + typeCombo->setCurrentIndex( static_cast(variable.type()) ); + nameEdit->setText( variable.name() ); + valueEdit->setText( variable.initialValue() ); + colorValueButton->setColor( QColor( variable.initialValue() ) ); + incrementCombo->setCurrentIndex( static_cast(variable.increment()) ); + stepSizeEdit->setText( variable.stepSize() ); + + updateControls(); + } + + + /// + /// Get variable + /// + model::Variable EditVariableDialog::variable() const + { + return model::Variable( static_cast(typeCombo->currentIndex()), + nameEdit->text(), + valueEdit->text(), + static_cast(incrementCombo->currentIndex()), + stepSizeEdit->text() ); + } + + + /// + /// nameEdit Changed + /// + void EditVariableDialog::onNameEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// typeCombo Changed + /// + void EditVariableDialog::onTypeComboChanged() + { + updateControls(); + } + + + /// + /// valueEdit Changed + /// + void EditVariableDialog::onValueEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// colorValueButton Changed + /// + void EditVariableDialog::onColorValueButtonChanged() + { + valueEdit->setText( colorValueButton->colorNode().color().name() ); + validateCurrentInputs(); + } + + + /// + /// incrementCombo Changed + /// + void EditVariableDialog::onIncrementComboChanged() + { + updateControls(); + } + + + /// + /// stepSizeEdit Changed + /// + void EditVariableDialog::onStepSizeEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// update controls + /// + void EditVariableDialog::updateControls() + { + auto type = static_cast(typeCombo->currentIndex()); + auto increment = static_cast(incrementCombo->currentIndex()); + + switch (type) + { + + case model::Variable::Type::INTEGER: + valueEdit->setValidator( new QIntValidator() ); + stepSizeEdit->setValidator( new QIntValidator() ); + break; + + case model::Variable::Type::FLOATING_POINT: + valueEdit->setValidator( new QDoubleValidator() ); + stepSizeEdit->setValidator( new QDoubleValidator() ); + break; + + default: + valueEdit->setValidator( nullptr ); + stepSizeEdit->setValidator( nullptr ); + break; + + } + + colorValueButton->setVisible( type == model::Variable::Type::COLOR ); + + bool isNumeric = ( type == model::Variable::Type::INTEGER ) || + ( type == model::Variable::Type::FLOATING_POINT ); + + incrementGroup->setVisible( isNumeric ); + stepSizeLabel->setEnabled( isNumeric && (increment != model::Variable::Increment::NEVER) ); + stepSizeEdit->setEnabled( isNumeric && (increment != model::Variable::Increment::NEVER) ); + + validateCurrentInputs(); + } + + + /// + /// validate current inputs + /// + void EditVariableDialog::validateCurrentInputs() + { + bool hasValidIdentifier = nameEdit->hasAcceptableInput(); + bool hasValidValue = valueEdit->hasAcceptableInput(); + bool hasValidStepSize = stepSizeEdit->hasAcceptableInput(); + + bool isValid = hasValidIdentifier && hasValidValue && hasValidStepSize; + buttonBox->button(QDialogButtonBox::Ok)->setEnabled( isValid ); + } + + +} // namespace glabels diff --git a/glabels/ColorPaletteButtonItem.h b/glabels/EditVariableDialog.h similarity index 59% rename from glabels/ColorPaletteButtonItem.h rename to glabels/EditVariableDialog.h index 0ffc627..6c71d61 100644 --- a/glabels/ColorPaletteButtonItem.h +++ b/glabels/EditVariableDialog.h @@ -1,6 +1,6 @@ -/* ColorPaletteButtonItem.h +/* EditVariableDialog.h * - * Copyright (C) 2014 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -18,21 +18,21 @@ * along with gLabels-qt. If not, see . */ -#ifndef ColorPaletteButtonItem_h -#define ColorPaletteButtonItem_h +#ifndef EditVariableDialog_h +#define EditVariableDialog_h -#include -#include +#include "ui_EditVariableDialog.h" +#include "model/Variable.h" namespace glabels { /// - /// Color Palette Item + /// New Label Dialog Widget /// - class ColorPaletteButtonItem : public QWidget + class EditVariableDialog : public QDialog, public Ui_EditVariableDialog { Q_OBJECT @@ -40,36 +40,37 @@ namespace glabels // Life Cycle ///////////////////////////////// public: - ColorPaletteButtonItem( const QString& text, QWidget* parent = nullptr ); + EditVariableDialog( QWidget *parent = nullptr ); ///////////////////////////////// - // Signals + // Public methods ///////////////////////////////// - signals: - void activated(); - + void setVariable( const model::Variable& variable ); + model::Variable variable() const; + ///////////////////////////////// - // Event handlers + // Slots ///////////////////////////////// - protected: - void paintEvent( QPaintEvent* event ) override; - void enterEvent( QEvent* event ) override; - void leaveEvent( QEvent* event ) override; - void mousePressEvent( QMouseEvent* event ) override; - + private slots: + void onNameEditChanged(); + void onTypeComboChanged(); + void onValueEditChanged(); + void onColorValueButtonChanged(); + void onIncrementComboChanged(); + void onStepSizeEditChanged(); + ///////////////////////////////// - // Private Data + // Private methods ///////////////////////////////// - private: - QString mText; + void updateControls(); + void validateCurrentInputs(); - bool mHover; }; } -#endif // ColorPaletteButtonItem_h +#endif // EditVariableDialog_h diff --git a/glabels/FieldButton.cpp b/glabels/FieldButton.cpp index f6c25b6..483e770 100644 --- a/glabels/FieldButton.cpp +++ b/glabels/FieldButton.cpp @@ -1,6 +1,6 @@ /* FieldButton.cpp * - * Copyright (C) 2014-2016 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -30,79 +30,61 @@ namespace glabels /// /// Constructor /// - FieldButton::FieldButton( QWidget* parent ) - : QComboBox(parent) + FieldButton::FieldButton( QWidget* parent ) : QPushButton(parent) { setEnabled( false ); - - connect( this, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndexChanged(int)) ); + setMenu( &mMenu ); + + connect( &mMenu, SIGNAL(triggered(QAction*)), + this, SLOT(onMenuActionTriggered(QAction*)) ); } - void FieldButton::setName( const QString& name ) - { - mName = name; - if ( count() == 0 ) - { - addItem( mName ); - } - else - { - setItemText( 0, mName ); - } - - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* itemModel = qobject_cast(model()); - QStandardItem* item = itemModel->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - } - - - void FieldButton::setKeys( const QStringList& keyList ) + /// + /// Set Keys + /// + void FieldButton::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { // Clear old keys - clear(); - addItem( mName ); + mMenu.clear(); + + // Add merge keys, if any + mMenu.addSection( tr("Merge fields") ); + for ( auto& key : merge->keys() ) + { + auto* action = mMenu.addAction( QString( "${%1}" ).arg( key ) ); + action->setData( key ); + } + if ( merge->keys().empty() ) + { + auto* action = mMenu.addAction( "None" ); + action->setEnabled( false ); + } - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* itemModel = qobject_cast(model()); - QStandardItem* item = itemModel->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - - // Add new keys - if ( keyList.size() > 0 ) + // Add variable keys, if any + mMenu.addSection( tr("Variables") ); + for ( auto& key : variables->keys() ) { - addItems( keyList ); - setEnabled( true ); + auto* action = mMenu.addAction( QString( "${%1}" ).arg( key ) ); + action->setData( key ); } - else + if ( variables->keys().empty() ) { - setEnabled( false ); + auto* action = mMenu.addAction( "None" ); + action->setEnabled( false ); } + + setEnabled( !merge->keys().empty() || !variables->keys().empty() ); } - void FieldButton::clearKeys() - { - clear(); - addItem( mName ); - - setEnabled( false ); - } - - - /// - /// onMenuKeySelected slot + /// onMenuActionTriggered slot /// - void FieldButton::onIndexChanged( int index ) + void FieldButton::onMenuActionTriggered( QAction* action ) { - if ( index > 0 ) - { - emit keySelected( itemText(index) ); - - setCurrentIndex( 0 ); - } + emit keySelected( action->data().toString() ); } } // namespace glabels diff --git a/glabels/FieldButton.h b/glabels/FieldButton.h index 1da2d57..230a86d 100644 --- a/glabels/FieldButton.h +++ b/glabels/FieldButton.h @@ -1,6 +1,6 @@ /* FieldButton.h * - * Copyright (C) 2014-2016 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -22,8 +22,13 @@ #define FieldButton_h -#include -#include +#include "model/Variables.h" +#include "merge/Merge.h" + +#include +#include +#include +#include namespace glabels @@ -32,7 +37,7 @@ namespace glabels /// /// Field Button /// - class FieldButton : public QComboBox + class FieldButton : public QPushButton { Q_OBJECT @@ -54,23 +59,22 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void setName( const QString& name = "" ); - void setKeys( const QStringList& keyList ); - void clearKeys(); + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// // Slots ///////////////////////////////// private slots: - void onIndexChanged( int index ); + void onMenuActionTriggered( QAction* action ); ///////////////////////////////// // Private Data ///////////////////////////////// private: - QString mName; + QMenu mMenu; }; diff --git a/glabels/File.cpp b/glabels/File.cpp index 71fa160..5a35473 100644 --- a/glabels/File.cpp +++ b/glabels/File.cpp @@ -112,8 +112,6 @@ namespace glabels model::Model *model = model::XmlLabelParser::readFile( fileName ); if ( model ) { - model->setFileName( fileName ); - // Either apply to current window or open a new one if ( window->isEmpty() ) { @@ -152,8 +150,6 @@ namespace glabels model::Model *model = model::XmlLabelParser::readFile( fileName ); if ( model ) { - model->setFileName( fileName ); - // Either apply to current window or open a new one if ( window->isEmpty() ) { @@ -213,7 +209,8 @@ namespace glabels /// bool File::saveAs( MainWindow *window ) { - // Either use the saved CWD from a previous open/save or grab it from the path of the current file + // Either use the saved CWD from a previous open/save or grab it from the path + // of the current file. QString cwd = mCwd; if ( window->model() && !window->model()->fileName().isEmpty() ) { diff --git a/glabels/Icons.h b/glabels/Icons.h index 5f02ed2..37dd081 100644 --- a/glabels/Icons.h +++ b/glabels/Icons.h @@ -473,6 +473,16 @@ namespace glabels }; + class Variables : public QIcon + { + public: + Variables() + { + addPixmap( QPixmap( ":icons/flat/48x48/glabels-variables.svg" ) ); + } + }; + + class ZoomBestFit : public QIcon { public: diff --git a/glabels/LabelEditor.cpp b/glabels/LabelEditor.cpp index ef7fe05..85ede54 100644 --- a/glabels/LabelEditor.cpp +++ b/glabels/LabelEditor.cpp @@ -469,6 +469,10 @@ namespace glabels mResizeObject = handle->owner(); mResizeHandle = handle; mResizeHonorAspect = event->modifiers() & Qt::ControlModifier; + if ( mResizeObject->lockAspectRatio() ) + { + mResizeHonorAspect = !mResizeHonorAspect; + } mState = ArrowResize; } @@ -657,6 +661,7 @@ namespace glabels break; case ArrowResize: + mUndoRedoModel->checkpoint( tr("Resize") ); handleResizeMotion( xWorld, yWorld ); break; @@ -1157,7 +1162,7 @@ namespace glabels void LabelEditor::drawObjectsLayer( QPainter* painter ) { - mModel->draw( painter ); + mModel->draw( painter, true, nullptr, nullptr ); } diff --git a/glabels/MainWindow.cpp b/glabels/MainWindow.cpp index 15bfda9..42c939d 100644 --- a/glabels/MainWindow.cpp +++ b/glabels/MainWindow.cpp @@ -31,6 +31,7 @@ #include "PropertiesView.h" #include "StartupView.h" #include "UndoRedoModel.h" +#include "VariablesView.h" #include "model/Db.h" #include "model/Model.h" @@ -51,7 +52,8 @@ namespace EDITOR_PAGE_INDEX = 1, PROPERTIES_PAGE_INDEX = 2, MERGE_PAGE_INDEX = 3, - PRINT_PAGE_INDEX = 4, + VARIABLES_PAGE_INDEX = 4, + PRINT_PAGE_INDEX = 5, }; } @@ -62,7 +64,7 @@ namespace glabels /// /// Constructor /// - MainWindow::MainWindow() : mModel(nullptr) + MainWindow::MainWindow() : mModel(nullptr), mUndoRedoModel(nullptr) { setWindowIcon( Icons::Glabels() ); @@ -76,6 +78,7 @@ namespace glabels QWidget* editorPage = createEditorPage(); QWidget* propertiesPage = createPropertiesPage(); QWidget* mergePage = createMergePage(); + QWidget* variablesPage = createVariablesPage(); QWidget* printPage = createPrintPage(); // Table of contents widget @@ -141,6 +144,18 @@ namespace glabels mMergeAction = mContents->addWidget( mMergeButton ); group->addButton( mMergeButton ); + // Add "Variables" page + mPages->addWidget( variablesPage ); + mVariablesButton = new QToolButton( this ); + mVariablesButton->setIcon( Icons::Variables() ); + mVariablesButton->setText( tr("Variables") ); + mVariablesButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mVariablesButton->setCheckable( true ); + mVariablesButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mVariablesAction = mContents->addWidget( mVariablesButton ); + group->addButton( mVariablesButton ); + // Add "Print" page mPages->addWidget( printPage ); mPrintButton = new QToolButton( this ); @@ -175,6 +190,7 @@ namespace glabels connect( mEditorButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); connect( mPropertiesButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); connect( mMergeButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); + connect( mVariablesButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); connect( mPrintButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); connect( mLabelEditor, SIGNAL(zoomChanged()), this, SLOT(onZoomChanged()) ); connect( model::Settings::instance(), SIGNAL(changed()), this, SLOT(onSettingsChanged()) ); @@ -194,7 +210,16 @@ namespace glabels /// MainWindow::~MainWindow() { - // empty + if ( mUndoRedoModel ) + { + delete mUndoRedoModel; + } + if ( mModel ) + { + delete mModel->merge(); // Ownership of final Merge instance is ours + delete mModel->variables(); // Ownership of Variables instance is ours + delete mModel; + } } @@ -212,13 +237,14 @@ namespace glabels /// void MainWindow::setModel( model::Model* model ) { - mModel = model; + mModel = model; // Ownership passes to us mUndoRedoModel = new UndoRedoModel( mModel ); mPropertiesView->setModel( mModel, mUndoRedoModel ); mLabelEditor->setModel( mModel, mUndoRedoModel ); mObjectEditor->setModel( mModel, mUndoRedoModel ); - mMergeView->setModel( mModel , mUndoRedoModel ); + mMergeView->setModel( mModel, mUndoRedoModel ); + mVariablesView->setModel( mModel, mUndoRedoModel ); mPrintView->setModel( mModel ); mEditorButton->setChecked( true ); @@ -315,6 +341,11 @@ namespace glabels fileShowMergePageAction->setStatusTip( tr("Select project Merge mode") ); connect( fileShowMergePageAction, SIGNAL(triggered()), this, SLOT(fileShowMergePage()) ); + fileShowVariablesPageAction = new QAction( tr("&Variables") , this ); + fileShowVariablesPageAction->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ) ); + fileShowVariablesPageAction->setStatusTip( tr("Select project Variables mode") ); + connect( fileShowVariablesPageAction, SIGNAL(triggered()), this, SLOT(fileShowVariablesPage()) ); + fileShowPrintPageAction = new QAction( tr("&Print") , this ); fileShowPrintPageAction->setShortcut( QKeySequence::Print ); fileShowPrintPageAction->setStatusTip( tr("Select project Print mode") ); @@ -603,6 +634,7 @@ namespace glabels fileMenu->addAction( fileShowEditorPageAction ); fileMenu->addAction( fileShowPropertiesPageAction ); fileMenu->addAction( fileShowMergePageAction ); + fileMenu->addAction( fileShowVariablesPageAction ); fileMenu->addAction( fileShowPrintPageAction ); fileMenu->addSeparator(); fileMenu->addAction( fileTemplateDesignerAction ); @@ -815,6 +847,17 @@ namespace glabels } + /// + /// Create Variables Page + /// + QWidget* MainWindow::createVariablesPage() + { + mVariablesView = new VariablesView(); + + return mVariablesView; + } + + /// /// Create Print Page /// @@ -839,6 +882,7 @@ namespace glabels bool isEditorPage = mEditorButton->isChecked(); bool isPropertiesPage = mPropertiesButton->isChecked(); bool isMergePage = mMergeButton->isChecked(); + bool isVariablesPage = mVariablesButton->isChecked(); bool isPrintPage = mPrintButton->isChecked(); // What is the current selection state? @@ -851,6 +895,7 @@ namespace glabels mEditorAction->setVisible( !isWelcomePage ); mPropertiesAction->setVisible( !isWelcomePage ); mMergeAction->setVisible( !isWelcomePage ); + mVariablesAction->setVisible( !isWelcomePage ); mPrintAction->setVisible( !isWelcomePage ); // Recent file actions @@ -876,14 +921,45 @@ namespace glabels fileShowEditorPageAction->setEnabled( !isWelcomePage && !isEditorPage ); fileShowPropertiesPageAction->setEnabled( !isWelcomePage && !isPropertiesPage ); fileShowMergePageAction->setEnabled( !isWelcomePage && !isMergePage ); + fileShowVariablesPageAction->setEnabled( !isWelcomePage && !isVariablesPage ); fileShowPrintPageAction->setEnabled( !isWelcomePage && !isPrintPage ); fileTemplateDesignerAction->setEnabled( true ); fileCloseAction->setEnabled( true ); fileExitAction->setEnabled( true ); // Edit actions - editUndoAction->setEnabled( hasModel && mUndoRedoModel->canUndo() ); - editRedoAction->setEnabled( hasModel && mUndoRedoModel->canRedo() ); + if ( hasModel ) + { + if ( mUndoRedoModel->canUndo() ) + { + editUndoAction->setEnabled( true ); + /* Translators: %1 is the action description to undo. */ + editUndoAction->setText( QString( tr("Undo %1") ).arg( mUndoRedoModel->undoDescription() ) ); + } + else + { + editUndoAction->setEnabled( false ); + editUndoAction->setText( tr("Undo") ); + } + if ( mUndoRedoModel->canRedo() ) + { + editRedoAction->setEnabled( true ); + /* Translators: %1 is the action description to redo. */ + editRedoAction->setText( QString( tr("Redo %1") ).arg( mUndoRedoModel->redoDescription() ) ); + } + else + { + editRedoAction->setEnabled( false ); + editRedoAction->setText( tr("Redo") ); + } + } + else + { + editUndoAction->setEnabled( false ); + editUndoAction->setText( tr("Undo") ); + editRedoAction->setEnabled( false ); + editRedoAction->setText( tr("Redo") ); + } editCutAction->setEnabled( isEditorPage && hasSelection ); editCopyAction->setEnabled( isEditorPage && hasSelection ); editPasteAction->setEnabled( isEditorPage && canPaste ); @@ -1072,6 +1148,10 @@ namespace glabels { mPages->setCurrentIndex( MERGE_PAGE_INDEX ); } + else if ( mVariablesButton->isChecked() ) + { + mPages->setCurrentIndex( VARIABLES_PAGE_INDEX ); + } else if ( mPrintButton->isChecked() ) { mPages->setCurrentIndex( PRINT_PAGE_INDEX ); @@ -1168,6 +1248,15 @@ namespace glabels } + /// + /// File->Show Variables Page + /// + void MainWindow::fileShowVariablesPage() + { + mVariablesButton->setChecked( true ); + } + + /// /// File->Show Print Page /// diff --git a/glabels/MainWindow.h b/glabels/MainWindow.h index dc6a2a2..56e4fab 100644 --- a/glabels/MainWindow.h +++ b/glabels/MainWindow.h @@ -47,6 +47,7 @@ namespace glabels class PropertiesView; class StartupView; class UndoRedoModel; + class VariablesView; /// @@ -97,6 +98,7 @@ namespace glabels void fileShowEditorPage(); void fileShowPropertiesPage(); void fileShowMergePage(); + void fileShowVariablesPage(); void fileShowPrintPage(); void fileTemplateDesigner(); void fileClose(); @@ -175,6 +177,7 @@ namespace glabels QWidget* createEditorPage(); QWidget* createPropertiesPage(); QWidget* createMergePage(); + QWidget* createVariablesPage(); QWidget* createPrintPage(); void manageActions(); @@ -222,12 +225,14 @@ namespace glabels QToolButton* mEditorButton; QToolButton* mPropertiesButton; QToolButton* mMergeButton; + QToolButton* mVariablesButton; QToolButton* mPrintButton; QAction* mWelcomeAction; QAction* mEditorAction; QAction* mPropertiesAction; QAction* mMergeAction; + QAction* mVariablesAction; QAction* mPrintAction; QStackedWidget* mPages; @@ -237,6 +242,7 @@ namespace glabels ObjectEditor* mObjectEditor; PropertiesView* mPropertiesView; MergeView* mMergeView; + VariablesView* mVariablesView; PrintView* mPrintView; QLabel* zoomInfoLabel; @@ -249,6 +255,7 @@ namespace glabels QAction* fileShowEditorPageAction; QAction* fileShowPropertiesPageAction; QAction* fileShowMergePageAction; + QAction* fileShowVariablesPageAction; QAction* fileShowPrintPageAction; QAction* fileTemplateDesignerAction; QAction* fileCloseAction; diff --git a/glabels/MergeView.cpp b/glabels/MergeView.cpp index 0a1eea6..c932324 100644 --- a/glabels/MergeView.cpp +++ b/glabels/MergeView.cpp @@ -22,6 +22,8 @@ #include "merge/Factory.h" +#include "model/FileUtil.h" + #include #include #include @@ -63,14 +65,7 @@ namespace glabels mUndoRedoModel = undoRedoModel; // Initialize CWD - if ( model->fileName().isEmpty() ) - { - mCwd = "."; - } - else - { - mCwd = QFileInfo( model->fileName() ).absolutePath(); - } + mCwd = mModel->dirPath(); onMergeChanged(); connect( mModel, SIGNAL(mergeChanged()), this, SLOT(onMergeChanged()) ); @@ -87,26 +82,22 @@ namespace glabels mOldFormatComboIndex = index; formatCombo->setCurrentIndex( index ); + QString fn; + switch ( merge::Factory::idToType( mModel->merge()->id() ) ) { case merge::Factory::NONE: case merge::Factory::FIXED: locationLabel->setEnabled( false ); - locationButton->setEnabled( false ); - locationButton->setText( "" ); + locationLineEdit->setText( "" ); + locationBrowseButton->setVisible( false ); break; case merge::Factory::FILE: locationLabel->setEnabled( true ); - locationButton->setEnabled( true ); - if ( mModel->merge()->source().isEmpty() ) - { - locationButton->setText( "Select file..." ); - } - else - { - locationButton->setText( mModel->merge()->source() ); - } + fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), mModel->merge()->source() ); + locationLineEdit->setText( fn ); + locationBrowseButton->setVisible( true ); break; default: @@ -135,7 +126,8 @@ namespace glabels /// void MergeView::onMergeSourceChanged() { - locationButton->setText( mModel->merge()->source() ); + QString fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), mModel->merge()->source() ); + locationLineEdit->setText( fn ); recordsTable->clear(); recordsTable->setColumnCount( 0 ); @@ -185,7 +177,7 @@ namespace glabels /// /// Location button clicked handler /// - void MergeView::onLocationButtonClicked() + void MergeView::onLocationBrowseButtonClicked() { QString fileName = QFileDialog::getOpenFileName( this, diff --git a/glabels/MergeView.h b/glabels/MergeView.h index 8734994..e0ddead 100644 --- a/glabels/MergeView.h +++ b/glabels/MergeView.h @@ -67,7 +67,7 @@ namespace glabels void onMergeSelectionChanged(); void onFormatComboActivated(); - void onLocationButtonClicked(); + void onLocationBrowseButtonClicked(); void onSelectAllButtonClicked(); void onUnselectAllButtonClicked(); void onCellChanged( int iRow, int iCol ); diff --git a/glabels/ObjectEditor.cpp b/glabels/ObjectEditor.cpp index f61e9fe..0ddefde 100644 --- a/glabels/ObjectEditor.cpp +++ b/glabels/ObjectEditor.cpp @@ -30,12 +30,14 @@ #include "model/ModelImageObject.h" #include "model/ModelLineObject.h" #include "model/ModelTextObject.h" +#include "model/FileUtil.h" #include "model/Settings.h" #include "model/Size.h" #include "merge/Merge.h" #include +#include #include #include @@ -67,9 +69,9 @@ namespace glabels barcodeColorButton->init( tr("Default"), QColor(0,0,0,255), QColor(0,0,0,255) ); shadowColorButton->init( tr("Default"), QColor(0,0,0,255), QColor(0,0,0,255) ); - textInsertFieldCombo->setName( tr("Insert Field") ); - barcodeInsertFieldCombo->setName( tr("Insert Field") ); - imageFieldCombo->setName( tr("Key") ); + textInsertFieldButton->setText( tr("Insert substitution field") ); + barcodeInsertFieldButton->setText( tr("Insert substitution field") ); + imageFieldButton->setText( tr("Use substitution field") ); setEnabled( false ); hidePages(); @@ -93,11 +95,14 @@ namespace glabels this, SLOT(onSelectionChanged()) ); connect( mModel, SIGNAL(mergeSourceChanged()), - this, SLOT(onMergeSourceChanged()) ); + this, SLOT(onFieldsAvailableChanged()) ); + + connect( mModel, SIGNAL(variablesChanged()), + this, SLOT(onFieldsAvailableChanged()) ); onLabelSizeChanged(); onSelectionChanged(); - onMergeSourceChanged(); + onFieldsAvailableChanged(); } @@ -122,12 +127,12 @@ namespace glabels if ( filenameNode.isField() ) { - QString field = QString("${%1}").arg( filenameNode.data() ); - imageFilenameLineEdit->setText( field ); + imageFilenameLineEdit->setText( QString("${%1}").arg(filenameNode.data()) ); } else { - imageFilenameLineEdit->setText( filenameNode.data() ); + QString fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), filenameNode.data() ); + imageFilenameLineEdit->setText( fn ); } mBlocked = false; @@ -188,6 +193,7 @@ namespace glabels sizeWSpin->setValue( mObject->w().inUnits(mUnits) ); sizeHSpin->setValue( mObject->h().inUnits(mUnits) ); + sizeAspectCheck->setChecked( mObject->lockAspectRatio() ); model::Size originalSize = mObject->naturalSize(); QString originalSizeString = QString( "%1: %2 x %3 %4" ) @@ -454,8 +460,8 @@ namespace glabels loadTextPage(); loadPositionPage(); + loadRectSizePage(); loadShadowPage(); - loadRectSizePage(); setEnabled( true ); } @@ -473,6 +479,7 @@ namespace glabels loadBarcodePage(); loadPositionPage(); + loadRectSizePage(); setEnabled( true ); } @@ -497,17 +504,19 @@ namespace glabels } - void ObjectEditor::onMergeSourceChanged() + void ObjectEditor::onFieldsAvailableChanged() { if ( !mBlocked ) { - QStringList keys = mModel->merge()->keys(); - lineColorButton->setKeys( keys ); - fillColorButton->setKeys( keys ); - textInsertFieldCombo->setKeys( keys ); - barcodeInsertFieldCombo->setKeys( keys ); - imageFieldCombo->setKeys( keys ); - shadowColorButton->setKeys( keys ); + lineColorButton->setKeys( mModel->merge(), mModel->variables() ); + fillColorButton->setKeys( mModel->merge(), mModel->variables() ); + textColorButton->setKeys( mModel->merge(), mModel->variables() ); + barcodeColorButton->setKeys( mModel->merge(), mModel->variables() ); + shadowColorButton->setKeys( mModel->merge(), mModel->variables() ); + + textInsertFieldButton->setKeys( mModel->merge(), mModel->variables() ); + barcodeInsertFieldButton->setKeys( mModel->merge(), mModel->variables() ); + imageFieldButton->setKeys( mModel->merge(), mModel->variables() ); } } @@ -618,8 +627,11 @@ namespace glabels void ObjectEditor::onImageKeySelected( QString key ) { - mUndoRedoModel->checkpoint( tr("Set image") ); - mObject->setFilenameNode( model::TextNode( true, key ) ); + if ( mObject ) + { + mUndoRedoModel->checkpoint( tr("Set image") ); + mObject->setFilenameNode( model::TextNode( true, key ) ); + } } @@ -629,7 +641,7 @@ namespace glabels { mBlocked = true; - mUndoRedoModel->checkpoint( tr("Move") ); + mUndoRedoModel->checkpoint( tr("Position") ); model::Distance x = model::Distance(posXSpin->value(), mUnits); model::Distance y = model::Distance(posYSpin->value(), mUnits); @@ -654,6 +666,7 @@ namespace glabels if ( sizeAspectCheck->isChecked() ) { + mObject->setLockAspectRatio( true ); if ( fabs(spinW - mObject->w()) > fabs(spinH - mObject->h()) ) { mObject->setWHonorAspect( spinW ); @@ -667,6 +680,7 @@ namespace glabels } else { + mObject->setLockAspectRatio( false ); mObject->setSize( spinW, spinH ); } @@ -740,6 +754,8 @@ namespace glabels { mBlocked = true; + mUndoRedoModel->checkpoint( tr("Barcode") ); + barcode::Style bcStyle = barcodeStyleButton->bcStyle(); barcodeShowTextCheck->setEnabled( bcStyle.textOptional() ); @@ -774,6 +790,8 @@ namespace glabels void ObjectEditor::onResetImageSize() { + mUndoRedoModel->checkpoint( tr("Reset") ); + mObject->setSize( mObject->naturalSize() ); } diff --git a/glabels/ObjectEditor.h b/glabels/ObjectEditor.h index 6e84687..57b380f 100644 --- a/glabels/ObjectEditor.h +++ b/glabels/ObjectEditor.h @@ -80,7 +80,7 @@ namespace glabels void onSettingsChanged(); void onLabelSizeChanged(); void onSelectionChanged(); - void onMergeSourceChanged(); + void onFieldsAvailableChanged(); void onObjectChanged(); void onObjectMoved(); void onObjectDestroyed(); diff --git a/glabels/PrintView.cpp b/glabels/PrintView.cpp index a900af3..a6f3f7d 100644 --- a/glabels/PrintView.cpp +++ b/glabels/PrintView.cpp @@ -70,6 +70,9 @@ namespace glabels connect( mModel, SIGNAL(changed()), this, SLOT(onModelChanged()) ); + copiesSpin->setRange( 1, 100*mModel->frame()->nLabels() ); + copiesStartSpin->setRange( 1, mModel->frame()->nLabels() ); + onFormChanged(); } @@ -79,6 +82,9 @@ namespace glabels /// void PrintView::onModelChanged() { + copiesSpin->setRange( 1, 100*mModel->frame()->nLabels() ); + copiesStartSpin->setRange( 1, mModel->frame()->nLabels() ); + updateView(); } @@ -88,8 +94,6 @@ namespace glabels /// void PrintView::updateView() { - copiesStartSpin->setRange( 1, mModel->frame()->nLabels() ); - if ( mRenderer.nPages() == 1 ) { if ( mRenderer.nItems() == 1 ) diff --git a/glabels/VariablesView.cpp b/glabels/VariablesView.cpp new file mode 100644 index 0000000..ea13f33 --- /dev/null +++ b/glabels/VariablesView.cpp @@ -0,0 +1,254 @@ +/* VariablesView.cpp + * + * Copyright (C) 2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "VariablesView.h" + +#include "EditVariableDialog.h" + +#include +#include + + +namespace +{ + enum ICol { + I_COL_NAME, + I_COL_TYPE, + I_COL_VALUE, + I_COL_INCREMENT, + I_COL_STEP_SIZE, + I_COL_DUMMY, + N_COLS + }; +} + + +namespace glabels +{ + + /// + /// Constructor + /// + VariablesView::VariablesView( QWidget *parent ) + : QWidget(parent), mModel(nullptr), mUndoRedoModel(nullptr) + { + setupUi( this ); + + titleLabel->setText( QString( "%1" ).arg( tr("Variables") ) ); + + table->setColumnCount( N_COLS ); + + auto* nameHeaderItem = new QTableWidgetItem( tr("Name") ); + nameHeaderItem->setFlags( nameHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_NAME, nameHeaderItem ); + + auto* typeHeaderItem = new QTableWidgetItem( tr("Type") ); + typeHeaderItem->setFlags( typeHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_TYPE, typeHeaderItem ); + + auto* valueHeaderItem = new QTableWidgetItem( tr("Value") ); + valueHeaderItem->setFlags( valueHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_VALUE, valueHeaderItem ); + + auto* incrementHeaderItem = new QTableWidgetItem( tr("Increment") ); + incrementHeaderItem->setFlags( incrementHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_INCREMENT, incrementHeaderItem ); + + auto* stepSizeHeaderItem = new QTableWidgetItem( tr("Step Size") ); + stepSizeHeaderItem->setFlags( stepSizeHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_STEP_SIZE, stepSizeHeaderItem ); + + auto* dummyHeaderItem = new QTableWidgetItem(); + dummyHeaderItem->setFlags( Qt::NoItemFlags ); + table->setHorizontalHeaderItem( I_COL_DUMMY, dummyHeaderItem ); + table->horizontalHeader()->setStretchLastSection( true ); + } + + + /// + /// Destructor + /// + VariablesView::~VariablesView() + { + // empty + } + + + /// + /// Set Model + /// + void VariablesView::setModel( model::Model* model, UndoRedoModel* undoRedoModel ) + { + mModel = model; + mUndoRedoModel = undoRedoModel; + + updateControls(); + loadTable(); + + connect( mModel, SIGNAL(variablesChanged()), this, SLOT(onVariablesChanged()) ); + } + + + /// + /// table Selection Changed + /// + void VariablesView::onTableSelectionChanged() + { + updateControls(); + } + + + /// + /// addButton Clicked + /// + void VariablesView::onAddButtonClicked() + { + EditVariableDialog dialog( this ); + + model::Variable v( model::Variable::Type::INTEGER, + "x", + "0", + model::Variable::Increment::NEVER, + "1" ); + dialog.setVariable( v ); + dialog.setWindowTitle( tr("Add Variable") ); + + if ( dialog.exec() == QDialog::Accepted ) + { + mModel->variables()->addVariable( dialog.variable() ); + selectVariable( dialog.variable().name() ); + } + } + + + /// + /// editButton Clicked + /// + void VariablesView::onEditButtonClicked() + { + int iRow = table->selectedItems()[0]->row(); + QString name = table->item( iRow, I_COL_NAME )->text(); + + if ( mModel->variables()->hasVariable( name ) ) + { + model::Variable v = mModel->variables()->value( name ); + + EditVariableDialog dialog( this ); + dialog.setVariable( v ); + dialog.setWindowTitle( tr("Edit Variable") ); + + if ( dialog.exec() == QDialog::Accepted ) + { + mModel->variables()->replaceVariable( name, dialog.variable() ); + selectVariable( dialog.variable().name() ); + } + } + } + + + /// + /// deleteButton Clicked + /// + void VariablesView::onDeleteButtonClicked() + { + int iRow = table->selectedItems()[0]->row(); + + QString name = table->item( iRow, I_COL_NAME )->text(); + mModel->variables()->deleteVariable( name ); + } + + + /// + /// Variables Changed + /// + void VariablesView::onVariablesChanged() + { + // Reload table from variables + loadTable(); + } + + + /// + /// update controls + /// + void VariablesView::updateControls() + { + bool hasSelection = !table->selectedItems().isEmpty(); + + editButton->setEnabled( hasSelection ); + deleteButton->setEnabled( hasSelection ); + } + + + /// + /// load table from variables + /// + void VariablesView::loadTable() + { + table->clearContents(); + table->setRowCount( mModel->variables()->size() ); + + int iRow = 0; + for( const auto& v : *mModel->variables() ) + { + auto* typeItem = new QTableWidgetItem( model::Variable::typeToI18nString(v.type()) ); + typeItem->setFlags( typeItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_TYPE, typeItem ); + + auto* nameItem = new QTableWidgetItem( v.name() ); + nameItem->setFlags( nameItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_NAME, nameItem ); + + auto* valueItem = new QTableWidgetItem( v.initialValue() ); + valueItem->setFlags( valueItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_VALUE, valueItem ); + + auto* incrementItem = new QTableWidgetItem( model::Variable::incrementToI18nString(v.increment()) ); + incrementItem->setFlags( incrementItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_INCREMENT, incrementItem ); + + auto* stepSizeItem = new QTableWidgetItem( v.stepSize() ); + stepSizeItem->setFlags( stepSizeItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_STEP_SIZE, stepSizeItem ); + + table->showRow( iRow ); + iRow++; + } + } + + + void VariablesView::selectVariable( const QString& name ) + { + int iRow = 0; + for( const auto& v : *mModel->variables() ) + { + if ( v.name() == name ) + { + table->setCurrentCell( iRow, 0, + (QItemSelectionModel::Select|QItemSelectionModel::Rows) ); + break; + } + + iRow++; + } + } + + +} // namespace glabels diff --git a/glabels/VariablesView.h b/glabels/VariablesView.h new file mode 100644 index 0000000..93dca3d --- /dev/null +++ b/glabels/VariablesView.h @@ -0,0 +1,91 @@ +/* VariablesView.h + * + * Copyright (C) 2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef VariablesView_h +#define VariablesView_h + + +#include "ui_VariablesView.h" + +#include "model/Model.h" + + +namespace glabels +{ + + // Forward references + class UndoRedoModel; + + + /// + /// Variables Property Editor Widget + /// + class VariablesView : public QWidget, public Ui_VariablesView + { + Q_OBJECT + + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + VariablesView( QWidget *parent = nullptr ); + ~VariablesView() override; + + + ///////////////////////////////// + // Public methods + ///////////////////////////////// + void setModel( model::Model* model, UndoRedoModel* undoRedoModel ); + + + ///////////////////////////////// + // Slots + ///////////////////////////////// + private slots: + void onTableSelectionChanged(); + void onAddButtonClicked(); + void onEditButtonClicked(); + void onDeleteButtonClicked(); + void onVariablesChanged(); + + + ///////////////////////////////// + // Private methods + ///////////////////////////////// + private: + void updateControls(); + void loadTable(); + void selectVariable( const QString& name ); + + + ///////////////////////////////// + // Private Data + ///////////////////////////////// + private: + model::Model* mModel; + UndoRedoModel* mUndoRedoModel; + + }; + +} + + +#endif // VariablesView_h diff --git a/glabels/icons.qrc b/glabels/icons.qrc index 8d4f777..faa0f30 100644 --- a/glabels/icons.qrc +++ b/glabels/icons.qrc @@ -102,6 +102,7 @@ icons/flat/48x48/glabels-merge.svg icons/flat/48x48/glabels-print.svg icons/flat/48x48/glabels-properties.svg + icons/flat/48x48/glabels-variables.svg icons/apps/48x48/glabels.svg icons/apps/128x128/glabels.svg diff --git a/glabels/icons/flat/48x48/glabels-variables.svg b/glabels/icons/flat/48x48/glabels-variables.svg new file mode 100644 index 0000000..9b47813 --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-variables.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/glabels/images/checkerboard.png b/glabels/images/checkerboard.png index c97f40e..575edc3 100644 Binary files a/glabels/images/checkerboard.png and b/glabels/images/checkerboard.png differ diff --git a/glabels/main.cpp b/glabels/main.cpp index 1acdf1c..67b9c7c 100644 --- a/glabels/main.cpp +++ b/glabels/main.cpp @@ -45,7 +45,7 @@ int main( int argc, char **argv ) QCoreApplication::setOrganizationName( "glabels.org" ); QCoreApplication::setOrganizationDomain( "glabels.org" ); QCoreApplication::setApplicationName( "glabels-qt" ); - QCoreApplication::setApplicationVersion( glabels::model::Version::STRING ); + QCoreApplication::setApplicationVersion( glabels::model::Version::LONG_STRING ); // // Setup translators diff --git a/glabels/ui/EditVariableDialog.ui b/glabels/ui/EditVariableDialog.ui new file mode 100644 index 0000000..72f8575 --- /dev/null +++ b/glabels/ui/EditVariableDialog.ui @@ -0,0 +1,274 @@ + + + EditVariableDialog + + + + 0 + 0 + 469 + 297 + + + + Dialog + + + + 12 + + + + + Increment + + + + + + + + + + + Step size: + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Variable + + + + + + + + + + + Value: + + + + + + + Name: + + + + + + + + + + Type: + + + + + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + glabels::ColorButton + QPushButton +
ColorButton.h
+ + colorChanged() + +
+
+ + + + buttonBox + accepted() + EditVariableDialog + accept() + + + 236 + 287 + + + 157 + 236 + + + + + buttonBox + rejected() + EditVariableDialog + reject() + + + 304 + 287 + + + 286 + 236 + + + + + typeCombo + currentIndexChanged(int) + EditVariableDialog + onTypeComboChanged() + + + 252 + 70 + + + 33 + 161 + + + + + incrementCombo + currentIndexChanged(int) + EditVariableDialog + onIncrementComboChanged() + + + 100 + 223 + + + 97 + 176 + + + + + stepSizeEdit + textChanged(QString) + EditVariableDialog + onStepSizeEditChanged() + + + 440 + 223 + + + 333 + 166 + + + + + nameEdit + textChanged(QString) + EditVariableDialog + onNameEditChanged() + + + 440 + 103 + + + 393 + 165 + + + + + valueEdit + textChanged(QString) + EditVariableDialog + onValueEditChanged() + + + 318 + 129 + + + 459 + 157 + + + + + colorValueButton + colorChanged() + EditVariableDialog + onColorValueButtonChanged() + + + 406 + 114 + + + 458 + 122 + + + + + + onTypeComboChanged() + onValueEditChanged() + onIncrementComboChanged() + onStepSizeEditChanged() + onNameEditChanged() + onColorValueButtonChanged() + +
diff --git a/glabels/ui/MergeView.ui b/glabels/ui/MergeView.ui index e73b77a..c7f682a 100644 --- a/glabels/ui/MergeView.ui +++ b/glabels/ui/MergeView.ui @@ -11,12 +11,21 @@ - Form + Form - + + 12 + + + 12 + + + 12 + + 12 @@ -37,16 +46,9 @@ Source - - + + - - - - Location - - - @@ -54,6 +56,27 @@ + + + + + + + + + true + + + + + + + Browse... + + + + + @@ -61,24 +84,8 @@ - - - - - - - Qt::Horizontal - - - - 360 - 20 - - - - @@ -89,7 +96,11 @@ - + + + Qt::NoFocus + + @@ -138,8 +149,8 @@ onSelectAllButtonClicked() - 63 - 571 + 97 + 570 69 @@ -163,22 +174,6 @@ - - locationButton - clicked() - MergeView - onLocationButtonClicked() - - - 174 - 93 - - - 570 - 75 - - - formatCombo activated(int) @@ -186,8 +181,8 @@ onFormatComboActivated() - 162 - 48 + 257 + 109 563 @@ -195,11 +190,27 @@ + + locationBrowseButton + clicked() + MergeView + onLocationBrowseButtonClicked() + + + 296 + 130 + + + 565 + 149 + + + onSelectAllButtonClicked() onUnselectAllButtonClicked() - onLocationButtonClicked() onFormatComboActivated() + onLocationBrowseButtonClicked() diff --git a/glabels/ui/ObjectEditor.ui b/glabels/ui/ObjectEditor.ui index 3e0589e..29ca098 100644 --- a/glabels/ui/ObjectEditor.ui +++ b/glabels/ui/ObjectEditor.ui @@ -7,7 +7,7 @@ 0 0 400 - 640 + 648 @@ -29,9 +29,9 @@ - Form + Form - + @@ -579,7 +579,11 @@ - + + + Insert field + + @@ -729,7 +733,11 @@ - + + + Insert field + + @@ -758,14 +766,27 @@ Image - + + + + Qt::Vertical + + + + 20 + 646 + + + + + File - + - + @@ -789,68 +810,47 @@ - - - - - - 0 - 0 - - - - Select File... - - - - - - - - 0 - 0 - - - - or - - - - - - - - 0 - 0 - - - - - Select Merge Field... - - - - - + + + + 0 + 0 + + + + Browse... + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use field + + - - - - Qt::Vertical - - - - 20 - 646 - - - - @@ -1202,16 +1202,7 @@ 15 - - 0 - - - 0 - - - 0 - - + 0 @@ -1529,14 +1520,6 @@ colorChanged() - - glabels::FieldButton - QComboBox -
FieldButton.h
- - keySelected(QString) - -
glabels::BarcodeMenuButton QPushButton @@ -1545,6 +1528,14 @@ selectionChanged() + + glabels::FieldButton + QPushButton +
FieldButton.h
+ + keySelected(QString) + +
@@ -1653,8 +1644,8 @@ onTextControlsChanged() - 157 - 333 + 160 + 332 396 @@ -1669,8 +1660,8 @@ onTextControlsChanged() - 198 - 333 + 200 + 332 398 @@ -1685,8 +1676,8 @@ onTextControlsChanged() - 238 - 333 + 240 + 332 395 @@ -1701,8 +1692,8 @@ onTextControlsChanged() - 284 - 333 + 286 + 332 393 @@ -1717,8 +1708,8 @@ onTextControlsChanged() - 325 - 333 + 326 + 332 396 @@ -1734,7 +1725,7 @@ 365 - 333 + 332 397 @@ -1749,8 +1740,8 @@ onTextControlsChanged() - 184 - 407 + 189 + 404 394 @@ -1766,7 +1757,7 @@ 178 - 143 + 139 392 @@ -1781,8 +1772,8 @@ onLineControlsChanged() - 137 - 179 + 136 + 174 1 @@ -1797,8 +1788,8 @@ onFillControlsChanged() - 136 - 263 + 135 + 256 6 @@ -1813,8 +1804,8 @@ onPositionControlsChanged() - 159 - 142 + 160 + 138 399 @@ -1829,8 +1820,8 @@ onPositionControlsChanged() - 159 - 179 + 160 + 174 325 @@ -1845,8 +1836,8 @@ onRectSizeControlsChanged() - 159 - 265 + 160 + 258 3 @@ -1861,8 +1852,8 @@ onRectSizeControlsChanged() - 159 - 302 + 160 + 294 0 @@ -1877,8 +1868,8 @@ onResetImageSize() - 210 - 372 + 213 + 362 4 @@ -1909,8 +1900,8 @@ onShadowControlsChanged() - 165 - 142 + 166 + 138 398 @@ -1925,8 +1916,8 @@ onShadowControlsChanged() - 165 - 179 + 166 + 174 294 @@ -1941,8 +1932,8 @@ onShadowControlsChanged() - 142 - 215 + 141 + 209 399 @@ -1957,8 +1948,8 @@ onShadowControlsChanged() - 159 - 252 + 162 + 245 399 @@ -1973,8 +1964,8 @@ onLineSizeControlsChanged() - 174 - 456 + 177 + 444 5 @@ -1989,8 +1980,8 @@ onLineSizeControlsChanged() - 174 - 493 + 177 + 480 1 @@ -1999,14 +1990,14 @@ - imageFileButton + imageBrowseButton clicked() ObjectEditor onImageFileButtonClicked() - 133 - 175 + 367 + 135 394 @@ -2014,22 +2005,6 @@ - - imageFieldCombo - keySelected(QString) - ObjectEditor - onImageKeySelected(QString) - - - 302 - 175 - - - 397 - 32 - - - textEdit textChanged() @@ -2046,22 +2021,6 @@ - - textInsertFieldCombo - keySelected(QString) - ObjectEditor - onTextInsertFieldKeySelected(QString) - - - 239 - 599 - - - 395 - 645 - - - barcodeShowTextCheck toggled(bool) @@ -2069,8 +2028,8 @@ onBarcodeControlsChanged() - 178 - 172 + 195 + 167 4 @@ -2085,8 +2044,8 @@ onBarcodeControlsChanged() - 164 - 204 + 195 + 198 1 @@ -2102,7 +2061,7 @@ 126 - 239 + 232 1 @@ -2126,22 +2085,6 @@ - - barcodeInsertFieldCombo - keySelected(QString) - ObjectEditor - onBarcodeInsertFieldKeySelected(QString) - - - 239 - 400 - - - 403 - 625 - - - barcodeStyleButton selectionChanged() @@ -2149,8 +2092,8 @@ onBarcodeControlsChanged() - 178 - 140 + 195 + 136 5 @@ -2190,6 +2133,70 @@ + + sizeAspectCheck + toggled(bool) + ObjectEditor + onRectSizeControlsChanged() + + + 231 + 289 + + + 199 + 319 + + + + + textInsertFieldButton + keySelected(QString) + ObjectEditor + onTextInsertFieldKeySelected(QString) + + + 191 + 589 + + + 227 + 642 + + + + + barcodeInsertFieldButton + keySelected(QString) + ObjectEditor + onBarcodeInsertFieldKeySelected(QString) + + + 208 + 379 + + + 205 + 649 + + + + + imageFieldButton + keySelected(QString) + ObjectEditor + onImageKeySelected(QString) + + + 317 + 160 + + + 331 + -12 + + + onChanged() @@ -2206,5 +2213,6 @@ onTextInsertFieldKeySelected(QString) onBarcodeControlsChanged() onBarcodeInsertFieldKeySelected(QString) + onImageComboChanged() diff --git a/glabels/ui/PrintView.ui b/glabels/ui/PrintView.ui index d4ea498..348bada 100644 --- a/glabels/ui/PrintView.ui +++ b/glabels/ui/PrintView.ui @@ -6,7 +6,7 @@ 0 0 - 759 + 852 792
@@ -17,7 +17,7 @@ - Form + Form @@ -73,21 +73,8 @@ Copies - - - - - - 0 - 0 - - - - (Will print a total of xx items on nn pages.) - - - - + + @@ -104,11 +91,17 @@ + + QAbstractSpinBox::UpDownArrows + + + true + 1 - 96 + 9999 @@ -127,7 +120,7 @@ - + @@ -183,6 +176,29 @@ + + + + + + + 0 + 0 + + + + + 330 + 0 + + + + (Will print a total of xxxx items on nnn pages.) + + + + +
diff --git a/glabels/ui/PropertiesView.ui b/glabels/ui/PropertiesView.ui index 60c0632..6481733 100644 --- a/glabels/ui/PropertiesView.ui +++ b/glabels/ui/PropertiesView.ui @@ -23,7 +23,7 @@ - Form + Form @@ -241,6 +241,12 @@ + + + 330 + 0 + + <html><head/><body><p>Select another product for this gLabels project.</p></body></html> diff --git a/glabels/ui/StartupView.ui b/glabels/ui/StartupView.ui index 246e5c0..0aca3cb 100644 --- a/glabels/ui/StartupView.ui +++ b/glabels/ui/StartupView.ui @@ -11,7 +11,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerApplyPage.ui b/glabels/ui/TemplateDesignerApplyPage.ui index d0ed63b..3b1b913 100644 --- a/glabels/ui/TemplateDesignerApplyPage.ui +++ b/glabels/ui/TemplateDesignerApplyPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerCdPage.ui b/glabels/ui/TemplateDesignerCdPage.ui index f61fa31..a9add20 100644 --- a/glabels/ui/TemplateDesignerCdPage.ui +++ b/glabels/ui/TemplateDesignerCdPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerContinuousPage.ui b/glabels/ui/TemplateDesignerContinuousPage.ui index 8960aa6..d3cc112 100644 --- a/glabels/ui/TemplateDesignerContinuousPage.ui +++ b/glabels/ui/TemplateDesignerContinuousPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerEllipsePage.ui b/glabels/ui/TemplateDesignerEllipsePage.ui index 53dec92..0ea9ee5 100644 --- a/glabels/ui/TemplateDesignerEllipsePage.ui +++ b/glabels/ui/TemplateDesignerEllipsePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerIntroPage.ui b/glabels/ui/TemplateDesignerIntroPage.ui index 90665ff..9b397cb 100644 --- a/glabels/ui/TemplateDesignerIntroPage.ui +++ b/glabels/ui/TemplateDesignerIntroPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerNLayoutsPage.ui b/glabels/ui/TemplateDesignerNLayoutsPage.ui index df42ffd..e88dd2f 100644 --- a/glabels/ui/TemplateDesignerNLayoutsPage.ui +++ b/glabels/ui/TemplateDesignerNLayoutsPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerNamePage.ui b/glabels/ui/TemplateDesignerNamePage.ui index 9bf0d13..a9ed4d7 100644 --- a/glabels/ui/TemplateDesignerNamePage.ui +++ b/glabels/ui/TemplateDesignerNamePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerOneLayoutPage.ui b/glabels/ui/TemplateDesignerOneLayoutPage.ui index 508cfd4..315266d 100644 --- a/glabels/ui/TemplateDesignerOneLayoutPage.ui +++ b/glabels/ui/TemplateDesignerOneLayoutPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerPageSizePage.ui b/glabels/ui/TemplateDesignerPageSizePage.ui index 38c631f..41686e7 100644 --- a/glabels/ui/TemplateDesignerPageSizePage.ui +++ b/glabels/ui/TemplateDesignerPageSizePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerPathPage.ui b/glabels/ui/TemplateDesignerPathPage.ui index 2f78452..3b59a4c 100644 --- a/glabels/ui/TemplateDesignerPathPage.ui +++ b/glabels/ui/TemplateDesignerPathPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerRectPage.ui b/glabels/ui/TemplateDesignerRectPage.ui index 7265e8f..a4a4758 100644 --- a/glabels/ui/TemplateDesignerRectPage.ui +++ b/glabels/ui/TemplateDesignerRectPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerRoundPage.ui b/glabels/ui/TemplateDesignerRoundPage.ui index c51aa03..2a6651d 100644 --- a/glabels/ui/TemplateDesignerRoundPage.ui +++ b/glabels/ui/TemplateDesignerRoundPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerShapePage.ui b/glabels/ui/TemplateDesignerShapePage.ui index 0466fbb..a471d6c 100644 --- a/glabels/ui/TemplateDesignerShapePage.ui +++ b/glabels/ui/TemplateDesignerShapePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerTwoLayoutPage.ui b/glabels/ui/TemplateDesignerTwoLayoutPage.ui index d7cc9dc..e3cc62a 100644 --- a/glabels/ui/TemplateDesignerTwoLayoutPage.ui +++ b/glabels/ui/TemplateDesignerTwoLayoutPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/VariablesView.ui b/glabels/ui/VariablesView.ui new file mode 100644 index 0000000..c0953d6 --- /dev/null +++ b/glabels/ui/VariablesView.ui @@ -0,0 +1,190 @@ + + + VariablesView + + + + 0 + 0 + 1105 + 605 + + + + Form + + + + 21 + + + 21 + + + 21 + + + 21 + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-size:18pt;">Variables</span></p></body></html> + + + + + + + Qt::NoFocus + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 0 + + + false + + + true + + + + + + + + + <html><head/><body><p>Add variable</p></body></html> + + + Add + + + + + + + <html><head/><body><p>Edit selected variable</p></body></html> + + + Edit + + + + + + + <html><head/><body><p>Delete selected variable</p></body></html> + + + Delete + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + addButton + clicked() + VariablesView + onAddButtonClicked() + + + 63 + 586 + + + 98 + 598 + + + + + editButton + clicked() + VariablesView + onEditButtonClicked() + + + 167 + 576 + + + 317 + 608 + + + + + deleteButton + clicked() + VariablesView + onDeleteButtonClicked() + + + 245 + 575 + + + 508 + 613 + + + + + table + itemSelectionChanged() + VariablesView + onTableSelectionChanged() + + + 380 + 258 + + + 787 + 610 + + + + + + onSelectAllButtonClicked() + onUnselectAllButtonClicked() + onLocationButtonClicked() + onFormatComboActivated() + onAddButtonClicked() + onEditButtonClicked() + onDeleteButtonClicked() + onTableSelectionChanged() + + diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index 0edacf8..1690772 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -56,6 +56,8 @@ set (Model_sources Template.cpp TextNode.cpp Units.cpp + Variable.cpp + Variables.cpp Vendor.cpp XmlCategoryParser.cpp XmlLabelCreator.cpp @@ -80,6 +82,7 @@ set (Model_qobject_headers ModelTextObject.h PageRenderer.h Settings.h + Variables.h ) qt5_wrap_cpp (Model_moc_sources ${Model_qobject_headers}) diff --git a/model/ColorNode.cpp b/model/ColorNode.cpp index f399a1a..ceee003 100644 --- a/model/ColorNode.cpp +++ b/model/ColorNode.cpp @@ -175,30 +175,32 @@ namespace glabels /// /// Get color, expand if necessary /// - QColor ColorNode::color( merge::Record* record ) const + QColor ColorNode::color( const merge::Record* record, + const Variables* variables ) const { - if ( mIsField ) + QColor value = QColor( 192, 192, 192, 128 ); + + bool haveRecordField = mIsField && record && + record->contains(mKey) && + !record->value(mKey).isEmpty(); + bool haveVariable = mIsField && variables && + variables->contains(mKey) && + !(*variables)[mKey].value().isEmpty(); + + if ( haveRecordField ) { - if ( record == nullptr ) - { - return mColor; - } - else - { - if ( record->contains( mKey ) ) - { - return QColor( (*record)[ mKey ] ); - } - else - { - return mColor; - } - } + value = QColor( record->value(mKey) ); } - else + else if ( haveVariable ) { - return mColor; + value = QColor( (*variables)[mKey].value() ); } + else if ( !mIsField ) + { + value = mColor; + } + + return value; } } diff --git a/model/ColorNode.h b/model/ColorNode.h index 89b0201..dab30af 100644 --- a/model/ColorNode.h +++ b/model/ColorNode.h @@ -22,6 +22,7 @@ #define model_ColorNode_h +#include "Variables.h" #include "merge/Record.h" #include @@ -95,7 +96,8 @@ namespace glabels ///////////////////////////////// public: uint32_t rgba() const; - QColor color( merge::Record* record ) const; + QColor color( const merge::Record* record, + const Variables* variables ) const; ///////////////////////////////// diff --git a/model/FileUtil.cpp b/model/FileUtil.cpp index 09e61d2..a3e2bcb 100644 --- a/model/FileUtil.cpp +++ b/model/FileUtil.cpp @@ -108,5 +108,17 @@ namespace glabels return QDir("/"); } + + QString FileUtil::makeRelativeIfInDir( const QDir& dir, + const QString& filename ) + { + QString relativeFilePath = dir.relativeFilePath( filename ); // Note: directory separators canonicalized to slash by Qt path methods + if ( !relativeFilePath.startsWith( "../" ) ) + { + return relativeFilePath; + } + return filename; + } + } } diff --git a/model/FileUtil.h b/model/FileUtil.h index ed76de0..0cac0db 100644 --- a/model/FileUtil.h +++ b/model/FileUtil.h @@ -41,6 +41,9 @@ namespace glabels QDir userTemplatesDir(); QDir translationsDir(); + + QString makeRelativeIfInDir( const QDir& dir, + const QString& filename ); } } diff --git a/model/Model.cpp b/model/Model.cpp index b0eb83d..4bcf47f 100644 --- a/model/Model.cpp +++ b/model/Model.cpp @@ -57,7 +57,18 @@ namespace glabels Model::Model() : mUntitledInstance(0), mModified(true), mRotate(false) { + mVariables = new Variables(); mMerge = new merge::None(); + + connect( mVariables, SIGNAL(changed()), this, SLOT(onVariablesChanged()) ); + } + + + Model::Model( merge::Merge* merge, Variables* variables ) + : mUntitledInstance(0), mModified(true), mRotate(false) + { + mVariables = variables; // Shared + mMerge = merge; // Shared } @@ -66,7 +77,8 @@ namespace glabels /// Model::~Model() { - delete mMerge; + qDeleteAll( mObjectList ); + // Final instance of mMerge and mVariables to be deleted by Model owner } @@ -75,7 +87,13 @@ namespace glabels /// Model* Model::save() const { - auto* savedModel = new Model; + auto* savedModel = new Model( mMerge, mVariables ); // mMerge and mVariables shared between models + + if ( mFileName.isEmpty() && mUntitledInstance == 0 ) + { + qDebug() << "Model::save: Warning: called before mUntitledInstance has been initialized: untitled names will differ"; + } + savedModel->restore( this ); return savedModel; @@ -112,18 +130,12 @@ namespace glabels connect( object, SIGNAL(moved()), this, SLOT(onObjectMoved()) ); } - delete mMerge; - mMerge = savedModel->mMerge->clone(); - // Emit signals based on potential changes emit changed(); emit selectionChanged(); emit modifiedChanged(); emit nameChanged(); emit sizeChanged(); - emit mergeChanged(); - emit mergeSourceChanged(); - emit mergeSelectionChanged(); } @@ -276,6 +288,38 @@ namespace glabels } + /// + /// Get directory as a QDir. + /// + QDir Model::dir() const + { + if ( mFileName.isEmpty() ) + { + return QDir::current(); + } + else + { + return QFileInfo( mFileName ).absoluteDir(); + } + } + + + /// + /// Get directory as a path. + /// + QString Model::dirPath() const + { + if ( mFileName.isEmpty() ) + { + return QDir::currentPath(); + } + else + { + return QFileInfo( mFileName ).absolutePath(); + } + } + + /// /// Get short name. /// @@ -302,6 +346,15 @@ namespace glabels } + /// + /// Get variables object + /// + Variables* Model::variables() const + { + return mVariables; + } + + /// /// Get merge object /// @@ -451,6 +504,17 @@ namespace glabels } + /// + /// Variables Changed Slot + /// + void Model::onVariablesChanged() + { + setModified(); + emit changed(); + emit variablesChanged(); + } + + /// /// Merge Source Changed Slot /// @@ -1359,7 +1423,7 @@ namespace glabels QClipboard *clipboard = QApplication::clipboard(); QByteArray buffer; - XmlLabelCreator::serializeObjects( getSelection(), buffer ); + XmlLabelCreator::serializeObjects( getSelection(), this, buffer ); auto *mimeData = new QMimeData; mimeData->setData( MIME_TYPE, buffer ); @@ -1415,7 +1479,7 @@ namespace glabels { // Native objects QByteArray buffer = mimeData->data( MIME_TYPE ); - QList objects = XmlLabelParser::deserializeObjects( buffer ); + QList objects = XmlLabelParser::deserializeObjects( buffer, this ); unselectAll(); foreach ( ModelObject* object, objects ) @@ -1452,11 +1516,11 @@ namespace glabels /// /// Draw label objects /// - void Model::draw( QPainter* painter, bool inEditor, merge::Record* record ) const + void Model::draw( QPainter* painter, bool inEditor, merge::Record* record, Variables* variables ) const { foreach ( ModelObject* object, mObjectList ) { - object->draw( painter, inEditor, record ); + object->draw( painter, inEditor, record, variables ); } } diff --git a/model/Model.h b/model/Model.h index 992606e..e31574d 100644 --- a/model/Model.h +++ b/model/Model.h @@ -24,10 +24,12 @@ #include "Settings.h" #include "Template.h" +#include "Variables.h" #include "merge/Merge.h" #include "merge/Record.h" +#include #include #include #include @@ -57,6 +59,7 @@ namespace glabels ///////////////////////////////// public: Model(); + Model( merge::Merge* merge, Variables* variables ); ~Model(); @@ -76,6 +79,7 @@ namespace glabels void sizeChanged(); void selectionChanged(); void modifiedChanged(); + void variablesChanged(); void mergeChanged(); void mergeSourceChanged(); void mergeSelectionChanged(); @@ -89,6 +93,8 @@ namespace glabels void setModified(); void clearModified(); + QDir dir() const; + QString dirPath() const; QString shortName(); const QString& fileName() const; void setFileName( const QString &fileName ); @@ -107,6 +113,8 @@ namespace glabels const QList& objectList() const; + Variables* variables() const; + merge::Merge* merge() const; void setMerge( merge::Merge* merge ); @@ -204,7 +212,10 @@ namespace glabels // Drawing operations ///////////////////////////////// public: - void draw( QPainter* painter, bool inEditor = true, merge::Record* record = nullptr ) const; + void draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const; ///////////////////////////////// @@ -213,6 +224,7 @@ namespace glabels private slots: void onObjectChanged(); void onObjectMoved(); + void onVariablesChanged(); void onMergeSourceChanged(); void onMergeSelectionChanged(); @@ -229,6 +241,7 @@ namespace glabels QList mObjectList; + Variables* mVariables; merge::Merge* mMerge; }; diff --git a/model/ModelBarcodeObject.cpp b/model/ModelBarcodeObject.cpp index 315632e..ed4f1bf 100644 --- a/model/ModelBarcodeObject.cpp +++ b/model/ModelBarcodeObject.cpp @@ -89,13 +89,14 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const barcode::Style& bcStyle, bool bcTextFlag, bool bcChecksumFlag, QString bcData, const ColorNode& bcColorNode, const QMatrix& matrix ) - : ModelObject( x0, y0, w, h, matrix ) + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix ) { mOutline = new Outline( this ); @@ -311,7 +312,8 @@ namespace glabels /// void ModelBarcodeObject::drawShadow( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { // Barcodes don't support shadows. } @@ -322,9 +324,10 @@ namespace glabels /// void ModelBarcodeObject::drawObject( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor bcColor = mBcColorNode.color( record ); + QColor bcColor = mBcColorNode.color( record, variables ); if ( inEditor ) { @@ -332,7 +335,7 @@ namespace glabels } else { - drawBc( painter, bcColor, record ); + drawBc( painter, bcColor, record, variables ); } } @@ -450,7 +453,8 @@ namespace glabels void ModelBarcodeObject::drawBc( QPainter* painter, const QColor& color, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { painter->setPen( QPen( color ) ); @@ -458,7 +462,7 @@ namespace glabels bc->setChecksum(mBcChecksumFlag); bc->setShowText(mBcTextFlag); - bc->build( mBcData.expand( record ).toStdString(), mW.pt(), mH.pt() ); + bc->build( mBcData.expand( record, variables ).toStdString(), mW.pt(), mH.pt() ); glbarcode::QtRenderer renderer(painter); bc->render( renderer ); diff --git a/model/ModelBarcodeObject.h b/model/ModelBarcodeObject.h index 4931ddf..2a49304 100644 --- a/model/ModelBarcodeObject.h +++ b/model/ModelBarcodeObject.h @@ -51,6 +51,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const barcode::Style& bcStyle, bool bcTextFlag, bool bcChecksumFlag, @@ -126,8 +127,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -139,7 +148,12 @@ namespace glabels void update(); void drawBcInEditor( QPainter* painter, const QColor& color ) const; - void drawBc( QPainter* painter, const QColor& color, merge::Record* record ) const; + + void drawBc( QPainter* painter, + const QColor& color, + merge::Record* record, + Variables* variables ) const; + void drawPlaceHolder( QPainter* painter, const QColor& color, const QString& text ) const; diff --git a/model/ModelBoxObject.cpp b/model/ModelBoxObject.cpp index 94e81d5..4fd66e7 100644 --- a/model/ModelBoxObject.cpp +++ b/model/ModelBoxObject.cpp @@ -54,6 +54,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -63,7 +64,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelShapeObject( x0, y0, w, h, + : ModelShapeObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) @@ -103,11 +104,14 @@ namespace glabels /// /// Draw shadow of object /// - void ModelBoxObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelBoxObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -148,10 +152,13 @@ namespace glabels /// /// Draw object itself /// - void ModelBoxObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelBoxObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->setBrush( fillColor ); diff --git a/model/ModelBoxObject.h b/model/ModelBoxObject.h index 8705c99..21b5826 100644 --- a/model/ModelBoxObject.h +++ b/model/ModelBoxObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -72,8 +73,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; }; diff --git a/model/ModelEllipseObject.cpp b/model/ModelEllipseObject.cpp index c3ca82e..7e35791 100644 --- a/model/ModelEllipseObject.cpp +++ b/model/ModelEllipseObject.cpp @@ -54,6 +54,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -63,7 +64,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelShapeObject( x0, y0, w, h, + : ModelShapeObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) @@ -103,11 +104,14 @@ namespace glabels /// /// Draw shadow of object /// - void ModelEllipseObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelEllipseObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -148,10 +152,13 @@ namespace glabels /// /// Draw object itself /// - void ModelEllipseObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelEllipseObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->setBrush( fillColor ); diff --git a/model/ModelEllipseObject.h b/model/ModelEllipseObject.h index e26e9ba..26ae6d4 100644 --- a/model/ModelEllipseObject.h +++ b/model/ModelEllipseObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -72,8 +73,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; }; diff --git a/model/ModelImageObject.cpp b/model/ModelImageObject.cpp index 5848625..8bfd0e3 100644 --- a/model/ModelImageObject.cpp +++ b/model/ModelImageObject.cpp @@ -20,9 +20,11 @@ #include "ModelImageObject.h" +#include "Model.h" #include "Size.h" #include +#include #include #include #include @@ -40,6 +42,17 @@ namespace glabels QImage* ModelImageObject::smDefaultImage = nullptr; + // + // Private + // + namespace + { + const QColor fillColor = QColor( 224, 224, 224, 255 ); + const QColor labelColor = QColor( 102, 102, 102, 255 ); + const Distance pad = Distance::pt(2); + } + + /// /// Constructor /// @@ -70,6 +83,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const TextNode& filenameNode, const QMatrix& matrix, bool shadowState, @@ -77,7 +91,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -101,6 +115,8 @@ namespace glabels mImage = nullptr; mSvgRenderer = nullptr; + + loadImage(); } @@ -111,6 +127,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QImage& image, const QMatrix& matrix, @@ -119,7 +136,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -152,6 +169,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QByteArray& svg, const QMatrix& matrix, @@ -160,7 +178,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -395,27 +413,58 @@ namespace glabels /// /// Draw shadow of object /// - void ModelImageObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelImageObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { QRectF destRect( 0, 0, mW.pt(), mH.pt() ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); if ( mImage && mImage->hasAlphaChannel() && (mImage->depth() == 32) ) { - QImage* shadowImage = createShadowImage( shadowColor ); + QImage* shadowImage = createShadowImage( *mImage, shadowColor ); painter->drawImage( destRect, *shadowImage ); delete shadowImage; } + else if ( mImage || mSvgRenderer || inEditor ) + { + painter->setBrush( shadowColor ); + painter->setPen( QPen( Qt::NoPen ) ); + + painter->drawRect( destRect ); + } else { - if ( mImage || inEditor ) + QString filename = mFilenameNode.text( record, variables ); + QImage* image; + QSvgRenderer* svgRenderer; + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) { - painter->setBrush( shadowColor ); - painter->setPen( QPen( Qt::NoPen ) ); + if ( image && image->hasAlphaChannel() && (image->depth() == 32) ) + { + QImage* shadowImage = createShadowImage( *image, shadowColor ); + painter->drawImage( destRect, *shadowImage ); + delete shadowImage; + } + else + { + painter->setBrush( shadowColor ); + painter->setPen( QPen( Qt::NoPen ) ); - painter->drawRect( destRect ); + painter->drawRect( destRect ); + } + if ( image ) + { + delete image; + } + else + { + delete svgRenderer; + } } } } @@ -424,16 +473,70 @@ namespace glabels /// /// Draw object itself /// - void ModelImageObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelImageObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { QRectF destRect( 0, 0, mW.pt(), mH.pt() ); if ( inEditor && (mFilenameNode.isField() || (!mImage && !mSvgRenderer) ) ) { + // + // Render default place holder image + // painter->save(); painter->setRenderHint( QPainter::SmoothPixmapTransform, false ); painter->drawImage( destRect, *smDefaultImage ); painter->restore(); + + // + // Print label on top of place holder image, if we have room + // + if ( (mW > 6*pad) && (mH > 4*pad) ) + { + QString labelText = tr("No image"); + if ( mFilenameNode.isField() ) + { + labelText = QString( "${%1}" ).arg( mFilenameNode.data() ); + } + + // Determine font size for labelText + QFont font( "Sans" ); + font.setPointSizeF( 6 ); + + QFontMetricsF fm( font ); + QRectF textRect = fm.boundingRect( labelText ); + + double wPts = (mW - 2*pad).pt(); + double hPts = (mH - 2*pad).pt(); + if ( (wPts < textRect.width()) || (hPts < textRect.height()) ) + { + double scaleX = wPts / textRect.width(); + double scaleY = hPts / textRect.height(); + font.setPointSizeF( 6 * std::min( scaleX, scaleY ) ); + } + + // Render hole for text (font size may have changed above) + fm = QFontMetricsF( font ); + textRect = fm.boundingRect( labelText ); + + QRectF holeRect( (mW.pt() - textRect.width())/2 - pad.pt(), + (mH.pt() - textRect.height())/2 - pad.pt(), + textRect.width() + 2*pad.pt(), + textRect.height() + 2*pad.pt() ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( QBrush( fillColor ) ); + painter->drawRect( holeRect ); + + // Render text + painter->setFont( font ); + painter->setPen( QPen( labelColor ) ); + painter->drawText( QRectF( 0, 0, mW.pt(), mH.pt() ), + Qt::AlignCenter, + labelText ); + } } else if ( mImage ) { @@ -445,7 +548,23 @@ namespace glabels } else if ( mFilenameNode.isField() ) { - // TODO + QString filename = mFilenameNode.text( record, variables ); + QImage* image; + QSvgRenderer* svgRenderer; + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) + { + if ( image ) + { + painter->drawImage( destRect, *image ); + delete image; + } + else + { + svgRenderer->render( painter, destRect ); + delete svgRenderer; + } + } } } @@ -481,59 +600,32 @@ namespace glabels if ( !mFilenameNode.isField() ) { QString filename = mFilenameNode.data(); - QFileInfo fileInfo( filename ); - - if ( fileInfo.isReadable() ) + if ( readImageFile( filename, mImage, mSvgRenderer, mSvg ) ) { - if ( (fileInfo.suffix() == "svg") || (fileInfo.suffix() == "SVG") ) + double aspectRatio = 0; + if ( mSvgRenderer ) { - QFile file( filename ); - if ( file.open( QFile::ReadOnly ) ) - { - mSvg = file.readAll(); - file.close(); - mSvgRenderer = new QSvgRenderer( mSvg ); - if ( !mSvgRenderer->isValid() ) - { - mSvgRenderer = nullptr; - } - else - { - // Adjust size based on aspect ratio of SVG image - QRectF rect = mSvgRenderer->viewBoxF(); - double aspectRatio = rect.height() / rect.width(); - if ( mH > mW*aspectRatio ) - { - mH = mW*aspectRatio; - } - else - { - mW = mH/aspectRatio; - } - } - } + // Adjust size based on aspect ratio of SVG image + QRectF rect = mSvgRenderer->viewBoxF(); + aspectRatio = rect.width() ? rect.height() / rect.width() : 0; } else { - mImage = new QImage( filename ); - if ( mImage->isNull() ) + // Adjust size based on aspect ratio of image + double imageW = mImage->width(); + double imageH = mImage->height(); + aspectRatio = imageW ? imageH / imageW : 0; + } + + if ( aspectRatio ) + { + if ( mH > mW*aspectRatio ) { - mImage = nullptr; + mH = mW*aspectRatio; } else { - // Adjust size based on aspect ratio of image - double imageW = mImage->width(); - double imageH = mImage->height(); - double aspectRatio = imageH / imageW; - if ( mH > mW*aspectRatio ) - { - mH = mW*aspectRatio; - } - else - { - mW = mH/aspectRatio; - } + mW = mH/aspectRatio; } } } @@ -541,17 +633,75 @@ namespace glabels } + /// + /// Read an image or svg file + /// + bool ModelImageObject::readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const + { + image = nullptr; + svgRenderer = nullptr; + svg.clear(); + + if ( !fileName.isEmpty() ) + { + QFileInfo fileInfo( fileName ); + if ( fileInfo.isRelative() ) + { + // Look for image file relative to project file 1st then CWD 2nd + auto* model = dynamic_cast( parent() ); + QDir::setSearchPaths( "images", {model ? model->dirPath() : "", QDir::currentPath()} ); + fileInfo.setFile( QString("images:") + fileName ); + } + + if ( fileInfo.isReadable() ) + { + if ( fileInfo.suffix().toLower() == "svg" ) + { + QFile file( fileInfo.filePath() ); + if ( file.open( QFile::ReadOnly ) ) + { + svg = file.readAll(); + file.close(); + svgRenderer = new QSvgRenderer( svg ); + if ( !svgRenderer->isValid() ) + { + delete svgRenderer; + svgRenderer = nullptr; + svg.clear(); + } + } + } + else + { + image = new QImage( fileInfo.filePath() ); + if ( image->isNull() ) + { + delete image; + image = nullptr; + } + } + } + } + + return image != nullptr || svgRenderer != nullptr; + } + + /// /// Create shadow image /// - QImage* ModelImageObject::createShadowImage( const QColor& color ) const + QImage* ModelImageObject::createShadowImage( const QImage& image, + const QColor& color ) const { int r = color.red(); int g = color.green(); int b = color.blue(); int a = color.alpha(); - auto* shadow = new QImage( *mImage ); + auto* shadow = new QImage( image ); for ( int iy = 0; iy < shadow->height(); iy++ ) { auto* scanLine = (QRgb*)shadow->scanLine( iy ); diff --git a/model/ModelImageObject.h b/model/ModelImageObject.h index ba87078..3e982f5 100644 --- a/model/ModelImageObject.h +++ b/model/ModelImageObject.h @@ -49,6 +49,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const TextNode& filenameNode, const QMatrix& matrix = QMatrix(), bool shadowState = false, @@ -61,6 +62,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QImage& image, const QMatrix& matrix = QMatrix(), @@ -74,6 +76,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QByteArray& svg, const QMatrix& matrix = QMatrix(), @@ -132,8 +135,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -141,7 +152,14 @@ namespace glabels // Private /////////////////////////////////////////////////////////////// void loadImage(); - QImage* createShadowImage( const QColor& color ) const; + + bool readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const; + + QImage* createShadowImage( const QImage& image, + const QColor& color ) const; /////////////////////////////////////////////////////////////// diff --git a/model/ModelLineObject.cpp b/model/ModelLineObject.cpp index 5d7b5e1..5bef065 100644 --- a/model/ModelLineObject.cpp +++ b/model/ModelLineObject.cpp @@ -68,7 +68,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, dx, dy, + : ModelObject( x0, y0, dx, dy, false /*lockAspectRatio*/, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -186,10 +186,13 @@ namespace glabels /// /// Draw shadow of object /// - void ModelLineObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelLineObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -204,9 +207,12 @@ namespace glabels /// /// Draw object itself /// - void ModelLineObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelLineObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->drawLine( 0, 0, mW.pt(), mH.pt() ); diff --git a/model/ModelLineObject.h b/model/ModelLineObject.h index 16cfdb6..a09670e 100644 --- a/model/ModelLineObject.h +++ b/model/ModelLineObject.h @@ -97,8 +97,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; diff --git a/model/ModelObject.cpp b/model/ModelObject.cpp index 637352c..65ed72a 100644 --- a/model/ModelObject.cpp +++ b/model/ModelObject.cpp @@ -51,6 +51,7 @@ namespace glabels mY0 = 0; mW = 0; mH = 0; + mLockAspectRatio = false; mMatrix = QMatrix(); mShadowState = false; @@ -72,6 +73,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QMatrix& matrix, bool shadowState, const Distance& shadowX, @@ -85,6 +87,7 @@ namespace glabels mY0 = y0; mW = w; mH = h; + mLockAspectRatio = lockAspectRatio; mMatrix = matrix; mShadowState = shadowState; @@ -112,6 +115,7 @@ namespace glabels mY0 = object->mY0; mW = object->mW; mH = object->mH; + mLockAspectRatio = object->mLockAspectRatio; mShadowState = object->mShadowState; mShadowX = object->mShadowX; @@ -272,6 +276,28 @@ namespace glabels } + /// + /// Lock Aspect Ratio Property Getter + /// + bool ModelObject::lockAspectRatio() const + { + return mLockAspectRatio; + } + + + /// + /// Lock Aspect Ratio Property Setter + /// + void ModelObject::setLockAspectRatio( bool value ) + { + if ( mLockAspectRatio != value ) + { + mLockAspectRatio = value; + emit changed(); + } + } + + /// /// Matrix Property Getter /// @@ -1200,7 +1226,10 @@ namespace glabels /// /// Draw object + shadow /// - void ModelObject::draw( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelObject::draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { painter->save(); painter->translate( mX0.pt(), mY0.pt() ); @@ -1210,12 +1239,12 @@ namespace glabels painter->save(); painter->translate( mShadowX.pt(), mShadowY.pt() ); painter->setMatrix( mMatrix, true ); - drawShadow( painter, inEditor, record ); + drawShadow( painter, inEditor, record, variables ); painter->restore(); } painter->setMatrix( mMatrix, true ); - drawObject( painter, inEditor, record ); + drawObject( painter, inEditor, record, variables ); painter->restore(); } diff --git a/model/ModelObject.h b/model/ModelObject.h index 6af51e8..4962ffd 100644 --- a/model/ModelObject.h +++ b/model/ModelObject.h @@ -27,6 +27,7 @@ #include "Handles.h" #include "Outline.h" #include "TextNode.h" +#include "Variables.h" #include "barcode/Style.h" #include "merge/Record.h" @@ -64,6 +65,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio = false, const QMatrix& matrix = QMatrix(), bool shadowState = false, const Distance& shadowX = 0, @@ -136,6 +138,13 @@ namespace glabels void setH( const Distance& value ); + // + // Lock Aspect Ratio Property + // + bool lockAspectRatio() const; + void setLockAspectRatio( bool value ); + + // // Transformation Matrix Property // @@ -403,12 +412,24 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// public: - void draw( QPainter* painter, bool inEditor, merge::Record* record ) const; + void draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const; + void drawSelectionHighlight( QPainter* painter, double scale ) const; protected: - virtual void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const = 0; - virtual void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const = 0; + virtual void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const = 0; + + virtual void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const = 0; + virtual QPainterPath hoverPath( double scale ) const = 0; virtual void sizeUpdated(); @@ -424,6 +445,7 @@ namespace glabels Distance mY0; Distance mW; Distance mH; + bool mLockAspectRatio; bool mShadowState; Distance mShadowX; diff --git a/model/ModelShapeObject.cpp b/model/ModelShapeObject.cpp index 989f5af..afca487 100644 --- a/model/ModelShapeObject.cpp +++ b/model/ModelShapeObject.cpp @@ -58,6 +58,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -67,7 +68,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { diff --git a/model/ModelShapeObject.h b/model/ModelShapeObject.h index b33c5a3..888ac03 100644 --- a/model/ModelShapeObject.h +++ b/model/ModelShapeObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, diff --git a/model/ModelTextObject.cpp b/model/ModelTextObject.cpp index 434d48b..bb5c7e6 100644 --- a/model/ModelTextObject.cpp +++ b/model/ModelTextObject.cpp @@ -82,6 +82,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& text, const QString& fontFamily, double fontSize, @@ -100,7 +101,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -518,13 +519,14 @@ namespace glabels /// void ModelTextObject::drawShadow( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor textColor = mTextColorNode.color( record ); + QColor textColor = mTextColorNode.color( record, variables ); if ( textColor.alpha() ) { - QColor shadowColor = mShadowColorNode.color( record ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); if ( inEditor ) @@ -533,7 +535,7 @@ namespace glabels } else { - drawText( painter, shadowColor, record ); + drawText( painter, shadowColor, record, variables ); } } } @@ -544,9 +546,10 @@ namespace glabels /// void ModelTextObject::drawObject( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor textColor = mTextColorNode.color( record ); + QColor textColor = mTextColorNode.color( record, variables ); if ( inEditor ) { @@ -554,7 +557,7 @@ namespace glabels } else { - drawText( painter, textColor, record ); + drawText( painter, textColor, record, variables ); } } @@ -696,7 +699,8 @@ namespace glabels void ModelTextObject::drawText( QPainter* painter, const QColor& color, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { painter->save(); @@ -704,7 +708,7 @@ namespace glabels QFont font; font.setFamily( mFontFamily ); - font.setPointSizeF( mTextAutoShrink ? autoShrinkFontSize( record ) : mFontSize ); + font.setPointSizeF( mTextAutoShrink ? autoShrinkFontSize( record, variables ) : mFontSize ); font.setWeight( mFontWeight ); font.setItalic( mFontItalicFlag ); font.setUnderline( mFontUnderlineFlag ); @@ -716,7 +720,7 @@ namespace glabels QFontMetricsF fontMetrics( font ); double dy = fontMetrics.lineSpacing() * mTextLineSpacing; - QTextDocument document( mText.expand( record ) ); + QTextDocument document( mText.expand( record, variables ) ); QList layouts; @@ -790,7 +794,7 @@ namespace glabels /// Determine auto shrink font size /// double - ModelTextObject::autoShrinkFontSize( merge::Record* record ) const + ModelTextObject::autoShrinkFontSize( merge::Record* record, Variables* variables ) const { QFont font; font.setFamily( mFontFamily ); @@ -802,7 +806,7 @@ namespace glabels textOption.setAlignment( mTextHAlign ); textOption.setWrapMode( mTextWrapMode ); - QTextDocument document( mText.expand( record ) ); + QTextDocument document( mText.expand( record, variables ) ); double candidateSize = mFontSize; while ( candidateSize > 1.0 ) diff --git a/model/ModelTextObject.h b/model/ModelTextObject.h index 1ab62c6..0f7d1e9 100644 --- a/model/ModelTextObject.h +++ b/model/ModelTextObject.h @@ -50,6 +50,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& text, const QString& fontFamily, double fontSize, @@ -185,8 +186,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -196,10 +205,17 @@ namespace glabels private: void sizeUpdated() override; void update(); - void drawTextInEditor( QPainter* painter, const QColor& color ) const; - void drawText( QPainter* painter, const QColor&color, merge::Record* record ) const; - QString expandText( QString text, merge::Record* record ) const; - double autoShrinkFontSize( merge::Record* record ) const; + + void drawTextInEditor( QPainter* painter, + const QColor& color ) const; + + void drawText( QPainter* painter, + const QColor& color, + merge::Record* record, + Variables* variables ) const; + + double autoShrinkFontSize( merge::Record* record, + Variables* variables ) const; /////////////////////////////////////////////////////////////// diff --git a/model/PageRenderer.cpp b/model/PageRenderer.cpp index b450ff7..78a3383 100644 --- a/model/PageRenderer.cpp +++ b/model/PageRenderer.cpp @@ -47,7 +47,7 @@ namespace glabels PageRenderer::PageRenderer( const Model* model ) - : mModel(nullptr), mMerge(nullptr), mNCopies(0), mStartLabel(0), mLastLabel(0), + : mModel(nullptr), mMerge(nullptr), mVariables(nullptr), mNCopies(0), mStartLabel(0), mLastLabel(0), mPrintOutlines(false), mPrintCropMarks(false), mPrintReverse(false), mIPage(0), mIsMerge(false), mNPages(0), mNLabelsPerPage(0) { @@ -65,6 +65,7 @@ namespace glabels connect( mModel, SIGNAL(changed()), this, SLOT(onModelChanged()) ); onModelChanged(); + mVariables = mModel->variables(); } @@ -246,83 +247,107 @@ namespace glabels void PageRenderer::printSimplePage( QPainter* painter, int iPage ) const { - int iStart = 0; - int iEnd = mNLabelsPerPage; - - if ( iPage == 0 ) - { - iStart = mStartLabel; - } - - if ( (mLastLabel / mNLabelsPerPage) == iPage ) - { - iEnd = mLastLabel % mNLabelsPerPage; - } - printCropMarks( painter ); - for ( int i = iStart; i < iEnd; i++ ) + int iCopy = 0; + int iLabel = mStartLabel; + int iCurrentPage = 0; + mVariables->resetVariables(); + + while ( (iCopy < mNCopies) && (iCurrentPage <= iPage) ) { - painter->save(); + if ( iCurrentPage == iPage ) + { + int i = iLabel % mNLabelsPerPage; + + painter->save(); - painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); + painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); - painter->save(); + painter->save(); - clipLabel( painter ); - printLabel( painter, nullptr ); + clipLabel( painter ); + printLabel( painter, nullptr, mVariables ); - painter->restore(); // From before clip + painter->restore(); // From before clip - printOutline( painter ); + printOutline( painter ); - painter->restore(); // From before translation + painter->restore(); // From before translation + } + + iCopy++; + iLabel++; + iCurrentPage = iLabel / mNLabelsPerPage; + + mVariables->incrementVariablesOnItem(); + mVariables->incrementVariablesOnCopy(); + if ( (iLabel % mNLabelsPerPage) == 0 /* starting a new page */ ) + { + mVariables->incrementVariablesOnPage(); + } } } void PageRenderer::printMergePage( QPainter* painter, int iPage ) const { - int iRecord = 0; - int iStart = 0; - int iEnd = mNLabelsPerPage; - - if ( iPage == 0 ) - { - iStart = mStartLabel; - } - - if ( (mLastLabel / mNLabelsPerPage) == iPage ) - { - iEnd = mLastLabel % mNLabelsPerPage; - } - - const QList records = mMerge->selectedRecords(); - if ( records.size() ) - { - iRecord = (iPage*mNLabelsPerPage + iStart - mStartLabel) % records.size(); - } - printCropMarks( painter ); - for ( int i = iStart; i < iEnd; i++ ) + int iCopy = 0; + int iLabel = mStartLabel; + int iCurrentPage = 0; + + const QList records = mMerge->selectedRecords(); + int iRecord = 0; + int nRecords = records.size(); + + if ( nRecords == 0 ) { - painter->save(); - - painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); + return; + } - painter->save(); + mVariables->resetVariables(); - clipLabel( painter ); - printLabel( painter, records[iRecord] ); + while ( (iCopy < mNCopies) && (iCurrentPage <= iPage) ) + { + if ( iCurrentPage == iPage ) + { + int i = iLabel % mNLabelsPerPage; + + painter->save(); - painter->restore(); // From before clip - - printOutline( painter ); + painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); - painter->restore(); // From before translation + painter->save(); - iRecord = (iRecord + 1) % records.size(); + clipLabel( painter ); + printLabel( painter, records[iRecord], mVariables ); + + painter->restore(); // From before clip + + printOutline( painter ); + + painter->restore(); // From before translation + } + + iRecord = (iRecord + 1) % nRecords; + if ( iRecord == 0 ) + { + iCopy++; + } + iLabel++; + iCurrentPage = iLabel / mNLabelsPerPage; + + mVariables->incrementVariablesOnItem(); + if ( iRecord == 0 ) + { + mVariables->incrementVariablesOnCopy(); + } + if ( (iLabel % mNLabelsPerPage) == 0 /* starting a new page */ ) + { + mVariables->incrementVariablesOnPage(); + } } } @@ -408,7 +433,9 @@ namespace glabels } - void PageRenderer::printLabel( QPainter* painter, merge::Record* record ) const + void PageRenderer::printLabel( QPainter* painter, + merge::Record* record, + Variables* variables ) const { painter->save(); @@ -424,7 +451,7 @@ namespace glabels painter->scale( -1, 1 ); } - mModel->draw( painter, false, record ); + mModel->draw( painter, false, record, variables ); painter->restore(); } diff --git a/model/PageRenderer.h b/model/PageRenderer.h index 2db3ae4..08c93a9 100644 --- a/model/PageRenderer.h +++ b/model/PageRenderer.h @@ -23,6 +23,7 @@ #include "Point.h" +#include "Variables.h" #include "merge/Merge.h" #include "merge/Record.h" @@ -100,7 +101,7 @@ namespace glabels void printCropMarks( QPainter* painter ) const; void printOutline( QPainter* painter ) const; void clipLabel( QPainter* painter ) const; - void printLabel( QPainter* painter, merge::Record* record ) const; + void printLabel( QPainter* painter, merge::Record* record, Variables* variables ) const; ///////////////////////////////// @@ -109,6 +110,7 @@ namespace glabels private: const Model* mModel; const merge::Merge* mMerge; + Variables* mVariables; int mNCopies; int mStartLabel; diff --git a/model/RawText.cpp b/model/RawText.cpp index 08ee6bf..ed4cd29 100644 --- a/model/RawText.cpp +++ b/model/RawText.cpp @@ -66,7 +66,7 @@ namespace glabels /// /// Expand all place holders /// - QString RawText::expand( merge::Record* record ) const + QString RawText::expand( merge::Record* record, Variables* variables ) const { QString text; @@ -74,7 +74,7 @@ namespace glabels { if ( token.isField ) { - text += token.field.evaluate( record ); + text += token.field.evaluate( record, variables ); } else { diff --git a/model/RawText.h b/model/RawText.h index 9b19084..2e8ab44 100644 --- a/model/RawText.h +++ b/model/RawText.h @@ -52,7 +52,7 @@ namespace glabels ///////////////////////////////// QString toString() const; std::string toStdString() const; - QString expand( merge::Record* record ) const; + QString expand( merge::Record* record, Variables* variables ) const; bool hasPlaceHolders() const; bool isEmpty() const; diff --git a/model/SubstitutionField.cpp b/model/SubstitutionField.cpp index 178b73e..76c4047 100644 --- a/model/SubstitutionField.cpp +++ b/model/SubstitutionField.cpp @@ -42,21 +42,33 @@ namespace glabels } - QString SubstitutionField::evaluate( const merge::Record* record ) const + QString SubstitutionField::evaluate( const merge::Record* record, + const Variables* variables ) const { QString value = mDefaultValue; - if ( record && record->contains(mFieldName) && !record->value(mFieldName).isEmpty() ) + bool haveRecordField = record && + record->contains(mFieldName) && + !record->value(mFieldName).isEmpty(); + bool haveVariable = variables && + variables->contains(mFieldName) && + !(*variables)[mFieldName].value().isEmpty(); + + if ( haveRecordField ) { value = record->value(mFieldName); } + else if ( haveVariable ) + { + value = (*variables)[mFieldName].value(); + } if ( !mFormatType.isNull() ) { value = formatValue( value ); } - if ( record && record->contains(mFieldName) && !record->value(mFieldName).isEmpty() && mNewLine ) + if ( mNewLine && (haveRecordField || haveVariable) ) { value = "\n" + value; } diff --git a/model/SubstitutionField.h b/model/SubstitutionField.h index ec08a00..baaa255 100644 --- a/model/SubstitutionField.h +++ b/model/SubstitutionField.h @@ -21,6 +21,7 @@ #ifndef model_SubstitutionField_h #define model_SubstitutionField_h +#include "Variables.h" #include "merge/Record.h" @@ -39,7 +40,7 @@ namespace glabels SubstitutionField(); SubstitutionField( const QString& string ); - QString evaluate( const merge::Record* record ) const; + QString evaluate( const merge::Record* record, const Variables* variables ) const; QString fieldName() const; QString defaultValue() const; diff --git a/model/TextNode.cpp b/model/TextNode.cpp index 332367e..b0e2271 100644 --- a/model/TextNode.cpp +++ b/model/TextNode.cpp @@ -105,48 +105,34 @@ namespace glabels /// /// Get text, expand if necessary /// - QString TextNode::text( merge::Record* record ) const + QString TextNode::text( const merge::Record* record, + const Variables* variables ) const { - if ( mIsField ) + QString value(""); + + bool haveRecordField = mIsField && record && + record->contains(mData) && + !record->value(mData).isEmpty(); + bool haveVariable = mIsField && variables && + variables->contains(mData) && + !(*variables)[mData].value().isEmpty(); + + if ( haveRecordField ) { - if ( !record ) - { - return QString("${%1}").arg( mData ); - } - else - { - if ( record->contains( mData ) ) - { - return (*record)[ mData ]; - } - else - { - return ""; - } - } + value = record->value(mData); } - else + else if ( haveVariable ) { - return mData; + value = (*variables)[mData].value(); } + else if ( !mIsField ) + { + value = mData; + } + + return value; } - /// - /// Is it an empty field - /// - bool TextNode::isEmptyField( merge::Record* record ) const - { - if ( record && mIsField ) - { - if ( record->contains( mData ) ) - { - return (*record)[mData].isEmpty(); - } - } - - return false; - } - } } diff --git a/model/TextNode.h b/model/TextNode.h index 499f863..c7716d7 100644 --- a/model/TextNode.h +++ b/model/TextNode.h @@ -22,6 +22,7 @@ #define model_TextNode_h +#include "Variables.h" #include "merge/Record.h" #include @@ -76,8 +77,8 @@ namespace glabels ///////////////////////////////// // Misc. Methods ///////////////////////////////// - QString text( merge::Record* record ) const; - bool isEmptyField( merge::Record* record ) const; + QString text( const merge::Record* record, + const Variables* variables ) const; ///////////////////////////////// diff --git a/model/Variable.cpp b/model/Variable.cpp new file mode 100644 index 0000000..fa96462 --- /dev/null +++ b/model/Variable.cpp @@ -0,0 +1,323 @@ +/* Variable.cpp + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "Variable.h" + + +namespace glabels +{ + namespace model + { + + Variable::Variable() + : mType(Type::STRING), + mIncrement(Increment::NEVER), + mStepSize("0"), + mIntegerValue(0), + mIntegerStep(0), + mFloatingPointValue(0), + mFloatingPointStep(0) + { + // empty + } + + + Variable::Variable( Variable::Type type, + const QString& name, + const QString& initialValue, + Variable::Increment increment, + const QString& stepSize ) + : mType(type), + mName(name), + mInitialValue(initialValue), + mIncrement(increment), + mStepSize(stepSize), + mIntegerValue(0), + mIntegerStep(0), + mFloatingPointValue(0), + mFloatingPointStep(0) + { + resetValue(); + } + + + Variable::Type Variable::type() const + { + return mType; + } + + + QString Variable::name() const + { + return mName; + } + + + QString Variable::initialValue() const + { + return mInitialValue; + } + + + Variable::Increment Variable::increment() const + { + return mIncrement; + } + + + QString Variable::stepSize() const + { + return mStepSize; + } + + + void Variable::resetValue() + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue = mInitialValue.toLongLong(); + mIntegerStep = mStepSize.toLongLong(); + break; + case Type::FLOATING_POINT: + mFloatingPointValue = mInitialValue.toDouble(); + mFloatingPointStep = mStepSize.toDouble(); + break; + case Type::COLOR: + // do nothing + break; + } + } + + + void Variable::incrementValueOnItem() + { + if ( mIncrement == Increment::PER_ITEM ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + void Variable::incrementValueOnCopy() + { + if ( mIncrement == Increment::PER_COPY ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + void Variable::incrementValueOnPage() + { + if ( mIncrement == Increment::PER_PAGE ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + QString Variable::value() const + { + switch (mType) + { + case Type::STRING: + return mInitialValue; + case Type::INTEGER: + return QString::number( mIntegerValue ); + case Type::FLOATING_POINT: + return QString::number( mFloatingPointValue, 'g', 15 ); + case Type::COLOR: + return mInitialValue; + default: + return mInitialValue; + } + } + + + QString Variable::typeToI18nString( Type type ) + { + switch (type) + { + case Type::STRING: + return tr("String"); + case Type::INTEGER: + return tr("Integer"); + case Type::FLOATING_POINT: + return tr("Floating Point"); + case Type::COLOR: + return tr("Color"); + default: + return tr("String"); + } + } + + + QString Variable::typeToIdString( Type type ) + { + switch (type) + { + case Type::STRING: + return "string"; + case Type::INTEGER: + return "integer"; + case Type::FLOATING_POINT: + return "float"; + case Type::COLOR: + return "color"; + default: + return "string"; + } + } + + + Variable::Type Variable::idStringToType( const QString& id ) + { + if ( id == "string" ) + { + return Type::STRING; + } + else if ( id == "integer" ) + { + return Type::INTEGER; + } + else if ( id == "float" ) + { + return Type::FLOATING_POINT; + } + if ( id == "color" ) + { + return Type::COLOR; + } + else + { + return Type::STRING; // Default + } + } + + + QString Variable::incrementToI18nString( Increment increment ) + { + switch (increment) + { + case Increment::NEVER: + return tr("Never"); + case Increment::PER_ITEM: + return tr("Per item"); + case Increment::PER_COPY: + return tr("Per copy"); + case Increment::PER_PAGE: + return tr("Per page"); + default: + return tr("Never"); + } + } + + + QString Variable::incrementToIdString( Increment increment ) + { + switch (increment) + { + case Increment::NEVER: + return "never"; + case Increment::PER_ITEM: + return "per_item"; + case Increment::PER_COPY: + return "per_copy"; + case Increment::PER_PAGE: + return "per_page"; + default: + return "never"; + } + } + + + Variable::Increment Variable::idStringToIncrement( const QString& id ) + { + if ( id == "never" ) + { + return Increment::NEVER; + } + else if ( id == "per_item" ) + { + return Increment::PER_ITEM; + } + else if ( id == "per_copy" ) + { + return Increment::PER_COPY; + } + else if ( id == "per_page" ) + { + return Increment::PER_PAGE; + } + else + { + return Increment::NEVER; // Default + } + } + + + } +} diff --git a/model/Variable.h b/model/Variable.h new file mode 100644 index 0000000..c18f74f --- /dev/null +++ b/model/Variable.h @@ -0,0 +1,107 @@ +/* Variable.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_Variable_h +#define model_Variable_h + + +#include +#include + + +namespace glabels +{ + namespace model + { + + class Variable + { + Q_DECLARE_TR_FUNCTIONS(Variable) + + public: + enum class Type + { + STRING, + INTEGER, + FLOATING_POINT, + COLOR + }; + + enum class Increment + { + NEVER, + PER_ITEM, + PER_COPY, + PER_PAGE + }; + + + public: + Variable(); + + Variable( Type type, + const QString& name, + const QString& initialValue, + Increment increment = Increment::NEVER, + const QString& stepSize = "0" ); + + virtual ~Variable() = default; + + + Type type() const; + QString name() const; + QString initialValue() const; + Increment increment() const; + QString stepSize() const; + + void resetValue(); + void incrementValueOnItem(); + void incrementValueOnCopy(); + void incrementValueOnPage(); + QString value() const; + + static QString typeToI18nString( Type type ); + static QString typeToIdString( Type type ); + static Type idStringToType( const QString& string ); + + static QString incrementToI18nString( Increment increment ); + static QString incrementToIdString( Increment increment ); + static Increment idStringToIncrement( const QString& string ); + + + private: + Type mType; + QString mName; + QString mInitialValue; + Increment mIncrement; + QString mStepSize; + + long long mIntegerValue; + long long mIntegerStep; + double mFloatingPointValue; + double mFloatingPointStep; + + }; + + } +} + + +#endif // model_Variable_h diff --git a/model/Variables.cpp b/model/Variables.cpp new file mode 100644 index 0000000..03fa927 --- /dev/null +++ b/model/Variables.cpp @@ -0,0 +1,138 @@ +/* Variables.cpp + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "Variables.h" + +#include + + +namespace glabels +{ + namespace model + { + /// + /// Copy constructor + /// + Variables::Variables( const Variables* variables ) + : QMap(*variables) + { + } + + + /// + /// Clone + /// + Variables* Variables::clone() const + { + return new Variables( this ); + } + + + /// + /// Do we have variable? + /// + bool Variables::hasVariable( const QString& name ) const + { + return contains(name); + } + + + /// + /// Add variable ( will replace if name is the same ) + /// + void Variables::addVariable( const Variable& variable ) + { + insert( variable.name(), variable ); + emit changed(); + } + + + /// + /// Delete variable + /// + void Variables::deleteVariable( const QString& name ) + { + remove( name ); + emit changed(); + } + + + /// + /// Replace variable + /// + void Variables::replaceVariable( const QString& origName, const Variable& variable ) + { + remove( origName ); + insert( variable.name(), variable ); + emit changed(); + } + + + /// + /// Reset variables to their initial values + /// + void Variables::resetVariables() + { + for ( auto& v : *this ) + { + v.resetValue(); + } + } + + + /// + /// Increment variables on item + /// + void Variables::incrementVariablesOnItem() + { + for ( auto& v : *this ) + { + v.incrementValueOnItem(); + } + } + + + /// + /// Increment variables on copy + /// + void Variables::incrementVariablesOnCopy() + { + for ( auto& v : *this ) + { + v.incrementValueOnCopy(); + } + } + + + /// + /// Increment variables on page + /// + void Variables::incrementVariablesOnPage() + { + for ( auto& v : *this ) + { + v.incrementValueOnPage(); + } + } + + + } // namespace model + +} // namespace glabels diff --git a/model/Variables.h b/model/Variables.h new file mode 100644 index 0000000..379d56f --- /dev/null +++ b/model/Variables.h @@ -0,0 +1,90 @@ +/* Variables.h + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_Variables_h +#define model_Variables_h + + +#include "Variable.h" + +#include +#include +#include + + +namespace glabels +{ + namespace model + { + + /// + /// Variables Collection + /// + class Variables : public QObject, public QMap + { + Q_OBJECT + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + Variables() = default; + Variables( const Variables* variables ); + + + ///////////////////////////////// + // Object duplication + ///////////////////////////////// + Variables* clone() const; + + + ///////////////////////////////// + // Methods + ///////////////////////////////// + bool hasVariable( const QString& name ) const; + void addVariable( const Variable& variable ); + void deleteVariable( const QString& name ); + void replaceVariable( const QString& name, const Variable& variable ); + + void resetVariables(); + void incrementVariablesOnItem(); + void incrementVariablesOnCopy(); + void incrementVariablesOnPage(); + + + ///////////////////////////////// + // Signals + ///////////////////////////////// + signals: + void changed(); + + + ///////////////////////////////// + // Private data + ///////////////////////////////// + private: + + }; + + } +} + + +#endif // model_Variables_h diff --git a/model/Version.h.in b/model/Version.h.in index b9cf033..0444361 100644 --- a/model/Version.h.in +++ b/model/Version.h.in @@ -37,6 +37,7 @@ namespace glabels const int MICRO = @glabels-qt_VERSION_PATCH@; const QString STRING = "@VERSION_STRING@"; + const QString LONG_STRING = "@LONG_VERSION_STRING@"; } } diff --git a/model/XmlLabelCreator.cpp b/model/XmlLabelCreator.cpp index 87b3114..b7f7cc9 100644 --- a/model/XmlLabelCreator.cpp +++ b/model/XmlLabelCreator.cpp @@ -29,9 +29,12 @@ #include "ModelImageObject.h" #include "ModelTextObject.h" #include "DataCache.h" +#include "FileUtil.h" +#include "Variables.h" #include "XmlTemplateCreator.h" #include "XmlUtil.h" +#include "merge/Factory.h" #include "merge/None.h" #include @@ -48,38 +51,41 @@ namespace glabels { void - XmlLabelCreator::writeFile( const Model* label, const QString& fileName ) + XmlLabelCreator::writeFile( Model* model, const QString& fileName ) { - QDomDocument doc; - - createDoc( doc, label ); - QByteArray buffer = doc.toByteArray( 2 ); - QFile file( fileName ); - if ( !file.open( QFile::WriteOnly | QFile::Text) ) { qWarning() << "Error: Cannot write file " << fileName << ": " << file.errorString(); + return; } + model->setFileName( fileName ); + model->clearModified(); + + QDomDocument doc; + createDoc( doc, model ); + + QByteArray buffer = doc.toByteArray( 2 ); file.write( buffer.data(), buffer.size() ); } void - XmlLabelCreator::writeBuffer( const Model* label, QByteArray& buffer ) + XmlLabelCreator::writeBuffer( const Model* model, QByteArray& buffer ) { QDomDocument doc; - createDoc( doc, label ); + createDoc( doc, model ); buffer = doc.toByteArray( 2 ); } void XmlLabelCreator::serializeObjects( const QList& objects, - QByteArray& buffer ) + const Model* model, + QByteArray& buffer ) { QDomDocument doc; @@ -90,15 +96,15 @@ namespace glabels doc.appendChild( root ); XmlUtil::setStringAttr( root, "version", "4.0" ); - createDataNode( root, objects ); - createObjectsNode( root, objects, false ); + createDataNode( root, model, objects ); + createObjectsNode( root, model, objects, false ); buffer = doc.toByteArray( 2 ); } void - XmlLabelCreator::createDoc( QDomDocument& doc, const Model* label ) + XmlLabelCreator::createDoc( QDomDocument& doc, const Model* model ) { QDomNode xmlNode( doc.createProcessingInstruction( "xml", "version=\"1.0\"" ) ); doc.appendChild( xmlNode ); @@ -107,21 +113,29 @@ namespace glabels doc.appendChild( root ); XmlUtil::setStringAttr( root, "version", "4.0" ); - XmlTemplateCreator().createTemplateNode( root, label->tmplate() ); + XmlTemplateCreator().createTemplateNode( root, model->tmplate() ); - createObjectsNode( root, label->objectList(), label->rotate() ); + createObjectsNode( root, model, model->objectList(), model->rotate() ); - if ( label->merge() && !dynamic_cast(label->merge()) ) + if ( model->merge() && !dynamic_cast(model->merge()) ) { - createMergeNode( root, label ); + createMergeNode( root, model ); } - createDataNode( root, label->objectList() ); + if ( model->variables()->size() != 0 ) + { + createVariablesNode( root, model ); + } + + createDataNode( root, model, model->objectList() ); } void - XmlLabelCreator::createObjectsNode( QDomElement &parent, const QList& objects, bool rotate ) + XmlLabelCreator::createObjectsNode( QDomElement& parent, + const Model* model, + const QList& objects, + bool rotate ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Objects" ); @@ -146,7 +160,7 @@ namespace glabels } else if ( auto* imageObject = dynamic_cast(object) ) { - createObjectImageNode( node, imageObject ); + createObjectImageNode( node, model, imageObject ); } else if ( auto* barcodeObject = dynamic_cast(object) ) { @@ -244,7 +258,9 @@ namespace glabels void - XmlLabelCreator::createObjectImageNode( QDomElement &parent, const ModelImageObject* object ) + XmlLabelCreator::createObjectImageNode( QDomElement& parent, + const Model* model, + const ModelImageObject* object ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Object-image" ); @@ -263,7 +279,8 @@ namespace glabels } else { - XmlUtil::setStringAttr( node, "src", object->filenameNode().data() ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), object->filenameNode().data() ); + XmlUtil::setStringAttr( node, "src", fn ); } /* affine attrs */ @@ -384,6 +401,7 @@ namespace glabels { XmlUtil::setLengthAttr( node, "w", object->w() ); XmlUtil::setLengthAttr( node, "h", object->h() ); + XmlUtil::setBoolAttr( node, "lock_aspect_ratio", object->lockAspectRatio() ); } @@ -433,41 +451,99 @@ namespace glabels void XmlLabelCreator::createShadowAttrs( QDomElement &node, const ModelObject* object ) { - if ( object->shadow() ) + XmlUtil::setBoolAttr( node, "shadow", object->shadow() ); + + XmlUtil::setLengthAttr( node, "shadow_x", object->shadowX() ); + XmlUtil::setLengthAttr( node, "shadow_y", object->shadowY() ); + + if ( object->fillColorNode().isField() ) { - XmlUtil::setBoolAttr( node, "shadow", object->shadow() ); - - XmlUtil::setLengthAttr( node, "shadow_x", object->shadowX() ); - XmlUtil::setLengthAttr( node, "shadow_y", object->shadowY() ); - - if ( object->fillColorNode().isField() ) - { - XmlUtil::setStringAttr( node, "shadow_color_field", object->shadowColorNode().key() ); - } - else - { - XmlUtil::setUIntAttr( node, "shadow_color", object->shadowColorNode().rgba() ); - } - - XmlUtil::setDoubleAttr( node, "shadow_opacity", object->shadowOpacity() ); + XmlUtil::setStringAttr( node, "shadow_color_field", object->shadowColorNode().key() ); } + else + { + XmlUtil::setUIntAttr( node, "shadow_color", object->shadowColorNode().rgba() ); + } + + XmlUtil::setDoubleAttr( node, "shadow_opacity", object->shadowOpacity() ); } void - XmlLabelCreator::createMergeNode( QDomElement &parent, const Model* label ) + XmlLabelCreator::createMergeNode( QDomElement &parent, const Model* model ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Merge" ); parent.appendChild( node ); - XmlUtil::setStringAttr( node, "type", label->merge()->id() ); - XmlUtil::setStringAttr( node, "src", label->merge()->source() ); + QString id = model->merge()->id(); + QString src = model->merge()->source(); + + XmlUtil::setStringAttr( node, "type", id ); + + switch ( merge::Factory::idToType( id ) ) + { + case merge::Factory::NONE: + case merge::Factory::FIXED: + break; + + case merge::Factory::FILE: + { + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), src ); + XmlUtil::setStringAttr( node, "src", fn ); + } + break; + + default: + qWarning() << "XmlLabelCreator::createMergeNode(): Should not be reached!"; + break; + } } void - XmlLabelCreator::createDataNode( QDomElement &parent, const QList& objects ) + XmlLabelCreator::createVariablesNode( QDomElement &parent, const Model* model ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Variables" ); + parent.appendChild( node ); + + for ( const auto& v : *model->variables() ) + { + createVariableNode( node, v ); + } + } + + + void + XmlLabelCreator::createVariableNode( QDomElement &parent, const Variable& v ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Variable" ); + parent.appendChild( node ); + + XmlUtil::setStringAttr( node, "type", Variable::typeToIdString( v.type() ) ); + XmlUtil::setStringAttr( node, "name", v.name() ); + XmlUtil::setStringAttr( node, "initialValue", v.initialValue() ); + + if ( (v.type() == Variable::Type::INTEGER) || + (v.type() == Variable::Type::FLOATING_POINT) ) + { + XmlUtil::setStringAttr( node, "increment", + Variable::incrementToIdString( v.increment() ) ); + + if ( v.increment() != Variable::Increment::NEVER ) + { + XmlUtil::setStringAttr( node, "stepSize", v.stepSize() ); + } + } + } + + + void + XmlLabelCreator::createDataNode( QDomElement& parent, + const Model* model, + const QList& objects ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Data" ); @@ -477,12 +553,14 @@ namespace glabels foreach ( QString name, data.imageNames() ) { - createPngFileNode( node, name, data.getImage( name ) ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), name ); + createPngFileNode( node, fn, data.getImage( name ) ); } foreach ( QString name, data.svgNames() ) { - createSvgFileNode( node, name, data.getSvg( name ) ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), name ); + createSvgFileNode( node, fn, data.getSvg( name ) ); } } @@ -521,7 +599,5 @@ namespace glabels node.appendChild( doc.createCDATASection( QString( svg ) ) ); } - - } } diff --git a/model/XmlLabelCreator.h b/model/XmlLabelCreator.h index 93c235e..9c765ac 100644 --- a/model/XmlLabelCreator.h +++ b/model/XmlLabelCreator.h @@ -40,6 +40,7 @@ namespace glabels class ModelImageObject; class ModelBarcodeObject; class ModelTextObject; + class Variable; /// @@ -50,31 +51,87 @@ namespace glabels Q_OBJECT public: - static void writeFile( const Model* label, const QString& fileName ); - static void writeBuffer( const Model* label, QByteArray& buffer ); - static void serializeObjects( const QList& objects, QByteArray& buffer ); + static void writeFile( Model* model, + const QString& fileName ); + + static void writeBuffer( const Model* model, + QByteArray& buffer ); + + static void serializeObjects( const QList& objects, + const Model* model, + QByteArray& buffer ); private: - static void createDoc( QDomDocument& doc, const Model* label ); - static void createRootNode( const Model* label ); - static void createObjectsNode( QDomElement &parent, const QList& objects, bool rotate ); - static void createObjectBoxNode( QDomElement &parent, const ModelBoxObject* object ); - static void createObjectEllipseNode( QDomElement &parent, const ModelEllipseObject* object ); - static void createObjectLineNode( QDomElement &parent, const ModelLineObject* object ); - static void createObjectImageNode( QDomElement &parent, const ModelImageObject* object ); - static void createObjectBarcodeNode( QDomElement &parent, const ModelBarcodeObject* object ); - static void createObjectTextNode( QDomElement &parent, const ModelTextObject* object ); - static void createPNode( QDomElement &parent, const QString& blockText ); - static void createPositionAttrs( QDomElement &node, const ModelObject* object ); - static void createSizeAttrs( QDomElement &node, const ModelObject* object ); - static void createLineAttrs( QDomElement &node, const ModelObject* object ); - static void createFillAttrs( QDomElement &node, const ModelObject* object ); - static void createAffineAttrs( QDomElement &node, const ModelObject* object ); - static void createShadowAttrs( QDomElement &node, const ModelObject* object ); - static void createMergeNode( QDomElement &parent, const Model* label ); - static void createDataNode( QDomElement &parent, const QList& objects ); - static void createPngFileNode( QDomElement &parent, const QString& name, const QImage& image ); - static void createSvgFileNode( QDomElement &parent, const QString& name, const QByteArray& svg ); + static void createDoc( QDomDocument& doc, + const Model* model ); + + static void createRootNode( const Model* model ); + + static void createObjectsNode( QDomElement& parent, + const Model* model, + const QList& objects, + bool rotate ); + + static void createObjectBoxNode( QDomElement& parent, + const ModelBoxObject* object ); + + static void createObjectEllipseNode( QDomElement& parent, + const ModelEllipseObject* object ); + + static void createObjectLineNode( QDomElement& parent, + const ModelLineObject* object ); + + static void createObjectImageNode( QDomElement& parent, + const Model* model, + const ModelImageObject* object ); + + static void createObjectBarcodeNode( QDomElement& parent, + const ModelBarcodeObject* object ); + + static void createObjectTextNode( QDomElement& parent, + const ModelTextObject* object ); + + static void createPNode( QDomElement& parent, + const QString& blockText ); + + static void createPositionAttrs( QDomElement& node, + const ModelObject* object ); + + static void createSizeAttrs( QDomElement& node, + const ModelObject* object ); + + static void createLineAttrs( QDomElement& node, + const ModelObject* object ); + + static void createFillAttrs( QDomElement& node, + const ModelObject* object ); + + static void createAffineAttrs( QDomElement& node, + const ModelObject* object ); + + static void createShadowAttrs( QDomElement& node, + const ModelObject* object ); + + static void createMergeNode( QDomElement& parent, + const Model* model ); + + static void createVariablesNode( QDomElement& parent, + const Model* model ); + + static void createVariableNode( QDomElement& parent, + const Variable& v ); + + static void createDataNode( QDomElement& parent, + const Model* model, + const QList& objects ); + + static void createPngFileNode( QDomElement& parent, + const QString& name, + const QImage& image ); + + static void createSvgFileNode( QDomElement& parent, + const QString& name, + const QByteArray& svg ); }; diff --git a/model/XmlLabelParser.cpp b/model/XmlLabelParser.cpp index 779149c..fce0f2d 100644 --- a/model/XmlLabelParser.cpp +++ b/model/XmlLabelParser.cpp @@ -105,7 +105,7 @@ namespace glabels return nullptr; } - return parseRootNode( root ); + return parseRootNode( root, fileName ); } @@ -132,12 +132,12 @@ namespace glabels return nullptr; } - return parseRootNode( root ); + return parseRootNode( root, QString() ); } QList - XmlLabelParser::deserializeObjects( const QByteArray& buffer ) + XmlLabelParser::deserializeObjects( const QByteArray& buffer, const Model* model ) { QList list; @@ -167,7 +167,7 @@ namespace glabels { if ( child.toElement().tagName() == "Data" ) { - parseDataNode( child.toElement(), data ); + parseDataNode( child.toElement(), model, data ); } } @@ -176,9 +176,10 @@ namespace glabels { if ( child.toElement().tagName() == "Objects" ) { - list = parseObjectsNode( child.toElement(), data ); + list = parseObjectsNode( child.toElement(), model, data ); } } + return list; } @@ -236,16 +237,22 @@ namespace glabels Model* - XmlLabelParser::parseRootNode( const QDomElement &node ) + XmlLabelParser::parseRootNode( const QDomElement &node, const QString& fileName ) { QString version = XmlUtil::getStringAttr( node, "version", "" ); if ( version != "4.0" ) { // Attempt to import as version 3.0 format (glabels 2.0 - glabels 3.4) - return XmlLabelParser_3::parseRootNode(node); + auto* model = XmlLabelParser_3::parseRootNode( node ); + if ( model ) + { + model->setFileName( fileName ); + } + return model; } - auto* label = new Model(); + auto* model = new Model(); + model->setFileName( fileName ); /* Pass 1, extract data nodes to pre-load cache. */ DataCache data; @@ -253,7 +260,7 @@ namespace glabels { if ( child.toElement().tagName() == "Data" ) { - parseDataNode( child.toElement(), data ); + parseDataNode( child.toElement(), model, data ); } } @@ -268,23 +275,28 @@ namespace glabels if ( tmplate == nullptr ) { qWarning() << "Unable to parse template"; - delete label; + delete model; return nullptr; } - label->setTmplate( tmplate ); + model->setTmplate( tmplate ); // Copies arg + delete tmplate; } else if ( tagName == "Objects" ) { - label->setRotate( parseRotateAttr( child.toElement() ) ); - QList list = parseObjectsNode( child.toElement(), data ); + model->setRotate( parseRotateAttr( child.toElement() ) ); + auto list = parseObjectsNode( child.toElement(), model, data ); foreach ( ModelObject* object, list ) { - label->addObject( object ); + model->addObject( object ); } } else if ( tagName == "Merge" ) { - parseMergeNode( child.toElement(), label ); + parseMergeNode( child.toElement(), model ); + } + else if ( tagName == "Variables" ) + { + parseVariablesNode( child.toElement(), model ); } else if ( tagName == "Data" ) { @@ -296,13 +308,15 @@ namespace glabels } } - label->clearModified(); - return label; + model->clearModified(); + return model; } QList - XmlLabelParser::parseObjectsNode( const QDomElement &node, const DataCache& data ) + XmlLabelParser::parseObjectsNode( const QDomElement& node, + const Model* model, + const DataCache& data ) { QList list; @@ -328,7 +342,7 @@ namespace glabels } else if ( tagName == "Object-image" ) { - list.append( parseObjectImageNode( child.toElement(), data ) ); + list.append( parseObjectImageNode( child.toElement(), model, data ) ); } else if ( tagName == "Object-barcode" ) { @@ -354,19 +368,20 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + color = XmlUtil::getUIntAttr( node, "fill_color", 0xFF ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ @@ -386,10 +401,10 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); - return new ModelBoxObject( x0, y0, w, h, + return new ModelBoxObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), @@ -407,19 +422,20 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + color = XmlUtil::getUIntAttr( node, "fill_color", 0xFF ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ @@ -439,10 +455,10 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); - return new ModelEllipseObject( x0, y0, w, h, + return new ModelEllipseObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), @@ -466,7 +482,7 @@ namespace glabels QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* affine attrs */ @@ -486,7 +502,7 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); return new ModelLineObject( x0, y0, dx, dy, @@ -497,7 +513,9 @@ namespace glabels ModelImageObject* - XmlLabelParser::parseObjectImageNode( const QDomElement &node, const DataCache& data ) + XmlLabelParser::parseObjectImageNode( const QDomElement& node, + const Model* model, + const DataCache& data ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); @@ -506,6 +524,7 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* file attrs */ QString key = XmlUtil::getStringAttr( node, "src_field", "" ); @@ -530,36 +549,42 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); if ( filenameNode.isField() ) { - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { - if ( data.hasImage( filename ) ) + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( filename ) ); + + if ( data.hasImage( fn ) ) { - return new ModelImageObject( x0, y0, w, h, - filename, data.getImage( filename ), + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, + filename, data.getImage( fn ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } - else if ( data.hasSvg( filename ) ) + else if ( data.hasSvg( fn ) ) { - return new ModelImageObject( x0, y0, w, h, - filename, data.getSvg( filename ), + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, + filename, data.getSvg( fn ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { - qWarning() << "Embedded file" << filename << "missing. Trying actual file."; - return new ModelImageObject( x0, y0, w, h, + if ( !filename.isEmpty() ) + { + qWarning() << "Embedded file" << fn << "missing. Trying actual file."; + filenameNode.setData( fn ); + } + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); @@ -578,6 +603,7 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* barcode attrs */ barcode::Style bcStyle = barcode::Backends::style( XmlUtil::getStringAttr( node, "backend", "" ), @@ -587,7 +613,7 @@ namespace glabels QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "color", 0xFF ); ColorNode bcColorNode( field_flag, color, key ); QString bcData = XmlUtil::getStringAttr( node, "data", "" ); @@ -601,7 +627,7 @@ namespace glabels a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); - return new ModelBarcodeObject( x0, y0, w, h, + return new ModelBarcodeObject( x0, y0, w, h, lockAspectRatio, bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ) ); } @@ -617,11 +643,12 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* color attr */ QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "color", 0xFF ); ColorNode textColorNode( field_flag, color, key ); /* font attrs */ @@ -655,7 +682,7 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); /* deserialize contents. */ @@ -682,7 +709,7 @@ namespace glabels } QString text = document.toPlainText(); - return new ModelTextObject( x0, y0, w, h, + return new ModelTextObject( x0, y0, w, h, lockAspectRatio, text, fontFamily, fontSize, fontWeight, fontItalicFlag, fontUnderlineFlag, textColorNode, textHAlign, textVAlign, textWrapMode, textLineSpacing, @@ -707,28 +734,45 @@ namespace glabels void - XmlLabelParser::parseMergeNode( const QDomElement &node, Model* label ) + XmlLabelParser::parseMergeNode( const QDomElement &node, Model* model ) { - QString type = XmlUtil::getStringAttr( node, "type", "None" ); - QString src = XmlUtil::getStringAttr( node, "src", "" ); + QString id = XmlUtil::getStringAttr( node, "type", "None" ); + QString src = XmlUtil::getStringAttr( node, "src", "" ); - merge::Merge* merge = merge::Factory::createMerge( type ); - merge->setSource( src ); + merge::Merge* merge = merge::Factory::createMerge( id ); - label->setMerge( merge ); + switch ( merge::Factory::idToType( id ) ) + { + case merge::Factory::NONE: + case merge::Factory::FIXED: + break; + + case merge::Factory::FILE: + { + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( src ) ); + merge->setSource( fn ); + } + break; + + default: + qWarning() << "XmlLabelParser::parseMergeNode(): Should not be reached!"; + break; + } + + model->setMerge( merge ); } void - XmlLabelParser::parseDataNode( const QDomElement &node, DataCache& data ) + XmlLabelParser::parseVariablesNode( const QDomElement &node, Model* model ) { for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); - if ( tagName == "File" ) + if ( tagName == "Variable" ) { - parseFileNode( child.toElement(), data ); + parseVariableNode( child.toElement(), model ); } else if ( !child.isComment() ) { @@ -739,19 +783,55 @@ namespace glabels void - XmlLabelParser::parsePixdataNode( const QDomElement& node, DataCache& data ) + XmlLabelParser::parseVariableNode( const QDomElement &node, Model* model ) { - // TODO, compatibility with glabels-3 + QString typeString = XmlUtil::getStringAttr( node, "type", "string" ); + QString name = XmlUtil::getStringAttr( node, "name", "unknown" ); + QString initialValue = XmlUtil::getStringAttr( node, "initialValue", "0" ); + QString incrementString = XmlUtil::getStringAttr( node, "increment", "never" ); + QString stepSize = XmlUtil::getStringAttr( node, "stepSize", "0" ); + + auto type = Variable::idStringToType( typeString ); + auto increment = Variable::idStringToIncrement( incrementString ); + + Variable v( type, name, initialValue, increment, stepSize ); + model->variables()->addVariable( v ); } void - XmlLabelParser::parseFileNode( const QDomElement& node, DataCache& data ) + XmlLabelParser::parseDataNode( const QDomElement &node, + const Model* model, + DataCache& data ) + { + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + QString tagName = child.toElement().tagName(); + + if ( tagName == "File" ) + { + parseFileNode( child.toElement(), model, data ); + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } + } + + + void + XmlLabelParser::parseFileNode( const QDomElement& node, + const Model* model, + DataCache& data ) { QString name = XmlUtil::getStringAttr( node, "name", "" ); QString mimetype = XmlUtil::getStringAttr( node, "mimetype", "image/png" ); QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" ); + // Rewrite name as absolute file path + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( name ) ); + if ( mimetype == "image/png" ) { if ( encoding == "base64" ) @@ -761,7 +841,7 @@ namespace glabels QImage image; image.loadFromData( ba, "PNG" ); - data.addImage( name, image ); + data.addImage( fn, image ); } else { @@ -770,7 +850,7 @@ namespace glabels } else if ( mimetype == "image/svg+xml" ) { - data.addSvg( name, node.text().toUtf8() ); + data.addSvg( fn, node.text().toUtf8() ); } } diff --git a/model/XmlLabelParser.h b/model/XmlLabelParser.h index f135860..93e90e8 100644 --- a/model/XmlLabelParser.h +++ b/model/XmlLabelParser.h @@ -52,25 +52,57 @@ namespace glabels public: static Model* readFile( const QString& fileName ); + static Model* readBuffer( const QByteArray& buffer ); - static QList deserializeObjects( const QByteArray& buffer ); + + static QList deserializeObjects( const QByteArray& buffer, + const Model* model ); private: - static void gunzip( const QByteArray& gzippedData, QByteArray& data ); - static Model* parseRootNode( const QDomElement &node ); - static QList parseObjectsNode( const QDomElement &node, const DataCache& data ); - static ModelBoxObject* parseObjectBoxNode( const QDomElement &node ); - static ModelEllipseObject* parseObjectEllipseNode( const QDomElement &node ); - static ModelLineObject* parseObjectLineNode( const QDomElement &node ); - static ModelImageObject* parseObjectImageNode( const QDomElement &node, const DataCache& data ); - static ModelBarcodeObject* parseObjectBarcodeNode( const QDomElement &node ); - static ModelTextObject* parseObjectTextNode( const QDomElement &node ); - static QString parsePNode( const QDomElement &node ); - static bool parseRotateAttr( const QDomElement &node ); - static void parseMergeNode( const QDomElement &node, Model* label ); - static void parseDataNode( const QDomElement &node, DataCache& data ); - static void parsePixdataNode( const QDomElement &node, DataCache& data ); - static void parseFileNode( const QDomElement &node, DataCache& data ); + static void gunzip( const QByteArray& gzippedData, + QByteArray& data ); + + static Model* parseRootNode( const QDomElement& node, + const QString& fileName ); + + static QList parseObjectsNode( const QDomElement& node, + const Model* model, + const DataCache& data ); + + static ModelBoxObject* parseObjectBoxNode( const QDomElement& node ); + + static ModelEllipseObject* parseObjectEllipseNode( const QDomElement& node ); + + static ModelLineObject* parseObjectLineNode( const QDomElement& node ); + + static ModelImageObject* parseObjectImageNode( const QDomElement& node, + const Model* model, + const DataCache& data ); + + static ModelBarcodeObject* parseObjectBarcodeNode( const QDomElement& node ); + + static ModelTextObject* parseObjectTextNode( const QDomElement& node ); + + static QString parsePNode( const QDomElement& node ); + + static bool parseRotateAttr( const QDomElement& node ); + + static void parseMergeNode( const QDomElement& node, + Model* model ); + + static void parseVariablesNode( const QDomElement& node, + Model* model ); + + static void parseVariableNode( const QDomElement& node, + Model* model ); + + static void parseDataNode( const QDomElement& node, + const Model* model, + DataCache& data ); + + static void parseFileNode( const QDomElement& node, + const Model* model, + DataCache& data ); }; diff --git a/model/XmlLabelParser_3.cpp b/model/XmlLabelParser_3.cpp index e2a442f..fe129d8 100644 --- a/model/XmlLabelParser_3.cpp +++ b/model/XmlLabelParser_3.cpp @@ -111,7 +111,8 @@ namespace glabels delete label; return nullptr; } - label->setTmplate( tmplate ); + label->setTmplate( tmplate ); // Copies arg + delete tmplate; } else if ( tagName == "Objects" ) { @@ -229,7 +230,7 @@ namespace glabels color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); const ColorNode shadowColorNode( field_flag, color, key ); - return new ModelBoxObject( x0, y0, w, h, + return new ModelBoxObject( x0, y0, w, h, false /*lockAspectRatio*/, lineWidth, lineColorNode, fillColorNode, affineTransformation, @@ -276,7 +277,7 @@ namespace glabels color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); const ColorNode shadowColorNode( field_flag, color, key ); - return new ModelEllipseObject( x0, y0, w, h, + return new ModelEllipseObject( x0, y0, w, h, false /*lockAspectRatio*/, lineWidth, lineColorNode, fillColorNode, affineTransformation, @@ -357,27 +358,27 @@ namespace glabels if ( filenameNode.isField() ) { - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, filenameNode, affineTransformation, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } if ( data.hasImage( filename ) ) { - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, filename, data.getImage( filename ), affineTransformation, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } if ( data.hasSvg( filename ) ) { - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, filename, data.getSvg( filename ), affineTransformation, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } qWarning() << "Embedded file" << filename << "missing. Trying actual file."; - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, filenameNode, affineTransformation, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); @@ -396,10 +397,34 @@ namespace glabels const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* barcode attrs */ - const auto backend = XmlUtil::getStringAttr( node, "backend", ""); + auto backend = XmlUtil::getStringAttr( node, "backend", "" ); // one major difference between glabels-3.0.dtd and glabels-4.0.dtd // is the lowercase of the style names - const auto style = XmlUtil::getStringAttr( node, "style", "").toLower(); + auto style = XmlUtil::getStringAttr( node, "style", "" ).toLower(); + + if ( backend == "built-in" ) + { + backend = ""; + } + else if ( backend == "libiec16022" ) + { + backend = ""; + style = "datamatrix"; + } + else if ( backend == "libqrencode" ) + { + if ( barcode::Backends::style( "qrencode", "qrcode" ) != barcode::Backends::defaultStyle() ) + { + backend = "qrencode"; + style = "qrcode"; + } + else + { + // Will use defaultStyle if Zint not available + backend = "zint"; + style = "qr"; + } + } const barcode::Style bcStyle = barcode::Backends::style( backend, style ); const bool bcTextFlag = XmlUtil::getBoolAttr( node, "text", true ); @@ -419,7 +444,7 @@ namespace glabels /* affine attrs */ const auto affineTransformation = parseAffineTransformation(node); - return new ModelBarcodeObject( x0, y0, w, h, + return new ModelBarcodeObject( x0, y0, w, h, false /*lockAspectRatio*/, bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode, affineTransformation ); } @@ -554,7 +579,7 @@ namespace glabels } const QString text = document.toPlainText(); - auto textNode = new ModelTextObject( x0, y0, w, h, text, + auto textNode = new ModelTextObject( x0, y0, w, h, false /*lockAspectRatio*/, text, fontFamily, fontSize, fontWeight, fontItalicFlag, false, textColorNode, textHAlign, textVAlign, textWrapMode, textLineSpacing, textAutoShrink, diff --git a/model/unit_tests/CMakeLists.txt b/model/unit_tests/CMakeLists.txt index 5edddd8..21fc314 100644 --- a/model/unit_tests/CMakeLists.txt +++ b/model/unit_tests/CMakeLists.txt @@ -16,4 +16,84 @@ if (Qt5Test_FOUND) target_link_libraries (TestXmlUtil Model Qt5::Test) add_test (NAME XmlUtil COMMAND TestXmlUtil) + #======================================= + # Test XmlLabelCreator/Parser classes + #======================================= + qt5_wrap_cpp (TestXmlLabel_moc_sources TestXmlLabel.h) + add_executable (TestXmlLabel TestXmlLabel.cpp ${TestXmlLabel_moc_sources}) + target_link_libraries (TestXmlLabel Model Qt5::Test) + add_test (NAME XmlLabel COMMAND TestXmlLabel) + + #======================================= + # Test ColorNode class + #======================================= + qt5_wrap_cpp (TestColorNode_moc_sources TestColorNode.h) + add_executable (TestColorNode TestColorNode.cpp ${TestColorNode_moc_sources}) + target_link_libraries (TestColorNode Model Qt5::Test) + add_test (NAME ColorNode COMMAND TestColorNode) + + #======================================= + # Test FileUtil class + #======================================= + qt5_wrap_cpp (TestFileUtil_moc_sources TestFileUtil.h) + add_executable (TestFileUtil TestFileUtil.cpp ${TestFileUtil_moc_sources}) + target_link_libraries (TestFileUtil Model Qt5::Test) + add_test (NAME FileUtil COMMAND TestFileUtil) + + #======================================= + # Test Merge classes + #======================================= + qt5_wrap_cpp (TestMerge_moc_sources TestMerge.h) + add_executable (TestMerge TestMerge.cpp ${TestMerge_moc_sources}) + target_link_libraries (TestMerge Model Qt5::Test) + add_test (NAME Merge COMMAND TestMerge) + + #======================================= + # Test Model class + #======================================= + qt5_wrap_cpp (TestModel_moc_sources TestModel.h) + add_executable (TestModel TestModel.cpp ${TestModel_moc_sources}) + target_link_libraries (TestModel Model Qt5::Test) + add_test (NAME Model COMMAND TestModel) + + #======================================= + # Test ModelImageObject class + #======================================= + qt5_wrap_cpp (TestModelImageObject_moc_sources TestModelImageObject.h) + add_executable (TestModelImageObject TestModelImageObject.cpp ${TestModelImageObject_moc_sources}) + target_link_libraries (TestModelImageObject Model Qt5::Test) + add_test (NAME ModelImageObject COMMAND TestModelImageObject) + + #======================================= + # Test RawText class + #======================================= + qt5_wrap_cpp (TestRawText_moc_sources TestRawText.h) + add_executable (TestRawText TestRawText.cpp ${TestRawText_moc_sources}) + target_link_libraries (TestRawText Model Qt5::Test) + add_test (NAME RawText COMMAND TestRawText) + + #======================================= + # Test TextNode class + #======================================= + qt5_wrap_cpp (TestTextNode_moc_sources TestTextNode.h) + add_executable (TestTextNode TestTextNode.cpp ${TestTextNode_moc_sources}) + target_link_libraries (TestTextNode Model Qt5::Test) + add_test (NAME TextNode COMMAND TestTextNode) + + #======================================= + # Test Variable class + #======================================= + qt5_wrap_cpp (TestVariable_moc_sources TestVariable.h) + add_executable (TestVariable TestVariable.cpp ${TestVariable_moc_sources}) + target_link_libraries (TestVariable Model Qt5::Test) + add_test (NAME Variable COMMAND TestVariable) + + #======================================= + # Test Variables class + #======================================= + qt5_wrap_cpp (TestVariables_moc_sources TestVariables.h) + add_executable (TestVariables TestVariables.cpp ${TestVariables_moc_sources}) + target_link_libraries (TestVariables Model Qt5::Test) + add_test (NAME Variables COMMAND TestVariables) + endif (Qt5Test_FOUND) diff --git a/model/unit_tests/TestColorNode.cpp b/model/unit_tests/TestColorNode.cpp new file mode 100644 index 0000000..251ac97 --- /dev/null +++ b/model/unit_tests/TestColorNode.cpp @@ -0,0 +1,177 @@ +/* TestColorNode.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestColorNode.h" + +#include "model/ColorNode.h" + +#include + + +QTEST_MAIN(TestColorNode) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestColorNode::colorNode() +{ + uint32_t rgbaBlackTransparent = 0; + uint32_t rgbaWhite = 0xFFFFFFFF; + uint32_t rgbaRed = 0xFF0000FF; // ColorNode uses RGBA. QColor set alpha to opaque 0xFF by default + uint32_t qRgbaRed = 0xFFFF0000; // QColor uses ARGB, ie alpha at top + uint32_t qRgbaGreen80 = 0x8000FF00; + + QColor blackTransparent = QColor::fromRgba( rgbaBlackTransparent ); + QColor white = QColor::fromRgba( rgbaWhite ); + QColor red = QColor::fromRgba( qRgbaRed ); + QColor green80 = QColor::fromRgba( qRgbaGreen80 ); + QColor silver80 = QColor( 192, 192, 192, 128 ); + + Record record; + Variables vars; + + ColorNode colorNode; + QVERIFY( !colorNode.isField() ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.key(), QString( "" ) ); + QCOMPARE( colorNode.rgba(), rgbaBlackTransparent ); + QCOMPARE( colorNode.color( nullptr, nullptr ), blackTransparent ); + QCOMPARE( colorNode.color( &record, nullptr ), blackTransparent ); + QCOMPARE( colorNode.color( nullptr, &vars ), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), blackTransparent ); + + colorNode.setField( true ); + QVERIFY( colorNode.isField() ); + colorNode.setField( false ); + QVERIFY( !colorNode.isField() ); + + colorNode.setColor( white ); + QCOMPARE( colorNode.color(), white ); + QCOMPARE( colorNode.rgba(), rgbaWhite ); + QCOMPARE( colorNode.color( nullptr, nullptr ), white ); + QCOMPARE( colorNode.color( &record, nullptr ), white ); + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + QCOMPARE( colorNode.color( &record, &vars ), white ); + + colorNode.setKey( "key1" ); + QCOMPARE( colorNode.key(), QString( "key1" ) ); + + /// + /// Constructors + /// + ColorNode colorNode2( true, white, QString( "key2" ) ); + QVERIFY( colorNode2.isField() ); + QCOMPARE( colorNode2.key(), QString( "key2" ) ); + QCOMPARE( colorNode2.color(), white ); + + QVERIFY( colorNode2 != colorNode ); + colorNode.setField( true ); + QVERIFY( colorNode2 != colorNode ); + colorNode.setKey( "key2" ); + QVERIFY( colorNode2 == colorNode ); + + ColorNode colorNode3( red ); + QVERIFY( !colorNode3.isField() ); + QCOMPARE( colorNode3.key(), QString( "" ) ); + QCOMPARE( colorNode3.color(), red ); + QCOMPARE( colorNode3.rgba(), rgbaRed ); + + QVERIFY( colorNode3 != colorNode ); + colorNode.setField( false ); + QVERIFY( colorNode3 != colorNode ); + colorNode.setKey( "" ); + QVERIFY( colorNode3 != colorNode ); + colorNode.setColor( red ); + QVERIFY( colorNode3 == colorNode ); + + /// + /// Record + /// + colorNode = ColorNode( QString( "key1" ) ); + QVERIFY( colorNode.isField() ); // Defaults to true if given key only + QCOMPARE( colorNode.key(), QString( "key1" ) ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), silver80 ); // Defaults to silver if given non-matching record/variables + + record["key1"] = "white"; + QCOMPARE( colorNode.color( &record, nullptr ), white ); + + record["key1"] = "red"; + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#FF0000"; + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#FFFF0000"; // ARGB + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#8000FF00"; + QCOMPARE( colorNode.color( &record, nullptr ), green80 ); + + colorNode.setKey( "key2" ); + QCOMPARE( colorNode.color( &record, nullptr ), silver80 ); + record["key2"] = "#8000FF00"; + QCOMPARE( colorNode.color( &record, nullptr ), green80 ); + + /// + /// Variable + /// + colorNode = ColorNode( QString( "c1" ) ); + QVERIFY( colorNode.isField() ); // Defaults to true if given key only + QCOMPARE( colorNode.key(), QString( "c1" ) ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), silver80 ); // Defaults to silver if given non-matching record/variables + + { + Variable c1( Variable::Type::COLOR, "c1", "white", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + vars.incrementVariablesOnItem(); + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + + { + Variable c1( Variable::Type::COLOR, "c1", "red", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), red ); + + { + Variable c1( Variable::Type::COLOR, "c1", "#8000FF00", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), green80 ); + + colorNode.setKey( "c2" ); + QCOMPARE( colorNode.color( &record, nullptr ), silver80 ); + + { + Variable c2( Variable::Type::COLOR, "c2", "#8000FF00", Variable::Increment::PER_ITEM ); + vars.addVariable( c2 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), green80 ); + + /// + /// Record beats variable + /// + record["c2"] = "red"; + QCOMPARE( colorNode.color( &record, &vars ), red ); +} diff --git a/model/unit_tests/TestColorNode.h b/model/unit_tests/TestColorNode.h new file mode 100644 index 0000000..4837c5a --- /dev/null +++ b/model/unit_tests/TestColorNode.h @@ -0,0 +1,30 @@ +/* TestColorNode.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestColorNode : public QObject +{ + Q_OBJECT + +private slots: + void colorNode(); +}; diff --git a/model/unit_tests/TestFileUtil.cpp b/model/unit_tests/TestFileUtil.cpp new file mode 100644 index 0000000..7625d44 --- /dev/null +++ b/model/unit_tests/TestFileUtil.cpp @@ -0,0 +1,104 @@ +/* TestFileUtil.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestFileUtil.h" + +#include "model/FileUtil.h" + +#include + + +QTEST_MAIN(TestFileUtil) + +using namespace glabels::model; + + +void TestFileUtil::addExtension() +{ + QCOMPARE( FileUtil::addExtension( "/tmp/file", ".ext" ), QString( "/tmp/file.ext" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file.ext", ".ext" ), QString( "/tmp/file.ext" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file.ext", ".txt" ), QString( "/tmp/file.ext.txt" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file", "txt" ), QString( "/tmp/filetxt" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/filetxt", "txt" ), QString( "/tmp/filetxt" ) ); +} + + +void TestFileUtil::systemTemplatesDir() +{ + QDir dir = FileUtil::systemTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( "templates" ) ); +} + + +void TestFileUtil::manualUserTemplatesDir() +{ + QDir dir = FileUtil::manualUserTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( ".glabels" ) ); +} + + +void TestFileUtil::userTemplatesDir() +{ + QDir dir = FileUtil::userTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QFileInfo fileInfo( dir.path() ); + QVERIFY( fileInfo.isWritable() ); +} + + +void TestFileUtil::translationsDir() +{ + QDir dir = FileUtil::translationsDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( "translations" ) ); +} + + +void TestFileUtil::makeRelativeIfInDir_data() +{ + QTest::addColumn( "dir" ); + QTest::addColumn( "filename" ); + QTest::addColumn( "expected" ); + + QTest::newRow( "1" ) << "/dir/subdir" << "/dir/subdir/filename" << "filename"; + QTest::newRow( "2" ) << "/dir/subdir" << "filename" << "filename"; + QTest::newRow( "3" ) << "/dir" << "subdir/filename" << "subdir/filename"; + QTest::newRow( "4" ) << "/dir" << "/dir/subdir/subdir/filename" << "subdir/subdir/filename"; + QTest::newRow( "5" ) << "/dir/subdir" << "/dir/subdir/subdir/filename" << "subdir/filename"; + QTest::newRow( "6" ) << "/dir/subdir" << "/dir/subdir2/filename" << "/dir/subdir2/filename"; + QTest::newRow( "7" ) << "/dir2/subdir" << "/dir/subdir/filename" << "/dir/subdir/filename"; + QTest::newRow( "8" ) << "/dir/subdir" << "/dir/filename" << "/dir/filename"; +} + + +void TestFileUtil::makeRelativeIfInDir() +{ + QFETCH( QString, dir ); + QFETCH( QString, filename ); + QFETCH( QString, expected ); + + QCOMPARE( FileUtil::makeRelativeIfInDir( QDir( dir ), filename ), expected ); +} diff --git a/model/unit_tests/TestFileUtil.h b/model/unit_tests/TestFileUtil.h new file mode 100644 index 0000000..50e1e02 --- /dev/null +++ b/model/unit_tests/TestFileUtil.h @@ -0,0 +1,36 @@ +/* TestFileUtil.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestFileUtil : public QObject +{ + Q_OBJECT + +private slots: + void addExtension(); + void systemTemplatesDir(); + void manualUserTemplatesDir(); + void userTemplatesDir(); + void translationsDir(); + void makeRelativeIfInDir_data(); + void makeRelativeIfInDir(); +}; diff --git a/model/unit_tests/TestMerge.cpp b/model/unit_tests/TestMerge.cpp new file mode 100644 index 0000000..72c9dd3 --- /dev/null +++ b/model/unit_tests/TestMerge.cpp @@ -0,0 +1,347 @@ +/* TestMerge.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestMerge.h" + +#include "merge/Factory.h" +#include "merge/None.h" +#include "merge/TextCsv.h" +#include "merge/TextCsvKeys.h" +#include "merge/TextTsv.h" +#include "merge/TextTsvKeys.h" +#include "merge/TextColon.h" +#include "merge/TextColonKeys.h" +#include "merge/TextSemicolon.h" +#include "merge/TextSemicolonKeys.h" + +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestMerge) + +Q_DECLARE_METATYPE(glabels::merge::Factory::SourceType) + +using namespace glabels::merge; + + +void TestMerge::initTestCase() +{ + Factory::init(); +} + + +void TestMerge::factory_data() +{ + QTest::addColumn( "id" ); + QTest::addColumn( "name" ); + QTest::addColumn( "type" ); + QTest::addColumn( "index" ); + + int index = 0; + QTest::newRow( "None" ) << None::id() << "None" << Factory::NONE << index++; + QTest::newRow( "TextCsv" ) << TextCsv::id() << "Text: Comma Separated Values (CSV)" << Factory::FILE << index++; + QTest::newRow( "TextCsvKeys" ) << TextCsvKeys::id() << "Text: Comma Separated Values (CSV), keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextTsv" ) << TextTsv::id() << "Text: Tab Separated Values (TSV)" << Factory::FILE << index++; + QTest::newRow( "TextTsvKeys" ) << TextTsvKeys::id() << "Text: Tab Separated Values (TSV), keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextColon" ) << TextColon::id() << "Text: Colon Separated Values" << Factory::FILE << index++; + QTest::newRow( "TextColonKeys" ) << TextColonKeys::id() << "Text: Colon Separated Values, keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextSemicolon" ) << TextSemicolon::id() << "Text: Semicolon Separated Values" << Factory::FILE << index++; + QTest::newRow( "TextSemicolonKeys" ) << TextSemicolonKeys::id() << "Text: Semicolon Separated Values, keys on line 1" << Factory::FILE << index++; +} + + +void TestMerge::factory() +{ + QFETCH( QString, id ); + QFETCH( QString, name ); + QFETCH( Factory::SourceType, type ); + QFETCH( int, index ); + + QVERIFY( Factory::nameList().contains( name ) ); + + QString outName = Factory::idToName( id ); + QCOMPARE( outName, name ); + + QString outId = Factory::nameToId( name ); + QCOMPARE( outId, id ); + + Factory::SourceType outType = Factory::idToType( id ); + QCOMPARE( outType, type ); + + outId = Factory::indexToId( index ); + QCOMPARE( outId, id ); + + Merge* merge = Factory::createMerge( id ); + QVERIFY( merge ); + QCOMPARE( merge->id(), id ); + + Merge* cloneMerge = merge->clone(); + QCOMPARE( cloneMerge->id(), merge->id() ); + delete cloneMerge; + delete merge; +} + + +void TestMerge::factoryNotRegistered() +{ + QString id( "unregistered" ); + Merge* merge = Factory::createMerge( id ); + QVERIFY( merge ); + QVERIFY( merge->id() != id ); + QCOMPARE( merge->id(), None::id() ); + delete merge; +} + + +void TestMerge::text_data() +{ + QTest::addColumn( "id" ); + QTest::addColumn( "keyed" ); + QTest::addColumn( "delim" ); + + QTest::newRow( "TextCsv" ) << TextCsv::id() << false << ','; + QTest::newRow( "TextCsvKeys" ) << TextCsvKeys::id() << true << ','; + QTest::newRow( "TextTsv" ) << TextTsv::id() << false << '\t'; + QTest::newRow( "TextTsvKeys" ) << TextTsvKeys::id() << true << '\t'; + QTest::newRow( "TextColon" ) << TextColon::id() << false << ':'; + QTest::newRow( "TextColonKeys" ) << TextColonKeys::id() << true << ':'; + QTest::newRow( "TextSemicolon" ) << TextSemicolon::id() << false << ';'; + QTest::newRow( "TextSemicolonKeys" ) << TextSemicolonKeys::id() << true << ';'; +} + + +void TestMerge::text() +{ + QFETCH( QString, id ); + QFETCH( bool, keyed ); + QFETCH( char, delim ); + + QTemporaryFile file; + file.open(); + if ( keyed ) + { + file.write( "header1" ); + file.putChar( delim ); + file.write( "\"header 2\"" ); + file.putChar( delim ); + file.write( "header3\r\n" ); + } + file.write( " val11" ); // Leading spaces in SIMPLE entry + file.putChar( delim ); + file.write( "\"\"\"val 12\"\"\"" ); // 2DQUOTE at beginning and end of DQUOTE entry + file.putChar( delim ); + file.write( " \"val 13\"\n" ); // Leading spaces before DQUOTE entry, end line with LF only + + file.write( "\" val21\"\"\"" ); // Leading spaces within DQUOTE entry, 2DQUOTE at end + file.putChar( delim ); + file.write( "\"\"\"val 22\"" ); // 2DQUOTE at beginning of DQUOTE entry + file.putChar( delim ); + file.write( "\r\n" ); // Last field blank + + file.write( "\"\"\"\"\"\"" ); // 2 2DQUOTES alone in DQUOTE entry + file.putChar( delim ); + file.write( "val \"32" ); // DQUOTE in SIMPLE entry + file.putChar( delim ); + file.write( "val \"\\\"33\r\n" ); // DQUOTE backslashed-DQUOTE in SIMPLE entry + + file.putChar( delim ); file.putChar( delim ); // All fields blank + file.write( "\r\n" ); + + file.write( "val\\n \\t \\r \\\\ \\x51" ); // Backslashed-n/-t/-r/-backslash/-x in SIMPLE entry + file.putChar( delim ); + file.write( "\"val\\n \\t \\r \\\\ \\x52\"" ); // Backslashed-n/-t/-r/-backslash/-x in QUOTE entry + file.write( "\r\n" ); // No last delim + + file.write( "\"val \"\"61\"" ); // 2DQUOTE in middle of DQUOTE entry + file.putChar( delim ); + file.write( "\"val\"\"" ); file.putChar( delim ); file.write( "\r\n\\\"\u2019\\\\52\"" ); // 2DQUOTE delim CRLF backslashed-DQUOTE U+2019 backslashed-backslash + file.putChar( delim ); + file.write( "\"val63\"" ); // End without CRLF + file.close(); + + Merge* merge = Factory::createMerge( id ); + QCOMPARE( merge->id(), id ); + + merge->setSource( file.fileName() ); + QCOMPARE( merge->source(), file.fileName() ); + + const QList& recordList = merge->recordList(); + QCOMPARE( recordList.size(), 6 ); + + // + // Records + // + const char* h1 = keyed ? "header1" : "1"; + const char* h2 = keyed ? "header 2" : "2"; + const char* h3 = keyed ? "header3" : "3"; + const Record* record; + + record = recordList[0]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( " val11" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "\"val 12\"" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( " \"val 13\"" ) ); // NOTE: Treats as unquoted due to leading spaces + + record = recordList[1]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( " val21\"" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "\"val 22" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "" ) ); + + record = recordList[2]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "\"\"" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val \"32" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "val \"\"33" ) ); + + record = recordList[3]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "" ) ); + + record = recordList[4]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "val\n \t r \\ x51" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val\n \t r \\ x52" ) ); + QVERIFY( !record->contains( h3 ) ); + + record = recordList[5]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "val \"61" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val\"" ).append( delim ).append( "\n\"\u2019\\52" ) ); // NOTE: CR missing (QIODevice::Text strips all CRs from stream) + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "val63" ) ); + + // + // Selection + // + QCOMPARE( merge->nSelectedRecords(), 6 ); // Initially all selected + merge->unselectAll(); + QCOMPARE( merge->nSelectedRecords(), 0 ); + + record = recordList[1]; + merge->select( (Record*)record ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + QCOMPARE( merge->selectedRecords().size(), 1 ); + QCOMPARE( merge->selectedRecords().first(), record ); // Pointers same + + merge->unselect( (Record*)record ); + QCOMPARE( merge->nSelectedRecords(), 0 ); + QCOMPARE( merge->selectedRecords().size(), 0 ); + + merge->setSelected( 0 ); + merge->setSelected( 3 ); + QCOMPARE( merge->nSelectedRecords(), 2 ); + QCOMPARE( merge->selectedRecords().size(), 2 ); + QCOMPARE( merge->selectedRecords().first(), recordList[0] ); + QCOMPARE( merge->selectedRecords().last(), recordList[3] ); + + merge->setSelected( 0, false ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + QCOMPARE( merge->selectedRecords().size(), 1 ); + + // + // Keys + // + QStringList keys = merge->keys(); + QCOMPARE( keys.size(), 3 ); + QCOMPARE( keys[0], QString( h1 ) ); + QCOMPARE( keys[1], QString( h2 ) ); + QCOMPARE( keys[2], QString( h3 ) ); + QCOMPARE( merge->primaryKey(), QString( h1 ) ); + + // + // Clone + // + merge->unselectAll(); + merge->setSelected( 0 ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + + Merge* cloneMerge = merge->clone(); + QCOMPARE( cloneMerge->id(), merge->id() ); + QCOMPARE( cloneMerge->source(), merge->source() ); + QCOMPARE( cloneMerge->recordList().size(), merge->recordList().size() ); + QCOMPARE( *(cloneMerge->recordList()[0]), *(merge->recordList()[0]) ); // Pointers different + QCOMPARE( *(cloneMerge->recordList()[1]), *(merge->recordList()[1]) ); + QCOMPARE( *(cloneMerge->recordList()[2]), *(merge->recordList()[2]) ); + QCOMPARE( *(cloneMerge->recordList()[3]), *(merge->recordList()[3]) ); + QCOMPARE( *(cloneMerge->recordList()[4]), *(merge->recordList()[4]) ); + QCOMPARE( *(cloneMerge->recordList()[5]), *(merge->recordList()[5]) ); + QCOMPARE( cloneMerge->nSelectedRecords(), merge->nSelectedRecords() ); + QCOMPARE( cloneMerge->selectedRecords().size(), merge->selectedRecords().size() ); + QCOMPARE( *(cloneMerge->selectedRecords()[0]), *(merge->selectedRecords()[0]) ); + QCOMPARE( cloneMerge->keys(), merge->keys() ); + QCOMPARE( cloneMerge->primaryKey(), merge->primaryKey() ); + delete cloneMerge; + delete merge; +} + + +void TestMerge::none() +{ + None none; + QCOMPARE( none.id(), QString( "None" ) ); + + None* cloneNone = none.clone(); + QCOMPARE( cloneNone->id(), none.id() ); + QCOMPARE( cloneNone->keys(), none.keys() ); + QCOMPARE( cloneNone->primaryKey(), none.primaryKey() ); + delete cloneNone; +} + + +void TestMerge::record() +{ + Record record; + QCOMPARE( record.isSelected(), true ); + record.setSelected( false ); + QCOMPARE( record.isSelected(), false ); + record.setSelected( true ); + QCOMPARE( record.isSelected(), true ); + + record["key"] = "val"; + QVERIFY( record.contains( "key" ) ); + QCOMPARE( record["key"], QString( "val" ) ); + + Record* cloneRecord = record.clone(); + QCOMPARE( cloneRecord->isSelected(), true ); + QVERIFY( cloneRecord->contains( "key" ) ); + QCOMPARE( cloneRecord->value( "key" ), QString( "val" ) ); + delete cloneRecord; + + record.setSelected( false ); + Record record2( &record ); + QCOMPARE( record2.isSelected(), false ); + QVERIFY( record2.contains( "key" ) ); + QCOMPARE( record2["key"], QString( "val" ) ); +} diff --git a/model/unit_tests/TestMerge.h b/model/unit_tests/TestMerge.h new file mode 100644 index 0000000..5fd43bd --- /dev/null +++ b/model/unit_tests/TestMerge.h @@ -0,0 +1,37 @@ +/* TestMerge.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestMerge : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void factory_data(); + void factory(); + void factoryNotRegistered(); + void text_data(); + void text(); + void none(); + void record(); +}; diff --git a/model/unit_tests/TestModel.cpp b/model/unit_tests/TestModel.cpp new file mode 100644 index 0000000..f52b4c4 --- /dev/null +++ b/model/unit_tests/TestModel.cpp @@ -0,0 +1,496 @@ +/* TestModel.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestModel.h" + +#include "model/Model.h" +#include "model/ModelBoxObject.h" +#include "model/ModelEllipseObject.h" +#include "model/ModelLineObject.h" +#include "model/ModelTextObject.h" +#include "model/FrameRect.h" +#include "model/FrameContinuous.h" +#include "model/Region.h" +#include "model/Settings.h" + +#include "merge/Factory.h" +#include "merge/Merge.h" +#include "merge/None.h" +#include "merge/TextCsv.h" +#include "merge/TextCsvKeys.h" + +#include + + +QTEST_MAIN(TestModel) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestModel::initTestCase() +{ + Factory::init(); + Settings::init(); +} + + +void TestModel::model() +{ + Model model; + QVERIFY( model.isModified() ); + model.clearModified(); + QVERIFY( !model.isModified() ); + + QVERIFY( model.shortName().contains( QRegExp( "^Untitled[1-9][0-9]*$" ) ) ); + model.setFileName( "dir/file1.ext" ); + QCOMPARE( model.fileName(), QString( "dir/file1.ext" ) ); + QCOMPARE( model.shortName(), QString( "file1" ) ); + QVERIFY( !model.isModified() ); + + QCOMPARE( model.w(), Distance( 0 ) ); + QCOMPARE( model.h(), Distance( 0 ) ); + + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 100, 400 ); + FrameRect* frame = new FrameRect( 100, 200, 5, 0, 0, "rect1" ); + QVERIFY( frame->w() != frame->h() ); + tmplate.addFrame( frame ); + model.setTmplate( &tmplate ); // Copies + QCOMPARE( model.tmplate()->brand(), QString( "Test Brand" ) ); + QCOMPARE( model.tmplate()->part(), QString( "part" ) ); + QCOMPARE( model.tmplate()->description(), QString( "desc" ) ); + QCOMPARE( model.tmplate()->paperId(), QString( "testPaperId" ) ); + QCOMPARE( model.tmplate()->pageWidth(), Distance( 100 ) ); + QCOMPARE( model.tmplate()->pageHeight(), Distance( 400 ) ); + QVERIFY( model.isModified() ); + + QVERIFY( model.frame()->id() == frame->id() ); + QCOMPARE( model.w(), Distance( 100 ) ); + QCOMPARE( model.h(), Distance( 200 ) ); + QCOMPARE( model.w(), frame->w() ); + QCOMPARE( model.h(), frame->h() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + QVERIFY( !model.rotate() ); + model.setRotate( false ); + QVERIFY( !model.rotate() ); + QVERIFY( !model.isModified() ); + model.setRotate( true ); + QVERIFY( model.rotate() ); + QVERIFY( model.isModified() ); + + QCOMPARE( model.w(), frame->h() ); + QCOMPARE( model.h(), frame->w() ); + + model.setRotate( false ); + QVERIFY( !model.rotate() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.setH( 300 ); // Default does nothing + QCOMPARE( model.h(), Distance( 200 ) ); + QVERIFY( model.isModified() ); // Set anyway + + // Continuous frame implements setH() + Template tmplate2( "Test Brand2", "part2", "desc2", "testPaperId2", 100, 400 ); + FrameContinuous* frame2 = new FrameContinuous( 100, 0, 500, 200, "continuous1" ); + QCOMPARE( frame2->h(), Distance( 200 ) ); + tmplate2.addFrame( frame2 ); + model.setTmplate( &tmplate2 ); + QVERIFY( model.frame()->id() == frame2->id() ); + QCOMPARE( model.w(), Distance( 100 ) ); + QCOMPARE( model.h(), Distance( 200 ) ); + QCOMPARE( model.w(), frame2->w() ); + QCOMPARE( model.h(), frame2->h() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.setH( 300 ); + QCOMPARE( model.h(), Distance( 300 ) ); + QVERIFY( model.isModified() ); + + // + // Objects + // + ColorNode black( Qt::black ); + ModelObject* ellipse = new ModelEllipseObject( 1, 0, 100, 100, false, 1, black, black ); + ModelObject* box = new ModelBoxObject( 1, 100, 100, 100, false, 1, black, black ); + ModelObject* line = new ModelLineObject( 1, 200, 99 /*dx*/, 1 /*dy*/, 1.0, black ); + ModelObject* text = new ModelTextObject( 1, 201, 100, 30, false, "", "Sans", 10, QFont::Normal, false, false, black, Qt::AlignLeft, Qt::AlignTop, QTextOption::WordWrap, 1, false ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.addObject( ellipse ); + QVERIFY( model.isModified() ); + model.addObject( box ); + model.addObject( line ); + model.addObject( text ); + + QCOMPARE( model.objectList().size(), 4 ); + + ModelObject* line2 = new ModelLineObject( 1, 231, 100 /*dx*/, 1 /*dy*/, 1.0, black ); + model.addObject( line2 ); + QCOMPARE( model.objectList().size(), 5 ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.deleteObject( line2 ); + QCOMPARE( model.objectList().size(), 4 ); + QVERIFY( model.isModified() ); + + ModelObject* object; + + object = model.objectAt( 1 /*scale*/, 1, 200 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), line->id() ); + + object = model.objectAt( 1 /*scale*/, 100, 150 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), box->id() ); + + object = model.objectAt( 1 /*scale*/, 50, 0 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), ellipse->id() ); + + object = model.objectAt( 1 /*scale*/, 1 + 3, 201 + 3 ); // Allow for text offset + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), text->id() ); + + // + // Selection + // + QVERIFY( model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( !model.getFirstSelectedObject() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( !model.canSelectionLineColor() ); + QVERIFY( !model.canSelectionLineWidth() ); + + model.selectAll(); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( !model.getSelection().isEmpty() ); + QCOMPARE( model.getSelection().size(), 4 ); + QCOMPARE( model.getSelection().first()->id(), ellipse->id() ); + QCOMPARE( model.getSelection().at(1)->id(), box->id() ); + QCOMPARE( model.getSelection().at(2)->id(), line->id() ); + QCOMPARE( model.getSelection().at(3)->id(), text->id() ); + QVERIFY( model.getFirstSelectedObject() ); + QCOMPARE( model.getFirstSelectedObject()->id(), ellipse->id() ); + + QVERIFY( model.canSelectionText() ); + QVERIFY( model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( !model.getFirstSelectedObject() ); + + model.selectObject( text ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), text->id() ); + + QVERIFY( model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( !model.canSelectionLineColor() ); + QVERIFY( !model.canSelectionLineWidth() ); + + model.unselectObject( text ); + QVERIFY( model.isSelectionEmpty() ); + + model.selectObject( line ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), line->id() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + + double margin = 0.5; // Allow 0.5pt margin + Region region( 1 - margin, 302 - margin, 101 + margin /*x2*/, 302 + margin /*y2*/ ); // Outside all objects + model.selectRegion( region ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( model.isSelectionEmpty() ); + + region.setY1( 0 - margin ); // Ellipse + region.setY2( 100 + margin ); + model.selectRegion( region ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), ellipse->id() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + region.setY1( 200 - margin ); // Line + region.setY2( 201 + margin ); + model.selectRegion( region ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); // Accumulative + QCOMPARE( model.getSelection().size(), 2 ); + QCOMPARE( model.getSelection().at(0)->id(), ellipse->id() ); + QCOMPARE( model.getSelection().at(1)->id(), line->id() ); + + model.unselectObject( ellipse ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), line->id() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + + // TODO: Operations on selections etc +} + + +void TestModel::saveRestore() +{ + Model* model = new Model; + QVERIFY( model->isModified() ); + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Set template/frame + // + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 110, 410 ); + FrameRect* frame = new FrameRect( 120, 220, 5, 0, 0, "rect1" ); + tmplate.addFrame( frame ); + model->setTmplate( &tmplate ); // Copies + QCOMPARE( model->tmplate()->brand(), QString( "Test Brand" ) ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Set merge + // + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + + model->setMerge( merge ); + QCOMPARE( model->merge(), merge ); + QVERIFY( model->isModified() ); + + // + // Add some variables + // + model->clearModified(); + QVERIFY( !model->isModified() ); + + Variable i( Variable::Type::INTEGER, "i", "2", Variable::Increment::PER_ITEM, "2" ); + Variable f( Variable::Type::FLOATING_POINT, "f", "6.54", Variable::Increment::PER_COPY, "0.12" ); + model->variables()->addVariable( i ); + QVERIFY( model->isModified() ); + model->variables()->addVariable( f ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + QTemporaryFile csv; + csv.open(); + csv.write( "id,text\n1,text1\n2,text2\n3,text3\n" ); + csv.close(); + + merge->setSource( csv.fileName() ); + QCOMPARE( merge->source(), csv.fileName() ); + QCOMPARE( merge->recordList().size(), 3 ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Add some objects + // + ColorNode black( Qt::black ); + ModelObject* object1 = new ModelLineObject( 1, 1, 90, 80, 1.0, black ); + model->addObject( object1 ); + QVERIFY( model->isModified() ); + QCOMPARE( model->objectList().size(), 1 ); + QCOMPARE( model->objectList().first(), object1 ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + ModelObject* object2 = new ModelTextObject( 2, 2, 70, 30, false, "", "Sans", 10, QFont::Normal, false, false, black, Qt::AlignLeft, Qt::AlignTop, QTextOption::WordWrap, 1, false ); + model->addObject( object2 ); + QVERIFY( model->isModified() ); + QCOMPARE( model->objectList().size(), 2 ); + QCOMPARE( model->objectList().last(), object2 ); + + QString modelShortName = model->shortName(); // If no fileName set then model expects to have have this called before being saved/restored (otherwise get differing untitled names) + + // + // Test + // + Model* saved = model->save(); + QVERIFY( saved->isModified() ); + QCOMPARE( saved->merge(), model->merge() ); // Shared + QCOMPARE( saved->variables(), model->variables() ); // Shared + QCOMPARE( saved->isModified(), model->isModified() ); + QCOMPARE( saved->shortName(), modelShortName ); + QCOMPARE( saved->shortName(), model->shortName() ); + QCOMPARE( saved->fileName(), model->fileName() ); + QCOMPARE( saved->rotate(), model->rotate() ); + QCOMPARE( saved->objectList().size(), model->objectList().size() ); + QVERIFY( saved->objectList().at(0) != object1 ); // Objects copied + QVERIFY( saved->objectList().at(1) != object2 ); // Objects copied + QCOMPARE( saved->objectList().at(0)->x0(), model->objectList().at(0)->x0() ); + QCOMPARE( saved->objectList().at(0)->y0(), model->objectList().at(0)->y0() ); + QCOMPARE( saved->objectList().at(1)->x0(), model->objectList().at(1)->x0() ); + QCOMPARE( saved->objectList().at(1)->y0(), model->objectList().at(1)->y0() ); + + // Modify original + Template tmplate2( "Test Brand2", "part2", "desc2", "testPaperId2", 230, 630 ); + FrameRect* frame2 = new FrameRect( 240, 340, 5, 0, 0, "rect2" ); + tmplate2.addFrame( frame2 ); + model->setTmplate( &tmplate2 ); + QCOMPARE( model->tmplate()->brand(), QString( "Test Brand2" ) ); + QCOMPARE( model->w(), Distance( 240 ) ); + QCOMPARE( model->h(), Distance( 340 ) ); + + model->setFileName( "dir/file1.ext" ); + QCOMPARE( model->shortName(), QString( "file1" ) ); + + model->setRotate( true ); + QVERIFY( model->rotate() ); + QCOMPARE( model->w(), Distance( 340 ) ); + QCOMPARE( model->h(), Distance( 240 ) ); + + model->deleteObject( model->objectList().first() ); + QCOMPARE( model->objectList().size(), 1 ); + QCOMPARE( model->objectList().first(), object2 ); + + model->objectList().first()->setY0( model->objectList().first()->y0() + 1 ); + + Merge* merge2 = Factory::createMerge( TextCsv::id() ); + QCOMPARE( merge2->id(), TextCsv::id() ); + QTemporaryFile csv2; csv2.open(); csv2.write( "21,text21\n22,text22\n23,text23\n24,text24\n" ); csv2.close(); + merge2->setSource( csv2.fileName() ); + QCOMPARE( merge2->source(), csv2.fileName() ); + QCOMPARE( merge2->recordList().size(), 4 ); + + model->setMerge( merge2 ); // Deletes original so saved->merge() now invalid + QCOMPARE( model->merge(), merge2 ); + + Model* modified = model->save(); + QCOMPARE( modified->merge(), merge2 ); // Shared + + Variable c( Variable::Type::COLOR, "c", "blue", Variable::Increment::PER_PAGE ); + model->variables()->addVariable( c ); + QCOMPARE( model->variables(), saved->variables() ); // Shared. + + // Verify differences + QVERIFY( model->shortName() != modelShortName ); + QVERIFY( model->shortName() != saved->shortName() ); + QVERIFY( model->fileName() != saved->fileName() ); + QVERIFY( model->tmplate()->brand() != saved->tmplate()->brand() ); + QVERIFY( model->rotate() != saved->rotate() ); + QVERIFY( model->w() != saved->w() ); + QVERIFY( model->h() != saved->h() ); + QVERIFY( model->objectList().size() != saved->objectList().size() ); + QVERIFY( model->objectList().at(0)->x0() != saved->objectList().at(0)->x0() ); + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(0)->y0() ); + QCOMPARE( model->objectList().at(0)->x0(), saved->objectList().at(1)->x0() ); // Unchanged + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(1)->y0() ); + + // Restore + model->restore( saved ); + QCOMPARE( model->shortName(), modelShortName ); + QCOMPARE( model->shortName(), saved->shortName() ); + QCOMPARE( model->fileName(), saved->fileName() ); + QCOMPARE( model->tmplate()->brand(), saved->tmplate()->brand() ); + QCOMPARE( model->rotate(), saved->rotate() ); + QCOMPARE( model->w(), saved->w() ); + QCOMPARE( model->h(), saved->h() ); + QCOMPARE( model->objectList().size(), saved->objectList().size() ); + QCOMPARE( model->objectList().size(), 2 ); + QCOMPARE( model->objectList().at(0)->x0(), saved->objectList().at(0)->x0() ); + QCOMPARE( model->objectList().at(0)->y0(), saved->objectList().at(0)->y0() ); + QCOMPARE( model->objectList().at(1)->x0(), saved->objectList().at(1)->x0() ); + QCOMPARE( model->objectList().at(1)->y0(), saved->objectList().at(1)->y0() ); + + QCOMPARE( model->merge(), merge2 ); // Unchanged + QVERIFY( model->merge() != saved->merge() ); // NOTE saved->merge() now points to deleted object + QCOMPARE( model->variables(), saved->variables() ); // Unchanged + + // Unrestore + model->restore( modified ); + QVERIFY( model->shortName() != modelShortName ); + QVERIFY( model->shortName() != saved->shortName() ); + QVERIFY( model->fileName() != saved->fileName() ); + QVERIFY( model->tmplate()->brand() != saved->tmplate()->brand() ); + QVERIFY( model->rotate() != saved->rotate() ); + QVERIFY( model->w() != saved->w() ); + QVERIFY( model->h() != saved->h() ); + QCOMPARE( model->objectList().size(), 1 ); + QVERIFY( model->objectList().size() != saved->objectList().size() ); + QVERIFY( model->objectList().at(0)->x0() != saved->objectList().at(0)->x0() ); + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(0)->y0() ); + QCOMPARE( model->merge(), merge2 ); // Same + QCOMPARE( model->variables(), saved->variables() ); // Same + + QCOMPARE( model->shortName(), modified->shortName() ); + QCOMPARE( model->fileName(), modified->fileName() ); + QCOMPARE( model->tmplate()->brand(), modified->tmplate()->brand() ); + QCOMPARE( model->rotate(), modified->rotate() ); + QCOMPARE( model->w(), modified->w() ); + QCOMPARE( model->h(), modified->h() ); + QCOMPARE( model->objectList().size(), modified->objectList().size() ); + QCOMPARE( model->objectList().at(0)->x0(), modified->objectList().at(0)->x0() ); + QCOMPARE( model->objectList().at(0)->y0(), modified->objectList().at(0)->y0() ); + + delete model->merge(); // Final instance owned by us + delete model->variables(); // Instance owned by us + delete model; + delete saved; + delete modified; +} diff --git a/model/unit_tests/TestModel.h b/model/unit_tests/TestModel.h new file mode 100644 index 0000000..5a7f580 --- /dev/null +++ b/model/unit_tests/TestModel.h @@ -0,0 +1,32 @@ +/* TestModel.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestModel : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void model(); + void saveRestore(); +}; diff --git a/model/unit_tests/TestModelImageObject.cpp b/model/unit_tests/TestModelImageObject.cpp new file mode 100644 index 0000000..3bcdcb4 --- /dev/null +++ b/model/unit_tests/TestModelImageObject.cpp @@ -0,0 +1,252 @@ +/* TestModelImageObject.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestModelImageObject.h" +#include "Test_Constants.h" + +#include "model/Model.h" +#include "model/ModelImageObject.h" +#include "model/Size.h" + +#include "merge/Factory.h" +#include "merge/TextCsvKeys.h" +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestModelImageObject) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestModelImageObject::initTestCase() +{ + Factory::init(); +} + + +void TestModelImageObject::readImageFile() +{ + QByteArray pngArray; + QImage png; + QString svgTemplate = QDir::tempPath().append( "/TestModelImageObject_XXXXXX.svg" ); // Note: directory separators canonicalized to slash by Qt path methods + + Model model; + + // Needed for relative file names to work + QString modelFileName = QDir::tempPath().append( "/TestModelImageObject.glabels" ); + model.setFileName( modelFileName ); + + ModelImageObject object; + + /// + /// Merge object, no shadow + /// + object.setX0( 1 ); + object.setY0( 1 ); + object.setSize( 8, 8 ); + object.setFilenameNode( TextNode( true, "image" ) ); + + model.addObject( object.clone() ); + + /// + /// Variable object, green pgn, gray shadow + /// + object.setY0( 11 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::gray ) ); + object.setShadowOpacity( 1 ); + TextNode( true, "var" ); + object.setFilenameNode( TextNode( true, "var" ) ); + + // Green 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::green_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngGreen; pngGreen.open(); pngGreen.close(); png.save( pngGreen.fileName(), "PNG" ); + QFileInfo pngGreenFileInfo( pngGreen.fileName() ); + + Variable var( Variable::Type::STRING, "var", pngGreenFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Relative path + model.variables()->addVariable( var ); + + model.addObject( object.clone() ); + + /// + /// Variable object 2, magenta svg, yellow shadow + /// + object.setY0( 21 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::yellow ) ); + object.setShadowOpacity( 1 ); + object.setFilenameNode( TextNode( true, "var2" ) ); + + // Magenta 8x8 square svg + QTemporaryFile svgMagenta( svgTemplate ); svgMagenta.open(); svgMagenta.write( glabels::test::magenta_8x8_svg ); svgMagenta.close(); + QFileInfo svgMagentaFileInfo( svgMagenta.fileName() ); + + Variable var2( Variable::Type::STRING, "var2", svgMagentaFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Absolute path + model.variables()->addVariable( var2 ); + + model.addObject( object.clone() ); + + /// + /// Filename object, yellow png, cyan shadow + /// + object.setY0( 31 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::cyan ) ); + object.setShadowOpacity( 1 ); + + // Yellow 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::yellow_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngYellowFile; pngYellowFile.open(); pngYellowFile.close(); png.save( pngYellowFile.fileName(), "PNG" ); + + QFileInfo pngYellowFileInfo( pngYellowFile.fileName() ); + + // Need to set object parent for relative paths to work + object.setParent( &model ); + + object.setFilenameNode( TextNode( false, pngYellowFileInfo.fileName() ) ); // Relative path + + model.addObject( object.clone() ); + + /// + /// Filename object, cyan svg, magenta shadow + /// + object.setY0( 41 ); + object.setSize( 8, 8 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::magenta ) ); + object.setShadowOpacity( 1 ); + + // Cyan 8x8 square svg + QTemporaryFile svgCyanFile( svgTemplate ); svgCyanFile.open(); svgCyanFile.write( glabels::test::cyan_8x8_svg ); svgCyanFile.close(); + + QFileInfo svgCyanFileInfo( svgCyanFile.fileName() ); + object.setFilenameNode( TextNode( false, svgCyanFileInfo.filePath() ) ); // Absolute path + + model.addObject( object.clone() ); + + /// + /// Set up merge + /// + + // Blue 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::blue_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile png1; png1.open(); png1.close(); png.save( png1.fileName(), "PNG" ); + QTemporaryFile png2; png2.open(); png2.close(); png.save( png2.fileName(), "PNG" ); + + // Red 8x8 square svg + QTemporaryFile svg1( svgTemplate ); svg1.open(); svg1.write( glabels::test::red_8x8_svg ); svg1.close(); + QTemporaryFile svg2( svgTemplate ); svg2.open(); svg2.write( glabels::test::red_8x8_svg ); svg2.close(); + + QFileInfo png1FileInfo( png1.fileName() ); + QFileInfo png2FileInfo( png2.fileName() ); + QFileInfo svg1FileInfo( svg1.fileName() ); + QFileInfo svg2FileInfo( svg2.fileName() ); + + QTemporaryFile csv; + csv.open(); + csv.write( "id,image,type\n" ); + csv.write( "1," ); csv.write( png1FileInfo.fileName().toUtf8() ); csv.write( ",png\n" ); + csv.write( "2," ); csv.write( png1FileInfo.filePath().toUtf8() ); csv.write( ",png\n" ); + csv.write( "3," ); csv.write( svg1FileInfo.fileName().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "4," ); csv.write( svg2FileInfo.filePath().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "5," ); csv.write( svg2FileInfo.fileName().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "6," ); csv.write( png2FileInfo.fileName().toUtf8() ); csv.write( ",png\n" ); + csv.write( "7," ); csv.write( svg1FileInfo.filePath().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "8," ); csv.write( png2FileInfo.filePath().toUtf8() ); csv.write( ",png\n" ); + csv.close(); + + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QVERIFY( merge ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + merge->setSource( csv.fileName() ); + model.setMerge( merge ); + + /// + /// Draw + /// + const QList records = merge->selectedRecords(); + QCOMPARE( records.size(), 8 ); + + QImage paintDevice( 10, 10 * model.objectList().size() * records.size(), QImage::Format_RGB32 ); + paintDevice.fill( Qt::white ); + QPainter painter( &paintDevice ); + + int i, cnt; + int yTranslate = 10 * model.objectList().size(); + for ( i = 0, cnt = records.size(); i < cnt; i++ ) + { + model.draw( &painter, false, records[i], model.variables() ); + painter.translate( 0, yTranslate ); + } + + QColor color, white = Qt::white, grayShadow = Qt::gray, yellowShadow = Qt::yellow, cyanShadow = Qt::cyan, magentaShadow = Qt::magenta; + for ( i = 0, cnt = records.size(); i < cnt; i++ ) + { + // Merge + qDebug() << "record" << i; + color = records[i]->value( "type" ) == "png" ? Qt::blue : Qt::red; + QCOMPARE( paintDevice.pixelColor( 1, 0 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 1 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 8 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 9 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 9 + i * yTranslate ), white ); // No shadow + + // Variable + color = Qt::green; + QCOMPARE( paintDevice.pixelColor( 1, 10 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 11 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 18 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 19 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 19 + i * yTranslate ), grayShadow ); + + // Variable 2 + color = Qt::magenta; + QCOMPARE( paintDevice.pixelColor( 1, 20 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 21 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 28 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 29 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 29 + i * yTranslate ), yellowShadow ); + + // Filename pgn + color = Qt::yellow; + QCOMPARE( paintDevice.pixelColor( 1, 30 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 31 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 38 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 39 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 39 + i * yTranslate ), cyanShadow ); + + // Filename svg + color = Qt::cyan; + QCOMPARE( paintDevice.pixelColor( 1, 40 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 41 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 48 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 49 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 49 + i * yTranslate ), magentaShadow ); + } + + delete model.merge(); + delete model.variables(); +} diff --git a/model/unit_tests/TestModelImageObject.h b/model/unit_tests/TestModelImageObject.h new file mode 100644 index 0000000..b508d1d --- /dev/null +++ b/model/unit_tests/TestModelImageObject.h @@ -0,0 +1,31 @@ +/* TestModelImageObject.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestModelImageObject : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void readImageFile(); +}; diff --git a/model/unit_tests/TestRawText.cpp b/model/unit_tests/TestRawText.cpp new file mode 100644 index 0000000..f523328 --- /dev/null +++ b/model/unit_tests/TestRawText.cpp @@ -0,0 +1,94 @@ +/* TestRawText.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestRawText.h" + +#include "model/RawText.h" + +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestRawText) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestRawText::rawText() +{ + RawText rawText; + Record record; + + QVERIFY( rawText.isEmpty() ); + QVERIFY( !rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "" ) ); + QCOMPARE( rawText.toStdString(), std::string( "" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "" ) ); + + rawText = "text"; + QVERIFY( !rawText.isEmpty() ); + QVERIFY( !rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "text" ) ); + QCOMPARE( rawText.toStdString(), std::string( "text" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text" ) ); + + RawText rawText2( "text" ); + QVERIFY( !rawText2.isEmpty() ); + QVERIFY( !rawText2.hasPlaceHolders() ); + QCOMPARE( rawText2.toString(), QString( "text" ) ); + + rawText = "${key1}"; + QVERIFY( !rawText.isEmpty() ); + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "${key1}" ) ); + QCOMPARE( rawText.toStdString(), std::string( "${key1}" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "" ) ); + + /// + /// Record + /// + record["key1"] = "val1"; + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); + + rawText = "${key1}${key2}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); + + record["key2"] = "val2"; + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1val2" ) ); + + rawText = "${key1}text${key2}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); + + rawText = "text1${key1}text2${key2}text3"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text1val1text2val2text3" ) ); + + rawText = "${key1}text${key2}${key3}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); + + rawText = "${key2}${key3}${key1}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val2val1" ) ); +} diff --git a/model/unit_tests/TestRawText.h b/model/unit_tests/TestRawText.h new file mode 100644 index 0000000..781ecf8 --- /dev/null +++ b/model/unit_tests/TestRawText.h @@ -0,0 +1,30 @@ +/* TestRawText.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestRawText : public QObject +{ + Q_OBJECT + +private slots: + void rawText(); +}; diff --git a/model/unit_tests/TestSubstitutionField.cpp b/model/unit_tests/TestSubstitutionField.cpp index fa9135a..04476b6 100644 --- a/model/unit_tests/TestSubstitutionField.cpp +++ b/model/unit_tests/TestSubstitutionField.cpp @@ -139,6 +139,8 @@ void TestSubstitutionField::simpleEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1}" ); model::SubstitutionField f2( "${2}" ); model::SubstitutionField f3( "${3}" ); @@ -150,10 +152,10 @@ void TestSubstitutionField::simpleEvaluation() record1[ "3" ] = "Opqrstu"; record1[ "4" ] = "Vwxyz!@"; - QCOMPARE( f1.evaluate( &record1 ), QString( "Abcdefg" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "Hijklmn" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "Opqrstu" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "Vwxyz!@" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "Abcdefg" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "Hijklmn" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "Opqrstu" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "Vwxyz!@" ) ); merge::Record record2; record2[ "1" ] = "1234567"; @@ -161,10 +163,10 @@ void TestSubstitutionField::simpleEvaluation() record2[ "3" ] = "8901234"; record2[ "4" ] = "#$%^&*"; - QCOMPARE( f1.evaluate( &record2 ), QString( "1234567" ) ); - QCOMPARE( f2.evaluate( &record2 ), QString( "FooBar" ) ); - QCOMPARE( f3.evaluate( &record2 ), QString( "8901234" ) ); - QCOMPARE( f4.evaluate( &record2 ), QString( "#$%^&*" ) ); + QCOMPARE( f1.evaluate( &record2, &variables ), QString( "1234567" ) ); + QCOMPARE( f2.evaluate( &record2, &variables ), QString( "FooBar" ) ); + QCOMPARE( f3.evaluate( &record2, &variables ), QString( "8901234" ) ); + QCOMPARE( f4.evaluate( &record2, &variables ), QString( "#$%^&*" ) ); } @@ -172,6 +174,8 @@ void TestSubstitutionField::defaultValueEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:=foo1}" ); model::SubstitutionField f2( "${2:=foo2}" ); model::SubstitutionField f3( "${3:=foo3}" ); @@ -183,17 +187,17 @@ void TestSubstitutionField::defaultValueEvaluation() record1[ "3" ] = "Opqrstu"; record1[ "4" ] = "Vwxyz!@"; - QCOMPARE( f1.evaluate( &record1 ), QString( "Abcdefg" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "Hijklmn" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "Opqrstu" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "Vwxyz!@" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "Abcdefg" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "Hijklmn" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "Opqrstu" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "Vwxyz!@" ) ); merge::Record record2; // All fields empty - QCOMPARE( f1.evaluate( &record2 ), QString( "foo1" ) ); - QCOMPARE( f2.evaluate( &record2 ), QString( "foo2" ) ); - QCOMPARE( f3.evaluate( &record2 ), QString( "foo3" ) ); - QCOMPARE( f4.evaluate( &record2 ), QString( "foo4" ) ); + QCOMPARE( f1.evaluate( &record2, &variables ), QString( "foo1" ) ); + QCOMPARE( f2.evaluate( &record2, &variables ), QString( "foo2" ) ); + QCOMPARE( f3.evaluate( &record2, &variables ), QString( "foo3" ) ); + QCOMPARE( f4.evaluate( &record2, &variables ), QString( "foo4" ) ); merge::Record record3; record3[ "1" ] = "xyzzy"; @@ -201,10 +205,10 @@ void TestSubstitutionField::defaultValueEvaluation() // Field "3" empty record3[ "4" ] = "plugh"; - QCOMPARE( f1.evaluate( &record3 ), QString( "xyzzy" ) ); - QCOMPARE( f2.evaluate( &record3 ), QString( "foo2" ) ); - QCOMPARE( f3.evaluate( &record3 ), QString( "foo3" ) ); - QCOMPARE( f4.evaluate( &record3 ), QString( "plugh" ) ); + QCOMPARE( f1.evaluate( &record3, &variables ), QString( "xyzzy" ) ); + QCOMPARE( f2.evaluate( &record3, &variables ), QString( "foo2" ) ); + QCOMPARE( f3.evaluate( &record3, &variables ), QString( "foo3" ) ); + QCOMPARE( f4.evaluate( &record3, &variables ), QString( "plugh" ) ); } @@ -212,6 +216,8 @@ void TestSubstitutionField::formattedStringEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%10s}" ); model::SubstitutionField f2( "${2:%10s}" ); model::SubstitutionField f3( "${3:%10s}" ); @@ -233,15 +239,15 @@ void TestSubstitutionField::formattedStringEvaluation() record1[ "7" ] = "-100"; record1[ "8" ] = "3.14"; - QCOMPARE( f1.evaluate( &record1 ), QString( " 0" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( " 1" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( " -1" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( " 3.14" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( " 0" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( " 1" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( " -1" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( " 3.14" ) ); - QCOMPARE( f5.evaluate( &record1 ), QString( "0 " ) ); - QCOMPARE( f6.evaluate( &record1 ), QString( "100 " ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "-100 " ) ); - QCOMPARE( f8.evaluate( &record1 ), QString( "3.14 " ) ); + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "0 " ) ); + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "100 " ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "-100 " ) ); + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "3.14 " ) ); } @@ -249,6 +255,8 @@ void TestSubstitutionField::formattedFloatEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%+5.2f}" ); model::SubstitutionField f2( "${2:%+5.2f}" ); model::SubstitutionField f3( "${3:%+5.2f}" ); @@ -270,15 +278,15 @@ void TestSubstitutionField::formattedFloatEvaluation() record1[ "7" ] = "-100"; record1[ "8" ] = "3.14"; - QCOMPARE( f1.evaluate( &record1 ), QString( "+0.00" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "+1.00" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "-1.00" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "+3.14" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "+0.00" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "+1.00" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "-1.00" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "+3.14" ) ); - QCOMPARE( f5.evaluate( &record1 ), QString( "+0.00e+00" ) ); - QCOMPARE( f6.evaluate( &record1 ), QString( "+1.00e+02" ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "-1.00e+02" ) ); - QCOMPARE( f8.evaluate( &record1 ), QString( "+3.14e+00" ) ); + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "+0.00e+00" ) ); + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "+1.00e+02" ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "-1.00e+02" ) ); + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "+3.14e+00" ) ); } @@ -286,6 +294,8 @@ void TestSubstitutionField::formattedIntEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%08d}" ); model::SubstitutionField f2( "${2:%08d}" ); model::SubstitutionField f3( "${3:%08d}" ); @@ -307,15 +317,15 @@ void TestSubstitutionField::formattedIntEvaluation() record1[ "7" ] = "-1"; record1[ "8" ] = "314"; - QCOMPARE( f1.evaluate( &record1 ), QString( "00000000" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "00000001" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "-0000001" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "00000000" ) ); // Invalid integer value + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "00000000" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "00000001" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "-0000001" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "00000000" ) ); // Invalid integer value - QCOMPARE( f5.evaluate( &record1 ), QString( "00000064" ) ); // 100(decimal) == 64(hex) - QCOMPARE( f6.evaluate( &record1 ), QString( "00000100" ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "00000000" ) ); // Invalid unsigned integer - QCOMPARE( f8.evaluate( &record1 ), QString( "0000013a" ) ); // 314(decimal) == 13a(hex) + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "00000064" ) ); // 100(decimal) == 64(hex) + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "00000100" ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "00000000" ) ); // Invalid unsigned integer + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "0000013a" ) ); // 314(decimal) == 13a(hex) } @@ -323,6 +333,8 @@ void TestSubstitutionField::newLineEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField addr2( "${ADDR2:n}" ); QCOMPARE( addr2.fieldName(), QString( "ADDR2" ) ); QCOMPARE( addr2.newLine(), true ); @@ -336,7 +348,7 @@ void TestSubstitutionField::newLineEvaluation() merge::Record record3; // ADDR2 not defined - QCOMPARE( addr2.evaluate( &record1 ), QString( "\nApt. 5B" ) ); // Prepends a newline - QCOMPARE( addr2.evaluate( &record2 ), QString( "" ) ); // Evaluates empty - QCOMPARE( addr2.evaluate( &record3 ), QString( "" ) ); // Evaluates empty + QCOMPARE( addr2.evaluate( &record1, &variables ), QString( "\nApt. 5B" ) ); // Prepends a newline + QCOMPARE( addr2.evaluate( &record2, &variables ), QString( "" ) ); // Evaluates empty + QCOMPARE( addr2.evaluate( &record3, &variables ), QString( "" ) ); // Evaluates empty } diff --git a/model/unit_tests/TestTextNode.cpp b/model/unit_tests/TestTextNode.cpp new file mode 100644 index 0000000..19b2532 --- /dev/null +++ b/model/unit_tests/TestTextNode.cpp @@ -0,0 +1,124 @@ +/* TestTextNode.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestTextNode.h" + +#include "model/TextNode.h" + +#include + + +QTEST_MAIN(TestTextNode) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestTextNode::textNode() +{ + Record record; + Variables vars; + + TextNode textNode; + QVERIFY( !textNode.isField() ); + QCOMPARE( textNode.data(), QString( "" ) ); + QVERIFY( textNode == TextNode() ); + QVERIFY( !(textNode != TextNode()) ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "" ) ); + + textNode.setField( true ); + QVERIFY( textNode.isField() ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + textNode.setField( false ); + QVERIFY( !textNode.isField() ); + + textNode.setData( QString( "data1" ) ); + QCOMPARE( textNode.data(), QString( "data1" ) ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "data1" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "data1" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "data1" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "data1" ) ); + + textNode.setField( true ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "" ) ); + + /// + /// Constructors + /// + TextNode textNode2( true, "data2" ); + QVERIFY( textNode2.isField() ); + QCOMPARE( textNode2.data(), QString( "data2" ) ); + textNode.setField( false ); + QVERIFY( !(textNode2 == textNode) ); + QVERIFY( textNode2 != textNode ); + textNode.setField( true ); + QVERIFY( !(textNode2 == textNode) ); + QVERIFY( textNode2 != textNode ); + textNode.setData( QString( "data2" ) ); + QVERIFY( textNode2 == textNode ); + QVERIFY( !(textNode2 != textNode) ); + + /// + /// Record + /// + record["key1"] = ""; + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + textNode.setData( QString( "key1" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + record["key1"] = "val1"; + QCOMPARE( textNode.text( &record, nullptr ), QString( "val1" ) ); + + /// + /// Variable + /// + { + Variable key1( Variable::Type::STRING, "key1", "", Variable::Increment::PER_ITEM ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + + { + Variable key1( Variable::Type::STRING, "key1", "val1", Variable::Increment::PER_ITEM ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "val1" ) ); + + { + Variable key1( Variable::Type::INTEGER, "key1", "1", Variable::Increment::PER_ITEM, "1" ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "1" ) ); + vars.incrementVariablesOnItem(); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "2" ) ); + + /// + /// Record beats variable + /// + QCOMPARE( textNode.text( &record, &vars ), QString( "val1" ) ); +} diff --git a/model/unit_tests/TestTextNode.h b/model/unit_tests/TestTextNode.h new file mode 100644 index 0000000..76553d3 --- /dev/null +++ b/model/unit_tests/TestTextNode.h @@ -0,0 +1,30 @@ +/* TestTextNode.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestTextNode : public QObject +{ + Q_OBJECT + +private slots: + void textNode(); +}; diff --git a/model/unit_tests/TestVariable.cpp b/model/unit_tests/TestVariable.cpp new file mode 100644 index 0000000..85efadc --- /dev/null +++ b/model/unit_tests/TestVariable.cpp @@ -0,0 +1,300 @@ +/* TestVariable.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestVariable.h" + +#include "model/Variable.h" + +#include + + +QTEST_MAIN(TestVariable) + +using namespace glabels::model; + + +void TestVariable::variable() +{ + { + Variable var; + + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString() ); + QCOMPARE( var.initialValue(), QString() ); + QCOMPARE( var.increment(), Variable::Increment::NEVER ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString() ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString() ); + QCOMPARE( var.initialValue(), QString() ); + QCOMPARE( var.increment(), Variable::Increment::NEVER ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString() ); + } + { + Variable var( Variable::Type::STRING, "s", "initial", Variable::Increment::PER_ITEM, "2" ); + + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + } + { + Variable var( Variable::Type::INTEGER, "i", "123", Variable::Increment::PER_ITEM, "1" ); + + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + } + { + Variable var( Variable::Type::INTEGER, "i", "1", Variable::Increment::PER_PAGE, "2" ); + + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "5" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + } + { + Variable var( Variable::Type::FLOATING_POINT, "f", "1.2", Variable::Increment::PER_COPY, "0.2" ); + + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1.6" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "1.6" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + } + { + Variable var( Variable::Type::COLOR, "c", "white", Variable::Increment::PER_PAGE ); + + QCOMPARE( var.type(), Variable::Type::COLOR ); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + } +} + + +void TestVariable::statics() +{ + QCOMPARE( Variable::typeToI18nString( Variable::Type::STRING ), QString( "String" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::INTEGER ), QString( "Integer" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::FLOATING_POINT ), QString( "Floating Point" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::COLOR ), QString( "Color" ) ); + QCOMPARE( Variable::typeToI18nString( (Variable::Type)4 ), QString( "String" ) ); + + QCOMPARE( Variable::typeToIdString( Variable::Type::STRING ), QString( "string" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::INTEGER ), QString( "integer" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::FLOATING_POINT ), QString( "float" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::COLOR ), QString( "color" ) ); + QCOMPARE( Variable::typeToIdString( (Variable::Type)4 ), QString( "string" ) ); + + QCOMPARE( Variable::idStringToType( "string" ), Variable::Type::STRING ); + QCOMPARE( Variable::idStringToType( "integer"), Variable::Type::INTEGER ); + QCOMPARE( Variable::idStringToType( "float" ), Variable::Type::FLOATING_POINT ); + QCOMPARE( Variable::idStringToType( "color" ), Variable::Type::COLOR ); + QCOMPARE( Variable::idStringToType( "non_existent" ), Variable::Type::STRING ); + + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::NEVER ), QString( "Never" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_ITEM ), QString( "Per item" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_COPY ), QString( "Per copy" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_PAGE ), QString( "Per page" ) ); + QCOMPARE( Variable::incrementToI18nString( (Variable::Increment)4 ), QString( "Never" ) ); + + QCOMPARE( Variable::incrementToIdString( Variable::Increment::NEVER ), QString( "never" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_ITEM ), QString( "per_item" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_COPY ), QString( "per_copy" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_PAGE ), QString( "per_page" ) ); + QCOMPARE( Variable::incrementToIdString( (Variable::Increment)4 ), QString( "never" ) ); + + QCOMPARE( Variable::idStringToIncrement( "never" ), Variable::Increment::NEVER ); + QCOMPARE( Variable::idStringToIncrement( "per_item" ), Variable::Increment::PER_ITEM ); + QCOMPARE( Variable::idStringToIncrement( "per_copy" ), Variable::Increment::PER_COPY ); + QCOMPARE( Variable::idStringToIncrement( "per_page" ), Variable::Increment::PER_PAGE ); + QCOMPARE( Variable::idStringToIncrement( "non_existent" ), Variable::Increment::NEVER ); +} diff --git a/model/unit_tests/TestVariable.h b/model/unit_tests/TestVariable.h new file mode 100644 index 0000000..9cad55d --- /dev/null +++ b/model/unit_tests/TestVariable.h @@ -0,0 +1,31 @@ +/* TestVariable.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestVariable : public QObject +{ + Q_OBJECT + +private slots: + void variable(); + void statics(); +}; diff --git a/model/unit_tests/TestVariables.cpp b/model/unit_tests/TestVariables.cpp new file mode 100644 index 0000000..c7f6ce4 --- /dev/null +++ b/model/unit_tests/TestVariables.cpp @@ -0,0 +1,157 @@ +/* TestVariables.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestVariables.h" + +#include "model/Variables.h" + +#include + + +QTEST_MAIN(TestVariables) + +using namespace glabels::model; + + +void TestVariables::variables() +{ + Variables vars; + + Variable i( Variable::Type::INTEGER, "i", "3", Variable::Increment::PER_ITEM, "3" ); + QCOMPARE( i.value(), QString( "3" ) ); + + Variable i2( Variable::Type::INTEGER, "i2", "100", Variable::Increment::PER_COPY, "2" ); + QCOMPARE( i2.value(), QString( "100" ) ); + + Variable f( Variable::Type::FLOATING_POINT, "f", "0.0", Variable::Increment::PER_PAGE, "0.1" ); + QCOMPARE( f.value(), QString( "0" ) ); + + Variable s( Variable::Type::STRING, "s", "initial", Variable::Increment::PER_ITEM, "1" ); + QCOMPARE( s.value(), QString( "initial" ) ); + + Variable c( Variable::Type::COLOR, "c", "white", Variable::Increment::PER_ITEM, "01" ); + QCOMPARE( c.value(), QString( "white" ) ); + + QVERIFY( !vars.hasVariable( "i" ) ); + QVERIFY( !vars.hasVariable( "i2" ) ); + + // Add, delete + vars.addVariable( i ); + QVERIFY( vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), i.value() ); + vars.deleteVariable( "i" ); + QVERIFY( !vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), QString() ); + + // Add, replace + vars.addVariable( i ); + QVERIFY( vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), i.value() ); + vars.replaceVariable( "i", i2 ); + QVERIFY( !vars.hasVariable( "i" ) ); + QVERIFY( vars.hasVariable( "i2" ) ); + QCOMPARE( vars["i2"].value(), i2.value() ); + QCOMPARE( vars["i"].value(), QString() ); + vars.deleteVariable( "i2" ); + QVERIFY( !vars.hasVariable( "i2" ) ); + + // Increment + vars.addVariable( i ); + vars.addVariable( i2 ); + vars.addVariable( f ); + vars.addVariable( s ); + vars.addVariable( c ); + + QVERIFY( vars.hasVariable( "i" ) ); // PER_ITEM + QVERIFY( vars.hasVariable( "i2" ) ); // PER_COPY + QVERIFY( vars.hasVariable( "f" ) ); // PER_PAGE + QVERIFY( vars.hasVariable( "s" ) ); + QVERIFY( vars.hasVariable( "c" ) ); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.resetVariables(); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnItem(); + + QCOMPARE( vars["i"].value(), QString( "6" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnItem(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnCopy(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "102" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnCopy(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnPage(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0.1" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnPage(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0.2" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.resetVariables(); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); +} diff --git a/model/unit_tests/TestVariables.h b/model/unit_tests/TestVariables.h new file mode 100644 index 0000000..93b56b4 --- /dev/null +++ b/model/unit_tests/TestVariables.h @@ -0,0 +1,30 @@ +/* TestVariables.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestVariables : public QObject +{ + Q_OBJECT + +private slots: + void variables(); +}; diff --git a/model/unit_tests/TestXmlLabel.cpp b/model/unit_tests/TestXmlLabel.cpp new file mode 100644 index 0000000..e70221f --- /dev/null +++ b/model/unit_tests/TestXmlLabel.cpp @@ -0,0 +1,703 @@ +/* TestXmlLabel.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestXmlLabel.h" +#include "Test_Constants.h" + +#include "model/XmlLabelCreator.h" +#include "model/XmlLabelParser.h" + +#include "barcode/Backends.h" +#include "model/ColorNode.h" +#include "model/FrameRect.h" +#include "model/Markup.h" +#include "model/Model.h" +#include "model/PageRenderer.h" +#include "model/Size.h" + +#include "model/ModelBarcodeObject.h" +#include "model/ModelBoxObject.h" +#include "model/ModelEllipseObject.h" +#include "model/ModelLineObject.h" +#include "model/ModelImageObject.h" +#include "model/ModelTextObject.h" + +#include "model/Db.h" +#include "merge/Factory.h" +#include "merge/Merge.h" +#include "merge/TextCsvKeys.h" + +#include + + +QTEST_MAIN(TestXmlLabel) + +using namespace glabels::model; +using namespace glabels::barcode; +using namespace glabels::merge; + + +namespace +{ + const double FONT_SCALE_FACTOR {0.75}; +} + + +void TestXmlLabel::initTestCase() +{ + Settings::init(); + Db::init(); + Factory::init(); + Backends::init(); +} + + +void TestXmlLabel::serializeDeserialize() +{ + Model* model = new Model(); + + QList objects, outObjects; + QByteArray buffer, outBuffer; + + // + // Empty object list + // + QCOMPARE( objects.count(), 0 ); + XmlLabelCreator::serializeObjects( objects, model, buffer ); + outObjects = XmlLabelParser::deserializeObjects( buffer, model ); + QCOMPARE( objects.count(), outObjects.count() ); + QCOMPARE( objects, outObjects ); + XmlLabelCreator::serializeObjects( outObjects, model, outBuffer ); + QCOMPARE( buffer, outBuffer ); + + // + // All objects list + // + QImage png; + QVERIFY( png.loadFromData( QByteArray::fromBase64( glabels::test::blue_8x8_png ), "PNG" ) ); + + QString svgTemplate = QDir::tempPath().append( "/TestXmlLabel_XXXXXX.svg" ); // Note: directory separators canonicalized to slash by Qt path methods + QTemporaryFile svgRelative( svgTemplate ); svgRelative.open(); svgRelative.write( glabels::test::cyan_8x8_svg ); svgRelative.close(); + + bool lock = true, noLock = false, shadow = true, noShadow = false; + ColorNode black( Qt::black ), white( Qt::white ), red( Qt::red ), green( Qt::green ), blue( Qt::blue ); + QMatrix tMatrix( 1, 0, 0, 1, 50.0, 50.0 ), sMatrix( 0.5, 0, 0, 1.0, 0, 0 ); + Style bcStyle = Backends::defaultStyle(); + + objects << new ModelBoxObject( 0, 1, 10, 20, lock, 2, red, green, tMatrix, shadow, 1, 2, 0.7, black ); + objects << new ModelEllipseObject( 1, 2, 30, 40, noLock, 3, black, white, sMatrix, shadow, 2, 3, 0.8, blue ); + objects << new ModelImageObject( 2, 3, 50, 50, lock, TextNode( false, "" ), tMatrix, noShadow, 3, 4, 0.9, white ); + objects << new ModelImageObject( 3, 4, 60, 70, noLock, "image3.png", png, sMatrix, shadow, 6, 4, 0.9, black ); + objects << new ModelImageObject( 4, 5, 70, 80, lock, "image4.svg", glabels::test::red_8x8_svg ); + objects << new ModelImageObject( 5, 6, 80, 90, noLock, TextNode( true, "${key}" ), tMatrix, shadow ); + objects << new ModelImageObject( 6, 7, 90, 100, lock, TextNode( false, "image6.jpg" ) ); // Will give warning on parse that embedded file missing + objects << new ModelLineObject( 7, 8, 100, 110, 4, green, sMatrix, shadow, 5, 5, 0.5, red ); + objects << new ModelTextObject( 8, 9, 110, 120, lock, "text", "Serif", 12, QFont::Bold, true, true, red, + Qt::AlignHCenter, Qt::AlignBottom, QTextOption::NoWrap, 1.3, false, sMatrix, shadow, 5, 5, 0.5, red ); + objects << new ModelBarcodeObject( 9, 10, 50, 50, noLock, bcStyle, true, true, QString("1234"), black, tMatrix ); + objects << new ModelImageObject( 10, 11, 8, 8, lock, TextNode( false, svgRelative.fileName() ) ); + + QCOMPARE( objects.count(), 11 ); + + buffer.clear(); + XmlLabelCreator::serializeObjects( objects, model, buffer ); + + QVERIFY( svgRelative.remove() ); // Delete to make sure it's not read from file on parse + + QTest::ignoreMessage( QtWarningMsg, QRegularExpression( "^Embedded file \"[^\"]+image6.jpg\" missing\\. Trying actual file\\.$" ) ); + outObjects = XmlLabelParser::deserializeObjects( buffer, model ); + QCOMPARE( objects.count(), outObjects.count() ); + + QString modelDirPath = model->dir().path() + "/"; + + for ( int i = 0; i < objects.count(); i++ ) + { + qDebug() << "object" << i; + QVERIFY( objects.at(i)->id() != outObjects.at(i)->id() ); // Ids are generated and unique + QCOMPARE( objects.at(i)->x0(), outObjects.at(i)->x0() ); + QCOMPARE( objects.at(i)->x0().pt(), (double)i ); + QCOMPARE( objects.at(i)->y0(), outObjects.at(i)->y0() ); + QCOMPARE( objects.at(i)->y0().pt(), (double)(i + 1) ); + QCOMPARE( objects.at(i)->w().pt(), outObjects.at(i)->w().pt() ); // Use `pt()` so invoke `qFuzzyCompare(double, double)` otherwise get rounding difference for Barcode + QCOMPARE( objects.at(i)->h().pt(), outObjects.at(i)->h().pt() ); // Fuzzy + QCOMPARE( objects.at(i)->lockAspectRatio(), outObjects.at(i)->lockAspectRatio() ); + QCOMPARE( objects.at(i)->lockAspectRatio(), (bool)((i + 1) % 2) ); + QCOMPARE( objects.at(i)->matrix(), outObjects.at(i)->matrix() ); + QCOMPARE( objects.at(i)->shadow(), outObjects.at(i)->shadow() ); + QCOMPARE( objects.at(i)->shadowX(), outObjects.at(i)->shadowX() ); + QCOMPARE( objects.at(i)->shadowY(), outObjects.at(i)->shadowY() ); + QCOMPARE( objects.at(i)->shadowOpacity(), outObjects.at(i)->shadowOpacity() ); + QVERIFY( objects.at(i)->shadowColorNode() == outObjects.at(i)->shadowColorNode() ); + QCOMPARE( objects.at(i)->naturalSize().w().pt(), outObjects.at(i)->naturalSize().w().pt() ); // Fuzzy + QCOMPARE( objects.at(i)->naturalSize().h().pt(), outObjects.at(i)->naturalSize().h().pt() ); // Fuzzy + + QCOMPARE( objects.at(i)->text(), outObjects.at(i)->text() ); + QCOMPARE( objects.at(i)->fontFamily(), outObjects.at(i)->fontFamily() ); + QCOMPARE( objects.at(i)->fontSize(), outObjects.at(i)->fontSize() ); + QCOMPARE( objects.at(i)->fontWeight(), outObjects.at(i)->fontWeight() ); + QCOMPARE( objects.at(i)->fontItalicFlag(), outObjects.at(i)->fontItalicFlag() ); + QCOMPARE( objects.at(i)->fontUnderlineFlag(), outObjects.at(i)->fontUnderlineFlag() ); + QVERIFY( objects.at(i)->textColorNode() == outObjects.at(i)->textColorNode() ); + QCOMPARE( objects.at(i)->textHAlign(), outObjects.at(i)->textHAlign() ); + QCOMPARE( objects.at(i)->textVAlign(), outObjects.at(i)->textVAlign() ); + QCOMPARE( objects.at(i)->textWrapMode(), outObjects.at(i)->textWrapMode() ); + QCOMPARE( objects.at(i)->textLineSpacing(), outObjects.at(i)->textLineSpacing() ); + QCOMPARE( objects.at(i)->textAutoShrink(), outObjects.at(i)->textAutoShrink() ); + + QCOMPARE( objects.at(i)->filenameNode().isField(), outObjects.at(i)->filenameNode().isField() ); + if ( i == 6 /*image6.jpg*/ ) + { + // Not in data so absolute path set + QCOMPARE( modelDirPath + objects.at(i)->filenameNode().data(), outObjects.at(i)->filenameNode().data() ); + } + else + { + QCOMPARE( objects.at(i)->filenameNode().data(), outObjects.at(i)->filenameNode().data() ); + } + + if ( objects.at(i)->image() ) + { + QCOMPARE( *(objects.at(i)->image()), *(outObjects.at(i)->image()) ); + } + else + { + QCOMPARE( objects.at(i)->image(), outObjects.at(i)->image() ); + } + QCOMPARE( objects.at(i)->svg(), outObjects.at(i)->svg() ); + + QCOMPARE( objects.at(i)->lineWidth(), outObjects.at(i)->lineWidth() ); + QVERIFY( objects.at(i)->lineColorNode() == outObjects.at(i)->lineColorNode() ); + QVERIFY( objects.at(i)->fillColorNode() == outObjects.at(i)->fillColorNode() ); + + QCOMPARE( objects.at(i)->bcData(), outObjects.at(i)->bcData() ); + QCOMPARE( objects.at(i)->bcTextFlag(), outObjects.at(i)->bcTextFlag() ); + QCOMPARE( objects.at(i)->bcChecksumFlag(), outObjects.at(i)->bcChecksumFlag() ); + QVERIFY( objects.at(i)->bcColorNode() == outObjects.at(i)->bcColorNode() ); + QVERIFY( !( objects.at(i)->bcStyle() != outObjects.at(i)->bcStyle() ) ); // Only != operator + QCOMPARE( objects.at(i)->bcFormatDigits(), outObjects.at(i)->bcFormatDigits() ); + + QCOMPARE( objects.at(i)->canText(), outObjects.at(i)->canText() ); + QCOMPARE( objects.at(i)->canFill(), outObjects.at(i)->canFill() ); + QCOMPARE( objects.at(i)->canLineColor(), outObjects.at(i)->canLineColor() ); + QCOMPARE( objects.at(i)->canLineWidth(), outObjects.at(i)->canLineWidth() ); + } + + outBuffer.clear(); + XmlLabelCreator::serializeObjects( outObjects, model, outBuffer ); + + QCOMPARE( buffer, outBuffer ); + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::writeReadFile() +{ + Model* model = new Model(); + + // Make subdir in temp dir to use as model dir + QTemporaryDir subDir; + QVERIFY( subDir.isValid() ); + + QString glabelsTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX.glabels" ); // Note: directory separators canonicalized to slash by Qt path methods + QTemporaryFile glabels( glabelsTemplate ); + glabels.open(); glabels.close(); + + model->setFileName( glabels.fileName() ); + + // Make subdir in subdir + QString subSubTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX" ); + QTemporaryDir subSubDir( subSubTemplate ); + QVERIFY( subSubDir.isValid() ); + + QString relPath = model->dir().relativeFilePath( subSubDir.path() ); + QVERIFY( !relPath.contains( '/' ) ); // Make sure subdir of model dir + + QImage png; + QVERIFY( png.loadFromData( QByteArray::fromBase64( glabels::test::blue_8x8_png ), "PNG" ) ); + + // Make png file in temp dir (ie not in model dir) + QImage pngAbsoluteImage; + QVERIFY( pngAbsoluteImage.loadFromData( QByteArray::fromBase64( glabels::test::green_8x8_png ), "PNG" ) ); + QTemporaryFile pngAbsolute; pngAbsolute.open(); pngAbsolute.close(); pngAbsoluteImage.save( pngAbsolute.fileName(), "PNG" ); + + // Make png file in model dir + QImage pngRelativeImage; + QVERIFY( pngRelativeImage.loadFromData( QByteArray::fromBase64( glabels::test::yellow_8x8_png ), "PNG" ) ); + QString pngTemplate = model->dir().path().append( "/TestXmlLabel_XXXXXX.png" ); + QTemporaryFile pngRelative( pngTemplate ); pngRelative.open(); pngRelative.close(); pngRelativeImage.save( pngRelative.fileName(), "PNG" ); + + // Make svg file in subdir of model dir + QString svgTemplate = subSubDir.path().append( "/TestXmlLabel_XXXXXX.svg" ); + QTemporaryFile svgRelative( svgTemplate ); svgRelative.open(); svgRelative.write( glabels::test::cyan_8x8_svg ); svgRelative.close(); + + bool lock = true, noLock = false, shadow = true, noShadow = false; + ColorNode black( Qt::black ), white( Qt::white ), red( Qt::red ), green( Qt::green ), blue( Qt::blue ); + QMatrix tMatrix( 1, 0, 0, 1, 50.0, 50.0 ), sMatrix( 0.5, 0, 0, 1.0, 0, 0 ); + Style bcStyle = Backends::defaultStyle(); + + /// + /// Add objects + /// + model->addObject( new ModelBoxObject( 0, 1, 10, 20, noLock, 2, red, green, tMatrix, shadow, 1, 2, 0.7, black ) ); + model->addObject( new ModelEllipseObject( 1, 2, 30, 40, lock, 3, black, white, sMatrix, shadow, 2, 3, 0.8, blue ) ); + model->addObject( new ModelImageObject( 2, 3, 50, 50, noLock, TextNode( false, "" ), tMatrix, noShadow, 3, 4, 0.9, white ) ); + model->addObject( new ModelImageObject( 3, 4, 60, 70, lock, "image3.png", png, sMatrix, shadow, 6, 4, 0.9, black ) ); + model->addObject( new ModelImageObject( 4, 5, 70, 80, noLock, "image4.svg", glabels::test::red_8x8_svg ) ); + model->addObject( new ModelImageObject( 5, 6, 80, 90, lock, TextNode( true, "${key}" ), tMatrix, shadow ) ); + model->addObject( new ModelImageObject( 6, 7, 90, 100, noLock, TextNode( false, "image6.jpg" ) ) ); // Will give warning on parse that embedded file missing + model->addObject( new ModelTextObject( 7, 8, 110, 120, lock, "text", "Serif", 12, QFont::Bold, true, true, red, + Qt::AlignHCenter, Qt::AlignBottom, QTextOption::NoWrap, 1.3, false, sMatrix, shadow, 5, 5, 0.5, red ) ); + model->addObject( new ModelLineObject( 8, 9, 100, 110, 4, green, sMatrix, shadow, 5, 5, 0.5, red ) ); + model->addObject( new ModelBarcodeObject( 9, 10, 50, 50, lock, bcStyle, true, true, QString("1234"), black, tMatrix ) ); + model->addObject( new ModelImageObject( 10, 11, 8, 8, noLock, TextNode( false, pngAbsolute.fileName() ) ) ); + model->addObject( new ModelImageObject( 11, 12, 8, 8, lock, TextNode( false, pngRelative.fileName() ) ) ); + model->addObject( new ModelImageObject( 12, 13, 8, 8, noLock, TextNode( false, svgRelative.fileName() ) ) ); + + QCOMPARE( model->objectList().size(), 13 ); + + /// + /// Add template + /// + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 110, 410 ); + FrameRect* frame = new FrameRect( 120, 220, 5, 0, 0, "rect1" ); + tmplate.addFrame( frame ); + model->setTmplate( &tmplate ); // Copies + + /// + /// Add variables + /// + Variables vars; + Variable s( Variable::Type::STRING, "s", "initial", Variable::Increment::NEVER ); + Variable c( Variable::Type::COLOR, "c", "red", Variable::Increment::PER_COPY ); + Variable i( Variable::Type::INTEGER, "i", "123", Variable::Increment::PER_ITEM, "1" ); + Variable f( Variable::Type::FLOATING_POINT, "f", "12.3", Variable::Increment::PER_PAGE, "0.2" ); + model->variables()->addVariable( s ); + model->variables()->addVariable( c ); + model->variables()->addVariable( i ); + model->variables()->addVariable( f ); + QCOMPARE( model->variables()->size(), 4 ); + + // + // Add merge + // + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + + model->setMerge( merge ); + QCOMPARE( model->merge(), merge ); + + QString csvTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX.csv" ); + QTemporaryFile csv( csvTemplate ); + csv.open(); + csv.write( "id,text\n1,text1\n2,text2\n3,text3\n" ); + csv.close(); + + merge->setSource( csv.fileName() ); + QCOMPARE( merge->source(), csv.fileName() ); + + QCOMPARE( merge->recordList().size(), 3 ); + + model->setRotate( true ); + QVERIFY( model->rotate() ); + + /// + /// Write to file and read + /// + XmlLabelCreator::writeFile( model, glabels.fileName() ); + + QCOMPARE( model->dir(), QFileInfo( glabels.fileName() ).dir() ); + + // Copy before deletion else nulled + QString pngAbsoluteFileName = pngAbsolute.fileName(); + QString pngRelativeFileName = model->dir().relativeFilePath( pngRelative.fileName() ); + QString svgRelativeFileName = model->dir().relativeFilePath( svgRelative.fileName() ); + + QFileInfo pngAbsoluteFileInfo( pngAbsoluteFileName ); + QVERIFY( pngAbsoluteFileInfo.isAbsolute() ); + QFileInfo pngRelativeFileInfo( pngRelativeFileName ); + QVERIFY( pngRelativeFileInfo.isRelative() ); + QFileInfo svgRelativeFileInfo( svgRelativeFileName ); + QVERIFY( svgRelativeFileInfo.isRelative() ); + + // Delete to make sure they're not read from file on parse + QVERIFY( pngAbsolute.remove() ); + QVERIFY( pngRelative.remove() ); + QVERIFY( svgRelative.remove() ); + + QTest::ignoreMessage( QtWarningMsg, QRegularExpression( "^Embedded file \"[^\"]+image6.jpg\" missing\\. Trying actual file\\.$" ) ); + Model* readModel = XmlLabelParser::readFile( glabels.fileName() ); + QVERIFY( readModel ); + QCOMPARE( readModel->dir(), model->dir() ); + QCOMPARE( readModel->fileName(), model->fileName() ); + + QCOMPARE( readModel->tmplate()->brand(), model->tmplate()->brand() ); + QCOMPARE( readModel->tmplate()->part(), model->tmplate()->part() ); + QCOMPARE( readModel->tmplate()->description(), model->tmplate()->description() ); + QCOMPARE( readModel->tmplate()->paperId(), model->tmplate()->paperId() ); + QCOMPARE( readModel->tmplate()->pageWidth().pt(), model->tmplate()->pageWidth().pt() ); + QCOMPARE( readModel->tmplate()->pageHeight().pt(), model->tmplate()->pageHeight().pt() ); + + QCOMPARE( readModel->frame()->id(), model->frame()->id() ); + QCOMPARE( readModel->frame()->w().pt(), model->frame()->w().pt() ); + QCOMPARE( readModel->frame()->h().pt(), model->frame()->h().pt() ); + + QCOMPARE( readModel->rotate(), model->rotate() ); + QCOMPARE( readModel->w(), model->w() ); + QCOMPARE( readModel->h(), model->h() ); + + const QList& readObjects = readModel->objectList(); + const QList& modelObjects = model->objectList(); + QCOMPARE( readObjects.size(), modelObjects.size() ); + + QString modelDirPath = model->dir().path() + "/"; + + for ( int i = 0; i < readObjects.count(); i++ ) + { + qDebug() << "object" << i; + QVERIFY( readObjects.at(i)->id() != modelObjects.at(i)->id() ); // Ids are generated and unique + QCOMPARE( readObjects.at(i)->x0(), modelObjects.at(i)->x0() ); + QCOMPARE( readObjects.at(i)->x0().pt(), (double)i ); + QCOMPARE( readObjects.at(i)->y0(), modelObjects.at(i)->y0() ); + QCOMPARE( readObjects.at(i)->y0().pt(), (double)(i + 1) ); + QCOMPARE( readObjects.at(i)->w().pt(), modelObjects.at(i)->w().pt() ); // Use `pt()` so invoke `qFuzzyCompare(double, double)` otherwise get rounding difference for Barcode + QCOMPARE( readObjects.at(i)->h().pt(), modelObjects.at(i)->h().pt() ); // Fuzzy + QCOMPARE( readObjects.at(i)->lockAspectRatio(), modelObjects.at(i)->lockAspectRatio() ); + QCOMPARE( readObjects.at(i)->lockAspectRatio(), (bool)(i % 2) ); + QCOMPARE( readObjects.at(i)->matrix(), modelObjects.at(i)->matrix() ); + QCOMPARE( readObjects.at(i)->shadow(), modelObjects.at(i)->shadow() ); + QCOMPARE( readObjects.at(i)->shadowX(), modelObjects.at(i)->shadowX() ); + QCOMPARE( readObjects.at(i)->shadowY(), modelObjects.at(i)->shadowY() ); + QCOMPARE( readObjects.at(i)->shadowOpacity(), modelObjects.at(i)->shadowOpacity() ); + QVERIFY( readObjects.at(i)->shadowColorNode() == modelObjects.at(i)->shadowColorNode() ); + QCOMPARE( readObjects.at(i)->naturalSize().w().pt(), modelObjects.at(i)->naturalSize().w().pt() ); // Fuzzy + QCOMPARE( readObjects.at(i)->naturalSize().h().pt(), modelObjects.at(i)->naturalSize().h().pt() ); // Fuzzy + + QCOMPARE( readObjects.at(i)->text(), modelObjects.at(i)->text() ); + QCOMPARE( readObjects.at(i)->fontFamily(), modelObjects.at(i)->fontFamily() ); + QCOMPARE( readObjects.at(i)->fontSize(), modelObjects.at(i)->fontSize() ); + QCOMPARE( readObjects.at(i)->fontWeight(), modelObjects.at(i)->fontWeight() ); + QCOMPARE( readObjects.at(i)->fontItalicFlag(), modelObjects.at(i)->fontItalicFlag() ); + QCOMPARE( readObjects.at(i)->fontUnderlineFlag(), modelObjects.at(i)->fontUnderlineFlag() ); + QVERIFY( readObjects.at(i)->textColorNode() == modelObjects.at(i)->textColorNode() ); + QCOMPARE( readObjects.at(i)->textHAlign(), modelObjects.at(i)->textHAlign() ); + QCOMPARE( readObjects.at(i)->textVAlign(), modelObjects.at(i)->textVAlign() ); + QCOMPARE( readObjects.at(i)->textWrapMode(), modelObjects.at(i)->textWrapMode() ); + QCOMPARE( readObjects.at(i)->textLineSpacing(), modelObjects.at(i)->textLineSpacing() ); + QCOMPARE( readObjects.at(i)->textAutoShrink(), modelObjects.at(i)->textAutoShrink() ); + + QCOMPARE( readObjects.at(i)->filenameNode().isField(), modelObjects.at(i)->filenameNode().isField() ); + if ( i == 6 /*image6.jpg*/ ) + { + // Not in data so absolute path set + QCOMPARE( readObjects.at(i)->filenameNode().data(), modelDirPath + modelObjects.at(i)->filenameNode().data() ); + } + else if ( modelObjects.at(i)->filenameNode().data().startsWith( modelDirPath ) ) + { + // Made relative to model dir + QCOMPARE( modelDirPath + readObjects.at(i)->filenameNode().data(), modelObjects.at(i)->filenameNode().data() ); + } + else + { + QCOMPARE( readObjects.at(i)->filenameNode().data(), modelObjects.at(i)->filenameNode().data() ); + } + + if ( readObjects.at(i)->image() ) + { + QCOMPARE( *(readObjects.at(i)->image()), *(modelObjects.at(i)->image()) ); + } + else + { + QCOMPARE( readObjects.at(i)->image(), modelObjects.at(i)->image() ); + } + QCOMPARE( readObjects.at(i)->svg(), modelObjects.at(i)->svg() ); + + QCOMPARE( readObjects.at(i)->lineWidth(), modelObjects.at(i)->lineWidth() ); + QVERIFY( readObjects.at(i)->lineColorNode() == modelObjects.at(i)->lineColorNode() ); + QVERIFY( readObjects.at(i)->fillColorNode() == modelObjects.at(i)->fillColorNode() ); + + QCOMPARE( readObjects.at(i)->bcData(), modelObjects.at(i)->bcData() ); + QCOMPARE( readObjects.at(i)->bcTextFlag(), modelObjects.at(i)->bcTextFlag() ); + QCOMPARE( readObjects.at(i)->bcChecksumFlag(), modelObjects.at(i)->bcChecksumFlag() ); + QVERIFY( readObjects.at(i)->bcColorNode() == modelObjects.at(i)->bcColorNode() ); + QVERIFY( !( readObjects.at(i)->bcStyle() != modelObjects.at(i)->bcStyle() ) ); // Only != operator + QCOMPARE( readObjects.at(i)->bcFormatDigits(), modelObjects.at(i)->bcFormatDigits() ); + + QCOMPARE( readObjects.at(i)->canText(), modelObjects.at(i)->canText() ); + QCOMPARE( readObjects.at(i)->canFill(), modelObjects.at(i)->canFill() ); + QCOMPARE( readObjects.at(i)->canLineColor(), modelObjects.at(i)->canLineColor() ); + QCOMPARE( readObjects.at(i)->canLineWidth(), modelObjects.at(i)->canLineWidth() ); + } + + QCOMPARE( readObjects[10]->filenameNode().data(), pngAbsoluteFileName ); + QCOMPARE( readObjects[11]->filenameNode().data(), pngRelativeFileName ); + QCOMPARE( readObjects[12]->filenameNode().data(), svgRelativeFileName ); + + QCOMPARE( readModel->variables()->size(), model->variables()->size() ); + for ( const auto& modelV : *model->variables() ) + { + QVERIFY( readModel->variables()->hasVariable( modelV.name() ) ); + const auto& readV = readModel->variables()->value( modelV.name() ); + QCOMPARE( readV.type(), modelV.type() ); + QCOMPARE( readV.initialValue(), modelV.initialValue() ); + if ( readV.type() == Variable::Type::INTEGER || readV.type() == Variable::Type::FLOATING_POINT ) + { + QCOMPARE( readV.stepSize(), modelV.stepSize() ); + QCOMPARE( readV.increment(), modelV.increment() ); + } + QCOMPARE( readV.value(), modelV.value() ); + } + + QCOMPARE( readModel->merge()->id(), model->merge()->id() ); + QCOMPARE( readModel->merge()->source(), model->merge()->source() ); + QCOMPARE( readModel->merge()->recordList().size(), model->merge()->recordList().size() ); + for ( int i = 0; i < readModel->merge()->recordList().size(); i++ ) + { + QCOMPARE( readModel->merge()->recordList().at(i)->keys(), model->merge()->recordList().at(i)->keys() ); + QCOMPARE( readModel->merge()->recordList().at(i)->values(), model->merge()->recordList().at(i)->values() ); + } + + delete readModel->merge(); + delete readModel->variables(); + delete readModel; + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::parser_3ReadFile() +{ + // Current path is "build/model/unit_tests" so go up 3 levels + QFileInfo glabelsFileInfo( "../../../model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels" ); + QVERIFY( glabelsFileInfo.isReadable() ); + + Model* model = XmlLabelParser::readFile( glabelsFileInfo.filePath() ); + QVERIFY( model ); + + QCOMPARE( model->fileName(), glabelsFileInfo.filePath() ); + + QCOMPARE( model->tmplate()->brand(), QString( "Avery" ) ); + QCOMPARE( model->tmplate()->part(), QString( "5395" ) ); + QCOMPARE( model->tmplate()->description(), QString( "Name Badge Labels" ) ); + QCOMPARE( model->tmplate()->paperId(), QString( "US-Letter" ) ); + QCOMPARE( model->tmplate()->pageWidth().in(), 8.5 ); + QCOMPARE( model->tmplate()->pageHeight().in(), 11.0 ); + + QCOMPARE( model->frame()->id(), QString( "0" ) ); + const FrameRect* frameRect = dynamic_cast( model->frame() ); + QVERIFY( frameRect ); + QCOMPARE( frameRect->w().in(), 3.375 ); + QCOMPARE( frameRect->h().in(), 2.33333 ); + QCOMPARE( frameRect->r().in(), 0.1875 ); + QCOMPARE( frameRect->xWaste().in(), 0.0625 ); + QCOMPARE( frameRect->yWaste().in(), 0.0625 ); + + QCOMPARE( model->frame()->markups().size(), 1 ); + MarkupMargin* markupMargin = dynamic_cast( model->frame()->markups()[0] ); + QVERIFY( markupMargin ); + QCOMPARE( markupMargin->xSize().in(), 0.0625 ); + QCOMPARE( markupMargin->ySize().in(), 0.0625 ); + + QCOMPARE( model->frame()->layouts().size(), 1 ); + QCOMPARE( model->frame()->layouts()[0].nx(), 2 ); + QCOMPARE( model->frame()->layouts()[0].ny(), 4 ); + QCOMPARE( model->frame()->layouts()[0].x0().in(), 0.6875 ); + QCOMPARE( model->frame()->layouts()[0].y0().in(), 0.583333 ); + QCOMPARE( model->frame()->layouts()[0].dx().in(), 3.75 ); + QCOMPARE( model->frame()->layouts()[0].dy().in(), 2.5 ); + + QCOMPARE( model->rotate(), false ); + + QCOMPARE( model->objectList().size(), 4 ); + + ModelTextObject* modelTextObject0 = dynamic_cast( model->objectList()[0] ); + QVERIFY( modelTextObject0 ); + QCOMPARE( modelTextObject0->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject0->y0().in(), 0.2625 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject0->lockAspectRatio(), false ); + QCOMPARE( modelTextObject0->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject0->shadow(), false ); + QCOMPARE( modelTextObject0->text(), QString( "Hello, my name is" ) ); + QCOMPARE( modelTextObject0->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject0->fontSize(), 16 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject0->fontWeight(), QFont::Bold ); + QCOMPARE( modelTextObject0->fontItalicFlag(), false ); + QCOMPARE( modelTextObject0->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject0->textAutoShrink(), false ); + QCOMPARE( modelTextObject0->textColorNode().color(), QColor::fromRgba( 0xff3366ff ) ); // QColor uses ARGB + QCOMPARE( modelTextObject0->textHAlign(), Qt::AlignLeft ); + + ModelTextObject* modelTextObject1 = dynamic_cast( model->objectList()[1] ); + QVERIFY( modelTextObject1 ); + QCOMPARE( modelTextObject1->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject1->y0().in(), 0.645 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject1->lockAspectRatio(), false ); + QCOMPARE( modelTextObject1->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject1->shadow(), false ); + QCOMPARE( modelTextObject1->text(), QString( "${Name}" ) ); + QCOMPARE( modelTextObject1->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject1->fontSize(), 20 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject1->fontWeight(), QFont::Normal ); + QCOMPARE( modelTextObject1->fontItalicFlag(), false ); + QCOMPARE( modelTextObject1->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject1->textAutoShrink(), false ); + QCOMPARE( modelTextObject1->textColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelTextObject1->textHAlign(), Qt::AlignLeft ); + + ModelTextObject* modelTextObject2 = dynamic_cast( model->objectList()[2] ); + QVERIFY( modelTextObject2 ); + QCOMPARE( modelTextObject2->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject2->y0().in(), 1.14 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject2->lockAspectRatio(), false ); + QCOMPARE( modelTextObject2->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject2->shadow(), false ); + QCOMPARE( modelTextObject2->text(), QString( "Department: ${Department}" ) ); + QCOMPARE( modelTextObject2->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject2->fontSize(), 11 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject2->fontWeight(), QFont::Normal ); + QCOMPARE( modelTextObject2->fontItalicFlag(), false ); + QCOMPARE( modelTextObject2->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject2->textAutoShrink(), false ); + QCOMPARE( modelTextObject2->textColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelTextObject2->textHAlign(), Qt::AlignLeft ); + + ModelBarcodeObject* modelBarcodeObject3 = dynamic_cast( model->objectList()[3] ); + QVERIFY( modelBarcodeObject3 ); + QCOMPARE( modelBarcodeObject3->x0().in(), 0.150603 ); + QCOMPARE( modelBarcodeObject3->y0().in(), 1.395 ); + QCOMPARE( modelBarcodeObject3->w().in(), 3.06944 ); + QCOMPARE( modelBarcodeObject3->h().in(), 0.847222 ); + QCOMPARE( modelBarcodeObject3->lockAspectRatio(), false ); + QCOMPARE( modelBarcodeObject3->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelBarcodeObject3->shadow(), false ); + + QCOMPARE( modelBarcodeObject3->bcData(), QString( "${SN}" ) ); + QVERIFY( modelBarcodeObject3->bcTextFlag() ); + QVERIFY( modelBarcodeObject3->bcChecksumFlag() ); + QCOMPARE( modelBarcodeObject3->bcColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelBarcodeObject3->bcStyle().fullId(), QString( "code39" ) ); + QCOMPARE( modelBarcodeObject3->bcFormatDigits(), 10 ); + + QVERIFY( model->merge() ); + QVERIFY( !model->merge()->source().isEmpty() ); // Merge source hacked to work relatively so not realistic + QCOMPARE( model->merge()->recordList().size(), 4 ); + + QCOMPARE( model->merge()->recordList()[0]->keys().size(), 3 ); + QList keys, values0, values1, values2, values3; + keys << "Department" << "Name" << "SN"; + values0 << "Management" << "Jim Kirk" << "SC937-0176 CEC"; + values1 << "Sciences" << "Mr. Spock" << "S179-276SP"; + values2 << "Medicine" << "Leonard McCoy" << "unknown"; + values3 << "Engineering" << "Montgomery Scott" << "SE-197-54T"; + + QCOMPARE( model->merge()->recordList()[0]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[0]->values(), values0 ); + QCOMPARE( model->merge()->recordList()[1]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[1]->values(), values1 ); + QCOMPARE( model->merge()->recordList()[2]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[2]->values(), values2 ); + QCOMPARE( model->merge()->recordList()[3]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[3]->values(), values3 ); + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::parser_3Barcode() +{ + QTemporaryFile glabels( "TestXmlLabel_XXXXXX.glabels" ); + glabels.open(); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.close(); + + Model* model = XmlLabelParser::readFile( glabels.fileName() ); + QVERIFY( model ); + + QCOMPARE( model->objectList().size(), 5 ); + + ModelBarcodeObject* modelBarcodeObject; + + modelBarcodeObject = dynamic_cast( model->objectList()[0] ); + QVERIFY( modelBarcodeObject ); + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39ext" ) ); + + modelBarcodeObject = dynamic_cast( model->objectList()[1] ); + QVERIFY( modelBarcodeObject ); + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "datamatrix" ) ); + + modelBarcodeObject = dynamic_cast( model->objectList()[2] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "qrencode", "qrcode" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "qrencode::qrcode" ) ); + } + else if ( Backends::style( "zint", "qr" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "zint::qr" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + modelBarcodeObject = dynamic_cast( model->objectList()[3] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "gnu-barcode", "upc-a+2" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "gnu-barcode::upc-a+2" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + modelBarcodeObject = dynamic_cast( model->objectList()[4] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "zint", "gs1-128" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "zint::gs1-128" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + delete model->merge(); + delete model->variables(); + delete model; +} diff --git a/model/unit_tests/TestXmlLabel.h b/model/unit_tests/TestXmlLabel.h new file mode 100644 index 0000000..57464ed --- /dev/null +++ b/model/unit_tests/TestXmlLabel.h @@ -0,0 +1,36 @@ +/* TestXmlLabel.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestXmlLabel : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void serializeDeserialize(); + void writeReadFile(); + void parser_3ReadFile(); + void parser_3Barcode(); +}; + + diff --git a/model/unit_tests/Test_Constants.h b/model/unit_tests/Test_Constants.h new file mode 100644 index 0000000..f57b640 --- /dev/null +++ b/model/unit_tests/Test_Constants.h @@ -0,0 +1,41 @@ +/* Test_Constants.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef test_Constants_h +#define test_Constants_h + + +namespace glabels +{ + namespace test + { + + const char* blue_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpVIqDhYRcchQnSyIijhqFYpQIdQKrTqYvPQPmjQkKS6OgmvBwZ/FqoOLs64OroIg+APi4uqk6CIl3pcUWsR44fE+zrvn8N59gNCoMM3qGgc03TbTyYSYza2KoVeEEMYAAhBkZhlzkpSCb33dUx/VXZxn+ff9Wb1q3mJAQCSeZYZpE28QT2/aBud94igrySrxOfGYSRckfuS64vEb56LLAs+Mmpn0PHGUWCx2sNLBrGRqxFPEMVXTKV/Ieqxy3uKsVWqsdU/+wkheX1nmOq1hJLGIJUgQoaCGMiqwEaddJ8VCms4TPv4h1y+RSyFXGYwcC6hCg+z6wf/g92ytwuSElxRJAN0vjvMxAoR2gWbdcb6PHad5AgSfgSu97a82gJlP0uttLXYE9G0DF9dtTdkDLneAwSdDNmVXCtISCgXg/Yy+KQf03wLhNW9urXOcPgAZmlXqBjg4BEaLlL3u8+6ezrn929Oa3w/Q2XJm1/XlIwAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+MHChYzAoNXJCYAAAAWSURBVBjTY2Rg+P+fAQ9gYiAAhocCABBdAg7zMxsKAAAAAElFTkSuQmCC"; + const char* green_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIw1AUhv+mikUqDlYQcchQnSz4Qhy1CkWoEGqFVh1MbvqCJg1Jiouj4Fpw8LFYdXBx1tXBVRAEHyAurk6KLlLiuUmhRYwXDvfjv/f/OfdcQKiXmWZ1jAGabpupRFzMZFfFrleE0E81jpDMLGNOkpLwXV/3CPD9Lsaz/O/9uXrUnMWAgEg8ywzTJt4gnt60Dc77xBFWlFXic+JRkxokfuS64vEb54LLAs+MmOnUPHGEWCy0sdLGrGhqxFPEUVXTKV/IeKxy3uKslaus2Sd/YTinryxznWoICSxiCRJEKKiihDJsxGjXSbGQovO4j3/Q9UvkUshVAiPHAirQILt+8D/4PVsrPznhJYXjQOeL43wMA127QKPmON/HjtM4AYLPwJXe8lfqwMwn6bWWFj0CereBi+uWpuwBlzvAwJMhm7IrBamEfB54P6NvygJ9t0D3mje35jlOH4A0zSp5AxwcAiMFyl73eXeofW7/3mnO7wdSvnKatbS90wAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFQovFXnldrAAAAAWSURBVBjTY2T4z/CfAQ9gYiAAhocCABFcAg5KXrI7AAAAAElFTkSuQmCC"; + const char* yellow_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpUUqIhYs4pChOlkQFXHUKhShQqgVWnUwufQLmjQkLS6OgmvBwY/FqoOLs64OroIg+AHi4uqk6CIl/i8ptIjx4Lgf7+497t4BQqPMNKtrHND0qplKxMVMdlUMvCKIQfjRj4jMLGNOkpLwHF/38PH1LsazvM/9OXrVnMUAn0g8ywyzSrxBPL1ZNTjvE4dZUVaJz4nHTLog8SPXFZffOBccFnhm2Eyn5onDxGKhg5UOZkVTI54ijqqaTvlCxmWV8xZnrVxjrXvyF4Zy+soy12kOI4FFLEGCCAU1lFBGFTFadVIspGg/7uEfcvwSuRRylcDIsYAKNMiOH/wPfndr5Scn3KRQHOh+se2PESCwCzTrtv19bNvNE8D/DFzpbX+lAcx8kl5va9EjoG8buLhua8oecLkDRJ4M2ZQdyU9TyOeB9zP6piwwcAv0rLm9tfZx+gCkqavkDXBwCIwWKHvd493Bzt7+PdPq7wcjL3KHuPu4MgAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFwMyBT7m+cwAAAAWSURBVBjTY/z/n+E/Ax7AxEAADA8FABdkAw08uCaCAAAAAElFTkSuQmCC"; + const char* red_8x8_svg = ""; + const char* cyan_8x8_svg = ""; + const char* magenta_8x8_svg = ""; + + } +} + + +#endif // test_Constants_h diff --git a/model/unit_tests/data/glabels-3/crew-orientation-list.csv b/model/unit_tests/data/glabels-3/crew-orientation-list.csv new file mode 100644 index 0000000..1af4b38 --- /dev/null +++ b/model/unit_tests/data/glabels-3/crew-orientation-list.csv @@ -0,0 +1,5 @@ +Name,Department,SN +"Jim Kirk",Management,"SC937-0176 CEC" +"Mr. Spock",Sciences,S179-276SP +"Leonard McCoy",Medicine,unknown +"Montgomery Scott",Engineering,SE-197-54T diff --git a/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels b/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels new file mode 100644 index 0000000..29546fa --- /dev/null +++ b/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels @@ -0,0 +1,25 @@ + + + + + + Hello, my name is + + + + + + + + Department: + + + + + + diff --git a/templates/glabels-4.0.dtd b/templates/glabels-4.0.dtd index 5834460..5a584b2 100644 --- a/templates/glabels-4.0.dtd +++ b/templates/glabels-4.0.dtd @@ -87,6 +87,10 @@ iec16022)" --> + + + + @@ -139,7 +143,7 @@ - + + + + + + + + + diff --git a/templates/herma-iso-templates.xml b/templates/herma-iso-templates.xml index ad88d03..002abdf 100644 --- a/templates/herma-iso-templates.xml +++ b/templates/herma-iso-templates.xml @@ -223,6 +223,18 @@