File indexing completed on 2024-04-28 04:32:00

0001 /*
0002  * Copyright (C) 2010-2015 by Stephen Allewell
0003  * steve.allewell@gmail.com
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2 of the License, or
0008  * (at your option) any later version.
0009  */
0010 
0011 #include "Element.h"
0012 
0013 #include <math.h>
0014 
0015 #include <QAbstractTextDocumentLayout>
0016 #include <QPaintEngine>
0017 #include <QPainter>
0018 #include <QTextDocument>
0019 
0020 #include <KLocalizedString>
0021 
0022 #include "Document.h"
0023 #include "FlossScheme.h"
0024 #include "Page.h"
0025 #include "SchemeManager.h"
0026 #include "Symbol.h"
0027 #include "SymbolLibrary.h"
0028 #include "SymbolManager.h"
0029 
0030 /*
0031  * Convenience function to round a double to a fixed number of decimal places
0032  */
0033 double round_n(double v, int n)
0034 {
0035     double places = pow(10.0, n);
0036     return round(v * places) / places;
0037 }
0038 
0039 Element::Element(Page *parent, const QRect &rectangle, Element::Type type)
0040     : m_parent(parent)
0041     , m_rectangle(rectangle)
0042     , m_type(type)
0043 {
0044 }
0045 
0046 Element::Element(const Element &other)
0047     : m_parent(nullptr)
0048     , // needs to be reparented by cloner
0049     m_rectangle(other.m_rectangle)
0050     , m_type(other.m_type)
0051 {
0052 }
0053 
0054 Page *Element::parent() const
0055 {
0056     return m_parent;
0057 }
0058 
0059 Element::Type Element::type() const
0060 {
0061     return m_type;
0062 }
0063 
0064 const QRect &Element::rectangle() const
0065 {
0066     return m_rectangle;
0067 }
0068 
0069 void Element::setParent(Page *parent)
0070 {
0071     m_parent = parent;
0072 }
0073 
0074 void Element::setRectangle(const QRect &rectangle)
0075 {
0076     m_rectangle = rectangle;
0077 }
0078 
0079 void Element::move(const QPoint &offset)
0080 {
0081     m_rectangle.translate(offset);
0082 }
0083 
0084 QDataStream &operator<<(QDataStream &stream, const Element &element)
0085 {
0086     return element.streamOut(stream);
0087 }
0088 
0089 QDataStream &operator>>(QDataStream &stream, Element &element)
0090 {
0091     return element.streamIn(stream);
0092 }
0093 
0094 QDataStream &Element::streamOut(QDataStream &stream) const
0095 {
0096     stream << qint32(version);
0097 
0098     stream << m_rectangle;
0099 
0100     return stream;
0101 }
0102 
0103 QDataStream &Element::streamIn(QDataStream &stream)
0104 {
0105     qint32 version;
0106     qint32 visible;
0107 
0108     stream >> version;
0109 
0110     switch (version) {
0111     case 101:
0112         stream >> m_rectangle;
0113         break;
0114 
0115     case 100:
0116         stream >> m_rectangle >> visible; // ignore
0117         break;
0118 
0119     default:
0120         // not supported
0121         // throw exception
0122         break;
0123     }
0124 
0125     return stream;
0126 }
0127 
0128 KeyElement::KeyElement(Page *parent, const QRect &rectangle, Element::Type type)
0129     : Element(parent, rectangle, type)
0130     , m_showBorder(Configuration::keyElement_ShowBorder())
0131     , m_borderColor(Configuration::keyElement_BorderColor())
0132     , m_borderThickness(Configuration::keyElement_BorderThickness())
0133     , m_fillBackground(Configuration::keyElement_FillBackground())
0134     , m_backgroundColor(Configuration::keyElement_BackgroundColor())
0135     , m_backgroundTransparency(Configuration::keyElement_BackgroundTransparency())
0136     , m_margins(QMargins(Configuration::keyElement_MarginLeft(),
0137                          Configuration::keyElement_MarginTop(),
0138                          Configuration::keyElement_MarginRight(),
0139                          Configuration::keyElement_MarginBottom()))
0140     , m_indexStart(0)
0141     , m_indexCount(0)
0142     , m_symbolColumn(Configuration::keyElement_SymbolColumn())
0143     , m_symbolColumnColor(Configuration::keyElement_SymbolColumnColor())
0144     , m_flossNameColumn(Configuration::keyElement_FlossNameColumn())
0145     , m_strandsColumn(Configuration::keyElement_StrandsColumn())
0146     , m_flossDescriptionColumn(Configuration::keyElement_FlossDescriptionColumn())
0147     , m_stitchesColumn(Configuration::keyElement_StitchesColumn())
0148     , m_lengthColumn(Configuration::keyElement_LengthColumn())
0149     , m_skeinsColumn(Configuration::keyElement_SkeinsColumn())
0150 {
0151 }
0152 
0153 KeyElement::KeyElement(const KeyElement &other)
0154     : Element(other)
0155     , m_showBorder(other.m_showBorder)
0156     , m_borderColor(other.m_borderColor)
0157     , m_borderThickness(other.m_borderThickness)
0158     , m_fillBackground(other.m_fillBackground)
0159     , m_backgroundColor(other.m_backgroundColor)
0160     , m_backgroundTransparency(other.m_backgroundTransparency)
0161     , m_margins(other.m_margins)
0162     , m_textColor(other.m_textColor)
0163     , m_textFont(other.m_textFont)
0164     , m_indexStart(other.m_indexStart)
0165     , m_indexCount(other.m_indexCount)
0166     , m_symbolColumn(other.m_symbolColumn)
0167     , m_symbolColumnColor(other.m_symbolColumnColor)
0168     , m_flossNameColumn(other.m_flossNameColumn)
0169     , m_strandsColumn(other.m_strandsColumn)
0170     , m_flossDescriptionColumn(other.m_flossDescriptionColumn)
0171     , m_stitchesColumn(other.m_stitchesColumn)
0172     , m_lengthColumn(other.m_lengthColumn)
0173     , m_skeinsColumn(other.m_skeinsColumn)
0174 {
0175 }
0176 
0177 bool KeyElement::showBorder() const
0178 {
0179     return m_showBorder;
0180 }
0181 
0182 QColor KeyElement::borderColor() const
0183 {
0184     return m_borderColor;
0185 }
0186 
0187 int KeyElement::borderThickness() const
0188 {
0189     return m_borderThickness;
0190 }
0191 
0192 bool KeyElement::fillBackground() const
0193 {
0194     return m_fillBackground;
0195 }
0196 
0197 QColor KeyElement::backgroundColor() const
0198 {
0199     return m_backgroundColor;
0200 }
0201 
0202 int KeyElement::backgroundTransparency() const
0203 {
0204     return m_backgroundTransparency;
0205 }
0206 
0207 QMargins KeyElement::margins() const
0208 {
0209     return m_margins;
0210 }
0211 
0212 QColor KeyElement::textColor() const
0213 {
0214     return m_textColor;
0215 }
0216 
0217 QFont KeyElement::textFont() const
0218 {
0219     return m_textFont;
0220 }
0221 
0222 int KeyElement::indexStart() const
0223 {
0224     return m_indexStart;
0225 }
0226 
0227 int KeyElement::indexCount() const
0228 {
0229     return m_indexCount;
0230 }
0231 
0232 bool KeyElement::symbolColumn() const
0233 {
0234     return m_symbolColumn;
0235 }
0236 
0237 bool KeyElement::symbolColumnColor() const
0238 {
0239     return m_symbolColumnColor;
0240 }
0241 
0242 bool KeyElement::flossNameColumn() const
0243 {
0244     return m_flossNameColumn;
0245 }
0246 
0247 bool KeyElement::strandsColumn() const
0248 {
0249     return m_strandsColumn;
0250 }
0251 
0252 bool KeyElement::flossDescriptionColumn() const
0253 {
0254     return m_flossDescriptionColumn;
0255 }
0256 
0257 bool KeyElement::stitchesColumn() const
0258 {
0259     return m_stitchesColumn;
0260 }
0261 
0262 bool KeyElement::lengthColumn() const
0263 {
0264     return m_lengthColumn;
0265 }
0266 
0267 bool KeyElement::skeinsColumn() const
0268 {
0269     return m_skeinsColumn;
0270 }
0271 
0272 void KeyElement::setShowBorder(bool showBorder)
0273 {
0274     m_showBorder = showBorder;
0275 }
0276 
0277 void KeyElement::setBorderColor(const QColor &borderColor)
0278 {
0279     m_borderColor = borderColor;
0280 }
0281 
0282 void KeyElement::setBorderThickness(int borderThickness)
0283 {
0284     m_borderThickness = borderThickness;
0285 }
0286 
0287 void KeyElement::setFillBackground(bool fillBackground)
0288 {
0289     m_fillBackground = fillBackground;
0290 }
0291 
0292 void KeyElement::setBackgroundColor(const QColor &backgroundColor)
0293 {
0294     m_backgroundColor = backgroundColor;
0295 }
0296 
0297 void KeyElement::setBackgroundTransparency(int backgroundTransparency)
0298 {
0299     m_backgroundTransparency = backgroundTransparency;
0300 }
0301 
0302 void KeyElement::setMargins(const QMargins &margins)
0303 {
0304     m_margins = margins;
0305 }
0306 
0307 void KeyElement::setTextColor(const QColor &textColor)
0308 {
0309     m_textColor = textColor;
0310 }
0311 
0312 void KeyElement::setTextFont(const QFont &textFont)
0313 {
0314     m_textFont = textFont;
0315 }
0316 
0317 void KeyElement::setIndexStart(int indexStart)
0318 {
0319     m_indexStart = indexStart;
0320 }
0321 
0322 void KeyElement::setIndexCount(int indexCount)
0323 {
0324     m_indexCount = indexCount;
0325 }
0326 
0327 void KeyElement::setSymbolColumn(bool symbolColumn)
0328 {
0329     m_symbolColumn = symbolColumn;
0330 }
0331 
0332 void KeyElement::setSymbolColumnColor(bool symbolColumnColor)
0333 {
0334     m_symbolColumnColor = symbolColumnColor;
0335 }
0336 
0337 void KeyElement::setFlossNameColumn(bool flossNameColumn)
0338 {
0339     m_flossNameColumn = flossNameColumn;
0340 }
0341 
0342 void KeyElement::setStrandsColumn(bool strandsColumn)
0343 {
0344     m_strandsColumn = strandsColumn;
0345 }
0346 
0347 void KeyElement::setFlossDescriptionColumn(bool flossDescriptionColumn)
0348 {
0349     m_flossDescriptionColumn = flossDescriptionColumn;
0350 }
0351 
0352 void KeyElement::setStitchesColumn(bool stitchesColumn)
0353 {
0354     m_stitchesColumn = stitchesColumn;
0355 }
0356 
0357 void KeyElement::setLengthColumn(bool lengthColumn)
0358 {
0359     m_lengthColumn = lengthColumn;
0360 }
0361 
0362 void KeyElement::setSkeinsColumn(bool skeinsColumn)
0363 {
0364     m_skeinsColumn = skeinsColumn;
0365 }
0366 
0367 KeyElement *KeyElement::clone() const
0368 {
0369     return new KeyElement(*this);
0370 }
0371 
0372 void KeyElement::render(Document *document, QPainter *painter) const
0373 {
0374     painter->save();
0375 
0376     double unitLength = (1
0377                          / (document->property(QStringLiteral("horizontalClothCount")).toDouble()
0378                             * (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt()
0379                                                                                             == Configuration::EnumEditor_ClothCountUnits::Centimeters)
0380                                    ? 2.54
0381                                    : 1.0)))
0382         * 0.0254;
0383     QMap<int, FlossUsage> flossUsage = document->pattern()->stitches().flossUsage();
0384     QMap<int, DocumentFloss *> flosses = document->pattern()->palette().flosses();
0385     QVector<int> sortedFlosses = document->pattern()->palette().sortedFlosses();
0386 
0387     FlossScheme *scheme = SchemeManager::scheme(document->pattern()->palette().schemeName());
0388 
0389     double deviceVRatio = double(painter->device()->height()) / double(painter->window().height());
0390 
0391     // set the viewport to be the rectangle converted to device coordinates
0392     painter->setViewport(painter->combinedTransform().mapRect(m_rectangle));
0393     // set the window to be the size of the rectangle in mm which the viewport will be mapped to.
0394     painter->setWindow(0, 0, m_rectangle.width(), m_rectangle.height());
0395 
0396     QPen pen(Qt::NoPen);
0397 
0398     // if a border is valid, draw it, otherwise if drawing on screen draw a faint border to indicate the
0399     // extents of the element.
0400     if (m_showBorder) {
0401         pen = QPen(m_borderColor);
0402         pen.setWidthF(double(m_borderThickness) / 10.0);
0403     } else if (painter->device()->paintEngine() == nullptr) {
0404         // TODO This is a hack to avoid a crash in QWidget::paintEngine returning a null pointer
0405         // There should be a better way to do this.
0406         pen = QPen(Qt::lightGray);
0407         pen.setCosmetic(true);
0408     }
0409 
0410     painter->setPen(pen);
0411 
0412     QColor backgroundColor = m_backgroundColor;
0413     backgroundColor.setAlpha(m_backgroundTransparency);
0414 
0415     if (m_fillBackground) {
0416         painter->setBrush(backgroundColor);
0417     } else {
0418         painter->setBrush(Qt::NoBrush);
0419     }
0420 
0421     painter->drawRect(painter->window());
0422 
0423     // Calculate field widths
0424     QFont font = m_textFont;
0425     font.setPixelSize(int(((font.pointSizeF() / 72.0) * 25.4) * deviceVRatio));
0426 
0427     QRect deviceTextArea = painter->combinedTransform().mapRect(
0428         QRect(0, 0, m_rectangle.width(), m_rectangle.height()).adjusted(m_margins.left(), m_margins.top(), -m_margins.left(), -m_margins.bottom()));
0429 
0430     painter->resetTransform();
0431     painter->setClipRect(deviceTextArea);
0432 
0433     pen = QPen(m_textColor);
0434     painter->setPen(pen);
0435 
0436     painter->setFont(font);
0437 
0438     QFontMetrics fontMetrics(painter->font(), painter->device());
0439     int lineSpacing = fontMetrics.lineSpacing();
0440     int ascent = fontMetrics.ascent();
0441     int y = lineSpacing;
0442     int symbolWidth = 0;
0443     int flossNameWidth = 0;
0444     int strandsWidth = 0;
0445     int flossDescriptionWidth = 0;
0446     int stitchesWidth = 0;
0447     int lengthWidth = 0;
0448     int skeinsWidth = 0;
0449 
0450     QVectorIterator<int> sortedFlossesIterator(sortedFlosses);
0451 
0452     while (sortedFlossesIterator.hasNext()) {
0453         int index = sortedFlossesIterator.next();
0454         FlossUsage usage = flossUsage[index];
0455 
0456         flossNameWidth = std::max(flossNameWidth, fontMetrics.width(flosses[index]->flossName()));
0457         strandsWidth =
0458             std::max(strandsWidth,
0459                      fontMetrics.width(QString::fromLatin1("%1 / %2").arg(flosses[index]->stitchStrands()).arg(flosses[index]->backstitchStrands())));
0460         flossDescriptionWidth = std::max(flossDescriptionWidth, fontMetrics.width(scheme->find(flosses[index]->flossName())->description()));
0461         stitchesWidth = std::max(stitchesWidth, fontMetrics.width(QString::fromLatin1("%1").arg(usage.totalStitches())));
0462         double flossLength = round_n(usage.stitchLength() * unitLength * flosses[index]->stitchStrands()
0463                                          + usage.backstitchLength * unitLength * flosses[index]->backstitchStrands(),
0464                                      2);
0465         lengthWidth = std::max(lengthWidth, fontMetrics.width(QString::fromLatin1("%1").arg(flossLength)));
0466         skeinsWidth = std::max(skeinsWidth, fontMetrics.width(QString::fromLatin1("%1").arg(flossLength / 48))); // 1 skein = 6 strands of 8m
0467     }
0468 
0469     font.setBold(true);
0470     fontMetrics = QFontMetrics(font, painter->device());
0471     int spacing = fontMetrics.averageCharWidth() * 2;
0472 
0473     symbolWidth = std::max(symbolWidth, fontMetrics.width(i18n("Symbol")));
0474     flossNameWidth = std::max(flossNameWidth, fontMetrics.width(i18nc("The name of the floss", "Name")));
0475     strandsWidth = std::max(strandsWidth, fontMetrics.width(i18n("Strands")));
0476     flossDescriptionWidth = std::max(flossDescriptionWidth, fontMetrics.width(i18n("Description")));
0477     stitchesWidth = std::max(stitchesWidth, fontMetrics.width(i18n("Stitches")));
0478     lengthWidth = std::max(lengthWidth, fontMetrics.width(i18n("Length(m)")));
0479     skeinsWidth = std::max(skeinsWidth, fontMetrics.width(i18n("Skeins (8m)")));
0480 
0481     // if a column is not being displayed zero the width, otherwise add the spacing value
0482     symbolWidth = (m_symbolColumn) ? symbolWidth + spacing : 0;
0483     flossNameWidth = (m_flossNameColumn) ? flossNameWidth + spacing : 0;
0484     strandsWidth = (m_strandsColumn) ? strandsWidth + spacing : 0;
0485     flossDescriptionWidth = (m_flossDescriptionColumn) ? flossDescriptionWidth + spacing : 0;
0486     stitchesWidth = (m_stitchesColumn) ? stitchesWidth + spacing : 0;
0487     lengthWidth = (m_lengthColumn) ? lengthWidth + spacing : 0;
0488     skeinsWidth = (m_skeinsColumn) ? skeinsWidth + spacing : 0;
0489 
0490     painter->setFont(font);
0491 
0492     painter->drawText(deviceTextArea.topLeft() + QPoint(0, y), i18nc("%1 is a scheme name", "%1 Flosses", scheme->schemeName()));
0493     y += (2 * lineSpacing);
0494 
0495     if (m_symbolColumn) {
0496         painter->drawText(deviceTextArea.topLeft() + QPointF(0, y), i18n("Symbol"));
0497     }
0498 
0499     if (m_flossNameColumn) {
0500         painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth, y), i18nc("The name of the floss", "Name"));
0501     }
0502 
0503     if (m_strandsColumn) {
0504         painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth, y), i18n("Strands"));
0505     }
0506 
0507     if (m_flossDescriptionColumn) {
0508         painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth, y), i18n("Description"));
0509     }
0510 
0511     if (m_stitchesColumn) {
0512         painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth, y), i18n("Stitches"));
0513     }
0514 
0515     if (m_lengthColumn) {
0516         painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth + stitchesWidth, y),
0517                           i18n("Length(m)"));
0518     }
0519 
0520     if (m_skeinsColumn) {
0521         painter->drawText(deviceTextArea.topLeft()
0522                               + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth + stitchesWidth + lengthWidth, y),
0523                           i18n("Skeins (8m)"));
0524     }
0525 
0526     y += (1.5 * lineSpacing);
0527 
0528     font.setBold(false);
0529     painter->setFont(font);
0530 
0531     sortedFlossesIterator.toFront();
0532     int currentSortedFloss = 0;
0533 
0534     while (sortedFlossesIterator.hasNext()) {
0535         int index = sortedFlossesIterator.next();
0536 
0537         if (currentSortedFloss >= m_indexStart && (m_indexCount == 0 || currentSortedFloss < m_indexStart + m_indexCount)) {
0538             FlossUsage usage = flossUsage[index];
0539 
0540             if (m_symbolColumn) {
0541                 Symbol symbol = SymbolManager::library(document->pattern()->palette().symbolLibrary())->symbol(flosses[index]->stitchSymbol());
0542 
0543                 painter->setViewport(deviceTextArea.left() + symbolWidth / 3,
0544                                      deviceTextArea.top() + y - (lineSpacing - 2 - ((lineSpacing - ascent) / 2)),
0545                                      lineSpacing - 2,
0546                                      lineSpacing - 2);
0547                 painter->setViewport(deviceTextArea.left(),
0548                                      deviceTextArea.top() + y - (lineSpacing - 2 - ((lineSpacing - ascent) / 2)),
0549                                      lineSpacing - 2,
0550                                      lineSpacing - 2);
0551                 painter->setWindow(0, 0, 1, 1);
0552 
0553                 QBrush brush = symbol.brush();
0554                 QPen pen = symbol.pen();
0555 
0556                 if (m_symbolColumnColor) {
0557                     brush.setColor(flosses[index]->flossColor());
0558                     pen.setColor(flosses[index]->flossColor());
0559                 }
0560 
0561                 painter->setBrush(brush);
0562                 painter->setPen(pen);
0563                 painter->drawPath(symbol.path());
0564                 painter->resetTransform();
0565             }
0566 
0567             painter->setPen(Qt::black);
0568 
0569             if (m_flossNameColumn) {
0570                 painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth, y), flosses[index]->flossName());
0571             }
0572 
0573             if (m_strandsColumn) {
0574                 painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth, y),
0575                                   QString::fromLatin1("%1 / %2").arg(flosses[index]->stitchStrands()).arg(flosses[index]->backstitchStrands()));
0576             }
0577 
0578             if (m_flossDescriptionColumn) {
0579                 painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth, y),
0580                                   scheme->find(flosses[index]->flossName())->description());
0581             }
0582 
0583             if (m_stitchesColumn) {
0584                 painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth, y),
0585                                   QString::fromLatin1("%1").arg(usage.totalStitches()));
0586             }
0587 
0588             double totalLength =
0589                 usage.stitchLength() * unitLength * flosses[index]->stitchStrands() + usage.backstitchLength * unitLength * flosses[index]->backstitchStrands();
0590 
0591             if (m_lengthColumn) {
0592                 painter->drawText(deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth + stitchesWidth, y),
0593                                   QString::fromLatin1("%1").arg(round_n(totalLength, 2)));
0594             }
0595 
0596             if (m_skeinsColumn) {
0597                 painter->drawText(
0598                     deviceTextArea.topLeft() + QPointF(symbolWidth + flossNameWidth + strandsWidth + flossDescriptionWidth + stitchesWidth + lengthWidth, y),
0599                     QString::fromLatin1("%1 (%2)").arg(ceil(totalLength / 48)).arg(round_n(totalLength / 48, 2))); // total length / 48m (6 strands of 8m)
0600             }
0601 
0602             y += lineSpacing;
0603         }
0604 
0605         ++currentSortedFloss;
0606     }
0607 
0608     painter->restore();
0609 }
0610 
0611 QDataStream &KeyElement::streamOut(QDataStream &stream) const
0612 {
0613     Element::streamOut(stream);
0614 
0615     stream << qint32(version);
0616 
0617     stream << qint32(m_showBorder) << m_borderColor << qint32(m_borderThickness) << qint32(m_fillBackground) << m_backgroundColor
0618            << qint32(m_backgroundTransparency) << qint32(m_margins.left()) << qint32(m_margins.top()) << qint32(m_margins.right()) << qint32(m_margins.bottom())
0619            << m_textColor << m_textFont << qint32(m_indexStart) << qint32(m_indexCount) << qint32(m_symbolColumn) << qint32(m_symbolColumnColor)
0620            << qint32(m_flossNameColumn) << qint32(m_strandsColumn) << qint32(m_flossDescriptionColumn) << qint32(m_stitchesColumn) << qint32(m_lengthColumn)
0621            << qint32(m_skeinsColumn);
0622 
0623     return stream;
0624 }
0625 
0626 QDataStream &KeyElement::streamIn(QDataStream &stream)
0627 {
0628     Element::streamIn(stream);
0629 
0630     qint32 version;
0631     qint32 showBorder;
0632     qint32 borderThickness;
0633     qint32 fillBackground;
0634     qint32 backgroundTransparency;
0635     qint32 left;
0636     qint32 top;
0637     qint32 right;
0638     qint32 bottom;
0639     qint32 indexStart;
0640     qint32 indexCount;
0641     qint32 symbolColumn;
0642     qint32 symbolColumnColor;
0643     qint32 flossNameColumn;
0644     qint32 strandsColumn;
0645     qint32 flossDescriptionColumn;
0646     qint32 stitchesColumn;
0647     qint32 stitchBreakdownColumn;
0648     qint32 lengthColumn;
0649     qint32 skeinsColumn;
0650     qint32 totalStitchesColumn;
0651 
0652     stream >> version;
0653 
0654     switch (version) {
0655     case 103:
0656         stream >> showBorder >> m_borderColor >> borderThickness >> fillBackground >> m_backgroundColor >> backgroundTransparency >> left >> top >> right
0657             >> bottom >> m_textColor >> m_textFont >> indexStart >> indexCount >> symbolColumn >> symbolColumnColor >> flossNameColumn >> strandsColumn
0658             >> flossDescriptionColumn >> stitchesColumn >> lengthColumn >> skeinsColumn;
0659         m_showBorder = (bool)showBorder;
0660         m_borderThickness = borderThickness;
0661         m_fillBackground = (bool)fillBackground;
0662         m_backgroundTransparency = backgroundTransparency;
0663         m_margins = QMargins(left, top, right, bottom);
0664         m_indexStart = indexStart;
0665         m_indexCount = (indexCount == -1) ? 0 : indexCount;
0666         m_symbolColumn = bool(symbolColumn);
0667         m_symbolColumnColor = bool(symbolColumnColor);
0668         m_flossNameColumn = bool(flossNameColumn);
0669         m_strandsColumn = bool(strandsColumn);
0670         m_flossDescriptionColumn = bool(flossDescriptionColumn);
0671         m_stitchesColumn = bool(stitchesColumn);
0672         m_lengthColumn = bool(lengthColumn);
0673         m_skeinsColumn = bool(skeinsColumn);
0674         break;
0675 
0676     case 102:
0677         stream >> showBorder >> m_borderColor >> borderThickness >> fillBackground >> m_backgroundColor >> backgroundTransparency >> left >> top >> right
0678             >> bottom >> m_textColor >> m_textFont >> indexStart >> indexCount >> symbolColumn >> flossNameColumn >> strandsColumn >> flossDescriptionColumn
0679             >> stitchesColumn >> lengthColumn >> skeinsColumn;
0680         m_showBorder = (bool)showBorder;
0681         m_borderThickness = borderThickness;
0682         m_fillBackground = (bool)fillBackground;
0683         m_backgroundTransparency = backgroundTransparency;
0684         m_margins = QMargins(left, top, right, bottom);
0685         m_indexStart = indexStart;
0686         m_indexCount = (indexCount == -1) ? 0 : indexCount;
0687         m_symbolColumn = bool(symbolColumn);
0688         m_symbolColumnColor = false;
0689         m_flossNameColumn = bool(flossNameColumn);
0690         m_strandsColumn = bool(strandsColumn);
0691         m_flossDescriptionColumn = bool(flossDescriptionColumn);
0692         m_stitchesColumn = bool(stitchesColumn);
0693         m_lengthColumn = bool(lengthColumn);
0694         m_skeinsColumn = bool(skeinsColumn);
0695         break;
0696 
0697     case 101:
0698         stream >> showBorder >> m_borderColor >> borderThickness >> fillBackground >> m_backgroundColor >> backgroundTransparency >> left >> top >> right
0699             >> bottom >> m_textColor >> m_textFont >> symbolColumn >> flossNameColumn >> strandsColumn >> flossDescriptionColumn >> stitchesColumn
0700             >> lengthColumn >> skeinsColumn;
0701         m_showBorder = (bool)showBorder;
0702         m_borderThickness = borderThickness;
0703         m_fillBackground = (bool)fillBackground;
0704         m_backgroundTransparency = backgroundTransparency;
0705         m_margins = QMargins(left, top, right, bottom);
0706         m_indexStart = 0;
0707         m_indexCount = 0;
0708         m_symbolColumn = bool(symbolColumn);
0709         m_symbolColumnColor = false;
0710         m_flossNameColumn = bool(flossNameColumn);
0711         m_strandsColumn = bool(strandsColumn);
0712         m_flossDescriptionColumn = bool(flossDescriptionColumn);
0713         m_stitchesColumn = bool(stitchesColumn);
0714         m_lengthColumn = bool(lengthColumn);
0715         m_skeinsColumn = bool(skeinsColumn);
0716         break;
0717 
0718     case 100:
0719         stream >> showBorder >> m_borderColor >> borderThickness >> fillBackground >> m_backgroundColor >> backgroundTransparency >> left >> top >> right
0720             >> bottom >> m_textColor >> m_textFont >> symbolColumn >> flossNameColumn >> strandsColumn >> flossDescriptionColumn >> stitchesColumn
0721             >> stitchBreakdownColumn >> lengthColumn >> skeinsColumn >> totalStitchesColumn;
0722         m_showBorder = (bool)showBorder;
0723         m_borderThickness = borderThickness;
0724         m_fillBackground = (bool)fillBackground;
0725         m_backgroundTransparency = backgroundTransparency;
0726         m_margins = QMargins(left, top, right, bottom);
0727         m_indexStart = 0;
0728         m_indexCount = 0;
0729         m_symbolColumn = bool(symbolColumn);
0730         m_symbolColumnColor = false;
0731         m_flossNameColumn = bool(flossNameColumn);
0732         m_strandsColumn = bool(strandsColumn);
0733         m_flossDescriptionColumn = bool(flossDescriptionColumn);
0734         m_stitchesColumn = bool(stitchesColumn);
0735         m_lengthColumn = bool(lengthColumn);
0736         m_skeinsColumn = bool(skeinsColumn);
0737         break;
0738 
0739     default:
0740         // not supported
0741         // throw exception
0742         break;
0743     }
0744 
0745     return stream;
0746 }
0747 
0748 PlanElement::PlanElement(Page *parent, const QRect &rectangle, Element::Type type)
0749     : Element(parent, rectangle, type)
0750 {
0751 }
0752 
0753 PlanElement::PlanElement(const PlanElement &other)
0754     : Element(other)
0755     , m_patternRect(other.m_patternRect)
0756 {
0757 }
0758 
0759 void PlanElement::setPatternRect(const QRect &rect)
0760 {
0761     m_patternRect = rect;
0762 }
0763 
0764 PlanElement *PlanElement::clone() const
0765 {
0766     return new PlanElement(*this);
0767 }
0768 
0769 void PlanElement::render(Document *document, QPainter *painter) const
0770 {
0771     painter->save();
0772 
0773     painter->setViewport(painter->combinedTransform().mapRect(m_rectangle));
0774     painter->setWindow(0, 0, m_rectangle.width(), m_rectangle.height());
0775 
0776     int documentWidth = document->pattern()->stitches().width();
0777     int documentHeight = document->pattern()->stitches().height();
0778     double aspect = document->property(QStringLiteral("horizontalClothCount")).toDouble() / document->property(QStringLiteral("verticalClothCount")).toDouble();
0779     double mapWidth = m_rectangle.width() - 1;
0780     double cellWidth = mapWidth / documentWidth;
0781     double cellHeight = cellWidth * aspect;
0782     double patternHeight = cellHeight * documentHeight;
0783     double mapHeight = patternHeight * aspect;
0784 
0785     if (mapHeight > m_rectangle.height() - 1) {
0786         mapHeight = m_rectangle.height() - 1;
0787         mapWidth = ((mapHeight / documentHeight) / aspect) * documentWidth;
0788     }
0789 
0790     cellWidth = mapWidth / documentWidth;
0791     cellHeight = mapHeight / documentHeight;
0792 
0793     double hOffset = ((m_rectangle.width() - mapWidth - 1) / 2);
0794     double vOffset = ((m_rectangle.height() - mapHeight - 1) / 2);
0795 
0796     QRectF page(hOffset, vOffset, mapWidth, mapHeight);
0797     QRectF pattern(m_patternRect.left() * cellWidth, m_patternRect.top() * cellHeight, m_patternRect.width() * cellWidth, m_patternRect.height() * cellHeight);
0798 
0799     QPen pen(Qt::black);
0800     pen.setWidthF(0.05);
0801 
0802     painter->setPen(pen);
0803     painter->setBrush(Qt::black);
0804     painter->drawRect(page.translated(0.5, 0.5)); // drop shadow
0805     painter->setBrush(Qt::white);
0806     painter->drawRect(page);
0807     painter->setBrush(Qt::lightGray);
0808     painter->drawRect(pattern.translated(hOffset, vOffset));
0809 
0810     painter->restore();
0811 }
0812 
0813 QDataStream &PlanElement::streamOut(QDataStream &stream) const
0814 {
0815     Element::streamOut(stream);
0816 
0817     // no need to stream the m_patternRect variable as it will be set by the PatternElement class when read
0818 
0819     return stream;
0820 }
0821 
0822 QDataStream &PlanElement::streamIn(QDataStream &stream)
0823 {
0824     Element::streamIn(stream);
0825 
0826     return stream;
0827 }
0828 
0829 PatternElement::PatternElement(Page *parent, const QRect &rectangle, Element::Type type)
0830     : Element(parent, rectangle, type)
0831     , m_showBorder(Configuration::patternElement_ShowBorder())
0832     , m_borderColor(Configuration::patternElement_BorderColor())
0833     , m_borderThickness(Configuration::patternElement_BorderThickness())
0834     , m_showScales(false)
0835     , m_showPlan(false)
0836     , m_formatScalesAs(Configuration::editor_FormatScalesAs())
0837     , m_renderStitchesAs(Configuration::renderer_RenderStitchesAs())
0838     , m_renderBackstitchesAs(Configuration::renderer_RenderBackstitchesAs())
0839     , m_renderKnotsAs(Configuration::renderer_RenderKnotsAs())
0840     , m_showGrid(true)
0841     , m_showStitches(true)
0842     , m_showBackstitches(true)
0843     , m_showKnots(true)
0844     , m_planElement(nullptr)
0845 {
0846 }
0847 
0848 PatternElement::PatternElement(const PatternElement &other)
0849     : Element(other)
0850     , m_showBorder(other.m_showBorder)
0851     , m_borderColor(other.m_borderColor)
0852     , m_borderThickness(other.m_borderThickness)
0853     , m_patternRect(other.m_patternRect)
0854     , m_showScales(other.m_showScales)
0855     , m_showPlan(other.m_showPlan)
0856     , m_formatScalesAs(other.m_formatScalesAs)
0857     , m_renderStitchesAs(other.m_renderStitchesAs)
0858     , m_renderBackstitchesAs(other.m_renderBackstitchesAs)
0859     , m_renderKnotsAs(other.m_renderKnotsAs)
0860     , m_showGrid(other.m_showGrid)
0861     , m_showStitches(other.m_showStitches)
0862     , m_showBackstitches(other.m_showBackstitches)
0863     , m_showKnots(other.m_showKnots)
0864     , m_planElement(nullptr)
0865 {
0866     if (other.m_planElement) {
0867         m_planElement = new PlanElement(*other.m_planElement);
0868     }
0869 }
0870 
0871 bool PatternElement::showBorder() const
0872 {
0873     return m_showBorder;
0874 }
0875 
0876 QColor PatternElement::borderColor() const
0877 {
0878     return m_borderColor;
0879 }
0880 
0881 int PatternElement::borderThickness() const
0882 {
0883     return m_borderThickness;
0884 }
0885 
0886 void PatternElement::setShowBorder(bool showBorder)
0887 {
0888     m_showBorder = showBorder;
0889 }
0890 
0891 void PatternElement::setBorderColor(const QColor &borderColor)
0892 {
0893     m_borderColor = borderColor;
0894 }
0895 
0896 void PatternElement::setBorderThickness(int borderThickness)
0897 {
0898     m_borderThickness = borderThickness;
0899 }
0900 
0901 PatternElement *PatternElement::clone() const
0902 {
0903     return new PatternElement(*this);
0904 }
0905 
0906 void PatternElement::render(Document *document, QPainter *painter) const
0907 {
0908     Renderer renderer;
0909     renderer.setRenderStitchesAs(m_renderStitchesAs);
0910     renderer.setRenderBackstitchesAs(m_renderBackstitchesAs);
0911     renderer.setRenderKnotsAs(m_renderKnotsAs);
0912 
0913     int scaleSize = (m_showScales) ? 6 : 0;
0914 
0915     painter->save();
0916 
0917     QPen pen;
0918     pen.setWidthF(0.05);
0919     painter->setPen(pen);
0920     painter->setBrush(Qt::black);
0921 
0922     double deviceHRatio = double(painter->device()->width()) / double(painter->window().width());
0923     double deviceVRatio = double(painter->device()->height()) / double(painter->window().height());
0924 
0925     int documentWidth = document->pattern()->stitches().width();
0926     int documentHeight = document->pattern()->stitches().height();
0927 
0928     double horizontalClothCount = document->property(QStringLiteral("horizontalClothCount")).toDouble();
0929     double verticalClothCount = document->property(QStringLiteral("verticalClothCount")).toDouble();
0930 
0931     bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt())
0932                                   == Configuration::EnumEditor_ClothCountUnits::Inches);
0933 
0934     // calculate the aspect ratio an the size of the cells to fit within the rectangle and the overall paint area size
0935     double patternWidth = m_rectangle.width() - scaleSize;
0936     double aspect = horizontalClothCount / verticalClothCount;
0937     double cellWidth = patternWidth / m_patternRect.width();
0938     double cellHeight = cellWidth * aspect;
0939     double patternHeight = cellHeight * m_patternRect.height();
0940 
0941     if (patternHeight > m_rectangle.height() - scaleSize) {
0942         patternHeight = m_rectangle.height() - scaleSize;
0943         patternWidth = ((patternHeight / m_patternRect.height()) / aspect) * m_patternRect.width();
0944         cellWidth = patternWidth / m_patternRect.width();
0945         cellHeight = patternHeight / m_patternRect.height();
0946     }
0947 
0948     int cellHorizontalGrouping = document->property(QStringLiteral("cellHorizontalGrouping")).toInt();
0949     int cellVerticalGrouping = document->property(QStringLiteral("cellVerticalGrouping")).toInt();
0950 
0951     renderer.setCellGrouping(cellHorizontalGrouping, cellVerticalGrouping);
0952     renderer.setGridLineWidths(Configuration::editor_ThinLineWidth(), Configuration::editor_ThickLineWidth());
0953     renderer.setGridLineColors(document->property(QStringLiteral("thinLineColor")).value<QColor>(),
0954                                document->property(QStringLiteral("thickLineColor")).value<QColor>());
0955 
0956     // find the position of the top left coordinate of the top left cell of the cells to be printed
0957     double patternHOffset = ((double(m_rectangle.width()) - double(patternWidth + scaleSize)) / 2);
0958     double patternVOffset = ((double(m_rectangle.height()) - double(patternHeight + scaleSize)) / 2);
0959 
0960     int vpLeft = int(deviceHRatio * (patternHOffset + m_rectangle.left()));
0961     int vpTop = int(deviceVRatio * (patternVOffset + m_rectangle.top()));
0962     int vpWidth = int((patternWidth + scaleSize) * deviceHRatio);
0963     int vpHeight = int((patternHeight + scaleSize) * deviceVRatio);
0964 
0965     double vpCellWidth = deviceHRatio * cellWidth;
0966     double vpScaleWidth = deviceHRatio * scaleSize;
0967     double vpScaleHeight = deviceVRatio * scaleSize;
0968 
0969     if (m_showScales) {
0970         QFont font = painter->font();
0971         font.setPixelSize(int(deviceVRatio * 2));
0972         painter->setFont(font);
0973 
0974         // draw horizontal ruler
0975         // default to Stitches values
0976         double subTick = 1.0;
0977         int minorTicks = 1;
0978         int majorTicks = cellHorizontalGrouping;
0979 
0980         int textValueIncrement = cellHorizontalGrouping;
0981 
0982         switch (m_formatScalesAs) {
0983         case Configuration::EnumEditor_FormatScalesAs::Stitches:
0984             // set as defaults above
0985             break;
0986 
0987         case Configuration::EnumEditor_FormatScalesAs::Centimeters:
0988             // subtick should be 1/10 Centimeters
0989             subTick = horizontalClothCount / (clothCountUnitsInches ? 25.4 : 10);
0990             minorTicks = 5;
0991             majorTicks = 10;
0992             textValueIncrement = 1;
0993             break;
0994 
0995         case Configuration::EnumEditor_FormatScalesAs::Inches:
0996             // subtick should be 1/16 inch
0997             subTick = horizontalClothCount / (clothCountUnitsInches ? 16 : 6.299);
0998             minorTicks = 4;
0999             majorTicks = 16;
1000             textValueIncrement = 1;
1001             break;
1002 
1003         default:
1004             break;
1005         }
1006 
1007         painter->setViewport(vpLeft + vpScaleWidth, vpTop, vpWidth - vpScaleWidth, vpScaleHeight);
1008         painter->setWindow(m_patternRect.left(), 0, m_patternRect.width(), 1);
1009         painter->drawLine(QLineF(m_patternRect.left(), 0.9, m_patternRect.right() + 1, 0.9));
1010 
1011         int ticks = int(double(documentWidth) / subTick);
1012 
1013         for (int i = 0; i <= ticks; ++i) {
1014             double ticklen = 0.8;
1015             double tickPosition = subTick * i;
1016 
1017             if ((i % minorTicks) == 0) {
1018                 ticklen = 0.7;
1019             }
1020 
1021             if ((i % majorTicks) == 0) {
1022                 ticklen = 0.6;
1023             }
1024 
1025             if (tickPosition >= m_patternRect.left() && tickPosition <= m_patternRect.right() + 1) {
1026                 painter->drawLine(QPointF(tickPosition, ticklen), QPointF(tickPosition, 0.9));
1027             }
1028         }
1029 
1030         QTransform transform = painter->combinedTransform();
1031 
1032         double patternHCenter = double(documentWidth) / 2;
1033         QPoint vpPatternHCenter = transform.map(QPointF(patternHCenter, 0.9)).toPoint();
1034 
1035         painter->resetTransform();
1036 
1037         if (patternHCenter >= m_patternRect.left() && patternHCenter <= m_patternRect.right() + 1) {
1038             QPolygon patternHCenterMarker;
1039             int markerSize = deviceHRatio * scaleSize / 3;
1040             patternHCenterMarker << vpPatternHCenter << vpPatternHCenter + QPoint(-markerSize / 2, -markerSize)
1041                                  << vpPatternHCenter + QPoint(markerSize / 2, -markerSize);
1042             painter->drawPolygon(patternHCenterMarker);
1043         }
1044 
1045         for (int i = 0; i <= ticks; ++i) {
1046             int tickPosition = transform.map(QPointF(subTick * i, 0)).toPoint().x();
1047 
1048             if (tickPosition >= vpLeft + vpScaleWidth && tickPosition <= vpLeft + vpWidth && (i % majorTicks) == 0) {
1049                 painter->drawText(QRect(tickPosition - vpCellWidth, vpTop, vpCellWidth * 2, int(3 * deviceVRatio)),
1050                                   Qt::AlignHCenter | Qt::AlignBottom,
1051                                   QString::fromLatin1("%1").arg((i / majorTicks) * textValueIncrement));
1052             }
1053         }
1054 
1055         // draw vertical ruler
1056         switch (m_formatScalesAs) {
1057         case Configuration::EnumEditor_FormatScalesAs::Stitches:
1058             // subTick should be 1 cell
1059             subTick = 1.0;
1060             minorTicks = 1;
1061             majorTicks = cellVerticalGrouping;
1062             textValueIncrement = cellVerticalGrouping;
1063             break;
1064 
1065         case Configuration::EnumEditor_FormatScalesAs::Centimeters:
1066             // subTick should be 1/10 Centimeters
1067             subTick = verticalClothCount / (clothCountUnitsInches ? 25.4 : 10);
1068             minorTicks = 5;
1069             majorTicks = 10;
1070             textValueIncrement = 1;
1071             break;
1072 
1073         case Configuration::EnumEditor_FormatScalesAs::Inches:
1074             // subTick should be 1/16 inch
1075             subTick = verticalClothCount / (clothCountUnitsInches ? 16 : 6.299);
1076             minorTicks = 4;
1077             majorTicks = 16;
1078             textValueIncrement = 1;
1079             break;
1080 
1081         default:
1082             break;
1083         }
1084 
1085         painter->setViewport(vpLeft, vpTop + vpScaleHeight, vpScaleWidth, vpHeight - vpScaleHeight);
1086         painter->setWindow(0, m_patternRect.top(), 1, m_patternRect.height());
1087         painter->drawLine(QLineF(0.9, m_patternRect.top(), 0.9, m_patternRect.bottom() + 1));
1088 
1089         ticks = int(double(documentHeight) / subTick);
1090 
1091         for (int i = 0; i <= ticks; ++i) {
1092             double ticklen = 0.8;
1093             double tickPosition = subTick * i;
1094 
1095             if ((i % minorTicks) == 0) {
1096                 ticklen = 0.7;
1097             }
1098 
1099             if ((i % majorTicks) == 0) {
1100                 ticklen = 0.6;
1101             }
1102 
1103             if (tickPosition >= m_patternRect.top() && tickPosition <= m_patternRect.bottom() + 1) {
1104                 painter->drawLine(QPointF(ticklen, tickPosition), QPointF(0.9, tickPosition));
1105             }
1106         }
1107 
1108         transform = painter->combinedTransform();
1109 
1110         double patternVCenter = double(documentHeight) / 2;
1111         QPoint vpPatternVCenter = transform.map(QPointF(0.9, patternVCenter)).toPoint();
1112 
1113         painter->resetTransform();
1114 
1115         if (patternVCenter >= m_patternRect.top() && patternVCenter <= m_patternRect.bottom() + 1) {
1116             QPolygon patternVCenterMarker;
1117             int markerSize = deviceVRatio * scaleSize / 3;
1118             patternVCenterMarker << vpPatternVCenter << vpPatternVCenter + QPoint(-markerSize, -markerSize / 2)
1119                                  << vpPatternVCenter + QPoint(-markerSize, markerSize / 2);
1120             painter->drawPolygon(patternVCenterMarker);
1121         }
1122 
1123         for (int i = 0; i <= ticks; ++i) {
1124             int tickPosition = transform.map(QPointF(0, subTick * i)).toPoint().y();
1125 
1126             if (tickPosition >= vpTop + vpScaleHeight && tickPosition <= vpTop + vpHeight && (i % majorTicks) == 0) {
1127                 painter->drawText(QRect(vpLeft, tickPosition - vpScaleHeight, int(3 * deviceHRatio), vpScaleHeight * 2),
1128                                   Qt::AlignRight | Qt::AlignVCenter,
1129                                   QString::fromLatin1("%1").arg((i / majorTicks) * textValueIncrement));
1130             }
1131         }
1132 
1133         painter->setViewport(vpLeft + vpScaleWidth, vpTop + vpScaleHeight, vpWidth - vpScaleWidth, vpHeight - vpScaleHeight);
1134         painter->setWindow(m_patternRect);
1135         painter->setClipRect(m_patternRect);
1136     } else {
1137         painter->setViewport(vpLeft, vpTop, vpWidth, vpHeight);
1138         painter->setWindow(m_patternRect);
1139         painter->setClipRect(m_patternRect);
1140     }
1141 
1142     renderer.render(painter, document->pattern(), m_patternRect, m_showGrid, m_showStitches, m_showBackstitches, m_showKnots, -1);
1143 
1144     if (m_showBorder) {
1145         QPen pen(m_borderColor);
1146         pen.setWidthF(m_borderThickness / 10.0);
1147         painter->setPen(pen);
1148         painter->setBrush(Qt::NoBrush);
1149         painter->drawRect(m_patternRect);
1150     }
1151 
1152     painter->restore();
1153 }
1154 
1155 QRect PatternElement::patternRect() const
1156 {
1157     return m_patternRect;
1158 }
1159 
1160 bool PatternElement::showScales() const
1161 {
1162     return m_showScales;
1163 }
1164 
1165 bool PatternElement::showPlan() const
1166 {
1167     return m_showPlan;
1168 }
1169 
1170 Element *PatternElement::planElement() const
1171 {
1172     return m_planElement;
1173 }
1174 
1175 Configuration::EnumRenderer_RenderStitchesAs::type PatternElement::renderStitchesAs() const
1176 {
1177     return m_renderStitchesAs;
1178 }
1179 
1180 Configuration::EnumRenderer_RenderBackstitchesAs::type PatternElement::renderBackstitchesAs() const
1181 {
1182     return m_renderBackstitchesAs;
1183 }
1184 
1185 Configuration::EnumRenderer_RenderKnotsAs::type PatternElement::renderKnotsAs() const
1186 {
1187     return m_renderKnotsAs;
1188 }
1189 
1190 bool PatternElement::showGrid() const
1191 {
1192     return m_showGrid;
1193 }
1194 
1195 bool PatternElement::showStitches() const
1196 {
1197     return m_showStitches;
1198 }
1199 
1200 bool PatternElement::showBackstitches() const
1201 {
1202     return m_showBackstitches;
1203 }
1204 
1205 bool PatternElement::showKnots() const
1206 {
1207     return m_showKnots;
1208 }
1209 
1210 void PatternElement::setPatternRect(const QRect &patternRect)
1211 {
1212     m_patternRect = patternRect;
1213 }
1214 
1215 void PatternElement::setShowScales(bool showScales)
1216 {
1217     m_showScales = showScales;
1218 }
1219 
1220 void PatternElement::setShowPlan(bool showPlan)
1221 {
1222     m_showPlan = showPlan;
1223 }
1224 
1225 void PatternElement::setRenderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::type renderStitchesAs)
1226 {
1227     m_renderStitchesAs = renderStitchesAs;
1228 }
1229 
1230 void PatternElement::setRenderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::type renderBackstitchesAs)
1231 {
1232     m_renderBackstitchesAs = renderBackstitchesAs;
1233 }
1234 
1235 void PatternElement::setRenderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::type renderKnotsAs)
1236 {
1237     m_renderKnotsAs = renderKnotsAs;
1238 }
1239 
1240 void PatternElement::setShowGrid(bool showGrid)
1241 {
1242     m_showGrid = showGrid;
1243 }
1244 
1245 void PatternElement::setShowStitches(bool showStitches)
1246 {
1247     m_showStitches = showStitches;
1248 }
1249 
1250 void PatternElement::setShowBackstitches(bool showBackstitches)
1251 {
1252     m_showBackstitches = showBackstitches;
1253 }
1254 
1255 void PatternElement::setShowKnots(bool showKnots)
1256 {
1257     m_showKnots = showKnots;
1258 }
1259 
1260 QDataStream &PatternElement::streamOut(QDataStream &stream) const
1261 {
1262     Element::streamOut(stream);
1263 
1264     stream << qint32(version);
1265 
1266     stream << qint32(m_showBorder) << m_borderColor << qint32(m_borderThickness) << m_patternRect << qint32(m_showScales) << qint32(m_showPlan)
1267            << qint32(m_formatScalesAs) << qint32(m_renderStitchesAs) << qint32(m_renderBackstitchesAs) << qint32(m_renderKnotsAs) << qint32(m_showGrid)
1268            << qint32(m_showStitches) << qint32(m_showBackstitches) << qint32(m_showKnots) << qint32((m_planElement == nullptr) ? false : true);
1269 
1270     if (m_planElement) {
1271         stream << *m_planElement;
1272     }
1273 
1274     return stream;
1275 }
1276 
1277 QDataStream &PatternElement::streamIn(QDataStream &stream)
1278 {
1279     Element::streamIn(stream);
1280 
1281     qint32 version;
1282     qint32 showBorder;
1283     qint32 borderThickness;
1284     qint32 showScales;
1285     qint32 showPlan;
1286     qint32 formatScalesAs;
1287     qint32 renderStitchesAs;
1288     qint32 renderBackstitchesAs;
1289     qint32 renderKnotsAs;
1290     qint32 showGrid;
1291     qint32 showStitches;
1292     qint32 showBackstitches;
1293     qint32 showKnots;
1294     qint32 planElement;
1295     QList<int> visibleLayers;
1296     QList<int> layerOrder;
1297 
1298     stream >> version;
1299 
1300     switch (version) {
1301     case 102:
1302         stream >> showBorder >> m_borderColor >> borderThickness >> m_patternRect >> showScales >> showPlan >> formatScalesAs >> renderStitchesAs
1303             >> renderBackstitchesAs >> renderKnotsAs >> showGrid >> showStitches >> showBackstitches >> showKnots >> planElement;
1304 
1305         m_showBorder = bool(showBorder);
1306         m_borderThickness = borderThickness;
1307         m_showScales = bool(showScales);
1308         m_showPlan = bool(showPlan);
1309         m_formatScalesAs = static_cast<Configuration::EnumEditor_FormatScalesAs::type>(formatScalesAs);
1310         m_renderStitchesAs = static_cast<Configuration::EnumRenderer_RenderStitchesAs::type>(renderStitchesAs);
1311         m_renderBackstitchesAs = static_cast<Configuration::EnumRenderer_RenderBackstitchesAs::type>(renderBackstitchesAs);
1312         m_renderKnotsAs = static_cast<Configuration::EnumRenderer_RenderKnotsAs::type>(renderKnotsAs);
1313         m_showGrid = bool(showGrid);
1314         m_showStitches = bool(showStitches);
1315         m_showBackstitches = bool(showBackstitches);
1316         m_showKnots = bool(showKnots);
1317 
1318         if (m_showPlan) {
1319             m_planElement = new PlanElement(parent(), QRect());
1320             stream >> *m_planElement;
1321             m_planElement->setPatternRect(m_patternRect);
1322         } else {
1323             m_planElement = nullptr;
1324         }
1325 
1326         break;
1327 
1328     case 101:
1329         stream >> m_patternRect >> showScales >> showPlan >> formatScalesAs >> renderStitchesAs >> renderBackstitchesAs >> renderKnotsAs >> showGrid
1330             >> showStitches >> showBackstitches >> showKnots >> planElement;
1331 
1332         m_showScales = bool(showScales);
1333         m_showPlan = bool(showPlan);
1334         m_formatScalesAs = static_cast<Configuration::EnumEditor_FormatScalesAs::type>(formatScalesAs);
1335         m_renderStitchesAs = static_cast<Configuration::EnumRenderer_RenderStitchesAs::type>(renderStitchesAs);
1336         m_renderBackstitchesAs = static_cast<Configuration::EnumRenderer_RenderBackstitchesAs::type>(renderBackstitchesAs);
1337         m_renderKnotsAs = static_cast<Configuration::EnumRenderer_RenderKnotsAs::type>(renderKnotsAs);
1338         m_showGrid = bool(showGrid);
1339         m_showStitches = bool(showStitches);
1340         m_showBackstitches = bool(showBackstitches);
1341         m_showKnots = bool(showKnots);
1342 
1343         if (m_showPlan) {
1344             m_planElement = new PlanElement(parent(), QRect());
1345             stream >> *m_planElement;
1346             m_planElement->setPatternRect(m_patternRect);
1347         } else {
1348             m_planElement = nullptr;
1349         }
1350 
1351         break;
1352 
1353     case 100:
1354         stream >> m_patternRect >> showScales >> showPlan >> formatScalesAs >> renderStitchesAs >> renderBackstitchesAs >> renderKnotsAs >> visibleLayers
1355             >> layerOrder >> showGrid >> showStitches >> showBackstitches >> showKnots >> planElement;
1356 
1357         m_showScales = bool(showScales);
1358         m_showPlan = bool(showPlan);
1359         m_formatScalesAs = static_cast<Configuration::EnumEditor_FormatScalesAs::type>(formatScalesAs);
1360         m_renderStitchesAs = static_cast<Configuration::EnumRenderer_RenderStitchesAs::type>(renderStitchesAs);
1361         m_renderBackstitchesAs = static_cast<Configuration::EnumRenderer_RenderBackstitchesAs::type>(renderBackstitchesAs);
1362         m_renderKnotsAs = static_cast<Configuration::EnumRenderer_RenderKnotsAs::type>(renderKnotsAs);
1363         m_showGrid = bool(showGrid);
1364         m_showStitches = bool(showStitches);
1365         m_showBackstitches = bool(showBackstitches);
1366         m_showKnots = bool(showKnots);
1367 
1368         if (bool(planElement)) {
1369             m_planElement = new PlanElement(parent(), QRect());
1370             stream >> *m_planElement;
1371             m_planElement->setPatternRect(m_patternRect);
1372         } else {
1373             m_planElement = nullptr;
1374         }
1375 
1376         break;
1377 
1378     default:
1379         // not supported
1380         // throw exception
1381         break;
1382     }
1383 
1384     /* A bug was introduced in commit d59704c30d709a23dd8f9e66fdf98e9d15702d07 causing patterns
1385      * created before this version to have rendering modes +1 from those currently.
1386      * The following code rectifies the limits. Other patterns created before this change will be
1387      * valid but will not be as the user originally set.
1388      */
1389     if (m_renderStitchesAs > 4) {
1390         m_renderStitchesAs = static_cast<Configuration::EnumRenderer_RenderStitchesAs::type>(4);
1391     }
1392 
1393     if (m_renderBackstitchesAs > 1) {
1394         m_renderBackstitchesAs = static_cast<Configuration::EnumRenderer_RenderBackstitchesAs::type>(1);
1395     }
1396 
1397     if (m_renderKnotsAs > 3) {
1398         m_renderKnotsAs = static_cast<Configuration::EnumRenderer_RenderKnotsAs::type>(3);
1399     }
1400 
1401     return stream;
1402 }
1403 
1404 ImageElement::ImageElement(Page *parent, const QRect &rectangle, Element::Type type)
1405     : PatternElement(parent, rectangle, type)
1406 {
1407     setShowBorder(Configuration::imageElement_ShowBorder());
1408     setBorderColor(Configuration::imageElement_BorderColor());
1409     setBorderThickness(Configuration::imageElement_BorderThickness());
1410     setShowScales(false);
1411     setShowPlan(false);
1412     setRenderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks);
1413     setRenderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines);
1414     setRenderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks);
1415     setShowGrid(false);
1416     setShowStitches(true);
1417     setShowBackstitches(true);
1418     setShowKnots(true);
1419 }
1420 
1421 ImageElement::ImageElement(const ImageElement &other)
1422     : PatternElement(other)
1423 {
1424 }
1425 
1426 ImageElement *ImageElement::clone() const
1427 {
1428     return new ImageElement(*this);
1429 }
1430 
1431 void ImageElement::render(Document *document, QPainter *painter) const
1432 {
1433     PatternElement::render(document, painter);
1434 }
1435 
1436 QDataStream &ImageElement::streamOut(QDataStream &stream) const
1437 {
1438     PatternElement::streamOut(stream);
1439 
1440     stream << qint32(version); // stream the version in case of future expansion
1441     // All other variables held in the base class
1442 
1443     return stream;
1444 }
1445 
1446 QDataStream &ImageElement::streamIn(QDataStream &stream)
1447 {
1448     PatternElement::streamIn(stream);
1449 
1450     qint32 version;
1451 
1452     stream >> version;
1453 
1454     switch (version) {
1455     case 100:
1456         // nothing to stream in for this version
1457         break;
1458 
1459     default:
1460         // not supported
1461         // throw exception
1462         break;
1463     }
1464 
1465     return stream;
1466 }
1467 
1468 TextElement::TextElement(Page *parent, const QRect &rectangle, Element::Type type)
1469     : Element(parent, rectangle, type)
1470     , m_showBorder(Configuration::textElement_ShowBorder())
1471     , m_borderColor(Configuration::textElement_BorderColor())
1472     , m_borderThickness(Configuration::textElement_BorderThickness())
1473     , m_fillBackground(Configuration::textElement_FillBackground())
1474     , m_backgroundColor(Configuration::textElement_BackgroundColor())
1475     , m_backgroundTransparency(Configuration::textElement_BackgroundTransparency())
1476     , m_margins(QMargins(Configuration::textElement_MarginTop(),
1477                          Configuration::textElement_MarginLeft(),
1478                          Configuration::textElement_MarginRight(),
1479                          Configuration::textElement_MarginBottom()))
1480     , m_textColor(Configuration::textElement_TextColor())
1481 {
1482 }
1483 
1484 TextElement::TextElement(const TextElement &other)
1485     : Element(other)
1486     , m_showBorder(other.m_showBorder)
1487     , m_borderColor(other.m_borderColor)
1488     , m_borderThickness(other.m_borderThickness)
1489     , m_fillBackground(other.m_fillBackground)
1490     , m_backgroundColor(other.m_backgroundColor)
1491     , m_backgroundTransparency(other.m_backgroundTransparency)
1492     , m_margins(other.m_margins)
1493     , m_textFont(other.m_textFont)
1494     , m_textColor(other.m_textColor)
1495     , m_alignment(other.m_alignment)
1496     , m_text(other.m_text)
1497 {
1498 }
1499 
1500 bool TextElement::showBorder() const
1501 {
1502     return m_showBorder;
1503 }
1504 
1505 QColor TextElement::borderColor() const
1506 {
1507     return m_borderColor;
1508 }
1509 
1510 int TextElement::borderThickness() const
1511 {
1512     return m_borderThickness;
1513 }
1514 
1515 bool TextElement::fillBackground() const
1516 {
1517     return m_fillBackground;
1518 }
1519 
1520 QColor TextElement::backgroundColor() const
1521 {
1522     return m_backgroundColor;
1523 }
1524 
1525 int TextElement::backgroundTransparency() const
1526 {
1527     return m_backgroundTransparency;
1528 }
1529 
1530 QMargins TextElement::margins() const
1531 {
1532     return m_margins;
1533 }
1534 
1535 QFont TextElement::textFont() const
1536 {
1537     return m_textFont;
1538 }
1539 
1540 QColor TextElement::textColor() const
1541 {
1542     return m_textColor;
1543 }
1544 
1545 Qt::Alignment TextElement::alignment() const
1546 {
1547     return m_alignment;
1548 }
1549 
1550 QString TextElement::text() const
1551 {
1552     return m_text;
1553 }
1554 
1555 void TextElement::setShowBorder(bool showBorder)
1556 {
1557     m_showBorder = showBorder;
1558 }
1559 
1560 void TextElement::setBorderColor(const QColor &borderColor)
1561 {
1562     m_borderColor = borderColor;
1563 }
1564 
1565 void TextElement::setBorderThickness(int borderThickness)
1566 {
1567     m_borderThickness = borderThickness;
1568     ;
1569 }
1570 
1571 void TextElement::setFillBackground(bool fillBackground)
1572 {
1573     m_fillBackground = fillBackground;
1574 }
1575 
1576 void TextElement::setBackgroundColor(const QColor &backgroundColor)
1577 {
1578     m_backgroundColor = backgroundColor;
1579 }
1580 
1581 void TextElement::setBackgroundTransparency(int backgroundTransparency)
1582 {
1583     m_backgroundTransparency = backgroundTransparency;
1584 }
1585 
1586 void TextElement::setMargins(const QMargins &margins)
1587 {
1588     m_margins = margins;
1589 }
1590 
1591 void TextElement::setTextFont(const QFont &textFont)
1592 {
1593     m_textFont = textFont;
1594 }
1595 
1596 void TextElement::setTextColor(const QColor &textColor)
1597 {
1598     m_textColor = textColor;
1599 }
1600 
1601 void TextElement::setAlignment(Qt::Alignment alignment)
1602 {
1603     m_alignment = alignment;
1604 }
1605 
1606 void TextElement::setText(const QString &text)
1607 {
1608     if (text.contains(QLatin1String("<html>"))) {
1609         m_text = text;
1610     } else {
1611         m_text = encodeToHtml(text);
1612     }
1613 }
1614 
1615 TextElement *TextElement::clone() const
1616 {
1617     return new TextElement(*this);
1618 }
1619 
1620 void TextElement::render(Document *document, QPainter *painter) const
1621 {
1622     painter->save();
1623 
1624     double pageWidthMM = painter->window().width();
1625     double pageHeightMM = painter->window().height();
1626     double deviceWidthPixels = painter->device()->width();
1627     double deviceHeightPixels = painter->device()->height();
1628     double devicePageWidthPixels = (double(pageWidthMM) / 25.4) * painter->device()->logicalDpiX();
1629     double devicePageHeightPixels = (double(pageHeightMM) / 25.4) * painter->device()->logicalDpiY();
1630 
1631     double deviceHRatio = devicePageWidthPixels / deviceWidthPixels;
1632 
1633     // set the viewport to be the rectangle converted to device coordinates
1634     painter->setViewport(painter->combinedTransform().mapRect(m_rectangle));
1635     // set the window to be the size of the rectangle in mm which the viewport will be mapped to.
1636     painter->setWindow(0, 0, m_rectangle.width(), m_rectangle.height());
1637 
1638     QPen pen(Qt::NoPen);
1639 
1640     // if a border is valid, draw it, otherwise if drawing on screen draw a faint border to indicate the
1641     // extents of the element.
1642     if (m_showBorder) {
1643         pen = QPen(m_borderColor);
1644         pen.setWidthF(double(m_borderThickness) / 10.0);
1645     } else if (painter->device()->paintEngine() == nullptr) {
1646         // TODO This is a hack to avoid a crash in QWidget::paintEngine returning a null pointer
1647         // There should be a better way to do this.
1648         pen = QPen(Qt::lightGray);
1649         pen.setCosmetic(true);
1650     }
1651 
1652     painter->setPen(pen);
1653 
1654     QColor backgroundColor = m_backgroundColor;
1655     backgroundColor.setAlpha(m_backgroundTransparency);
1656 
1657     if (m_fillBackground) {
1658         painter->setBrush(backgroundColor);
1659     } else {
1660         painter->setBrush(Qt::NoBrush);
1661     }
1662 
1663     painter->drawRect(painter->window());
1664 
1665     QRect deviceTextArea = painter->combinedTransform().mapRect(
1666         QRect(0, 0, m_rectangle.width(), m_rectangle.height()).adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom()));
1667 
1668     painter->resetTransform();
1669     painter->translate(deviceTextArea.topLeft());
1670     painter->scale(deviceWidthPixels / devicePageWidthPixels, deviceHeightPixels / devicePageHeightPixels);
1671 
1672     QTextDocument textDocument;
1673     textDocument.documentLayout()->setPaintDevice(painter->device());
1674     textDocument.setHtml(convertedText(document));
1675     textDocument.setTextWidth(deviceHRatio * deviceTextArea.width());
1676     textDocument.drawContents(painter);
1677 
1678     painter->restore();
1679 }
1680 
1681 QString TextElement::convertedText(Document *document) const
1682 {
1683     QString replacement = m_text;
1684     replacement.replace(QRegExp(QStringLiteral("\\$\\{title\\}")), document->property(QStringLiteral("title")).toString());
1685     replacement.replace(QRegExp(QStringLiteral("\\$\\{author\\}")), document->property(QStringLiteral("author")).toString());
1686     replacement.replace(QRegExp(QStringLiteral("\\$\\{copyright\\}")), document->property(QStringLiteral("copyright")).toString());
1687     replacement.replace(QRegExp(QStringLiteral("\\$\\{fabric\\}")), document->property(QStringLiteral("fabric")).toString());
1688     replacement.replace(QRegExp(QStringLiteral("\\$\\{instructions\\}")), document->property(QStringLiteral("instructions")).toString());
1689     replacement.replace(QRegExp(QStringLiteral("\\$\\{horizontalClothCount\\}")), document->property(QStringLiteral("horizontalClothCount")).toString());
1690     replacement.replace(QRegExp(QStringLiteral("\\$\\{verticalClothCount\\}")), document->property(QStringLiteral("verticalClothCount")).toString());
1691     replacement.replace(QRegExp(QStringLiteral("\\$\\{width.stitches\\}")), QString::fromLatin1("%1").arg(document->pattern()->stitches().width()));
1692     replacement.replace(QRegExp(QStringLiteral("\\$\\{height.stitches\\}")), QString::fromLatin1("%1").arg(document->pattern()->stitches().height()));
1693     replacement.replace(
1694         QRegExp(QStringLiteral("\\$\\{width.inches\\}")),
1695         QString::fromLatin1("%1").arg(
1696             round_n(document->pattern()->stitches().width()
1697                         / (document->property(QStringLiteral("horizontalClothCount")).toDouble()
1698                            * ((static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt())
1699                                == Configuration::EnumEditor_ClothCountUnits::Centimeters)
1700                                   ? 2.54
1701                                   : 1)),
1702                     2)));
1703     replacement.replace(
1704         QRegExp(QStringLiteral("\\$\\{height.inches\\}")),
1705         QString::fromLatin1("%1").arg(
1706             round_n(document->pattern()->stitches().height()
1707                         / (document->property(QStringLiteral("verticalClothCount")).toDouble()
1708                            * ((static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt())
1709                                == Configuration::EnumEditor_ClothCountUnits::Centimeters)
1710                                   ? 2.54
1711                                   : 1)),
1712                     2)));
1713     replacement.replace(
1714         QRegExp(QStringLiteral("\\$\\{width.cm\\}")),
1715         QString::fromLatin1("%1").arg(
1716             round_n(document->pattern()->stitches().width()
1717                         / (document->property(QStringLiteral("horizontalClothCount")).toDouble()
1718                            / ((static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt())
1719                                == Configuration::EnumEditor_ClothCountUnits::Inches)
1720                                   ? 2.54
1721                                   : 1)),
1722                     2)));
1723     replacement.replace(
1724         QRegExp(QStringLiteral("\\$\\{height.cm\\}")),
1725         QString::fromLatin1("%1").arg(
1726             round_n(document->pattern()->stitches().height()
1727                         / (document->property(QStringLiteral("verticalClothCount")).toDouble()
1728                            / ((static_cast<Configuration::EnumEditor_ClothCountUnits::type>(document->property(QStringLiteral("clothCountUnits")).toInt())
1729                                == Configuration::EnumEditor_ClothCountUnits::Inches)
1730                                   ? 2.54
1731                                   : 1)),
1732                     2)));
1733     replacement.replace(QRegExp(QStringLiteral("\\$\\{scheme\\}")), document->pattern()->palette().schemeName());
1734     replacement.replace(QRegExp(QStringLiteral("\\$\\{page\\}")), QString::fromLatin1("%1").arg(parent()->pageNumber()));
1735     // repeat for all possible values
1736 
1737     return replacement;
1738 }
1739 
1740 QDataStream &TextElement::streamOut(QDataStream &stream) const
1741 {
1742     Element::streamOut(stream);
1743 
1744     stream << qint32(version);
1745 
1746     stream << qint32(m_showBorder) << m_borderColor << qint32(m_borderThickness) << qint32(m_fillBackground) << m_backgroundColor
1747            << qint32(m_backgroundTransparency) << qint32(m_margins.left()) << qint32(m_margins.top()) << qint32(m_margins.right()) << qint32(m_margins.bottom())
1748            << m_textFont << m_textColor << qint32(m_alignment) << m_text;
1749 
1750     return stream;
1751 }
1752 
1753 QDataStream &TextElement::streamIn(QDataStream &stream)
1754 {
1755     Element::streamIn(stream);
1756 
1757     qint32 version;
1758     qint32 showBorder;
1759     qint32 borderThickness;
1760     qint32 fillBackground;
1761     qint32 backgroundTransparency;
1762     qint32 left;
1763     qint32 top;
1764     qint32 right;
1765     qint32 bottom;
1766     qint32 alignment;
1767 
1768     stream >> version;
1769 
1770     switch (version) {
1771     case 100:
1772         stream >> showBorder >> m_borderColor >> borderThickness >> fillBackground >> m_backgroundColor >> backgroundTransparency >> left >> top >> right
1773             >> bottom >> m_textFont >> m_textColor >> alignment >> m_text;
1774         m_showBorder = bool(showBorder);
1775         m_borderThickness = borderThickness;
1776         m_fillBackground = bool(fillBackground);
1777         m_backgroundTransparency = backgroundTransparency;
1778         m_margins = QMargins(left, top, right, bottom);
1779         m_alignment = Qt::Alignment(alignment);
1780         break;
1781 
1782     default:
1783         // not supported
1784         // throw exception
1785         break;
1786     }
1787 
1788     return stream;
1789 }
1790 
1791 QString TextElement::encodeToHtml(const QString &text) const
1792 {
1793     QTextDocument document;
1794     document.setDefaultFont(m_textFont);
1795 
1796     QTextBlockFormat format;
1797     format.setAlignment(m_alignment);
1798     format.setForeground(QBrush(m_textColor));
1799 
1800     QTextCursor cursor(&document);
1801     cursor.movePosition(QTextCursor::Start);
1802     cursor.setBlockFormat(format);
1803     cursor.insertText(text);
1804 
1805     return document.toHtml();
1806 }