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"