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 }