diff --git a/CMakeLists.txt b/CMakeLists.txt index ce923e1..14b5bee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ find_package (Qt5Test 5.4 QUIET) # (not recommended -- only for testing -- also not portable) # #add_compile_options("-Wall" "-Werror" "-Wpedantic") +add_compile_options("-g") #======================================= diff --git a/README.md b/README.md index 29d796c..e0c25cf 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,8 @@ gLabels-qt is the development version of the next major version of gLabels (4.0) gLabels-qt has been under off-and-on development for several years.. It is still missing several features to bring it in parity with glabels-3.4. These include -- Batch mode -- Compatability with older glabels files -- Custom product templates designer -- Online manual +- Compatability with older glabels project files +- An online manual ## Download diff --git a/docs/PRODUCT-TEMPLATES.md b/docs/PRODUCT-TEMPLATES.md index fadf9d0..1be42f4 100644 --- a/docs/PRODUCT-TEMPLATES.md +++ b/docs/PRODUCT-TEMPLATES.md @@ -6,11 +6,11 @@ This document is a reference for manually creating *gLabels* product templates. *gLabels* searches for templates in several locations as described here:

-Location | Description ------------------------------------------|----------------------------------------- -${prefix}/share/libglabels-qt/templates/ | Predefined templates distributed with glabels. -${XDG_CONFIG_HOME}/libglabels/templates | User defined templates created with the gLabels Template Designer. -${HOME}/.glabels | Manually created templates should be placed here. +Location | Description +------------------------------------------|----------------------------------------- +${prefix}/share/glabels-qt/templates/ | Predefined templates distributed with glabels. +${XDG_CONFIG_HOME}/glabels.org/glabels-qt | User defined templates created with the gLabels Product Template Designer. **Do not place manually created templates here!** +${HOME}/.glabels | Manually created templates should be placed here. diff --git a/glabels/CMakeLists.txt b/glabels/CMakeLists.txt index 30aa1cd..262395b 100644 --- a/glabels/CMakeLists.txt +++ b/glabels/CMakeLists.txt @@ -33,6 +33,7 @@ set (glabels_sources SelectProductDialog.cpp SimplePreview.cpp StartupView.cpp + TemplateDesigner.cpp TemplatePicker.cpp TemplatePickerItem.cpp UndoRedoModel.cpp @@ -61,6 +62,7 @@ set (glabels_qobject_headers SelectProductDialog.h SimplePreview.h StartupView.h + TemplateDesigner.h TemplatePicker.h UndoRedoModel.h ) @@ -74,6 +76,18 @@ set (glabels_forms ui/PropertiesView.ui ui/SelectProductDialog.ui ui/StartupView.ui + ui/TemplateDesignerIntroPage.ui + ui/TemplateDesignerNamePage.ui + ui/TemplateDesignerPageSizePage.ui + ui/TemplateDesignerShapePage.ui + ui/TemplateDesignerRectPage.ui + ui/TemplateDesignerRoundPage.ui + ui/TemplateDesignerEllipsePage.ui + ui/TemplateDesignerCdPage.ui + ui/TemplateDesignerNLayoutsPage.ui + ui/TemplateDesignerOneLayoutPage.ui + ui/TemplateDesignerTwoLayoutPage.ui + ui/TemplateDesignerApplyPage.ui ) set (glabels_resource_files diff --git a/glabels/File.cpp b/glabels/File.cpp index d07d0e4..f87a4c8 100644 --- a/glabels/File.cpp +++ b/glabels/File.cpp @@ -22,6 +22,7 @@ #include "MainWindow.h" #include "SelectProductDialog.h" +#include "TemplateDesigner.h" #include "model/FileUtil.h" #include "model/Model.h" @@ -221,6 +222,16 @@ namespace glabels } + /// + /// Template Designer + /// + void File::templateDesigner( MainWindow *window ) + { + TemplateDesigner dialog( window ); + dialog.exec(); + } + + /// /// Close file /// diff --git a/glabels/File.h b/glabels/File.h index 0329a1f..f151b04 100644 --- a/glabels/File.h +++ b/glabels/File.h @@ -46,6 +46,7 @@ namespace glabels static void open( MainWindow *window ); static bool save( MainWindow *window ); static bool saveAs( MainWindow *window ); + static void templateDesigner( MainWindow *window ); static void close( MainWindow *window ); static void exit(); diff --git a/glabels/MainWindow.cpp b/glabels/MainWindow.cpp index 603e453..112ae2d 100644 --- a/glabels/MainWindow.cpp +++ b/glabels/MainWindow.cpp @@ -246,7 +246,7 @@ namespace glabels fileSaveAsAction->setStatusTip( tr("Save current gLabels project to a different name") ); connect( fileSaveAsAction, SIGNAL(triggered()), this, SLOT(fileSaveAs()) ); - fileTemplateDesignerAction = new QAction( tr("Template &Designer..."), this ); + fileTemplateDesignerAction = new QAction( tr("Product Template &Designer..."), this ); fileTemplateDesignerAction->setStatusTip( tr("Create custom templates") ); connect( fileTemplateDesignerAction, SIGNAL(triggered()), this, SLOT(fileTemplateDesigner()) ); @@ -1041,7 +1041,7 @@ namespace glabels /// void MainWindow::fileTemplateDesigner() { - qDebug() << "ACTION: file->Template Designer"; + File::templateDesigner( this ); } diff --git a/glabels/TemplateDesigner.cpp b/glabels/TemplateDesigner.cpp new file mode 100644 index 0000000..3ed5bce --- /dev/null +++ b/glabels/TemplateDesigner.cpp @@ -0,0 +1,1436 @@ +/* TemplateDesigner.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#include "TemplateDesigner.h" + +#include "SelectProductDialog.h" +#include "model/Db.h" +#include "model/Distance.h" +#include "model/FrameCd.h" +#include "model/FrameEllipse.h" +#include "model/FrameRect.h" +#include "model/FrameRound.h" +#include "model/Markup.h" +#include "model/Model.h" +#include "model/PageRenderer.h" +#include "model/Settings.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace glabels +{ + + // + // Private types and constants + // + namespace + { + + enum PageId + { + IntroPageId, + NamePageId, + PageSizePageId, + ShapePageId, + RectPageId, + RoundPageId, + EllipsePageId, + CdPageId, + NLayoutsPageId, + OneLayoutPageId, + TwoLayoutPageId, + ApplyPageId + }; + + + const QString defaultPageSize[] = + { + /* ISO */ "A4", + /* US */ "US Letter" + }; + + + const double maxPageSize[] = + { + /* PT */ 5000, + /* IN */ 70, + /* MM */ 1800, + /* CM */ 180, + /* PC */ 420 + }; + + const model::Distance defaultMargin = model::Distance::in(0.125); + const model::Distance defaultWaste = model::Distance::in(0); + + const model::Distance defaultRectW = model::Distance::in(3.5); + const model::Distance defaultRectH = model::Distance::in(2.0); + const model::Distance defaultRectR = model::Distance::in(0); + + const model::Distance defaultRoundR = model::Distance::in(0.75); + + const model::Distance defaultEllipseW = model::Distance::in(3.5); + const model::Distance defaultEllipseH = model::Distance::in(2.0); + + const model::Distance defaultCdR1 = model::Distance::in(2.3125); + const model::Distance defaultCdR2 = model::Distance::in(0.8125); + const model::Distance defaultCdClip = model::Distance::in(0); + + } + + + /// + /// Constructor + /// + TemplateDesigner::TemplateDesigner( QWidget* parent ) + : mIsBasedOnCopy(false), QWizard(parent) + { + setWindowTitle( tr("Product Template Designer") ); + setPixmap( QWizard::LogoPixmap, QPixmap( ":icons/scalable/apps/glabels.svg" ) ); + setWizardStyle( QWizard::ModernStyle ); + setOption( QWizard::IndependentPages, false ); + setOption( QWizard::NoBackButtonOnStartPage, true ); + + setPage( IntroPageId, new TemplateDesignerIntroPage() ); + setPage( NamePageId, new TemplateDesignerNamePage() ); + setPage( PageSizePageId, new TemplateDesignerPageSizePage() ); + setPage( ShapePageId, new TemplateDesignerShapePage() ); + setPage( RectPageId, new TemplateDesignerRectPage() ); + setPage( RoundPageId, new TemplateDesignerRoundPage() ); + setPage( EllipsePageId, new TemplateDesignerEllipsePage() ); + setPage( CdPageId, new TemplateDesignerCdPage() ); + setPage( NLayoutsPageId, new TemplateDesignerNLayoutsPage() ); + setPage( OneLayoutPageId, new TemplateDesignerOneLayoutPage() ); + setPage( TwoLayoutPageId, new TemplateDesignerTwoLayoutPage() ); + setPage( ApplyPageId, new TemplateDesignerApplyPage() ); + } + + + /// + /// Control wizard's non-linear page order + /// + int TemplateDesigner::nextId() const + { + switch (currentId()) + { + + case IntroPageId: + return NamePageId; + + case NamePageId: + return PageSizePageId; + + case PageSizePageId: + return ShapePageId; + + case ShapePageId: + if ( field( "shape.rect" ).toBool() ) + { + return RectPageId; + } + else if ( field( "shape.round" ).toBool() ) + { + return RoundPageId; + } + else if ( field( "shape.ellipse" ).toBool() ) + { + return EllipsePageId; + } + else + { + return CdPageId; + } + + case RectPageId: + return NLayoutsPageId; + + case RoundPageId: + return NLayoutsPageId; + + case EllipsePageId: + return NLayoutsPageId; + + case CdPageId: + return NLayoutsPageId; + + case NLayoutsPageId: + if ( field( "nLayouts.one" ).toBool() ) + { + return OneLayoutPageId; + } + else + { + return TwoLayoutPageId; + } + + case OneLayoutPageId: + return ApplyPageId; + + case TwoLayoutPageId: + return ApplyPageId; + + case ApplyPageId: + default: + return -1; + } + } + + + /// + /// Determine width of individual item + /// + double TemplateDesigner::itemWidth() + { + // Note: all distance units are the same in wizard, so no conversions needed + if ( field( "shape.rect" ).toBool() ) + { + return field( "rect.w" ).toDouble(); + } + else if ( field( "shape.round" ).toBool() ) + { + return 2 * field( "round.r" ).toDouble(); + } + else if ( field( "shape.ellipse" ).toBool() ) + { + return field( "ellipse.w" ).toDouble(); + } + else + { + if ( field( "cd.xClip" ).toDouble() == 0 ) + { + return 2 * field( "cd.r1" ).toDouble(); + } + else + { + return field( "cd.xClip" ).toDouble(); + } + } + } + + + /// + /// Determine height of individual item + /// + double TemplateDesigner::itemHeight() + { + // Note: all distance units are the same in wizard, so no conversions needed + if ( field( "shape.rect" ).toBool() ) + { + return field( "rect.h" ).toDouble(); + } + else if ( field( "shape.round" ).toBool() ) + { + return 2 * field( "round.r" ).toDouble(); + } + else if ( field( "shape.ellipse" ).toBool() ) + { + return field( "ellipse.h" ).toDouble(); + } + else + { + if ( field( "cd.xClip" ).toDouble() == 0 ) + { + return 2 * field( "cd.r1" ).toDouble(); + } + else + { + return field( "cd.yClip" ).toDouble(); + } + } + } + + + /// + /// Determine X Waste of individual item + /// + double TemplateDesigner::itemXWaste() + { + // Note: all distance units are the same in wizard, so no conversions needed + if ( field( "shape.rect" ).toBool() ) + { + return field( "rect.xWaste" ).toDouble(); + } + else if ( field( "shape.round" ).toBool() ) + { + return 2 * field( "round.waste" ).toDouble(); + } + else if ( field( "shape.ellipse" ).toBool() ) + { + return field( "ellipse.waste" ).toDouble(); + } + else + { + return field( "cd.waste" ).toDouble(); + } + } + + + /// + /// Determine Y Waste of individual item + /// + double TemplateDesigner::itemYWaste() + { + // Note: all distance units are the same in wizard, so no conversions needed + if ( field( "shape.rect" ).toBool() ) + { + return field( "rect.yWaste" ).toDouble(); + } + else if ( field( "shape.round" ).toBool() ) + { + return 2 * field( "round.waste" ).toDouble(); + } + else if ( field( "shape.ellipse" ).toBool() ) + { + return field( "ellipse.waste" ).toDouble(); + } + else + { + return field( "cd.waste" ).toDouble(); + } + } + + + /// + /// Build template from wizard pages + /// + model::Template* TemplateDesigner::buildTemplate() + { + model::Units units = model::Settings::units(); + + QString brand = field( "name.brand" ).toString(); + QString part = field( "name.part" ).toString(); + QString description = field( "name.description" ).toString(); + QString paperId = model::Db::lookupPaperIdFromName( field( "pageSize.pageSize" ).toString() ); + model::Distance pageW( field( "pageSize.w" ).toDouble(), units ); + model::Distance pageH( field( "pageSize.h" ).toDouble(), units ); + + auto t = new model::Template( brand, part, description, paperId, pageW, pageH, true ); + + model::Frame* frame; + if ( field( "shape.rect" ).toBool() ) + { + model::Distance w( field( "rect.w" ).toDouble(), units ); + model::Distance h( field( "rect.h" ).toDouble(), units ); + model::Distance r( field( "rect.r" ).toDouble(), units ); + model::Distance xWaste( field( "rect.xWaste" ).toDouble(), units ); + model::Distance yWaste( field( "rect.yWaste" ).toDouble(), units ); + model::Distance margin( field( "rect.margin" ).toDouble(), units ); + + frame = new model::FrameRect( w, h, r, xWaste, yWaste ); + frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + } + else if ( field( "shape.round" ).toBool() ) + { + model::Distance r( field( "round.r" ).toDouble(), units ); + model::Distance waste( field( "round.waste" ).toDouble(), units ); + model::Distance margin( field( "round.margin" ).toDouble(), units ); + + frame = new model::FrameRound( r, waste ); + frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + } + else if ( field( "shape.ellipse" ).toBool() ) + { + model::Distance w( field( "ellipse.w" ).toDouble(), units ); + model::Distance h( field( "ellipse.h" ).toDouble(), units ); + model::Distance waste( field( "ellipse.waste" ).toDouble(), units ); + model::Distance margin( field( "ellipse.margin" ).toDouble(), units ); + + frame = new model::FrameEllipse( w, h, waste ); + frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + } + else + { + model::Distance r1( field( "cd.r1" ).toDouble(), units ); + model::Distance r2( field( "cd.r2" ).toDouble(), units ); + model::Distance xClip( field( "cd.xClip" ).toDouble(), units ); + model::Distance yClip( field( "cd.yClip" ).toDouble(), units ); + model::Distance waste( field( "cd.waste" ).toDouble(), units ); + model::Distance margin( field( "cd.margin" ).toDouble(), units ); + + frame = new model::FrameCd( r1, r2, xClip, yClip, waste ); + frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + } + t->addFrame( frame ); + + if ( field( "nLayouts.one" ).toBool() ) + { + int nx = field( "oneLayout.nx" ).toInt(); + int ny = field( "oneLayout.ny" ).toInt(); + model::Distance x0( field( "oneLayout.x0" ).toDouble(), units ); + model::Distance y0( field( "oneLayout.y0" ).toDouble(), units ); + model::Distance dx( field( "oneLayout.dx" ).toDouble(), units ); + model::Distance dy( field( "oneLayout.dy" ).toDouble(), units ); + + frame->addLayout( new model::Layout( nx, ny, x0, y0, dx, dy ) ); + } + else + { + int nx1 = field( "twoLayout.nx1" ).toInt(); + int ny1 = field( "twoLayout.ny1" ).toInt(); + model::Distance x01( field( "twoLayout.x01" ).toDouble(), units ); + model::Distance y01( field( "twoLayout.y01" ).toDouble(), units ); + model::Distance dx1( field( "twoLayout.dx1" ).toDouble(), units ); + model::Distance dy1( field( "twoLayout.dy1" ).toDouble(), units ); + + int nx2 = field( "twoLayout.nx2" ).toInt(); + int ny2 = field( "twoLayout.ny2" ).toInt(); + model::Distance x02( field( "twoLayout.x02" ).toDouble(), units ); + model::Distance y02( field( "twoLayout.y02" ).toDouble(), units ); + model::Distance dx2( field( "twoLayout.dx2" ).toDouble(), units ); + model::Distance dy2( field( "twoLayout.dy2" ).toDouble(), units ); + + frame->addLayout( new model::Layout( nx1, ny1, x01, y01, dx1, dy1 ) ); + frame->addLayout( new model::Layout( nx2, ny2, x02, y02, dx2, dy2 ) ); + } + + return t; + } + + + /// + /// Print test sheet + /// + void TemplateDesigner::printTestSheet() + { + auto sheet = new model::Model(); + sheet->setTmplate( buildTemplate() ); + + model::PageRenderer renderer( sheet ); + renderer.setNCopies( sheet->frame()->nLabels() ); + renderer.setStartLabel( 0 ); + renderer.setPrintOutlines( true ); + + QPrinter printer( QPrinter::HighResolution ); + + QPrintDialog printDialog( &printer, this ); + printDialog.setOption( QAbstractPrintDialog::PrintToFile, true ); + printDialog.setOption( QAbstractPrintDialog::PrintSelection, false ); + printDialog.setOption( QAbstractPrintDialog::PrintPageRange, false ); + printDialog.setOption( QAbstractPrintDialog::PrintShowPageSize, true ); + printDialog.setOption( QAbstractPrintDialog::PrintCollateCopies, false ); + printDialog.setOption( QAbstractPrintDialog::PrintCurrentPage, false ); + + if ( printDialog.exec() == QDialog::Accepted ) + { + renderer.print( &printer ); + } + + delete sheet; + } + + + /// + /// Load wizard from template + /// + void TemplateDesigner::loadFromTemplate( const model::Template* tmplate ) + { + mIsBasedOnCopy = true; + + model::Units units = model::Settings::units(); + + setField( "name.brand", tmplate->brand() ); + setField( "name.part", tmplate->part() + QString(" (%1)").arg( tr("Copy") ) ); + setField( "name.description", tmplate->description() ); + + setField( "pageSize.pageSize", model::Db::lookupPaperNameFromId( tmplate->paperId() ) ); + setField( "pageSize.w", tmplate->pageWidth().inUnits( units ) ); + setField( "pageSize.h", tmplate->pageHeight().inUnits( units ) ); + + const model::Frame* frame = tmplate->frames().first(); + if ( auto frameRect = dynamic_cast( frame ) ) + { + setField( "shape.rect", true ); + + setField( "rect.w", frameRect->w().inUnits( units ) ); + setField( "rect.h", frameRect->h().inUnits( units ) ); + setField( "rect.r", frameRect->r().inUnits( units ) ); + setField( "rect.xWaste", frameRect->xWaste().inUnits( units ) ); + setField( "rect.yWaste", frameRect->yWaste().inUnits( units ) ); + } + else if ( auto frameRound = dynamic_cast( frame ) ) + { + setField( "shape.round", true ); + + setField( "round.r", frameRound->r().inUnits( units ) ); + setField( "round.waste", frameRound->waste().inUnits( units ) ); + } + else if ( auto frameEllipse = dynamic_cast( frame ) ) + { + setField( "shape.ellipse", true ); + + setField( "ellipse.w", frameEllipse->w().inUnits( units ) ); + setField( "ellipse.h", frameEllipse->h().inUnits( units ) ); + setField( "ellipse.waste", frameEllipse->waste().inUnits( units ) ); + } + else if ( auto frameCd = dynamic_cast( frame ) ) + { + setField( "shape.cd", true ); + + setField( "cd.r1", frameCd->r1().inUnits( units ) ); + setField( "cd.r2", frameCd->r2().inUnits( units ) ); + setField( "cd.xClip", frameCd->w().inUnits( units ) ); + setField( "cd.yClip", frameCd->h().inUnits( units ) ); + setField( "cd.waste", frameCd->waste().inUnits( units ) ); + } + + foreach( auto markup, frame->markups() ) + { + if ( auto markupMargin = dynamic_cast( markup ) ) + { + setField( "rect.margin", markupMargin->size().inUnits( units ) ); + setField( "round.margin", markupMargin->size().inUnits( units ) ); + setField( "ellipse.margin", markupMargin->size().inUnits( units ) ); + setField( "cd.margin", markupMargin->size().inUnits( units ) ); + } + } + + QList layouts = frame->layouts(); + if ( layouts.size() == 1 ) + { + setField( "oneLayout.nx", layouts[0]->nx() ); + setField( "oneLayout.ny", layouts[0]->ny() ); + setField( "oneLayout.x0", layouts[0]->x0().inUnits( units ) ); + setField( "oneLayout.y0", layouts[0]->y0().inUnits( units ) ); + setField( "oneLayout.dx", layouts[0]->dx().inUnits( units ) ); + setField( "oneLayout.dy", layouts[0]->dy().inUnits( units ) ); + } + else if ( layouts.size() > 1 ) + { + setField( "twoLayout.nx1", layouts[0]->nx() ); + setField( "twoLayout.ny1", layouts[0]->ny() ); + setField( "twoLayout.x01", layouts[0]->x0().inUnits( units ) ); + setField( "twoLayout.y01", layouts[0]->y0().inUnits( units ) ); + setField( "twoLayout.dx1", layouts[0]->dx().inUnits( units ) ); + setField( "twoLayout.dy1", layouts[0]->dy().inUnits( units ) ); + + setField( "twoLayout.nx2", layouts[1]->nx() ); + setField( "twoLayout.ny2", layouts[1]->ny() ); + setField( "twoLayout.x02", layouts[1]->x0().inUnits( units ) ); + setField( "twoLayout.y02", layouts[1]->y0().inUnits( units ) ); + setField( "twoLayout.dx2", layouts[1]->dx().inUnits( units ) ); + setField( "twoLayout.dy2", layouts[1]->dy().inUnits( units ) ); + } + } + + + /// + /// Is the wizard based on a copy? + /// + bool TemplateDesigner::isBasedOnCopy() + { + return mIsBasedOnCopy; + } + + + /// + /// Intro Page + /// + TemplateDesignerIntroPage::TemplateDesignerIntroPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Welcome") ); + setSubTitle( tr("Welcome to the gLabels Product Template Designer.") ); + setPixmap( QWizard::WatermarkPixmap, QPixmap( ":images/TemplateDesigner/wizard-banner.png" ) ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + connect( copyButton, &QCommandLinkButton::clicked, this, &TemplateDesignerIntroPage::onCopyButtonClicked ); + connect( newButton, &QCommandLinkButton::clicked, this, &TemplateDesignerIntroPage::onNewButtonClicked ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + bool TemplateDesignerIntroPage::isComplete() const + { + // Use CommandLinkButtons on intro page to advance + return false; + } + + + void TemplateDesignerIntroPage::onCopyButtonClicked() + { + SelectProductDialog dialog; + dialog.exec(); + + const model::Template* tmplate = dialog.tmplate(); + if ( tmplate ) + { + if ( auto td = dynamic_cast( wizard() ) ) + { + td->loadFromTemplate( tmplate ); + td->next(); + } + } + } + + + void TemplateDesignerIntroPage::onNewButtonClicked() + { + wizard()->next(); + } + + + /// + /// Name and Description Page + /// + TemplateDesignerNamePage::TemplateDesignerNamePage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Name and Description") ); + setSubTitle( tr("Please enter the following identifying information about the product.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + warningLabel->setText( "" ); + + registerField( "name.brand", brandEntry ); + registerField( "name.part", partEntry ); + registerField( "name.description", descriptionEntry ); + + connect( brandEntry, &QLineEdit::textChanged, this, &TemplateDesignerNamePage::onChanged ); + connect( partEntry, &QLineEdit::textChanged, this, &TemplateDesignerNamePage::onChanged ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + bool TemplateDesignerNamePage::isComplete() const + { + return mCanContinue; + } + + + void TemplateDesignerNamePage::onChanged() + { + bool isDuplicate = model::Db::isSystemTemplateKnown( brandEntry->text(), partEntry->text() ); + if ( isDuplicate ) + { + QString warningText = tr("Brand and part number match an existing built-in product template!"); + warningLabel->setText( "" + warningText + "" ); + } + else + { + warningLabel->setText( "" ); + } + + mCanContinue = !brandEntry->text().isEmpty() && !partEntry->text().isEmpty() && !isDuplicate; + emit completeChanged(); + } + + + /// + /// Page Size Page + /// + TemplateDesignerPageSizePage::TemplateDesignerPageSizePage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Page Size") ); + setSubTitle( tr("Please select the product page size.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + pageSizeCombo->insertItem( 0, tr("Other") ); + pageSizeCombo->insertItems( 1, model::Db::paperNames() ); + pageSizeCombo->setCurrentText( defaultPageSize[ model::Settings::preferedPageSizeFamily() ] ); + + wSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wSpin->setSingleStep( model::Settings::units().resolution() ); + wSpin->setMaximum( maxPageSize[ model::Settings::units().toEnum() ] ); + wSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + + hSpin->setSuffix( " " + model::Settings::units().toTrName() ); + hSpin->setDecimals( model::Settings::units().resolutionDigits() ); + hSpin->setSingleStep( model::Settings::units().resolution() ); + hSpin->setMaximum( maxPageSize[ model::Settings::units().toEnum() ] ); + hSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + + if ( pageSizeCombo->currentText() != tr("Other") ) + { + const model::Paper* paper = model::Db::lookupPaperFromName( pageSizeCombo->currentText() ); + wSpin->setValue( paper->width().inUnits( model::Settings::units() ) ); + hSpin->setValue( paper->height().inUnits( model::Settings::units() ) ); + } + + registerField( "pageSize.pageSize", pageSizeCombo, "currentText" ); + registerField( "pageSize.w", wSpin, "value" ); + registerField( "pageSize.h", hSpin, "value" ); + + connect( pageSizeCombo, &QComboBox::currentTextChanged, this, &TemplateDesignerPageSizePage::onComboChanged ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerPageSizePage::initializePage() + { + } + + + void TemplateDesignerPageSizePage::cleanupPage() + { + // Leave current settings alone + } + + + void TemplateDesignerPageSizePage::onComboChanged() + { + if ( pageSizeCombo->currentText() != tr("Other") ) + { + const model::Paper* paper = model::Db::lookupPaperFromName( pageSizeCombo->currentText() ); + wSpin->setValue( paper->width().inUnits( model::Settings::units() ) ); + hSpin->setValue( paper->height().inUnits( model::Settings::units() ) ); + } + + wSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + hSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + } + + + /// + /// Shape Page + /// + TemplateDesignerShapePage::TemplateDesignerShapePage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Product Shape") ); + setSubTitle( tr("Please select the basic product shape.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + registerField( "shape.rect", rectRadio ); + registerField( "shape.round", roundRadio ); + registerField( "shape.ellipse", ellipseRadio ); + registerField( "shape.cd", cdRadio ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerShapePage::initializePage() + { + } + + + void TemplateDesignerShapePage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// Rectangular Product Page + /// + TemplateDesignerRectPage::TemplateDesignerRectPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Product Size") ); + setSubTitle( tr("Please adjust the size parameters of a single product item.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + wSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wSpin->setSingleStep( model::Settings::units().resolution() ); + + hSpin->setSuffix( " " + model::Settings::units().toTrName() ); + hSpin->setDecimals( model::Settings::units().resolutionDigits() ); + hSpin->setSingleStep( model::Settings::units().resolution() ); + + rSpin->setSuffix( " " + model::Settings::units().toTrName() ); + rSpin->setDecimals( model::Settings::units().resolutionDigits() ); + rSpin->setSingleStep( model::Settings::units().resolution() ); + + xWasteSpin->setSuffix( " " + model::Settings::units().toTrName() ); + xWasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); + xWasteSpin->setSingleStep( model::Settings::units().resolution() ); + + yWasteSpin->setSuffix( " " + model::Settings::units().toTrName() ); + yWasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); + yWasteSpin->setSingleStep( model::Settings::units().resolution() ); + + marginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + marginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + marginSpin->setSingleStep( model::Settings::units().resolution() ); + + registerField( "rect.w", wSpin, "value" ); + registerField( "rect.h", hSpin, "value" ); + registerField( "rect.r", rSpin, "value" ); + registerField( "rect.xWaste", xWasteSpin, "value" ); + registerField( "rect.yWaste", yWasteSpin, "value" ); + registerField( "rect.margin", marginSpin, "value" ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerRectPage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen page size + double wMax = field("pageSize.w").toDouble(); + double hMax = field("pageSize.h").toDouble(); + + wSpin->setMaximum( wMax ); + hSpin->setMaximum( hMax ); + rSpin->setMaximum( std::min(wMax,hMax)/2.0 ); + xWasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + yWasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + marginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults + wSpin->setValue( defaultRectW.inUnits( model::Settings::units() ) ); + hSpin->setValue( defaultRectH.inUnits( model::Settings::units() ) ); + rSpin->setValue( defaultRectR.inUnits( model::Settings::units() ) ); + xWasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); + yWasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); + marginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + } + } + } + + + void TemplateDesignerRectPage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// Round Product Page + /// + TemplateDesignerRoundPage::TemplateDesignerRoundPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Product Size") ); + setSubTitle( tr("Please adjust the size parameters of a single product item.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + rSpin->setSuffix( " " + model::Settings::units().toTrName() ); + rSpin->setDecimals( model::Settings::units().resolutionDigits() ); + rSpin->setSingleStep( model::Settings::units().resolution() ); + + wasteSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wasteSpin->setSingleStep( model::Settings::units().resolution() ); + + marginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + marginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + marginSpin->setSingleStep( model::Settings::units().resolution() ); + + registerField( "round.r", rSpin, "value" ); + registerField( "round.waste", wasteSpin, "value" ); + registerField( "round.margin", marginSpin, "value" ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerRoundPage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen page size + double wMax = field("pageSize.w").toDouble(); + double hMax = field("pageSize.h").toDouble(); + + rSpin->setMaximum( std::min(wMax,hMax)/2.0 ); + wasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + marginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults + rSpin->setValue( defaultRoundR.inUnits( model::Settings::units() ) ); + wasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); + marginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + } + } + } + + + void TemplateDesignerRoundPage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// Elliptical Product Page + /// + TemplateDesignerEllipsePage::TemplateDesignerEllipsePage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Product Size") ); + setSubTitle( tr("Please adjust the size parameters of a single product item.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + wSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wSpin->setSingleStep( model::Settings::units().resolution() ); + + hSpin->setSuffix( " " + model::Settings::units().toTrName() ); + hSpin->setDecimals( model::Settings::units().resolutionDigits() ); + hSpin->setSingleStep( model::Settings::units().resolution() ); + + wasteSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wasteSpin->setSingleStep( model::Settings::units().resolution() ); + + marginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + marginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + marginSpin->setSingleStep( model::Settings::units().resolution() ); + + registerField( "ellipse.w", wSpin, "value" ); + registerField( "ellipse.h", hSpin, "value" ); + registerField( "ellipse.waste", wasteSpin, "value" ); + registerField( "ellipse.margin", marginSpin, "value" ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerEllipsePage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen page size + double wMax = field("pageSize.w").toDouble(); + double hMax = field("pageSize.h").toDouble(); + + wSpin->setMaximum( wMax ); + hSpin->setMaximum( hMax ); + wasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + marginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults + wSpin->setValue( defaultEllipseW.inUnits( model::Settings::units() ) ); + hSpin->setValue( defaultEllipseH.inUnits( model::Settings::units() ) ); + wasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); + marginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + } + } + } + + + void TemplateDesignerEllipsePage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// CD/DVD Product Page + /// + TemplateDesignerCdPage::TemplateDesignerCdPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Product Size") ); + setSubTitle( tr("Please adjust the size parameters of a single product item.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + r1Spin->setSuffix( " " + model::Settings::units().toTrName() ); + r1Spin->setDecimals( model::Settings::units().resolutionDigits() ); + r1Spin->setSingleStep( model::Settings::units().resolution() ); + + r2Spin->setSuffix( " " + model::Settings::units().toTrName() ); + r2Spin->setDecimals( model::Settings::units().resolutionDigits() ); + r2Spin->setSingleStep( model::Settings::units().resolution() ); + + xClipSpin->setSuffix( " " + model::Settings::units().toTrName() ); + xClipSpin->setDecimals( model::Settings::units().resolutionDigits() ); + xClipSpin->setSingleStep( model::Settings::units().resolution() ); + + yClipSpin->setSuffix( " " + model::Settings::units().toTrName() ); + yClipSpin->setDecimals( model::Settings::units().resolutionDigits() ); + yClipSpin->setSingleStep( model::Settings::units().resolution() ); + + wasteSpin->setSuffix( " " + model::Settings::units().toTrName() ); + wasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); + wasteSpin->setSingleStep( model::Settings::units().resolution() ); + + marginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + marginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + marginSpin->setSingleStep( model::Settings::units().resolution() ); + + registerField( "cd.r1", r1Spin, "value" ); + registerField( "cd.r2", r2Spin, "value" ); + registerField( "cd.xClip", xClipSpin, "value" ); + registerField( "cd.yClip", yClipSpin, "value" ); + registerField( "cd.waste", wasteSpin, "value" ); + registerField( "cd.margin", marginSpin, "value" ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerCdPage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen page size + double wMax = field("pageSize.w").toDouble(); + double hMax = field("pageSize.h").toDouble(); + + r1Spin->setMaximum( std::min(wMax,hMax)/2.0 ); + r2Spin->setMaximum( std::min(wMax,hMax)/4.0 ); + xClipSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + yClipSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + wasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + marginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults + r1Spin->setValue( defaultCdR1.inUnits( model::Settings::units() ) ); + r2Spin->setValue( defaultCdR2.inUnits( model::Settings::units() ) ); + xClipSpin->setValue( defaultCdClip.inUnits( model::Settings::units() ) ); + yClipSpin->setValue( defaultCdClip.inUnits( model::Settings::units() ) ); + wasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); + marginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + } + } + } + + + void TemplateDesignerCdPage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// Number of Layouts Page + /// + TemplateDesignerNLayoutsPage::TemplateDesignerNLayoutsPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Number of Layouts") ); + setSubTitle( tr("Please select the number of layouts required.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + registerField( "nLayouts.one", oneLayoutRadio ); + registerField( "nLayouts.two", twoLayoutsRadio ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerNLayoutsPage::initializePage() + { + } + + + void TemplateDesignerNLayoutsPage::cleanupPage() + { + // Leave current settings alone + } + + + /// + /// One Layout Page + /// + TemplateDesignerOneLayoutPage::TemplateDesignerOneLayoutPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Layout") ); + setSubTitle( tr("Please enter parameters for your single layout.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + x0Spin->setSuffix( " " + model::Settings::units().toTrName() ); + x0Spin->setDecimals( model::Settings::units().resolutionDigits() ); + x0Spin->setSingleStep( model::Settings::units().resolution() ); + + y0Spin->setSuffix( " " + model::Settings::units().toTrName() ); + y0Spin->setDecimals( model::Settings::units().resolutionDigits() ); + y0Spin->setSingleStep( model::Settings::units().resolution() ); + + dxSpin->setSuffix( " " + model::Settings::units().toTrName() ); + dxSpin->setDecimals( model::Settings::units().resolutionDigits() ); + dxSpin->setSingleStep( model::Settings::units().resolution() ); + + dySpin->setSuffix( " " + model::Settings::units().toTrName() ); + dySpin->setDecimals( model::Settings::units().resolutionDigits() ); + dySpin->setSingleStep( model::Settings::units().resolution() ); + + registerField( "oneLayout.nx", nxSpin ); + registerField( "oneLayout.ny", nySpin ); + registerField( "oneLayout.x0", x0Spin, "value" ); + registerField( "oneLayout.y0", y0Spin, "value" ); + registerField( "oneLayout.dx", dxSpin, "value" ); + registerField( "oneLayout.dy", dySpin, "value" ); + + connect( nxSpin, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( nySpin, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( x0Spin, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( y0Spin, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dxSpin, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dySpin, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + + connect( printButton, SIGNAL(clicked()), this, SLOT(onPrintButtonClicked()) ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerOneLayoutPage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen values + double pageW = field("pageSize.w").toDouble(); + double pageH = field("pageSize.h").toDouble(); + double w = td->itemWidth(); + double h = td->itemHeight(); + double xWaste = td->itemXWaste(); + double yWaste = td->itemYWaste(); + + int nxMax = std::max( pageW/(w + 2*xWaste), 1.0 ); + int nyMax = std::max( pageH/(h + 2*yWaste), 1.0 ); + double x0Min = xWaste; + double x0Max = pageW - w - 2*xWaste; + double y0Min = yWaste; + double y0Max = pageH - h - 2*yWaste; + double dxMin = w + 2*xWaste; + double dxMax = pageW - w - 2*xWaste; + double dyMin = h + 2*yWaste; + double dyMax = pageH - h - 2*yWaste; + + nxSpin->setRange( 1, nxMax ); + nySpin->setRange( 1, nyMax ); + x0Spin->setRange( x0Min, x0Max ); + y0Spin->setRange( y0Min, y0Max ); + dxSpin->setRange( dxMin, dxMax ); + dySpin->setRange( dyMin, dxMax ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults based on symetric sheet using previosly chosen values + nxSpin->setValue( nxMax ); + nySpin->setValue( nyMax ); + x0Spin->setValue( (pageW - (nxMax-1)*dxMin - w) / 2 ); + y0Spin->setValue( (pageH - (nyMax-1)*dyMin - h) / 2 ); + dxSpin->setValue( dxMin ); + dySpin->setValue( dyMin ); + } + + preview->setTemplate( td->buildTemplate() ); + } + } + + + void TemplateDesignerOneLayoutPage::cleanupPage() + { + // Leave current settings alone + } + + + void TemplateDesignerOneLayoutPage::onChanged() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + preview->setTemplate( td->buildTemplate() ); + } + } + + + void TemplateDesignerOneLayoutPage::onPrintButtonClicked() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + td->printTestSheet(); + } + } + + + /// + /// Two Layout Page + /// + TemplateDesignerTwoLayoutPage::TemplateDesignerTwoLayoutPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Layouts") ); + setSubTitle( tr("Please enter parameters for your two layouts.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + x0Spin1->setSuffix( " " + model::Settings::units().toTrName() ); + x0Spin1->setDecimals( model::Settings::units().resolutionDigits() ); + x0Spin1->setSingleStep( model::Settings::units().resolution() ); + + y0Spin1->setSuffix( " " + model::Settings::units().toTrName() ); + y0Spin1->setDecimals( model::Settings::units().resolutionDigits() ); + y0Spin1->setSingleStep( model::Settings::units().resolution() ); + + dxSpin1->setSuffix( " " + model::Settings::units().toTrName() ); + dxSpin1->setDecimals( model::Settings::units().resolutionDigits() ); + dxSpin1->setSingleStep( model::Settings::units().resolution() ); + + dySpin1->setSuffix( " " + model::Settings::units().toTrName() ); + dySpin1->setDecimals( model::Settings::units().resolutionDigits() ); + dySpin1->setSingleStep( model::Settings::units().resolution() ); + + x0Spin2->setSuffix( " " + model::Settings::units().toTrName() ); + x0Spin2->setDecimals( model::Settings::units().resolutionDigits() ); + x0Spin2->setSingleStep( model::Settings::units().resolution() ); + + y0Spin2->setSuffix( " " + model::Settings::units().toTrName() ); + y0Spin2->setDecimals( model::Settings::units().resolutionDigits() ); + y0Spin2->setSingleStep( model::Settings::units().resolution() ); + + dxSpin2->setSuffix( " " + model::Settings::units().toTrName() ); + dxSpin2->setDecimals( model::Settings::units().resolutionDigits() ); + dxSpin2->setSingleStep( model::Settings::units().resolution() ); + + dySpin2->setSuffix( " " + model::Settings::units().toTrName() ); + dySpin2->setDecimals( model::Settings::units().resolutionDigits() ); + dySpin2->setSingleStep( model::Settings::units().resolution() ); + + registerField( "twoLayout.nx1", nxSpin1 ); + registerField( "twoLayout.ny1", nySpin1 ); + registerField( "twoLayout.x01", x0Spin1, "value" ); + registerField( "twoLayout.y01", y0Spin1, "value" ); + registerField( "twoLayout.dx1", dxSpin1, "value" ); + registerField( "twoLayout.dy1", dySpin1, "value" ); + + registerField( "twoLayout.nx2", nxSpin2 ); + registerField( "twoLayout.ny2", nySpin2 ); + registerField( "twoLayout.x02", x0Spin2, "value" ); + registerField( "twoLayout.y02", y0Spin2, "value" ); + registerField( "twoLayout.dx2", dxSpin2, "value" ); + registerField( "twoLayout.dy2", dySpin2, "value" ); + + connect( nxSpin1, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( nySpin1, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( x0Spin1, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( y0Spin1, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dxSpin1, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dySpin1, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + + connect( nxSpin2, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( nySpin2, SIGNAL(valueChanged(int)), this, SLOT(onChanged()) ); + connect( x0Spin2, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( y0Spin2, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dxSpin2, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + connect( dySpin2, SIGNAL(valueChanged(double)), this, SLOT(onChanged()) ); + + connect( printButton, SIGNAL(clicked()), this, SLOT(onPrintButtonClicked()) ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + void TemplateDesignerTwoLayoutPage::initializePage() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + // set realistic limits based on previously chosen values + double pageW = field("pageSize.w").toDouble(); + double pageH = field("pageSize.h").toDouble(); + double w = td->itemWidth(); + double h = td->itemHeight(); + double xWaste = td->itemXWaste(); + double yWaste = td->itemYWaste(); + + int nxMax = std::max( pageW/(w + 2*xWaste), 1.0 ); + int nyMax = std::max( pageH/(h + 2*yWaste), 1.0 ); + double x0Min = xWaste; + double x0Max = pageW - w - 2*xWaste; + double y0Min = yWaste; + double y0Max = pageH - h - 2*yWaste; + double dxMin = w + 2*xWaste; + double dxMax = pageW - w - 2*xWaste; + double dyMin = h + 2*yWaste; + double dyMax = pageH - h - 2*yWaste; + + nxSpin1->setRange( 1, nxMax ); + nySpin1->setRange( 1, nyMax ); + x0Spin1->setRange( x0Min, x0Max ); + y0Spin1->setRange( y0Min, y0Max ); + dxSpin1->setRange( dxMin, dxMax ); + dySpin1->setRange( dyMin, dyMax ); + + nxSpin2->setRange( 1, nxMax ); + nySpin2->setRange( 1, nyMax ); + x0Spin2->setRange( x0Min, x0Max ); + y0Spin2->setRange( y0Min, y0Max ); + dxSpin2->setRange( dxMin, dxMax ); + dySpin2->setRange( dyMin, dyMax ); + + static bool alreadyInitialized = false; + if ( !td->isBasedOnCopy() && !alreadyInitialized ) + { + alreadyInitialized = true; + + // Set some realistic defaults based on symetric sheet using previosly chosen values + nxSpin1->setValue( nxMax ); + nySpin1->setValue( nyMax - nyMax/2 ); + x0Spin1->setValue( (pageW - (nxMax-1)*dxMin - w) / 2 ); + y0Spin1->setValue( (pageH - (nyMax-1)*dyMin - h) / 2 ); + dxSpin1->setValue( dxMin ); + dySpin1->setValue( 2*dyMin ); + + nxSpin2->setValue( nxMax ); + nySpin2->setValue( nyMax/2 ); + x0Spin2->setValue( (pageW - (nxMax-1)*dxMin - w) / 2 ); + y0Spin2->setValue( (pageH - (nyMax-1)*dyMin - h) / 2 + dyMin ); + dxSpin2->setValue( dxMin ); + dySpin2->setValue( 2*dyMin ); + } + + preview->setTemplate( td->buildTemplate() ); + } + } + + + void TemplateDesignerTwoLayoutPage::cleanupPage() + { + // Leave current settings alone + } + + + void TemplateDesignerTwoLayoutPage::onChanged() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + preview->setTemplate( td->buildTemplate() ); + } + } + + + void TemplateDesignerTwoLayoutPage::onPrintButtonClicked() + { + if ( auto td = dynamic_cast( wizard() ) ) + { + td->printTestSheet(); + } + } + + + /// + /// Apply Page + /// + TemplateDesignerApplyPage::TemplateDesignerApplyPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Save Product Template") ); + setSubTitle( tr("Click \"Save\" to save your custom product template!") ); + + setFinalPage( true ); + setButtonText( QWizard::FinishButton, "Save" ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + bool TemplateDesignerApplyPage::validatePage() + { + // + // Save button pressed + // + QString brand = field( "name.brand" ).toString(); + QString part = field( "name.part" ).toString(); + QString filename = model::Db::userTemplateFilename( brand, part ); + + if ( QFileInfo::exists(filename) ) + { + QMessageBox msgBox( wizard() ); + msgBox.setWindowTitle( tr("Save Product Template") ); + msgBox.setIcon( QMessageBox::Warning ); + msgBox.setText( tr("User product template (%1 %2) already exists.").arg(brand).arg(part) ); + msgBox.setInformativeText( tr("Do you want to replace it?") ); + msgBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No ); + msgBox.setDefaultButton( QMessageBox::No ); + + if ( msgBox.exec() == QMessageBox::No ) + { + return false; + } + + model::Db::deleteUserTemplateByBrandPart( brand, part ); + } + + if ( auto td = dynamic_cast( wizard() ) ) + { + model::Db::registerUserTemplate( td->buildTemplate() ); + } + return true; + } + + +} // namespace glabels diff --git a/glabels/TemplateDesigner.h b/glabels/TemplateDesigner.h new file mode 100644 index 0000000..15534f3 --- /dev/null +++ b/glabels/TemplateDesigner.h @@ -0,0 +1,289 @@ +/* TemplateDesigner.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#ifndef TemplateDesigner_h +#define TemplateDesigner_h + + +#include "ui_TemplateDesignerIntroPage.h" +#include "ui_TemplateDesignerNamePage.h" +#include "ui_TemplateDesignerPageSizePage.h" +#include "ui_TemplateDesignerShapePage.h" +#include "ui_TemplateDesignerRectPage.h" +#include "ui_TemplateDesignerRoundPage.h" +#include "ui_TemplateDesignerEllipsePage.h" +#include "ui_TemplateDesignerCdPage.h" +#include "ui_TemplateDesignerNLayoutsPage.h" +#include "ui_TemplateDesignerOneLayoutPage.h" +#include "ui_TemplateDesignerTwoLayoutPage.h" +#include "ui_TemplateDesignerApplyPage.h" + +#include "model/Template.h" + +#include +#include + + +namespace glabels +{ + /// + /// About Dialog Widget + /// + class TemplateDesigner : public QWizard + { + Q_OBJECT + + // My subpages are my friends :-) + friend class TemplateDesignerIntroPage; + friend class TemplateDesignerNamePage; + friend class TemplateDesignerPageSizePage; + friend class TemplateDesignerShapePage; + friend class TemplateDesignerRectPage; + friend class TemplateDesignerRoundPage; + friend class TemplateDesignerEllipsePage; + friend class TemplateDesignerCdPage; + friend class TemplateDesignerNLayoutsPage; + friend class TemplateDesignerOneLayoutPage; + friend class TemplateDesignerTwoLayoutPage; + friend class TemplateDesignerApplyPage; + + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + TemplateDesigner( QWidget *parent = nullptr ); + + + ///////////////////////////////// + // Private methods + ///////////////////////////////// + private: + int nextId() const override; + + double itemWidth(); + double itemHeight(); + double itemXWaste(); + double itemYWaste(); + model::Template* buildTemplate(); + void printTestSheet(); + void loadFromTemplate( const model::Template* tmplate ); + bool isBasedOnCopy(); + + + ///////////////////////////////// + // Private methods + ///////////////////////////////// + private: + bool mIsBasedOnCopy; + }; + + + // + // Intro Page + // + class TemplateDesignerIntroPage : public QWizardPage, public Ui::TemplateDesignerIntroPage + { + Q_OBJECT + + public: + TemplateDesignerIntroPage( QWidget* parent = nullptr ); + + bool isComplete() const override; + + private slots: + void onCopyButtonClicked(); + void onNewButtonClicked(); + }; + + + // + // Name Page + // + class TemplateDesignerNamePage : public QWizardPage, public Ui::TemplateDesignerNamePage + { + Q_OBJECT + public: + TemplateDesignerNamePage( QWidget* parent = nullptr ); + + bool isComplete() const override; + + private slots: + void onChanged(); + + private: + bool mCanContinue = false; + }; + + + // + // Page Size Page + // + class TemplateDesignerPageSizePage : public QWizardPage, public Ui::TemplateDesignerPageSizePage + { + Q_OBJECT + public: + TemplateDesignerPageSizePage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + + private slots: + void onComboChanged(); + }; + + + // + // Shape Page + // + class TemplateDesignerShapePage : public QWizardPage, public Ui::TemplateDesignerShapePage + { + Q_OBJECT + public: + TemplateDesignerShapePage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // Rect Page + // + class TemplateDesignerRectPage : public QWizardPage, public Ui::TemplateDesignerRectPage + { + Q_OBJECT + public: + TemplateDesignerRectPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // Round Page + // + class TemplateDesignerRoundPage : public QWizardPage, public Ui::TemplateDesignerRoundPage + { + Q_OBJECT + public: + TemplateDesignerRoundPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // Ellipse Page + // + class TemplateDesignerEllipsePage : public QWizardPage, public Ui::TemplateDesignerEllipsePage + { + Q_OBJECT + public: + TemplateDesignerEllipsePage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // Cd Page + // + class TemplateDesignerCdPage : public QWizardPage, public Ui::TemplateDesignerCdPage + { + Q_OBJECT + public: + TemplateDesignerCdPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // NLayouts Page + // + class TemplateDesignerNLayoutsPage : public QWizardPage, public Ui::TemplateDesignerNLayoutsPage + { + Q_OBJECT + public: + TemplateDesignerNLayoutsPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + }; + + + // + // OneLayout Page + // + class TemplateDesignerOneLayoutPage : public QWizardPage, public Ui::TemplateDesignerOneLayoutPage + { + Q_OBJECT + public: + TemplateDesignerOneLayoutPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + + private slots: + void onChanged(); + void onPrintButtonClicked(); + }; + + + // + // TwoLayout Page + // + class TemplateDesignerTwoLayoutPage : public QWizardPage, public Ui::TemplateDesignerTwoLayoutPage + { + Q_OBJECT + public: + TemplateDesignerTwoLayoutPage( QWidget* parent = nullptr ); + + void initializePage() override; + void cleanupPage() override; + + private slots: + void onChanged(); + void onPrintButtonClicked(); + }; + + + // + // Apply Page + // + class TemplateDesignerApplyPage : public QWizardPage, public Ui::TemplateDesignerApplyPage + { + Q_OBJECT + public: + TemplateDesignerApplyPage( QWidget* parent = nullptr ); + + bool validatePage(); + }; + + +} + + +#endif // TemplateDesigner_h diff --git a/glabels/images.qrc b/glabels/images.qrc index 315a144..9b6b53b 100644 --- a/glabels/images.qrc +++ b/glabels/images.qrc @@ -2,8 +2,19 @@ + images/glabels-label-designer.png images/glabels-logo.png + images/checkerboard.png + + images/TemplateDesigner/wizard-banner.png + images/TemplateDesigner/ex-1layout.png + images/TemplateDesigner/ex-2layouts.png + images/TemplateDesigner/ex-cd-size.png + images/TemplateDesigner/ex-ellipse-size.png + images/TemplateDesigner/ex-rect-size.png + images/TemplateDesigner/ex-round-size.png + diff --git a/glabels/images/TemplateDesigner/ex-1layout.png b/glabels/images/TemplateDesigner/ex-1layout.png new file mode 100644 index 0000000..3045c5f Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-1layout.png differ diff --git a/glabels/images/TemplateDesigner/ex-2layouts.png b/glabels/images/TemplateDesigner/ex-2layouts.png new file mode 100644 index 0000000..1bb5931 Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-2layouts.png differ diff --git a/glabels/images/TemplateDesigner/ex-cd-size.png b/glabels/images/TemplateDesigner/ex-cd-size.png new file mode 100644 index 0000000..68261f0 Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-cd-size.png differ diff --git a/glabels/images/TemplateDesigner/ex-ellipse-size.png b/glabels/images/TemplateDesigner/ex-ellipse-size.png new file mode 100644 index 0000000..456ec2e Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-ellipse-size.png differ diff --git a/glabels/images/TemplateDesigner/ex-rect-size.png b/glabels/images/TemplateDesigner/ex-rect-size.png new file mode 100644 index 0000000..2710472 Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-rect-size.png differ diff --git a/glabels/images/TemplateDesigner/ex-round-size.png b/glabels/images/TemplateDesigner/ex-round-size.png new file mode 100644 index 0000000..c45d6c3 Binary files /dev/null and b/glabels/images/TemplateDesigner/ex-round-size.png differ diff --git a/glabels/images/TemplateDesigner/wizard-banner.png b/glabels/images/TemplateDesigner/wizard-banner.png new file mode 100644 index 0000000..b4fc605 Binary files /dev/null and b/glabels/images/TemplateDesigner/wizard-banner.png differ diff --git a/glabels/ui/TemplateDesignerApplyPage.ui b/glabels/ui/TemplateDesignerApplyPage.ui new file mode 100644 index 0000000..d0ed63b --- /dev/null +++ b/glabels/ui/TemplateDesignerApplyPage.ui @@ -0,0 +1,69 @@ + + + TemplateDesignerApplyPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + 18 + + + + + You have completed the gLabels Product Template Designer. If you wish to accept and save your product template, click "Save." + + + true + + + + + + + Otherwise, you may click "Cancel" to abandon your design or "Back" to review or continue editing this product template. + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerCdPage.ui b/glabels/ui/TemplateDesignerCdPage.ui new file mode 100644 index 0000000..f61fa31 --- /dev/null +++ b/glabels/ui/TemplateDesignerCdPage.ui @@ -0,0 +1,190 @@ + + + TemplateDesignerCdPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + + + true + + + in + + + + + + + 6. Margin: + + + + + + + 1. Outer radius: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 4. Clipping height: + + + + + + + 2. Inner radius: + + + + + + + 3. Clipping width: + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + 5. Waste: + + + + + + + + + + 0 + 0 + + + + + + + :/images/TemplateDesigner/ex-cd-size.png + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 65 + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerEllipsePage.ui b/glabels/ui/TemplateDesignerEllipsePage.ui new file mode 100644 index 0000000..53dec92 --- /dev/null +++ b/glabels/ui/TemplateDesignerEllipsePage.ui @@ -0,0 +1,156 @@ + + + TemplateDesignerEllipsePage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + + + true + + + in + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 2. Height: + + + + + + + 1. Width: + + + + + + + 3. Waste: + + + + + + + true + + + in + + + + + + + 4. Margin: + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + + + + 0 + 0 + + + + + + + :/images/TemplateDesigner/ex-ellipse-size.png + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 65 + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerIntroPage.ui b/glabels/ui/TemplateDesignerIntroPage.ui new file mode 100644 index 0000000..4f9a594 --- /dev/null +++ b/glabels/ui/TemplateDesignerIntroPage.ui @@ -0,0 +1,76 @@ + + + TemplateDesignerIntroPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + 12 + + + + + <html><head/><body><p>This dialog will help you create a custom product template. Let's get started:</p></body></html> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Copy/Edit Product + + + Copy and edit an existing product template + + + + + + + New Product + + + Create a a new product template from scratch + + + + + + + + diff --git a/glabels/ui/TemplateDesignerNLayoutsPage.ui b/glabels/ui/TemplateDesignerNLayoutsPage.ui new file mode 100644 index 0000000..df42ffd --- /dev/null +++ b/glabels/ui/TemplateDesignerNLayoutsPage.ui @@ -0,0 +1,250 @@ + + + TemplateDesignerNLayoutsPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + QLayout::SetDefaultConstraint + + + + + 18 + + + + + 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. + + + true + + + + + + + QLayout::SetDefaultConstraint + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 0 + 75 + + + + + + + :/images/TemplateDesigner/ex-1layout.png + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Products needing only one layout. + + + Qt::AlignCenter + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 0 + 75 + + + + + + + :/images/TemplateDesigner/ex-2layouts.png + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Products needing two layouts. + + + Qt::AlignCenter + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Note: if more than two layouts are required, the product template must be edited manually. + + + true + + + + + + + 3 + + + + + One layout + + + true + + + + + + + Two layouts + + + + + + + + + + + Qt::Vertical + + + + 20 + 58 + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerNamePage.ui b/glabels/ui/TemplateDesignerNamePage.ui new file mode 100644 index 0000000..9bf0d13 --- /dev/null +++ b/glabels/ui/TemplateDesignerNamePage.ui @@ -0,0 +1,134 @@ + + + TemplateDesignerNamePage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + 6 + + + + + Qt::Vertical + + + + 20 + 123 + + + + + + + + 12 + + + + + + + + + + + + + + + (e.g. "Mailing Labels," "Business Cards," ...) + + + + + + + Brand: + + + + + + + Part #: + + + + + + + <b>Warning Text</b> + + + Qt::RichText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Description: + + + + + + + + + + (e.g. 8163A) + + + + + + + + + + (e.g. Avery, Acme, ...) + + + + + + + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerOneLayoutPage.ui b/glabels/ui/TemplateDesignerOneLayoutPage.ui new file mode 100644 index 0000000..47ec90e --- /dev/null +++ b/glabels/ui/TemplateDesignerOneLayoutPage.ui @@ -0,0 +1,218 @@ + + + TemplateDesignerOneLayoutPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + 12 + + + + + Number across (nx): + + + + + + + 1 + + + + + + + Number down (ny): + + + + + + + 1 + + + + + + + Distance from left edge (x0): + + + + + + + true + + + in + + + + + + + Distance from top edge (y0); + + + + + + + true + + + in + + + + + + + Horizontal pitch (dx): + + + + + + + true + + + in + + + + + + + Vertical pitch (dy): + + + + + + + true + + + in + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Print test sheet + + + + :/icons/32x32/actions/print.svg:/icons/32x32/actions/print.svg + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 133 + + + + + + + + + glabels::SimplePreview + QGraphicsView +
SimplePreview.h
+
+
+ + + + +
diff --git a/glabels/ui/TemplateDesignerPageSizePage.ui b/glabels/ui/TemplateDesignerPageSizePage.ui new file mode 100644 index 0000000..c65edaa --- /dev/null +++ b/glabels/ui/TemplateDesignerPageSizePage.ui @@ -0,0 +1,107 @@ + + + TemplateDesignerPageSizePage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + Page size: + + + + + + + Width: + + + + + + + true + + + in + + + + + + + Height: + + + + + + + true + + + in + + + + + + + + + + + + Qt::Horizontal + + + + 182 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 173 + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerRectPage.ui b/glabels/ui/TemplateDesignerRectPage.ui new file mode 100644 index 0000000..d8b2132 --- /dev/null +++ b/glabels/ui/TemplateDesignerRectPage.ui @@ -0,0 +1,177 @@ + + + TemplateDesignerRectPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + + + 2. Height: + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + 1. Width: + + + + + + + true + + + in + + + + + + + 4. Horizontal waste: + + + + + + + 3. Corner radius + + + + + + + 6. Margin: + + + + + + + true + + + in + + + + + + + 5. Vertical waste: + + + + + + + + + + 0 + 0 + + + + + + + :/images/TemplateDesigner/ex-rect-size.png + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 65 + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerRoundPage.ui b/glabels/ui/TemplateDesignerRoundPage.ui new file mode 100644 index 0000000..c51aa03 --- /dev/null +++ b/glabels/ui/TemplateDesignerRoundPage.ui @@ -0,0 +1,139 @@ + + + TemplateDesignerRoundPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + in + + + + + + + 3. Margin + + + + + + + 1. Radius: + + + + + + + 2. Waste: + + + + + + + + + + 0 + 0 + + + + + + + :/images/TemplateDesigner/ex-round-size.png + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + + 20 + 65 + + + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerShapePage.ui b/glabels/ui/TemplateDesignerShapePage.ui new file mode 100644 index 0000000..0466fbb --- /dev/null +++ b/glabels/ui/TemplateDesignerShapePage.ui @@ -0,0 +1,81 @@ + + + TemplateDesignerShapePage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + + + Rectangular or square (can have rounded corners) + + + true + + + + + + + Round + + + + + + + Elliptical + + + + + + + CD/DVD (including credit card CDs) + + + + + + + + + Qt::Vertical + + + + 20 + 149 + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerTwoLayoutPage.ui b/glabels/ui/TemplateDesignerTwoLayoutPage.ui new file mode 100644 index 0000000..11cb70e --- /dev/null +++ b/glabels/ui/TemplateDesignerTwoLayoutPage.ui @@ -0,0 +1,256 @@ + + + TemplateDesignerTwoLayoutPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + + + 12 + + + + + Distance from left edge (x0): + + + + + + + + + + true + + + in + + + + + + + Number down (ny): + + + + + + + true + + + in + + + + + + + Distance from top edge (y0); + + + + + + + true + + + in + + + + + + + + + + Number across (nx): + + + + + + + Horizontal pitch (dx): + + + + + + + true + + + in + + + + + + + Vertical pitch (dy): + + + + + + + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + true + + + in + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Print test sheet + + + + :/icons/32x32/actions/print.svg:/icons/32x32/actions/print.svg + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 133 + + + + + + + + + glabels::SimplePreview + QGraphicsView +
SimplePreview.h
+
+
+ + + + +
diff --git a/model/Db.cpp b/model/Db.cpp index 5321c2a..6592193 100644 --- a/model/Db.cpp +++ b/model/Db.cpp @@ -23,9 +23,11 @@ #include "Config.h" #include "StrUtil.h" #include "FileUtil.h" +#include "Settings.h" #include "XmlCategoryParser.h" #include "XmlPaperParser.h" #include "XmlTemplateParser.h" +#include "XmlTemplateCreator.h" #include "XmlVendorParser.h" #include @@ -497,6 +499,22 @@ namespace glabels } + bool Db::isSystemTemplateKnown( const QString& brand, const QString& part ) + { + foreach ( Template *tmplate, mTemplates ) + { + if ( (tmplate->brand() == brand) && + (tmplate->part() == part) && + !tmplate->isUserDefined() ) + { + return true; + } + } + + return false; + } + + QStringList Db::getNameListOfSimilarTemplates( const QString& name ) { QStringList list; @@ -523,21 +541,52 @@ namespace glabels } - void Db::registerUserTemplate( Template *templat ) + QString Db::userTemplateFilename( const QString& brand, const QString& part ) { - // TODO + QString filename = brand + "_" + part + ".template"; + return FileUtil::userTemplatesDir().filePath( filename ); } - void Db::deleteUserTemplateByName( const QString& name ) + void Db::registerUserTemplate( Template *tmplate ) { - // TODO + QString filename = userTemplateFilename( tmplate->brand(), tmplate->part() ); + + // Write file + if ( XmlTemplateCreator().writeTemplate( tmplate, filename ) ) + { + // Add template to list of registered templates + registerTemplate( tmplate ); + Settings::addToRecentTemplateList( tmplate->name() ); + } + else + { + qWarning() << "Problem writing user template" << filename; + } } void Db::deleteUserTemplateByBrandPart( const QString& brand, const QString& part ) { - // TODO + Template* tmplate; + foreach ( Template *candidate, mTemplates ) + { + if ( candidate->isUserDefined() && + (candidate->brand() == brand) && (candidate->part() == part) ) + { + tmplate = candidate; + break; + } + } + + if ( tmplate ) + { + mTemplates.removeOne( tmplate ); + delete tmplate; + + QString filename = userTemplateFilename( brand, part ); + QFile( filename ).remove(); + } } @@ -667,15 +716,15 @@ namespace glabels void Db::readTemplates() { - readTemplatesFromDir( FileUtil::systemTemplatesDir() ); - - // TODO: Read user directories + readTemplatesFromDir( FileUtil::systemTemplatesDir(), false ); + readTemplatesFromDir( FileUtil::manualUserTemplatesDir(), false ); + readTemplatesFromDir( FileUtil::userTemplatesDir(), true ); std::stable_sort( mTemplates.begin(), mTemplates.end(), partNameLessThan ); } - void Db::readTemplatesFromDir( const QDir& dir ) + void Db::readTemplatesFromDir( const QDir& dir, bool isUserDefined ) { QStringList filters; filters << "*-templates.xml" << "*.template"; @@ -684,7 +733,7 @@ namespace glabels foreach ( QString fileName, dir.entryList( filters, QDir::Files ) ) { - parser.readFile( dir.absoluteFilePath( fileName ) ); + parser.readFile( dir.absoluteFilePath( fileName ), isUserDefined ); } } diff --git a/model/Db.h b/model/Db.h index a7be79e..6964a81 100644 --- a/model/Db.h +++ b/model/Db.h @@ -90,10 +90,11 @@ namespace glabels static const Template *lookupTemplateFromBrandPart( const QString& brand, const QString& part ); static bool isTemplateKnown( const QString& brand, const QString& part ); + static bool isSystemTemplateKnown( const QString& brand, const QString& part ); static QStringList getNameListOfSimilarTemplates( const QString& name ); + static QString userTemplateFilename( const QString& brand, const QString& part ); static void registerUserTemplate( Template *tmplate ); - static void deleteUserTemplateByName( const QString& name ); static void deleteUserTemplateByBrandPart( const QString& brand, const QString& part ); @@ -116,7 +117,7 @@ namespace glabels static void readVendorsFromDir( const QDir& dir ); static void readTemplates(); - static void readTemplatesFromDir( const QDir& dir ); + static void readTemplatesFromDir( const QDir& dir, bool isUserDefined ); private: diff --git a/model/FileUtil.cpp b/model/FileUtil.cpp index c31b790..09e61d2 100644 --- a/model/FileUtil.cpp +++ b/model/FileUtil.cpp @@ -23,6 +23,7 @@ #include "Config.h" #include +#include namespace glabels @@ -64,6 +65,27 @@ namespace glabels } + QDir FileUtil::manualUserTemplatesDir() + { + // Location for manually created user-defined templates + QDir dir( QStandardPaths::writableLocation(QStandardPaths::HomeLocation) ); + dir.mkpath( ".glabels" ); + dir.cd( ".glabels" ); + + return dir; + } + + + QDir FileUtil::userTemplatesDir() + { + // Location for user-defined templates created using TemplateDesigner + QDir dir( QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) ); + dir.mkpath( "." ); + + return dir; + } + + QDir FileUtil::translationsDir() { QDir dir; diff --git a/model/FileUtil.h b/model/FileUtil.h index 00d9c5b..ed76de0 100644 --- a/model/FileUtil.h +++ b/model/FileUtil.h @@ -37,6 +37,7 @@ namespace glabels QString addExtension( const QString& rawFilename, const QString& extension ); QDir systemTemplatesDir(); + QDir manualUserTemplatesDir(); QDir userTemplatesDir(); QDir translationsDir(); diff --git a/model/Settings.cpp b/model/Settings.cpp index b8096b8..b0ac10e 100644 --- a/model/Settings.cpp +++ b/model/Settings.cpp @@ -92,6 +92,40 @@ namespace glabels } + Settings::PageSizeFamily Settings::preferedPageSizeFamily() + { + // Guess at a suitable default + QString defaultFamily; + switch (QLocale::system().country()) + { + case QLocale::UnitedStates: + case QLocale::Canada: + defaultFamily = "us"; + break; + + default: + defaultFamily = "iso"; + break; + } + + mInstance->beginGroup( "Locale" ); + QString value = mInstance->value( "preferedPageSizeFamily", defaultFamily ).toString(); + mInstance->endGroup(); + + return (value == "iso") ? ISO : US; + } + + + void Settings::setPreferedPageSizeFamily( PageSizeFamily preferedPageSizeFamily ) + { + mInstance->beginGroup( "Locale" ); + mInstance->setValue( "preferedPageSizeFamily", preferedPageSizeFamily == ISO ? "iso" : "us" ); + mInstance->endGroup(); + + emit mInstance->changed(); + } + + bool Settings::searchIsoPaperSizes() { // Guess at a suitable default diff --git a/model/Settings.h b/model/Settings.h index bd950d4..f16c4f0 100644 --- a/model/Settings.h +++ b/model/Settings.h @@ -41,7 +41,7 @@ namespace glabels Q_OBJECT public: - enum class PageSizeFamilty { ISO, US, }; + enum PageSizeFamily { ISO, US, }; ///////////////////////////////// @@ -69,6 +69,9 @@ namespace glabels static Units units(); static void setUnits( const Units& units ); + static PageSizeFamily preferedPageSizeFamily(); + static void setPreferedPageSizeFamily( PageSizeFamily preferedPageSizeFamily ); + static bool searchIsoPaperSizes(); static void setSearchIsoPaperSizes( bool searchIsoPaperSizes ); diff --git a/model/Template.cpp b/model/Template.cpp index eaffb1e..6acc685 100644 --- a/model/Template.cpp +++ b/model/Template.cpp @@ -35,13 +35,15 @@ namespace glabels const QString& description, const QString& paperId, const Distance& pageWidth, - const Distance& pageHeight ) + const Distance& pageHeight, + bool isUserDefined ) : mBrand(brand), mPart(part), mDescription(description), mPaperId(paperId), mPageWidth(pageWidth), mPageHeight(pageHeight), + mIsUserDefined(isUserDefined), mIsSizeIso(false), mIsSizeUs(false), mName("") @@ -179,6 +181,12 @@ namespace glabels } + bool Template::isUserDefined() const + { + return mIsUserDefined; + } + + QString Template::equivPart() const { return mEquivPart; diff --git a/model/Template.h b/model/Template.h index b05d087..144d8a6 100644 --- a/model/Template.h +++ b/model/Template.h @@ -48,7 +48,8 @@ namespace glabels const QString& description, const QString& paperId, const Distance& pageWidth, - const Distance& pageHeight ); + const Distance& pageHeight, + bool isUserDefined = false ); Template( const Template& other ); @@ -74,6 +75,8 @@ namespace glabels bool isSizeUs() const; bool isSizeOther() const; + bool isUserDefined() const; + QString equivPart() const; void setEquivPart( const QString& value ); @@ -104,6 +107,8 @@ namespace glabels bool mIsSizeIso; bool mIsSizeUs; + bool mIsUserDefined; + QString mEquivPart; QString mName; diff --git a/model/XmlTemplateParser.cpp b/model/XmlTemplateParser.cpp index 2c063a1..3bf7452 100644 --- a/model/XmlTemplateParser.cpp +++ b/model/XmlTemplateParser.cpp @@ -41,7 +41,7 @@ namespace glabels namespace model { - bool XmlTemplateParser::readFile( const QString &fileName ) + bool XmlTemplateParser::readFile( const QString &fileName, bool isUserDefined ) { QFile file( fileName ); @@ -73,18 +73,18 @@ namespace glabels return false; } - parseRootNode( root ); + parseRootNode( root, isUserDefined ); return true; } - void XmlTemplateParser::parseRootNode( const QDomElement &node ) + void XmlTemplateParser::parseRootNode( const QDomElement &node, bool isUserDefined ) { for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { if ( child.toElement().tagName() == "Template" ) { - Template *tmplate = parseTemplateNode( child.toElement() ); + Template *tmplate = parseTemplateNode( child.toElement(), isUserDefined ); if ( tmplate != nullptr ) { Db::registerTemplate( tmplate ); @@ -104,7 +104,7 @@ namespace glabels } - Template *XmlTemplateParser::parseTemplateNode( const QDomElement &node ) + Template *XmlTemplateParser::parseTemplateNode( const QDomElement &node, bool isUserDefined ) { QString brand = XmlUtil::getStringAttr( node, "brand", "" ); QString part = XmlUtil::getStringAttr( node, "part", "" ); @@ -163,14 +163,14 @@ namespace glabels } tmplate = new Template( brand, part, description, - paper->id(), paper->width(), paper->height() ); + paper->id(), paper->width(), paper->height(), isUserDefined ); } else { Distance width = XmlUtil::getLengthAttr( node, "width", Distance(0) ); Distance height = XmlUtil::getLengthAttr( node, "height", Distance(0) ); - tmplate = new Template( brand, part, description, paperId, width, height ); + tmplate = new Template( brand, part, description, paperId, width, height, isUserDefined ); } for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) diff --git a/model/XmlTemplateParser.h b/model/XmlTemplateParser.h index a64413d..e48434f 100644 --- a/model/XmlTemplateParser.h +++ b/model/XmlTemplateParser.h @@ -38,11 +38,11 @@ namespace glabels public: XmlTemplateParser() = default; - bool readFile( const QString &fileName ); - Template *parseTemplateNode( const QDomElement &node ); + bool readFile( const QString &fileName, bool isUserDefined = false ); + Template *parseTemplateNode( const QDomElement &node, bool isUserDefined = false ); private: - void parseRootNode( const QDomElement &node ); + void parseRootNode( const QDomElement &node, bool isUserDefined ); void parseMetaNode( const QDomElement &node, Template *tmplate ); void parseLabelRectangleNode( const QDomElement &node, Template *tmplate ); void parseLabelEllipseNode( const QDomElement &node, Template *tmplate ); diff --git a/translations/glabels_C.ts b/translations/glabels_C.ts index e4e4d65..665f97a 100644 --- a/translations/glabels_C.ts +++ b/translations/glabels_C.ts @@ -785,6 +785,397 @@ + + TemplateDesignerApplyPage + + + Form + + + + + You have completed the gLabels Product Template Designer. If you wish to accept and save your product template, click "Save." + + + + + Otherwise, you may click "Cancel" to abandon your design or "Back" to review or continue editing this product template. + + + + + TemplateDesignerCdPage + + + Form + + + + + 4. Clipping height: + + + + + 2. Inner radius: + + + + + 1. Outer radius: + + + + + 5. Waste: + + + + + 3. Clipping width: + + + + + 6. Margin: + + + + + TemplateDesignerEllipsePage + + + Form + + + + + 3. Waste: + + + + + 2. Height: + + + + + 1. Width: + + + + + 4. Margin: + + + + + TemplateDesignerIntroPage + + + Form + + + + + <html><head/><body><p>This dialog will help you create a custom product template. Let's get started:</p></body></html> + + + + + Copy/Edit Product + + + + + Copy and edit an existing product template + + + + + New Product + + + + + Create a a new product template from scratch + + + + + 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. + + + + + Products needing only one layout. + + + + + Products needing two layouts. + + + + + Note: if more than two layouts are required, the product template must be edited manually. + + + + + One layout + + + + + Two layouts + + + + + TemplateDesignerNamePage + + + Form + + + + + Brand: + + + + + (e.g. Avery, Acme, ...) + + + + + Part #: + + + + + (e.g. 8163A) + + + + + Description: + + + + + (e.g. "Mailing Labels," "Business Cards," ...) + + + + + TemplateDesignerOneLayoutPage + + + Form + + + + + Number across (nx): + + + + + Number down (ny): + + + + + Distance from left edge (x0): + + + + + Distance from top edge (y0); + + + + + Horizontal pitch (dx): + + + + + Vertical pitch (dy): + + + + + Print test sheet + + + + + TemplateDesignerPageSizePage + + + Form + + + + + Page size: + + + + + Width: + + + + + Height: + + + + + TemplateDesignerRectPage + + + Form + + + + + 1. Width: + + + + + 2. Height: + + + + + 3. Corner radius + + + + + 4. Horizontal waste: + + + + + 5. Vertical waste: + + + + + 6. Margin: + + + + + TemplateDesignerRoundPage + + + Form + + + + + 2. Waste: + + + + + 1. Radius: + + + + + 3. Margin + + + + + TemplateDesignerShapePage + + + Form + + + + + Rectangular or square (can have rounded corners) + + + + + Round + + + + + Elliptical + + + + + CD/DVD (including credit card CDs) + + + + + TemplateDesignerTwoLayoutPage + + + Form + + + + + Distance from left edge (x0): + + + + + Number down (ny): + + + + + Distance from top edge (y0); + + + + + Number across (nx): + + + + + Horizontal pitch (dx): + + + + + Vertical pitch (dy): + + + + + Print test sheet + + + glabels::AboutDialog @@ -834,43 +1225,43 @@ glabels::File - + gLabels - Open Project - - + + glabels files (*.glabels);;All files (*) - + Unable to open " - + ". - + gLabels - Save Project As - + Save Label As - + %1 already exists. - + Do you want to replace it? @@ -961,7 +1352,7 @@ - Template &Designer... + Product Template &Designer... @@ -1004,7 +1395,7 @@ - + Cut @@ -1232,7 +1623,7 @@ - + Bring To Front @@ -1243,7 +1634,7 @@ - + Send To Back @@ -1254,7 +1645,7 @@ - + Rotate Left @@ -1265,7 +1656,7 @@ - + Rotate Right @@ -1276,7 +1667,7 @@ - + Flip Horizontally @@ -1287,7 +1678,7 @@ - + Flip Vertically @@ -1298,7 +1689,7 @@ - + Align Left @@ -1309,7 +1700,7 @@ - + Align Center @@ -1320,7 +1711,7 @@ - + Align Right @@ -1331,7 +1722,7 @@ - + Align Top @@ -1342,7 +1733,7 @@ - + Align Middle @@ -1353,7 +1744,7 @@ - + Align Bottom @@ -1364,7 +1755,7 @@ - + Center Horizontally @@ -1375,7 +1766,7 @@ - + Center Vertically @@ -1470,62 +1861,62 @@ - + (modified) - + Save changes to project "%1" before closing? - + Your changes will be lost if you don't save them. - + Save project? - + Paste - + Delete - + Create Text - + Create Box - + Create Line - + Create Ellipse - + Create Image - + Create Barcode @@ -1727,6 +2118,202 @@ + + glabels::TemplateDesigner + + + Product Template Designer + + + + + Copy + + + + + glabels::TemplateDesignerApplyPage + + + + Save Product Template + + + + + Click "Save" to save your custom product template! + + + + + User product template (%1 %2) already exists. + + + + + Do you want to replace it? + + + + + glabels::TemplateDesignerCdPage + + + Product Size + + + + + Please adjust the size parameters of a single product item. + + + + + glabels::TemplateDesignerEllipsePage + + + Product Size + + + + + Please adjust the size parameters of a single product item. + + + + + glabels::TemplateDesignerIntroPage + + + Welcome + + + + + Welcome to the gLabels Product Template Designer. + + + + + glabels::TemplateDesignerNLayoutsPage + + + Number of Layouts + + + + + Please select the number of layouts required. + + + + + glabels::TemplateDesignerNamePage + + + Name and Description + + + + + Please enter the following identifying information about the product. + + + + + Brand and part number match an existing built-in product template! + + + + + glabels::TemplateDesignerOneLayoutPage + + + Layout + + + + + Please enter parameters for your single layout. + + + + + glabels::TemplateDesignerPageSizePage + + + Page Size + + + + + Please select the product page size. + + + + + + + + + + + Other + + + + + glabels::TemplateDesignerRectPage + + + Product Size + + + + + Please adjust the size parameters of a single product item. + + + + + glabels::TemplateDesignerRoundPage + + + Product Size + + + + + Please adjust the size parameters of a single product item. + + + + + glabels::TemplateDesignerShapePage + + + Product Shape + + + + + Please select the basic product shape. + + + + + glabels::TemplateDesignerTwoLayoutPage + + + Layouts + + + + + Please enter parameters for your two layouts. + + + main