File indexing completed on 2024-04-28 04:31:59
0001 /* 0002 * Copyright (C) 2010-2022 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 "Editor.h" 0012 0013 #include <QAction> 0014 #include <QApplication> 0015 #include <QBitmap> 0016 #include <QClipboard> 0017 #include <QContextMenuEvent> 0018 #include <QMenu> 0019 #include <QMimeData> 0020 #include <QMouseEvent> 0021 #include <QPainter> 0022 #include <QRubberBand> 0023 #include <QScrollArea> 0024 #include <QStyleOptionRubberBand> 0025 #include <QToolTip> 0026 0027 #include <KLocalizedString> 0028 #include <KMessageBox> 0029 #include <KXMLGUIFactory> 0030 0031 #include <math.h> 0032 0033 #include "BackgroundImage.h" 0034 #include "Commands.h" 0035 #include "Document.h" 0036 #include "Floss.h" 0037 #include "FlossScheme.h" 0038 #include "LibraryManagerDlg.h" 0039 #include "LibraryPattern.h" 0040 #include "LibraryTreeWidgetItem.h" 0041 #include "MainWindow.h" 0042 #include "Palette.h" 0043 #include "Preview.h" 0044 #include "Scale.h" 0045 #include "SchemeManager.h" 0046 #include "TextToolDlg.h" 0047 0048 const Editor::keyPressCallPointer Editor::keyPressCallPointers[] = { 0049 nullptr, // Paint 0050 nullptr, // Draw 0051 nullptr, // Erase 0052 nullptr, // Rectangle 0053 nullptr, // Fill Rectangle 0054 nullptr, // Ellipse 0055 nullptr, // Fill Ellipse 0056 &Editor::keyPressPolygon, // Fill Polygon 0057 &Editor::keyPressText, // Text 0058 &Editor::keyPressAlphabet, // Alphabet 0059 nullptr, // Select 0060 nullptr, // Backstitch 0061 nullptr, // Color Picker 0062 &Editor::keyPressPaste, // Paste 0063 &Editor::keyPressMirror, // Mirror 0064 &Editor::keyPressRotate // Rotate 0065 }; 0066 0067 const Editor::toolInitCallPointer Editor::toolInitCallPointers[] = { 0068 nullptr, // Paint 0069 nullptr, // Draw 0070 nullptr, // Erase 0071 nullptr, // Rectangle 0072 nullptr, // Fill Rectangle 0073 nullptr, // Ellipse 0074 nullptr, // Fill Ellipse 0075 &Editor::toolInitPolygon, // Fill Polygon 0076 &Editor::toolInitText, // Text 0077 &Editor::toolInitAlphabet, // Alphabet 0078 nullptr, // Select 0079 nullptr, // Backstitch 0080 nullptr, // Color Picker 0081 nullptr, // Paste 0082 nullptr, // Mirror 0083 nullptr // Rotate 0084 }; 0085 0086 const Editor::toolCleanupCallPointer Editor::toolCleanupCallPointers[] = { 0087 nullptr, // Paint 0088 nullptr, // Draw 0089 nullptr, // Erase 0090 nullptr, // Rectangle 0091 nullptr, // Fill Rectangle 0092 nullptr, // Ellipse 0093 nullptr, // Fill Ellipse 0094 &Editor::toolCleanupPolygon, // Fill Polygon 0095 nullptr, // Text 0096 &Editor::toolCleanupAlphabet, // Alphabet 0097 &Editor::toolCleanupSelect, // Select 0098 nullptr, // Backstitch 0099 nullptr, // Color Picker 0100 nullptr, // Paste 0101 &Editor::toolCleanupMirror, // Mirror 0102 &Editor::toolCleanupRotate // Rotate 0103 }; 0104 0105 const Editor::mouseEventCallPointer Editor::mousePressEventCallPointers[] = {&Editor::mousePressEvent_Paint, 0106 &Editor::mousePressEvent_Draw, 0107 &Editor::mousePressEvent_Erase, 0108 &Editor::mousePressEvent_Rectangle, 0109 &Editor::mousePressEvent_FillRectangle, 0110 &Editor::mousePressEvent_Ellipse, 0111 &Editor::mousePressEvent_FillEllipse, 0112 &Editor::mousePressEvent_FillPolygon, 0113 &Editor::mousePressEvent_Text, 0114 &Editor::mousePressEvent_Alphabet, 0115 &Editor::mousePressEvent_Select, 0116 &Editor::mousePressEvent_Backstitch, 0117 &Editor::mousePressEvent_ColorPicker, 0118 &Editor::mousePressEvent_Paste, 0119 &Editor::mousePressEvent_Mirror, 0120 &Editor::mousePressEvent_Rotate}; 0121 0122 const Editor::mouseEventCallPointer Editor::mouseMoveEventCallPointers[] = {&Editor::mouseMoveEvent_Paint, 0123 &Editor::mouseMoveEvent_Draw, 0124 &Editor::mouseMoveEvent_Erase, 0125 &Editor::mouseMoveEvent_Rectangle, 0126 &Editor::mouseMoveEvent_FillRectangle, 0127 &Editor::mouseMoveEvent_Ellipse, 0128 &Editor::mouseMoveEvent_FillEllipse, 0129 &Editor::mouseMoveEvent_FillPolygon, 0130 &Editor::mouseMoveEvent_Text, 0131 &Editor::mouseMoveEvent_Alphabet, 0132 &Editor::mouseMoveEvent_Select, 0133 &Editor::mouseMoveEvent_Backstitch, 0134 &Editor::mouseMoveEvent_ColorPicker, 0135 &Editor::mouseMoveEvent_Paste, 0136 &Editor::mouseMoveEvent_Mirror, 0137 &Editor::mouseMoveEvent_Rotate}; 0138 0139 const Editor::mouseEventCallPointer Editor::mouseReleaseEventCallPointers[] = {&Editor::mouseReleaseEvent_Paint, 0140 &Editor::mouseReleaseEvent_Draw, 0141 &Editor::mouseReleaseEvent_Erase, 0142 &Editor::mouseReleaseEvent_Rectangle, 0143 &Editor::mouseReleaseEvent_FillRectangle, 0144 &Editor::mouseReleaseEvent_Ellipse, 0145 &Editor::mouseReleaseEvent_FillEllipse, 0146 &Editor::mouseReleaseEvent_FillPolygon, 0147 &Editor::mouseReleaseEvent_Text, 0148 &Editor::mouseReleaseEvent_Alphabet, 0149 &Editor::mouseReleaseEvent_Select, 0150 &Editor::mouseReleaseEvent_Backstitch, 0151 &Editor::mouseReleaseEvent_ColorPicker, 0152 &Editor::mouseReleaseEvent_Paste, 0153 &Editor::mouseReleaseEvent_Mirror, 0154 &Editor::mouseReleaseEvent_Rotate}; 0155 0156 const Editor::renderToolSpecificGraphicsCallPointer Editor::renderToolSpecificGraphics[] = { 0157 nullptr, // Paint 0158 &Editor::renderRubberBandLine, // Draw 0159 nullptr, // Erase 0160 &Editor::renderRubberBandRectangle, // Rectangle 0161 &Editor::renderRubberBandRectangle, // Fill Rectangle 0162 &Editor::renderRubberBandEllipse, // Ellipse 0163 &Editor::renderRubberBandEllipse, // Fill Ellipse 0164 &Editor::renderFillPolygon, // Fill Polygon 0165 &Editor::renderPasteImage, // Text 0166 &Editor::renderAlphabetCursor, // Alphabet 0167 &Editor::renderRubberBandRectangle, // Select 0168 &Editor::renderRubberBandLine, // Backstitch 0169 nullptr, // Color Picker 0170 &Editor::renderPasteImage, // Paste 0171 &Editor::renderPasteImage, // Mirror - Paste performs the same functions 0172 &Editor::renderPasteImage // Rotate - Paste performs the same functions 0173 }; 0174 0175 Editor::Editor(QWidget *parent) 0176 : QWidget(parent) 0177 , m_horizontalScale(new Scale(Qt::Horizontal)) 0178 , m_verticalScale(new Scale(Qt::Vertical)) 0179 , m_libraryManagerDlg(nullptr) 0180 , m_zoomFactor(Configuration::editor_DefaultZoomFactor()) 0181 , m_toolMode(ToolPaint) 0182 , m_renderBackgroundImages(Configuration::renderer_RenderBackgroundImages()) 0183 , m_renderGrid(Configuration::renderer_RenderGrid()) 0184 , m_renderStitches(Configuration::renderer_RenderStitches()) 0185 , m_renderBackstitches(Configuration::renderer_RenderBackstitches()) 0186 , m_renderFrenchKnots(Configuration::renderer_RenderFrenchKnots()) 0187 , m_maskStitch(false) 0188 , m_maskColor(false) 0189 , m_maskBackstitch(false) 0190 , m_maskKnot(false) 0191 , m_makesCopies(Configuration::tool_MakesCopies()) 0192 , m_activeCommand(nullptr) 0193 , m_colorHighlight(Configuration::renderer_ColorHilight()) 0194 , m_pastePattern(nullptr) 0195 { 0196 setAcceptDrops(true); 0197 setFocusPolicy(Qt::StrongFocus); 0198 setMouseTracking(true); 0199 } 0200 0201 void Editor::setDocument(Document *document) 0202 { 0203 m_document = document; 0204 readDocumentSettings(); 0205 } 0206 0207 Document *Editor::document() 0208 { 0209 return m_document; 0210 } 0211 0212 void Editor::setPreview(Preview *preview) 0213 { 0214 m_preview = preview; 0215 } 0216 0217 Scale *Editor::horizontalScale() 0218 { 0219 return m_horizontalScale; 0220 } 0221 0222 Scale *Editor::verticalScale() 0223 { 0224 return m_verticalScale; 0225 } 0226 0227 void Editor::readDocumentSettings() 0228 { 0229 m_cellHorizontalGrouping = m_document->property(QStringLiteral("cellHorizontalGrouping")).toInt(); 0230 m_cellVerticalGrouping = m_document->property(QStringLiteral("cellVerticalGrouping")).toInt(); 0231 0232 m_horizontalClothCount = m_document->property(QStringLiteral("horizontalClothCount")).toDouble(); 0233 m_verticalClothCount = m_document->property(QStringLiteral("verticalClothCount")).toDouble(); 0234 0235 m_horizontalScale->setCellGrouping(m_cellHorizontalGrouping); 0236 m_horizontalScale->setCellCount(m_document->pattern()->stitches().width()); 0237 m_horizontalScale->setClothCount(m_horizontalClothCount); 0238 m_horizontalScale->setClothCountUnits( 0239 static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt())); 0240 m_horizontalScale->setUnits(m_formatScalesAs); 0241 0242 m_verticalScale->setCellGrouping(m_cellVerticalGrouping); 0243 m_verticalScale->setCellCount(m_document->pattern()->stitches().height()); 0244 m_verticalScale->setClothCount(m_verticalClothCount); 0245 m_verticalScale->setClothCountUnits( 0246 static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt())); 0247 m_verticalScale->setUnits(m_formatScalesAs); 0248 0249 m_renderer.setCellGrouping(m_cellHorizontalGrouping, m_cellVerticalGrouping); 0250 m_renderer.setGridLineWidths(Configuration::editor_ThinLineWidth(), Configuration::editor_ThickLineWidth()); 0251 m_renderer.setGridLineColors(m_document->property(QStringLiteral("thinLineColor")).value<QColor>(), 0252 m_document->property(QStringLiteral("thickLineColor")).value<QColor>()); 0253 0254 zoom(m_zoomFactor); 0255 0256 drawContents(); 0257 } 0258 0259 void Editor::drawContents() 0260 { 0261 drawContents(visibleCells()); 0262 } 0263 0264 void Editor::drawContents(const QPoint &cell) 0265 { 0266 drawContents(QRect(cell, QSize(1, 1))); 0267 } 0268 0269 void Editor::drawContents(const QRect &cells) 0270 { 0271 if (!updatesEnabled() || (m_document == nullptr) || m_cachedContents.isNull()) { 0272 return; 0273 } 0274 0275 QPainter painter(&m_cachedContents); 0276 painter.setRenderHint(QPainter::Antialiasing, true); 0277 painter.setCompositionMode(QPainter::CompositionMode_Source); 0278 painter.setWindow(0, 0, m_document->pattern()->stitches().width(), m_document->pattern()->stitches().height()); 0279 painter.fillRect(cells, m_document->property(QStringLiteral("fabricColor")).value<QColor>()); 0280 0281 if (m_renderBackgroundImages) { 0282 renderBackgroundImages(painter, cells); 0283 } 0284 0285 m_renderer.render(&painter, 0286 m_document->pattern(), 0287 cells, 0288 m_renderGrid, 0289 m_renderStitches, 0290 m_renderBackstitches, 0291 m_renderFrenchKnots, 0292 (m_colorHighlight) ? m_document->pattern()->palette().currentIndex() : -1); 0293 0294 painter.end(); 0295 0296 update(); 0297 } 0298 0299 void Editor::libraryManager() 0300 { 0301 if (m_libraryManagerDlg == nullptr) { 0302 m_libraryManagerDlg = new LibraryManagerDlg(this); 0303 m_libraryManagerDlg->setCellSize(m_cellWidth, m_cellHeight); 0304 } 0305 0306 m_libraryManagerDlg->show(); 0307 } 0308 0309 void Editor::previewClicked(const QPoint &cell) 0310 { 0311 QRect contentsRect = parentWidget()->contentsRect(); 0312 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget()) 0313 ->ensureVisible(cell.x() * m_cellWidth, cell.y() * m_cellHeight, contentsRect.width() / 2, contentsRect.height() / 2); 0314 } 0315 0316 void Editor::previewClicked(const QRect &cells) 0317 { 0318 int documentWidth = m_document->pattern()->stitches().width(); 0319 int documentHeight = m_document->pattern()->stitches().height(); 0320 int left = cells.left(); 0321 int top = cells.top(); 0322 int right = std::min(cells.right(), documentWidth - 1); 0323 int bottom = std::min(cells.bottom(), documentHeight - 1); 0324 0325 QRect visibleArea = parentWidget()->contentsRect(); 0326 double visibleWidth = visibleArea.width(); 0327 double visibleHeight = visibleArea.height(); 0328 bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt()) 0329 == Configuration::EnumEditor_ClothCountUnits::Inches); 0330 double widthScaleFactor = 0331 visibleWidth / (right - left) * ((clothCountUnitsInches) ? m_horizontalClothCount : m_horizontalClothCount * 2.54) / physicalDpiX(); 0332 double heightScaleFactor = visibleHeight / (bottom - top) * ((clothCountUnitsInches) ? m_verticalClothCount : m_verticalClothCount * 2.54) / physicalDpiY(); 0333 0334 zoom(std::min(widthScaleFactor, heightScaleFactor)); 0335 0336 previewClicked(cells.center()); 0337 } 0338 0339 bool Editor::zoom(double factor) 0340 { 0341 if (factor < Configuration::editor_MinimumZoomFactor() || factor > Configuration::editor_MaximumZoomFactor()) { 0342 return false; 0343 } 0344 0345 m_zoomFactor = factor; 0346 0347 double dpiX = logicalDpiX(); 0348 double dpiY = logicalDpiY(); 0349 0350 bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt()) 0351 == Configuration::EnumEditor_ClothCountUnits::Inches); 0352 0353 m_cellWidth = dpiX * factor / ((clothCountUnitsInches) ? m_horizontalClothCount : m_horizontalClothCount * 2.54); 0354 m_cellHeight = dpiY * factor / ((clothCountUnitsInches) ? m_verticalClothCount : m_verticalClothCount * 2.54); 0355 0356 m_horizontalScale->setCellSize(m_cellWidth); 0357 m_verticalScale->setCellSize(m_cellHeight); 0358 0359 m_horizontalScale->setOffset(pos().x()); 0360 m_verticalScale->setOffset(pos().y()); 0361 0362 if (m_libraryManagerDlg) { 0363 m_libraryManagerDlg->setCellSize(m_cellWidth, m_cellHeight); 0364 } 0365 0366 int cacheWidth = int(m_cellWidth * m_document->pattern()->stitches().width()); 0367 int cacheHeight = int(m_cellHeight * m_document->pattern()->stitches().height()); 0368 0369 this->resize(cacheWidth, cacheHeight); 0370 0371 emit changedVisibleCells(visibleCells()); 0372 0373 return true; 0374 } 0375 0376 void Editor::zoomIn() 0377 { 0378 zoom(m_zoomFactor * 1.2); 0379 } 0380 0381 void Editor::zoomOut() 0382 { 0383 zoom(m_zoomFactor / 1.2); 0384 } 0385 0386 void Editor::actualSize() 0387 { 0388 zoom(1.0); 0389 } 0390 0391 void Editor::fitToPage() 0392 { 0393 int documentWidth = m_document->pattern()->stitches().width(); 0394 int documentHeight = m_document->pattern()->stitches().height(); 0395 QRect visibleArea = parentWidget()->contentsRect(); 0396 double visibleWidth = visibleArea.width(); 0397 double visibleHeight = visibleArea.height(); 0398 bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt()) 0399 == Configuration::EnumEditor_ClothCountUnits::Inches); 0400 double widthScaleFactor = 0401 visibleWidth / documentWidth * ((clothCountUnitsInches) ? m_horizontalClothCount : m_horizontalClothCount * 2.54) / physicalDpiX(); 0402 double heightScaleFactor = visibleHeight / documentHeight * ((clothCountUnitsInches) ? m_verticalClothCount : m_verticalClothCount * 2.54) / physicalDpiY(); 0403 0404 zoom(std::min(widthScaleFactor, heightScaleFactor)); 0405 } 0406 0407 void Editor::fitToWidth() 0408 { 0409 int documentWidth = m_document->pattern()->stitches().width(); 0410 double visibleWidth = parentWidget()->contentsRect().width(); 0411 bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt()) 0412 == Configuration::EnumEditor_ClothCountUnits::Inches); 0413 double widthScaleFactor = 0414 visibleWidth / documentWidth * ((clothCountUnitsInches) ? m_horizontalClothCount : m_horizontalClothCount * 2.54) / physicalDpiX(); 0415 0416 zoom(widthScaleFactor); 0417 } 0418 0419 void Editor::fitToHeight() 0420 { 0421 int documentHeight = m_document->pattern()->stitches().height(); 0422 double visibleHeight = parentWidget()->contentsRect().height(); 0423 bool clothCountUnitsInches = (static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt()) 0424 == Configuration::EnumEditor_ClothCountUnits::Inches); 0425 double heightScaleFactor = visibleHeight / documentHeight * ((clothCountUnitsInches) ? m_verticalClothCount : m_verticalClothCount * 2.54) / physicalDpiY(); 0426 0427 zoom(heightScaleFactor); 0428 } 0429 0430 QList<Stitch::Type> Editor::maskStitches() const 0431 { 0432 QList<Stitch::Type> maskStitches; 0433 0434 if (m_maskStitch) { 0435 if (m_currentStitchType == StitchFull) { 0436 maskStitches << Stitch::Full; 0437 } else { 0438 for (int i = 0; i < 4; ++i) { 0439 maskStitches << stitchMap[m_currentStitchType][i]; 0440 } 0441 } 0442 } else { 0443 maskStitches << Stitch::TLQtr << Stitch::TRQtr << Stitch::BLQtr << Stitch::BTHalf << Stitch::TL3Qtr << Stitch::BRQtr << Stitch::TBHalf << Stitch::TR3Qtr 0444 << Stitch::BL3Qtr << Stitch::BR3Qtr << Stitch::Full << Stitch::TLSmallHalf << Stitch::TRSmallHalf << Stitch::BLSmallHalf 0445 << Stitch::BRSmallHalf << Stitch::TLSmallFull << Stitch::TRSmallFull << Stitch::BLSmallFull << Stitch::BRSmallFull; 0446 } 0447 0448 return maskStitches; 0449 } 0450 0451 void Editor::editCut() 0452 { 0453 m_document->undoStack().push(new EditCutCommand(m_document, 0454 m_selectionArea, 0455 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0456 maskStitches(), 0457 m_maskBackstitch, 0458 m_maskKnot)); 0459 0460 toolCleanupSelect(); 0461 } 0462 0463 void Editor::editCopy() 0464 { 0465 Pattern *pattern = m_document->pattern()->copy(m_selectionArea, 0466 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0467 maskStitches(), 0468 m_maskBackstitch, 0469 m_maskKnot); 0470 0471 toolCleanupSelect(); 0472 0473 QByteArray data; 0474 QDataStream stream(&data, QIODevice::WriteOnly); 0475 stream << *pattern; 0476 0477 QMimeData *mimeData = new QMimeData(); 0478 mimeData->setData(QStringLiteral("application/kxstitch"), data); 0479 0480 QApplication::clipboard()->setMimeData(mimeData); 0481 0482 update(); 0483 } 0484 0485 void Editor::editPaste() 0486 { 0487 m_pasteData = QApplication::clipboard()->mimeData()->data(QStringLiteral("application/kxstitch")); 0488 m_pastePattern = new Pattern; 0489 QDataStream stream(&m_pasteData, QIODevice::ReadOnly); 0490 stream >> *m_pastePattern; 0491 0492 pastePattern(ToolPaste); 0493 } 0494 0495 void Editor::pastePattern(ToolMode toolMode) 0496 { 0497 m_oldToolMode = m_toolMode; 0498 m_toolMode = toolMode; 0499 0500 m_cellStart = m_cellTracking = m_cellEnd = QPoint(0, 0); 0501 QPoint pos = mapFromGlobal(QCursor::pos()); 0502 0503 if (rect().contains(pos)) { 0504 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(pos); 0505 } 0506 0507 update(); 0508 } 0509 0510 void Editor::mirrorSelection() 0511 { 0512 m_orientation = static_cast<Qt::Orientation>(qobject_cast<QAction *>(sender())->data().toInt()); 0513 0514 QDataStream stream(&m_pasteData, QIODevice::WriteOnly); 0515 stream << m_document->pattern()->stitches(); 0516 0517 if (m_makesCopies) { 0518 m_pastePattern = m_document->pattern()->copy(m_selectionArea, 0519 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0520 maskStitches(), 0521 m_maskBackstitch, 0522 m_maskKnot); 0523 } else { 0524 m_pastePattern = m_document->pattern()->cut(m_selectionArea, 0525 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0526 maskStitches(), 0527 m_maskBackstitch, 0528 m_maskKnot); 0529 } 0530 0531 toolCleanupSelect(); 0532 0533 m_pastePattern->stitches().mirror(m_orientation); 0534 pastePattern(ToolMirror); 0535 } 0536 0537 void Editor::rotateSelection() 0538 { 0539 m_rotation = static_cast<StitchData::Rotation>(qobject_cast<QAction *>(sender())->data().toInt()); 0540 0541 QDataStream stream(&m_pasteData, QIODevice::WriteOnly); 0542 stream << m_document->pattern()->stitches(); 0543 0544 if (m_makesCopies) { 0545 m_pastePattern = m_document->pattern()->copy(m_selectionArea, 0546 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0547 maskStitches(), 0548 m_maskBackstitch, 0549 m_maskKnot); 0550 } else { 0551 m_pastePattern = m_document->pattern()->cut(m_selectionArea, 0552 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0553 maskStitches(), 0554 m_maskBackstitch, 0555 m_maskKnot); 0556 } 0557 0558 toolCleanupSelect(); 0559 0560 m_pastePattern->stitches().rotate(m_rotation); 0561 pastePattern(ToolRotate); 0562 } 0563 0564 void Editor::formatScalesAsStitches() 0565 { 0566 m_formatScalesAs = Configuration::EnumEditor_FormatScalesAs::Stitches; 0567 m_horizontalScale->setUnits(m_formatScalesAs); 0568 m_verticalScale->setUnits(m_formatScalesAs); 0569 } 0570 0571 void Editor::formatScalesAsCentimeters() 0572 { 0573 m_formatScalesAs = Configuration::EnumEditor_FormatScalesAs::Centimeters; 0574 m_horizontalScale->setUnits(m_formatScalesAs); 0575 m_verticalScale->setUnits(m_formatScalesAs); 0576 } 0577 0578 void Editor::formatScalesAsInches() 0579 { 0580 m_formatScalesAs = Configuration::EnumEditor_FormatScalesAs::Inches; 0581 m_horizontalScale->setUnits(m_formatScalesAs); 0582 m_verticalScale->setUnits(m_formatScalesAs); 0583 } 0584 0585 void Editor::renderStitches(bool show) 0586 { 0587 m_renderStitches = show; 0588 drawContents(); 0589 } 0590 0591 void Editor::renderBackstitches(bool show) 0592 { 0593 m_renderBackstitches = show; 0594 drawContents(); 0595 } 0596 0597 void Editor::renderFrenchKnots(bool show) 0598 { 0599 m_renderFrenchKnots = show; 0600 drawContents(); 0601 } 0602 0603 void Editor::renderGrid(bool show) 0604 { 0605 m_renderGrid = show; 0606 drawContents(); 0607 } 0608 0609 void Editor::renderBackgroundImages(bool show) 0610 { 0611 m_renderBackgroundImages = show; 0612 drawContents(); 0613 } 0614 0615 void Editor::renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::type type) 0616 { 0617 m_renderStitchesAs = type; 0618 m_renderer.setRenderStitchesAs(m_renderStitchesAs); 0619 drawContents(); 0620 } 0621 0622 void Editor::renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::type type) 0623 { 0624 m_renderBackstitchesAs = type; 0625 m_renderer.setRenderBackstitchesAs(m_renderBackstitchesAs); 0626 drawContents(); 0627 } 0628 0629 void Editor::renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::type type) 0630 { 0631 m_renderKnotsAs = type; 0632 m_renderer.setRenderKnotsAs(m_renderKnotsAs); 0633 drawContents(); 0634 } 0635 0636 void Editor::colorHighlight(bool set) 0637 { 0638 m_colorHighlight = set; 0639 drawContents(); 0640 } 0641 0642 void Editor::selectTool(ToolMode toolMode) 0643 { 0644 if (toolCleanupCallPointers[m_toolMode]) { 0645 (this->*toolCleanupCallPointers[m_toolMode])(); 0646 } 0647 0648 m_toolMode = toolMode; 0649 0650 if (toolInitCallPointers[m_toolMode]) { 0651 (this->*toolInitCallPointers[m_toolMode])(); 0652 } 0653 } 0654 0655 void Editor::selectStitch(Editor::SelectedStitchType stitchType) 0656 { 0657 m_currentStitchType = stitchType; 0658 } 0659 0660 void Editor::setMaskStitch(bool set) 0661 { 0662 m_maskStitch = set; 0663 } 0664 0665 void Editor::setMaskColor(bool set) 0666 { 0667 m_maskColor = set; 0668 } 0669 0670 void Editor::setMaskBackstitch(bool set) 0671 { 0672 m_maskBackstitch = set; 0673 } 0674 0675 void Editor::setMaskKnot(bool set) 0676 { 0677 m_maskKnot = set; 0678 } 0679 0680 void Editor::setMakesCopies(bool set) 0681 { 0682 m_makesCopies = set; 0683 } 0684 0685 void Editor::loadSettings() 0686 { 0687 readDocumentSettings(); 0688 } 0689 0690 bool Editor::event(QEvent *e) 0691 { 0692 if ((e->type() == QEvent::ToolTip) && (m_toolMode == ToolColorPicker)) { 0693 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e); 0694 int colorIndex = -1; 0695 QPoint cell = contentsToCell(helpEvent->pos()); 0696 int zone = contentsToZone(helpEvent->pos()); 0697 StitchQueue *queue = m_document->pattern()->stitches().stitchQueueAt(cell); 0698 0699 if (queue) { 0700 Stitch::Type type = stitchMap[0][zone]; 0701 QListIterator<Stitch *> q(*queue); 0702 0703 while (q.hasNext()) { 0704 Stitch *stitch = q.next(); 0705 0706 if (stitch->type & type) { 0707 colorIndex = stitch->colorIndex; 0708 break; 0709 } 0710 } 0711 } 0712 0713 if (colorIndex != -1) { 0714 const DocumentFloss *documentFloss = m_document->pattern()->palette().flosses().value(colorIndex); 0715 Floss *floss = SchemeManager::scheme(m_document->pattern()->palette().schemeName())->find(documentFloss->flossName()); 0716 QToolTip::showText(helpEvent->globalPos(), QString::fromLatin1("%1 %2").arg(floss->name(), floss->description())); 0717 } else { 0718 QToolTip::hideText(); 0719 e->ignore(); 0720 } 0721 0722 return true; 0723 } 0724 0725 return QWidget::event(e); 0726 } 0727 0728 void Editor::contextMenuEvent(QContextMenuEvent *e) 0729 { 0730 MainWindow *mainWindow = qobject_cast<MainWindow *>(topLevelWidget()); 0731 QMenu *context = static_cast<QMenu *>(mainWindow->guiFactory()->container(QStringLiteral("EditorPopup"), mainWindow)); 0732 context->popup(e->globalPos()); 0733 e->accept(); 0734 } 0735 0736 void Editor::dragEnterEvent(QDragEnterEvent *e) 0737 { 0738 if (e->mimeData()->hasFormat(QStringLiteral("application/kxstitch"))) { 0739 e->accept(); 0740 } 0741 } 0742 0743 void Editor::dragMoveEvent(QDragMoveEvent *e) 0744 { 0745 if (e->mimeData()->hasFormat(QStringLiteral("application/kxstitch"))) { 0746 e->accept(); 0747 } 0748 } 0749 0750 void Editor::dragLeaveEvent(QDragLeaveEvent *) 0751 { 0752 // don't need to do anything here 0753 } 0754 0755 void Editor::dropEvent(QDropEvent *e) 0756 { 0757 m_pasteData = e->mimeData()->data(QStringLiteral("application/kxstitch")); 0758 m_pastePattern = new Pattern; 0759 QDataStream stream(&m_pasteData, QIODevice::ReadOnly); 0760 stream >> *m_pastePattern; 0761 m_document->undoStack().push( 0762 new EditPasteCommand(m_document, m_pastePattern, contentsToCell(e->pos()), e->keyboardModifiers() & Qt::ShiftModifier, i18n("Drag"))); 0763 m_pastePattern = nullptr; 0764 m_pasteData.clear(); 0765 e->accept(); 0766 } 0767 0768 void Editor::keyPressEvent(QKeyEvent *e) 0769 { 0770 if (keyPressCallPointers[m_toolMode]) { 0771 (this->*keyPressCallPointers[m_toolMode])(e); 0772 } else { 0773 e->ignore(); 0774 } 0775 } 0776 0777 void Editor::keyReleaseEvent(QKeyEvent *) 0778 { 0779 } 0780 0781 void Editor::keyPressPolygon(QKeyEvent *e) 0782 { 0783 switch (e->key()) { 0784 case Qt::Key_Backspace: 0785 if (!m_polygon.isEmpty()) { 0786 m_polygon.pop_back(); 0787 update(); 0788 } 0789 0790 e->accept(); 0791 break; 0792 0793 case Qt::Key_Escape: 0794 if (!m_polygon.isEmpty()) { 0795 m_polygon.clear(); 0796 update(); 0797 } 0798 0799 e->accept(); 0800 break; 0801 0802 default: 0803 e->ignore(); 0804 break; 0805 } 0806 } 0807 0808 void Editor::keyPressText(QKeyEvent *e) 0809 { 0810 switch (e->key()) { 0811 case Qt::Key_Return: 0812 case Qt::Key_Enter: 0813 m_document->undoStack().push(new EditPasteCommand(m_document, m_pastePattern, m_cellEnd, (e->modifiers() & Qt::ShiftModifier), i18n("Text"))); 0814 m_pastePattern = nullptr; 0815 m_pasteData.clear(); 0816 e->accept(); 0817 selectTool(m_oldToolMode); 0818 break; 0819 0820 default: 0821 keyPressMovePattern(e); 0822 break; 0823 } 0824 } 0825 0826 void Editor::keyPressAlphabet(QKeyEvent *e) 0827 { 0828 Qt::KeyboardModifiers modifiers = e->modifiers(); 0829 0830 if (m_keyInfo.isKeyPressed(Qt::Key_CapsLock) && Configuration::alphabet_UseCapsLock()) { 0831 modifiers = static_cast<Qt::KeyboardModifiers>(modifiers ^ Qt::ShiftModifier); 0832 } 0833 0834 int width = m_document->pattern()->stitches().width(); 0835 int height = m_document->pattern()->stitches().height(); 0836 LibraryPattern *libraryPattern = nullptr; 0837 0838 if (m_libraryManagerDlg->currentLibrary()) { 0839 switch (e->key()) { 0840 case Qt::Key_Backspace: 0841 if (m_cursorStack.count() > 1) { 0842 m_cellEnd = m_cursorStack.pop(); 0843 m_cursorCommands.remove(m_cursorStack.count()); 0844 update(); 0845 int commandsToUndo = m_cursorCommands[m_cursorStack.count() - 1]; 0846 0847 while (commandsToUndo--) { 0848 QUndoCommand *cmd = static_cast<AlphabetCommand *>(m_activeCommand)->pop(); 0849 m_cursorCommands[m_cursorStack.count() - 1]--; 0850 delete cmd; 0851 } 0852 } 0853 0854 e->accept(); 0855 break; 0856 0857 case Qt::Key_Return: 0858 case Qt::Key_Enter: 0859 m_cellTracking = QPoint(m_cursorStack.at(0).x(), 0860 m_cursorStack.top().y() + m_libraryManagerDlg->currentLibrary()->maxHeight() + Configuration::alphabet_LineSpacing()); 0861 0862 if (m_cellTracking.y() >= height) { 0863 static_cast<AlphabetCommand *>(m_activeCommand) 0864 ->push(new ExtendPatternCommand(m_document, 0, 0, 0, m_cellTracking.y() - height + Configuration::alphabet_ExtendPatternHeight())); 0865 m_cursorCommands[m_cursorStack.count() - 1]++; 0866 } 0867 0868 m_cellEnd = m_cursorStack.top(); 0869 m_cursorStack.push(m_cellTracking); 0870 update(); 0871 e->accept(); 0872 break; 0873 0874 default: 0875 libraryPattern = m_libraryManagerDlg->currentLibrary()->findCharacter(e->key(), modifiers); 0876 0877 if (libraryPattern) { 0878 if ((m_cursorStack.top() + QPoint(libraryPattern->pattern()->stitches().width(), 0)).x() >= width) { 0879 if (m_cursorCommands[m_cursorStack.count() - 2]) { 0880 static_cast<AlphabetCommand *>(m_activeCommand) 0881 ->push(new ExtendPatternCommand(m_document, 0882 0, 0883 0, 0884 m_cursorStack.top().x() + libraryPattern->pattern()->stitches().width() - width 0885 + Configuration::alphabet_ExtendPatternWidth(), 0886 0)); 0887 m_cursorCommands[m_cursorStack.count() - 1]++; 0888 } else { 0889 m_cellTracking = 0890 QPoint(m_cursorStack.at(0).x(), 0891 m_cursorStack.top().y() + m_libraryManagerDlg->currentLibrary()->maxHeight() + Configuration::alphabet_LineSpacing()); 0892 0893 if (m_cellTracking.y() >= height) { 0894 static_cast<AlphabetCommand *>(m_activeCommand) 0895 ->push( 0896 new ExtendPatternCommand(m_document, 0, 0, 0, m_cellTracking.y() - height + Configuration::alphabet_ExtendPatternHeight())); 0897 m_cursorCommands[m_cursorStack.count() - 1]++; 0898 } 0899 0900 m_cellEnd = m_cursorStack.top(); 0901 m_cursorStack.push(m_cellTracking); 0902 update(); 0903 } 0904 } 0905 0906 QPoint insertionPoint = m_cursorStack.top() - QPoint(0, libraryPattern->pattern()->stitches().height() - 1 - libraryPattern->baseline()); 0907 static_cast<AlphabetCommand *>(m_activeCommand) 0908 ->push(new EditPasteCommand(m_document, libraryPattern->pattern(), insertionPoint, true, i18n("Add Character"))); 0909 m_cursorCommands[m_cursorStack.count() - 1]++; 0910 m_cursorStack.push(m_cursorStack.top() + QPoint(libraryPattern->pattern()->stitches().width() + 1, 0)); 0911 } else { 0912 if (e->key() == Qt::Key_Space) { 0913 m_cellTracking = m_cursorStack.top() + QPoint(Configuration::alphabet_SpaceWidth(), 0); 0914 0915 if (m_cellTracking.x() >= width) { 0916 if (Configuration::alphabet_WordWrap()) { 0917 m_cellTracking = 0918 QPoint(m_cursorStack.at(0).x(), 0919 m_cellTracking.y() + m_libraryManagerDlg->currentLibrary()->maxHeight() + Configuration::alphabet_LineSpacing()); 0920 0921 if (m_cellTracking.y() >= height) { 0922 static_cast<AlphabetCommand *>(m_activeCommand) 0923 ->push(new ExtendPatternCommand(m_document, 0924 0, 0925 0, 0926 0, 0927 m_cellTracking.y() - height + Configuration::alphabet_ExtendPatternHeight())); 0928 m_cursorCommands[m_cursorStack.count() - 1]++; 0929 } 0930 } else { 0931 static_cast<AlphabetCommand *>(m_activeCommand) 0932 ->push( 0933 new ExtendPatternCommand(m_document, 0, 0, m_cellTracking.x() - width + Configuration::alphabet_ExtendPatternWidth(), 0)); 0934 m_cursorCommands[m_cursorStack.count() - 1]++; 0935 } 0936 } 0937 0938 m_cursorStack.push(m_cellTracking); 0939 update(); 0940 } 0941 } 0942 0943 e->accept(); 0944 break; 0945 } 0946 } 0947 0948 QPoint contentPoint = rectToContents(cellToRect(m_cursorStack.top())).center(); 0949 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(contentPoint.x(), contentPoint.y()); 0950 } 0951 0952 void Editor::keyPressPaste(QKeyEvent *e) 0953 { 0954 switch (e->key()) { 0955 case Qt::Key_Return: 0956 case Qt::Key_Enter: 0957 m_document->undoStack().push(new EditPasteCommand(m_document, m_pastePattern, m_cellEnd, (e->modifiers() & Qt::ShiftModifier), i18n("Paste"))); 0958 m_pastePattern = nullptr; 0959 m_pasteData.clear(); 0960 e->accept(); 0961 selectTool(m_oldToolMode); 0962 break; 0963 0964 default: 0965 keyPressMovePattern(e); 0966 break; 0967 } 0968 } 0969 0970 void Editor::keyPressMirror(QKeyEvent *e) 0971 { 0972 switch (e->key()) { 0973 case Qt::Key_Return: 0974 case Qt::Key_Enter: 0975 m_document->undoStack().push(new MirrorSelectionCommand(m_document, 0976 m_selectionArea, 0977 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 0978 maskStitches(), 0979 m_maskBackstitch, 0980 m_maskKnot, 0981 m_orientation, 0982 m_makesCopies, 0983 m_pasteData, 0984 m_pastePattern, 0985 m_cellEnd, 0986 (e->modifiers() & Qt::ShiftModifier))); 0987 m_pastePattern = nullptr; 0988 m_pasteData.clear(); 0989 e->accept(); 0990 selectTool(m_oldToolMode); 0991 break; 0992 0993 default: 0994 keyPressMovePattern(e); 0995 break; 0996 } 0997 } 0998 0999 void Editor::keyPressRotate(QKeyEvent *e) 1000 { 1001 switch (e->key()) { 1002 case Qt::Key_Return: 1003 case Qt::Key_Enter: 1004 m_document->undoStack().push(new RotateSelectionCommand(m_document, 1005 m_selectionArea, 1006 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 1007 maskStitches(), 1008 m_maskBackstitch, 1009 m_maskKnot, 1010 m_rotation, 1011 m_makesCopies, 1012 m_pasteData, 1013 m_pastePattern, 1014 m_cellEnd, 1015 (e->modifiers() & Qt::ShiftModifier))); 1016 m_pastePattern = nullptr; 1017 m_pasteData.clear(); 1018 e->accept(); 1019 selectTool(m_oldToolMode); 1020 break; 1021 1022 default: 1023 keyPressMovePattern(e); 1024 break; 1025 } 1026 } 1027 1028 void Editor::keyPressMovePattern(QKeyEvent *e) 1029 { 1030 switch (e->key()) { 1031 case Qt::Key_Escape: 1032 e->accept(); 1033 selectTool(m_oldToolMode); 1034 update(); 1035 break; 1036 1037 case Qt::Key_Up: 1038 m_cellTracking = QPoint(m_cellEnd.x(), std::max(m_cellEnd.y() - 1, 0)); 1039 1040 if (m_cellTracking != m_cellEnd) { 1041 m_cellEnd = m_cellTracking; 1042 update(); 1043 } 1044 1045 e->accept(); 1046 break; 1047 1048 case Qt::Key_Down: 1049 m_cellTracking = QPoint(m_cellEnd.x(), std::min(m_cellEnd.y() + 1, m_document->pattern()->stitches().height() - 1)); 1050 1051 if (m_cellTracking != m_cellEnd) { 1052 m_cellEnd = m_cellTracking; 1053 update(); 1054 } 1055 1056 e->accept(); 1057 break; 1058 1059 case Qt::Key_Left: 1060 m_cellTracking = QPoint(std::max(m_cellEnd.x() - 1, 0), m_cellEnd.y()); 1061 1062 if (m_cellTracking != m_cellEnd) { 1063 m_cellEnd = m_cellTracking; 1064 update(); 1065 } 1066 1067 e->accept(); 1068 break; 1069 1070 case Qt::Key_Right: 1071 m_cellTracking = QPoint(std::min(m_cellEnd.x() + 1, m_document->pattern()->stitches().width() - 1), m_cellEnd.y()); 1072 1073 if (m_cellTracking != m_cellEnd) { 1074 m_cellEnd = m_cellTracking; 1075 update(); 1076 } 1077 1078 e->accept(); 1079 break; 1080 1081 default: 1082 e->ignore(); 1083 break; 1084 } 1085 } 1086 1087 void Editor::toolInitPolygon() 1088 { 1089 m_polygon.clear(); 1090 } 1091 1092 void Editor::toolInitText() 1093 { 1094 QPointer<TextToolDlg> textToolDlg = new TextToolDlg(this); 1095 1096 if (textToolDlg->exec()) { 1097 m_pastePattern = new Pattern; 1098 QImage image = textToolDlg->image(); 1099 1100 m_pastePattern->palette().setSchemeName(m_document->pattern()->palette().schemeName()); 1101 int currentIndex = m_document->pattern()->palette().currentIndex(); 1102 m_pastePattern->palette().add(currentIndex, new DocumentFloss(m_document->pattern()->palette().currentFloss())); 1103 m_pastePattern->stitches().resize(image.width(), image.height()); 1104 1105 int stitchesAdded = 0; 1106 1107 for (int row = 0; row < image.height(); ++row) { 1108 for (int col = 0; col < image.width(); ++col) { 1109 if (image.pixelIndex(col, row) == 1) { 1110 QPoint cell(col, row); 1111 m_pastePattern->stitches().addStitch(cell, Stitch::Full, currentIndex); 1112 ++stitchesAdded; 1113 } 1114 } 1115 } 1116 1117 if (stitchesAdded) { 1118 pastePattern(ToolText); 1119 } else { 1120 delete m_pastePattern; 1121 m_pastePattern = nullptr; 1122 } 1123 } else { // dialog was cancelled, reset tool to paint 1124 topLevelWidget()->findChild<QAction *>(QStringLiteral("toolPaint"))->trigger(); 1125 } 1126 } 1127 1128 void Editor::toolInitAlphabet() 1129 { 1130 libraryManager(); 1131 } 1132 1133 void Editor::toolCleanupPolygon() 1134 { 1135 m_polygon.clear(); 1136 drawContents(); 1137 } 1138 1139 void Editor::toolCleanupAlphabet() 1140 { 1141 m_activeCommand = nullptr; 1142 m_cursorStack.clear(); 1143 m_cursorCommands.clear(); 1144 } 1145 1146 void Editor::toolCleanupSelect() 1147 { 1148 if (m_rubberBand.isValid()) { 1149 QRect updateArea = m_rubberBand; 1150 m_rubberBand = QRect(); 1151 drawContents(updateArea.adjusted(-1, -1, 1, 1)); 1152 } 1153 1154 emit(selectionMade(false)); 1155 } 1156 1157 void Editor::toolCleanupMirror() 1158 { 1159 delete m_pastePattern; 1160 m_pastePattern = nullptr; 1161 1162 if (!m_pasteData.isEmpty()) { 1163 m_document->pattern()->stitches().clear(); 1164 QDataStream stream(&m_pasteData, QIODevice::ReadOnly); 1165 stream >> m_document->pattern()->stitches(); 1166 m_pasteData.clear(); 1167 } 1168 1169 drawContents(); 1170 } 1171 1172 void Editor::toolCleanupRotate() 1173 { 1174 delete m_pastePattern; 1175 m_pastePattern = nullptr; 1176 1177 if (!m_pasteData.isEmpty()) { 1178 m_document->pattern()->stitches().clear(); 1179 QDataStream stream(&m_pasteData, QIODevice::ReadOnly); 1180 stream >> m_document->pattern()->stitches(); 1181 m_pasteData.clear(); 1182 } 1183 1184 drawContents(); 1185 } 1186 1187 void Editor::mousePressEvent(QMouseEvent *e) 1188 { 1189 if (!rect().contains(e->pos())) { 1190 return; 1191 } 1192 1193 if ((e->buttons() & Qt::LeftButton) 1194 && ((m_document->pattern()->palette().currentIndex() != -1) || (m_toolMode == Editor::ToolSelect) || (m_toolMode == Editor::ToolAlphabet) 1195 || (m_toolMode == Editor::ToolPaste))) { 1196 (this->*mousePressEventCallPointers[m_toolMode])(e); 1197 } 1198 } 1199 1200 void Editor::mouseMoveEvent(QMouseEvent *e) 1201 { 1202 if (!rect().contains(e->pos())) { 1203 return; 1204 } 1205 1206 if ((e->buttons() & Qt::LeftButton) 1207 && ((m_document->pattern()->palette().currentIndex() != -1) || (m_toolMode == Editor::ToolSelect) || (m_toolMode == Editor::ToolAlphabet) 1208 || (m_toolMode == Editor::ToolPaste))) { 1209 (this->*mouseMoveEventCallPointers[m_toolMode])(e); 1210 } else if (m_toolMode == Editor::ToolPaste || m_toolMode == Editor::ToolMirror || m_toolMode == Editor::ToolRotate || m_toolMode == Editor::ToolText) { 1211 if (m_pastePattern && rectToContents(m_pastePattern->stitches().extents().translated(m_cellStart)).contains(e->pos())) { 1212 setCursor(Qt::SizeAllCursor); 1213 } else { 1214 setCursor(Qt::ArrowCursor); 1215 } 1216 } 1217 } 1218 1219 void Editor::mouseReleaseEvent(QMouseEvent *e) 1220 { 1221 if (!rect().contains(e->pos())) { 1222 return; 1223 } 1224 1225 if ((m_document->pattern()->palette().currentIndex() != -1) || (m_toolMode == Editor::ToolSelect) || (m_toolMode == Editor::ToolAlphabet) 1226 || (m_toolMode == Editor::ToolPaste)) { 1227 (this->*mouseReleaseEventCallPointers[m_toolMode])(e); 1228 } 1229 } 1230 1231 void Editor::moveEvent(QMoveEvent *) 1232 { 1233 drawContents(); 1234 } 1235 1236 void Editor::resizeEvent(QResizeEvent *e) 1237 { 1238 int width = e->size().width(); 1239 int height = e->size().height(); 1240 1241 if (m_cachedContents.isNull()) { 1242 m_cachedContents = QPixmap(width, height); 1243 } else { 1244 m_cachedContents = m_cachedContents.scaled(width, height); 1245 repaint(); 1246 } 1247 1248 drawContents(); 1249 } 1250 1251 void Editor::paintEvent(QPaintEvent *e) 1252 { 1253 if (m_cachedContents.isNull()) { 1254 return; 1255 } 1256 1257 static QPoint oldpos = pos(); 1258 QRect dirtyRect = e->rect(); 1259 1260 QPainter painter(this); 1261 1262 painter.fillRect(dirtyRect, Qt::white); 1263 painter.drawPixmap(dirtyRect, m_cachedContents, dirtyRect); 1264 painter.setWindow(0, 0, m_document->pattern()->stitches().width(), m_document->pattern()->stitches().height()); 1265 1266 if (renderToolSpecificGraphics[m_toolMode]) { 1267 (this->*renderToolSpecificGraphics[m_toolMode])(&painter, e->rect()); 1268 } 1269 1270 if (pos() != oldpos) { 1271 m_horizontalScale->setOffset(pos().x()); 1272 m_verticalScale->setOffset(pos().y()); 1273 oldpos = pos(); 1274 } 1275 1276 emit changedVisibleCells(visibleCells()); 1277 } 1278 1279 void Editor::wheelEvent(QWheelEvent *e) 1280 { 1281 bool zoomed; 1282 QPoint p = e->pos(); 1283 QPoint offset = parentWidget()->rect().center() - pos(); 1284 1285 setUpdatesEnabled(false); 1286 1287 if (e->delta() > 0) { 1288 zoomed = zoom(m_zoomFactor * 1.2); 1289 offset -= (p - (p * 1.2)); 1290 } else { 1291 zoomed = zoom(m_zoomFactor / 1.2); 1292 offset -= (p - (p / 1.2)); 1293 } 1294 1295 if (zoomed) { 1296 int marginX = parentWidget()->width() / 2; 1297 int marginY = parentWidget()->height() / 2; 1298 1299 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget()) 1300 ->ensureVisible(static_cast<int>(offset.x()), static_cast<int>(offset.y()), marginX, marginY); 1301 } 1302 1303 setUpdatesEnabled(true); 1304 drawContents(); 1305 1306 e->accept(); 1307 } 1308 1309 bool Editor::eventFilter(QObject *object, QEvent *e) 1310 { 1311 if (object == parentWidget()->parentWidget()) { 1312 if (e->type() == QEvent::Wheel) { 1313 wheelEvent(static_cast<QWheelEvent *>(e)); 1314 return true; 1315 } 1316 1317 if (e->type() == QEvent::Resize) { 1318 drawContents(); 1319 // Don't want to intercept this, just act on it to update the editor content 1320 } 1321 } 1322 1323 return false; 1324 } 1325 1326 void Editor::renderBackgroundImages(QPainter &painter, const QRect &updateRectangle) 1327 { 1328 auto backgroundImages = m_document->backgroundImages().backgroundImages(); 1329 1330 while (backgroundImages.hasNext()) { 1331 auto backgroundImage = backgroundImages.next(); 1332 1333 if (backgroundImage->isVisible()) { 1334 if (backgroundImage->location().intersects(updateRectangle)) { 1335 painter.setClipRect(updateRectangle.x(), updateRectangle.y(), updateRectangle.width(), updateRectangle.height()); 1336 painter.drawImage(backgroundImage->location(), backgroundImage->image()); 1337 painter.setClipping(false); 1338 } 1339 } 1340 } 1341 } 1342 1343 void Editor::renderRubberBandLine(QPainter *painter, const QRect &) 1344 { 1345 painter->save(); 1346 1347 if (m_rubberBand.isValid()) { 1348 QPen pen; 1349 pen.setWidthF(0.5); 1350 1351 painter->setOpacity(0.5); 1352 1353 if (m_toolMode == ToolBackstitch) { 1354 pen.setColor(m_document->pattern()->palette().currentFloss()->flossColor()); 1355 painter->setPen(pen); 1356 painter->drawLine(QPointF(m_cellStart) / 2, QPointF(m_cellEnd) / 2); 1357 } else { 1358 pen.setColor(QColor(200, 225, 255)); 1359 painter->setPen(pen); 1360 painter->drawLine(QPointF(m_cellStart) + QPointF(0.5, 0.5), QPointF(m_cellEnd) + QPointF(0.5, 0.5)); 1361 } 1362 } 1363 1364 painter->restore(); 1365 } 1366 1367 void Editor::renderRubberBandRectangle(QPainter *painter, const QRect &) 1368 { 1369 painter->save(); 1370 1371 if (m_rubberBand.isValid()) { 1372 painter->setRenderHint(QPainter::Qt4CompatiblePainting, true); 1373 1374 painter->resetTransform(); 1375 QStyleOptionRubberBand opt; 1376 opt.initFrom(this); 1377 opt.shape = QRubberBand::Rectangle; 1378 opt.opaque = false; 1379 opt.rect = rectToContents(m_rubberBand); 1380 1381 style()->drawControl(QStyle::CE_RubberBand, &opt, painter); 1382 } 1383 1384 painter->restore(); 1385 } 1386 1387 void Editor::renderRubberBandEllipse(QPainter *painter, const QRect &) 1388 { 1389 painter->save(); 1390 1391 if (m_rubberBand.isValid()) { 1392 painter->setRenderHint(QPainter::Qt4CompatiblePainting, true); 1393 1394 painter->resetTransform(); 1395 QStyleOptionRubberBand opt; 1396 opt.initFrom(this); 1397 1398 painter->setPen(opt.palette.color(QPalette::WindowText)); 1399 painter->setBrush(QBrush(opt.palette.color(QPalette::Highlight), Qt::Dense4Pattern)); 1400 painter->setBackground(QBrush(opt.palette.base())); 1401 painter->setBackgroundMode(Qt::TransparentMode); 1402 painter->drawEllipse(rectToContents(m_rubberBand)); 1403 } 1404 1405 painter->restore(); 1406 } 1407 1408 void Editor::renderFillPolygon(QPainter *painter, const QRect &) 1409 { 1410 QPolygonF polyline; 1411 painter->save(); 1412 1413 QPen pen(Qt::green); 1414 pen.setWidth(0); 1415 1416 painter->setPen(pen); // use green for the first point 1417 painter->setBrush(Qt::green); 1418 1419 QVector<QPoint>::const_iterator i; 1420 1421 for (i = m_polygon.constBegin(); i != m_polygon.constEnd(); ++i) { 1422 QPointF cell = QPointF(*i) + QPointF(0.5, 0.5); 1423 painter->drawEllipse(QRectF(-0.5, -0.5, 1, 1).translated(cell)); 1424 pen.setColor(Qt::blue); 1425 painter->setPen(pen); // use blue for subsequent points 1426 painter->setBrush(Qt::blue); 1427 polyline.append(cell); 1428 } 1429 1430 painter->drawPolyline(polyline); 1431 painter->restore(); 1432 } 1433 1434 void Editor::renderAlphabetCursor(QPainter *painter, const QRect &) 1435 { 1436 if (m_cursorStack.isEmpty()) { 1437 return; 1438 } 1439 1440 painter->save(); 1441 painter->setPen(Qt::red); 1442 painter->fillRect(QRect(m_cursorStack.top(), QSize(1, 1)), Qt::red); 1443 painter->restore(); 1444 } 1445 1446 void Editor::renderPasteImage(QPainter *painter, const QRect &) 1447 { 1448 painter->save(); 1449 1450 if (m_pastePattern) { 1451 QPen outlinePen(Qt::red); 1452 outlinePen.setCosmetic(true); 1453 painter->translate(m_cellEnd); 1454 painter->setPen(outlinePen); 1455 QRect outline(0, 0, m_pastePattern->stitches().width(), m_pastePattern->stitches().height()); 1456 painter->drawRect(outline); 1457 1458 m_renderer.render(painter, 1459 m_pastePattern, // the pattern data to render 1460 outline, // update rectangle in cells 1461 false, // don't render the grid 1462 true, // render stitches 1463 true, // render backstitches 1464 true, // render knots 1465 -1); // all colors 1466 } 1467 1468 painter->restore(); 1469 } 1470 1471 void Editor::mousePressEvent_Paint(QMouseEvent *e) 1472 { 1473 QPoint p = e->pos(); 1474 1475 if (m_currentStitchType == StitchFrenchKnot) { 1476 m_cellStart = m_cellTracking = m_cellEnd = contentsToSnap(p); 1477 m_activeCommand = new PaintKnotsCommand(m_document); 1478 new AddKnotCommand(m_document, m_cellStart, m_document->pattern()->palette().currentIndex(), m_activeCommand); 1479 m_document->undoStack().push(m_activeCommand); 1480 drawContents(snapToCells(m_cellStart)); 1481 } else { 1482 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(p); 1483 m_zoneStart = m_zoneTracking = m_zoneEnd = contentsToZone(p); 1484 Stitch::Type stitchType = stitchMap[m_currentStitchType][m_zoneStart]; 1485 m_activeCommand = new PaintStitchesCommand(m_document); 1486 new AddStitchCommand(m_document, m_cellStart, stitchType, m_document->pattern()->palette().currentIndex(), m_activeCommand); 1487 m_document->undoStack().push(m_activeCommand); 1488 drawContents(m_cellStart); 1489 } 1490 } 1491 1492 void Editor::mouseMoveEvent_Paint(QMouseEvent *e) 1493 { 1494 QPoint p = e->pos(); 1495 1496 if (m_currentStitchType == StitchFrenchKnot) { 1497 m_cellTracking = contentsToSnap(p); 1498 1499 if (m_cellTracking != m_cellStart) { 1500 m_cellStart = m_cellTracking; 1501 QUndoCommand *cmd = new AddKnotCommand(m_document, m_cellStart, m_document->pattern()->palette().currentIndex(), m_activeCommand); 1502 cmd->redo(); 1503 drawContents(snapToCells(m_cellStart)); 1504 } 1505 } else { 1506 m_cellTracking = contentsToCell(p); 1507 m_zoneTracking = contentsToZone(p); 1508 1509 if ((m_cellTracking != m_cellStart) || (m_zoneTracking != m_zoneStart)) { 1510 m_cellStart = m_cellTracking; 1511 m_zoneStart = m_zoneTracking; 1512 Stitch::Type stitchType = stitchMap[m_currentStitchType][m_zoneStart]; 1513 QUndoCommand *cmd = new AddStitchCommand(m_document, m_cellStart, stitchType, m_document->pattern()->palette().currentIndex(), m_activeCommand); 1514 cmd->redo(); 1515 drawContents(m_cellStart); 1516 } 1517 } 1518 } 1519 1520 void Editor::mouseReleaseEvent_Paint(QMouseEvent *) 1521 { 1522 m_activeCommand = nullptr; 1523 m_preview->drawContents(); 1524 } 1525 1526 void Editor::mousePressEvent_Draw(QMouseEvent *e) 1527 { 1528 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1529 m_rubberBand = QRect(); 1530 } 1531 1532 void Editor::mouseMoveEvent_Draw(QMouseEvent *e) 1533 { 1534 QPoint p = e->pos(); 1535 1536 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1537 1538 m_cellEnd = m_cellTracking = contentsToCell(p); 1539 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1540 1541 update(); 1542 } 1543 1544 void Editor::mouseReleaseEvent_Draw(QMouseEvent *) 1545 { 1546 int bitmapWidth = m_document->pattern()->stitches().width(); 1547 int bitmapHeight = m_document->pattern()->stitches().height(); 1548 bool useFractionals = Configuration::toolShapes_UseFractionals(); 1549 1550 if (useFractionals) { 1551 bitmapWidth *= 2; 1552 bitmapHeight *= 2; 1553 m_cellStart *= 2; 1554 m_cellEnd *= 2; 1555 } 1556 1557 if (m_cellStart != m_cellEnd) { 1558 QBitmap canvas(bitmapWidth, bitmapHeight); 1559 QPainter painter; 1560 1561 canvas.fill(Qt::color0); 1562 painter.begin(&canvas); 1563 painter.setRenderHint(QPainter::Antialiasing, false); 1564 painter.setRenderHint(QPainter::Qt4CompatiblePainting, true); 1565 painter.setPen(QPen(Qt::color1)); 1566 painter.drawLine(m_cellStart, m_cellEnd); 1567 painter.end(); 1568 1569 QUndoCommand *cmd = new DrawLineCommand(m_document); 1570 processBitmap(cmd, canvas); 1571 1572 m_document->undoStack().push(cmd); 1573 } 1574 1575 m_rubberBand = QRect(); 1576 } 1577 1578 void Editor::mousePressEvent_Erase(QMouseEvent *e) 1579 { 1580 QPoint p = e->pos(); 1581 QUndoCommand *cmd; 1582 1583 if (e->modifiers() & Qt::ControlModifier) { 1584 // Erase a backstitch 1585 m_cellStart = m_cellTracking = m_cellEnd = contentsToSnap(p); 1586 } else { 1587 m_activeCommand = new EraseStitchesCommand(m_document); 1588 m_document->undoStack().push(m_activeCommand); 1589 1590 if (e->modifiers() & Qt::ShiftModifier) { 1591 // Delete french knots 1592 m_cellStart = m_cellTracking = m_cellEnd = contentsToSnap(p); 1593 1594 if (Knot *knot = m_document->pattern()->stitches().findKnot(m_cellStart, (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1)) { 1595 cmd = new DeleteKnotCommand(m_document, knot->position, knot->colorIndex, m_activeCommand); 1596 cmd->redo(); 1597 drawContents(snapToCells(m_cellStart)); 1598 } 1599 } else { 1600 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(p); 1601 m_zoneStart = m_zoneTracking = m_zoneEnd = contentsToZone(p); 1602 1603 if (Stitch *stitch = m_document->pattern()->stitches().findStitch(m_cellStart, 1604 m_maskStitch ? stitchMap[m_currentStitchType][m_zoneStart] : Stitch::Delete, 1605 m_maskColor ? m_document->pattern()->palette().currentIndex() : -1)) { 1606 cmd = new DeleteStitchCommand(m_document, 1607 m_cellStart, 1608 m_maskStitch ? stitchMap[m_currentStitchType][m_zoneStart] : Stitch::Delete, 1609 stitch->colorIndex, 1610 m_activeCommand); 1611 cmd->redo(); 1612 drawContents(cellToRect(m_cellStart).adjusted(-1, -1, 1, 1)); 1613 } 1614 } 1615 } 1616 } 1617 1618 void Editor::mouseMoveEvent_Erase(QMouseEvent *e) 1619 { 1620 QPoint p = e->pos(); 1621 QUndoCommand *cmd; 1622 1623 if (e->modifiers() & Qt::ControlModifier) { 1624 // Erasing a backstitch 1625 // Don't need to do anything here 1626 } else { 1627 if (e->modifiers() & Qt::ShiftModifier) { 1628 // Delete french knots 1629 m_cellTracking = contentsToSnap(p); 1630 1631 if (m_cellTracking != m_cellStart) { 1632 m_cellStart = m_cellTracking; 1633 1634 if (Knot *knot = 1635 m_document->pattern()->stitches().findKnot(m_cellStart, (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1)) { 1636 cmd = new DeleteKnotCommand(m_document, knot->position, knot->colorIndex, m_activeCommand); 1637 cmd->redo(); 1638 drawContents(snapToCells(m_cellStart)); 1639 } 1640 } 1641 } else { 1642 m_cellTracking = contentsToCell(p); 1643 m_zoneTracking = contentsToZone(p); 1644 1645 if ((m_cellTracking != m_cellStart) || (m_zoneTracking != m_zoneStart)) { 1646 m_cellStart = m_cellTracking; 1647 m_zoneStart = m_zoneTracking; 1648 1649 if (Stitch *stitch = m_document->pattern()->stitches().findStitch(m_cellStart, 1650 m_maskStitch ? stitchMap[m_currentStitchType][m_zoneStart] : Stitch::Delete, 1651 m_maskColor ? m_document->pattern()->palette().currentIndex() : -1)) { 1652 cmd = new DeleteStitchCommand(m_document, 1653 m_cellStart, 1654 m_maskStitch ? stitchMap[m_currentStitchType][m_zoneStart] : Stitch::Delete, 1655 stitch->colorIndex, 1656 m_activeCommand); 1657 cmd->redo(); 1658 drawContents(cellToRect(m_cellStart).adjusted(-1, -1, 1, 1)); 1659 } 1660 } 1661 } 1662 } 1663 } 1664 1665 void Editor::mouseReleaseEvent_Erase(QMouseEvent *e) 1666 { 1667 if (e->modifiers() & Qt::ControlModifier) { 1668 // Erase a backstitch 1669 m_cellEnd = contentsToSnap(e->pos()); 1670 1671 if (Backstitch *backstitch = 1672 m_document->pattern()->stitches().findBackstitch(m_cellStart, m_cellEnd, m_maskColor ? m_document->pattern()->palette().currentIndex() : -1)) { 1673 m_document->undoStack().push(new DeleteBackstitchCommand(m_document, backstitch->start, backstitch->end, backstitch->colorIndex)); 1674 } 1675 } 1676 1677 // Nothing needs to be done for french knots or stitches which are handled in mouseMoveEvent_Erase 1678 } 1679 1680 void Editor::mousePressEvent_Rectangle(QMouseEvent *e) 1681 { 1682 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1683 m_rubberBand = QRect(); 1684 } 1685 1686 void Editor::mouseMoveEvent_Rectangle(QMouseEvent *e) 1687 { 1688 QPoint p = e->pos(); 1689 1690 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1691 1692 m_cellEnd = m_cellTracking = contentsToCell(p); 1693 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1694 1695 update(); 1696 } 1697 1698 void Editor::mouseReleaseEvent_Rectangle(QMouseEvent *) 1699 { 1700 int x = m_rubberBand.left(); 1701 int y = m_rubberBand.top(); 1702 QPoint cell(x, y); 1703 1704 QUndoCommand *cmd = new DrawRectangleCommand(m_document); 1705 1706 while (++x <= m_rubberBand.right()) { 1707 new AddStitchCommand(m_document, cell, Stitch::Full, m_document->pattern()->palette().currentIndex(), cmd); 1708 cell.setX(x); 1709 } 1710 1711 while (++y <= m_rubberBand.bottom()) { 1712 new AddStitchCommand(m_document, cell, Stitch::Full, m_document->pattern()->palette().currentIndex(), cmd); 1713 cell.setY(y); 1714 } 1715 1716 while (--x >= m_rubberBand.left()) { 1717 new AddStitchCommand(m_document, cell, Stitch::Full, m_document->pattern()->palette().currentIndex(), cmd); 1718 cell.setX(x); 1719 } 1720 1721 while (--y >= m_rubberBand.top()) { 1722 new AddStitchCommand(m_document, cell, Stitch::Full, m_document->pattern()->palette().currentIndex(), cmd); 1723 cell.setY(y); 1724 } 1725 1726 m_rubberBand = QRect(); // this will clear the rubber band rectangle on the next repaint 1727 1728 m_document->undoStack().push(cmd); 1729 } 1730 1731 void Editor::mousePressEvent_FillRectangle(QMouseEvent *e) 1732 { 1733 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1734 m_rubberBand = QRect(); 1735 } 1736 1737 void Editor::mouseMoveEvent_FillRectangle(QMouseEvent *e) 1738 { 1739 QPoint p = e->pos(); 1740 1741 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1742 1743 m_cellEnd = m_cellTracking = contentsToCell(p); 1744 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1745 1746 update(); 1747 } 1748 1749 void Editor::mouseReleaseEvent_FillRectangle(QMouseEvent *) 1750 { 1751 QUndoCommand *cmd = new FillRectangleCommand(m_document); 1752 1753 for (int y = m_rubberBand.top(); y <= m_rubberBand.bottom(); y++) { 1754 for (int x = m_rubberBand.left(); x <= m_rubberBand.right(); x++) { 1755 new AddStitchCommand(m_document, QPoint(x, y), Stitch::Full, m_document->pattern()->palette().currentIndex(), cmd); 1756 } 1757 } 1758 1759 m_rubberBand = QRect(); // this will clear the rubber band rectangle on the next repaint 1760 1761 m_document->undoStack().push(cmd); 1762 } 1763 1764 void Editor::mousePressEvent_Ellipse(QMouseEvent *e) 1765 { 1766 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1767 m_rubberBand = QRect(); 1768 } 1769 1770 void Editor::mouseMoveEvent_Ellipse(QMouseEvent *e) 1771 { 1772 QPoint p = e->pos(); 1773 1774 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1775 1776 m_cellEnd = m_cellTracking = contentsToCell(p); 1777 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1778 1779 update(); 1780 } 1781 1782 void Editor::mouseReleaseEvent_Ellipse(QMouseEvent *) 1783 { 1784 int bitmapWidth = m_document->pattern()->stitches().width(); 1785 int bitmapHeight = m_document->pattern()->stitches().height(); 1786 bool useFractionals = Configuration::toolShapes_UseFractionals(); 1787 1788 if (useFractionals) { 1789 bitmapWidth *= 2; 1790 bitmapHeight *= 2; 1791 m_cellStart *= 2; 1792 m_cellEnd *= 2; 1793 } 1794 1795 if (m_cellStart != m_cellEnd) { 1796 QBitmap canvas(bitmapWidth, bitmapHeight); 1797 QPainter painter; 1798 1799 canvas.fill(Qt::color0); 1800 painter.begin(&canvas); 1801 painter.setRenderHint(QPainter::Antialiasing, !Configuration::toolShapes_UseFractionals()); 1802 painter.setPen(QPen(Qt::color1)); 1803 painter.setBrush(Qt::NoBrush); 1804 painter.drawEllipse(m_rubberBand); 1805 painter.end(); 1806 1807 QUndoCommand *cmd = new DrawEllipseCommand(m_document); 1808 processBitmap(cmd, canvas); 1809 1810 m_document->undoStack().push(cmd); 1811 } 1812 1813 m_rubberBand = QRect(); 1814 } 1815 1816 void Editor::mousePressEvent_FillEllipse(QMouseEvent *e) 1817 { 1818 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1819 m_rubberBand = QRect(); 1820 } 1821 1822 void Editor::mouseMoveEvent_FillEllipse(QMouseEvent *e) 1823 { 1824 QPoint p = e->pos(); 1825 1826 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1827 1828 m_cellEnd = m_cellTracking = contentsToCell(p); 1829 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1830 1831 update(); 1832 } 1833 1834 void Editor::mouseReleaseEvent_FillEllipse(QMouseEvent *) 1835 { 1836 int bitmapWidth = m_document->pattern()->stitches().width(); 1837 int bitmapHeight = m_document->pattern()->stitches().height(); 1838 bool useFractionals = Configuration::toolShapes_UseFractionals(); 1839 1840 if (useFractionals) { 1841 bitmapWidth *= 2; 1842 bitmapHeight *= 2; 1843 m_cellStart *= 2; 1844 m_cellEnd *= 2; 1845 } 1846 1847 if (m_cellStart != m_cellEnd) { 1848 QBitmap canvas(bitmapWidth, bitmapHeight); 1849 QPainter painter; 1850 1851 canvas.fill(Qt::color0); 1852 painter.begin(&canvas); 1853 painter.setRenderHint(QPainter::Antialiasing, !useFractionals); 1854 painter.setPen(QPen(Qt::color1)); 1855 painter.setBrush(Qt::color1); 1856 painter.drawEllipse(QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1)))); 1857 painter.end(); 1858 1859 QUndoCommand *cmd = new FillEllipseCommand(m_document); 1860 processBitmap(cmd, canvas); 1861 1862 m_document->undoStack().push(cmd); 1863 } 1864 1865 m_rubberBand = QRect(); 1866 } 1867 1868 void Editor::mousePressEvent_FillPolygon(QMouseEvent *e) 1869 { 1870 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1871 m_polygon.append(m_cellStart); 1872 1873 update(); 1874 } 1875 1876 void Editor::mouseMoveEvent_FillPolygon(QMouseEvent *e) 1877 { 1878 m_cellTracking = contentsToCell(e->pos()); 1879 1880 if (m_cellTracking != m_cellStart) { 1881 m_polygon.append(m_cellTracking); 1882 m_cellStart = m_cellTracking; 1883 } 1884 1885 update(); 1886 } 1887 1888 void Editor::mouseReleaseEvent_FillPolygon(QMouseEvent *e) 1889 { 1890 int bitmapWidth = m_document->pattern()->stitches().width(); 1891 int bitmapHeight = m_document->pattern()->stitches().height(); 1892 bool useFractionals = Configuration::toolShapes_UseFractionals(); 1893 1894 if (useFractionals) { 1895 bitmapWidth *= 2; 1896 bitmapHeight *= 2; 1897 m_cellStart *= 2; 1898 m_cellEnd *= 2; 1899 } 1900 1901 m_cellEnd = contentsToCell(e->pos()); 1902 1903 if ((m_cellEnd == m_polygon.point(0)) && (m_polygon.count() > 2)) { 1904 QBitmap canvas(bitmapWidth, bitmapHeight); 1905 QPainter painter; 1906 1907 if (useFractionals) { 1908 for (int i = 0; i < m_polygon.size(); ++i) { 1909 m_polygon[i] *= 2; 1910 } 1911 } 1912 1913 canvas.fill(Qt::color0); 1914 painter.begin(&canvas); 1915 painter.setRenderHint(QPainter::Antialiasing, false); 1916 painter.setRenderHint(QPainter::Qt4CompatiblePainting, true); 1917 painter.setPen(QPen(Qt::color1)); 1918 painter.setBrush(Qt::color1); 1919 painter.drawPolygon(m_polygon); 1920 painter.end(); 1921 1922 QUndoCommand *cmd = new FillPolygonCommand(m_document); 1923 processBitmap(cmd, canvas); 1924 1925 m_document->undoStack().push(cmd); 1926 1927 m_polygon.clear(); 1928 } 1929 } 1930 1931 void Editor::mousePressEvent_Text(QMouseEvent *e) 1932 { 1933 mousePressEvent_Paste(e); // performs the required functions 1934 } 1935 1936 void Editor::mouseMoveEvent_Text(QMouseEvent *e) 1937 { 1938 mouseMoveEvent_Paste(e); // performs the required functions 1939 } 1940 1941 void Editor::mouseReleaseEvent_Text(QMouseEvent *e) 1942 { 1943 m_document->undoStack().push( 1944 new EditPasteCommand(m_document, m_pastePattern, contentsToCell(e->pos()) - m_pasteOffset, (e->modifiers() & Qt::ShiftModifier), i18n("Text"))); 1945 m_pastePattern = nullptr; 1946 setCursor(Qt::ArrowCursor); 1947 selectTool(m_oldToolMode); 1948 } 1949 1950 void Editor::mousePressEvent_Alphabet(QMouseEvent *) 1951 { 1952 // nothing to do 1953 } 1954 1955 void Editor::mouseMoveEvent_Alphabet(QMouseEvent *) 1956 { 1957 // nothing to do 1958 } 1959 1960 void Editor::mouseReleaseEvent_Alphabet(QMouseEvent *e) 1961 { 1962 if (m_activeCommand == nullptr) { 1963 m_activeCommand = new AlphabetCommand(m_document); 1964 m_document->undoStack().push(m_activeCommand); 1965 } else if ((m_activeCommand->text() == i18n("Alphabet")) && static_cast<AlphabetCommand *>(m_activeCommand)->childCount()) { 1966 toolCleanupAlphabet(); 1967 m_activeCommand = new AlphabetCommand(m_document); 1968 m_document->undoStack().push(m_activeCommand); 1969 } 1970 1971 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1972 m_cursorStack.push(m_cellEnd); 1973 1974 update(); 1975 } 1976 1977 void Editor::mousePressEvent_Select(QMouseEvent *e) 1978 { 1979 if (m_rubberBand.isValid()) { 1980 m_rubberBand = QRect(); 1981 update(); 1982 } 1983 1984 m_cellStart = m_cellTracking = m_cellEnd = contentsToCell(e->pos()); 1985 m_rubberBand = QRect(cellToRect(m_cellStart)); 1986 } 1987 1988 void Editor::mouseMoveEvent_Select(QMouseEvent *e) 1989 { 1990 QPoint p = e->pos(); 1991 1992 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 1993 1994 m_cellEnd = m_cellTracking = contentsToCell(p); 1995 m_rubberBand = QRect(m_cellStart, QSize(1, 1)).united(QRect(m_cellEnd, QSize(1, 1))); 1996 1997 update(); 1998 1999 QToolTip::showText( 2000 QCursor::pos(), 2001 QString::fromLatin1("%1,%2 %3 x %4").arg(m_rubberBand.left()).arg(m_rubberBand.top()).arg(m_rubberBand.width()).arg(m_rubberBand.height())); 2002 } 2003 2004 void Editor::mouseReleaseEvent_Select(QMouseEvent *) 2005 { 2006 m_selectionArea = m_rubberBand; 2007 emit(selectionMade(true)); 2008 } 2009 2010 void Editor::mousePressEvent_Backstitch(QMouseEvent *e) 2011 { 2012 m_cellStart = m_cellTracking = m_cellEnd = contentsToSnap(e->pos()); 2013 m_rubberBand = QRect(); 2014 } 2015 2016 void Editor::mouseMoveEvent_Backstitch(QMouseEvent *e) 2017 { 2018 QPoint p = e->pos(); 2019 2020 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 2021 2022 m_cellEnd = m_cellTracking = contentsToSnap(p); 2023 m_rubberBand = snapToCells(m_cellStart).united(snapToCells(m_cellEnd)); 2024 2025 update(); 2026 } 2027 2028 void Editor::mouseReleaseEvent_Backstitch(QMouseEvent *) 2029 { 2030 m_rubberBand = QRect(); 2031 2032 if (m_cellStart != m_cellEnd) { 2033 m_document->undoStack().push(new AddBackstitchCommand(m_document, m_cellStart, m_cellEnd, m_document->pattern()->palette().currentIndex())); 2034 } 2035 } 2036 2037 void Editor::mousePressEvent_ColorPicker(QMouseEvent *) 2038 { 2039 // nothing to be done 2040 } 2041 2042 void Editor::mouseMoveEvent_ColorPicker(QMouseEvent *) 2043 { 2044 // nothing to be done 2045 } 2046 2047 void Editor::mouseReleaseEvent_ColorPicker(QMouseEvent *e) 2048 { 2049 int colorIndex = -1; 2050 StitchQueue *queue = m_document->pattern()->stitches().stitchQueueAt(contentsToCell(e->pos())); 2051 2052 if (queue) { 2053 Stitch::Type type = stitchMap[0][m_zoneStart]; 2054 QListIterator<Stitch *> q(*queue); 2055 2056 while (q.hasNext()) { 2057 Stitch *stitch = q.next(); 2058 2059 if (stitch->type & type) { 2060 colorIndex = stitch->colorIndex; 2061 break; 2062 } 2063 } 2064 2065 if (colorIndex != -1) { 2066 m_document->pattern()->palette().setCurrentIndex(colorIndex); 2067 m_document->palette()->update(); 2068 2069 if (m_colorHighlight) { 2070 drawContents(); 2071 } 2072 } 2073 } 2074 } 2075 2076 void Editor::mousePressEvent_Paste(QMouseEvent *e) 2077 { 2078 QPoint cell = contentsToCell(e->pos()); 2079 2080 if (!m_pastePattern->stitches().extents().translated(m_cellStart).contains(cell)) { 2081 m_cellStart = m_cellTracking = m_cellEnd = cell; 2082 } 2083 2084 m_pasteOffset = cell - m_cellStart; 2085 2086 update(); 2087 } 2088 2089 void Editor::mouseMoveEvent_Paste(QMouseEvent *e) 2090 { 2091 QPoint p = e->pos(); 2092 2093 dynamic_cast<QScrollArea *>(parentWidget()->parentWidget())->ensureVisible(p.x(), p.y()); 2094 2095 m_cellTracking = contentsToCell(p) - m_pasteOffset; 2096 2097 if (m_cellTracking != m_cellEnd) { 2098 m_cellEnd = m_cellTracking; 2099 update(); 2100 } 2101 } 2102 2103 void Editor::mouseReleaseEvent_Paste(QMouseEvent *e) 2104 { 2105 m_document->undoStack().push( 2106 new EditPasteCommand(m_document, m_pastePattern, contentsToCell(e->pos()) - m_pasteOffset, (e->modifiers() & Qt::ShiftModifier), i18n("Paste"))); 2107 m_pasteData.clear(); 2108 m_pastePattern = nullptr; 2109 setCursor(Qt::ArrowCursor); 2110 selectTool(m_oldToolMode); 2111 } 2112 2113 void Editor::mousePressEvent_Mirror(QMouseEvent *e) 2114 { 2115 mousePressEvent_Paste(e); 2116 } 2117 2118 void Editor::mouseMoveEvent_Mirror(QMouseEvent *e) 2119 { 2120 mouseMoveEvent_Paste(e); 2121 } 2122 2123 void Editor::mouseReleaseEvent_Mirror(QMouseEvent *e) 2124 { 2125 m_document->undoStack().push(new MirrorSelectionCommand(m_document, 2126 m_selectionArea, 2127 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 2128 maskStitches(), 2129 m_maskBackstitch, 2130 m_maskKnot, 2131 m_orientation, 2132 m_makesCopies, 2133 m_pasteData, 2134 m_pastePattern, 2135 contentsToCell(e->pos()) - m_pasteOffset, 2136 (e->modifiers() & Qt::ShiftModifier))); 2137 m_pasteData.clear(); 2138 m_pastePattern = nullptr; 2139 setCursor(Qt::ArrowCursor); 2140 selectTool(m_oldToolMode); 2141 } 2142 2143 void Editor::mousePressEvent_Rotate(QMouseEvent *e) 2144 { 2145 mousePressEvent_Paste(e); 2146 } 2147 2148 void Editor::mouseMoveEvent_Rotate(QMouseEvent *e) 2149 { 2150 mouseMoveEvent_Paste(e); 2151 } 2152 2153 void Editor::mouseReleaseEvent_Rotate(QMouseEvent *e) 2154 { 2155 m_document->undoStack().push(new RotateSelectionCommand(m_document, 2156 m_selectionArea, 2157 (m_maskColor) ? m_document->pattern()->palette().currentIndex() : -1, 2158 maskStitches(), 2159 m_maskBackstitch, 2160 m_maskKnot, 2161 m_rotation, 2162 m_makesCopies, 2163 m_pasteData, 2164 m_pastePattern, 2165 contentsToCell(e->pos()) - m_pasteOffset, 2166 (e->modifiers() & Qt::ShiftModifier))); 2167 m_pastePattern = nullptr; 2168 m_pasteData.clear(); 2169 setCursor(Qt::ArrowCursor); 2170 selectTool(m_oldToolMode); 2171 } 2172 2173 QPoint Editor::contentsToCell(const QPoint &p) const 2174 { 2175 return QPoint(p.x() / m_cellWidth, p.y() / m_cellHeight); 2176 } 2177 2178 int Editor::contentsToZone(const QPoint &p) const 2179 { 2180 QPoint cell(p.x() * 2 / m_cellWidth, p.y() * 2 / m_cellHeight); 2181 int zone = (cell.y() % 2) * 2 + (cell.x() % 2); 2182 2183 return zone; 2184 } 2185 2186 QPoint Editor::contentsToSnap(const QPoint &p) const 2187 { 2188 int w = m_document->pattern()->stitches().width() * 2; 2189 int h = m_document->pattern()->stitches().height() * 2; 2190 2191 int x = (int)(round((double)(w * p.x()) / width())); 2192 int y = (int)(round((double)(h * p.y()) / height())); 2193 2194 return QPoint(x, y); 2195 } 2196 2197 QRect Editor::snapToCells(const QPoint &p) const 2198 { 2199 int x = p.x(); 2200 int y = p.y(); 2201 2202 int rectWidth = (x % 2) ? 1 : 2; 2203 int rectHeight = (y % 2) ? 1 : 2; 2204 int rectLeft = (x - rectWidth) / 2; 2205 int rectTop = (y - rectHeight) / 2; 2206 2207 return QRect(rectLeft, rectTop, rectWidth, rectHeight); 2208 } 2209 2210 QRect Editor::cellToRect(const QPoint &cell) const 2211 { 2212 return QRect(cell.x(), cell.y(), 1, 1); 2213 } 2214 2215 QRect Editor::rectToContents(const QRect &rect) const 2216 { 2217 return QRect(rect.left() * m_cellWidth, rect.top() * m_cellHeight, rect.width() * m_cellWidth, rect.height() * m_cellHeight); 2218 } 2219 2220 void Editor::processBitmap(QUndoCommand *parent, const QBitmap &canvas) 2221 { 2222 QImage image = canvas.toImage(); 2223 int colorIndex = m_document->pattern()->palette().currentIndex(); 2224 2225 for (int y = 0; y < image.height(); y++) { 2226 for (int x = 0; x < image.width(); x++) { 2227 if (image.pixelIndex(x, y) == 1) { 2228 if (Configuration::toolShapes_UseFractionals()) { 2229 int zone = (y % 2) * 2 + (x % 2); 2230 new AddStitchCommand(m_document, QPoint(x / 2, y / 2), stitchMap[0][zone], colorIndex, parent); 2231 } else { 2232 new AddStitchCommand(m_document, QPoint(x, y), Stitch::Full, colorIndex, parent); 2233 } 2234 } 2235 } 2236 } 2237 } 2238 2239 QRect Editor::selectionArea() 2240 { 2241 return m_selectionArea; 2242 } 2243 2244 void Editor::resetSelectionArea() 2245 { 2246 toolCleanupSelect(); 2247 } 2248 2249 QRect Editor::visibleCells() 2250 { 2251 QRect cells; 2252 2253 int left = -pos().x(); 2254 int top = -pos().y(); 2255 QSize viewportSize = dynamic_cast<QScrollArea *>(parent()->parent())->viewport()->size(); 2256 int viewportWidth = viewportSize.width(); 2257 int viewportHeight = viewportSize.height(); 2258 int documentWidth = m_document->pattern()->stitches().width(); 2259 int documentHeight = m_document->pattern()->stitches().height(); 2260 2261 QPoint topLeft = contentsToCell(QPoint(left, top)); 2262 QPoint bottomRight = contentsToCell(QPoint(left + viewportWidth, top + viewportHeight)); 2263 cells.setCoords(topLeft.x(), topLeft.y(), std::min(bottomRight.x(), documentWidth), std::min(bottomRight.y(), documentHeight)); 2264 2265 return cells; 2266 } 2267 2268 #include "moc_Editor.cpp"