diff --git a/.gitignore b/.gitignore index f512be11..168c78af 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ core *.sav* .directory TEST-DATA +SAV* +OLD* + diff --git a/backends/barcode/Backends.cpp b/backends/barcode/Backends.cpp index 040e81ea..96fc83b4 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/docs/SUBSTITUTION-FIELD-SPEC.md b/docs/SUBSTITUTION-FIELD-SPEC.md index b69ff38e..ddfc2ef2 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 6eb3cb87..039f6828 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/CMakeLists.txt b/glabels/CMakeLists.txt index 6679ac9d..f8ba83f7 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 1abb9a03..28ea4c88 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 eb16611e..81ba63d0 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 60ff08f9..ba564dee 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(); + QList colorList; + + for ( QString& nameColor : readNameColorList() ) + { + 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; } - QColor ColorHistory::getColor( int id ) + QStringList ColorHistory::getNames() { - QList colors = readColorList(); - return colors[id]; + QStringList nameList; + + for ( QString& nameColor : readNameColorList() ) + { + 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; } - QList ColorHistory::readColorList() + QStringList ColorHistory::readNameColorList() { QStringList defaultList; QSettings settings; settings.beginGroup( "ColorHistory" ); - QStringList colorNameList = settings.value( "colors", defaultList ).toStringList(); + QStringList nameColorList = settings.value( "colors", defaultList ).toStringList(); settings.endGroup(); - QList colorList; - foreach ( QString colorName, colorNameList ) - { - colorList << QColor( colorName ); - } - // Remove oldest colors, if size exceeds current max - while ( colorList.size() > MAX_COLORS ) + while ( nameColorList.size() > MAX_COLORS ) { - colorList.removeFirst(); + nameColorList.removeFirst(); } - return colorList; + return nameColorList; } - void ColorHistory::writeColorList( const QList& colorList ) + void ColorHistory::writeNameColorList( const QStringList& nameColorList ) { - // Build name list - QStringList colorNameList; - foreach ( QColor color, colorList ) - { - colorNameList << color.name(); - } - // 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 b74b3b40..eb148128 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 ce8d5bf3..00000000 --- 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 71feb32c..9ad496a4 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 ); - - 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) ); - + // + // 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; + } + setLayout( vLayout ); loadCustomColorHistory(); @@ -183,55 +196,39 @@ 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-- ) - { - mMergeFieldCombo->removeItem( index ); - } - - // Add new keys - if ( keyList.size() > 0 ) - { - mMergeFieldCombo->addItems( keyList ); - mMergeFieldCombo->setEnabled( true ); - } - else + if (mFieldButton) { - mMergeFieldCombo->setEnabled( false ); + mFieldButton->setKeys( merge, variables ); } } - void ColorPaletteDialog::clearKeys() + void ColorPaletteDialog::onPaletteItemActivated( int id ) { - - for ( int index = mMergeFieldCombo->count()-1; index > 0; index-- ) - { - mMergeFieldCombo->removeItem( index ); - } - mMergeFieldCombo->setEnabled( false ); - } - + model::ColorNode newColorNode; + newColorNode.setField( false ); + newColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); + newColorNode.setKey( "" ); - void ColorPaletteDialog::onDefaultItemActivated() - { - mColorNode.setField( false ); - mColorNode.setColor( mDefaultColor ); - mColorNode.setKey( "" ); + if ( newColorNode != mColorNode ) + { + mColorNode = newColorNode; + + mColorHistory->addColor( mColorNode.color(), mColorTable[id].trname ); - emit colorChanged( mColorNode, true ); - accept(); + emit colorChanged( mColorNode, false ); + accept(); + } } - void ColorPaletteDialog::onPaletteItemActivated( int id ) + void ColorPaletteDialog::onHistoryItemActivated( int id ) { mColorNode.setField( false ); - mColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); + mColorNode.setColor( mColorHistory->getColors()[id] ); mColorNode.setKey( "" ); emit colorChanged( mColorNode, false ); @@ -239,18 +236,18 @@ namespace glabels } - void ColorPaletteDialog::onHistoryItemActivated( int id ) + void ColorPaletteDialog::onDefaultButtonClicked() { mColorNode.setField( false ); - mColorNode.setColor( mColorHistory->getColor(id) ); + mColorNode.setColor( mDefaultColor ); mColorNode.setKey( "" ); - emit colorChanged( mColorNode, false ); + emit colorChanged( mColorNode, true ); 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 ) - { - if ( index != 0 ) - { - mColorNode.setField( true ); - mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); - mColorNode.setKey( mKeys[index-1] ); - - emit colorChanged( mColorNode, false ); - accept(); - } - } - - - void ColorPaletteDialog::showEvent( QShowEvent* event ) + void ColorPaletteDialog::onKeySelected( QString key ) { - mMergeFieldCombo->setCurrentIndex( 0 ); + mColorNode.setField( true ); + mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); + mColorNode.setKey( key ); - QDialog::showEvent( event ); + emit colorChanged( mColorNode, false ); + accept(); } } // namespace glabels diff --git a/glabels/ColorPaletteDialog.h b/glabels/ColorPaletteDialog.h index db5f7e18..6e1a22c1 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 00000000..a685e73a --- /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 0ffc6272..6c71d613 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 f6c25b67..483e7705 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 ) + /// + /// Set Keys + /// + void FieldButton::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mName = name; - if ( count() == 0 ) + // Clear old keys + mMenu.clear(); + + // Add merge keys, if any + mMenu.addSection( tr("Merge fields") ); + for ( auto& key : merge->keys() ) { - addItem( mName ); + auto* action = mMenu.addAction( QString( "${%1}" ).arg( key ) ); + action->setData( key ); } - else + if ( merge->keys().empty() ) { - setItemText( 0, mName ); + 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) ); - } - - - void FieldButton::setKeys( const QStringList& keyList ) - { - // Clear old keys - clear(); - addItem( 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) ); - - // 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 ); } - } - - void FieldButton::clearKeys() - { - clear(); - addItem( mName ); - - setEnabled( false ); + setEnabled( !merge->keys().empty() || !variables->keys().empty() ); } - /// - /// 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 1da2d576..230a86dd 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 71fa1606..5a354738 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 5f02ed2e..37dd0816 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 560eb41c..85ede544 100644 --- a/glabels/LabelEditor.cpp +++ b/glabels/LabelEditor.cpp @@ -1162,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 fb09531a..42c939d4 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, }; } @@ -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()) ); @@ -201,6 +217,7 @@ namespace glabels if ( mModel ) { delete mModel->merge(); // Ownership of final Merge instance is ours + delete mModel->variables(); // Ownership of Variables instance is ours delete mModel; } } @@ -226,7 +243,8 @@ namespace glabels 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 ); @@ -323,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") ); @@ -611,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 ); @@ -823,6 +847,17 @@ namespace glabels } + /// + /// Create Variables Page + /// + QWidget* MainWindow::createVariablesPage() + { + mVariablesView = new VariablesView(); + + return mVariablesView; + } + + /// /// Create Print Page /// @@ -847,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? @@ -859,6 +895,7 @@ namespace glabels mEditorAction->setVisible( !isWelcomePage ); mPropertiesAction->setVisible( !isWelcomePage ); mMergeAction->setVisible( !isWelcomePage ); + mVariablesAction->setVisible( !isWelcomePage ); mPrintAction->setVisible( !isWelcomePage ); // Recent file actions @@ -884,6 +921,7 @@ 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 ); @@ -1110,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 ); @@ -1206,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 dc6a2a2f..56e4fab5 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 0a1eea68..c9323246 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 87349945..e0ddead8 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 b635ae33..0ddefde4 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; @@ -499,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() ); } } @@ -620,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 ) ); + } } diff --git a/glabels/ObjectEditor.h b/glabels/ObjectEditor.h index 6e84687a..57b380f1 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/VariablesView.cpp b/glabels/VariablesView.cpp new file mode 100644 index 00000000..ea13f334 --- /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 00000000..93dca3d3 --- /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 8d4f777d..faa0f306 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 00000000..9b478135 --- /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 c97f40e2..575edc36 100644 Binary files a/glabels/images/checkerboard.png and b/glabels/images/checkerboard.png differ diff --git a/glabels/ui/EditVariableDialog.ui b/glabels/ui/EditVariableDialog.ui new file mode 100644 index 00000000..72f85752 --- /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 e73b77aa..c7f682a3 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 @@ -164,34 +175,34 @@ - locationButton - clicked() + formatCombo + activated(int) MergeView - onLocationButtonClicked() + onFormatComboActivated() - 174 - 93 + 257 + 109 - 570 - 75 + 563 + 50 - formatCombo - activated(int) + locationBrowseButton + clicked() MergeView - onFormatComboActivated() + onLocationBrowseButtonClicked() - 162 - 48 + 296 + 130 - 563 - 50 + 565 + 149 @@ -199,7 +210,7 @@ onSelectAllButtonClicked() onUnselectAllButtonClicked() - onLocationButtonClicked() onFormatComboActivated() + onLocationBrowseButtonClicked() diff --git a/glabels/ui/ObjectEditor.ui b/glabels/ui/ObjectEditor.ui index 7a2b6317..29ca0985 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 - - - - @@ -1521,19 +1521,19 @@ - glabels::FieldButton - QComboBox -
FieldButton.h
+ glabels::BarcodeMenuButton + QPushButton +
BarcodeMenuButton.h
- keySelected(QString) + selectionChanged()
- glabels::BarcodeMenuButton + glabels::FieldButton QPushButton -
BarcodeMenuButton.h
+
FieldButton.h
- selectionChanged() + keySelected(QString)
@@ -1644,8 +1644,8 @@ onTextControlsChanged() - 157 - 333 + 160 + 332 396 @@ -1660,8 +1660,8 @@ onTextControlsChanged() - 198 - 333 + 200 + 332 398 @@ -1676,8 +1676,8 @@ onTextControlsChanged() - 238 - 333 + 240 + 332 395 @@ -1692,8 +1692,8 @@ onTextControlsChanged() - 284 - 333 + 286 + 332 393 @@ -1708,8 +1708,8 @@ onTextControlsChanged() - 325 - 333 + 326 + 332 396 @@ -1725,7 +1725,7 @@ 365 - 333 + 332 397 @@ -1740,8 +1740,8 @@ onTextControlsChanged() - 184 - 407 + 189 + 404 394 @@ -1757,7 +1757,7 @@ 178 - 143 + 139 392 @@ -1772,8 +1772,8 @@ onLineControlsChanged() - 137 - 179 + 136 + 174 1 @@ -1788,8 +1788,8 @@ onFillControlsChanged() - 136 - 263 + 135 + 256 6 @@ -1804,8 +1804,8 @@ onPositionControlsChanged() - 159 - 142 + 160 + 138 399 @@ -1820,8 +1820,8 @@ onPositionControlsChanged() - 159 - 179 + 160 + 174 325 @@ -1836,8 +1836,8 @@ onRectSizeControlsChanged() - 159 - 265 + 160 + 258 3 @@ -1852,8 +1852,8 @@ onRectSizeControlsChanged() - 159 - 302 + 160 + 294 0 @@ -1868,8 +1868,8 @@ onResetImageSize() - 210 - 372 + 213 + 362 4 @@ -1900,8 +1900,8 @@ onShadowControlsChanged() - 165 - 142 + 166 + 138 398 @@ -1916,8 +1916,8 @@ onShadowControlsChanged() - 165 - 179 + 166 + 174 294 @@ -1932,8 +1932,8 @@ onShadowControlsChanged() - 142 - 215 + 141 + 209 399 @@ -1948,8 +1948,8 @@ onShadowControlsChanged() - 159 - 252 + 162 + 245 399 @@ -1964,8 +1964,8 @@ onLineSizeControlsChanged() - 174 - 456 + 177 + 444 5 @@ -1980,8 +1980,8 @@ onLineSizeControlsChanged() - 174 - 493 + 177 + 480 1 @@ -1990,14 +1990,14 @@ - imageFileButton + imageBrowseButton clicked() ObjectEditor onImageFileButtonClicked() - 133 - 175 + 367 + 135 394 @@ -2005,22 +2005,6 @@ - - imageFieldCombo - keySelected(QString) - ObjectEditor - onImageKeySelected(QString) - - - 302 - 175 - - - 397 - 32 - - - textEdit textChanged() @@ -2037,22 +2021,6 @@ - - textInsertFieldCombo - keySelected(QString) - ObjectEditor - onTextInsertFieldKeySelected(QString) - - - 239 - 599 - - - 395 - 645 - - - barcodeShowTextCheck toggled(bool) @@ -2060,8 +2028,8 @@ onBarcodeControlsChanged() - 178 - 172 + 195 + 167 4 @@ -2076,8 +2044,8 @@ onBarcodeControlsChanged() - 164 - 204 + 195 + 198 1 @@ -2093,7 +2061,7 @@ 126 - 239 + 232 1 @@ -2117,22 +2085,6 @@ - - barcodeInsertFieldCombo - keySelected(QString) - ObjectEditor - onBarcodeInsertFieldKeySelected(QString) - - - 239 - 400 - - - 403 - 625 - - - barcodeStyleButton selectionChanged() @@ -2140,8 +2092,8 @@ onBarcodeControlsChanged() - 178 - 140 + 195 + 136 5 @@ -2197,6 +2149,54 @@ + + 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() @@ -2213,5 +2213,6 @@ onTextInsertFieldKeySelected(QString) onBarcodeControlsChanged() onBarcodeInsertFieldKeySelected(QString) + onImageComboChanged() diff --git a/glabels/ui/PrintView.ui b/glabels/ui/PrintView.ui index 0301ee05..348badaf 100644 --- a/glabels/ui/PrintView.ui +++ b/glabels/ui/PrintView.ui @@ -17,7 +17,7 @@ - Form + Form diff --git a/glabels/ui/PropertiesView.ui b/glabels/ui/PropertiesView.ui index 6b8f115d..64817333 100644 --- a/glabels/ui/PropertiesView.ui +++ b/glabels/ui/PropertiesView.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/StartupView.ui b/glabels/ui/StartupView.ui index 246e5c0f..0aca3cb6 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 d0ed63b9..3b1b9132 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 f61fa315..a9add20f 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 8960aa62..d3cc1123 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 53dec92c..0ea9ee54 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 90665ff1..9b397cbd 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 df42ffda..e88dd2ff 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 9bf0d133..a9ed4d7d 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 508cfd46..315266df 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 38c631f0..41686e71 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 2f784522..3b59a4c7 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 7265e8ff..a4a47589 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 c51aa036..2a6651da 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 0466fbb9..a471d6c8 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 d7cc9dc4..e3cc62a3 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 00000000..c0953d66 --- /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 0edacf88..1690772a 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 f399a1a6..ceee003c 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 89b0201e..dab30afe 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 09e61d2e..a3e2bcb0 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 ed76de01..0cac0db1 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 32630d83..4bcf47fc 100644 --- a/model/Model.cpp +++ b/model/Model.cpp @@ -57,13 +57,17 @@ 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 ) + Model::Model( merge::Merge* merge, Variables* variables ) : mUntitledInstance(0), mModified(true), mRotate(false) { + mVariables = variables; // Shared mMerge = merge; // Shared } @@ -73,7 +77,8 @@ namespace glabels /// Model::~Model() { - // Final instance of mMerge to be deleted by Model owner + qDeleteAll( mObjectList ); + // Final instance of mMerge and mVariables to be deleted by Model owner } @@ -82,7 +87,7 @@ namespace glabels /// Model* Model::save() const { - auto* savedModel = new Model( mMerge ); // mMerge shared between models + auto* savedModel = new Model( mMerge, mVariables ); // mMerge and mVariables shared between models if ( mFileName.isEmpty() && mUntitledInstance == 0 ) { @@ -283,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. /// @@ -309,6 +346,15 @@ namespace glabels } + /// + /// Get variables object + /// + Variables* Model::variables() const + { + return mVariables; + } + + /// /// Get merge object /// @@ -458,6 +504,17 @@ namespace glabels } + /// + /// Variables Changed Slot + /// + void Model::onVariablesChanged() + { + setModified(); + emit changed(); + emit variablesChanged(); + } + + /// /// Merge Source Changed Slot /// @@ -1366,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 ); @@ -1422,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 ) @@ -1459,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 89537dbd..e31574dc 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,7 +59,7 @@ namespace glabels ///////////////////////////////// public: Model(); - Model( merge::Merge* merge ); + Model( merge::Merge* merge, Variables* variables ); ~Model(); @@ -77,6 +79,7 @@ namespace glabels void sizeChanged(); void selectionChanged(); void modifiedChanged(); + void variablesChanged(); void mergeChanged(); void mergeSourceChanged(); void mergeSelectionChanged(); @@ -90,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 ); @@ -108,6 +113,8 @@ namespace glabels const QList& objectList() const; + Variables* variables() const; + merge::Merge* merge() const; void setMerge( merge::Merge* merge ); @@ -205,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; ///////////////////////////////// @@ -214,6 +224,7 @@ namespace glabels private slots: void onObjectChanged(); void onObjectMoved(); + void onVariablesChanged(); void onMergeSourceChanged(); void onMergeSelectionChanged(); @@ -230,6 +241,7 @@ namespace glabels QList mObjectList; + Variables* mVariables; merge::Merge* mMerge; }; diff --git a/model/ModelBarcodeObject.cpp b/model/ModelBarcodeObject.cpp index 1cdd5e2a..ed4f1bf6 100644 --- a/model/ModelBarcodeObject.cpp +++ b/model/ModelBarcodeObject.cpp @@ -312,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. } @@ -323,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 ) { @@ -333,7 +335,7 @@ namespace glabels } else { - drawBc( painter, bcColor, record ); + drawBc( painter, bcColor, record, variables ); } } @@ -451,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 ) ); @@ -459,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 ef5918df..2a49304f 100644 --- a/model/ModelBarcodeObject.h +++ b/model/ModelBarcodeObject.h @@ -127,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; @@ -140,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 c6b9a8a2..4fd66e75 100644 --- a/model/ModelBoxObject.cpp +++ b/model/ModelBoxObject.cpp @@ -104,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 ); @@ -149,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 610fc3cf..21b58269 100644 --- a/model/ModelBoxObject.h +++ b/model/ModelBoxObject.h @@ -73,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 783ab96b..7e35791e 100644 --- a/model/ModelEllipseObject.cpp +++ b/model/ModelEllipseObject.cpp @@ -104,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 ); @@ -149,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 e690b61c..26ae6d4b 100644 --- a/model/ModelEllipseObject.h +++ b/model/ModelEllipseObject.h @@ -73,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 d0c5c522..8bfd0e33 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 /// @@ -102,6 +115,8 @@ namespace glabels mImage = nullptr; mSvgRenderer = nullptr; + + loadImage(); } @@ -398,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; + } } } } @@ -427,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 ) { @@ -448,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; + } + } } } @@ -484,77 +600,108 @@ namespace glabels if ( !mFilenameNode.isField() ) { QString filename = mFilenameNode.data(); - QFileInfo fileInfo( filename ); + if ( readImageFile( filename, mImage, mSvgRenderer, mSvg ) ) + { + double aspectRatio = 0; + if ( mSvgRenderer ) + { + // Adjust size based on aspect ratio of SVG image + QRectF rect = mSvgRenderer->viewBoxF(); + aspectRatio = rect.width() ? rect.height() / rect.width() : 0; + } + else + { + // 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 ) + { + mH = mW*aspectRatio; + } + else + { + mW = mH/aspectRatio; + } + } + } + } + } + + + /// + /// 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() == "svg") || (fileInfo.suffix() == "SVG") ) + if ( fileInfo.suffix().toLower() == "svg" ) { - QFile file( filename ); + QFile file( fileInfo.filePath() ); if ( file.open( QFile::ReadOnly ) ) { - mSvg = file.readAll(); + svg = file.readAll(); file.close(); - mSvgRenderer = new QSvgRenderer( mSvg ); - if ( !mSvgRenderer->isValid() ) - { - mSvgRenderer = nullptr; - } - else + svgRenderer = new QSvgRenderer( svg ); + if ( !svgRenderer->isValid() ) { - // 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; - } + delete svgRenderer; + svgRenderer = nullptr; + svg.clear(); } } } else { - mImage = new QImage( filename ); - if ( mImage->isNull() ) + image = new QImage( fileInfo.filePath() ); + if ( image->isNull() ) { - mImage = nullptr; - } - 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; - } + 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 eecf2b0b..3e982f5c 100644 --- a/model/ModelImageObject.h +++ b/model/ModelImageObject.h @@ -135,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; @@ -144,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 2ff38bfc..5bef0657 100644 --- a/model/ModelLineObject.cpp +++ b/model/ModelLineObject.cpp @@ -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 16cfdb68..a09670e2 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 9268be09..65ed72a4 100644 --- a/model/ModelObject.cpp +++ b/model/ModelObject.cpp @@ -1226,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() ); @@ -1236,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 4e9de0c9..4962ffd3 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" @@ -411,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(); diff --git a/model/ModelTextObject.cpp b/model/ModelTextObject.cpp index ba43f05c..bb5c7e60 100644 --- a/model/ModelTextObject.cpp +++ b/model/ModelTextObject.cpp @@ -519,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 ) @@ -534,7 +535,7 @@ namespace glabels } else { - drawText( painter, shadowColor, record ); + drawText( painter, shadowColor, record, variables ); } } } @@ -545,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 ) { @@ -555,7 +557,7 @@ namespace glabels } else { - drawText( painter, textColor, record ); + drawText( painter, textColor, record, variables ); } } @@ -697,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(); @@ -705,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 ); @@ -717,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; @@ -791,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 ); @@ -803,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 6cabd49a..0f7d1e97 100644 --- a/model/ModelTextObject.h +++ b/model/ModelTextObject.h @@ -186,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; @@ -197,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 b450ff71..78a3383a 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; - } + printCropMarks( painter ); - if ( (mLastLabel / mNLabelsPerPage) == iPage ) - { - iEnd = mLastLabel % mNLabelsPerPage; - } + int iCopy = 0; + int iLabel = mStartLabel; + int iCurrentPage = 0; const QList records = mMerge->selectedRecords(); - if ( records.size() ) + int iRecord = 0; + int nRecords = records.size(); + + if ( nRecords == 0 ) { - iRecord = (iPage*mNLabelsPerPage + iStart - mStartLabel) % records.size(); + return; } + + mVariables->resetVariables(); - printCropMarks( painter ); - - for ( int i = iStart; i < iEnd; i++ ) + 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, records[iRecord] ); + clipLabel( painter ); + printLabel( painter, records[iRecord], 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 + } + + iRecord = (iRecord + 1) % nRecords; + if ( iRecord == 0 ) + { + iCopy++; + } + iLabel++; + iCurrentPage = iLabel / mNLabelsPerPage; - iRecord = (iRecord + 1) % records.size(); + 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 2db3ae47..08c93a94 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 08ee6bf3..ed4cd293 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 9b190844..2e8ab447 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 178b73e8..76c4047d 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 ec08a006..baaa2558 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 332367e9..b0e22719 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(); } - } - - - /// - /// Is it an empty field - /// - bool TextNode::isEmptyField( merge::Record* record ) const - { - if ( record && mIsField ) + else if ( !mIsField ) { - if ( record->contains( mData ) ) - { - return (*record)[mData].isEmpty(); - } + value = mData; } - return false; + return value; } + } } diff --git a/model/TextNode.h b/model/TextNode.h index 499f863b..c7716d77 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 00000000..fa964620 --- /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 00000000..c18f74fd --- /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 00000000..03fa9276 --- /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 00000000..379d56ff --- /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/XmlLabelCreator.cpp b/model/XmlLabelCreator.cpp index 12bf3176..b7f7cc9e 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, model, model->objectList(), model->rotate() ); - createObjectsNode( root, label->objectList(), label->rotate() ); + if ( model->merge() && !dynamic_cast(model->merge()) ) + { + createMergeNode( root, model ); + } - if ( label->merge() && !dynamic_cast(label->merge()) ) + if ( model->variables()->size() != 0 ) { - createMergeNode( root, label ); + createVariablesNode( root, model ); } - createDataNode( root, label->objectList() ); + 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 */ @@ -453,19 +470,80 @@ namespace glabels 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::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::createDataNode( QDomElement &parent, const QList& objects ) + 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" ); @@ -475,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 ) ); } } @@ -519,7 +599,5 @@ namespace glabels node.appendChild( doc.createCDATASection( QString( svg ) ) ); } - - } } diff --git a/model/XmlLabelCreator.h b/model/XmlLabelCreator.h index 93c235e5..9c765ac1 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 9690a083..fce0f2de 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" ) { @@ -361,13 +375,13 @@ 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 ); /* 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 */ @@ -387,7 +401,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 ModelBoxObject( x0, y0, w, h, lockAspectRatio, @@ -415,13 +429,13 @@ 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 ); /* 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 */ @@ -441,7 +455,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 ModelEllipseObject( x0, y0, w, h, lockAspectRatio, @@ -468,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 */ @@ -488,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, @@ -499,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 ); @@ -533,7 +549,7 @@ 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() ) @@ -545,17 +561,19 @@ namespace glabels } else { - if ( data.hasImage( filename ) ) + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( filename ) ); + + if ( data.hasImage( fn ) ) { return new ModelImageObject( x0, y0, w, h, lockAspectRatio, - filename, data.getImage( filename ), + 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, lockAspectRatio, - filename, data.getSvg( filename ), + filename, data.getSvg( fn ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } @@ -563,7 +581,8 @@ namespace glabels { if ( !filename.isEmpty() ) { - qWarning() << "Embedded file" << filename << "missing. Trying actual file."; + qWarning() << "Embedded file" << fn << "missing. Trying actual file."; + filenameNode.setData( fn ); } return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, @@ -594,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", "" ); @@ -629,7 +648,7 @@ namespace glabels /* 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 */ @@ -663,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. */ @@ -715,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( id ); - merge::Merge* merge = merge::Factory::createMerge( type ); - merge->setSource( src ); + switch ( merge::Factory::idToType( id ) ) + { + case merge::Factory::NONE: + case merge::Factory::FIXED: + break; - label->setMerge( merge ); + 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() ) { @@ -747,19 +783,55 @@ namespace glabels void - XmlLabelParser::parsePixdataNode( const QDomElement& node, DataCache& data ) + XmlLabelParser::parseVariableNode( const QDomElement &node, Model* model ) + { + 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::parseDataNode( const QDomElement &node, + const Model* model, + DataCache& data ) { - // TODO, compatibility with glabels-3 + 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, DataCache& data ) + 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" ) @@ -769,7 +841,7 @@ namespace glabels QImage image; image.loadFromData( ba, "PNG" ); - data.addImage( name, image ); + data.addImage( fn, image ); } else { @@ -778,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 f1358603..93e90e8a 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 dca802ad..fe129d8b 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" ) { @@ -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 ); diff --git a/model/unit_tests/CMakeLists.txt b/model/unit_tests/CMakeLists.txt index c896762e..21fc314c 100644 --- a/model/unit_tests/CMakeLists.txt +++ b/model/unit_tests/CMakeLists.txt @@ -32,6 +32,14 @@ if (Qt5Test_FOUND) 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 #======================================= @@ -48,6 +56,14 @@ if (Qt5Test_FOUND) 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 #======================================= @@ -64,4 +80,20 @@ if (Qt5Test_FOUND) 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 index be30ebf0..251ac97f 100644 --- a/model/unit_tests/TestColorNode.cpp +++ b/model/unit_tests/TestColorNode.cpp @@ -22,8 +22,6 @@ #include "model/ColorNode.h" -#include "merge/Record.h" - #include @@ -45,16 +43,20 @@ void TestColorNode::colorNode() 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 ), blackTransparent ); - QCOMPARE( colorNode.color( &record ), blackTransparent ); + 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() ); @@ -64,8 +66,10 @@ void TestColorNode::colorNode() colorNode.setColor( white ); QCOMPARE( colorNode.color(), white ); QCOMPARE( colorNode.rgba(), rgbaWhite ); - QCOMPARE( colorNode.color( nullptr ), white ); - QCOMPARE( colorNode.color( &record ), white ); + 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" ) ); @@ -98,32 +102,76 @@ void TestColorNode::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 ), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), silver80 ); // Defaults to silver if given non-matching record/variables - /// - /// Record - /// record["key1"] = "white"; - QCOMPARE( colorNode.color( &record ), white ); + QCOMPARE( colorNode.color( &record, nullptr ), white ); record["key1"] = "red"; - QCOMPARE( colorNode.color( &record ), red ); + QCOMPARE( colorNode.color( &record, nullptr ), red ); record["key1"] = "#FF0000"; - QCOMPARE( colorNode.color( &record ), red ); + QCOMPARE( colorNode.color( &record, nullptr ), red ); record["key1"] = "#FFFF0000"; // ARGB - QCOMPARE( colorNode.color( &record ), red ); + QCOMPARE( colorNode.color( &record, nullptr ), red ); record["key1"] = "#8000FF00"; - QCOMPARE( colorNode.color( &record ), green80 ); + QCOMPARE( colorNode.color( &record, nullptr ), green80 ); colorNode.setKey( "key2" ); - QCOMPARE( colorNode.color( &record ), blackTransparent ); + QCOMPARE( colorNode.color( &record, nullptr ), silver80 ); record["key2"] = "#8000FF00"; - QCOMPARE( colorNode.color( &record ), green80 ); + 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/TestFileUtil.cpp b/model/unit_tests/TestFileUtil.cpp new file mode 100644 index 00000000..7625d449 --- /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 00000000..50e1e025 --- /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/TestModel.cpp b/model/unit_tests/TestModel.cpp index 31fc1038..f52b4c47 100644 --- a/model/unit_tests/TestModel.cpp +++ b/model/unit_tests/TestModel.cpp @@ -318,6 +318,19 @@ void TestModel::saveRestore() 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() ); @@ -361,6 +374,7 @@ void TestModel::saveRestore() 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() ); @@ -410,6 +424,10 @@ void TestModel::saveRestore() 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() ); @@ -442,6 +460,7 @@ void TestModel::saveRestore() 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 ); @@ -457,6 +476,7 @@ void TestModel::saveRestore() 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() ); @@ -469,6 +489,7 @@ void TestModel::saveRestore() 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/TestModelImageObject.cpp b/model/unit_tests/TestModelImageObject.cpp new file mode 100644 index 00000000..3bcdcb4a --- /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 00000000..b508d1d8 --- /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 index de481183..f5233286 100644 --- a/model/unit_tests/TestRawText.cpp +++ b/model/unit_tests/TestRawText.cpp @@ -42,14 +42,14 @@ void TestRawText::rawText() QVERIFY( !rawText.hasPlaceHolders() ); QCOMPARE( rawText.toString(), QString( "" ) ); QCOMPARE( rawText.toStdString(), std::string( "" ) ); - QCOMPARE( rawText.expand( &record ), QString( "" ) ); + 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 ), QString( "text" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text" ) ); RawText rawText2( "text" ); QVERIFY( !rawText2.isEmpty() ); @@ -61,34 +61,34 @@ void TestRawText::rawText() QVERIFY( rawText.hasPlaceHolders() ); QCOMPARE( rawText.toString(), QString( "${key1}" ) ); QCOMPARE( rawText.toStdString(), std::string( "${key1}" ) ); - QCOMPARE( rawText.expand( &record ), QString( "" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "" ) ); /// /// Record /// record["key1"] = "val1"; - QCOMPARE( rawText.expand( &record ), QString( "val1" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); rawText = "${key1}${key2}"; QVERIFY( rawText.hasPlaceHolders() ); - QCOMPARE( rawText.expand( &record ), QString( "val1" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); record["key2"] = "val2"; - QCOMPARE( rawText.expand( &record ), QString( "val1val2" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1val2" ) ); rawText = "${key1}text${key2}"; QVERIFY( rawText.hasPlaceHolders() ); - QCOMPARE( rawText.expand( &record ), QString( "val1textval2" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); rawText = "text1${key1}text2${key2}text3"; QVERIFY( rawText.hasPlaceHolders() ); - QCOMPARE( rawText.expand( &record ), QString( "text1val1text2val2text3" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text1val1text2val2text3" ) ); rawText = "${key1}text${key2}${key3}"; QVERIFY( rawText.hasPlaceHolders() ); - QCOMPARE( rawText.expand( &record ), QString( "val1textval2" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); rawText = "${key2}${key3}${key1}"; QVERIFY( rawText.hasPlaceHolders() ); - QCOMPARE( rawText.expand( &record ), QString( "val2val1" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val2val1" ) ); } diff --git a/model/unit_tests/TestSubstitutionField.cpp b/model/unit_tests/TestSubstitutionField.cpp index fa9135a2..04476b63 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( 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( 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, &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( 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( 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, &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( 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( 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, &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 index 38008035..19b25326 100644 --- a/model/unit_tests/TestTextNode.cpp +++ b/model/unit_tests/TestTextNode.cpp @@ -22,8 +22,6 @@ #include "model/TextNode.h" -#include "merge/Record.h" - #include @@ -36,38 +34,37 @@ 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 ), QString( "" ) ); - QCOMPARE( textNode.text( &record ), QString( "" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + 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 ), QString( "" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + 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 ), QString( "data1" ) ); - QCOMPARE( textNode.text( &record ), QString( "data1" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + 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 ), QString( "${data1}" ) ); - QCOMPARE( textNode.text( &record ), QString( "" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + 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 @@ -89,17 +86,39 @@ void TestTextNode::textNode() /// Record /// record["key1"] = ""; - QCOMPARE( textNode.text( &record ), QString( "" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); textNode.setData( QString( "key1" ) ); - QCOMPARE( textNode.text( &record ), QString( "" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( textNode.isEmptyField( &record ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); record["key1"] = "val1"; - QCOMPARE( textNode.text( &record ), QString( "val1" ) ); - QVERIFY( !textNode.isEmptyField( nullptr ) ); - QVERIFY( !textNode.isEmptyField( &record ) ); + 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/TestVariable.cpp b/model/unit_tests/TestVariable.cpp new file mode 100644 index 00000000..85efadcd --- /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 00000000..9cad55d1 --- /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 00000000..c7f6ce45 --- /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 00000000..93b56b42 --- /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 index cc4a510d..e70221fc 100644 --- a/model/unit_tests/TestXmlLabel.cpp +++ b/model/unit_tests/TestXmlLabel.cpp @@ -19,12 +19,17 @@ */ #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" @@ -34,66 +39,94 @@ #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() { - using namespace glabels::barcode; + Settings::init(); + Db::init(); + Factory::init(); Backends::init(); } void TestXmlLabel::serializeDeserialize() { - using namespace glabels::model; - using namespace glabels::barcode; + Model* model = new Model(); - // - // Empty object list - // QList objects, outObjects; QByteArray buffer, outBuffer; + // + // Empty object list + // QCOMPARE( objects.count(), 0 ); - XmlLabelCreator::serializeObjects( objects, buffer ); - outObjects = XmlLabelParser::deserializeObjects( buffer ); + XmlLabelCreator::serializeObjects( objects, model, buffer ); + outObjects = XmlLabelParser::deserializeObjects( buffer, model ); QCOMPARE( objects.count(), outObjects.count() ); QCOMPARE( objects, outObjects ); - XmlLabelCreator::serializeObjects( outObjects, outBuffer ); + 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 ); - QImage png( QFINDTESTDATA( "../../../glabels/images/glabels-logo.png" ) ); - QByteArray svg = ""; 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, "image2.png", png, sMatrix, shadow, 6, 4, 0.9, black ); - objects << new ModelImageObject( 4, 5, 70, 80, lock, "image3.svg", svg ); + 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, "image5.jpg" ) ); // Gives warning that embedded file missing + 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 ); - QCOMPARE( objects.count(), 10 ); + objects << new ModelImageObject( 10, 11, 8, 8, lock, TextNode( false, svgRelative.fileName() ) ); + + QCOMPARE( objects.count(), 11 ); buffer.clear(); - XmlLabelCreator::serializeObjects( objects, buffer ); - outObjects = XmlLabelParser::deserializeObjects( buffer ); + 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; @@ -128,7 +161,17 @@ void TestXmlLabel::serializeDeserialize() QCOMPARE( objects.at(i)->textLineSpacing(), outObjects.at(i)->textLineSpacing() ); QCOMPARE( objects.at(i)->textAutoShrink(), outObjects.at(i)->textAutoShrink() ); - QVERIFY( objects.at(i)->filenameNode() == outObjects.at(i)->filenameNode() ); + 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()) ); @@ -157,6 +200,504 @@ void TestXmlLabel::serializeDeserialize() } outBuffer.clear(); - XmlLabelCreator::serializeObjects( outObjects, outBuffer ); + 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 index 0483f54f..57464ede 100644 --- a/model/unit_tests/TestXmlLabel.h +++ b/model/unit_tests/TestXmlLabel.h @@ -28,6 +28,9 @@ class TestXmlLabel : public QObject 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 00000000..f57b640b --- /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 00000000..1af4b38a --- /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 00000000..29546fa3 --- /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 1ade6a98..5a584b21 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/translations/glabels_C.ts b/translations/glabels_C.ts index e60e71f2..324c7794 100644 --- a/translations/glabels_C.ts +++ b/translations/glabels_C.ts @@ -178,6 +178,37 @@ + + EditVariableDialog + + Dialog + + + + Name: + + + + Step size: + + + + Increment + + + + Variable + + + + Type: + + + + Value: + + + Factory @@ -251,18 +282,10 @@ MergeView - - Form - - Source - - Location - - Format: @@ -283,11 +306,15 @@ Unselect all + + Browse... + + ObjectEditor - Form + Object properties @@ -314,6 +341,14 @@ Word + + Anywhere + + + + None + + Allow printing to shrink text to fit object @@ -375,23 +410,7 @@ - None - - - - Anywhere - - - - Select File... - - - - or - - - - Select Merge Field... + Browse... @@ -470,10 +489,6 @@ Opacity: - - Object properties - - PreferencesDialog @@ -516,22 +531,6 @@ PrintView - - Form - - - - Page - - - - of - - - - nn - - Copies @@ -568,13 +567,21 @@ Print - - - PropertiesView - Form + Page + + + + of + + + + nn + + + PropertiesView Product @@ -616,31 +623,31 @@ - Orientation + Adjustable Parameters - Select horizontal or vertical orientation. + Label length: - Horizontal orientation + Orientation - Vertical orientation + Select horizontal or vertical orientation. - Similar Products + Horizontal orientation - Adjustable Parameters + Vertical orientation - Label length: + Similar Products @@ -724,10 +731,6 @@ StartupView - - Form - - Welcome to gLabels. Let's get started: @@ -751,10 +754,6 @@ TemplateDesignerApplyPage - - Form - - You have completed the gLabels Product Template Designer. If you wish to accept and save your product template, click "Save." @@ -767,23 +766,19 @@ TemplateDesignerCdPage - Form - - - - 4. Clipping height: + 6. Margin: - 2. Inner radius: + 1. Outer radius: - 1. Outer radius: + 4. Clipping height: - 5. Waste: + 2. Inner radius: @@ -791,16 +786,12 @@ - 6. Margin: + 5. Waste: TemplateDesignerContinuousPage - - Form - - <html><head/><body><p>Click &quot;Cancel&quot; to quit, or click &quot;Back&quot; to begin with a different product.</p></body></html> @@ -809,19 +800,15 @@ TemplateDesignerEllipsePage - Form - - - - 3. Waste: + 2. Height: - 2. Height: + 1. Width: - 1. Width: + 3. Waste: @@ -831,10 +818,6 @@ TemplateDesignerIntroPage - - Form - - <html><head/><body><p>This dialog will help you create a custom product template. Let's get started:</p></body></html> @@ -858,10 +841,6 @@ TemplateDesignerNLayoutsPage - - Form - - A layout is a set of labels or cards that can be arranged in a simple grid. Most products only need one layout, as in the first example below. The second example illustrates when two layouts are needed. @@ -890,40 +869,32 @@ TemplateDesignerNamePage - Form + (e.g. "Mailing Labels," "Business Cards," ...) Brand: - - (e.g. Avery, Acme, ...) - - Part #: - (e.g. 8163A) + Description: - Description: + (e.g. 8163A) - (e.g. "Mailing Labels," "Business Cards," ...) + (e.g. Avery, Acme, ...) TemplateDesignerOneLayoutPage - - Form - - Number across (nx): @@ -956,11 +927,11 @@ TemplateDesignerPageSizePage - Form + Roll width: - Page size: + Height: @@ -968,20 +939,12 @@ - Height: - - - - Roll width: + Page size: TemplateDesignerPathPage - - Form - - <html><head/><body><p>Click &quot;Cancel&quot; to quit, or click &quot;Back&quot; to begin with a different product.</p></body></html> @@ -990,27 +953,23 @@ TemplateDesignerRectPage - Form - - - - 1. Width: + 4. Horizontal waste: - 2. Height: + 3. Corner radius - 3. Corner radius + 1. Width: - 4. Horizontal waste: + 5. Vertical waste: - 5. Vertical waste: + 2. Height: @@ -1029,11 +988,7 @@ TemplateDesignerRoundPage - Form - - - - 2. Waste: + 3. Margin @@ -1041,16 +996,12 @@ - 3. Margin + 2. Waste: TemplateDesignerShapePage - - Form - - Rectangular or square (can have rounded corners) @@ -1070,10 +1021,6 @@ TemplateDesignerTwoLayoutPage - - Form - - Distance from left edge (x0): @@ -1126,6 +1073,68 @@ + + Variable + + String + + + + Integer + + + + Floating Point + + + + Never + + + + Per item + + + + Per copy + + + + Per page + + + + Color + + + + + VariablesView + + <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 + + + glabels::AboutDialog @@ -1145,14 +1154,25 @@ + + glabels::ColorHistory + + color %1 + + + glabels::ColorPaletteDialog - Custom color... + Standard Colors - Merge key... + Recent Colors + + + + Custom color... @@ -1160,7 +1180,30 @@ - Custom color #%1 + Custom Color %1 + %1 = color specification in hex. String must not contain a colon (:). + + + + Use substitution field + + + + + glabels::EditVariableDialog + + Default + + + + + glabels::FieldButton + + Merge fields + + + + Variables @@ -1220,6 +1263,10 @@ Welcome + + Edit + + Properties @@ -1229,11 +1276,11 @@ - Print + Variables - Edit + Print @@ -1292,6 +1339,14 @@ Select project Merge mode + + &Variables + + + + Select project Variables mode + + &Print @@ -1782,14 +1837,6 @@ Default - - Insert Field - - - - Key - - Original size @@ -1934,6 +1981,14 @@ Reset + + Insert substitution field + + + + Use substitution field + + glabels::PrintView @@ -2002,11 +2057,11 @@ - Copy + Roll - Roll + Copy @@ -2184,6 +2239,41 @@ + + glabels::VariablesView + + Variables + + + + Name + + + + Type + + + + Increment + + + + Step Size + + + + Add Variable + + + + Edit Variable + + + + Value + + + glabels::barcode::Backends @@ -2322,6 +2412,10 @@ IEC18004 (QRCode) + + Australia Post Standard + + Australia Post Reply Paid @@ -2374,10 +2468,6 @@ Code 49 - - Australia Post Standard - - Code 128 (Mode C suppression) @@ -2565,6 +2655,13 @@ + + glabels::model::ModelImageObject + + No image + + + glabels::model::ModelTextObject