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

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 "MainWindow.h"
0012 
0013 #include <QAction>
0014 #include <QActionGroup>
0015 #include <QClipboard>
0016 #include <QDataStream>
0017 #include <QDockWidget>
0018 #include <QFileDialog>
0019 #include <QGridLayout>
0020 #include <QMenu>
0021 #include <QMimeData>
0022 #include <QPaintEngine>
0023 #include <QPainter>
0024 #include <QPrintDialog>
0025 #include <QPrintEngine>
0026 #include <QPrintPreviewDialog>
0027 #include <QPrinter>
0028 #include <QProgressDialog>
0029 #include <QSaveFile>
0030 #include <QScrollArea>
0031 #include <QTemporaryFile>
0032 #include <QUndoView>
0033 #include <QUrl>
0034 
0035 #include <KActionCollection>
0036 #include <KConfigDialog>
0037 #include <KIO/FileCopyJob>
0038 #include <KIO/StatJob>
0039 #include <KLocalizedString>
0040 #include <KMessageBox>
0041 #include <KRecentFilesAction>
0042 #include <KSelectAction>
0043 #include <KXMLGUIFactory>
0044 #include <kwidgetsaddons_version.h>
0045 
0046 #include "BackgroundImage.h"
0047 #include "Commands.h"
0048 #include "ConfigurationDialogs.h"
0049 #include "Document.h"
0050 #include "Editor.h"
0051 #include "ExtendPatternDlg.h"
0052 #include "FilePropertiesDlg.h"
0053 #include "Floss.h"
0054 #include "FlossScheme.h"
0055 #include "ImportImageDlg.h"
0056 #include "Palette.h"
0057 #include "PaletteManagerDlg.h"
0058 #include "PaperSizes.h"
0059 #include "Preview.h"
0060 #include "PrintSetupDlg.h"
0061 #include "Scale.h"
0062 #include "ScaledPixmapLabel.h"
0063 #include "SchemeManager.h"
0064 #include "SymbolLibrary.h"
0065 #include "SymbolManager.h"
0066 #include "configuration.h"
0067 
0068 MainWindow::MainWindow()
0069     : m_printer(nullptr)
0070 {
0071     setupActions();
0072 }
0073 
0074 MainWindow::MainWindow(const QUrl &url)
0075     : m_printer(nullptr)
0076 {
0077     setupMainWindow();
0078     setupLayout();
0079     setupDockWindows();
0080     setupActions();
0081     setupDocument();
0082     setupConnections();
0083     setupActionDefaults();
0084     loadSettings();
0085     fileOpen(url);
0086     setupActionsFromDocument();
0087     setCaption(m_document->url().fileName(), !m_document->undoStack().isClean());
0088     this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->hide();
0089 }
0090 
0091 MainWindow::MainWindow(const QString &source)
0092     : m_printer(nullptr)
0093 {
0094     setupMainWindow();
0095     setupLayout();
0096     setupDockWindows();
0097     setupActions();
0098     setupDocument();
0099     setupConnections();
0100     setupActionDefaults();
0101     loadSettings();
0102     convertImage(source);
0103     setupActionsFromDocument();
0104     setCaption(m_document->url().fileName(), !m_document->undoStack().isClean());
0105     this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->show();
0106 }
0107 
0108 void MainWindow::setupMainWindow()
0109 {
0110     setObjectName(QStringLiteral("MainWindow#"));
0111     setAutoSaveSettings();
0112 }
0113 
0114 void MainWindow::setupLayout()
0115 {
0116     QScrollArea *scrollArea = new QScrollArea();
0117     scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
0118     scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
0119     m_editor = new Editor(scrollArea);
0120     scrollArea->installEventFilter(m_editor);
0121     scrollArea->setWidget(m_editor);
0122 
0123     m_horizontalScale = m_editor->horizontalScale();
0124     m_verticalScale = m_editor->verticalScale();
0125 
0126     QWidget *layout = new QWidget();
0127 
0128     QGridLayout *gridLayout = new QGridLayout(layout);
0129     gridLayout->addWidget(m_horizontalScale, 0, 1);
0130     gridLayout->addWidget(m_verticalScale, 1, 0);
0131     gridLayout->addWidget(scrollArea, 1, 1);
0132 
0133     setCentralWidget(layout);
0134 
0135     setStatusBar(nullptr);
0136 }
0137 
0138 void MainWindow::setupDocument()
0139 {
0140     m_document = new Document();
0141 
0142     m_editor->setDocument(m_document);
0143     m_editor->setPreview(m_preview);
0144     m_palette->setDocument(m_document);
0145     m_preview->setDocument(m_document);
0146     m_history->setStack(&(m_document->undoStack()));
0147 
0148     m_document->addView(m_editor);
0149     m_document->addView(m_preview);
0150     m_document->addView(m_palette);
0151 }
0152 
0153 void MainWindow::setupConnections()
0154 {
0155     KActionCollection *actions = actionCollection();
0156 
0157     connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("edit_undo")), &QAction::setEnabled);
0158     connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("file_revert")), &QAction::setEnabled);
0159     connect(&(m_document->undoStack()), &QUndoStack::canRedoChanged, actions->action(QStringLiteral("edit_redo")), &QAction::setEnabled);
0160     connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::clipboardDataChanged);
0161     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_cut")), &QAction::setEnabled);
0162     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_copy")), &QAction::setEnabled);
0163     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorHorizontal")), &QAction::setEnabled);
0164     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorVertical")), &QAction::setEnabled);
0165     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate90")), &QAction::setEnabled);
0166     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate180")), &QAction::setEnabled);
0167     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate270")), &QAction::setEnabled);
0168     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("patternCropToSelection")), &QAction::setEnabled);
0169     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertColumns")), &QAction::setEnabled);
0170     connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertRows")), &QAction::setEnabled);
0171     connect(&(m_document->undoStack()), &QUndoStack::undoTextChanged, this, &MainWindow::undoTextChanged);
0172     connect(&(m_document->undoStack()), &QUndoStack::redoTextChanged, this, &MainWindow::redoTextChanged);
0173     connect(&(m_document->undoStack()), &QUndoStack::cleanChanged, this, &MainWindow::documentModified);
0174     connect(m_palette, &Palette::colorSelected, m_editor, static_cast<void (Editor::*)()>(&Editor::drawContents));
0175     connect(m_palette, static_cast<void (Palette::*)(int, int)>(&Palette::swapColors), this, &MainWindow::paletteSwapColors);
0176     connect(m_palette, static_cast<void (Palette::*)(int, int)>(&Palette::replaceColor), this, &MainWindow::paletteReplaceColor);
0177     connect(m_palette, &Palette::signalStateChanged, this, static_cast<void (KXmlGuiWindow::*)(const QString &, bool)>(&KXmlGuiWindow::slotStateChanged));
0178     connect(m_palette, &Palette::customContextMenuRequested, this, &MainWindow::paletteContextMenu);
0179     connect(m_editor, &Editor::changedVisibleCells, m_preview, &Preview::setVisibleCells);
0180     connect(m_preview,
0181             static_cast<void (Preview::*)(QPoint)>(&Preview::clicked),
0182             m_editor,
0183             static_cast<void (Editor::*)(const QPoint &)>(&Editor::previewClicked));
0184     connect(m_preview,
0185             static_cast<void (Preview::*)(QRect)>(&Preview::clicked),
0186             m_editor,
0187             static_cast<void (Editor::*)(const QRect &)>(&Editor::previewClicked));
0188 }
0189 
0190 void MainWindow::setupActionDefaults()
0191 {
0192     KActionCollection *actions = actionCollection();
0193 
0194     actions->action(QStringLiteral("maskStitch"))->setChecked(false);
0195     actions->action(QStringLiteral("maskColor"))->setChecked(false);
0196     actions->action(QStringLiteral("maskBackstitch"))->setChecked(false);
0197     actions->action(QStringLiteral("maskKnot"))->setChecked(false);
0198 
0199     actions->action(QStringLiteral("stitchFull"))->trigger(); // Select full stitch
0200 
0201     actions->action(QStringLiteral("toolPaint"))->trigger(); // Select paint tool
0202 
0203     clipboardDataChanged();
0204 }
0205 
0206 MainWindow::~MainWindow()
0207 {
0208     delete m_printer;
0209 }
0210 
0211 Editor *MainWindow::editor()
0212 {
0213     return m_editor;
0214 }
0215 
0216 Preview *MainWindow::preview()
0217 {
0218     return m_preview;
0219 }
0220 
0221 Palette *MainWindow::palette()
0222 {
0223     return m_palette;
0224 }
0225 
0226 bool MainWindow::queryClose()
0227 {
0228     if (m_document->undoStack().isClean()) {
0229         return true;
0230     }
0231 
0232     while (true) {
0233 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0234         int messageBoxResult = KMessageBox::warningTwoActionsCancel(this,
0235 #else
0236         int messageBoxResult = KMessageBox::warningYesNoCancel(this,
0237 #endif
0238                                                                     i18n("Save changes to document?"),
0239                                                                     QString(),
0240                                                                     KStandardGuiItem::save(),
0241                                                                     KStandardGuiItem::discard(),
0242                                                                     KStandardGuiItem::cancel());
0243 
0244         switch (messageBoxResult) {
0245 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0246         case KMessageBox::PrimaryAction:
0247 #else
0248         case KMessageBox::Yes:
0249 #endif
0250             fileSave();
0251 
0252             if (m_document->undoStack().isClean()) {
0253                 return true;
0254             } else {
0255                 KMessageBox::error(this, i18n("Unable to save the file"));
0256             }
0257 
0258             break;
0259 
0260 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0261         case KMessageBox::SecondaryAction:
0262 #else
0263         case KMessageBox::No:
0264 #endif
0265             return true;
0266 
0267         case KMessageBox::Cancel:
0268             return false;
0269         }
0270     }
0271 }
0272 
0273 void MainWindow::setupActionsFromDocument()
0274 {
0275     KActionCollection *actions = actionCollection();
0276 
0277     actions->action(QStringLiteral("file_revert"))->setEnabled(!m_document->undoStack().isClean());
0278     actions->action(QStringLiteral("edit_undo"))->setEnabled(m_document->undoStack().canUndo());
0279     actions->action(QStringLiteral("edit_redo"))->setEnabled(m_document->undoStack().canRedo());
0280 
0281     updateBackgroundImageActionLists();
0282 }
0283 
0284 void MainWindow::fileNew()
0285 {
0286     MainWindow *window = new MainWindow(QUrl());
0287     window->show();
0288 }
0289 
0290 void MainWindow::fileOpen()
0291 {
0292     fileOpen(QFileDialog::getOpenFileUrl(this,
0293                                          i18n("Open file"),
0294                                          QUrl::fromLocalFile(QDir::homePath()),
0295                                          i18n("KXStitch Patterns (*.kxs);;PC Stitch Patterns (*.pat);;All Files (*)")));
0296 }
0297 
0298 void MainWindow::fileOpen(const QUrl &url)
0299 {
0300     MainWindow *window;
0301     bool docEmpty = (m_document->undoStack().isClean() && (m_document->url().toString() == i18n("Untitled")));
0302 
0303     if (url.isValid()) {
0304         if (docEmpty) {
0305             QTemporaryFile tmpFile;
0306 
0307             if (tmpFile.open()) {
0308                 tmpFile.close();
0309 
0310                 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
0311 
0312                 if (job->exec()) {
0313                     /* In earlier versions of KDE/Qt creating a QDataStream on tmpFile allowed reading the data from the copied file.
0314                      * Somewhere after KDE 5.55.0/Qt 5.9.7 this no longer possible as tmpFile size() is reported with a length of 0
0315                      * whereas previously tmpFile size() was reported as the size of the copied file.
0316                      * Therefore open a new QFile on the temporary file after downloading to allow reading.
0317                      */
0318                     QFile reader(tmpFile.fileName());
0319                     if (reader.open(QIODevice::ReadOnly)) {
0320                         QDataStream stream(&reader);
0321 
0322                         try {
0323                             m_document->readKXStitch(stream);
0324                             m_document->setUrl(url);
0325                             KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
0326                             action->addUrl(url);
0327                             action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
0328                         } catch (const InvalidFile &e) {
0329                             stream.device()->seek(0);
0330 
0331                             try {
0332                                 m_document->readPCStitch(stream);
0333                             } catch (const InvalidFile &e) {
0334                                 KMessageBox::error(nullptr, i18n("The file does not appear to be a recognized cross stitch file."));
0335                             }
0336                         } catch (const InvalidFileVersion &e) {
0337                             KMessageBox::error(nullptr, i18n("This version of the file is not supported.\n%1", e.version));
0338                         } catch (const FailedReadFile &e) {
0339                             KMessageBox::error(nullptr, i18n("Failed to read the file.\n%1.", e.status));
0340                             m_document->initialiseNew();
0341                         }
0342 
0343                         setupActionsFromDocument();
0344                         m_editor->readDocumentSettings();
0345                         m_preview->readDocumentSettings();
0346                         m_palette->update();
0347                         documentModified(true); // this is the clean value true
0348 
0349                         reader.close();
0350                     } else {
0351                         KMessageBox::error(nullptr, reader.errorString());
0352                     }
0353                 } else {
0354                     KMessageBox::error(nullptr, job->errorString());
0355                 }
0356 
0357                 tmpFile.close();
0358             } else {
0359                 KMessageBox::error(nullptr, tmpFile.errorString());
0360             }
0361         } else {
0362             window = new MainWindow(url);
0363             window->show();
0364         }
0365     }
0366 }
0367 
0368 void MainWindow::fileSave()
0369 {
0370     QUrl url = m_document->url();
0371 
0372     if (url.toString() == i18n("Untitled")) {
0373         fileSaveAs();
0374     } else {
0375         // ### Why use QUrl everywhere if this only supports local files?
0376         QSaveFile file(url.toLocalFile());
0377 
0378         if (file.open(QIODevice::WriteOnly)) {
0379             QDataStream stream(&file);
0380 
0381             try {
0382                 m_document->write(stream);
0383 
0384                 if (!file.commit()) {
0385                     throw FailedWriteFile(stream.status());
0386                 }
0387 
0388                 m_document->undoStack().setClean();
0389             } catch (const FailedWriteFile &e) {
0390                 KMessageBox::error(nullptr, QString(i18n("Failed to save the file.\n%1", file.errorString())));
0391                 file.cancelWriting();
0392             }
0393         } else {
0394             KMessageBox::error(nullptr, QString(i18n("Failed to open the file.\n%1", file.errorString())));
0395         }
0396     }
0397 }
0398 
0399 void MainWindow::fileSaveAs()
0400 {
0401     QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Patterns (*.kxs)"));
0402 
0403     if (url.isValid()) {
0404         m_document->setUrl(url);
0405         fileSave();
0406         KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
0407         action->addUrl(url);
0408         action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
0409     }
0410 }
0411 
0412 void MainWindow::fileRevert()
0413 {
0414     if (!m_document->undoStack().isClean()) {
0415 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0416         if (KMessageBox::warningTwoActions(this,
0417 #else
0418         if (KMessageBox::warningYesNo(this,
0419 #endif
0420                                            i18n("Revert changes to document?"),
0421                                            QString(),
0422                                            KGuiItem(i18nc("@action:button", "Revert"), QStringLiteral("document-revert")),
0423                                            KStandardGuiItem::cancel())
0424 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0425             == KMessageBox::PrimaryAction) {
0426 #else
0427             == KMessageBox::Yes) {
0428 #endif
0429             m_document->undoStack().setIndex(m_document->undoStack().cleanIndex());
0430         }
0431     }
0432 }
0433 
0434 void MainWindow::filePrintSetup()
0435 {
0436     if (m_printer == nullptr) {
0437         m_printer = new QPrinter();
0438     }
0439 
0440     QPointer<PrintSetupDlg> printSetupDlg = new PrintSetupDlg(this, m_document, m_printer);
0441 
0442     if (printSetupDlg->exec() == QDialog::Accepted) {
0443         m_document->undoStack().push(new UpdatePrinterConfigurationCommand(m_document, printSetupDlg->printerConfiguration()));
0444     }
0445 
0446     delete printSetupDlg;
0447 }
0448 
0449 void MainWindow::filePrint()
0450 {
0451     if (m_printer == nullptr) {
0452         filePrintSetup();
0453     }
0454 
0455     if (!m_document->printerConfiguration().pages().isEmpty()) {
0456         m_printer->setFullPage(true);
0457         m_printer->setPrintRange(QPrinter::AllPages);
0458         m_printer->setFromTo(1, m_document->printerConfiguration().pages().count());
0459 
0460         QPointer<QPrintDialog> printDialog = new QPrintDialog(m_printer, this);
0461 
0462         if (printDialog->exec() == QDialog::Accepted) {
0463             printPages();
0464         }
0465 
0466         delete printDialog;
0467     } else {
0468         KMessageBox::information(this, i18n("There is nothing to print"));
0469     }
0470 }
0471 
0472 void MainWindow::printPages()
0473 {
0474     QList<Page *> pages = m_document->printerConfiguration().pages();
0475 
0476     int fromPage = 1;
0477     int toPage = pages.count();
0478 
0479     if (m_printer->printRange() == QPrinter::PageRange) {
0480         fromPage = m_printer->fromPage();
0481         toPage = m_printer->toPage();
0482     }
0483 
0484     while (toPage < pages.count())
0485         pages.removeLast();
0486     while (--fromPage)
0487         pages.removeFirst();
0488 
0489     int totalPages = pages.count();
0490 
0491     const Page *page = (m_printer->pageOrder() == QPrinter::FirstPageFirst) ? pages.takeFirst() : pages.takeLast();
0492 
0493     m_printer->setPageSize(page->pageSize());
0494     m_printer->setPageOrientation(page->orientation());
0495 
0496     QPainter painter;
0497     painter.begin(m_printer);
0498     painter.setRenderHint(QPainter::Antialiasing, true);
0499 
0500     for (int p = 0; p < totalPages;) {
0501         int paperWidth = PageSizes::width(page->pageSize().id(), page->orientation());
0502         int paperHeight = PageSizes::height(page->pageSize().id(), page->orientation());
0503 
0504         painter.setWindow(0, 0, paperWidth, paperHeight);
0505 
0506         page->render(m_document, &painter);
0507 
0508         if (++p < totalPages) {
0509             page = (m_printer->pageOrder() == QPrinter::FirstPageFirst) ? pages.takeFirst() : pages.takeLast();
0510 
0511             m_printer->setPageSize(page->pageSize());
0512             m_printer->setPageOrientation(page->orientation());
0513 
0514             m_printer->newPage();
0515         }
0516     }
0517 
0518     painter.end();
0519 }
0520 
0521 void MainWindow::fileImportImage()
0522 {
0523     MainWindow *window;
0524     bool docEmpty = ((m_document->undoStack().isClean()) && (m_document->url().toString() == i18n("Untitled")));
0525     QUrl url = QFileDialog::getOpenFileUrl(this,
0526                                            i18n("Import Image"),
0527                                            QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)),
0528                                            i18n("Images (*.bmp *.gif *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm *.svg)"));
0529 
0530     if (url.isValid()) {
0531         QTemporaryFile tmpFile;
0532 
0533         if (tmpFile.open()) {
0534             KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
0535 
0536             if (job->exec()) {
0537                 if (docEmpty) {
0538                     convertImage(tmpFile.fileName());
0539                     this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->show();
0540                 } else {
0541                     window = new MainWindow(tmpFile.fileName());
0542                     window->show();
0543                 }
0544             } else {
0545                 KMessageBox::error(nullptr, job->errorString());
0546             }
0547         }
0548     }
0549 }
0550 
0551 void MainWindow::convertImage(const QString &source)
0552 {
0553     Magick::Image image(source.toStdString());
0554 
0555     QMap<int, QColor> documentFlosses;
0556     QList<qint16> symbolIndexes = SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes();
0557 
0558     QPointer<ImportImageDlg> importImageDlg = new ImportImageDlg(this, image);
0559 
0560     if (importImageDlg->exec()) {
0561         Magick::Image convertedImage = importImageDlg->convertedImage();
0562 
0563         int imageWidth = convertedImage.columns();
0564         int imageHeight = convertedImage.rows();
0565         int documentWidth = imageWidth;
0566         int documentHeight = imageHeight;
0567 
0568         bool useFractionals = importImageDlg->useFractionals();
0569 
0570 /*
0571  * ImageMagick prior to V7 used matte (opacity) to determine if an image has transparency.
0572  * 0.0 for transparent to 1.0 for opaque
0573  *
0574  * ImageMagick V7 now uses alpha (transparency).
0575  * 1.0 for transparent to 0.0 for opaque
0576  *
0577  * Access to pixels has changed too, V7 can use pixelColor to access the color of a particular
0578  * pixel, but although this was available in V6, it doesn't appear to produce the same result
0579  * and has resulted in black images when importing.
0580  */
0581 #if MagickLibVersion < 0x700
0582         bool hasTransparency = convertedImage.matte();
0583         double transparent = 1.0;
0584         const Magick::PixelPacket *pixels = convertedImage.getConstPixels(0, 0, imageWidth, imageHeight);
0585 #else
0586         bool hasTransparency = convertedImage.alpha();
0587         double transparent = 0.0;
0588 #endif
0589 
0590         bool ignoreColor = importImageDlg->ignoreColor();
0591         Magick::Color ignoreColorValue = importImageDlg->ignoreColorValue();
0592 
0593         int pixelCount = imageWidth * imageHeight;
0594 
0595         if (useFractionals) {
0596             documentWidth /= 2;
0597             documentHeight /= 2;
0598         }
0599 
0600         QString schemeName = importImageDlg->flossScheme();
0601         FlossScheme *flossScheme = SchemeManager::scheme(schemeName);
0602 
0603         QUndoCommand *importImageCommand = new ImportImageCommand(m_document);
0604         new ResizeDocumentCommand(m_document, documentWidth, documentHeight, importImageCommand);
0605         new ChangeSchemeCommand(m_document, schemeName, importImageCommand);
0606 
0607         QProgressDialog progress(i18n("Converting to stitches"), i18n("Cancel"), 0, pixelCount, this);
0608         progress.setWindowModality(Qt::WindowModal);
0609 
0610         for (int dy = 0; dy < imageHeight; dy++) {
0611             progress.setValue(dy * imageWidth);
0612             QApplication::processEvents();
0613 
0614             if (progress.wasCanceled()) {
0615                 delete importImageDlg;
0616                 delete importImageCommand;
0617                 return;
0618             }
0619 
0620             for (int dx = 0; dx < imageWidth; dx++) {
0621 #if MagickLibVersion < 0x700
0622                 Magick::ColorRGB rgb = Magick::Color(*pixels++);
0623 #else
0624                 Magick::ColorRGB rgb = convertedImage.pixelColor(dx, dy);
0625 #endif
0626 
0627                 if (hasTransparency && (rgb.alpha() == transparent)) {
0628                     // ignore this pixel as it is transparent
0629                 } else {
0630                     if (!(ignoreColor && (rgb == ignoreColorValue))) {
0631                         int flossIndex;
0632                         QColor color((int)(255 * rgb.red()), (int)(255 * rgb.green()), (int)(255 * rgb.blue()));
0633 
0634                         for (flossIndex = 0; flossIndex < documentFlosses.count(); ++flossIndex) {
0635                             if (documentFlosses[flossIndex] == color) {
0636                                 break;
0637                             }
0638                         }
0639 
0640                         if (flossIndex == documentFlosses.count()) { // reached the end of the list
0641                             qint16 stitchSymbol = symbolIndexes.takeFirst();
0642                             Qt::PenStyle backstitchSymbol(Qt::SolidLine);
0643                             Floss *floss = flossScheme->find(color);
0644 
0645                             DocumentFloss *documentFloss = new DocumentFloss(floss->name(),
0646                                                                              stitchSymbol,
0647                                                                              backstitchSymbol,
0648                                                                              Configuration::palette_StitchStrands(),
0649                                                                              Configuration::palette_BackstitchStrands());
0650                             documentFloss->setFlossColor(floss->color());
0651                             new AddDocumentFlossCommand(m_document, flossIndex, documentFloss, importImageCommand);
0652                             documentFlosses.insert(flossIndex, color);
0653                         }
0654 
0655                         // at this point
0656                         //   flossIndex will be the index for the found color
0657                         if (useFractionals) {
0658                             int zone = (dy % 2) * 2 + (dx % 2);
0659                             new AddStitchCommand(m_document, QPoint(dx / 2, dy / 2), stitchMap[0][zone], flossIndex, importImageCommand);
0660                         } else {
0661                             new AddStitchCommand(m_document, QPoint(dx, dy), Stitch::Full, flossIndex, importImageCommand);
0662                         }
0663                     }
0664                 }
0665             }
0666         }
0667 
0668         new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), importImageDlg->horizontalClothCount(), importImageCommand);
0669         new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), importImageDlg->verticalClothCount(), importImageCommand);
0670         m_document->undoStack().push(importImageCommand);
0671 
0672         convertPreview(source, importImageDlg->croppedArea());
0673     }
0674 
0675     delete importImageDlg;
0676 }
0677 
0678 void MainWindow::convertPreview(const QString &source, const QRect &croppedArea)
0679 {
0680     QPixmap pixmap;
0681     pixmap.load(source);
0682     pixmap = pixmap.copy(croppedArea);
0683     m_imageLabel->setPixmap(pixmap);
0684 }
0685 
0686 void MainWindow::fileProperties()
0687 {
0688     QPointer<FilePropertiesDlg> filePropertiesDlg = new FilePropertiesDlg(this, m_document);
0689 
0690     if (filePropertiesDlg->exec()) {
0691         QUndoCommand *cmd = new FilePropertiesCommand(m_document);
0692 
0693         if ((filePropertiesDlg->documentWidth() != m_document->pattern()->stitches().width())
0694             || (filePropertiesDlg->documentHeight() != m_document->pattern()->stitches().height())) {
0695             new ResizeDocumentCommand(m_document, filePropertiesDlg->documentWidth(), filePropertiesDlg->documentHeight(), cmd);
0696         }
0697 
0698         if (filePropertiesDlg->unitsFormat()
0699             != static_cast<Configuration::EnumDocument_UnitsFormat::type>(m_document->property(QStringLiteral("unitsFormat")).toInt())) {
0700             new SetPropertyCommand(m_document, QStringLiteral("unitsFormat"), QVariant(filePropertiesDlg->unitsFormat()), cmd);
0701         }
0702 
0703         if (filePropertiesDlg->horizontalClothCount() != m_document->property(QStringLiteral("horizontalClothCount")).toDouble()) {
0704             new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), QVariant(filePropertiesDlg->horizontalClothCount()), cmd);
0705         }
0706 
0707         if (filePropertiesDlg->clothCountLink() != m_document->property(QStringLiteral("clothCountLink")).toBool()) {
0708             new SetPropertyCommand(m_document, QStringLiteral("clothCountLink"), QVariant(filePropertiesDlg->clothCountLink()), cmd);
0709         }
0710 
0711         if (filePropertiesDlg->verticalClothCount() != m_document->property(QStringLiteral("verticalClothCount")).toDouble()) {
0712             new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), QVariant(filePropertiesDlg->verticalClothCount()), cmd);
0713         }
0714 
0715         if (filePropertiesDlg->clothCountUnits()
0716             != static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt())) {
0717             new SetPropertyCommand(m_document, QStringLiteral("clothCountUnits"), QVariant(filePropertiesDlg->clothCountUnits()), cmd);
0718         }
0719 
0720         if (filePropertiesDlg->title() != m_document->property(QStringLiteral("title")).toString()) {
0721             new SetPropertyCommand(m_document, QStringLiteral("title"), QVariant(filePropertiesDlg->title()), cmd);
0722         }
0723 
0724         if (filePropertiesDlg->author() != m_document->property(QStringLiteral("author")).toString()) {
0725             new SetPropertyCommand(m_document, QStringLiteral("author"), QVariant(filePropertiesDlg->author()), cmd);
0726         }
0727 
0728         if (filePropertiesDlg->copyright() != m_document->property(QStringLiteral("copyright")).toString()) {
0729             new SetPropertyCommand(m_document, QStringLiteral("copyright"), QVariant(filePropertiesDlg->copyright()), cmd);
0730         }
0731 
0732         if (filePropertiesDlg->fabric() != m_document->property(QStringLiteral("fabric")).toString()) {
0733             new SetPropertyCommand(m_document, QStringLiteral("fabric"), QVariant(filePropertiesDlg->fabric()), cmd);
0734         }
0735 
0736         if (filePropertiesDlg->fabricColor() != m_document->property(QStringLiteral("fabricColor")).value<QColor>()) {
0737             new SetPropertyCommand(m_document, QStringLiteral("fabricColor"), QVariant(filePropertiesDlg->fabricColor()), cmd);
0738         }
0739 
0740         if (filePropertiesDlg->instructions() != m_document->property(QStringLiteral("instructions")).toString()) {
0741             new SetPropertyCommand(m_document, QStringLiteral("instructions"), QVariant(filePropertiesDlg->instructions()), cmd);
0742         }
0743 
0744         if (filePropertiesDlg->flossScheme() != m_document->pattern()->palette().schemeName()) {
0745             new ChangeSchemeCommand(m_document, filePropertiesDlg->flossScheme(), cmd);
0746         }
0747 
0748         if (cmd->childCount()) {
0749             m_document->undoStack().push(cmd);
0750         } else {
0751             delete cmd;
0752         }
0753     }
0754 
0755     delete filePropertiesDlg;
0756 }
0757 
0758 void MainWindow::fileAddBackgroundImage()
0759 {
0760     QUrl url = QFileDialog::getOpenFileUrl(this,
0761                                            i18n("Background Image"),
0762                                            QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)),
0763                                            i18n("Images (*.bmp *.gif *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm *.svg)"));
0764 
0765     if (!url.isEmpty()) {
0766         QRect patternArea(0, 0, m_document->pattern()->stitches().width(), m_document->pattern()->stitches().height());
0767         QRect selectionArea = m_editor->selectionArea();
0768         QSharedPointer<BackgroundImage> backgroundImage(new BackgroundImage(url, (selectionArea.isValid() ? selectionArea : patternArea)));
0769 
0770         if (backgroundImage->isValid()) {
0771             m_document->undoStack().push(new AddBackgroundImageCommand(m_document, backgroundImage, this));
0772         }
0773     }
0774 }
0775 
0776 void MainWindow::fileRemoveBackgroundImage()
0777 {
0778     QAction *action = qobject_cast<QAction *>(sender());
0779     m_document->undoStack().push(new RemoveBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), this));
0780 }
0781 
0782 void MainWindow::fileClose()
0783 {
0784     if (queryClose()) {
0785         m_document->initialiseNew();
0786         setupActionsFromDocument();
0787         m_editor->readDocumentSettings();
0788         m_preview->readDocumentSettings();
0789     }
0790 
0791     close();
0792 }
0793 
0794 void MainWindow::fileQuit()
0795 {
0796     close();
0797 }
0798 
0799 void MainWindow::editUndo()
0800 {
0801     m_document->undoStack().undo();
0802 }
0803 
0804 void MainWindow::editRedo()
0805 {
0806     m_document->undoStack().redo();
0807 }
0808 
0809 void MainWindow::undoTextChanged(const QString &text)
0810 {
0811     actionCollection()->action(QStringLiteral("edit_undo"))->setText(i18n("Undo %1", text));
0812 }
0813 
0814 void MainWindow::redoTextChanged(const QString &text)
0815 {
0816     actionCollection()->action(QStringLiteral("edit_redo"))->setText(i18n("Redo %1", text));
0817 }
0818 
0819 void MainWindow::clipboardDataChanged()
0820 {
0821     actionCollection()
0822         ->action(QStringLiteral("edit_paste"))
0823         ->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QStringLiteral("application/kxstitch")));
0824 }
0825 
0826 void MainWindow::paletteManager()
0827 {
0828     QPointer<PaletteManagerDlg> paletteManagerDlg = new PaletteManagerDlg(this, m_document);
0829 
0830     if (paletteManagerDlg->exec()) {
0831         DocumentPalette palette = paletteManagerDlg->palette();
0832 
0833         if (palette != m_document->pattern()->palette()) {
0834             m_document->undoStack().push(new UpdateDocumentPaletteCommand(m_document, palette));
0835         }
0836     }
0837 
0838     delete paletteManagerDlg;
0839 }
0840 
0841 void MainWindow::paletteShowSymbols(bool show)
0842 {
0843     m_palette->showSymbols(show);
0844 }
0845 
0846 void MainWindow::paletteClearUnused()
0847 {
0848     QMap<int, FlossUsage> flossUsage = m_document->pattern()->stitches().flossUsage();
0849     QMapIterator<int, DocumentFloss *> flosses(m_document->pattern()->palette().flosses());
0850     ClearUnusedFlossesCommand *clearUnusedFlossesCommand = new ClearUnusedFlossesCommand(m_document);
0851 
0852     while (flosses.hasNext()) {
0853         flosses.next();
0854 
0855         if (flossUsage[flosses.key()].totalStitches() == 0) {
0856             new RemoveDocumentFlossCommand(m_document, flosses.key(), flosses.value(), clearUnusedFlossesCommand);
0857         }
0858     }
0859 
0860     if (clearUnusedFlossesCommand->childCount()) {
0861         m_document->undoStack().push(clearUnusedFlossesCommand);
0862     } else {
0863         delete clearUnusedFlossesCommand;
0864     }
0865 }
0866 
0867 void MainWindow::paletteCalibrateScheme()
0868 {
0869 }
0870 
0871 void MainWindow::paletteSwapColors(int originalIndex, int replacementIndex)
0872 {
0873     if (originalIndex != replacementIndex) {
0874         m_document->undoStack().push(new PaletteSwapColorCommand(m_document, originalIndex, replacementIndex));
0875     }
0876 }
0877 
0878 void MainWindow::paletteReplaceColor(int originalIndex, int replacementIndex)
0879 {
0880     if (originalIndex != replacementIndex) {
0881         m_document->undoStack().push(new PaletteReplaceColorCommand(m_document, originalIndex, replacementIndex));
0882     }
0883 }
0884 
0885 void MainWindow::viewFitBackgroundImage()
0886 {
0887     QAction *action = qobject_cast<QAction *>(sender());
0888     m_document->undoStack().push(new FitBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), m_editor->selectionArea()));
0889 }
0890 
0891 void MainWindow::paletteContextMenu(const QPoint &pos)
0892 {
0893     static_cast<QMenu *>(guiFactory()->container(QStringLiteral("PalettePopup"), this))->popup(qobject_cast<QWidget *>(sender())->mapToGlobal(pos));
0894 }
0895 
0896 void MainWindow::viewShowBackgroundImage()
0897 {
0898     QAction *action = qobject_cast<QAction *>(sender());
0899     m_document->undoStack().push(new ShowBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), action->isChecked()));
0900 }
0901 
0902 void MainWindow::patternExtend()
0903 {
0904     QPointer<ExtendPatternDlg> extendPatternDlg = new ExtendPatternDlg(this);
0905 
0906     if (extendPatternDlg->exec()) {
0907         int top = extendPatternDlg->top();
0908         int left = extendPatternDlg->left();
0909         int right = extendPatternDlg->right();
0910         int bottom = extendPatternDlg->bottom();
0911 
0912         if (top || left || right || bottom) {
0913             m_document->undoStack().push(new ExtendPatternCommand(m_document, top, left, right, bottom));
0914         }
0915     }
0916 
0917     delete extendPatternDlg;
0918 }
0919 
0920 void MainWindow::patternCentre()
0921 {
0922     m_document->undoStack().push(new CentrePatternCommand(m_document));
0923 }
0924 
0925 void MainWindow::patternCrop()
0926 {
0927     m_document->undoStack().push(new CropToPatternCommand(m_document));
0928 }
0929 
0930 void MainWindow::patternCropToSelection()
0931 {
0932     m_document->undoStack().push(new CropToSelectionCommand(m_document, m_editor->selectionArea()));
0933 }
0934 
0935 void MainWindow::insertColumns()
0936 {
0937     m_document->undoStack().push(new InsertColumnsCommand(m_document, m_editor->selectionArea()));
0938 }
0939 
0940 void MainWindow::insertRows()
0941 {
0942     m_document->undoStack().push(new InsertRowsCommand(m_document, m_editor->selectionArea()));
0943 }
0944 
0945 void MainWindow::preferences()
0946 {
0947     if (KConfigDialog::showDialog(QStringLiteral("preferences"))) {
0948         return;
0949     }
0950 
0951     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("preferences"), Configuration::self());
0952     dialog->setFaceType(KPageDialog::List);
0953 
0954     dialog->addPage(new EditorConfigPage(nullptr, QStringLiteral("EditorConfigPage")),
0955                     i18nc("The Editor config page", "Editor"),
0956                     QStringLiteral("preferences-desktop"));
0957     dialog->addPage(new PatternConfigPage(nullptr, QStringLiteral("PatternConfigPage")), i18n("Pattern"), QStringLiteral("ksnapshot"));
0958     PaletteConfigPage *paletteConfigPage = new PaletteConfigPage(nullptr, QStringLiteral("PaletteConfigPage"));
0959     dialog->addPage(paletteConfigPage, i18n("Palette"), QStringLiteral("preferences-desktop-color"));
0960     dialog->addPage(new ImportConfigPage(nullptr, QStringLiteral("ImportConfigPage")), i18n("Import"), QStringLiteral("insert-image"));
0961     dialog->addPage(new LibraryConfigPage(nullptr, QStringLiteral("LibraryConfigPage")), i18n("Library"), QStringLiteral("accessories-dictionary"));
0962     dialog->addPage(new PrinterConfigPage(nullptr, QStringLiteral("PrinterConfigPage")),
0963                     i18n("Printer Configuration"),
0964                     QStringLiteral("preferences-desktop-printer"));
0965 
0966     connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::settingsChanged);
0967 
0968     dialog->show();
0969 }
0970 
0971 void MainWindow::settingsChanged()
0972 {
0973     QList<QUndoCommand *> documentChanges;
0974     ConfigurationCommand *configurationCommand = new ConfigurationCommand(this);
0975 
0976     if (m_document->property(QStringLiteral("cellHorizontalGrouping")) != Configuration::editor_CellHorizontalGrouping()) {
0977         documentChanges.append(
0978             new SetPropertyCommand(m_document, QStringLiteral("cellHorizontalGrouping"), Configuration::editor_CellHorizontalGrouping(), configurationCommand));
0979     }
0980 
0981     if (m_document->property(QStringLiteral("cellVerticalGrouping")) != Configuration::editor_CellVerticalGrouping()) {
0982         documentChanges.append(
0983             new SetPropertyCommand(m_document, QStringLiteral("cellVerticalGrouping"), Configuration::editor_CellVerticalGrouping(), configurationCommand));
0984     }
0985 
0986     if (m_document->property(QStringLiteral("thickLineColor")) != Configuration::editor_ThickLineColor()) {
0987         documentChanges.append(
0988             new SetPropertyCommand(m_document, QStringLiteral("thickLineColor"), Configuration::editor_ThickLineColor(), configurationCommand));
0989     }
0990 
0991     if (m_document->property(QStringLiteral("thinLineColor")) != Configuration::editor_ThinLineColor()) {
0992         documentChanges.append(
0993             new SetPropertyCommand(m_document, QStringLiteral("thinLineColor"), Configuration::editor_ThinLineColor(), configurationCommand));
0994     }
0995 
0996     if (documentChanges.count()) {
0997         m_document->undoStack().push(configurationCommand);
0998     } else {
0999         delete configurationCommand;
1000     }
1001 
1002     loadSettings();
1003 }
1004 
1005 void MainWindow::loadSettings()
1006 {
1007     m_horizontalScale->setMinimumSize(0, Configuration::editor_HorizontalScaleHeight());
1008     m_verticalScale->setMinimumSize(Configuration::editor_VerticalScaleWidth(), 0);
1009     m_horizontalScale->setCellGrouping(Configuration::editor_CellHorizontalGrouping());
1010     m_verticalScale->setCellGrouping(Configuration::editor_CellVerticalGrouping());
1011 
1012     m_editor->loadSettings();
1013     m_preview->loadSettings();
1014     m_palette->loadSettings();
1015 
1016     KActionCollection *actions = actionCollection();
1017 
1018     actions->action(QStringLiteral("makesCopies"))->setChecked(Configuration::tool_MakesCopies());
1019 
1020     actions->action(QStringLiteral("colorHighlight"))->setChecked(Configuration::renderer_ColorHilight());
1021 
1022     actions->action(QStringLiteral("renderStitches"))->setChecked(Configuration::renderer_RenderStitches());
1023     actions->action(QStringLiteral("renderBackstitches"))->setChecked(Configuration::renderer_RenderBackstitches());
1024     actions->action(QStringLiteral("renderFrenchKnots"))->setChecked(Configuration::renderer_RenderFrenchKnots());
1025     actions->action(QStringLiteral("renderGrid"))->setChecked(Configuration::renderer_RenderGrid());
1026     actions->action(QStringLiteral("renderBackgroundImages"))->setChecked(Configuration::renderer_RenderBackgroundImages());
1027 
1028     switch (Configuration::editor_FormatScalesAs()) {
1029     case Configuration::EnumEditor_FormatScalesAs::Stitches:
1030         actions->action(QStringLiteral("formatScalesAsStitches"))->trigger();
1031         break;
1032 
1033     case Configuration::EnumEditor_FormatScalesAs::Inches:
1034         actions->action(QStringLiteral("formatScalesAsInches"))->trigger();
1035         break;
1036 
1037     case Configuration::EnumEditor_FormatScalesAs::Centimeters:
1038         actions->action(QStringLiteral("formatScalesAsCentimeters"))->trigger();
1039         break;
1040 
1041     default:
1042         break;
1043     }
1044 
1045     switch (Configuration::renderer_RenderStitchesAs()) {
1046     case Configuration::EnumRenderer_RenderStitchesAs::Stitches:
1047         actions->action(QStringLiteral("renderStitchesAsRegularStitches"))->trigger();
1048         break;
1049 
1050     case Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols:
1051         actions->action(QStringLiteral("renderStitchesAsBlackWhiteSymbols"))->trigger();
1052         break;
1053 
1054     case Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols:
1055         actions->action(QStringLiteral("renderStitchesAsColorSymbols"))->trigger();
1056         break;
1057 
1058     case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks:
1059         actions->action(QStringLiteral("renderStitchesAsColorBlocks"))->trigger();
1060         break;
1061 
1062     case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols:
1063         actions->action(QStringLiteral("renderStitchesAsColorBlocksSymbols"))->trigger();
1064         break;
1065 
1066     default:
1067         break;
1068     }
1069 
1070     switch (Configuration::renderer_RenderBackstitchesAs()) {
1071     case Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines:
1072         actions->action(QStringLiteral("renderBackstitchesAsColorLines"))->trigger();
1073         break;
1074 
1075     case Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols:
1076         actions->action(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"))->trigger();
1077         break;
1078 
1079     default:
1080         break;
1081     }
1082 
1083     switch (Configuration::renderer_RenderKnotsAs()) {
1084     case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks:
1085         actions->action(QStringLiteral("renderKnotsAsColorBlocks"))->trigger();
1086         break;
1087 
1088     case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols:
1089         actions->action(QStringLiteral("renderKnotsAsColorBlocksSymbols"))->trigger();
1090         break;
1091 
1092     case Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols:
1093         actions->action(QStringLiteral("renderKnotsAsColorSymbols"))->trigger();
1094         break;
1095 
1096     case Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols:
1097         actions->action(QStringLiteral("renderKnotsAsBlackWhiteSymbols"))->trigger();
1098         break;
1099 
1100     default:
1101         break;
1102     }
1103 
1104     actions->action(QStringLiteral("paletteShowSymbols"))->setChecked(Configuration::palette_ShowSymbols());
1105 }
1106 
1107 void MainWindow::documentModified(bool clean)
1108 {
1109     setCaption(m_document->url().fileName(), !clean);
1110 }
1111 
1112 void MainWindow::setupActions()
1113 {
1114     QAction *action;
1115     QActionGroup *actionGroup;
1116 
1117     KActionCollection *actions = actionCollection();
1118 
1119     // File menu
1120     KStandardAction::openNew(this, &MainWindow::fileNew, actions);
1121     KStandardAction::open(this, static_cast<void (MainWindow::*)()>(&MainWindow::fileOpen), actions);
1122     KStandardAction::openRecent(this, static_cast<void (MainWindow::*)(const QUrl &)>(&MainWindow::fileOpen), actions)
1123         ->loadEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
1124     KStandardAction::save(this, &MainWindow::fileSave, actions);
1125     KStandardAction::saveAs(this, &MainWindow::fileSaveAs, actions);
1126     KStandardAction::revert(this, &MainWindow::fileRevert, actions);
1127 
1128     action = new QAction(this);
1129     action->setText(i18n("Print Setup..."));
1130     connect(action, &QAction::triggered, this, &MainWindow::filePrintSetup);
1131     actions->addAction(QStringLiteral("filePrintSetup"), action);
1132 
1133     KStandardAction::print(this, &MainWindow::filePrint, actions);
1134 
1135     action = new QAction(this);
1136     action->setText(i18n("Import Image"));
1137     connect(action, &QAction::triggered, this, &MainWindow::fileImportImage);
1138     actions->addAction(QStringLiteral("fileImportImage"), action);
1139 
1140     action = new QAction(this);
1141     action->setText(i18n("File Properties"));
1142     connect(action, &QAction::triggered, this, &MainWindow::fileProperties);
1143     actions->addAction(QStringLiteral("fileProperties"), action);
1144 
1145     action = new QAction(this);
1146     action->setText(i18n("Add Background Image..."));
1147     connect(action, &QAction::triggered, this, &MainWindow::fileAddBackgroundImage);
1148     actions->addAction(QStringLiteral("fileAddBackgroundImage"), action);
1149 
1150     KStandardAction::close(this, &MainWindow::fileClose, actions);
1151     KStandardAction::quit(this, &MainWindow::fileQuit, actions);
1152 
1153     // Edit menu
1154     KStandardAction::undo(this, &MainWindow::editUndo, actions);
1155     KStandardAction::redo(this, &MainWindow::editRedo, actions);
1156     KStandardAction::cut(m_editor, &Editor::editCut, actions);
1157     actions->action(QStringLiteral("edit_cut"))->setEnabled(false);
1158     KStandardAction::copy(m_editor, &Editor::editCopy, actions);
1159     actions->action(QStringLiteral("edit_copy"))->setEnabled(false);
1160     KStandardAction::paste(m_editor, &Editor::editPaste, actions);
1161 
1162     action = new QAction(this);
1163     action->setText(i18n("Mirror/Rotate makes copies"));
1164     action->setCheckable(true);
1165     connect(action, &QAction::triggered, m_editor, &Editor::setMakesCopies);
1166     actions->addAction(QStringLiteral("makesCopies"), action);
1167 
1168     action = new QAction(this);
1169     action->setText(i18n("Horizontally"));
1170     action->setData(Qt::Horizontal);
1171     connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection);
1172     action->setEnabled(false);
1173     actions->addAction(QStringLiteral("mirrorHorizontal"), action);
1174 
1175     action = new QAction(this);
1176     action->setText(i18n("Vertically"));
1177     action->setData(Qt::Vertical);
1178     connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection);
1179     action->setEnabled(false);
1180     actions->addAction(QStringLiteral("mirrorVertical"), action);
1181 
1182     action = new QAction(this);
1183     action->setText(i18n("90 Degrees"));
1184     action->setData(StitchData::Rotate90);
1185     connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1186     action->setEnabled(false);
1187     actions->addAction(QStringLiteral("rotate90"), action);
1188 
1189     action = new QAction(this);
1190     action->setText(i18n("180 Degrees"));
1191     action->setData(StitchData::Rotate180);
1192     connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1193     action->setEnabled(false);
1194     actions->addAction(QStringLiteral("rotate180"), action);
1195 
1196     action = new QAction(this);
1197     action->setText(i18n("270 Degrees"));
1198     action->setData(StitchData::Rotate270);
1199     connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1200     action->setEnabled(false);
1201     actions->addAction(QStringLiteral("rotate270"), action);
1202 
1203     // Selection mask sub menu
1204     action = new QAction(this);
1205     action->setText(i18n("Stitch Mask"));
1206     action->setCheckable(true);
1207     connect(action, &QAction::triggered, m_editor, &Editor::setMaskStitch);
1208     actions->addAction(QStringLiteral("maskStitch"), action);
1209 
1210     action = new QAction(this);
1211     action->setText(i18n("Color Mask"));
1212     action->setCheckable(true);
1213     connect(action, &QAction::triggered, m_editor, &Editor::setMaskColor);
1214     actions->addAction(QStringLiteral("maskColor"), action);
1215 
1216     action = new QAction(this);
1217     action->setText(i18n("Exclude Backstitches"));
1218     action->setCheckable(true);
1219     connect(action, &QAction::triggered, m_editor, &Editor::setMaskBackstitch);
1220     actions->addAction(QStringLiteral("maskBackstitch"), action);
1221 
1222     action = new QAction(this);
1223     action->setText(i18n("Exclude Knots"));
1224     action->setCheckable(true);
1225     connect(action, &QAction::triggered, m_editor, &Editor::setMaskKnot);
1226     actions->addAction(QStringLiteral("maskKnot"), action);
1227 
1228     // View menu
1229     KStandardAction::zoomIn(m_editor, &Editor::zoomIn, actions);
1230     KStandardAction::zoomOut(m_editor, &Editor::zoomOut, actions);
1231     KStandardAction::actualSize(m_editor, &Editor::actualSize, actions);
1232     action = KStandardAction::fitToPage(m_editor, &Editor::fitToPage, actions);
1233     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best")));
1234     action = KStandardAction::fitToWidth(m_editor, &Editor::fitToWidth, actions);
1235     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-width")));
1236     action = KStandardAction::fitToHeight(m_editor, &Editor::fitToHeight, actions);
1237     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-height")));
1238 
1239     // Entries for Show/Hide Preview and Palette dock windows are added dynamically
1240     // Entries for Show/Hide and Remove background images are added dynamically
1241 
1242     // Stitches Menu
1243     actionGroup = new QActionGroup(this);
1244     actionGroup->setExclusive(true);
1245 
1246     action = new QAction(this);
1247     action->setText(i18n("Quarter Stitch"));
1248     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-quarter-stitch")));
1249     action->setCheckable(true);
1250     connect(action, &QAction::triggered, m_editor, [=]() {
1251         m_editor->selectStitch(Editor::StitchQuarter);
1252     });
1253     actions->addAction(QStringLiteral("stitchQuarter"), action);
1254     actionGroup->addAction(action);
1255 
1256     action = new QAction(this);
1257     action->setText(i18n("Half Stitch"));
1258     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-half-stitch")));
1259     action->setCheckable(true);
1260     connect(action, &QAction::triggered, m_editor, [=]() {
1261         m_editor->selectStitch(Editor::StitchHalf);
1262     });
1263     actions->addAction(QStringLiteral("stitchHalf"), action);
1264     actionGroup->addAction(action);
1265 
1266     action = new QAction(this);
1267     action->setText(i18n("3 Quarter Stitch"));
1268     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-3quarter-stitch")));
1269     action->setCheckable(true);
1270     connect(action, &QAction::triggered, m_editor, [=]() {
1271         m_editor->selectStitch(Editor::Stitch3Quarter);
1272     });
1273     actions->addAction(QStringLiteral("stitch3Quarter"), action);
1274     actionGroup->addAction(action);
1275 
1276     action = new QAction(this);
1277     action->setText(i18n("Full Stitch"));
1278     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-full-stitch")));
1279     action->setCheckable(true);
1280     connect(action, &QAction::triggered, m_editor, [=]() {
1281         m_editor->selectStitch(Editor::StitchFull);
1282     });
1283     actions->addAction(QStringLiteral("stitchFull"), action);
1284     actionGroup->addAction(action);
1285 
1286     action = new QAction(this);
1287     action->setText(i18n("Small Half Stitch"));
1288     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-small-half-stitch")));
1289     action->setCheckable(true);
1290     connect(action, &QAction::triggered, m_editor, [=]() {
1291         m_editor->selectStitch(Editor::StitchSmallHalf);
1292     });
1293     actions->addAction(QStringLiteral("stitchSmallHalf"), action);
1294     actionGroup->addAction(action);
1295 
1296     action = new QAction(this);
1297     action->setText(i18n("Small Full Stitch"));
1298     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-small-full-stitch")));
1299     action->setCheckable(true);
1300     connect(action, &QAction::triggered, m_editor, [=]() {
1301         m_editor->selectStitch(Editor::StitchSmallFull);
1302     });
1303     actions->addAction(QStringLiteral("stitchSmallFull"), action);
1304     actionGroup->addAction(action);
1305 
1306     action = new QAction(this);
1307     action->setText(i18n("French Knot"));
1308     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-frenchknot")));
1309     action->setCheckable(true);
1310     connect(action, &QAction::triggered, m_editor, [=]() {
1311         m_editor->selectStitch(Editor::StitchFrenchKnot);
1312     });
1313     actions->addAction(QStringLiteral("stitchFrenchKnot"), action);
1314     actionGroup->addAction(action);
1315 
1316     // Tools Menu
1317     actionGroup = new QActionGroup(this);
1318     actionGroup->setExclusive(true);
1319 
1320     action = new QAction(this);
1321     action->setText(i18n("Paint"));
1322     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-brush")));
1323     action->setCheckable(true);
1324     connect(action, &QAction::triggered, m_editor, [=]() {
1325         m_editor->selectTool(Editor::ToolPaint);
1326     });
1327     actions->addAction(QStringLiteral("toolPaint"), action);
1328     actionGroup->addAction(action);
1329 
1330     action = new QAction(this);
1331     action->setText(i18n("Draw"));
1332     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-freehand")));
1333     action->setCheckable(true);
1334     connect(action, &QAction::triggered, m_editor, [=]() {
1335         m_editor->selectTool(Editor::ToolDraw);
1336     });
1337     actions->addAction(QStringLiteral("toolDraw"), action);
1338     actionGroup->addAction(action);
1339 
1340     action = new QAction(this);
1341     action->setText(i18n("Erase"));
1342     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-eraser")));
1343     action->setCheckable(true);
1344     connect(action, &QAction::triggered, m_editor, [=]() {
1345         m_editor->selectTool(Editor::ToolErase);
1346     });
1347     actions->addAction(QStringLiteral("toolErase"), action);
1348     actionGroup->addAction(action);
1349 
1350     action = new QAction(this);
1351     action->setText(i18n("Draw Rectangle"));
1352     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-rectangle")));
1353     action->setCheckable(true);
1354     connect(action, &QAction::triggered, m_editor, [=]() {
1355         m_editor->selectTool(Editor::ToolRectangle);
1356     });
1357     actions->addAction(QStringLiteral("toolRectangle"), action);
1358     actionGroup->addAction(action);
1359 
1360     action = new QAction(this);
1361     action->setText(i18n("Fill Rectangle"));
1362     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-draw-rectangle-filled")));
1363     action->setCheckable(true);
1364     connect(action, &QAction::triggered, m_editor, [=]() {
1365         m_editor->selectTool(Editor::ToolFillRectangle);
1366     });
1367     actions->addAction(QStringLiteral("toolFillRectangle"), action);
1368     actionGroup->addAction(action);
1369 
1370     action = new QAction(this);
1371     action->setText(i18n("Draw Ellipse"));
1372     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-ellipse")));
1373     action->setCheckable(true);
1374     connect(action, &QAction::triggered, m_editor, [=]() {
1375         m_editor->selectTool(Editor::ToolEllipse);
1376     });
1377     actions->addAction(QStringLiteral("toolEllipse"), action);
1378     actionGroup->addAction(action);
1379 
1380     action = new QAction(this);
1381     action->setText(i18n("Fill Ellipse"));
1382     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-draw-ellipse-filled")));
1383     action->setCheckable(true);
1384     connect(action, &QAction::triggered, m_editor, [=]() {
1385         m_editor->selectTool(Editor::ToolFillEllipse);
1386     });
1387     actions->addAction(QStringLiteral("toolFillEllipse"), action);
1388     actionGroup->addAction(action);
1389 
1390     action = new QAction(this);
1391     action->setText(i18n("Fill Polygon"));
1392     action->setIcon(QIcon::fromTheme(QStringLiteral("tool_polygon")));
1393     action->setCheckable(true);
1394     connect(action, &QAction::triggered, m_editor, [=]() {
1395         m_editor->selectTool(Editor::ToolFillPolygon);
1396     });
1397     actions->addAction(QStringLiteral("toolFillPolygon"), action);
1398     actionGroup->addAction(action);
1399 
1400     action = new QAction(this);
1401     action->setText(i18n("Text"));
1402     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-text")));
1403     action->setCheckable(true);
1404     connect(action, &QAction::triggered, m_editor, [=]() {
1405         m_editor->selectTool(Editor::ToolText);
1406     });
1407     actions->addAction(QStringLiteral("toolText"), action);
1408     actionGroup->addAction(action);
1409 
1410     action = new QAction(this);
1411     action->setText(i18n("Alphabet"));
1412     action->setIcon(QIcon::fromTheme(QStringLiteral("text-field")));
1413     action->setCheckable(true);
1414     connect(action, &QAction::triggered, m_editor, [=]() {
1415         m_editor->selectTool(Editor::ToolAlphabet);
1416     });
1417     actions->addAction(QStringLiteral("toolAlphabet"), action);
1418     actionGroup->addAction(action);
1419 
1420     action = new QAction(this);
1421     action->setText(i18nc("Select an area of the pattern", "Select"));
1422     action->setIcon(QIcon::fromTheme(QStringLiteral("select-rectangular")));
1423     action->setCheckable(true);
1424     connect(action, &QAction::triggered, m_editor, [=]() {
1425         m_editor->selectTool(Editor::ToolSelect);
1426     });
1427     actions->addAction(QStringLiteral("toolSelectRectangle"), action);
1428     actionGroup->addAction(action);
1429 
1430     action = new QAction(this);
1431     action->setText(i18n("Backstitch"));
1432     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-backstitch")));
1433     action->setCheckable(true);
1434     connect(action, &QAction::triggered, m_editor, [=]() {
1435         m_editor->selectTool(Editor::ToolBackstitch);
1436     });
1437     actions->addAction(QStringLiteral("toolBackstitch"), action);
1438     actionGroup->addAction(action);
1439 
1440     action = new QAction(this);
1441     action->setText(i18n("Color Picker"));
1442     action->setIcon(QIcon::fromTheme(QStringLiteral("color-picker")));
1443     action->setCheckable(true);
1444     connect(action, &QAction::triggered, m_editor, [=]() {
1445         m_editor->selectTool(Editor::ToolColorPicker);
1446     });
1447     actions->addAction(QStringLiteral("toolColorPicker"), action);
1448     actionGroup->addAction(action);
1449 
1450     // Palette Menu
1451     action = new QAction(this);
1452     action->setText(i18n("Palette Manager..."));
1453     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-color-add")));
1454     connect(action, &QAction::triggered, this, &MainWindow::paletteManager);
1455     actions->addAction(QStringLiteral("paletteManager"), action);
1456 
1457     action = new QAction(this);
1458     action->setText(i18n("Show Symbols"));
1459     action->setCheckable(true);
1460     connect(action, &QAction::toggled, this, &MainWindow::paletteShowSymbols);
1461     actions->addAction(QStringLiteral("paletteShowSymbols"), action);
1462 
1463     action = new QAction(this);
1464     action->setText(i18n("Clear Unused"));
1465     connect(action, &QAction::triggered, this, &MainWindow::paletteClearUnused);
1466     actions->addAction(QStringLiteral("paletteClearUnused"), action);
1467 
1468     action = new QAction(this);
1469     action->setText(i18n("Calibrate Scheme..."));
1470     connect(action, &QAction::triggered, this, &MainWindow::paletteCalibrateScheme);
1471     actions->addAction(QStringLiteral("paletteCalibrateScheme"), action);
1472 
1473     action = new QAction(this);
1474     action->setText(i18n("Swap Colors"));
1475     connect(action, &QAction::triggered, m_palette, static_cast<void (Palette::*)()>(&Palette::swapColors));
1476     actions->addAction(QStringLiteral("paletteSwapColors"), action);
1477 
1478     action = new QAction(this);
1479     action->setText(i18n("Replace Colors"));
1480     connect(action, &QAction::triggered, m_palette, static_cast<void (Palette::*)()>(&Palette::replaceColor));
1481     actions->addAction(QStringLiteral("paletteReplaceColor"), action);
1482 
1483     // Pattern Menu
1484     action = new QAction(this);
1485     action->setText(i18n("Extend Pattern..."));
1486     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-extend-pattern")));
1487     connect(action, &QAction::triggered, this, &MainWindow::patternExtend);
1488     actions->addAction(QStringLiteral("patternExtend"), action);
1489 
1490     action = new QAction(this);
1491     action->setText(i18n("Center Pattern"));
1492     action->setIcon(QIcon::fromTheme(QStringLiteral("kxs-center-pattern")));
1493     connect(action, &QAction::triggered, this, &MainWindow::patternCentre);
1494     actions->addAction(QStringLiteral("patternCentre"), action);
1495 
1496     action = new QAction(this);
1497     action->setText(i18n("Crop Canvas to Pattern"));
1498     connect(action, &QAction::triggered, this, &MainWindow::patternCrop);
1499     actions->addAction(QStringLiteral("patternCrop"), action);
1500 
1501     action = new QAction(this);
1502     action->setText(i18n("Crop Canvas to Selection"));
1503     action->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop")));
1504     connect(action, &QAction::triggered, this, &MainWindow::patternCropToSelection);
1505     action->setEnabled(false);
1506     actions->addAction(QStringLiteral("patternCropToSelection"), action);
1507 
1508     action = new QAction(this);
1509     action->setText(i18n("Insert Rows"));
1510     connect(action, &QAction::triggered, this, &MainWindow::insertRows);
1511     action->setEnabled(false);
1512     actions->addAction(QStringLiteral("insertRows"), action);
1513 
1514     action = new QAction(this);
1515     action->setText(i18n("Insert Columns"));
1516     connect(action, &QAction::triggered, this, &MainWindow::insertColumns);
1517     action->setEnabled(false);
1518     actions->addAction(QStringLiteral("insertColumns"), action);
1519 
1520     // Library Menu
1521     action = new QAction(this);
1522     action->setText(i18n("Library Manager..."));
1523     connect(action, &QAction::triggered, m_editor, &Editor::libraryManager);
1524     actions->addAction(QStringLiteral("libraryManager"), action);
1525 
1526     // Settings Menu
1527     KStandardAction::preferences(this, &MainWindow::preferences, actions);
1528     // formatScalesAs
1529     actionGroup = new QActionGroup(this);
1530     actionGroup->setExclusive(true);
1531 
1532     action = new QAction(this);
1533     action->setText(i18n("Stitches"));
1534     action->setCheckable(true);
1535     connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsStitches);
1536     actions->addAction(QStringLiteral("formatScalesAsStitches"), action);
1537     actionGroup->addAction(action);
1538 
1539     action = new QAction(this);
1540     action->setText(i18n("Centimeters"));
1541     action->setCheckable(true);
1542     connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsCentimeters);
1543     actions->addAction(QStringLiteral("formatScalesAsCentimeters"), action);
1544     actionGroup->addAction(action);
1545 
1546     action = new QAction(this);
1547     action->setText(i18n("Inches"));
1548     action->setCheckable(true);
1549     connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsInches);
1550     actions->addAction(QStringLiteral("formatScalesAsInches"), action);
1551     actionGroup->addAction(action);
1552 
1553     // ShowStitchesAs
1554     actionGroup = new QActionGroup(this);
1555     actionGroup->setExclusive(true);
1556 
1557     action = new QAction(this);
1558     action->setText(i18n("Regular Stitches"));
1559     action->setCheckable(true);
1560     action->setChecked(true);
1561     connect(action, &QAction::triggered, m_editor, [=]() {
1562         m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::Stitches);
1563     });
1564     actions->addAction(QStringLiteral("renderStitchesAsRegularStitches"), action);
1565     actionGroup->addAction(action);
1566 
1567     action = new QAction(this);
1568     action->setText(i18n("Black & White Symbols"));
1569     action->setCheckable(true);
1570     connect(action, &QAction::triggered, m_editor, [=]() {
1571         m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols);
1572     });
1573     actions->addAction(QStringLiteral("renderStitchesAsBlackWhiteSymbols"), action);
1574     actionGroup->addAction(action);
1575 
1576     action = new QAction(this);
1577     action->setText(i18n("Color Symbols"));
1578     action->setCheckable(true);
1579     connect(action, &QAction::triggered, m_editor, [=]() {
1580         m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols);
1581     });
1582     actions->addAction(QStringLiteral("renderStitchesAsColorSymbols"), action);
1583     actionGroup->addAction(action);
1584 
1585     action = new QAction(this);
1586     action->setText(i18n("Color Blocks"));
1587     action->setCheckable(true);
1588     connect(action, &QAction::triggered, m_editor, [=]() {
1589         m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks);
1590     });
1591     actions->addAction(QStringLiteral("renderStitchesAsColorBlocks"), action);
1592     actionGroup->addAction(action);
1593 
1594     action = new QAction(this);
1595     action->setText(i18n("Color Blocks & Symbols"));
1596     action->setCheckable(true);
1597     connect(action, &QAction::triggered, m_editor, [=]() {
1598         m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols);
1599     });
1600     actions->addAction(QStringLiteral("renderStitchesAsColorBlocksSymbols"), action);
1601     actionGroup->addAction(action);
1602 
1603     // ShowBackstitchesAs
1604     actionGroup = new QActionGroup(this);
1605     actionGroup->setExclusive(true);
1606 
1607     action = new QAction(this);
1608     action->setText(i18n("Color Lines"));
1609     action->setCheckable(true);
1610     action->setChecked(true);
1611     connect(action, &QAction::triggered, m_editor, [=]() {
1612         m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines);
1613     });
1614     actions->addAction(QStringLiteral("renderBackstitchesAsColorLines"), action);
1615     actionGroup->addAction(action);
1616 
1617     action = new QAction(this);
1618     action->setText(i18n("Black & White Symbols"));
1619     action->setCheckable(true);
1620     connect(action, &QAction::triggered, m_editor, [=]() {
1621         m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols);
1622     });
1623     actions->addAction(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"), action);
1624     actionGroup->addAction(action);
1625 
1626     // ShowKnotsAs
1627     actionGroup = new QActionGroup(this);
1628     actionGroup->setExclusive(true);
1629 
1630     action = new QAction(this);
1631     action->setText(i18n("Color Blocks"));
1632     action->setCheckable(true);
1633     action->setChecked(true);
1634     connect(action, &QAction::triggered, m_editor, [=]() {
1635         m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks);
1636     });
1637     actions->addAction(QStringLiteral("renderKnotsAsColorBlocks"), action);
1638     actionGroup->addAction(action);
1639 
1640     action = new QAction(this);
1641     action->setText(i18n("Color Blocks & Symbols"));
1642     action->setCheckable(true);
1643     connect(action, &QAction::triggered, m_editor, [=]() {
1644         m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols);
1645     });
1646     actions->addAction(QStringLiteral("renderKnotsAsColorBlocksSymbols"), action);
1647     actionGroup->addAction(action);
1648 
1649     action = new QAction(this);
1650     action->setText(i18n("Color Symbols"));
1651     action->setCheckable(true);
1652     connect(action, &QAction::triggered, m_editor, [=]() {
1653         m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols);
1654     });
1655     actions->addAction(QStringLiteral("renderKnotsAsColorSymbols"), action);
1656     actionGroup->addAction(action);
1657 
1658     action = new QAction(this);
1659     action->setText(i18n("Black & White Symbols"));
1660     action->setCheckable(true);
1661     connect(action, &QAction::triggered, m_editor, [=]() {
1662         m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols);
1663     });
1664     actions->addAction(QStringLiteral("renderKnotsAsBlackWhiteSymbols"), action);
1665     actionGroup->addAction(action);
1666 
1667     action = new QAction(this);
1668     action->setText(i18n("Color Highlight"));
1669     action->setCheckable(true);
1670     connect(action, &QAction::toggled, m_editor, &Editor::colorHighlight);
1671     actions->addAction(QStringLiteral("colorHighlight"), action);
1672 
1673     action = new QAction(this);
1674     action->setText(i18n("Show Stitches"));
1675     action->setCheckable(true);
1676     connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderStitches));
1677     actions->addAction(QStringLiteral("renderStitches"), action);
1678 
1679     action = new QAction(this);
1680     action->setText(i18n("Show Backstitches"));
1681     action->setCheckable(true);
1682     connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderBackstitches));
1683     actions->addAction(QStringLiteral("renderBackstitches"), action);
1684 
1685     action = new QAction(this);
1686     action->setText(i18n("Show French Knots"));
1687     action->setCheckable(true);
1688     connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderFrenchKnots));
1689     actions->addAction(QStringLiteral("renderFrenchKnots"), action);
1690 
1691     action = new QAction(this);
1692     action->setText(i18n("Show Grid"));
1693     action->setCheckable(true);
1694     connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderGrid));
1695     actions->addAction(QStringLiteral("renderGrid"), action);
1696 
1697     action = new QAction(this);
1698     action->setText(i18n("Show Background Images"));
1699     action->setCheckable(true);
1700     connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderBackgroundImages));
1701     actions->addAction(QStringLiteral("renderBackgroundImages"), action);
1702 
1703     m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches")));
1704     m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters")));
1705     m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches")));
1706 
1707     m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches")));
1708     m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters")));
1709     m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches")));
1710 
1711     setupGUI(KXmlGuiWindow::Default, QStringLiteral("kxstitchui.rc"));
1712 }
1713 
1714 void MainWindow::updateBackgroundImageActionLists()
1715 {
1716     auto backgroundImages = m_document->backgroundImages().backgroundImages();
1717 
1718     unplugActionList(QStringLiteral("removeBackgroundImageActions"));
1719     unplugActionList(QStringLiteral("fitBackgroundImageActions"));
1720     unplugActionList(QStringLiteral("showBackgroundImageActions"));
1721 
1722     QList<QAction *> removeBackgroundImageActions;
1723     QList<QAction *> fitBackgroundImageActions;
1724     QList<QAction *> showBackgroundImageActions;
1725 
1726     while (backgroundImages.hasNext()) {
1727         QSharedPointer<BackgroundImage> backgroundImage = backgroundImages.next();
1728 
1729         QAction *action = new QAction(backgroundImage->url().fileName(), this);
1730         action->setData(QVariant::fromValue(backgroundImage));
1731         action->setIcon(backgroundImage->icon());
1732         connect(action, &QAction::triggered, this, &MainWindow::fileRemoveBackgroundImage);
1733         removeBackgroundImageActions.append(action);
1734 
1735         action = new QAction(backgroundImage->url().fileName(), this);
1736         action->setData(QVariant::fromValue(backgroundImage));
1737         action->setIcon(backgroundImage->icon());
1738         connect(action, &QAction::triggered, this, &MainWindow::viewFitBackgroundImage);
1739         fitBackgroundImageActions.append(action);
1740 
1741         action = new QAction(backgroundImage->url().fileName(), this);
1742         action->setData(QVariant::fromValue(backgroundImage));
1743         action->setIcon(backgroundImage->icon());
1744         action->setCheckable(true);
1745         action->setChecked(backgroundImage->isVisible());
1746         connect(action, &QAction::triggered, this, &MainWindow::viewShowBackgroundImage);
1747         showBackgroundImageActions.append(action);
1748     }
1749 
1750     plugActionList(QStringLiteral("removeBackgroundImageActions"), removeBackgroundImageActions);
1751     plugActionList(QStringLiteral("fitBackgroundImageActions"), fitBackgroundImageActions);
1752     plugActionList(QStringLiteral("showBackgroundImageActions"), showBackgroundImageActions);
1753 }
1754 
1755 void MainWindow::setupDockWindows()
1756 {
1757     QDockWidget *dock = new QDockWidget(i18n("Preview"), this);
1758     dock->setObjectName(QStringLiteral("PreviewDock#"));
1759     dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1760     QScrollArea *scrollArea = new QScrollArea();
1761     m_preview = new Preview(scrollArea);
1762     scrollArea->setWidget(m_preview);
1763     scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1764     scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1765     scrollArea->setMinimumSize(std::min(300, m_preview->width()), std::min(400, m_preview->height()));
1766     dock->setWidget(scrollArea);
1767     addDockWidget(Qt::LeftDockWidgetArea, dock);
1768     actionCollection()->addAction(QStringLiteral("showPreviewDockWidget"), dock->toggleViewAction());
1769 
1770     dock = new QDockWidget(i18n("Palette"), this);
1771     dock->setObjectName(QStringLiteral("PaletteDock#"));
1772     dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1773     m_palette = new Palette(this);
1774     m_palette->setContextMenuPolicy(Qt::CustomContextMenu);
1775     dock->setWidget(m_palette);
1776     addDockWidget(Qt::LeftDockWidgetArea, dock);
1777     actionCollection()->addAction(QStringLiteral("showPaletteDockWidget"), dock->toggleViewAction());
1778 
1779     dock = new QDockWidget(i18n("History"), this);
1780     dock->setObjectName(QStringLiteral("HistoryDock#"));
1781     dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1782     m_history = new QUndoView(this);
1783     dock->setWidget(m_history);
1784     addDockWidget(Qt::LeftDockWidgetArea, dock);
1785     actionCollection()->addAction(QStringLiteral("showHistoryDockWidget"), dock->toggleViewAction());
1786 
1787     dock = new QDockWidget(i18n("Imported Image"), this);
1788     dock->setObjectName(QStringLiteral("ImportedImage#"));
1789     dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1790     m_imageLabel = new ScaledPixmapLabel(this);
1791     m_imageLabel->setScaledContents(false);
1792     dock->setWidget(m_imageLabel);
1793     addDockWidget(Qt::LeftDockWidgetArea, dock);
1794     actionCollection()->addAction(QStringLiteral("showImportedDockWidget"), dock->toggleViewAction());
1795 }
1796 
1797 #include "moc_MainWindow.cpp"