diff --git a/src/ImportImageDlg.cpp b/src/ImportImageDlg.cpp index e6396c6..340ff1d 100644 --- a/src/ImportImageDlg.cpp +++ b/src/ImportImageDlg.cpp @@ -1,596 +1,612 @@ /* * Copyright (C) 2010-2015 by Stephen Allewell * steve.allewell@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include "ImportImageDlg.h" #include #include #include #include #include #include "configuration.h" #include "FlossScheme.h" #include "SchemeManager.h" #include "SymbolManager.h" #include "SymbolLibrary.h" const uchar alphaData[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0xf1, 0x9e, 0x7e, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0x08, 0x7c, 0x08, 0x64, 0x88, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x31, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x4d, 0xc1, 0xc1, 0x0d, 0xc0, 0x20, 0x0c, 0x04, 0x30, 0x53, 0xb1, 0x51, 0xa6, 0x65, 0x2a, 0xc4, 0x3e, 0x28, 0xd7, 0x4f, 0x1f, 0xb5, 0xc7, 0x5a, 0x2b, 0x70, 0xce, 0xd1, 0xdd, 0x1e, 0x9f, 0x7b, 0xaf, 0x24, 0xe6, 0xde, 0x1b, 0x54, 0x15, 0x98, 0x49, 0xfc, 0xbd, 0x57, 0x37, 0x14, 0x37, 0x6c, 0x77, 0x53, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; ImportImageDlg::ImportImageDlg(QWidget *parent, const Magick::Image &originalImage) : QDialog(parent), m_alphaSelect(nullptr), m_originalImage(originalImage) { ui.setupUi(this); m_crop = QRect(0, 0, m_originalImage.columns(), m_originalImage.rows()); m_originalSize = QSize(m_crop.width(), m_crop.height()); updateWindowTitle(); // disable some signals whilst the dialog is setup ui.FlossScheme->blockSignals(true); ui.UseMaximumColors->blockSignals(true); ui.MaximumColors->blockSignals(true); ui.IgnoreColor->blockSignals(true); ui.ColorButton->blockSignals(true); ui.HorizontalClothCount->blockSignals(true); ui.VerticalClothCount->blockSignals(true); ui.ClothCountLink->blockSignals(true); ui.PatternScale->blockSignals(true); ui.CropEnabled->blockSignals(true); ui.CropReset->blockSignals(true); ui.UseFractionals->blockSignals(true); ui.FlossScheme->addItems(SchemeManager::schemes()); ui.CropReset->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); resetImportParameters(); createImageMap(); renderPixmap(); // unblock signals now the dialog is setup ui.FlossScheme->blockSignals(false); ui.UseMaximumColors->blockSignals(false); ui.MaximumColors->blockSignals(false); ui.IgnoreColor->blockSignals(false); ui.ColorButton->blockSignals(false); ui.HorizontalClothCount->blockSignals(false); ui.VerticalClothCount->blockSignals(false); ui.ClothCountLink->blockSignals(false); ui.PatternScale->blockSignals(false); ui.CropEnabled->blockSignals(false); ui.CropReset->blockSignals(false); ui.UseFractionals->blockSignals(false); connect(ui.ImagePreview, &ScaledPixmapLabel::imageCropped, this, &ImportImageDlg::imageCropped); } void ImportImageDlg::updateWindowTitle() { QString caption = i18n("Import Image - Image Size %1 x %2 pixels", m_crop.width(), m_crop.height()); setWindowTitle(caption); } Magick::Image ImportImageDlg::convertedImage() const { return m_convertedImage; } bool ImportImageDlg::ignoreColor() const { return ui.IgnoreColor->isChecked(); } Magick::Color ImportImageDlg::ignoreColorValue() const { return m_ignoreColorValue; } QString ImportImageDlg::flossScheme() const { return ui.FlossScheme->currentText(); } double ImportImageDlg::horizontalClothCount() const { return ui.HorizontalClothCount->value(); } double ImportImageDlg::verticalClothCount() const { return ui.VerticalClothCount->value(); } bool ImportImageDlg::useFractionals() const { return ui.UseFractionals->isChecked(); } QRect ImportImageDlg::croppedArea() const { return m_crop; } void ImportImageDlg::hideEvent(QHideEvent *event) { KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("DialogSizes")).writeEntry(QStringLiteral("ImportImageDlg"), size()); QDialog::hideEvent(event); } void ImportImageDlg::showEvent(QShowEvent *event) { QDialog::showEvent(event); if (KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("DialogSizes")).hasKey(QStringLiteral("ImportImageDlg"))) { resize(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("DialogSizes")).readEntry(QStringLiteral("ImportImageDlg"), QSize())); } } void ImportImageDlg::on_FlossScheme_currentIndexChanged(const QString&) { createImageMap(); renderPixmap(); } void ImportImageDlg::on_UseMaximumColors_toggled(bool checked) { Q_UNUSED(checked); killTimer(m_timer); m_timer = startTimer(500); } void ImportImageDlg::on_MaximumColors_valueChanged(int) { killTimer(m_timer); m_timer = startTimer(500); } void ImportImageDlg::on_IgnoreColor_toggled(bool checked) { Q_UNUSED(checked); delete m_alphaSelect; m_alphaSelect = nullptr; renderPixmap(); } void ImportImageDlg::on_ColorButton_clicked(bool) { m_alphaSelect = new AlphaSelect(ui.ImagePreview); connect(m_alphaSelect, &AlphaSelect::clicked, this, &ImportImageDlg::selectColor); m_alphaSelect->show(); } void ImportImageDlg::on_HorizontalClothCount_valueChanged(double horizontalClothCount) { clothCountChanged(horizontalClothCount, ui.VerticalClothCount->value()); } void ImportImageDlg::on_VerticalClothCount_valueChanged(double verticalClothCount) { clothCountChanged(ui.HorizontalClothCount->value(), verticalClothCount); } void ImportImageDlg::clothCountChanged(double horizontalClothCount, double verticalClothCount) { double preferredSizeWidth = m_preferredSize.width(); double preferredSizeHeight = m_preferredSize.height(); QString suffix; switch (Configuration::document_UnitsFormat()) { case Configuration::EnumDocument_UnitsFormat::Stitches: suffix = QString(i18n("Stitches")); break; case Configuration::EnumDocument_UnitsFormat::Inches: preferredSizeWidth /= horizontalClothCount; preferredSizeHeight /= verticalClothCount; suffix = QString(i18n("Inches")); break; case Configuration::EnumDocument_UnitsFormat::Centimeters: preferredSizeWidth /= horizontalClothCount; preferredSizeHeight /= verticalClothCount; suffix = QString(i18n("Centimeters")); break; default: break; } if (ui.ClothCountLink->isChecked()) { ui.VerticalClothCount->setValue(horizontalClothCount); } if (Configuration::document_UnitsFormat() == Configuration::EnumDocument_UnitsFormat::Stitches) { ui.FinalSize->setText(QString(i18nc("%1 width, %2 height and %3 units", "%1 x %2 %3", QString::fromLatin1("%1").arg(preferredSizeWidth, 0, 'f', 0), QString::fromLatin1("%1").arg(preferredSizeHeight, 0, 'f', 0), suffix))); } else { ui.FinalSize->setText(QString(i18nc("%1 width, %2 height and %3 units", "%1 x %2 %3", QString::fromLatin1("%1").arg(preferredSizeWidth, 0, 'f', 2), QString::fromLatin1("%1").arg(preferredSizeHeight, 0, 'f', 2), suffix))); } } void ImportImageDlg::on_ClothCountLink_clicked(bool checked) { ui.ClothCountLink->setIcon((checked) ? QIcon::fromTheme(QStringLiteral("object-locked")) : QIcon::fromTheme(QStringLiteral("object-unlocked"))); if (checked) { ui.VerticalClothCount->setValue(ui.HorizontalClothCount->value()); ui.VerticalClothCount->setEnabled(false); } else { ui.VerticalClothCount->setEnabled(true); } } void ImportImageDlg::on_PatternScale_valueChanged(int) { killTimer(m_timer); m_timer = startTimer(500); } void ImportImageDlg::on_CropEnabled_toggled(bool checked) { ui.ImagePreview->setCropping(checked); if (!checked) { on_CropReset_clicked(true); } } void ImportImageDlg::on_CropReset_clicked(bool checked) { Q_UNUSED(checked); m_convertedImage = m_originalImage; m_crop = QRect(0, 0, m_originalImage.columns(), m_originalImage.rows()); updateWindowTitle(); renderPixmap(); } void ImportImageDlg::imageCropped(const QRectF &rectF) { // rectF is new crop rectangle relative to m_convertedImage size // scale rect from m_convertedImage size to m_originalSize // m_original size may be cropped from m_originalImage, but is not scaled. // add new crop to original one. double scaleFactor = (double)m_originalSize.width() / (double)m_convertedImage.columns(); QRect scaledCrop = QRect(scaleFactor*rectF.left(), scaleFactor*rectF.top(), scaleFactor*rectF.width(), scaleFactor*rectF.height()); m_crop = QRect(m_crop.left()+scaledCrop.left(), m_crop.top()+scaledCrop.top(), scaledCrop.width(), scaledCrop.height()); updateWindowTitle(); renderPixmap(); } void ImportImageDlg::on_UseFractionals_toggled(bool checked) { Q_UNUSED(checked); killTimer(m_timer); m_timer = startTimer(500); } void ImportImageDlg::calculateSizes() { m_convertedImage = m_originalImage; if (m_crop.isValid()) { m_convertedImage.chop(Magick::Geometry(m_crop.left(),m_crop.top())); m_convertedImage.crop(Magick::Geometry(m_crop.width(),m_crop.height())); m_originalSize = QSize(m_convertedImage.columns(), m_convertedImage.rows()); } m_preferredSize = m_originalSize * ui.PatternScale->value() / 100; QSize imageSize = m_preferredSize; if (ui.UseFractionals->isChecked()) { imageSize *= 2; } Magick::Geometry geometry(imageSize.width(), imageSize.height()); geometry.percent(false); geometry.aspect(true); // set to true to ignore maintaining the aspect ratio m_convertedImage.sample(geometry); on_HorizontalClothCount_valueChanged(ui.HorizontalClothCount->value()); } void ImportImageDlg::createImageMap() { FlossScheme *scheme = SchemeManager::scheme(ui.FlossScheme->currentText()); m_colorMap = *(scheme->createImageMap()); } void ImportImageDlg::renderPixmap() { QPixmap alpha; alpha.loadFromData(alphaData, 143); ui.ImagePreview->setCursor(Qt::WaitCursor); calculateSizes(); m_convertedImage.modifyImage(); m_pixmap = QPixmap(m_convertedImage.columns(), m_convertedImage.rows()); m_pixmap.fill(); m_convertedImage.quantizeColorSpace(Magick::RGBColorspace); m_convertedImage.quantizeColors(ui.UseMaximumColors->isChecked() ? std::min(ui.MaximumColors->value(), SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes().count()) : SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes().count()); m_convertedImage.quantize(); m_convertedImage.map(m_colorMap); m_convertedImage.modifyImage(); QPainter painter; painter.begin(&m_pixmap); painter.drawTiledPixmap(m_pixmap.rect(), alpha); int width = m_convertedImage.columns(); int height = m_convertedImage.rows(); int pixelCount = width * height; QProgressDialog progress(i18n("Rendering preview"), i18n("Cancel"), 0, pixelCount, this); progress.setWindowModality(Qt::WindowModal); +/* + * ImageMagick prior to V7 used matte (opacity) to determine if an image has transparency. + * 0.0 for transparent to 1.0 for opaque + * + * ImageMagick V7 now uses alpha (transparency). + * 1.0 for transparent to 0.0 for opaque + * + * Access to pixels has changed too, V7 can use pixelColor to access the color of a particular + * pixel, but although this was available in V6, it doesn't appear to produce the same result + * and has resulted in black images when importing. + */ #if MagickLibVersion < 0x700 bool hasTransparency = m_convertedImage.matte(); double transparent = 1.0; + const Magick::PixelPacket *pixels = m_convertedImage.getConstPixels(0, 0, width, height); #else bool hasTransparency = m_convertedImage.alpha(); double transparent = 0.0; #endif for (int dy = 0 ; dy < height ; dy++) { QApplication::processEvents(); progress.setValue(dy * width); if (progress.wasCanceled()) { break; } for (int dx = 0 ; dx < width ; dx++) { +#if MagickLibVersion < 0x700 + Magick::ColorRGB rgb = Magick::Color(*pixels++); +#else Magick::ColorRGB rgb = m_convertedImage.pixelColor(dx, dy); +#endif if (hasTransparency && (rgb.alpha() == transparent)) { //ignore this pixel as it is transparent } else { if (!(ui.IgnoreColor->isChecked() && rgb == m_ignoreColorValue)) { QColor color((int)(255*rgb.red()), (int)(255*rgb.green()), (int)(255*rgb.blue())); painter.setPen(QPen(color)); painter.drawPoint(dx, dy); } } } } painter.end(); ui.ImagePreview->setPixmap(m_pixmap); ui.ImagePreview->setCursor(Qt::ArrowCursor); } void ImportImageDlg::timerEvent(QTimerEvent*) { killTimer(m_timer); renderPixmap(); } void ImportImageDlg::pickColor() { if (ui.IgnoreColor->isChecked()) { m_alphaSelect = new AlphaSelect(ui.ImagePreview); connect(m_alphaSelect, &AlphaSelect::clicked, this, &ImportImageDlg::selectColor); m_alphaSelect->show(); } else { delete m_alphaSelect; m_alphaSelect = nullptr; } } void ImportImageDlg::selectColor(const QPoint &p) { QSize trueSize(m_convertedImage.columns(), m_convertedImage.rows()); QRect pixmapRect = m_alphaSelect->pixmapRect(); delete m_alphaSelect; m_alphaSelect = nullptr; // convert p that is relative to the ScaledImageLabel to the pixmap size int x = (((p.x() - pixmapRect.left()) * trueSize.width()) / pixmapRect.width()); int y = (((p.y() - pixmapRect.top()) * trueSize.height()) / pixmapRect.height()); x = (x < 0) ? 0 : std::min(x, trueSize.width()); y = (y < 0) ? 0 : std::min(y, trueSize.height()); m_ignoreColorValue = m_convertedImage.pixelColor(x, y); QPixmap swatch(ui.ColorButton->size()); swatch.fill(QColor((int)(255*m_ignoreColorValue.red()), (int)(255*m_ignoreColorValue.green()), (int)(255*m_ignoreColorValue.blue()))); ui.ColorButton->setIcon(swatch); renderPixmap(); } void ImportImageDlg::on_DialogButtonBox_accepted() { accept(); } void ImportImageDlg::on_DialogButtonBox_rejected() { reject(); } void ImportImageDlg::on_DialogButtonBox_helpRequested() { KHelpClient::invokeHelp(QStringLiteral("ImportImageDialog"), QStringLiteral("kxstitch")); } void ImportImageDlg::on_DialogButtonBox_clicked(QAbstractButton *button) { if (ui.DialogButtonBox->button(QDialogButtonBox::Reset) == button) { m_convertedImage = m_originalImage; resetImportParameters(); renderPixmap(); } } void ImportImageDlg::resetImportParameters() { double horizontalClothCount = Configuration::editor_HorizontalClothCount(); double verticalClothCount = Configuration::editor_VerticalClothCount(); Configuration::EnumEditor_ClothCountUnits::type clothCountUnits = Configuration::editor_ClothCountUnits(); if (clothCountUnits == Configuration::EnumEditor_ClothCountUnits::Default) { clothCountUnits = (QLocale::system().measurementSystem() == QLocale::MetricSystem) ? Configuration::EnumEditor_ClothCountUnits::Centimeters : Configuration::EnumEditor_ClothCountUnits::Inches; } if (clothCountUnits == Configuration::EnumEditor_ClothCountUnits::Centimeters) { ui.HorizontalClothCount->setSuffix(i18nc("Per centimeter measurements", "/cm")); ui.VerticalClothCount->setSuffix(i18nc("Per centimeter measurements", "/cm")); ui.HorizontalClothCount->setSingleStep(0.1); ui.VerticalClothCount->setSingleStep(0.1); ui.HorizontalClothCount->setDecimals(1); ui.VerticalClothCount->setDecimals(1); } else { ui.HorizontalClothCount->setSuffix(i18nc("Per inch measurements", "/in")); ui.VerticalClothCount->setSuffix(i18nc("Per inch measurements", "/in")); ui.HorizontalClothCount->setSingleStep(1.0); ui.VerticalClothCount->setSingleStep(1.0); ui.HorizontalClothCount->setDecimals(0); ui.VerticalClothCount->setDecimals(0); } ui.HorizontalClothCount->setValue(horizontalClothCount); ui.VerticalClothCount->setValue(verticalClothCount); ui.ClothCountLink->setChecked(Configuration::editor_ClothCountLink()); ui.VerticalClothCount->setEnabled(!ui.ClothCountLink->isChecked()); ui.ClothCountLink->setIcon(ui.ClothCountLink->isChecked() ? QIcon::fromTheme(QStringLiteral("object-locked")) : QIcon::fromTheme(QStringLiteral("object-unlocked"))); m_preferredSize = QSize(Configuration::document_Width(), Configuration::document_Height()); Configuration::EnumDocument_UnitsFormat::type preferredSizeUnits = Configuration::document_UnitsFormat(); switch (preferredSizeUnits) { case Configuration::EnumDocument_UnitsFormat::Inches: switch (clothCountUnits) { case Configuration::EnumEditor_ClothCountUnits::Inches: m_preferredSize = QSize(m_preferredSize.width() * horizontalClothCount, m_preferredSize.height() * verticalClothCount); break; case Configuration::EnumEditor_ClothCountUnits::Centimeters: m_preferredSize = QSize(m_preferredSize.width() * horizontalClothCount * 2.54, m_preferredSize.height() * verticalClothCount * 2.54); break; default: // No conversion required break; } break; case Configuration::EnumDocument_UnitsFormat::Centimeters: switch (clothCountUnits) { case Configuration::EnumEditor_ClothCountUnits::Inches: m_preferredSize = QSize(m_preferredSize.width() * horizontalClothCount / 2.54, m_preferredSize.height() * verticalClothCount / 2.54); break; case Configuration::EnumEditor_ClothCountUnits::Centimeters: m_preferredSize = QSize(m_preferredSize.width() * horizontalClothCount, m_preferredSize.height() * verticalClothCount); break; default: // No conversion required break; } break; default: break; } int scaledWidth = m_preferredSize.width() * 100 / m_originalSize.width(); int scaledHeight = m_preferredSize.height() * 100 / m_originalSize.height(); int scale = std::min(scaledWidth, scaledHeight); QString scheme = Configuration::palette_DefaultScheme(); if (SchemeManager::scheme(scheme) == nullptr) { scheme = SchemeManager::schemes().at(scheme.toInt()); } ui.FlossScheme->setCurrentItem(scheme); ui.PatternScale->setValue(scale); ui.UseMaximumColors->setChecked(Configuration::import_UseMaximumColors()); ui.MaximumColors->setEnabled(ui.UseMaximumColors->isChecked()); ui.MaximumColors->setValue(Configuration::import_MaximumColors()); ui.MaximumColors->setMaximum(SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes().count()); ui.MaximumColors->setToolTip(QString(i18n("Colors limited to %1 due to the number of symbols available", ui.MaximumColors->maximum()))); } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 1f12f5b..ecf552a 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1,1710 +1,1727 @@ /* * Copyright (C) 2010-2015 by Stephen Allewell * steve.allewell@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include "MainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BackgroundImage.h" #include "configuration.h" #include "ConfigurationDialogs.h" #include "Commands.h" #include "Document.h" #include "Editor.h" #include "ExtendPatternDlg.h" #include "FilePropertiesDlg.h" #include "Floss.h" #include "FlossScheme.h" #include "ImportImageDlg.h" #include "Palette.h" #include "PaletteManagerDlg.h" #include "PaperSizes.h" #include "Preview.h" #include "PrintSetupDlg.h" #include "QVariantPtr.h" #include "Scale.h" #include "ScaledPixmapLabel.h" #include "SchemeManager.h" #include "SymbolLibrary.h" #include "SymbolManager.h" MainWindow::MainWindow() : m_printer(nullptr) { setupActions(); } MainWindow::MainWindow(const QUrl &url) : m_printer(nullptr) { setupMainWindow(); setupLayout(); setupDockWindows(); setupActions(); setupDocument(); setupConnections(); setupActionDefaults(); loadSettings(); fileOpen(url); setupActionsFromDocument(); setCaption(m_document->url().fileName(), !m_document->undoStack().isClean()); this->findChild(QStringLiteral("ImportedImage#"))->hide(); } MainWindow::MainWindow(const QString &source) : m_printer(nullptr) { setupMainWindow(); setupLayout(); setupDockWindows(); setupActions(); setupDocument(); setupConnections(); setupActionDefaults(); loadSettings(); convertImage(source); setupActionsFromDocument(); setCaption(m_document->url().fileName(), !m_document->undoStack().isClean()); this->findChild(QStringLiteral("ImportedImage#"))->show(); } void MainWindow::setupMainWindow() { setObjectName(QStringLiteral("MainWindow#")); setAutoSaveSettings(); } void MainWindow::setupLayout() { QScrollArea *scrollArea = new QScrollArea(); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); m_editor = new Editor(scrollArea); scrollArea->installEventFilter(m_editor); scrollArea->setWidget(m_editor); m_horizontalScale = m_editor->horizontalScale(); m_verticalScale = m_editor->verticalScale(); QGridLayout *gridLayout = new QGridLayout(this); gridLayout->addWidget(m_horizontalScale, 0, 1); gridLayout->addWidget(m_verticalScale, 1, 0); gridLayout->addWidget(scrollArea, 1, 1); QWidget *layout = new QWidget(); layout->setLayout(gridLayout); setCentralWidget(layout); } void MainWindow::setupDocument() { m_document = new Document(); m_editor->setDocument(m_document); m_editor->setPreview(m_preview); m_palette->setDocument(m_document); m_preview->setDocument(m_document); m_history->setStack(&(m_document->undoStack())); m_document->addView(m_editor); m_document->addView(m_preview); m_document->addView(m_palette); } void MainWindow::setupConnections() { KActionCollection *actions = actionCollection(); connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("edit_undo")), &QAction::setEnabled); connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("file_revert")), &QAction::setEnabled); connect(&(m_document->undoStack()), &QUndoStack::canRedoChanged, actions->action(QStringLiteral("edit_redo")), &QAction::setEnabled); connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::clipboardDataChanged); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_cut")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_copy")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorHorizontal")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorVertical")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate90")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate180")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate270")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("patternCropToSelection")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertColumns")), &QAction::setEnabled); connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertRows")), &QAction::setEnabled); connect(&(m_document->undoStack()), &QUndoStack::undoTextChanged, this, &MainWindow::undoTextChanged); connect(&(m_document->undoStack()), &QUndoStack::redoTextChanged, this, &MainWindow::redoTextChanged); connect(&(m_document->undoStack()), &QUndoStack::cleanChanged, this, &MainWindow::documentModified); connect(m_palette, &Palette::colorSelected, m_editor, QOverload<>::of(&Editor::drawContents)); connect(m_palette, QOverload::of(&Palette::swapColors), this, &MainWindow::paletteSwapColors); connect(m_palette, QOverload::of(&Palette::replaceColor), this, &MainWindow::paletteReplaceColor); connect(m_palette, &Palette::signalStateChanged, this, QOverload::of(&KXmlGuiWindow::slotStateChanged)); connect(m_palette, &Palette::customContextMenuRequested, this, &MainWindow::paletteContextMenu); connect(m_editor, &Editor::changedVisibleCells, m_preview, &Preview::setVisibleCells); connect(m_preview, QOverload::of(&Preview::clicked), m_editor, QOverload::of(&Editor::previewClicked)); connect(m_preview, QOverload::of(&Preview::clicked), m_editor, QOverload::of(&Editor::previewClicked)); } void MainWindow::setupActionDefaults() { KActionCollection *actions = actionCollection(); actions->action(QStringLiteral("maskStitch"))->setChecked(false); actions->action(QStringLiteral("maskColor"))->setChecked(false); actions->action(QStringLiteral("maskBackstitch"))->setChecked(false); actions->action(QStringLiteral("maskKnot"))->setChecked(false); actions->action(QStringLiteral("stitchFull"))->trigger(); // Select full stitch actions->action(QStringLiteral("toolPaint"))->trigger(); // Select paint tool clipboardDataChanged(); } MainWindow::~MainWindow() { delete m_printer; } Editor *MainWindow::editor() { return m_editor; } Preview *MainWindow::preview() { return m_preview; } Palette *MainWindow::palette() { return m_palette; } bool MainWindow::queryClose() { if (m_document->undoStack().isClean()) { return true; } while (true) { int messageBoxResult = KMessageBox::warningYesNoCancel(this, i18n("Save changes to document?\nSelecting No discards changes.")); switch (messageBoxResult) { case KMessageBox::Yes : fileSave(); if (m_document->undoStack().isClean()) { return true; } else { KMessageBox::error(this, i18n("Unable to save the file")); } break; case KMessageBox::No : return true; case KMessageBox::Cancel : return false; } } } void MainWindow::setupActionsFromDocument() { KActionCollection *actions = actionCollection(); actions->action(QStringLiteral("file_revert"))->setEnabled(!m_document->undoStack().isClean()); actions->action(QStringLiteral("edit_undo"))->setEnabled(m_document->undoStack().canUndo()); actions->action(QStringLiteral("edit_redo"))->setEnabled(m_document->undoStack().canRedo()); updateBackgroundImageActionLists(); } void MainWindow::fileNew() { MainWindow *window = new MainWindow(QUrl()); window->show(); } void MainWindow::fileOpen() { fileOpen(QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl::fromLocalFile(QDir::homePath()), i18n("KXStitch Patterns (*.kxs);;PC Stitch Patterns (*.pat);;All Files (*)"))); } void MainWindow::fileOpen(const QUrl &url) { MainWindow *window; bool docEmpty = (m_document->undoStack().isClean() && (m_document->url().toString() == i18n("Untitled"))); if (url.isValid()) { if (docEmpty) { QTemporaryFile tmpFile; if (tmpFile.open()) { KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite); if (job->exec()) { QDataStream stream(&tmpFile); try { m_document->readKXStitch(stream); m_document->setUrl(url); KRecentFilesAction *action = static_cast(actionCollection()->action(QStringLiteral("file_open_recent"))); action->addUrl(url); action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles"))); } catch (const InvalidFile &e) { stream.device()->seek(0); try { m_document->readPCStitch(stream); } catch (const InvalidFile &e) { KMessageBox::sorry(nullptr, i18n("The file does not appear to be a recognized cross stitch file.")); } } catch (const InvalidFileVersion &e) { KMessageBox::sorry(nullptr, i18n("This version of the file is not supported.\n%1", e.version)); } catch (const FailedReadFile &e) { KMessageBox::error(nullptr, i18n("Failed to read the file.\n%1.", e.status)); m_document->initialiseNew(); } setupActionsFromDocument(); m_editor->readDocumentSettings(); m_preview->readDocumentSettings(); m_palette->update(); documentModified(true); // this is the clean value true } else { KMessageBox::error(nullptr, job->errorString()); } } else { KMessageBox::error(nullptr, tmpFile.errorString()); } } else { window = new MainWindow(url); window->show(); } } } void MainWindow::fileSave() { QUrl url = m_document->url(); if (url.toString() == i18n("Untitled")) { fileSaveAs(); } else { // ### Why use QUrl everywhere if this only supports local files? QSaveFile file(url.toLocalFile()); if (file.open(QIODevice::WriteOnly)) { QDataStream stream(&file); try { m_document->write(stream); if (!file.commit()) { throw FailedWriteFile(stream.status()); } m_document->undoStack().setClean(); } catch (const FailedWriteFile &e) { KMessageBox::error(nullptr, QString(i18n("Failed to save the file.\n%1", file.errorString()))); file.cancelWriting(); } } else { KMessageBox::error(nullptr, QString(i18n("Failed to open the file.\n%1", file.errorString()))); } } } void MainWindow::fileSaveAs() { QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Patterns (*.kxs)")); if (url.isValid()) { KIO::StatJob *statJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0); if (statJob->exec()) { if (KMessageBox::warningYesNo(this, i18n("This file already exists\nDo you want to overwrite it?")) == KMessageBox::No) { return; } } m_document->setUrl(url); fileSave(); KRecentFilesAction *action = static_cast(actionCollection()->action(QStringLiteral("file_open_recent"))); action->addUrl(url); action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles"))); } } void MainWindow::fileRevert() { if (!m_document->undoStack().isClean()) { if (KMessageBox::warningYesNo(this, i18n("Revert changes to document?")) == KMessageBox::Yes) { m_document->undoStack().setIndex(m_document->undoStack().cleanIndex()); } } } void MainWindow::filePrintSetup() { if (m_printer == nullptr) { m_printer = new QPrinter(); } QPointer printSetupDlg = new PrintSetupDlg(this, m_document, m_printer); if (printSetupDlg->exec() == QDialog::Accepted) { m_document->undoStack().push(new UpdatePrinterConfigurationCommand(m_document, printSetupDlg->printerConfiguration())); } delete printSetupDlg; } void MainWindow::filePrint() { if (m_printer == nullptr) { filePrintSetup(); } if (!m_document->printerConfiguration().pages().isEmpty()) { m_printer->setFullPage(true); m_printer->setPrintRange(QPrinter::AllPages); m_printer->setFromTo(1, m_document->printerConfiguration().pages().count()); QPointer printDialog = new QPrintDialog(m_printer, this); if (printDialog->exec() == QDialog::Accepted) { printPages(); } delete printDialog; } else { KMessageBox::information(this, i18n("There is nothing to print")); } } void MainWindow::printPages() { QList pages = m_document->printerConfiguration().pages(); int fromPage = 1; int toPage = pages.count(); if (m_printer->printRange() == QPrinter::PageRange) { fromPage = m_printer->fromPage(); toPage = m_printer->toPage(); } while (toPage < pages.count()) pages.removeLast(); while (--fromPage) pages.removeFirst(); int totalPages = pages.count(); const Page *page = (m_printer->pageOrder() == QPrinter::FirstPageFirst)?pages.takeFirst():pages.takeLast(); m_printer->setPaperSize(page->paperSize()); // DEPRECATED m_printer->setOrientation(page->orientation()); // DEPRECATED QPainter painter; painter.begin(m_printer); painter.setRenderHint(QPainter::Antialiasing, true); for (int p = 0 ; p < totalPages ;) { int paperWidth = PaperSizes::width(page->paperSize(), page->orientation()); int paperHeight = PaperSizes::height(page->paperSize(), page->orientation()); painter.setWindow(0, 0, paperWidth, paperHeight); page->render(m_document, &painter); if (++p < totalPages) { page = (m_printer->pageOrder() == QPrinter::FirstPageFirst)?pages.takeFirst():pages.takeLast(); m_printer->setPaperSize(page->paperSize()); // DEPRECATED m_printer->setOrientation(page->orientation()); // DEPRECATED m_printer->newPage(); } } painter.end(); } void MainWindow::fileImportImage() { MainWindow *window; bool docEmpty = ((m_document->undoStack().isClean()) && (m_document->url().toString() == i18n("Untitled"))); QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Import Image"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)), i18n("Images (*.bmp *.gif *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm *.svg)")); if (url.isValid()) { QTemporaryFile tmpFile; if (tmpFile.open()) { KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite); if (job->exec()) { if (docEmpty) { convertImage(tmpFile.fileName()); this->findChild(QStringLiteral("ImportedImage#"))->show(); } else { window = new MainWindow(tmpFile.fileName()); window->show(); } } else { KMessageBox::error(nullptr, job->errorString()); } } } } void MainWindow::convertImage(const QString &source) { Magick::Image image(source.toStdString()); QMap documentFlosses; QList symbolIndexes = SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes(); QPointer importImageDlg = new ImportImageDlg(this, image); if (importImageDlg->exec()) { Magick::Image convertedImage = importImageDlg->convertedImage(); int imageWidth = convertedImage.columns(); int imageHeight = convertedImage.rows(); int documentWidth = imageWidth; int documentHeight = imageHeight; bool useFractionals = importImageDlg->useFractionals(); +/* + * ImageMagick prior to V7 used matte (opacity) to determine if an image has transparency. + * 0.0 for transparent to 1.0 for opaque + * + * ImageMagick V7 now uses alpha (transparency). + * 1.0 for transparent to 0.0 for opaque + * + * Access to pixels has changed too, V7 can use pixelColor to access the color of a particular + * pixel, but although this was available in V6, it doesn't appear to produce the same result + * and has resulted in black images when importing. + */ #if MagickLibVersion < 0x700 bool hasTransparency = convertedImage.matte(); double transparent = 1.0; + const Magick::PixelPacket *pixels = convertedImage.getConstPixels(0, 0, imageWidth, imageHeight); #else bool hasTransparency = convertedImage.alpha(); double transparent = 0.0; #endif + bool ignoreColor = importImageDlg->ignoreColor(); Magick::Color ignoreColorValue = importImageDlg->ignoreColorValue(); int pixelCount = imageWidth * imageHeight; if (useFractionals) { documentWidth /= 2; documentHeight /= 2; } QString schemeName = importImageDlg->flossScheme(); FlossScheme *flossScheme = SchemeManager::scheme(schemeName); QUndoCommand *importImageCommand = new ImportImageCommand(m_document); new ResizeDocumentCommand(m_document, documentWidth, documentHeight, importImageCommand); new ChangeSchemeCommand(m_document, schemeName, importImageCommand); QProgressDialog progress(i18n("Converting to stitches"), i18n("Cancel"), 0, pixelCount, this); progress.setWindowModality(Qt::WindowModal); for (int dy = 0 ; dy < imageHeight ; dy++) { progress.setValue(dy * imageWidth); QApplication::processEvents(); if (progress.wasCanceled()) { delete importImageDlg; delete importImageCommand; return; } for (int dx = 0 ; dx < imageWidth ; dx++) { +#if MagickLibVersion < 0x700 + Magick::ColorRGB rgb = Magick::Color(*pixels++); // is this a memory leak +#else Magick::ColorRGB rgb = convertedImage.pixelColor(dx, dy); +#endif if (hasTransparency && (rgb.alpha() == transparent)) { // ignore this pixel as it is transparent } else { if (!(ignoreColor && (rgb == ignoreColorValue))) { int flossIndex; QColor color((int)(255*rgb.red()), (int)(255*rgb.green()), (int)(255*rgb.blue())); for (flossIndex = 0 ; flossIndex < documentFlosses.count() ; ++flossIndex) { if (documentFlosses[flossIndex] == color) { break; } } if (flossIndex == documentFlosses.count()) { // reached the end of the list qint16 stitchSymbol = symbolIndexes.takeFirst(); Qt::PenStyle backstitchSymbol(Qt::SolidLine); Floss *floss = flossScheme->find(color); DocumentFloss *documentFloss = new DocumentFloss(floss->name(), stitchSymbol, backstitchSymbol, Configuration::palette_StitchStrands(), Configuration::palette_BackstitchStrands()); documentFloss->setFlossColor(floss->color()); new AddDocumentFlossCommand(m_document, flossIndex, documentFloss, importImageCommand); documentFlosses.insert(flossIndex, color); } // at this point // flossIndex will be the index for the found color if (useFractionals) { int zone = (dy % 2) * 2 + (dx % 2); new AddStitchCommand(m_document, QPoint(dx / 2, dy / 2), stitchMap[0][zone], flossIndex, importImageCommand); } else { new AddStitchCommand(m_document, QPoint(dx, dy), Stitch::Full, flossIndex, importImageCommand); } } } } } new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), importImageDlg->horizontalClothCount(), importImageCommand); new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), importImageDlg->verticalClothCount(), importImageCommand); m_document->undoStack().push(importImageCommand); convertPreview(source, importImageDlg->croppedArea()); } delete importImageDlg; } void MainWindow::convertPreview(const QString &source, const QRect &croppedArea) { QPixmap pixmap; pixmap.load(source); pixmap = pixmap.copy(croppedArea); m_imageLabel->setPixmap(pixmap); } void MainWindow::fileProperties() { QPointer filePropertiesDlg = new FilePropertiesDlg(this, m_document); if (filePropertiesDlg->exec()) { QUndoCommand *cmd = new FilePropertiesCommand(m_document); if ((filePropertiesDlg->documentWidth() != m_document->pattern()->stitches().width()) || (filePropertiesDlg->documentHeight() != m_document->pattern()->stitches().height())) { new ResizeDocumentCommand(m_document, filePropertiesDlg->documentWidth(), filePropertiesDlg->documentHeight(), cmd); } if (filePropertiesDlg->unitsFormat() != static_cast(m_document->property(QStringLiteral("unitsFormat")).toInt())) { new SetPropertyCommand(m_document, QStringLiteral("unitsFormat"), QVariant(filePropertiesDlg->unitsFormat()), cmd); } if (filePropertiesDlg->horizontalClothCount() != m_document->property(QStringLiteral("horizontalClothCount")).toDouble()) { new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), QVariant(filePropertiesDlg->horizontalClothCount()), cmd); } if (filePropertiesDlg->clothCountLink() != m_document->property(QStringLiteral("clothCountLink")).toBool()) { new SetPropertyCommand(m_document, QStringLiteral("clothCountLink"), QVariant(filePropertiesDlg->clothCountLink()), cmd); } if (filePropertiesDlg->verticalClothCount() != m_document->property(QStringLiteral("verticalClothCount")).toDouble()) { new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), QVariant(filePropertiesDlg->verticalClothCount()), cmd); } if (filePropertiesDlg->clothCountUnits() != static_cast(m_document->property(QStringLiteral("clothCountUnits")).toInt())) { new SetPropertyCommand(m_document, QStringLiteral("clothCountUnits"), QVariant(filePropertiesDlg->clothCountUnits()), cmd); } if (filePropertiesDlg->title() != m_document->property(QStringLiteral("title")).toString()) { new SetPropertyCommand(m_document, QStringLiteral("title"), QVariant(filePropertiesDlg->title()), cmd); } if (filePropertiesDlg->author() != m_document->property(QStringLiteral("author")).toString()) { new SetPropertyCommand(m_document, QStringLiteral("author"), QVariant(filePropertiesDlg->author()), cmd); } if (filePropertiesDlg->copyright() != m_document->property(QStringLiteral("copyright")).toString()) { new SetPropertyCommand(m_document, QStringLiteral("copyright"), QVariant(filePropertiesDlg->copyright()), cmd); } if (filePropertiesDlg->fabric() != m_document->property(QStringLiteral("fabric")).toString()) { new SetPropertyCommand(m_document, QStringLiteral("fabric"), QVariant(filePropertiesDlg->fabric()), cmd); } if (filePropertiesDlg->fabricColor() != m_document->property(QStringLiteral("fabricColor")).value()) { new SetPropertyCommand(m_document, QStringLiteral("fabricColor"), QVariant(filePropertiesDlg->fabricColor()), cmd); } if (filePropertiesDlg->instructions() != m_document->property(QStringLiteral("instructions")).toString()) { new SetPropertyCommand(m_document, QStringLiteral("instructions"), QVariant(filePropertiesDlg->instructions()), cmd); } if (filePropertiesDlg->flossScheme() != m_document->pattern()->palette().schemeName()) { new ChangeSchemeCommand(m_document, filePropertiesDlg->flossScheme(), cmd); } if (cmd->childCount()) { m_document->undoStack().push(cmd); } else { delete cmd; } } delete filePropertiesDlg; } void MainWindow::fileAddBackgroundImage() { QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Background Image"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)), i18n("Images (*.bmp *.gif *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm *.svg)")); if (!url.isEmpty()) { QRect patternArea(0, 0, m_document->pattern()->stitches().width(), m_document->pattern()->stitches().height()); QRect selectionArea = m_editor->selectionArea(); QSharedPointer backgroundImage(new BackgroundImage(url, (selectionArea.isValid() ? selectionArea : patternArea))); if (backgroundImage->isValid()) { m_document->undoStack().push(new AddBackgroundImageCommand(m_document, backgroundImage, this)); } } } void MainWindow::fileRemoveBackgroundImage() { QAction *action = qobject_cast(sender()); m_document->undoStack().push(new RemoveBackgroundImageCommand(m_document, action->data().value>(), this)); } void MainWindow::fileClose() { if (queryClose()) { m_document->initialiseNew(); setupActionsFromDocument(); m_editor->readDocumentSettings(); m_preview->readDocumentSettings(); } close(); } void MainWindow::fileQuit() { close(); } void MainWindow::editUndo() { m_document->undoStack().undo(); } void MainWindow::editRedo() { m_document->undoStack().redo(); } void MainWindow::undoTextChanged(const QString &text) { actionCollection()->action(QStringLiteral("edit_undo"))->setText(i18n("Undo %1", text)); } void MainWindow::redoTextChanged(const QString &text) { actionCollection()->action(QStringLiteral("edit_redo"))->setText(i18n("Redo %1", text)); } void MainWindow::clipboardDataChanged() { actionCollection()->action(QStringLiteral("edit_paste"))->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QStringLiteral("application/kxstitch"))); } void MainWindow::paletteManager() { QPointer paletteManagerDlg = new PaletteManagerDlg(this, m_document); if (paletteManagerDlg->exec()) { DocumentPalette palette = paletteManagerDlg->palette(); if (palette != m_document->pattern()->palette()) { m_document->undoStack().push(new UpdateDocumentPaletteCommand(m_document, palette)); } } delete paletteManagerDlg; } void MainWindow::paletteShowSymbols(bool show) { m_palette->showSymbols(show); } void MainWindow::paletteClearUnused() { QMap flossUsage = m_document->pattern()->stitches().flossUsage(); QMapIterator flosses(m_document->pattern()->palette().flosses()); ClearUnusedFlossesCommand *clearUnusedFlossesCommand = new ClearUnusedFlossesCommand(m_document); while (flosses.hasNext()) { flosses.next(); if (flossUsage[flosses.key()].totalStitches() == 0) { new RemoveDocumentFlossCommand(m_document, flosses.key(), flosses.value(), clearUnusedFlossesCommand); } } if (clearUnusedFlossesCommand->childCount()) { m_document->undoStack().push(clearUnusedFlossesCommand); } else { delete clearUnusedFlossesCommand; } } void MainWindow::paletteCalibrateScheme() { } void MainWindow::paletteSwapColors(int originalIndex, int replacementIndex) { if (originalIndex != replacementIndex) { m_document->undoStack().push(new PaletteSwapColorCommand(m_document, originalIndex, replacementIndex)); } } void MainWindow::paletteReplaceColor(int originalIndex, int replacementIndex) { if (originalIndex != replacementIndex) { m_document->undoStack().push(new PaletteReplaceColorCommand(m_document, originalIndex, replacementIndex)); } } void MainWindow::viewFitBackgroundImage() { QAction *action = qobject_cast(sender()); m_document->undoStack().push(new FitBackgroundImageCommand(m_document, action->data().value>(), m_editor->selectionArea())); } void MainWindow::paletteContextMenu(const QPoint &pos) { static_cast(guiFactory()->container(QStringLiteral("PalettePopup"), this))->popup(qobject_cast(sender())->mapToGlobal(pos)); } void MainWindow::viewShowBackgroundImage() { QAction *action = qobject_cast(sender()); m_document->undoStack().push(new ShowBackgroundImageCommand(m_document, action->data().value>(), action->isChecked())); } void MainWindow::patternExtend() { QPointer extendPatternDlg = new ExtendPatternDlg(this); if (extendPatternDlg->exec()) { int top = extendPatternDlg->top(); int left = extendPatternDlg->left(); int right = extendPatternDlg->right(); int bottom = extendPatternDlg->bottom(); if (top || left || right || bottom) { m_document->undoStack().push(new ExtendPatternCommand(m_document, top, left, right, bottom)); } } delete extendPatternDlg; } void MainWindow::patternCentre() { m_document->undoStack().push(new CentrePatternCommand(m_document)); } void MainWindow::patternCrop() { m_document->undoStack().push(new CropToPatternCommand(m_document)); } void MainWindow::patternCropToSelection() { m_document->undoStack().push(new CropToSelectionCommand(m_document, m_editor->selectionArea())); } void MainWindow::insertColumns() { m_document->undoStack().push(new InsertColumnsCommand(m_document, m_editor->selectionArea())); } void MainWindow::insertRows() { m_document->undoStack().push(new InsertRowsCommand(m_document, m_editor->selectionArea())); } void MainWindow::preferences() { if (KConfigDialog::showDialog(QStringLiteral("preferences"))) { return; } KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("preferences"), Configuration::self()); dialog->setFaceType(KPageDialog::List); dialog->addPage(new EditorConfigPage(0, QStringLiteral("EditorConfigPage")), i18nc("The Editor config page", "Editor"), QStringLiteral("preferences-desktop")); dialog->addPage(new PatternConfigPage(0, QStringLiteral("PatternConfigPage")), i18n("Pattern"), QStringLiteral("ksnapshot")); PaletteConfigPage *paletteConfigPage = new PaletteConfigPage(0, QStringLiteral("PaletteConfigPage")); dialog->addPage(paletteConfigPage, i18n("Palette"), QStringLiteral("preferences-desktop-color")); dialog->addPage(new ImportConfigPage(0, QStringLiteral("ImportConfigPage")), i18n("Import"), QStringLiteral("insert-image")); dialog->addPage(new LibraryConfigPage(0, QStringLiteral("LibraryConfigPage")), i18n("Library"), QStringLiteral("accessories-dictionary")); dialog->addPage(new PrinterConfigPage(0, QStringLiteral("PrinterConfigPage")), i18n("Printer Configuration"), QStringLiteral("preferences-desktop-printer")); connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::settingsChanged); dialog->show(); } void MainWindow::settingsChanged() { QList documentChanges; ConfigurationCommand *configurationCommand = new ConfigurationCommand(this); if (m_document->property(QStringLiteral("cellHorizontalGrouping")) != Configuration::editor_CellHorizontalGrouping()) { documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("cellHorizontalGrouping"), Configuration::editor_CellHorizontalGrouping(), configurationCommand)); } if (m_document->property(QStringLiteral("cellVerticalGrouping")) != Configuration::editor_CellVerticalGrouping()) { documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("cellVerticalGrouping"), Configuration::editor_CellVerticalGrouping(), configurationCommand)); } if (m_document->property(QStringLiteral("thickLineColor")) != Configuration::editor_ThickLineColor()) { documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("thickLineColor"), Configuration::editor_ThickLineColor(), configurationCommand)); } if (m_document->property(QStringLiteral("thinLineColor")) != Configuration::editor_ThinLineColor()) { documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("thinLineColor"), Configuration::editor_ThinLineColor(), configurationCommand)); } if (documentChanges.count()) { m_document->undoStack().push(configurationCommand); } else { delete configurationCommand; } loadSettings(); } void MainWindow::loadSettings() { m_horizontalScale->setMinimumSize(0, Configuration::editor_HorizontalScaleHeight()); m_verticalScale->setMinimumSize(Configuration::editor_VerticalScaleWidth(), 0); m_horizontalScale->setCellGrouping(Configuration::editor_CellHorizontalGrouping()); m_verticalScale->setCellGrouping(Configuration::editor_CellVerticalGrouping()); m_editor->loadSettings(); m_preview->loadSettings(); m_palette->loadSettings(); KActionCollection *actions = actionCollection(); actions->action(QStringLiteral("makesCopies"))->setChecked(Configuration::tool_MakesCopies()); actions->action(QStringLiteral("colorHighlight"))->setChecked(Configuration::renderer_ColorHilight()); actions->action(QStringLiteral("renderStitches"))->setChecked(Configuration::renderer_RenderStitches()); actions->action(QStringLiteral("renderBackstitches"))->setChecked(Configuration::renderer_RenderBackstitches()); actions->action(QStringLiteral("renderFrenchKnots"))->setChecked(Configuration::renderer_RenderFrenchKnots()); actions->action(QStringLiteral("renderGrid"))->setChecked(Configuration::renderer_RenderGrid()); actions->action(QStringLiteral("renderBackgroundImages"))->setChecked(Configuration::renderer_RenderBackgroundImages()); switch (Configuration::editor_FormatScalesAs()) { case Configuration::EnumEditor_FormatScalesAs::Stitches: actions->action(QStringLiteral("formatScalesAsStitches"))->trigger(); break; case Configuration::EnumEditor_FormatScalesAs::Inches: actions->action(QStringLiteral("formatScalesAsInches"))->trigger(); break; case Configuration::EnumEditor_FormatScalesAs::Centimeters: actions->action(QStringLiteral("formatScalesAsCentimeters"))->trigger(); break; default: break; } switch (Configuration::renderer_RenderStitchesAs()) { case Configuration::EnumRenderer_RenderStitchesAs::Stitches: actions->action(QStringLiteral("renderStitchesAsRegularStitches"))->trigger(); break; case Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols: actions->action(QStringLiteral("renderStitchesAsBlackWhiteSymbols"))->trigger(); break; case Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols: actions->action(QStringLiteral("renderStitchesAsColorSymbols"))->trigger(); break; case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks: actions->action(QStringLiteral("renderStitchesAsColorBlocks"))->trigger(); break; case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols: actions->action(QStringLiteral("renderStitchesAsColorBlocksSymbols"))->trigger(); break; default: break; } switch (Configuration::renderer_RenderBackstitchesAs()) { case Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines: actions->action(QStringLiteral("renderBackstitchesAsColorLines"))->trigger(); break; case Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols: actions->action(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"))->trigger(); break; default: break; } switch (Configuration::renderer_RenderKnotsAs()) { case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks: actions->action(QStringLiteral("renderKnotsAsColorBlocks"))->trigger(); break; case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols: actions->action(QStringLiteral("renderKnotsAsColorBlocksSymbols"))->trigger(); break; case Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols: actions->action(QStringLiteral("renderKnotsAsColorSymbols"))->trigger(); break; case Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols: actions->action(QStringLiteral("renderKnotsAsBlackWhiteSymbols"))->trigger(); break; default: break; } actions->action(QStringLiteral("paletteShowSymbols"))->setChecked(Configuration::palette_ShowSymbols()); } void MainWindow::documentModified(bool clean) { setCaption(m_document->url().fileName(), !clean); } void MainWindow::setupActions() { QAction *action; QActionGroup *actionGroup; KActionCollection *actions = actionCollection(); // File menu KStandardAction::openNew(this, &MainWindow::fileNew, actions); KStandardAction::open(this, QOverload<>::of(&MainWindow::fileOpen), actions); KStandardAction::openRecent(this, QOverload::of(&MainWindow::fileOpen), actions)->loadEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles"))); KStandardAction::save(this, &MainWindow::fileSave, actions); KStandardAction::saveAs(this, &MainWindow::fileSaveAs, actions); KStandardAction::revert(this, &MainWindow::fileRevert, actions); action = new QAction(this); action->setText(i18n("Print Setup...")); connect(action, &QAction::triggered, this, &MainWindow::filePrintSetup); actions->addAction(QStringLiteral("filePrintSetup"), action); KStandardAction::print(this, &MainWindow::filePrint, actions); action = new QAction(this); action->setText(i18n("Import Image")); connect(action, &QAction::triggered, this, &MainWindow::fileImportImage); actions->addAction(QStringLiteral("fileImportImage"), action); action = new QAction(this); action->setText(i18n("File Properties")); connect(action, &QAction::triggered, this, &MainWindow::fileProperties); actions->addAction(QStringLiteral("fileProperties"), action); action = new QAction(this); action->setText(i18n("Add Background Image...")); connect(action, &QAction::triggered, this, &MainWindow::fileAddBackgroundImage); actions->addAction(QStringLiteral("fileAddBackgroundImage"), action); KStandardAction::close(this, &MainWindow::fileClose, actions); KStandardAction::quit(this, &MainWindow::fileQuit, actions); // Edit menu KStandardAction::undo(this, &MainWindow::editUndo, actions); KStandardAction::redo(this, &MainWindow::editRedo, actions); KStandardAction::cut(m_editor, &Editor::editCut, actions); actions->action(QStringLiteral("edit_cut"))->setEnabled(false); KStandardAction::copy(m_editor, &Editor::editCopy, actions); actions->action(QStringLiteral("edit_copy"))->setEnabled(false); KStandardAction::paste(m_editor, &Editor::editPaste, actions); action = new QAction(this); action->setText(i18n("Mirror/Rotate makes copies")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::setMakesCopies); actions->addAction(QStringLiteral("makesCopies"), action); action = new QAction(this); action->setText(i18n("Horizontally")); action->setData(Qt::Horizontal); connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection); action->setEnabled(false); actions->addAction(QStringLiteral("mirrorHorizontal"), action); action = new QAction(this); action->setText(i18n("Vertically")); action->setData(Qt::Vertical); connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection); action->setEnabled(false); actions->addAction(QStringLiteral("mirrorVertical"), action); action = new QAction(this); action->setText(i18n("90 Degrees")); action->setData(StitchData::Rotate90); connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection); action->setEnabled(false); actions->addAction(QStringLiteral("rotate90"), action); action = new QAction(this); action->setText(i18n("180 Degrees")); action->setData(StitchData::Rotate180); connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection); action->setEnabled(false); actions->addAction(QStringLiteral("rotate180"), action); action = new QAction(this); action->setText(i18n("270 Degrees")); action->setData(StitchData::Rotate270); connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection); action->setEnabled(false); actions->addAction(QStringLiteral("rotate270"), action); // Selection mask sub menu action = new QAction(this); action->setText(i18n("Stitch Mask")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::setMaskStitch); actions->addAction(QStringLiteral("maskStitch"), action); action = new QAction(this); action->setText(i18n("Color Mask")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::setMaskColor); actions->addAction(QStringLiteral("maskColor"), action); action = new QAction(this); action->setText(i18n("Exclude Backstitches")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::setMaskBackstitch); actions->addAction(QStringLiteral("maskBackstitch"), action); action = new QAction(this); action->setText(i18n("Exclude Knots")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::setMaskKnot); actions->addAction(QStringLiteral("maskKnot"), action); // View menu KStandardAction::zoomIn(m_editor, &Editor::zoomIn, actions); KStandardAction::zoomOut(m_editor, &Editor::zoomOut, actions); KStandardAction::actualSize(m_editor, &Editor::actualSize, actions); action = KStandardAction::fitToPage(m_editor, &Editor::fitToPage, actions); action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best"))); action = KStandardAction::fitToWidth(m_editor, &Editor::fitToWidth, actions); action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-width"))); action = KStandardAction::fitToHeight(m_editor, &Editor::fitToHeight, actions); action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-height"))); // Entries for Show/Hide Preview and Palette dock windows are added dynamically // Entries for Show/Hide and Remove background images are added dynamically // Stitches Menu actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Quarter Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-quarter-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchQuarter); }); actions->addAction(QStringLiteral("stitchQuarter"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Half Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-half-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchHalf); }); actions->addAction(QStringLiteral("stitchHalf"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("3 Quarter Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-3quarter-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::Stitch3Quarter); }); actions->addAction(QStringLiteral("stitch3Quarter"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Full Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-full-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchFull); }); actions->addAction(QStringLiteral("stitchFull"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Small Half Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-small-half-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchSmallHalf); }); actions->addAction(QStringLiteral("stitchSmallHalf"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Small Full Stitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-small-full-stitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchSmallFull); }); actions->addAction(QStringLiteral("stitchSmallFull"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("French Knot")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-frenchknot"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchFrenchKnot); }); actions->addAction(QStringLiteral("stitchFrenchKnot"), action); actionGroup->addAction(action); // Tools Menu actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Paint")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-brush"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolPaint);}); actions->addAction(QStringLiteral("toolPaint"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Draw")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-freehand"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolDraw);}); actions->addAction(QStringLiteral("toolDraw"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Erase")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-eraser"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolErase);}); actions->addAction(QStringLiteral("toolErase"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Draw Rectangle")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-rectangle"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolRectangle);}); actions->addAction(QStringLiteral("toolRectangle"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Fill Rectangle")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-draw-rectangle-filled"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillRectangle);}); actions->addAction(QStringLiteral("toolFillRectangle"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Draw Ellipse")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-ellipse"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolEllipse);}); actions->addAction(QStringLiteral("toolEllipse"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Fill Ellipse")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-draw-ellipse-filled"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillEllipse);}); actions->addAction(QStringLiteral("toolFillEllipse"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Fill Polygon")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-polyline"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillPolygon);}); actions->addAction(QStringLiteral("toolFillPolygon"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Text")); action->setIcon(QIcon::fromTheme(QStringLiteral("draw-text"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolText);}); actions->addAction(QStringLiteral("toolText"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Alphabet")); action->setIcon(QIcon::fromTheme(QStringLiteral("text-field"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolAlphabet);}); actions->addAction(QStringLiteral("toolAlphabet"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18nc("Select an area of the pattern", "Select")); action->setIcon(QIcon::fromTheme(QStringLiteral("select-rectangular"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolSelect);}); actions->addAction(QStringLiteral("toolSelectRectangle"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Backstitch")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-backstitch"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolBackstitch);}); actions->addAction(QStringLiteral("toolBackstitch"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Picker")); action->setIcon(QIcon::fromTheme(QStringLiteral("color-picker"))); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolColorPicker);}); actions->addAction(QStringLiteral("toolColorPicker"), action); actionGroup->addAction(action); // Palette Menu action = new QAction(this); action->setText(i18n("Palette Manager...")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-color-add"))); connect(action, &QAction::triggered, this, &MainWindow::paletteManager); actions->addAction(QStringLiteral("paletteManager"), action); action = new QAction(this); action->setText(i18n("Show Symbols")); action->setCheckable(true); connect(action, &QAction::toggled, this, &MainWindow::paletteShowSymbols); actions->addAction(QStringLiteral("paletteShowSymbols"), action); action = new QAction(this); action->setText(i18n("Clear Unused")); connect(action, &QAction::triggered, this, &MainWindow::paletteClearUnused); actions->addAction(QStringLiteral("paletteClearUnused"), action); action = new QAction(this); action->setText(i18n("Calibrate Scheme...")); connect(action, &QAction::triggered, this, &MainWindow::paletteCalibrateScheme); actions->addAction(QStringLiteral("paletteCalibrateScheme"), action); action = new QAction(this); action->setText(i18n("Swap Colors")); connect(action, &QAction::triggered, m_palette, QOverload<>::of(&Palette::swapColors)); actions->addAction(QStringLiteral("paletteSwapColors"), action); action = new QAction(this); action->setText(i18n("Replace Colors")); connect(action, &QAction::triggered, m_palette, QOverload<>::of(&Palette::replaceColor)); actions->addAction(QStringLiteral("paletteReplaceColor"), action); // Pattern Menu action = new QAction(this); action->setText(i18n("Extend Pattern...")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-extend-pattern"))); connect(action, &QAction::triggered, this, &MainWindow::patternExtend); actions->addAction(QStringLiteral("patternExtend"), action); action = new QAction(this); action->setText(i18n("Center Pattern")); action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-center-pattern"))); connect(action, &QAction::triggered, this, &MainWindow::patternCentre); actions->addAction(QStringLiteral("patternCentre"), action); action = new QAction(this); action->setText(i18n("Crop Canvas to Pattern")); connect(action, &QAction::triggered, this, &MainWindow::patternCrop); actions->addAction(QStringLiteral("patternCrop"), action); action = new QAction(this); action->setText(i18n("Crop Canvas to Selection")); action->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop"))); connect(action, &QAction::triggered, this, &MainWindow::patternCropToSelection); action->setEnabled(false); actions->addAction(QStringLiteral("patternCropToSelection"), action); action = new QAction(this); action->setText(i18n("Insert Rows")); connect(action, &QAction::triggered, this, &MainWindow::insertRows); action->setEnabled(false); actions->addAction(QStringLiteral("insertRows"), action); action = new QAction(this); action->setText(i18n("Insert Columns")); connect(action, &QAction::triggered, this, &MainWindow::insertColumns); action->setEnabled(false); actions->addAction(QStringLiteral("insertColumns"), action); // Library Menu action = new QAction(this); action->setText(i18n("Library Manager...")); connect(action, &QAction::triggered, m_editor, &Editor::libraryManager); actions->addAction(QStringLiteral("libraryManager"), action); // Settings Menu KStandardAction::preferences(this, &MainWindow::preferences, actions); // formatScalesAs actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Stitches")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsStitches); actions->addAction(QStringLiteral("formatScalesAsStitches"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Centimeters")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsCentimeters); actions->addAction(QStringLiteral("formatScalesAsCentimeters"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Inches")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsInches); actions->addAction(QStringLiteral("formatScalesAsInches"), action); actionGroup->addAction(action); // ShowStitchesAs actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Regular Stitches")); action->setCheckable(true); action->setChecked(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::Stitches);}); actions->addAction(QStringLiteral("renderStitchesAsRegularStitches"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Black & White Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols);}); actions->addAction(QStringLiteral("renderStitchesAsBlackWhiteSymbols"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols);}); actions->addAction(QStringLiteral("renderStitchesAsColorSymbols"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Blocks")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks);}); actions->addAction(QStringLiteral("renderStitchesAsColorBlocks"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Blocks & Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols);}); actions->addAction(QStringLiteral("renderStitchesAsColorBlocksSymbols"), action); actionGroup->addAction(action); // ShowBackstitchesAs actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Color Lines")); action->setCheckable(true); action->setChecked(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines);}); actions->addAction(QStringLiteral("renderBackstitchesAsColorLines"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Black & White Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols);}); actions->addAction(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"), action); actionGroup->addAction(action); // ShowKnotsAs actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); action = new QAction(this); action->setText(i18n("Color Blocks")); action->setCheckable(true); action->setChecked(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks);}); actions->addAction(QStringLiteral("renderKnotsAsColorBlocks"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Blocks & Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols);}); actions->addAction(QStringLiteral("renderKnotsAsColorBlocksSymbols"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols);}); actions->addAction(QStringLiteral("renderKnotsAsColorSymbols"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Black & White Symbols")); action->setCheckable(true); connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols);}); actions->addAction(QStringLiteral("renderKnotsAsBlackWhiteSymbols"), action); actionGroup->addAction(action); action = new QAction(this); action->setText(i18n("Color Highlight")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, &Editor::colorHighlight); actions->addAction(QStringLiteral("colorHighlight"), action); action = new QAction(this); action->setText(i18n("Show Stitches")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, QOverload::of(&Editor::renderStitches)); actions->addAction(QStringLiteral("renderStitches"), action); action = new QAction(this); action->setText(i18n("Show Backstitches")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, QOverload::of(&Editor::renderBackstitches)); actions->addAction(QStringLiteral("renderBackstitches"), action); action = new QAction(this); action->setText(i18n("Show French Knots")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, QOverload::of(&Editor::renderFrenchKnots)); actions->addAction(QStringLiteral("renderFrenchKnots"), action); action = new QAction(this); action->setText(i18n("Show Grid")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, QOverload::of(&Editor::renderGrid)); actions->addAction(QStringLiteral("renderGrid"), action); action = new QAction(this); action->setText(i18n("Show Background Images")); action->setCheckable(true); connect(action, &QAction::toggled, m_editor, QOverload::of(&Editor::renderBackgroundImages)); actions->addAction(QStringLiteral("renderBackgroundImages"), action); m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches"))); m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters"))); m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches"))); m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches"))); m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters"))); m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches"))); setupGUI(KXmlGuiWindow::Default, QStringLiteral("kxstitchui.rc")); } void MainWindow::updateBackgroundImageActionLists() { auto backgroundImages = m_document->backgroundImages().backgroundImages(); unplugActionList(QStringLiteral("removeBackgroundImageActions")); unplugActionList(QStringLiteral("fitBackgroundImageActions")); unplugActionList(QStringLiteral("showBackgroundImageActions")); QList removeBackgroundImageActions; QList fitBackgroundImageActions; QList showBackgroundImageActions; while (backgroundImages.hasNext()) { QSharedPointer backgroundImage = backgroundImages.next(); QAction *action = new QAction(backgroundImage->url().fileName(), this); action->setData(QVariant::fromValue(backgroundImage)); action->setIcon(backgroundImage->icon()); connect(action, &QAction::triggered, this, &MainWindow::fileRemoveBackgroundImage); removeBackgroundImageActions.append(action); action = new QAction(backgroundImage->url().fileName(), this); action->setData(QVariant::fromValue(backgroundImage)); action->setIcon(backgroundImage->icon()); connect(action, &QAction::triggered, this, &MainWindow::viewFitBackgroundImage); fitBackgroundImageActions.append(action); action = new QAction(backgroundImage->url().fileName(), this); action->setData(QVariant::fromValue(backgroundImage)); action->setIcon(backgroundImage->icon()); action->setCheckable(true); action->setChecked(backgroundImage->isVisible()); connect(action, &QAction::triggered, this, &MainWindow::viewShowBackgroundImage); showBackgroundImageActions.append(action); } plugActionList(QStringLiteral("removeBackgroundImageActions"), removeBackgroundImageActions); plugActionList(QStringLiteral("fitBackgroundImageActions"), fitBackgroundImageActions); plugActionList(QStringLiteral("showBackgroundImageActions"), showBackgroundImageActions); } void MainWindow::setupDockWindows() { QDockWidget *dock = new QDockWidget(i18n("Preview"), this); dock->setObjectName(QStringLiteral("PreviewDock#")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); QScrollArea *scrollArea = new QScrollArea(); m_preview = new Preview(scrollArea); scrollArea->setWidget(m_preview); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setMinimumSize(std::min(300, m_preview->width()), std::min(400, m_preview->height())); dock->setWidget(scrollArea); addDockWidget(Qt::LeftDockWidgetArea, dock); actionCollection()->addAction(QStringLiteral("showPreviewDockWidget"), dock->toggleViewAction()); dock = new QDockWidget(i18n("Palette"), this); dock->setObjectName(QStringLiteral("PaletteDock#")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); m_palette = new Palette(this); m_palette->setContextMenuPolicy(Qt::CustomContextMenu); dock->setWidget(m_palette); addDockWidget(Qt::LeftDockWidgetArea, dock); actionCollection()->addAction(QStringLiteral("showPaletteDockWidget"), dock->toggleViewAction()); dock = new QDockWidget(i18n("History"), this); dock->setObjectName(QStringLiteral("HistoryDock#")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); m_history = new QUndoView(this); dock->setWidget(m_history); addDockWidget(Qt::LeftDockWidgetArea, dock); actionCollection()->addAction(QStringLiteral("showHistoryDockWidget"), dock->toggleViewAction()); dock = new QDockWidget(i18n("Imported Image"), this); dock->setObjectName(QStringLiteral("ImportedImage#")); dock->setAllowedAreas(Qt::AllDockWidgetAreas); m_imageLabel = new ScaledPixmapLabel(this); m_imageLabel->setScaledContents(false); dock->setWidget(m_imageLabel); addDockWidget(Qt::LeftDockWidgetArea, dock); actionCollection()->addAction(QStringLiteral("showImportedDockWidget"), dock->toggleViewAction()); }