From 3fea813b9c807c3255ad26492e475cb3aaae0d1a Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Sat, 11 Sep 2021 19:39:38 +0300 Subject: [PATCH] Watermark support. --- .../puzzlepreferenceslayoutpage.cpp | 2 + .../puzzlepreferenceslayoutpage.ui | 10 + src/app/puzzle/layout/vplayout.cpp | 32 + src/app/puzzle/layout/vplayout.h | 3 + src/app/puzzle/layout/vplayoutsettings.cpp | 28 +- src/app/puzzle/layout/vplayoutsettings.h | 9 + src/app/puzzle/layout/vpsheet.cpp | 10 +- src/app/puzzle/layout/vpsheet.h | 1 + src/app/puzzle/puzzle.pro | 18 +- src/app/puzzle/scene/vpgraphicstilegrid.cpp | 45 +- src/app/puzzle/scene/vpgraphicstilegrid.h | 1 + src/app/puzzle/share/resources/puzzleicon.qrc | 1 + .../puzzleicon/svg/no_watermark_image.svg | 1 + src/app/puzzle/vpmainwindow.cpp | 195 ++- src/app/puzzle/vpmainwindow.h | 13 + src/app/puzzle/vpmainwindow.ui | 78 +- src/app/puzzle/vpsettings.cpp | 13 + src/app/puzzle/vpsettings.h | 3 + src/app/puzzle/vptilefactory.cpp | 219 +++ src/app/puzzle/vptilefactory.h | 12 +- src/app/puzzle/xml/vplayoutfilereader.cpp | 17 +- src/app/puzzle/xml/vplayoutfilereader.h | 1 + src/app/puzzle/xml/vplayoutfilewriter.cpp | 6 + src/app/puzzle/xml/vplayoutliterals.cpp | 2 + src/app/puzzle/xml/vplayoutliterals.h | 2 + src/app/valentina/mainwindow.cpp | 2 +- src/app/valentina/mainwindow.ui | 6 +- src/app/valentina/valentina.pri | 9 +- src/app/valentina/valentina.pro | 20 +- src/app/valentina/xml/vpattern.cpp | 2 +- src/app/valentina/xml/vpattern.h | 4 +- src/libs/ifc/ifcdef.h | 3 + src/libs/ifc/schema.qrc | 1 + src/libs/ifc/schema/layout/v0.1.0.xsd | 9 + src/libs/ifc/schema/watermark/v1.1.0.xsd | 54 + src/libs/ifc/xml/vwatermarkconverter.cpp | 41 +- src/libs/ifc/xml/vwatermarkconverter.h | 4 +- src/libs/vformat/vwatermark.cpp | 10 +- src/libs/vlayout/dialogs/dialogs.pri | 9 +- .../vlayout/dialogs}/watermarkwindow.cpp | 41 +- .../vlayout/dialogs}/watermarkwindow.h | 0 .../vlayout/dialogs}/watermarkwindow.ui | 39 +- src/libs/vlayout/vposter.cpp | 6 +- src/libs/vmisc/vcommonsettings.cpp | 64 + src/libs/vmisc/vcommonsettings.h | 6 + src/libs/vmisc/vvalentinasettings.cpp | 14 - src/libs/vmisc/vvalentinasettings.h | 3 - src/libs/vtools/tools/vtoolseamallowance.cpp | 2 +- src/libs/vtools/tools/vtooluniondetails.cpp | 8 +- src/libs/vwidgets/qtcolorpicker.cpp | 1274 +++++++++++++++++ src/libs/vwidgets/qtcolorpicker.h | 110 ++ src/libs/vwidgets/vwidgets.pri | 2 + 52 files changed, 2365 insertions(+), 100 deletions(-) create mode 100644 src/app/puzzle/share/resources/puzzleicon/svg/no_watermark_image.svg create mode 100644 src/libs/ifc/schema/watermark/v1.1.0.xsd rename src/{app/valentina => libs/vlayout/dialogs}/watermarkwindow.cpp (91%) rename src/{app/valentina => libs/vlayout/dialogs}/watermarkwindow.h (100%) rename src/{app/valentina => libs/vlayout/dialogs}/watermarkwindow.ui (91%) create mode 100644 src/libs/vwidgets/qtcolorpicker.cpp create mode 100644 src/libs/vwidgets/qtcolorpicker.h diff --git a/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.cpp b/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.cpp index 42544049d..08a140fb2 100644 --- a/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.cpp +++ b/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.cpp @@ -119,6 +119,7 @@ auto PuzzlePreferencesLayoutPage::Apply() -> QStringList settings->SetLayoutSheetMargins(GetSheetMargins()); settings->SetLayoutTileShowTiles(ui->checkBoxTileShowTiles->isChecked()); + settings->SetLayoutTileShowWatermark(ui->checkBoxTileShowWatermark->isChecked()); settings->SetLayoutTilePaperHeight( UnitConvertor(ui->doubleSpinBoxTilePaperHeight->value(), m_oldLayoutUnit, Unit::Px)); @@ -602,6 +603,7 @@ void PuzzlePreferencesLayoutPage::ReadSettings() TileSize(QSizeF(tileWidth, tileHeight)); ui->checkBoxTileShowTiles->setChecked(settings->GetLayoutTileShowTiles()); + ui->checkBoxTileShowWatermark->setChecked(settings->GetLayoutTileShowWatermark()); ui->checkBoxTileIgnoreFileds->setChecked(settings->GetLayoutTileIgnoreMargins()); SetTileMargins(settings->GetLayoutSheetMargins()); diff --git a/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.ui b/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.ui index 02593bac5..cc67e52e8 100644 --- a/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.ui +++ b/src/app/puzzle/dialogs/configpages/puzzlepreferenceslayoutpage.ui @@ -374,6 +374,16 @@ + + + + Show watermark preview + + + Show watermark + + + diff --git a/src/app/puzzle/layout/vplayout.cpp b/src/app/puzzle/layout/vplayout.cpp index ad540597e..71e1ec335 100644 --- a/src/app/puzzle/layout/vplayout.cpp +++ b/src/app/puzzle/layout/vplayout.cpp @@ -31,9 +31,13 @@ #include "vpsheet.h" #include "../vpapplication.h" #include "../vptilefactory.h" +#include "../ifc/xml/vwatermarkconverter.h" +#include "../vformat/vwatermark.h" +#include "../ifc/exception/vexception.h" #include #include +#include Q_LOGGING_CATEGORY(pLayout, "p.layout") @@ -63,6 +67,7 @@ auto VPLayout::CreateLayout(QUndoStack *undoStack) -> VPLayoutPtr layout->LayoutSettings().SetUnit(settings->LayoutUnit()); layout->LayoutSettings().SetShowTiles(settings->GetLayoutTileShowTiles()); + layout->LayoutSettings().SetShowWatermark(settings->GetLayoutTileShowWatermark()); layout->LayoutSettings().SetTilesSize(QSizeF(settings->GetLayoutTilePaperWidth(), settings->GetLayoutTilePaperHeight())); layout->LayoutSettings().SetIgnoreTilesMargins(settings->GetLayoutTileIgnoreMargins()); @@ -138,6 +143,33 @@ void VPLayout::RefreshScenePieces() const } } +//--------------------------------------------------------------------------------------------------------------------- +auto VPLayout::WatermarkData() const -> VWatermarkData +{ + VWatermarkData data; + if (not m_layoutSettings.WatermarkPath().isEmpty()) + { + try + { + VWatermarkConverter converter(m_layoutSettings.WatermarkPath()); + VWatermark watermark; + watermark.setXMLContent(converter.Convert()); + data = watermark.GetWatermark(); + } + catch (VException &e) + { + data.invalidFile = true; + data.opacity = 20; + data.showImage = true; + data.path = "fake.png"; + data.showText = false; + return data; + } + } + + return data; +} + //--------------------------------------------------------------------------------------------------------------------- auto VPLayout::GetPieces() const -> QList { diff --git a/src/app/puzzle/layout/vplayout.h b/src/app/puzzle/layout/vplayout.h index 114448f64..08f358336 100644 --- a/src/app/puzzle/layout/vplayout.h +++ b/src/app/puzzle/layout/vplayout.h @@ -39,6 +39,7 @@ class VPPiece; class VPSheet; class QUndoStack; class VPTileFactory; +struct VWatermarkData; class VPLayout : public QObject { @@ -93,6 +94,8 @@ public: void RefreshScenePieces() const; + auto WatermarkData() const -> VWatermarkData; + signals: void PieceSheetChanged(const VPPiecePtr &piece); void ActiveSheetChanged(const VPSheetPtr &focusedSheet); diff --git a/src/app/puzzle/layout/vplayoutsettings.cpp b/src/app/puzzle/layout/vplayoutsettings.cpp index aaba53418..fca313794 100644 --- a/src/app/puzzle/layout/vplayoutsettings.cpp +++ b/src/app/puzzle/layout/vplayoutsettings.cpp @@ -330,7 +330,7 @@ void VPLayoutSettings::SetIgnoreTilesMargins(bool newIgnoreTilesMargins) } //--------------------------------------------------------------------------------------------------------------------- -qreal VPLayoutSettings::HorizontalScale() const +auto VPLayoutSettings::HorizontalScale() const -> qreal { return m_horizontalScale; } @@ -342,7 +342,7 @@ void VPLayoutSettings::SetHorizontalScale(qreal newHorizontalScale) } //--------------------------------------------------------------------------------------------------------------------- -qreal VPLayoutSettings::VerticalScale() const +auto VPLayoutSettings::VerticalScale() const -> qreal { return m_verticalScale; } @@ -352,3 +352,27 @@ void VPLayoutSettings::SetVerticalScale(qreal newVerticalScale) { m_verticalScale = newVerticalScale; } + +//--------------------------------------------------------------------------------------------------------------------- +auto VPLayoutSettings::WatermarkPath() const -> const QString & +{ + return m_watermarkPath; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPLayoutSettings::SetWatermarkPath(const QString &newWatermarkPath) +{ + m_watermarkPath = newWatermarkPath; +} + +//--------------------------------------------------------------------------------------------------------------------- +auto VPLayoutSettings::GetShowWatermark() const -> bool +{ + return m_showWatermark; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPLayoutSettings::SetShowWatermark(bool newShowWatermark) +{ + m_showWatermark = newShowWatermark; +} diff --git a/src/app/puzzle/layout/vplayoutsettings.h b/src/app/puzzle/layout/vplayoutsettings.h index 0d7f58806..d0ba7b33f 100644 --- a/src/app/puzzle/layout/vplayoutsettings.h +++ b/src/app/puzzle/layout/vplayoutsettings.h @@ -306,6 +306,12 @@ public: auto VerticalScale() const -> qreal; void SetVerticalScale(qreal newVerticalScale); + auto WatermarkPath() const -> const QString &; + void SetWatermarkPath(const QString &newWatermarkPath); + + auto GetShowWatermark() const -> bool; + void SetShowWatermark(bool newShowWatermark); + private: Unit m_unit{Unit::Cm}; @@ -329,6 +335,7 @@ private: bool m_ignoreTilesMargins{false}; bool m_showTiles{false}; + bool m_showWatermark{false}; // control bool m_followGrainLine{false}; @@ -357,6 +364,8 @@ private: qreal m_horizontalScale{1.0}; qreal m_verticalScale{1.0}; + + QString m_watermarkPath{}; }; #endif // VPLAYOUTSETTINGS_H diff --git a/src/app/puzzle/layout/vpsheet.cpp b/src/app/puzzle/layout/vpsheet.cpp index e369c6df2..9d25ef565 100644 --- a/src/app/puzzle/layout/vpsheet.cpp +++ b/src/app/puzzle/layout/vpsheet.cpp @@ -263,6 +263,9 @@ void VPSheetSceneData::PrepareTilesScheme() { m_showTilesSchemeTmp = layout->LayoutSettings().GetShowTiles(); layout->LayoutSettings().SetShowTiles(true); + + m_showTilesWatermarkSchemeTmp = layout->LayoutSettings().GetShowWatermark(); + layout->LayoutSettings().SetShowWatermark(false); } RefreshLayout(); @@ -275,6 +278,7 @@ void VPSheetSceneData::ClearTilesScheme() if (not layout.isNull()) { layout->LayoutSettings().SetShowTiles(m_showTilesSchemeTmp); + layout->LayoutSettings().SetShowWatermark(m_showTilesWatermarkSchemeTmp); } RefreshLayout(); @@ -379,7 +383,7 @@ auto VPSheet::GetAsLayoutPieces() const -> QVector QVector details; details.reserve(pieces.size()); - for (auto piece : pieces) + for (const auto& piece : pieces) { if (not piece.isNull()) { @@ -421,7 +425,7 @@ void VPSheet::SetVisible(bool visible) } //--------------------------------------------------------------------------------------------------------------------- -GrainlineType VPSheet::GrainlineOrientation() const +auto VPSheet::GrainlineOrientation() const -> GrainlineType { if (m_grainlineType == GrainlineType::NotFixed) { @@ -694,7 +698,7 @@ void VPSheet::ClearSelection() const } //--------------------------------------------------------------------------------------------------------------------- -QPageLayout::Orientation VPSheet::GetSheetOrientation() const +auto VPSheet::GetSheetOrientation() const -> QPageLayout::Orientation { return m_size.height() >= m_size.width() ? QPageLayout::Portrait : QPageLayout::Landscape; } diff --git a/src/app/puzzle/layout/vpsheet.h b/src/app/puzzle/layout/vpsheet.h index fc9c758a8..8a138ff98 100644 --- a/src/app/puzzle/layout/vpsheet.h +++ b/src/app/puzzle/layout/vpsheet.h @@ -111,6 +111,7 @@ private: bool m_showTilesTmp{false}; bool m_showTilesSchemeTmp{false}; + bool m_showTilesWatermarkSchemeTmp{false}; /** * variable to hold temporarly hte value of the show grid diff --git a/src/app/puzzle/puzzle.pro b/src/app/puzzle/puzzle.pro index a2a9a1dce..10ed4c8be 100644 --- a/src/app/puzzle/puzzle.pro +++ b/src/app/puzzle/puzzle.pro @@ -189,15 +189,6 @@ noRunPath{ # For enable run qmake with CONFIG+=noRunPath #win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vtools/$${DESTDIR}/vtools.lib #else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vtools/$${DESTDIR}/libvtools.a -#VWidgets static library -unix|win32: LIBS += -L$$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/ -lvwidgets - -INCLUDEPATH += $$PWD/../../libs/vwidgets -DEPENDPATH += $$PWD/../../libs/vwidgets - -win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/vwidgets.lib -else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/libvwidgets.a - # VLayout static library (depend on VGeometry, VFormat) unix|win32: LIBS += -L$$OUT_PWD/../../libs/vlayout/$${DESTDIR}/ -lvlayout @@ -243,6 +234,15 @@ DEPENDPATH += $$PWD/../../libs/ifc win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/ifc.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/libifc.a +#VWidgets static library +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/ -lvwidgets + +INCLUDEPATH += $$PWD/../../libs/vwidgets +DEPENDPATH += $$PWD/../../libs/vwidgets + +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/vwidgets.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/libvwidgets.a + #VMisc static library unix|win32: LIBS += -L$$OUT_PWD/../../libs/vmisc/$${DESTDIR}/ -lvmisc diff --git a/src/app/puzzle/scene/vpgraphicstilegrid.cpp b/src/app/puzzle/scene/vpgraphicstilegrid.cpp index 75f45a500..102951dad 100644 --- a/src/app/puzzle/scene/vpgraphicstilegrid.cpp +++ b/src/app/puzzle/scene/vpgraphicstilegrid.cpp @@ -4,10 +4,15 @@ #include "../layout/vplayout.h" #include "../layout/vpsheet.h" +#include +#include +#include +#include + namespace { constexpr qreal penWidth = 1; -} +} // namespace //--------------------------------------------------------------------------------------------------------------------- VPGraphicsTileGrid::VPGraphicsTileGrid(const VPLayoutPtr &layout, const QUuid &sheetUuid, QGraphicsItem *parent): @@ -82,18 +87,44 @@ void VPGraphicsTileGrid::paint(QPainter *painter, const QStyleOptionGraphicsItem const int nbCol = layout->TileFactory()->ColNb(sheet); const int nbRow = layout->TileFactory()->RowNb(sheet); - for(int i=0;i<=nbCol;++i) - { - // vertical lines - painter->drawLine(QPointF(sheetMargins.left()+i*width, sheetMargins.top()), - QPointF(sheetMargins.left()+i*width, sheetMargins.top() + nbRow*height)); - } + VWatermarkData watermarkData = layout->TileFactory()->WatermarkData(); for(int j=0;j<=nbRow;++j) { // horizontal lines painter->drawLine(QPointF(sheetMargins.left(), sheetMargins.top()+j*height), QPointF(sheetMargins.left()+nbCol*width, sheetMargins.top()+j*height)); + + for(int i=0;i<=nbCol;++i) + { + // vertical lines + painter->drawLine(QPointF(sheetMargins.left()+i*width, sheetMargins.top()), + QPointF(sheetMargins.left()+i*width, sheetMargins.top() + nbRow*height)); + + if (j < nbRow && i < nbCol) + { + QRectF img(sheetMargins.left()+i*width, sheetMargins.top()+j*height, + width, height); + + if (not layout->LayoutSettings().WatermarkPath().isEmpty() && + layout->LayoutSettings().GetShowWatermark()) + { + if (watermarkData.opacity > 0) + { + if (watermarkData.showImage && not watermarkData.path.isEmpty()) + { + VPTileFactory::PaintWatermarkImage(painter, img, watermarkData, + layout->LayoutSettings().WatermarkPath()); + } + + if (watermarkData.showText && not watermarkData.text.isEmpty()) + { + VPTileFactory::PaintWatermarkText(painter, img, watermarkData); + } + } + } + } + } } } } diff --git a/src/app/puzzle/scene/vpgraphicstilegrid.h b/src/app/puzzle/scene/vpgraphicstilegrid.h index df3931da0..b53980fb3 100644 --- a/src/app/puzzle/scene/vpgraphicstilegrid.h +++ b/src/app/puzzle/scene/vpgraphicstilegrid.h @@ -38,6 +38,7 @@ class VPTileFactory; class VPLayout; +struct VWatermarkData; class VPGraphicsTileGrid : public QGraphicsItem { diff --git a/src/app/puzzle/share/resources/puzzleicon.qrc b/src/app/puzzle/share/resources/puzzleicon.qrc index b99ed6215..712fccda6 100644 --- a/src/app/puzzle/share/resources/puzzleicon.qrc +++ b/src/app/puzzle/share/resources/puzzleicon.qrc @@ -20,5 +20,6 @@ puzzleicon/32X32/horizontal_grainline@2x.png puzzleicon/32X32/vertical_grainline.png puzzleicon/32X32/vertical_grainline@2x.png + puzzleicon/svg/no_watermark_image.svg diff --git a/src/app/puzzle/share/resources/puzzleicon/svg/no_watermark_image.svg b/src/app/puzzle/share/resources/puzzleicon/svg/no_watermark_image.svg new file mode 100644 index 000000000..3cafa6dbf --- /dev/null +++ b/src/app/puzzle/share/resources/puzzleicon/svg/no_watermark_image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/puzzle/vpmainwindow.cpp b/src/app/puzzle/vpmainwindow.cpp index f5e0be6b3..b65a9cce0 100644 --- a/src/app/puzzle/vpmainwindow.cpp +++ b/src/app/puzzle/vpmainwindow.cpp @@ -43,6 +43,7 @@ #include "../vlayout/vrawlayout.h" #include "../vlayout/vlayoutexporter.h" #include "../vlayout/vprintlayout.h" +#include "../vlayout/dialogs/watermarkwindow.h" #include "../vmisc/vsysexits.h" #include "../vmisc/projectversion.h" #include "../ifc/xml/vlayoutconverter.h" @@ -63,6 +64,8 @@ #endif #include +#include +#include QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wmissing-prototypes") @@ -167,7 +170,8 @@ VPMainWindow::VPMainWindow(const VPCommandLinePtr &cmd, QWidget *parent) : m_undoStack(new QUndoStack(this)), m_layout{VPLayout::CreateLayout(m_undoStack)}, m_statusLabel(new QLabel(this)), - m_layoutWatcher(new QFileSystemWatcher(this)) + m_layoutWatcher(new QFileSystemWatcher(this)), + m_watermarkWatcher(new QFileSystemWatcher(this)) { ui->setupUi(this); @@ -220,7 +224,49 @@ VPMainWindow::VPMainWindow(const VPCommandLinePtr &cmd, QWidget *parent) : { if (not curFile.isEmpty() && curFile == path) { + QFileInfo checkFile(path); + if (not checkFile.exists()) + { + for(int i=0; i<=1000; i=i+10) + { + if (checkFile.exists()) + { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } UpdateWindowTitle(); + + if (checkFile.exists()) + { + m_layoutWatcher->addPath(path); + } + } + }); + + connect(m_layoutWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) + { + QFileInfo checkFile(path); + if (not checkFile.exists()) + { + for(int i=0; i<=1000; i=i+10) + { + if (checkFile.exists()) + { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + m_layout->TileFactory()->refreshTileInfos(); + m_graphicsView->RefreshLayout(); + + if (checkFile.exists()) + { + m_layoutWatcher->addPath(path); } }); @@ -331,6 +377,14 @@ auto VPMainWindow::LoadFile(QString path) -> bool VMainGraphicsView::NewSceneRect(m_graphicsView->scene(), m_graphicsView); + ui->actionRemoveWatermark->setEnabled(not m_layout->LayoutSettings().WatermarkPath().isEmpty()); + ui->actionEditCurrentWatermark->setEnabled(not m_layout->LayoutSettings().WatermarkPath().isEmpty()); + + if (not m_layout->LayoutSettings().WatermarkPath().isEmpty()) + { + m_layoutWatcher->addPath(m_layout->LayoutSettings().WatermarkPath()); + } + return true; } @@ -527,6 +581,12 @@ void VPMainWindow::SetupMenu() ui->menuSheet->addAction(redoAction); ui->toolBarUndoCommands->addAction(redoAction); + // Watermark + connect(ui->actionWatermarkEditor, &QAction::triggered, this, &VPMainWindow::CreateWatermark); + connect(ui->actionEditCurrentWatermark, &QAction::triggered, this, &VPMainWindow::EditCurrentWatermark); + connect(ui->actionLoadWatermark, &QAction::triggered, this, &VPMainWindow::LoadWatermark); + connect(ui->actionRemoveWatermark, &QAction::triggered, this, &VPMainWindow::RemoveWatermark); + // Window connect(ui->menuWindow, &QMenu::aboutToShow, this, [this]() { @@ -882,6 +942,17 @@ void VPMainWindow::InitPropertyTabTiles() VMainGraphicsView::NewSceneRect(m_graphicsView->scene(), m_graphicsView); } }); + + connect(ui->checkBoxTilesShowWatermark, &QCheckBox::toggled, this, [this](bool checked) + { + if (not m_layout.isNull()) + { + m_layout->LayoutSettings().SetShowWatermark(checked); + LayoutWasSaved(false); + m_graphicsView->RefreshLayout(); + VMainGraphicsView::NewSceneRect(m_graphicsView->scene(), m_graphicsView); + } + }); } //--------------------------------------------------------------------------------------------------------------------- @@ -1287,6 +1358,7 @@ void VPMainWindow::SetPropertyTabTilesData() ui->groupBoxTilesControl->setDisabled(false); SetCheckBoxValue(ui->checkBoxTilesShowTiles, m_layout->LayoutSettings().GetShowTiles()); + SetCheckBoxValue(ui->checkBoxTilesShowWatermark, m_layout->LayoutSettings().GetShowWatermark()); } else { @@ -2586,8 +2658,9 @@ void VPMainWindow::GeneratePdfTiledFile(const VPSheetPtr &sheet, bool showTilesS const int nbCol = m_layout->TileFactory()->ColNb(sheet); const int nbRow = m_layout->TileFactory()->RowNb(sheet); - QRectF source = QRectF(sheetRect.topLeft(), QSizeF(nbCol * ((width - VPTileFactory::tileStripeWidth) / xScale), - nbRow * ((height - VPTileFactory::tileStripeWidth) / yScale))); + QRectF source = QRectF(sheetRect.topLeft(), + QSizeF(nbCol * ((width - VPTileFactory::tileStripeWidth) / xScale), + nbRow * ((height - VPTileFactory::tileStripeWidth) / yScale))); QRectF target; if (tileOrientation != sheetOrientation) @@ -2610,6 +2683,25 @@ void VPMainWindow::GeneratePdfTiledFile(const VPSheetPtr &sheet, bool showTilesS sheet->SceneData()->Scene()->render(painter, VPrintLayout::SceneTargetRect(printer, target), source, Qt::KeepAspectRatio); + VWatermarkData watermarkData = m_layout->TileFactory()->WatermarkData(); + if (watermarkData.opacity > 0) + { + if (watermarkData.showImage && not watermarkData.path.isEmpty()) + { + VPTileFactory::PaintWatermarkImage(painter, target, watermarkData, + layout->LayoutSettings().WatermarkPath(), + layout->LayoutSettings().HorizontalScale(), + layout->LayoutSettings().VerticalScale()); + } + + if (watermarkData.showText && not watermarkData.text.isEmpty()) + { + VPTileFactory::PaintWatermarkText(painter, target, watermarkData, + layout->LayoutSettings().HorizontalScale(), + layout->LayoutSettings().VerticalScale()); + } + } + sheet->SceneData()->ClearTilesScheme(); firstPage = false; @@ -2648,6 +2740,42 @@ void VPMainWindow::UpdateScaleConnection() const ui->toolButtonScaleConnected->setIcon(icon); } +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::OpenWatermark(const QString &path) +{ + QList>::const_iterator i; + for (i = m_watermarkEditors.begin(); i != m_watermarkEditors.end(); ++i) + { + if (not (*i).isNull() && not (*i)->CurrentFile().isEmpty() + && (*i)->CurrentFile() == AbsoluteMPath(curFile, path)) + { + (*i)->show(); + return; + } + } + + auto *watermark = new WatermarkWindow(curFile, this); + connect(watermark, &WatermarkWindow::New, this, [this](){OpenWatermark();}); + connect(watermark, &WatermarkWindow::OpenAnother, this, [this](const QString &path){OpenWatermark(path);}); + m_watermarkEditors.append(watermark); + watermark->show(); + watermark->Open(path); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::CleanWaterkmarkEditors() +{ + QMutableListIterator> i(m_watermarkEditors); + while (i.hasNext()) + { + QPointer watermarkEditor = i.next(); + if (watermarkEditor.isNull()) + { + i.remove(); + } + } +} + //--------------------------------------------------------------------------------------------------------------------- void VPMainWindow::on_actionNew_triggered() { @@ -3715,6 +3843,67 @@ void VPMainWindow::on_actionPrintPreviewTiledSheet_triggered() } +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::CreateWatermark() +{ + CleanWaterkmarkEditors(); + OpenWatermark(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::EditCurrentWatermark() +{ + CleanWaterkmarkEditors(); + + QString watermarkFile = m_layout->LayoutSettings().WatermarkPath(); + if (not watermarkFile.isEmpty()) + { + OpenWatermark(watermarkFile); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::LoadWatermark() +{ + const QString filter(tr("Watermark files") + QLatin1String(" (*.vwm)")); + QString dir = QDir::homePath(); + qDebug("Run QFileDialog::getOpenFileName: dir = %s.", qUtf8Printable(dir)); + const QString filePath = QFileDialog::getOpenFileName(this, tr("Open file"), dir, filter, nullptr, + VAbstractApplication::VApp()->NativeFileDialog()); + if (filePath.isEmpty()) + { + return; + } + + m_layout->LayoutSettings().SetWatermarkPath(filePath); + LayoutWasSaved(false); + m_layout->TileFactory()->refreshTileInfos(); + m_graphicsView->RefreshLayout(); + ui->actionRemoveWatermark->setEnabled(true); + ui->actionEditCurrentWatermark->setEnabled(true); + + if (not m_layout->LayoutSettings().WatermarkPath().isEmpty()) + { + m_layoutWatcher->addPath(m_layout->LayoutSettings().WatermarkPath()); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPMainWindow::RemoveWatermark() +{ + m_layout->LayoutSettings().SetWatermarkPath(QString()); + LayoutWasSaved(false); + m_layout->TileFactory()->refreshTileInfos(); + m_graphicsView->RefreshLayout(); + ui->actionRemoveWatermark->setEnabled(false); + ui->actionEditCurrentWatermark->setEnabled(false); + + if (not m_layout->LayoutSettings().WatermarkPath().isEmpty()) + { + m_layoutWatcher->removePath(m_layout->LayoutSettings().WatermarkPath()); + } +} + //--------------------------------------------------------------------------------------------------------------------- #if defined(Q_OS_MAC) void VPMainWindow::AboutToShowDockMenu() diff --git a/src/app/puzzle/vpmainwindow.h b/src/app/puzzle/vpmainwindow.h index e0b43ad16..8f06c816e 100644 --- a/src/app/puzzle/vpmainwindow.h +++ b/src/app/puzzle/vpmainwindow.h @@ -55,6 +55,7 @@ class QFileSystemWatcher; template class QSharedPointer; class DialogPuzzlePreferences; struct VPExportData; +class WatermarkWindow; class VPMainWindow : public VAbstractMainWindow { @@ -273,6 +274,11 @@ private slots: void on_actionPrintTiledSheet_triggered(); void on_actionPrintPreviewTiledSheet_triggered(); + void CreateWatermark(); + void EditCurrentWatermark(); + void LoadWatermark(); + void RemoveWatermark(); + #if defined(Q_OS_MAC) void AboutToShowDockMenu(); #endif //defined(Q_OS_MAC) @@ -318,6 +324,10 @@ private: bool m_scaleConnected{true}; + QList> m_watermarkEditors{}; + + QFileSystemWatcher *m_watermarkWatcher{nullptr}; + /** * @brief InitMenuBar Inits the menu bar (File, Edit, Help ...) */ @@ -451,6 +461,9 @@ private: bool &firstPage); void UpdateScaleConnection() const; + + void OpenWatermark(const QString &path = QString()); + void CleanWaterkmarkEditors(); }; #endif // VPMAINWINDOW_H diff --git a/src/app/puzzle/vpmainwindow.ui b/src/app/puzzle/vpmainwindow.ui index c52e42942..901464d48 100644 --- a/src/app/puzzle/vpmainwindow.ui +++ b/src/app/puzzle/vpmainwindow.ui @@ -95,9 +95,20 @@ + + + Watermark + + + + + + + + @@ -208,7 +219,7 @@ QTabWidget::Rounded - 3 + 2 @@ -1520,6 +1531,16 @@ + + + + Show watermark preview + + + Show watermark + + + @@ -2243,6 +2264,59 @@ QAction::ApplicationSpecificRole + + + + + + Editor + + + Create or edit a watermark + + + QAction::ApplicationSpecificRole + + + + + false + + + Edit current + + + QAction::ApplicationSpecificRole + + + + + true + + + + + + Load + + + QAction::ApplicationSpecificRole + + + + + false + + + + + + Remove + + + QAction::ApplicationSpecificRole + + @@ -2270,8 +2344,8 @@ + - diff --git a/src/app/puzzle/vpsettings.cpp b/src/app/puzzle/vpsettings.cpp index af9f5787b..f7da12b33 100644 --- a/src/app/puzzle/vpsettings.cpp +++ b/src/app/puzzle/vpsettings.cpp @@ -43,6 +43,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutTileMargins, (QLatin1Strin Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutSheetIgnoreMargins, (QLatin1String("layout/sheetIgnoreMargins"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutTileIgnoreMargins, (QLatin1String("layout/tileIgnoreMargins"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutTileShowTiles, (QLatin1String("layout/tileShowTiles"))) +Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutTileShowWatermark, (QLatin1String("layout/tileShowWatermark"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutWarningPiecesSuperposition, (QLatin1String("layout/warningPiecesSuperposition"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutStickyEdges, (QLatin1String("layout/stickyEdges"))) @@ -226,6 +227,18 @@ auto VPSettings::GetLayoutTileShowTiles() const -> bool return value(*settingLayoutTileShowTiles, true).toBool(); } +//--------------------------------------------------------------------------------------------------------------------- +void VPSettings::SetLayoutTileShowWatermark(bool value) +{ + setValue(*settingLayoutTileShowWatermark, value); +} + +//--------------------------------------------------------------------------------------------------------------------- +auto VPSettings::GetLayoutTileShowWatermark() const -> bool +{ + return value(*settingLayoutTileShowWatermark, false).toBool(); +} + //--------------------------------------------------------------------------------------------------------------------- void VPSettings::SetLayoutWarningPiecesSuperposition(bool value) { diff --git a/src/app/puzzle/vpsettings.h b/src/app/puzzle/vpsettings.h index 75f589b6d..032f86a7f 100644 --- a/src/app/puzzle/vpsettings.h +++ b/src/app/puzzle/vpsettings.h @@ -78,6 +78,9 @@ public: void SetLayoutTileShowTiles(bool value); auto GetLayoutTileShowTiles() const -> bool; + void SetLayoutTileShowWatermark(bool value); + auto GetLayoutTileShowWatermark() const -> bool; + void SetLayoutWarningPiecesSuperposition(bool value); auto GetLayoutWarningPiecesSuperposition() const -> bool; diff --git a/src/app/puzzle/vptilefactory.cpp b/src/app/puzzle/vptilefactory.cpp index e17a3784e..7de5e03f3 100644 --- a/src/app/puzzle/vptilefactory.cpp +++ b/src/app/puzzle/vptilefactory.cpp @@ -12,8 +12,78 @@ namespace { const QColor tileColor(180, 180, 180); + +//--------------------------------------------------------------------------------------------------------------------- +auto Grayscale(QImage image) -> QImage +{ + for (int ii = 0; ii < image.height(); ii++) + { + uchar* scan = image.scanLine(ii); + int depth = 4; + for (int jj = 0; jj < image.width(); jj++) + { + QRgb* rgbpixel = reinterpret_cast(scan + jj * depth); + int gray = qGray(*rgbpixel); + *rgbpixel = QColor(gray, gray, gray, qAlpha(*rgbpixel)).rgba(); + } + } + + return image; } +//--------------------------------------------------------------------------------------------------------------------- +auto WatermarkImageFromCache(const VWatermarkData &watermarkData, const QString &watermarkPath, qreal xScale, + qreal yScale, QString &error) -> QPixmap +{ + const qreal opacity = watermarkData.opacity/100.; + QPixmap pixmap; + QString imagePath = AbsoluteMPath(watermarkPath, watermarkData.path); + QString imageCacheKey = QString("puzzle=path%1+opacity%2+rotation%3+grayscale%4+xscale%5+yxcale%6") + .arg(imagePath, QString::number(opacity), QString::number(watermarkData.imageRotation), + watermarkData.grayscale ? trueStr : falseStr ).arg(xScale).arg(yScale); + + if (not QPixmapCache::find(imageCacheKey, &pixmap)) + { + QImageReader imageReader(imagePath); + QImage watermark = imageReader.read(); + if (watermark.isNull()) + { + error = imageReader.errorString(); + return pixmap; + } + + if (watermarkData.grayscale) + { + watermark = Grayscale(watermark); + } + + // Workaround for QGraphicsPixmapItem opacity problem. + // Opacity applied only if use a cached pixmap and only after first draw. First image always has opacity 1. + // Preparing an image manually allows to avoid the problem. + QSize scaledSize(qRound(watermark.width() * xScale), qRound(watermark.height() * yScale)); + QImage tmp(scaledSize, watermark.format()); + tmp = tmp.convertToFormat(QImage::Format_ARGB32); + tmp.fill(Qt::transparent); + + QPainter p(&tmp); + p.setOpacity(opacity); + + QTransform t; + t.translate(tmp.width()/2., tmp.height()/2.); + t.rotate(-watermarkData.imageRotation); + t.translate(-tmp.width()/2., -tmp.height()/2.); + p.setTransform(t); + + p.drawImage(QRectF(QPointF(), scaledSize), watermark); + + pixmap = QPixmap::fromImage(tmp); + + QPixmapCache::insert(imageCacheKey, pixmap); + } + return pixmap; +} +} // namespace + //--------------------------------------------------------------------------------------------------------------------- VPTileFactory::VPTileFactory(const VPLayoutPtr &layout, VCommonSettings *commonSettings): m_layout(layout), @@ -45,6 +115,8 @@ void VPTileFactory::refreshTileInfos() { m_drawingAreaWidth -= tilesMargins.left() + tilesMargins.right(); } + + m_watermarkData = layout->WatermarkData(); } } @@ -229,6 +301,7 @@ void VPTileFactory::drawTile(QPainter *painter, QPrinter *printer, const VPSheet } DrawRuler(painter); + DrawWatermark(painter); if(col < nbCol-1) { @@ -357,6 +430,12 @@ auto VPTileFactory::DrawingAreaWidth() const -> qreal return m_drawingAreaWidth; } +//--------------------------------------------------------------------------------------------------------------------- +auto VPTileFactory::WatermarkData() const -> const VWatermarkData & +{ + return m_watermarkData; +} + //--------------------------------------------------------------------------------------------------------------------- void VPTileFactory::DrawRuler(QPainter *painter) { @@ -410,3 +489,143 @@ void VPTileFactory::DrawRuler(QPainter *painter) painter->restore(); } + +//--------------------------------------------------------------------------------------------------------------------- +void VPTileFactory::DrawWatermark(QPainter *painter) +{ + SCASSERT(painter != nullptr) + + VPLayoutPtr layout = m_layout.toStrongRef(); + if(layout.isNull()) + { + return; + } + + if (m_watermarkData.opacity > 0) + { + QRectF img(0, 0, + m_drawingAreaWidth - tileStripeWidth, + m_drawingAreaHeight - tileStripeWidth); + + if (m_watermarkData.showImage && not m_watermarkData.path.isEmpty()) + { + PaintWatermarkImage(painter, img, m_watermarkData, + layout->LayoutSettings().WatermarkPath(), + layout->LayoutSettings().HorizontalScale(), + layout->LayoutSettings().VerticalScale()); + } + + if (m_watermarkData.showText && not m_watermarkData.text.isEmpty()) + { + PaintWatermarkText(painter, img, m_watermarkData, + layout->LayoutSettings().HorizontalScale(), + layout->LayoutSettings().VerticalScale()); + } + } +} + + +//--------------------------------------------------------------------------------------------------------------------- +void VPTileFactory::PaintWatermarkText(QPainter *painter, const QRectF &img, const VWatermarkData &watermarkData, + qreal xScale, qreal yScale) +{ + SCASSERT(painter != nullptr) + + painter->save(); + + painter->setOpacity(watermarkData.opacity/100.); + + QPen pen = painter->pen(); + pen.setWidth(1); + pen.setColor(watermarkData.textColor); + pen.setStyle(Qt::SolidLine); + painter->setPen(pen); + + painter->setBrush(watermarkData.textColor); + + QTransform t; + t.translate(img.center().x(), img.center().y()); + t.rotate(-watermarkData.textRotation); + t.translate(-img.center().x(), -img.center().y()); + t.scale(xScale, yScale); + + QPainterPath text; + text.addText(img.center(), watermarkData.font, watermarkData.text); + + text = t.map(text); + + QPointF center = img.center() - text.boundingRect().center(); + t = QTransform(); + t.translate(center.x(), center.y()); + + text = t.map(text); + + painter->drawPath(text); + + painter->restore(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VPTileFactory::PaintWatermarkImage(QPainter *painter, const QRectF &img, const VWatermarkData &watermarkData, + const QString &watermarkPath, qreal xScale, qreal yScale) +{ + SCASSERT(painter != nullptr) + + auto BrokenImage = [img, watermarkData, watermarkPath]() + { + const qreal opacity = watermarkData.opacity/100.; + QPixmap watermark; + QString imagePath = QString("puzzle=path%1+opacity%2_broken") + .arg(AbsoluteMPath(watermarkPath, watermarkData.path), QString::number(opacity)); + + if (not QPixmapCache::find(imagePath, &watermark)) + { + QScopedPointer svgRenderer(new QSvgRenderer()); + + QRect imageRect(0, 0, qRound(img.width()/4.), qRound(img.width()/4.)); + watermark = QPixmap(imageRect.size()); + watermark.fill(Qt::transparent); + + QPainter imagePainter(&watermark); + imagePainter.setOpacity(opacity); + + svgRenderer->load(QStringLiteral("://puzzleicon/svg/no_watermark_image.svg")); + svgRenderer->render(&imagePainter, imageRect); + + QPixmapCache::insert(imagePath, watermark); + + return watermark; + } + + return watermark; + }; + + QPixmap watermark; + QFileInfo f(watermarkData.path); + if (f.suffix() == "png" || f.suffix() == "jpg" || f.suffix() == "jpeg" || f.suffix() == "bmp") + { + QString error; + watermark = WatermarkImageFromCache(watermarkData, watermarkPath, xScale, yScale, error); + + if (watermark.isNull()) + { + watermark = BrokenImage(); + } + } + else + { + watermark = BrokenImage(); + } + + if (watermark.width() < img.width() && watermark.height() < img.height()) + { + QRect imagePosition(0, 0, watermark.width(), watermark.height()); + imagePosition.translate(img.center().toPoint() - imagePosition.center()); + + painter->drawPixmap(imagePosition, watermark); + } + else + { + painter->drawPixmap(img.toRect(), watermark); + } +} diff --git a/src/app/puzzle/vptilefactory.h b/src/app/puzzle/vptilefactory.h index 263504629..55484e2a6 100644 --- a/src/app/puzzle/vptilefactory.h +++ b/src/app/puzzle/vptilefactory.h @@ -33,6 +33,7 @@ #include #include "layout/vplayout.h" +#include "../ifc/ifcdef.h" class QGraphicsScene; class VCommonSettings; @@ -90,6 +91,13 @@ public: */ static constexpr qreal tileStripeWidth = UnitConvertor(1, Unit::Cm, Unit::Px); + auto WatermarkData() const -> const VWatermarkData &; + + static void PaintWatermarkText(QPainter *painter, const QRectF &img, const VWatermarkData &watermarkData, + qreal xScale = 1.0, qreal yScale = 1.0); + static void PaintWatermarkImage(QPainter *painter, const QRectF &img, const VWatermarkData &watermarkData, + const QString &watermarkPath, qreal xScale = 1.0, qreal yScale = 1.0); + private: Q_DISABLE_COPY(VPTileFactory) @@ -106,8 +114,10 @@ private: */ qreal m_drawingAreaWidth{0}; - void DrawRuler(QPainter *painter); + VWatermarkData m_watermarkData{}; + void DrawRuler(QPainter *painter); + void DrawWatermark(QPainter *painter); }; #endif // VPTILEFACTORY_H diff --git a/src/app/puzzle/xml/vplayoutfilereader.cpp b/src/app/puzzle/xml/vplayoutfilereader.cpp index 3e39667ab..a7e277cba 100644 --- a/src/app/puzzle/xml/vplayoutfilereader.cpp +++ b/src/app/puzzle/xml/vplayoutfilereader.cpp @@ -243,7 +243,8 @@ void VPLayoutFileReader::ReadProperties(const VPLayoutPtr &layout) ML::TagDescription, // 2 ML::TagControl, // 3 ML::TagTiles, // 4 - ML::TagScale // 5 + ML::TagScale, // 5 + ML::TagWatermark // 6 }; while (readNextStartElement()) @@ -276,6 +277,10 @@ void VPLayoutFileReader::ReadProperties(const VPLayoutPtr &layout) qDebug("read scale"); ReadScale(layout); break; + case 6: // watermark + qDebug("read watermark"); + ReadWatermark(layout); + break; default: qCDebug(MLReader, "Ignoring tag %s", qUtf8Printable(name().toString())); skipCurrentElement(); @@ -818,6 +823,16 @@ auto VPLayoutFileReader::ReadLabelLine() -> TextLine return line; } +//--------------------------------------------------------------------------------------------------------------------- +void VPLayoutFileReader::ReadWatermark(const VPLayoutPtr &layout) +{ + AssertRootTag(ML::TagWatermark); + + QXmlStreamAttributes attribs = attributes(); + layout->LayoutSettings().SetShowWatermark(ReadAttributeBool(attribs, ML::AttrShowPreview, falseStr)); + layout->LayoutSettings().SetWatermarkPath(readElementText()); +} + //--------------------------------------------------------------------------------------------------------------------- void VPLayoutFileReader::ReadLayoutMargins(const VPLayoutPtr &layout) { diff --git a/src/app/puzzle/xml/vplayoutfilereader.h b/src/app/puzzle/xml/vplayoutfilereader.h index f87a9f855..e2a25b5c6 100644 --- a/src/app/puzzle/xml/vplayoutfilereader.h +++ b/src/app/puzzle/xml/vplayoutfilereader.h @@ -75,6 +75,7 @@ private: void ReadPatternLabel(const VPPiecePtr &piece); auto ReadLabelLines() -> VTextManager; auto ReadLabelLine() -> TextLine; + void ReadWatermark(const VPLayoutPtr &layout); void ReadLayoutMargins(const VPLayoutPtr &layout); void ReadSheetMargins(const VPSheetPtr &sheet); diff --git a/src/app/puzzle/xml/vplayoutfilewriter.cpp b/src/app/puzzle/xml/vplayoutfilewriter.cpp index 7a174d8f9..b5adcbd37 100644 --- a/src/app/puzzle/xml/vplayoutfilewriter.cpp +++ b/src/app/puzzle/xml/vplayoutfilewriter.cpp @@ -184,6 +184,12 @@ void VPLayoutFileWriter::WriteLayoutProperties(const VPLayoutPtr &layout) SetAttribute(ML::AttrYScale, layout->LayoutSettings().VerticalScale()); writeEndElement(); // scale + writeStartElement(ML::TagWatermark); + SetAttributeOrRemoveIf(ML::AttrShowPreview, layout->LayoutSettings().GetShowWatermark(), + [](bool show){return not show;}); + writeCharacters(layout->LayoutSettings().WatermarkPath()); + writeEndElement(); // watermark + writeEndElement(); // properties } diff --git a/src/app/puzzle/xml/vplayoutliterals.cpp b/src/app/puzzle/xml/vplayoutliterals.cpp index e1266931d..addd45cb5 100644 --- a/src/app/puzzle/xml/vplayoutliterals.cpp +++ b/src/app/puzzle/xml/vplayoutliterals.cpp @@ -59,6 +59,7 @@ const QString TagPatternLabel = QStringLiteral("patternLabel"); const QString TagLines = QStringLiteral("lines"); const QString TagLine = QStringLiteral("line"); const QString TagScale = QStringLiteral("scale"); +const QString TagWatermark = QStringLiteral("watermark"); const QString AttrVersion = QStringLiteral("version"); const QString AttrWarningSuperposition = QStringLiteral("warningSuperposition"); @@ -102,6 +103,7 @@ const QString AttrGrainlineType = QStringLiteral("grainlineType"); const QString AttrXScale = QStringLiteral("xScale"); const QString AttrYScale = QStringLiteral("yScale"); const QString AttrIgnoreMargins = QStringLiteral("ignoreMargins"); +const QString AttrShowPreview = QStringLiteral("showPreview"); const QString atFrontStr = QStringLiteral("atFront"); const QString atRearStr = QStringLiteral("atRear"); diff --git a/src/app/puzzle/xml/vplayoutliterals.h b/src/app/puzzle/xml/vplayoutliterals.h index be1cea44f..7a05f7020 100644 --- a/src/app/puzzle/xml/vplayoutliterals.h +++ b/src/app/puzzle/xml/vplayoutliterals.h @@ -64,6 +64,7 @@ extern const QString TagPatternLabel; extern const QString TagLines; extern const QString TagLine; extern const QString TagScale; +extern const QString TagWatermark; extern const QString AttrVersion; extern const QString AttrWarningSuperposition; @@ -107,6 +108,7 @@ extern const QString AttrGrainlineType; extern const QString AttrXScale; extern const QString AttrYScale; extern const QString AttrIgnoreMargins; +extern const QString AttrShowPreview; extern const QString atFrontStr; extern const QString atRearStr; diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 05f0e8971..65f611438 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -65,7 +65,7 @@ #include "../qmuparser/qmuparsererror.h" #include "../vtools/dialogs/support/dialogeditlabel.h" #include "../vformat/vpatternrecipe.h" -#include "watermarkwindow.h" +#include "../vlayout/dialogs/watermarkwindow.h" #include "../vmisc/backport/qoverload.h" #include "../vlayout/vlayoutexporter.h" #include "../vwidgets/vgraphicssimpletextitem.h" diff --git a/src/app/valentina/mainwindow.ui b/src/app/valentina/mainwindow.ui index 4e7b7b612..21deed217 100644 --- a/src/app/valentina/mainwindow.ui +++ b/src/app/valentina/mainwindow.ui @@ -1119,7 +1119,7 @@ 0 - -53 + 0 126 237 @@ -1713,7 +1713,7 @@ 0 0 1100 - 21 + 22 @@ -3017,7 +3017,7 @@ - false + true diff --git a/src/app/valentina/valentina.pri b/src/app/valentina/valentina.pri index 32fa0d9dd..ee92ea3db 100644 --- a/src/app/valentina/valentina.pri +++ b/src/app/valentina/valentina.pri @@ -10,8 +10,7 @@ include(core/core.pri) SOURCES += \ $$PWD/main.cpp \ $$PWD/mainwindow.cpp \ - $$PWD/mainwindowsnogui.cpp \ - $$PWD/watermarkwindow.cpp + $$PWD/mainwindowsnogui.cpp *msvc*:SOURCES += $$PWD/stable.cpp @@ -20,10 +19,8 @@ HEADERS += \ $$PWD/mainwindow.h \ $$PWD/stable.h \ $$PWD/version.h \ - $$PWD/mainwindowsnogui.h \ - $$PWD/watermarkwindow.h + $$PWD/mainwindowsnogui.h # Main forms FORMS += \ - $$PWD/mainwindow.ui \ - $$PWD/watermarkwindow.ui + $$PWD/mainwindow.ui diff --git a/src/app/valentina/valentina.pro b/src/app/valentina/valentina.pro index 1a77bfac3..097031c58 100644 --- a/src/app/valentina/valentina.pro +++ b/src/app/valentina/valentina.pro @@ -588,6 +588,15 @@ DEPENDPATH += $$PWD/../../libs/vtools win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vtools/$${DESTDIR}/vtools.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vtools/$${DESTDIR}/libvtools.a +# VLayout static library (depend on IFC, VGeometry) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vlayout/$${DESTDIR}/ -lvlayout + +INCLUDEPATH += $$PWD/../../libs/vlayout +DEPENDPATH += $$PWD/../../libs/vlayout + +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/vlayout.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/libvlayout.a + #VWidgets static library unix|win32: LIBS += -L$$OUT_PWD/../../libs/vwidgets/$${DESTDIR}/ -lvwidgets @@ -606,7 +615,7 @@ DEPENDPATH += $$PWD/../../libs/vformat win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vformat/$${DESTDIR}/vformat.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vformat/$${DESTDIR}/libvformat.a -#VPatternDB static library (depend on vgeometry, vmisc, VLayout) +#VPatternDB static library (depend on vgeometry, vmisc) unix|win32: LIBS += -L$$OUT_PWD/../../libs/vpatterndb/$${DESTDIR} -lvpatterndb INCLUDEPATH += $$PWD/../../libs/vpatterndb @@ -615,15 +624,6 @@ DEPENDPATH += $$PWD/../../libs/vpatterndb win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vpatterndb/$${DESTDIR}/vpatterndb.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vpatterndb/$${DESTDIR}/libvpatterndb.a -# VLayout static library (depend on IFC, VGeometry) -unix|win32: LIBS += -L$$OUT_PWD/../../libs/vlayout/$${DESTDIR}/ -lvlayout - -INCLUDEPATH += $$PWD/../../libs/vlayout -DEPENDPATH += $$PWD/../../libs/vlayout - -win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/vlayout.lib -else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/libvlayout.a - # VGeometry static library (depend on ifc) unix|win32: LIBS += -L$$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/ -lvgeometry diff --git a/src/app/valentina/xml/vpattern.cpp b/src/app/valentina/xml/vpattern.cpp index bdaffe4f4..1cbd3da6b 100644 --- a/src/app/valentina/xml/vpattern.cpp +++ b/src/app/valentina/xml/vpattern.cpp @@ -966,7 +966,7 @@ void VPattern::ParseDetailInternals(const QDomElement &domElement, VPiece &detai if (version == 1) { // TODO. Delete if minimal supported version is 0.4.0 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 4, 0), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 4, 0), "Time to refactor the code."); const bool closed = GetParametrUInt(domElement, AttrClosed, QChar('1')); const qreal width = GetParametrDouble(domElement, AttrWidth, QStringLiteral("0.0")); diff --git a/src/app/valentina/xml/vpattern.h b/src/app/valentina/xml/vpattern.h index c99c393f0..8e598eb2a 100644 --- a/src/app/valentina/xml/vpattern.h +++ b/src/app/valentina/xml/vpattern.h @@ -214,7 +214,7 @@ private: void ParseToolTrueDarts(VMainGraphicsScene *scene, const QDomElement &domElement, const Document &parse); // TODO. Delete if minimal supported version is 0.2.7 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 2, 7), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 2, 7), "Time to refactor the code."); void ParseOldToolSpline(VMainGraphicsScene *scene, QDomElement &domElement, const Document &parse); @@ -222,7 +222,7 @@ private: void ParseToolCubicBezier(VMainGraphicsScene *scene, const QDomElement &domElement, const Document &parse); // TODO. Delete if minimal supported version is 0.2.7 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 2, 7), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 2, 7), "Time to refactor the code."); void ParseOldToolSplinePath(VMainGraphicsScene *scene, QDomElement &domElement, const Document &parse); diff --git a/src/libs/ifc/ifcdef.h b/src/libs/ifc/ifcdef.h index 7a51d80b5..e37920783 100644 --- a/src/libs/ifc/ifcdef.h +++ b/src/libs/ifc/ifcdef.h @@ -35,6 +35,7 @@ # include #endif /*Q_OS_WIN*/ +#include #include #include #include @@ -262,6 +263,8 @@ struct VWatermarkData QString path{}; int imageRotation{0}; bool grayscale{false}; + bool invalidFile{false}; + QColor textColor{Qt::black}; }; QT_WARNING_POP diff --git a/src/libs/ifc/schema.qrc b/src/libs/ifc/schema.qrc index 16a95db94..96f6d0b17 100644 --- a/src/libs/ifc/schema.qrc +++ b/src/libs/ifc/schema.qrc @@ -82,6 +82,7 @@ schema/individual_measurements/v0.5.1.xsd schema/label_template/v1.0.0.xsd schema/watermark/v1.0.0.xsd + schema/watermark/v1.1.0.xsd schema/layout/v0.1.0.xsd diff --git a/src/libs/ifc/schema/layout/v0.1.0.xsd b/src/libs/ifc/schema/layout/v0.1.0.xsd index b0cc63860..7dfcdd9a4 100644 --- a/src/libs/ifc/schema/layout/v0.1.0.xsd +++ b/src/libs/ifc/schema/layout/v0.1.0.xsd @@ -46,6 +46,15 @@ + + + + + + + + + diff --git a/src/libs/ifc/schema/watermark/v1.1.0.xsd b/src/libs/ifc/schema/watermark/v1.1.0.xsd new file mode 100644 index 000000000..963ba099d --- /dev/null +++ b/src/libs/ifc/schema/watermark/v1.1.0.xsd @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libs/ifc/xml/vwatermarkconverter.cpp b/src/libs/ifc/xml/vwatermarkconverter.cpp index 04bc4d983..d5453637a 100644 --- a/src/libs/ifc/xml/vwatermarkconverter.cpp +++ b/src/libs/ifc/xml/vwatermarkconverter.cpp @@ -27,6 +27,8 @@ *************************************************************************/ #include "vwatermarkconverter.h" +#include + /* * Version rules: * 1. Version have three parts "major.minor.patch"; @@ -36,8 +38,8 @@ */ const QString VWatermarkConverter::WatermarkMinVerStr = QStringLiteral("1.0.0"); -const QString VWatermarkConverter::WatermarkMaxVerStr = QStringLiteral("1.0.0"); -const QString VWatermarkConverter::CurrentSchema = QStringLiteral("://schema/watermark/v1.0.0.xsd"); +const QString VWatermarkConverter::WatermarkMaxVerStr = QStringLiteral("1.1.0"); +const QString VWatermarkConverter::CurrentSchema = QStringLiteral("://schema/watermark/v1.1.0.xsd"); //VWatermarkConverter::WatermarkMinVer; // <== DON'T FORGET TO UPDATE TOO!!!! //VWatermarkConverter::WatermarkMaxVer; // <== DON'T FORGET TO UPDATE TOO!!!! @@ -77,15 +79,18 @@ QString VWatermarkConverter::MaxVerStr() const //--------------------------------------------------------------------------------------------------------------------- QString VWatermarkConverter::XSDSchema(int ver) const { - switch (ver) + QHash schemas = { - case (0x010000): - return CurrentSchema; - default: - InvalidVersion(ver); - break; + std::make_pair(FormatVersion(1, 0, 0), QStringLiteral("://schema/watermark/v1.0.0.xsd")), + std::make_pair(FormatVersion(1, 1, 0), CurrentSchema) + }; + + if (schemas.contains(ver)) + { + return schemas.value(ver); } - return QString();//unreachable code + + InvalidVersion(ver); } //--------------------------------------------------------------------------------------------------------------------- @@ -93,11 +98,14 @@ void VWatermarkConverter::ApplyPatches() { switch (m_ver) { - case (0x010000): + case (FormatVersion(1, 0, 0)): + ToV1_1_0(); + ValidateXML(XSDSchema(FormatVersion(1, 1, 0))); + Q_FALLTHROUGH(); + case (FormatVersion(1, 1, 0)): break; default: InvalidVersion(m_ver); - break; } } @@ -107,3 +115,14 @@ void VWatermarkConverter::DowngradeToCurrentMaxVersion() SetVersion(WatermarkMaxVerStr); Save(); } + +//--------------------------------------------------------------------------------------------------------------------- +void VWatermarkConverter::ToV1_1_0() +{ + // TODO. Delete if minimal supported version is 1.1.0 + Q_STATIC_ASSERT_X(VWatermarkConverter::WatermarkMinVer < FormatVersion(1, 1, 0), + "Time to refactor the code."); + + SetVersion(QStringLiteral("1.1.0")); + Save(); +} diff --git a/src/libs/ifc/xml/vwatermarkconverter.h b/src/libs/ifc/xml/vwatermarkconverter.h index 207e66cf2..0d4770c73 100644 --- a/src/libs/ifc/xml/vwatermarkconverter.h +++ b/src/libs/ifc/xml/vwatermarkconverter.h @@ -39,7 +39,7 @@ public: static const QString WatermarkMaxVerStr; static const QString CurrentSchema; static Q_DECL_CONSTEXPR const int WatermarkMinVer = FormatVersion(1, 0, 0); - static Q_DECL_CONSTEXPR const int WatermarkMaxVer = FormatVersion(1, 0, 0); + static Q_DECL_CONSTEXPR const int WatermarkMaxVer = FormatVersion(1, 1, 0); protected: virtual int MinVer() const override; @@ -57,6 +57,8 @@ protected: private: Q_DISABLE_COPY(VWatermarkConverter) static const QString WatermarkMinVerStr; + + void ToV1_1_0(); }; #endif // VWATERMARKCONVERTER_H diff --git a/src/libs/vformat/vwatermark.cpp b/src/libs/vformat/vwatermark.cpp index 19b9c6b0c..e7a91e241 100644 --- a/src/libs/vformat/vwatermark.cpp +++ b/src/libs/vformat/vwatermark.cpp @@ -28,6 +28,7 @@ #include "vwatermark.h" #include "../vmisc/projectversion.h" +#include "../ifc/xml/vwatermarkconverter.h" const QString VWatermark::TagWatermark = QStringLiteral("watermark"); const QString VWatermark::TagText = QStringLiteral("text"); @@ -61,7 +62,7 @@ void VWatermark::CreateEmptyWatermark() QDomElement wElement = this->createElement(TagWatermark); wElement.appendChild(createComment(FileComment())); - wElement.appendChild(CreateElementWithText(TagVersion, "1.0.0" /*VWatermarkConverter::WatermarkMaxVerStr*/)); + wElement.appendChild(CreateElementWithText(TagVersion, VWatermarkConverter::WatermarkMaxVerStr)); wElement.appendChild(createElement(TagText)); wElement.appendChild(createElement(TagImage)); @@ -102,6 +103,12 @@ VWatermarkData VWatermark::GetWatermark() const data.text = GetParametrEmptyString(text, AttrText); data.textRotation = GetParametrInt(text, AttrRotation, QChar('0')); data.font.fromString(GetParametrEmptyString(text, AttrFont)); + QColor color(GetParametrString(text, AttrColor, QColor(Qt::black).name())); + if (not color.isValid()) + { + color = Qt::black; + } + data.textColor = color; } QDomElement image = rootElement.firstChildElement(TagImage); @@ -136,6 +143,7 @@ void VWatermark::SetWatermark(const VWatermarkData &data) [](int textRotation){return textRotation == 0;}); SetAttributeOrRemoveIf(text, AttrFont, data.font.toString(), [](const QString &fontString){return fontString.isEmpty();}); + SetAttribute(text, AttrColor, data.textColor.name()); } QDomElement image = rootElement.firstChildElement(TagImage); diff --git a/src/libs/vlayout/dialogs/dialogs.pri b/src/libs/vlayout/dialogs/dialogs.pri index 98b4202d8..bd2234f7f 100644 --- a/src/libs/vlayout/dialogs/dialogs.pri +++ b/src/libs/vlayout/dialogs/dialogs.pri @@ -3,11 +3,14 @@ HEADERS += \ $$PWD/dialoglayoutscale.h \ - $$PWD/vabstractlayoutdialog.h + $$PWD/vabstractlayoutdialog.h \ + $$PWD/watermarkwindow.h SOURCES += \ $$PWD/dialoglayoutscale.cpp \ - $$PWD/vabstractlayoutdialog.cpp + $$PWD/vabstractlayoutdialog.cpp \ + $$PWD/watermarkwindow.cpp FORMS += \ - $$PWD/dialoglayoutscale.ui + $$PWD/dialoglayoutscale.ui \ + $$PWD/watermarkwindow.ui diff --git a/src/app/valentina/watermarkwindow.cpp b/src/libs/vlayout/dialogs/watermarkwindow.cpp similarity index 91% rename from src/app/valentina/watermarkwindow.cpp rename to src/libs/vlayout/dialogs/watermarkwindow.cpp index 061656423..fcd1525ac 100644 --- a/src/app/valentina/watermarkwindow.cpp +++ b/src/libs/vlayout/dialogs/watermarkwindow.cpp @@ -34,10 +34,11 @@ #include #include #include +#include #include #include "../vmisc/def.h" -#include "core/vapplication.h" +#include "../vmisc/vabstractapplication.h" #include "../vpropertyexplorer/checkablemessagebox.h" #include "../ifc/exception/vexception.h" #include "../ifc/xml/vwatermarkconverter.h" @@ -109,6 +110,31 @@ WatermarkWindow::WatermarkWindow(const QString &patternPath, QWidget *parent) : connect(ui->groupBoxWatermarkText, &QGroupBox::toggled, this, [this](){WatermarkChangesWereSaved(false);}); connect(ui->groupBoxWatermarkImage, &QGroupBox::toggled, this, [this](){WatermarkChangesWereSaved(false);}); + + ui->pushButtonColorPicker->insertColor(Qt::black, tr("Black", "color")); + ui->pushButtonColorPicker->insertColor(Qt::red, tr("Red", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkRed, tr("Dark red", "color")); + ui->pushButtonColorPicker->insertColor(Qt::green, tr("Green", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkGreen, tr("Dark green", "color")); + ui->pushButtonColorPicker->insertColor(Qt::blue, tr("Blue", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkBlue, tr("Dark blue", "color")); + ui->pushButtonColorPicker->insertColor(Qt::cyan, tr("Cyan", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkCyan, tr("Dark cyan", "color")); + ui->pushButtonColorPicker->insertColor(Qt::magenta, tr("Magenta", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkMagenta, tr("Dark magenta", "color")); + ui->pushButtonColorPicker->insertColor(Qt::yellow, tr("Yellow", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkYellow, tr("Dark yellow", "color")); + ui->pushButtonColorPicker->insertColor(Qt::gray, tr("Gray", "color")); + ui->pushButtonColorPicker->insertColor(Qt::darkGray, tr("Dark gray", "color")); + ui->pushButtonColorPicker->insertColor(Qt::lightGray, tr("Light gray", "color")); + + QVector colors = VAbstractApplication::VApp()->Settings()->GetWatermarkCustomColors(); + for (const auto& color : colors) + { + ui->pushButtonColorPicker->insertColor(color); + } + + connect(ui->pushButtonColorPicker, &QtColorPicker::colorChanged, this, [this](){WatermarkChangesWereSaved(false);}); } //--------------------------------------------------------------------------------------------------------------------- @@ -242,7 +268,7 @@ void WatermarkWindow::showEvent(QShowEvent *event) } // do your init stuff here - QSize sz = VAbstractValApplication::VApp()->ValentinaSettings()->GetWatermarkEditorSize(); + QSize sz = VAbstractApplication::VApp()->Settings()->GetWatermarkEditorSize(); if (sz.isEmpty() == false) { resize(sz); @@ -259,7 +285,7 @@ void WatermarkWindow::resizeEvent(QResizeEvent *event) // window creating, which would if (m_isInitialized) { - VAbstractValApplication::VApp()->ValentinaSettings()->SetWatermarkEditorSize(size()); + VAbstractApplication::VApp()->Settings()->SetWatermarkEditorSize(size()); } QMainWindow::resizeEvent(event); } @@ -584,7 +610,7 @@ bool WatermarkWindow::ContinueFormatRewrite(const QString ¤tFormatVersion, } //--------------------------------------------------------------------------------------------------------------------- -bool WatermarkWindow::SaveWatermark(const QString &fileName, QString &error) +auto WatermarkWindow::SaveWatermark(const QString &fileName, QString &error) -> bool { m_data.opacity = ui->spinBoxOpacity->value(); m_data.showText = ui->groupBoxWatermarkText->isChecked(); @@ -594,6 +620,7 @@ bool WatermarkWindow::SaveWatermark(const QString &fileName, QString &error) m_data.path = RelativeMPath(fileName, ui->lineEditPath->text()); m_data.imageRotation = ui->spinBoxImageRotation->value(); m_data.grayscale = ui->checkBoxGrayColor->isChecked(); + m_data.textColor = ui->pushButtonColorPicker->currentColor(); VWatermark doc; doc.CreateEmptyWatermark(); @@ -605,6 +632,8 @@ bool WatermarkWindow::SaveWatermark(const QString &fileName, QString &error) SetCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 5000); WatermarkChangesWereSaved(result); + + VAbstractApplication::VApp()->Settings()->SetWatermarkCustomColors(ui->pushButtonColorPicker->CustomColors()); } return result; } @@ -717,6 +746,10 @@ void WatermarkWindow::ShowWatermark() ui->checkBoxGrayColor->blockSignals(true); ui->checkBoxGrayColor->setChecked(m_data.grayscale); ui->checkBoxGrayColor->blockSignals(false); + + ui->pushButtonColorPicker->blockSignals(true); + ui->pushButtonColorPicker->setCurrentColor(m_data.textColor); + ui->pushButtonColorPicker->blockSignals(false); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/valentina/watermarkwindow.h b/src/libs/vlayout/dialogs/watermarkwindow.h similarity index 100% rename from src/app/valentina/watermarkwindow.h rename to src/libs/vlayout/dialogs/watermarkwindow.h diff --git a/src/app/valentina/watermarkwindow.ui b/src/libs/vlayout/dialogs/watermarkwindow.ui similarity index 91% rename from src/app/valentina/watermarkwindow.ui rename to src/libs/vlayout/dialogs/watermarkwindow.ui index 9258e69c0..c3d979f99 100644 --- a/src/app/valentina/watermarkwindow.ui +++ b/src/libs/vlayout/dialogs/watermarkwindow.ui @@ -14,7 +14,7 @@ Watermark - + :/icon/64x64/icon64x64.png:/icon/64x64/icon64x64.png @@ -32,8 +32,8 @@ 0 0 - 512 - 344 + 498 + 367 @@ -132,6 +132,9 @@ ° + + -360 + 360 @@ -171,7 +174,7 @@ ... - + :/icon/24x24/font_preferences.png:/icon/24x24/font_preferences.png @@ -184,6 +187,20 @@ + + + + Color + + + + + + + Color: + + + @@ -245,8 +262,11 @@ ° + + -360 + - 100 + 360 @@ -365,8 +385,15 @@ + + + QtColorPicker + QPushButton +
qtcolorpicker.h
+
+
- + diff --git a/src/libs/vlayout/vposter.cpp b/src/libs/vlayout/vposter.cpp index dab321692..3decf6ec4 100644 --- a/src/libs/vlayout/vposter.cpp +++ b/src/libs/vlayout/vposter.cpp @@ -95,7 +95,6 @@ QPixmap WatermarkImageFromCache(const VWatermarkData &watermarkData, const QStri if (watermarkData.grayscale) { watermark = Grayscale(watermark); - watermark.save("/home/dismine/grayscale.png", "PNG"); } // Workaround for QGraphicsPixmapItem opacity problem. @@ -272,6 +271,11 @@ QVector VPoster::TextWatermark(QGraphicsItem *parent, const Pos QGraphicsSimpleTextItem *text = new QGraphicsSimpleTextItem(watermarkData.text, parent); text->setFont(watermarkData.font); + + QPen pen = text->pen(); + pen.setColor(watermarkData.textColor); + text->setPen(pen); + text->setOpacity(watermarkData.opacity/100.); text->setTransformOriginPoint(text->boundingRect().center()); text->setRotation(-watermarkData.textRotation); diff --git a/src/libs/vmisc/vcommonsettings.cpp b/src/libs/vmisc/vcommonsettings.cpp index 3d2e54a0f..2609b26d3 100644 --- a/src/libs/vmisc/vcommonsettings.cpp +++ b/src/libs/vmisc/vcommonsettings.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include "../vmisc/def.h" #include "../vmisc/vmath.h" @@ -141,6 +142,9 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingScrollingAcceleration, (QLatin1S Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingTiledPDFMargins, (QLatin1String("tiledPDF/margins"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingTiledPDFOrientation, (QLatin1String("tiledPDF/orientation"))) +Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingWatermarkEditorSize, (QLatin1String("watermarkEditorSize"))) +Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingWatermarkCustomColors, (QLatin1String("watermarkCustomColors"))) + // Reading settings file is very expensive, cache curve approximation to speed up getting value qreal curveApproximationCached = -1; Q_GLOBAL_STATIC(QString, localeCached) @@ -1431,3 +1435,63 @@ void VCommonSettings::SetTiledPDFOrientation(PageOrientation value) { setValue(*settingTiledPDFOrientation, static_cast (value)); } + +//--------------------------------------------------------------------------------------------------------------------- +QSize VCommonSettings::GetWatermarkEditorSize() const +{ + return value(*settingWatermarkEditorSize, QSize(0, 0)).toSize(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCommonSettings::SetWatermarkEditorSize(const QSize &sz) +{ + setValue(*settingWatermarkEditorSize, sz); +} + +//--------------------------------------------------------------------------------------------------------------------- +QVector VCommonSettings::GetWatermarkCustomColors() const +{ + QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename); + QStringList colors = settings.value(*settingPatternGraphicalOutput, 1).toStringList(); + + QVector customColors; + customColors.reserve(colors.size()); + + for (auto color : colors) + { + QColor c(color); + if (c.isValid()) + { + customColors.append(c); + } + } + + if (customColors.count() > 7) + { + customColors.remove(0, customColors.count() - 7); + } + + return customColors; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VCommonSettings::SetWatermarkCustomColors(QVector colors) +{ + QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename); + + if (colors.count() > 7) + { + colors.remove(0, colors.count() - 7); + } + + QStringList customColors; + customColors.reserve(colors.size()); + + for (auto color : colors) + { + customColors.append(color.name()); + } + + settings.setValue(*settingWatermarkCustomColors, customColors); + settings.sync(); +} diff --git a/src/libs/vmisc/vcommonsettings.h b/src/libs/vmisc/vcommonsettings.h index 8f4a049d3..53293db6b 100644 --- a/src/libs/vmisc/vcommonsettings.h +++ b/src/libs/vmisc/vcommonsettings.h @@ -277,6 +277,12 @@ public: bool GetGraphicalOutput() const; void SetGraphicalOutput(const bool &value); + auto GetWatermarkEditorSize() const -> QSize; + void SetWatermarkEditorSize(const QSize& sz); + + auto GetWatermarkCustomColors() const -> QVector; + void SetWatermarkCustomColors(QVector colors); + protected: template diff --git a/src/libs/vmisc/vvalentinasettings.cpp b/src/libs/vmisc/vvalentinasettings.cpp index 4b93ad90f..6aa8baa18 100644 --- a/src/libs/vmisc/vvalentinasettings.cpp +++ b/src/libs/vmisc/vvalentinasettings.cpp @@ -93,8 +93,6 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingDockWidgetToolOptionsActive, Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingDockWidgetPatternMessagesActive, (QLatin1String("dockWidget/patternMessagesActive"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingPatternMessagesFontSize, (QLatin1String("font/patternMessagesSize"))) - -Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingWatermarkEditorSize, (QLatin1String("watermarkEditorSize"))) } //--------------------------------------------------------------------------------------------------------------------- @@ -619,18 +617,6 @@ void VValentinaSettings::SetAutoRefreshPatternMessage(bool value) setValue(*settingAutoRefreshPatternMessage, value); } -//--------------------------------------------------------------------------------------------------------------------- -QSize VValentinaSettings::GetWatermarkEditorSize() const -{ - return value(*settingWatermarkEditorSize, QSize(0, 0)).toSize(); -} - -//--------------------------------------------------------------------------------------------------------------------- -void VValentinaSettings::SetWatermarkEditorSize(const QSize &sz) -{ - setValue(*settingWatermarkEditorSize, sz); -} - //--------------------------------------------------------------------------------------------------------------------- bool VValentinaSettings::GetToolPanelScaling() const { diff --git a/src/libs/vmisc/vvalentinasettings.h b/src/libs/vmisc/vvalentinasettings.h index c5f222259..1a79d33d7 100644 --- a/src/libs/vmisc/vvalentinasettings.h +++ b/src/libs/vmisc/vvalentinasettings.h @@ -161,9 +161,6 @@ public: bool GetAutoRefreshPatternMessage() const; void SetAutoRefreshPatternMessage(bool value); - QSize GetWatermarkEditorSize() const; - void SetWatermarkEditorSize(const QSize& sz); - bool GetToolPanelScaling() const; void SetToolPanelScaling(const bool &value); diff --git a/src/libs/vtools/tools/vtoolseamallowance.cpp b/src/libs/vtools/tools/vtoolseamallowance.cpp index b8087d2e6..a6ac6a4be 100644 --- a/src/libs/vtools/tools/vtoolseamallowance.cpp +++ b/src/libs/vtools/tools/vtoolseamallowance.cpp @@ -1021,7 +1021,7 @@ void VToolSeamAllowance::RefreshDataInFile() // Refresh only parts that we possibly need to update { // TODO. Delete if minimal supported version is 0.4.0 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 4, 0), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 4, 0), "Time to refactor the code."); const uint version = doc->GetParametrUInt(domElement, AttrVersion, QChar('1')); diff --git a/src/libs/vtools/tools/vtooluniondetails.cpp b/src/libs/vtools/tools/vtooluniondetails.cpp index 5c5bfd8c2..f3c14902d 100644 --- a/src/libs/vtools/tools/vtooluniondetails.cpp +++ b/src/libs/vtools/tools/vtooluniondetails.cpp @@ -1339,7 +1339,7 @@ void UpdateUnitedNodes(const VToolUnionDetailsInitData &initData, qreal dx, qrea { // This check need for backward compatibility // Remove check and "else" part if min version is 0.3.2 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 3, 2), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 3, 2), "Time to refactor the code."); if (children.size() == countNodeD1 + countNodeD2-1) { @@ -1396,7 +1396,7 @@ void UpdateUnitedNodes(const VToolUnionDetailsInitData &initData, qreal dx, qrea QVector FixChildren(QVector records, QVector children, VContainer *data) { // TODO. Delete if minimal supported version is 0.7.0 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 7, 0), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 7, 0), "Time to refactor the code."); SCASSERT(data != nullptr) @@ -1434,7 +1434,7 @@ void UpdateUnitedDetailPaths(const VToolUnionDetailsInitData &initData, qreal dx if (initData.version == 1) { // TODO. Delete if minimal supported version is 0.7.0 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 7, 0), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 7, 0), "Time to refactor the code."); // Fixing bug in first version of the tool. Mostly for backward compatibility. children = FixChildren(records, children, initData.data); @@ -1447,7 +1447,7 @@ void UpdateUnitedDetailPaths(const VToolUnionDetailsInitData &initData, qreal dx if (initData.version == 1) { // TODO. Delete if minimal supported version is 0.7.0 - Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FORMAT_VERSION(0, 7, 0), + Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 7, 0), "Time to refactor the code."); const quint32 updatedId = TakeNextId(children); diff --git a/src/libs/vwidgets/qtcolorpicker.cpp b/src/libs/vwidgets/qtcolorpicker.cpp new file mode 100644 index 000000000..4d77c6edf --- /dev/null +++ b/src/libs/vwidgets/qtcolorpicker.cpp @@ -0,0 +1,1274 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtcolorpicker.h" + +/*! \class QtColorPicker + + \brief The QtColorPicker class provides a widget for selecting + colors from a popup color grid. + + Users can invoke the color picker by clicking on it, or by + navigating to it and pressing Space. They can use the mouse or + arrow keys to navigate between colors on the grid, and select a + color by clicking or by pressing Enter or Space. The + colorChanged() signal is emitted whenever the color picker's color + changes. + + The widget also supports negative selection: Users can click and + hold the mouse button on the QtColorPicker widget, then move the + mouse over the color grid and release the mouse button over the + color they wish to select. + + The color grid shows a customized selection of colors. An optional + ellipsis "..." button (signifying "more") can be added at the + bottom of the grid; if the user presses this, a QColorDialog pops + up and lets them choose any color they like. This button is made + available by using setColorDialogEnabled(). + + When a color is selected, the QtColorPicker widget shows the color + and its name. If the name cannot be determined, the translatable + name "Custom" is used. + + The QtColorPicker object is optionally initialized with the number + of columns in the color grid. Colors are then added left to right, + top to bottom using insertColor(). If the number of columns is not + set, QtColorPicker calculates the number of columns and rows that + will make the grid as square as possible. + + \code + DrawWidget::DrawWidget(QWidget *parent, const char *name) + { + QtColorPicker *picker = new QtColorPicker(this); + picker->insertColor(red, "Red")); + picker->insertColor(QColor("green"), "Green")); + picker->insertColor(QColor(0, 0, 255), "Blue")); + picker->insertColor(white); + + connect(colors, SIGNAL(colorChanged(const QColor &)), SLOT(setCurrentColor(const QColor &))); + } + \endcode + + An alternative to adding colors manually is to initialize the grid + with QColorDialog's standard colors using setStandardColors(). + + QtColorPicker also provides a the static function getColor(), + which pops up the grid of standard colors at any given point. + + \img colorpicker1.png + \img colorpicker2.png + + \sa QColorDialog +*/ + +/*! \fn QtColorPicker::colorChanged(const QColor &color) + + This signal is emitted when the QtColorPicker's color is changed. + \a color is the new color. + + To obtain the color's name, use text(). +*/ + +/* + A class that acts very much like a QPushButton. It's not styled, + so we can expect the exact same look, feel and geometry + everywhere. Also, this button always emits clicked on + mouseRelease, even if the mouse button was not pressed inside the + widget. +*/ +class ColorPickerButton : public QFrame +{ + Q_OBJECT + +public: + explicit ColorPickerButton(QWidget *parent); + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + void keyReleaseEvent(QKeyEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void focusInEvent(QFocusEvent *e) override; + void focusOutEvent(QFocusEvent *e) override; +}; + +/* + This class represents each "color" or item in the color grid. +*/ +class ColorPickerItem : public QFrame +{ + Q_OBJECT + +public: + ColorPickerItem(const QColor &color = Qt::white, const QString &text = QString(), + QWidget *parent = 0); + ~ColorPickerItem(); + + QColor color() const; + QString text() const; + + void setSelected(bool); + bool isSelected() const; +signals: + void clicked(); + void selected(); + +public slots: + void setColor(const QColor &color, const QString &text = QString()); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void paintEvent(QPaintEvent *e); + +private: + QColor c; + QString t; + bool sel; +}; + +/* + +*/ +class ColorPickerPopup : public QFrame +{ + Q_OBJECT + +public: + ColorPickerPopup(int width, bool withColorDialog, + QWidget *parent = nullptr); + ~ColorPickerPopup(); + + void insertColor(const QColor &col, const QString &text, int index); + void exec(); + + void setExecFlag(); + + QColor lastSelected() const; + + ColorPickerItem *find(const QColor &col) const; + QColor color(int index) const; + + auto CustomItems() const -> QVector; + +signals: + void selected(const QColor &); + void hid(); + +public slots: + void getColorFromDialog(); + +protected slots: + void updateSelected(); + +protected: + void keyPressEvent(QKeyEvent *e); + void showEvent(QShowEvent *e); + void hideEvent(QHideEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + void regenerateGrid(); + +private: + Q_DISABLE_COPY(ColorPickerPopup) + QMap > widgetAt{}; + QList items{}; + QGridLayout *grid{nullptr}; + ColorPickerButton *moreButton{nullptr}; + QEventLoop *eventLoop{nullptr}; + + int lastPos{0}; + int cols; + QColor lastSel{}; +}; + +/*! + Constructs a QtColorPicker widget. The popup will display a grid + with \a cols columns, or if \a cols is -1, the number of columns + will be calculated automatically. + + If \a enableColorDialog is true, the popup will also have a "More" + button (signified by an ellipsis "...") that presents a + QColorDialog when clicked. + + After constructing a QtColorPicker, call insertColor() to add + individual colors to the popup grid, or call setStandardColors() + to add all the standard colors in one go. + + The \a parent argument is passed to QFrame's constructor. + + \sa QFrame +*/ +QtColorPicker::QtColorPicker(QWidget *parent, + int cols, bool enableColorDialog) + : QPushButton(parent), popup(0), withColorDialog(enableColorDialog) +{ + setFocusPolicy(Qt::StrongFocus); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + setAutoDefault(false); + setAutoFillBackground(true); + setCheckable(true); + + // Set text + setText(tr("Black")); + firstInserted = false; + + // Create and set icon + col = Qt::black; + dirty = true; + + // Create color grid popup and connect to it. + popup = new ColorPickerPopup(cols, withColorDialog, this); + connect(popup, SIGNAL(selected(const QColor &)), + SLOT(setCurrentColor(const QColor &))); + connect(popup, SIGNAL(hid()), SLOT(popupClosed())); + + // Connect this push button's pressed() signal. + connect(this, SIGNAL(toggled(bool)), SLOT(buttonPressed(bool))); +} + +/*! + Destructs the QtColorPicker. +*/ +QtColorPicker::~QtColorPicker() +{ +} + +/*! \internal + + Pops up the color grid, and makes sure the status of + QtColorPicker's button is right. +*/ +void QtColorPicker::buttonPressed(bool toggled) +{ + if (!toggled) + return; + + const QRect desktop = this->parentWidget()->geometry(); + // Make sure the popup is inside the desktop. + QPoint pos = rect().bottomLeft(); + if (pos.x() < desktop.left()) + pos.setX(desktop.left()); + if (pos.y() < desktop.top()) + pos.setY(desktop.top()); + + if ((pos.x() + popup->sizeHint().width()) > desktop.width()) + pos.setX(desktop.width() - popup->sizeHint().width()); + if ((pos.y() + popup->sizeHint().height()) > desktop.bottom()) + pos.setY(desktop.bottom() - popup->sizeHint().height()); + popup->move(mapToGlobal(pos)); + + if (ColorPickerItem *item = popup->find(col)) + item->setSelected(true); + + // Remove focus from this widget, preventing the focus rect + // from showing when the popup is shown. Order an update to + // make sure the focus rect is cleared. + clearFocus(); + update(); + + // Allow keyboard navigation as soon as the popup shows. + popup->setFocus(); + + // Execute the popup. The popup will enter the event loop. + popup->show(); +} + +/*! + \internal +*/ +void QtColorPicker::paintEvent(QPaintEvent *e) +{ + if (dirty) { + int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pix(iconSize, iconSize); + pix.fill(palette().button().color()); + + QPainter p(&pix); + + int w = pix.width(); // width of cell in pixels + int h = pix.height(); // height of cell in pixels + p.setPen(QPen(Qt::gray)); + p.setBrush(col); + p.drawRect(2, 2, w - 5, h - 5); + setIcon(QIcon(pix)); + + dirty = false; + } + QPushButton::paintEvent(e); +} + +/*! \internal + + Makes sure the button isn't pressed when the popup hides. +*/ +void QtColorPicker::popupClosed() +{ + setChecked(false); + setFocus(); +} + +/*! + Returns the currently selected color. + + \sa text() +*/ +QColor QtColorPicker::currentColor() const +{ + return col; +} + +/*! + Returns the color at position \a index. +*/ +QColor QtColorPicker::color(int index) const +{ + return popup->color(index); +} + +/*! + Adds the 17 predefined colors from the Qt namespace. + + (The names given to the colors, "Black", "White", "Red", etc., are + all translatable.) + + \sa insertColor() +*/ +void QtColorPicker::setStandardColors() +{ + insertColor(Qt::black, tr("Black")); + insertColor(Qt::white, tr("White")); + insertColor(Qt::red, tr("Red")); + insertColor(Qt::darkRed, tr("Dark red")); + insertColor(Qt::green, tr("Green")); + insertColor(Qt::darkGreen, tr("Dark green")); + insertColor(Qt::blue, tr("Blue")); + insertColor(Qt::darkBlue, tr("Dark blue")); + insertColor(Qt::cyan, tr("Cyan")); + insertColor(Qt::darkCyan, tr("Dark cyan")); + insertColor(Qt::magenta, tr("Magenta")); + insertColor(Qt::darkMagenta, tr("Dark magenta")); + insertColor(Qt::yellow, tr("Yellow")); + insertColor(Qt::darkYellow, tr("Dark yellow")); + insertColor(Qt::gray, tr("Gray")); + insertColor(Qt::darkGray, tr("Dark gray")); + insertColor(Qt::lightGray, tr("Light gray")); +} + +auto QtColorPicker::CustomColors() const -> QVector +{ + QVector customColor; + if (popup != nullptr) + { + QVector items = popup->CustomItems(); + customColor.reserve(items.size()); + + for (auto *item : items) + { + if (item != nullptr) + { + customColor.append(item->color()); + } + } + } + return customColor; +} + + +/*! + Makes \a color current. If \a color is not already in the color grid, it + is inserted with the text "Custom". + + This function emits the colorChanged() signal if the new color is + valid, and different from the old one. +*/ +void QtColorPicker::setCurrentColor(const QColor &color) +{ + if (col == color || !color.isValid()) + return; + + ColorPickerItem *item = popup->find(color); + if (!item) + { + insertColor(color, tr("Custom")); + item = popup->find(color); + } + + col = color; + setText(item->text()); + + dirty = true; + + popup->hide(); + repaint(); + + item->setSelected(true); + emit colorChanged(color); +} + +/*! + Adds the color \a color with the name \a text to the color grid, + at position \a index. If index is -1, the color is assigned + automatically assigned a position, starting from left to right, + top to bottom. +*/ +void QtColorPicker::insertColor(const QColor &color, const QString &text, int index) +{ + popup->insertColor(color, text, index); + if (!firstInserted) { + col = color; + setText(text); + firstInserted = true; + } +} + +/*! \property QtColorPicker::colorDialog + \brief Whether the ellipsis "..." (more) button is available. + + If this property is set to TRUE, the color grid popup will include + a "More" button (signified by an ellipsis, "...") which pops up a + QColorDialog when clicked. The user will then be able to select + any custom color they like. +*/ +void QtColorPicker::setColorDialogEnabled(bool enabled) +{ + withColorDialog = enabled; +} +bool QtColorPicker::colorDialogEnabled() const +{ + return withColorDialog; +} + +/*! + Pops up a color grid with Qt default colors at \a point, using + global coordinates. If \a allowCustomColors is true, there will + also be a button on the popup that invokes QColorDialog. + + For example: + + \code + void Drawer::mouseReleaseEvent(QMouseEvent *e) + { + if (e->button() & RightButton) { + QColor color = QtColorPicker::getColor(mapToGlobal(e->pos())); + } + } + \endcode +*/ +QColor QtColorPicker::getColor(const QPoint &point, bool allowCustomColors) +{ + ColorPickerPopup popup(-1, allowCustomColors); + + popup.insertColor(Qt::black, tr("Black"), 0); + popup.insertColor(Qt::white, tr("White"), 1); + popup.insertColor(Qt::red, tr("Red"), 2); + popup.insertColor(Qt::darkRed, tr("Dark red"), 3); + popup.insertColor(Qt::green, tr("Green"), 4); + popup.insertColor(Qt::darkGreen, tr("Dark green"), 5); + popup.insertColor(Qt::blue, tr("Blue"), 6); + popup.insertColor(Qt::darkBlue, tr("Dark blue"), 7); + popup.insertColor(Qt::cyan, tr("Cyan"), 8); + popup.insertColor(Qt::darkCyan, tr("Dark cyan"), 9); + popup.insertColor(Qt::magenta, tr("Magenta"), 10); + popup.insertColor(Qt::darkMagenta, tr("Dark magenta"), 11); + popup.insertColor(Qt::yellow, tr("Yellow"), 12); + popup.insertColor(Qt::darkYellow, tr("Dark yellow"), 13); + popup.insertColor(Qt::gray, tr("Gray"), 14); + popup.insertColor(Qt::darkGray, tr("Dark gray"), 15); + popup.insertColor(Qt::lightGray, tr("Light gray"), 16); + + popup.move(point); + popup.exec(); + return popup.lastSelected(); +} + +/*! \internal + + Constructs the popup widget. +*/ +ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog, QWidget *parent) + : QFrame(parent, Qt::Popup), + cols(width) +{ + setFrameStyle(QFrame::StyledPanel); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + + if (withColorDialog) + { + moreButton = new ColorPickerButton(this); + moreButton->setFixedWidth(24); + moreButton->setFixedHeight(21); + moreButton->setFrameRect(QRect(2, 2, 20, 17)); + connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog())); + } + else + { + moreButton = 0; + } + + eventLoop = nullptr; + grid = nullptr; + regenerateGrid(); +} + + +/*! \internal + + Destructs the popup widget. +*/ +ColorPickerPopup::~ColorPickerPopup() +{ + if (eventLoop != nullptr) + { + eventLoop->exit(); + } +} + +/*! \internal + + If there is an item whole color is equal to \a col, returns a + pointer to this item; otherwise returns 0. +*/ +ColorPickerItem *ColorPickerPopup::find(const QColor &col) const +{ + for (auto *item : items) + { + if (item && item->color() == col) + { + return item; + } + } + + return nullptr; +} + +/*! \internal + + Adds \a item to the grid. The items are added from top-left to + bottom-right. +*/ +void ColorPickerPopup::insertColor(const QColor &col, const QString &text, int index) +{ + // Don't add colors that we have already. + ColorPickerItem *existingItem = find(col); + ColorPickerItem *lastSelectedItem = find(lastSelected()); + + if (existingItem != nullptr) + { + if (lastSelectedItem && existingItem != lastSelectedItem) + { + lastSelectedItem->setSelected(false); + } + existingItem->setFocus(); + existingItem->setSelected(true); + return; + } + + auto *item = new ColorPickerItem(col, text, this); + + if (lastSelectedItem) + { + lastSelectedItem->setSelected(false); + } + else + { + item->setSelected(true); + lastSel = col; + } + item->setFocus(); + + connect(item, SIGNAL(selected()), SLOT(updateSelected())); + + if (index == -1) + { + index = items.count(); + } + + items.insert(index, item); + regenerateGrid(); + + update(); +} + +/*! \internal + +*/ +QColor ColorPickerPopup::color(int index) const +{ + if (index < 0 || index > static_cast (items.count() - 1)) + { + return QColor(); + } + + auto *that = const_cast(this); + return that->items.at(index)->color(); +} + +auto ColorPickerPopup::CustomItems() const ->QVector +{ + QVector customItems; + customItems.reserve(items.size()); + + for (auto *item : items) + { + if (item!= nullptr && item->text() == tr("Custom")) + { + customItems.append(item); + } + } + + return customItems; +} + +/*! \internal + +*/ +void ColorPickerPopup::exec() +{ + show(); + + QEventLoop e; + eventLoop = &e; + (void) e.exec(); + eventLoop = nullptr; +} + +/*! \internal + +*/ +void ColorPickerPopup::updateSelected() +{ + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) + { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) + { + auto *litem = reinterpret_cast(layoutItem->widget()); + if (litem != sender()) + { + litem->setSelected(false); + } + } + ++i; + } + + if (sender() && sender()->inherits("ColorPickerItem")) + { + auto *item = static_cast(sender()); + lastSel = item->color(); + emit selected(item->color()); + } + + hide(); +} + +/*! \internal + +*/ +void ColorPickerPopup::mouseReleaseEvent(QMouseEvent *e) +{ + if (!rect().contains(e->pos())) + { + hide(); + } +} + +/*! \internal + + Controls keyboard navigation and selection on the color grid. +*/ +void ColorPickerPopup::keyPressEvent(QKeyEvent *e) +{ + int curRow = 0; + int curCol = 0; + + bool foundFocus = false; + for (int j = 0; !foundFocus && j < grid->rowCount(); ++j) + { + for (int i = 0; !foundFocus && i < grid->columnCount(); ++i) + { + if ((widgetAt[j][i] != nullptr) && widgetAt[j][i]->hasFocus()) + { + curRow = j; + curCol = i; + foundFocus = true; + break; + } + } + } + + switch (e->key()) + { + case Qt::Key_Left: + if (curCol > 0) + { + --curCol; + } + else if (curRow > 0) + { + --curRow; curCol = grid->columnCount() - 1; + } + break; + case Qt::Key_Right: + if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1]) + { + ++curCol; + } + else if (curRow < grid->rowCount() - 1) + { + ++curRow; curCol = 0; + } + break; + case Qt::Key_Up: + if (curRow > 0) + { + --curRow; + } + else curCol = 0; + break; + case Qt::Key_Down: + if (curRow < grid->rowCount() - 1) + { + QWidget *w = widgetAt[curRow + 1][curCol]; + if (w) + { + ++curRow; + } + else for (int i = 1; i < grid->columnCount(); ++i) + { + if (!widgetAt[curRow + 1][i]) + { + curCol = i - 1; + ++curRow; + break; + } + } + } + break; + case Qt::Key_Space: + case Qt::Key_Return: + case Qt::Key_Enter: + { + QWidget *w = widgetAt[curRow][curCol]; + if ((w != nullptr) && w->inherits("ColorPickerItem")) + { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); + + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) + { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) + { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + { + litem->setSelected(false); + } + } + ++i; + } + + lastSel = wi->color(); + emit selected(wi->color()); + hide(); + } + else if (w && w->inherits("QPushButton")) + { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); + + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) + { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) + { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + { + litem->setSelected(false); + } + } + ++i; + } + + lastSel = wi->color(); + emit selected(wi->color()); + hide(); + } + break; + } + case Qt::Key_Escape: + hide(); + break; + default: + e->ignore(); + break; + } + + widgetAt[curRow][curCol]->setFocus(); +} + +/*! \internal + +*/ +void ColorPickerPopup::hideEvent(QHideEvent *e) +{ + if (eventLoop) + { + eventLoop->exit(); + } + + setFocus(); + + emit hid(); + QFrame::hideEvent(e); +} + +/*! \internal + +*/ +QColor ColorPickerPopup::lastSelected() const +{ + return lastSel; +} + +/*! \internal + + Sets focus on the popup to enable keyboard navigation. Draws + focusRect and selection rect. +*/ +void ColorPickerPopup::showEvent(QShowEvent *) +{ + bool foundSelected = false; + for (int i = 0; i < grid->columnCount(); ++i) + { + for (int j = 0; j < grid->rowCount(); ++j) + { + QWidget *w = widgetAt[j][i]; + if (w && w->inherits("ColorPickerItem")) + { + if (static_cast(w)->isSelected()) + { + w->setFocus(); + foundSelected = true; + break; + } + } + } + } + + if (!foundSelected) + { + if (items.count() == 0) + { + setFocus(); + } + else + { + widgetAt[0][0]->setFocus(); + } + } +} + +/*! + +*/ +void ColorPickerPopup::regenerateGrid() +{ + widgetAt.clear(); + + int columns = cols; + if (columns == -1) + { + columns = static_cast(ceil(sqrt(static_cast(items.count())))); + } + + // When the number of columns grows, the number of rows will + // fall. There's no way to shrink a grid, so we create a new + // one. + if (grid) delete grid; + grid = new QGridLayout(this); + grid->setContentsMargins(1, 1, 1, 1); + grid->setSpacing(0); + + int ccol = 0, crow = 0; + for (int i = 0; i < items.size(); ++i) + { + if (items.at(i)) + { + widgetAt[crow][ccol] = items.at(i); + grid->addWidget(items.at(i), crow, ccol++); + if (ccol == columns) + { + ++crow; + ccol = 0; + } + } + } + + if (moreButton) + { + grid->addWidget(moreButton, crow, ccol); + widgetAt[crow][ccol] = moreButton; + } + updateGeometry(); +} + +/*! \internal + + Copies the color dialog's currently selected item and emits + itemSelected(). +*/ +void ColorPickerPopup::getColorFromDialog() +{ + QColor col = QColorDialog::getColor(lastSel, parentWidget()); + if (!col.isValid()) + { + return; + } + + insertColor(col, tr("Custom"), -1); + lastSel = col; + emit selected(col); +} + +/*! + Constructs a ColorPickerItem whose color is set to \a color, and + whose name is set to \a text. +*/ +ColorPickerItem::ColorPickerItem(const QColor &color, const QString &text, + QWidget *parent) + : QFrame(parent), c(color), t(text), sel(false) +{ + setToolTip(t); + setFixedWidth(24); + setFixedHeight(21); +} + +/*! + Destructs a ColorPickerItem. + */ +ColorPickerItem::~ColorPickerItem() +{ +} + +/*! + Returns the item's color. + + \sa text() +*/ +QColor ColorPickerItem::color() const +{ + return c; +} + +/*! + Returns the item's text. + + \sa color() +*/ +QString ColorPickerItem::text() const +{ + return t; +} + +/*! + +*/ +bool ColorPickerItem::isSelected() const +{ + return sel; +} + +/*! + +*/ +void ColorPickerItem::setSelected(bool selected) +{ + sel = selected; + update(); +} + +/*! + Sets the item's color to \a color, and its name to \a text. +*/ +void ColorPickerItem::setColor(const QColor &color, const QString &text) +{ + c = color; + t = text; + setToolTip(t); + update(); +} + +/*! + +*/ +void ColorPickerItem::mouseMoveEvent(QMouseEvent *) +{ + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerItem::mouseReleaseEvent(QMouseEvent *) +{ + sel = true; + emit selected(); +} + +/*! + +*/ +void ColorPickerItem::mousePressEvent(QMouseEvent *) +{ + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerItem::paintEvent(QPaintEvent *) +{ + QPainter p(this); + int w = width(); // width of cell in pixels + int h = height(); // height of cell in pixels + + p.setPen( QPen( Qt::gray, 0, Qt::SolidLine ) ); + + if (sel) + { + p.drawRect(1, 1, w - 3, h - 3); + } + + p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); + p.drawRect(3, 3, w - 7, h - 7); + p.fillRect(QRect(4, 4, w - 8, h - 8), QBrush(c)); + + if (hasFocus()) + { + p.drawRect(0, 0, w - 1, h - 1); + } +} + +/*! + +*/ +ColorPickerButton::ColorPickerButton(QWidget *parent) + : QFrame(parent) +{ + setFrameStyle(StyledPanel); +} + +/*! + +*/ +void ColorPickerButton::mousePressEvent(QMouseEvent *e) +{ + Q_UNUSED(e) + setFrameShadow(Sunken); + update(); +} + +/*! + +*/ +void ColorPickerButton::mouseMoveEvent(QMouseEvent *e) +{ + Q_UNUSED(e) + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerButton::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e) + setFrameShadow(Raised); + repaint(); + emit clicked(); +} + +/*! + +*/ +void ColorPickerButton::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Up + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) + { + QCoreApplication::sendEvent(parent(), e); + } + else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) + { + setFrameShadow(Sunken); + update(); + } + else + { + QFrame::keyPressEvent(e); + } +} + +/*! + +*/ +void ColorPickerButton::keyReleaseEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Up + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) + { + QCoreApplication::sendEvent(parent(), e); + } + else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) + { + setFrameShadow(Raised); + repaint(); + emit clicked(); + } + else + { + QFrame::keyReleaseEvent(e); + } + +} + +/*! + +*/ +void ColorPickerButton::focusInEvent(QFocusEvent *e) +{ + setFrameShadow(Raised); + update(); + QFrame::focusOutEvent(e); +} + +/*! + +*/ +void ColorPickerButton::focusOutEvent(QFocusEvent *e) +{ + setFrameShadow(Raised); + update(); + QFrame::focusOutEvent(e); +} + +/*! + +*/ +void ColorPickerButton::paintEvent(QPaintEvent *e) +{ + QFrame::paintEvent(e); + + QPainter p(this); + p.fillRect(contentsRect(), palette().button()); + + QRect r = rect(); + + int offset = frameShadow() == Sunken ? 1 : 0; + + QPen pen(palette().buttonText(), 1); + p.setPen(pen); + + p.drawRect(r.center().x() + offset - 4, r.center().y() + offset, 1, 1); + p.drawRect(r.center().x() + offset , r.center().y() + offset, 1, 1); + p.drawRect(r.center().x() + offset + 4, r.center().y() + offset, 1, 1); + if (hasFocus()) + { + p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); + p.drawRect(0, 0, width() - 1, height() - 1); + } + + p.end(); + +} + +#include "qtcolorpicker.moc" diff --git a/src/libs/vwidgets/qtcolorpicker.h b/src/libs/vwidgets/qtcolorpicker.h new file mode 100644 index 000000000..1ef681886 --- /dev/null +++ b/src/libs/vwidgets/qtcolorpicker.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#ifndef QTCOLORPICKER_H +#define QTCOLORPICKER_H +#include +#include +#include + +#include +#include +#include + + +class ColorPickerPopup; +class ColorPickerItem; + +class QtColorPicker : public QPushButton +{ + Q_OBJECT + + Q_PROPERTY(bool colorDialog READ colorDialogEnabled WRITE setColorDialogEnabled) + +public: + QtColorPicker(QWidget *parent = 0, + int columns = -1, bool enableColorDialog = true); + + ~QtColorPicker(); + + void insertColor(const QColor &color, const QString &text = QString(), int index = -1); + + QColor currentColor() const; + + QColor color(int index) const; + + void setColorDialogEnabled(bool enabled); + bool colorDialogEnabled() const; + + void setStandardColors(); + + auto CustomColors() const -> QVector; + + static QColor getColor(const QPoint &pos, bool allowCustomColors = true); + +public Q_SLOTS: + void setCurrentColor(const QColor &col); + +Q_SIGNALS: + void colorChanged(const QColor &); + +protected: + void paintEvent(QPaintEvent *e); + +private Q_SLOTS: + void buttonPressed(bool toggled); + void popupClosed(); + +private: + Q_DISABLE_COPY(QtColorPicker) + ColorPickerPopup *popup{nullptr}; + QColor col{}; + bool withColorDialog{false}; + bool dirty{false}; + bool firstInserted{false}; +}; + +#endif diff --git a/src/libs/vwidgets/vwidgets.pri b/src/libs/vwidgets/vwidgets.pri index eaf29bd78..023e5efb0 100644 --- a/src/libs/vwidgets/vwidgets.pri +++ b/src/libs/vwidgets/vwidgets.pri @@ -2,6 +2,7 @@ # This need for corect working file translations.pro SOURCES += \ + $$PWD/qtcolorpicker.cpp \ $$PWD/vcomboboxdelegate.cpp \ $$PWD/vdecorationaligningdelegate.cpp \ $$PWD/velidedlabel.cpp \ @@ -32,6 +33,7 @@ SOURCES += \ *msvc*:SOURCES += $$PWD/stable.cpp HEADERS += \ + $$PWD/qtcolorpicker.h \ $$PWD/stable.h \ $$PWD/vcomboboxdelegate.h \ $$PWD/vdecorationaligningdelegate.h \