1 /*
2 * Copyright (C) 2010-2015 by Stephen Allewell
3 * steve.allewell@gmail.com
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11
12 #include "MainWindow.h"
13
14 #include <QAction>
15 #include <QActionGroup>
16 #include <QClipboard>
17 #include <QDataStream>
18 #include <QDockWidget>
19 #include <QFileDialog>
20 #include <QGridLayout>
21 #include <QMenu>
22 #include <QMimeData>
23 #include <QPainter>
24 #include <QPaintEngine>
25 #include <QProgressDialog>
26 #include <QPrintDialog>
27 #include <QPrinter>
28 #include <QPrintEngine>
29 #include <QPrintPreviewDialog>
30 #include <QSaveFile>
31 #include <QScrollArea>
32 #include <QTemporaryFile>
33 #include <QUndoView>
34 #include <QUrl>
35
36 #include <KActionCollection>
37 #include <KConfigDialog>
38 #include <KIO/FileCopyJob>
39 #include <KIO/StatJob>
40 #include <KLocalizedString>
41 #include <KMessageBox>
42 #include <KRecentFilesAction>
43 #include <KSelectAction>
44 #include <KXMLGUIFactory>
45
46 #include "BackgroundImage.h"
47 #include "configuration.h"
48 #include "ConfigurationDialogs.h"
49 #include "Commands.h"
50 #include "Document.h"
51 #include "Editor.h"
52 #include "ExtendPatternDlg.h"
53 #include "FilePropertiesDlg.h"
54 #include "Floss.h"
55 #include "FlossScheme.h"
56 #include "ImportImageDlg.h"
57 #include "Palette.h"
58 #include "PaletteManagerDlg.h"
59 #include "PaperSizes.h"
60 #include "Preview.h"
61 #include "PrintSetupDlg.h"
62 #include "QVariantPtr.h"
63 #include "Scale.h"
64 #include "ScaledPixmapLabel.h"
65 #include "SchemeManager.h"
66 #include "SymbolLibrary.h"
67 #include "SymbolManager.h"
68
69
MainWindow()70 MainWindow::MainWindow()
71 : m_printer(nullptr)
72 {
73 setupActions();
74 }
75
76
MainWindow(const QUrl & url)77 MainWindow::MainWindow(const QUrl &url)
78 : m_printer(nullptr)
79 {
80 setupMainWindow();
81 setupLayout();
82 setupDockWindows();
83 setupActions();
84 setupDocument();
85 setupConnections();
86 setupActionDefaults();
87 loadSettings();
88 fileOpen(url);
89 setupActionsFromDocument();
90 setCaption(m_document->url().fileName(), !m_document->undoStack().isClean());
91 this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->hide();
92 }
93
94
MainWindow(const QString & source)95 MainWindow::MainWindow(const QString &source)
96 : m_printer(nullptr)
97 {
98 setupMainWindow();
99 setupLayout();
100 setupDockWindows();
101 setupActions();
102 setupDocument();
103 setupConnections();
104 setupActionDefaults();
105 loadSettings();
106 convertImage(source);
107 setupActionsFromDocument();
108 setCaption(m_document->url().fileName(), !m_document->undoStack().isClean());
109 this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->show();
110 }
111
112
setupMainWindow()113 void MainWindow::setupMainWindow()
114 {
115 setObjectName(QStringLiteral("MainWindow#"));
116 setAutoSaveSettings();
117 }
118
119
setupLayout()120 void MainWindow::setupLayout()
121 {
122 QScrollArea *scrollArea = new QScrollArea();
123 scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
124 scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
125 m_editor = new Editor(scrollArea);
126 scrollArea->installEventFilter(m_editor);
127 scrollArea->setWidget(m_editor);
128
129 m_horizontalScale = m_editor->horizontalScale();
130 m_verticalScale = m_editor->verticalScale();
131
132 QGridLayout *gridLayout = new QGridLayout(this);
133 gridLayout->addWidget(m_horizontalScale, 0, 1);
134 gridLayout->addWidget(m_verticalScale, 1, 0);
135 gridLayout->addWidget(scrollArea, 1, 1);
136
137 QWidget *layout = new QWidget();
138 layout->setLayout(gridLayout);
139
140 setCentralWidget(layout);
141 }
142
143
setupDocument()144 void MainWindow::setupDocument()
145 {
146 m_document = new Document();
147
148 m_editor->setDocument(m_document);
149 m_editor->setPreview(m_preview);
150 m_palette->setDocument(m_document);
151 m_preview->setDocument(m_document);
152 m_history->setStack(&(m_document->undoStack()));
153
154 m_document->addView(m_editor);
155 m_document->addView(m_preview);
156 m_document->addView(m_palette);
157 }
158
159
setupConnections()160 void MainWindow::setupConnections()
161 {
162 KActionCollection *actions = actionCollection();
163
164 connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("edit_undo")), &QAction::setEnabled);
165 connect(&(m_document->undoStack()), &QUndoStack::canUndoChanged, actions->action(QStringLiteral("file_revert")), &QAction::setEnabled);
166 connect(&(m_document->undoStack()), &QUndoStack::canRedoChanged, actions->action(QStringLiteral("edit_redo")), &QAction::setEnabled);
167 connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::clipboardDataChanged);
168 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_cut")), &QAction::setEnabled);
169 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("edit_copy")), &QAction::setEnabled);
170 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorHorizontal")), &QAction::setEnabled);
171 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("mirrorVertical")), &QAction::setEnabled);
172 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate90")), &QAction::setEnabled);
173 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate180")), &QAction::setEnabled);
174 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("rotate270")), &QAction::setEnabled);
175 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("patternCropToSelection")), &QAction::setEnabled);
176 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertColumns")), &QAction::setEnabled);
177 connect(m_editor, &Editor::selectionMade, actionCollection()->action(QStringLiteral("insertRows")), &QAction::setEnabled);
178 connect(&(m_document->undoStack()), &QUndoStack::undoTextChanged, this, &MainWindow::undoTextChanged);
179 connect(&(m_document->undoStack()), &QUndoStack::redoTextChanged, this, &MainWindow::redoTextChanged);
180 connect(&(m_document->undoStack()), &QUndoStack::cleanChanged, this, &MainWindow::documentModified);
181 connect(m_palette, &Palette::colorSelected, m_editor, static_cast<void (Editor::*)()>(&Editor::drawContents));
182 connect(m_palette, static_cast<void (Palette::*)(int, int)>(&Palette::swapColors), this, &MainWindow::paletteSwapColors);
183 connect(m_palette, static_cast<void (Palette::*)(int, int)>(&Palette::replaceColor), this, &MainWindow::paletteReplaceColor);
184 connect(m_palette, &Palette::signalStateChanged, this, static_cast<void (KXmlGuiWindow::*)(const QString &, bool)>(&KXmlGuiWindow::slotStateChanged));
185 connect(m_palette, &Palette::customContextMenuRequested, this, &MainWindow::paletteContextMenu);
186 connect(m_editor, &Editor::changedVisibleCells, m_preview, &Preview::setVisibleCells);
187 connect(m_preview, static_cast<void (Preview::*)(QPoint)>(&Preview::clicked), m_editor, static_cast<void (Editor::*)(const QPoint &)>(&Editor::previewClicked));
188 connect(m_preview, static_cast<void (Preview::*)(QRect)>(&Preview::clicked), m_editor, static_cast<void (Editor::*)(const QRect &)>(&Editor::previewClicked));
189 }
190
191
setupActionDefaults()192 void MainWindow::setupActionDefaults()
193 {
194 KActionCollection *actions = actionCollection();
195
196 actions->action(QStringLiteral("maskStitch"))->setChecked(false);
197 actions->action(QStringLiteral("maskColor"))->setChecked(false);
198 actions->action(QStringLiteral("maskBackstitch"))->setChecked(false);
199 actions->action(QStringLiteral("maskKnot"))->setChecked(false);
200
201 actions->action(QStringLiteral("stitchFull"))->trigger(); // Select full stitch
202
203 actions->action(QStringLiteral("toolPaint"))->trigger(); // Select paint tool
204
205 clipboardDataChanged();
206 }
207
208
~MainWindow()209 MainWindow::~MainWindow()
210 {
211 delete m_printer;
212 }
213
214
editor()215 Editor *MainWindow::editor()
216 {
217 return m_editor;
218 }
219
220
preview()221 Preview *MainWindow::preview()
222 {
223 return m_preview;
224 }
225
226
palette()227 Palette *MainWindow::palette()
228 {
229 return m_palette;
230 }
231
232
queryClose()233 bool MainWindow::queryClose()
234 {
235 if (m_document->undoStack().isClean()) {
236 return true;
237 }
238
239 while (true) {
240 int messageBoxResult = KMessageBox::warningYesNoCancel(this, i18n("Save changes to document?\nSelecting No discards changes."));
241
242 switch (messageBoxResult) {
243 case KMessageBox::Yes :
244 fileSave();
245
246 if (m_document->undoStack().isClean()) {
247 return true;
248 } else {
249 KMessageBox::error(this, i18n("Unable to save the file"));
250 }
251
252 break;
253
254 case KMessageBox::No :
255 return true;
256
257 case KMessageBox::Cancel :
258 return false;
259 }
260 }
261 }
262
263
setupActionsFromDocument()264 void MainWindow::setupActionsFromDocument()
265 {
266 KActionCollection *actions = actionCollection();
267
268 actions->action(QStringLiteral("file_revert"))->setEnabled(!m_document->undoStack().isClean());
269 actions->action(QStringLiteral("edit_undo"))->setEnabled(m_document->undoStack().canUndo());
270 actions->action(QStringLiteral("edit_redo"))->setEnabled(m_document->undoStack().canRedo());
271
272 updateBackgroundImageActionLists();
273 }
274
275
fileNew()276 void MainWindow::fileNew()
277 {
278 MainWindow *window = new MainWindow(QUrl());
279 window->show();
280 }
281
282
fileOpen()283 void MainWindow::fileOpen()
284 {
285 fileOpen(QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl::fromLocalFile(QDir::homePath()), i18n("KXStitch Patterns (*.kxs);;PC Stitch Patterns (*.pat);;All Files (*)")));
286 }
287
288
fileOpen(const QUrl & url)289 void MainWindow::fileOpen(const QUrl &url)
290 {
291 MainWindow *window;
292 bool docEmpty = (m_document->undoStack().isClean() && (m_document->url().toString() == i18n("Untitled")));
293
294 if (url.isValid()) {
295 if (docEmpty) {
296 QTemporaryFile tmpFile;
297
298 if (tmpFile.open()) {
299 tmpFile.close();
300
301 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
302
303 if (job->exec()) {
304 /* In earlier versions of KDE/Qt creating a QDataStream on tmpFile allowed reading the data from the copied file.
305 * Somewhere after KDE 5.55.0/Qt 5.9.7 this no longer possible as tmpFile size() is reported with a length of 0
306 * whereas previously tmpFile size() was reported as the size of the copied file.
307 * Therefore open a new QFile on the temporary file after downloading to allow reading.
308 */
309 QFile reader(tmpFile.fileName());
310 if (reader.open(QIODevice::ReadOnly)) {
311 QDataStream stream(&reader);
312
313 try {
314 m_document->readKXStitch(stream);
315 m_document->setUrl(url);
316 KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
317 action->addUrl(url);
318 action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
319 } catch (const InvalidFile &e) {
320 stream.device()->seek(0);
321
322 try {
323 m_document->readPCStitch(stream);
324 } catch (const InvalidFile &e) {
325 KMessageBox::sorry(nullptr, i18n("The file does not appear to be a recognized cross stitch file."));
326 }
327 } catch (const InvalidFileVersion &e) {
328 KMessageBox::sorry(nullptr, i18n("This version of the file is not supported.\n%1", e.version));
329 } catch (const FailedReadFile &e) {
330 KMessageBox::error(nullptr, i18n("Failed to read the file.\n%1.", e.status));
331 m_document->initialiseNew();
332 }
333
334 setupActionsFromDocument();
335 m_editor->readDocumentSettings();
336 m_preview->readDocumentSettings();
337 m_palette->update();
338 documentModified(true); // this is the clean value true
339
340 reader.close();
341 } else {
342 KMessageBox::error(nullptr, reader.errorString());
343 }
344 } else {
345 KMessageBox::error(nullptr, job->errorString());
346 }
347
348 tmpFile.close();
349 } else {
350 KMessageBox::error(nullptr, tmpFile.errorString());
351 }
352 } else {
353 window = new MainWindow(url);
354 window->show();
355 }
356 }
357 }
358
359
fileSave()360 void MainWindow::fileSave()
361 {
362 QUrl url = m_document->url();
363
364 if (url.toString() == i18n("Untitled")) {
365 fileSaveAs();
366 } else {
367 // ### Why use QUrl everywhere if this only supports local files?
368 QSaveFile file(url.toLocalFile());
369
370 if (file.open(QIODevice::WriteOnly)) {
371 QDataStream stream(&file);
372
373 try {
374 m_document->write(stream);
375
376 if (!file.commit()) {
377 throw FailedWriteFile(stream.status());
378 }
379
380 m_document->undoStack().setClean();
381 } catch (const FailedWriteFile &e) {
382 KMessageBox::error(nullptr, QString(i18n("Failed to save the file.\n%1", file.errorString())));
383 file.cancelWriting();
384 }
385 } else {
386 KMessageBox::error(nullptr, QString(i18n("Failed to open the file.\n%1", file.errorString())));
387 }
388 }
389
390 }
391
392
fileSaveAs()393 void MainWindow::fileSaveAs()
394 {
395 QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Patterns (*.kxs)"));
396
397 if (url.isValid()) {
398 KIO::StatJob *statJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0);
399
400 if (statJob->exec()) {
401 if (KMessageBox::warningYesNo(this, i18n("This file already exists\nDo you want to overwrite it?")) == KMessageBox::No) {
402 return;
403 }
404 }
405
406 m_document->setUrl(url);
407 fileSave();
408 KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
409 action->addUrl(url);
410 action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
411 }
412 }
413
414
fileRevert()415 void MainWindow::fileRevert()
416 {
417 if (!m_document->undoStack().isClean()) {
418 if (KMessageBox::warningYesNo(this, i18n("Revert changes to document?")) == KMessageBox::Yes) {
419 m_document->undoStack().setIndex(m_document->undoStack().cleanIndex());
420 }
421 }
422 }
423
424
filePrintSetup()425 void MainWindow::filePrintSetup()
426 {
427 if (m_printer == nullptr) {
428 m_printer = new QPrinter();
429 }
430
431 QPointer<PrintSetupDlg> printSetupDlg = new PrintSetupDlg(this, m_document, m_printer);
432
433 if (printSetupDlg->exec() == QDialog::Accepted) {
434 m_document->undoStack().push(new UpdatePrinterConfigurationCommand(m_document, printSetupDlg->printerConfiguration()));
435 }
436
437 delete printSetupDlg;
438 }
439
440
filePrint()441 void MainWindow::filePrint()
442 {
443 if (m_printer == nullptr) {
444 filePrintSetup();
445 }
446
447 if (!m_document->printerConfiguration().pages().isEmpty()) {
448 m_printer->setFullPage(true);
449 m_printer->setPrintRange(QPrinter::AllPages);
450 m_printer->setFromTo(1, m_document->printerConfiguration().pages().count());
451
452 QPointer<QPrintDialog> printDialog = new QPrintDialog(m_printer, this);
453
454 if (printDialog->exec() == QDialog::Accepted) {
455 printPages();
456 }
457
458 delete printDialog;
459 } else {
460 KMessageBox::information(this, i18n("There is nothing to print"));
461 }
462 }
463
464
printPages()465 void MainWindow::printPages()
466 {
467 QList<Page *> pages = m_document->printerConfiguration().pages();
468
469 int fromPage = 1;
470 int toPage = pages.count();
471
472 if (m_printer->printRange() == QPrinter::PageRange) {
473 fromPage = m_printer->fromPage();
474 toPage = m_printer->toPage();
475 }
476
477 while (toPage < pages.count()) pages.removeLast();
478 while (--fromPage) pages.removeFirst();
479
480 int totalPages = pages.count();
481
482 const Page *page = (m_printer->pageOrder() == QPrinter::FirstPageFirst)?pages.takeFirst():pages.takeLast();
483
484 m_printer->setPaperSize(page->paperSize()); // DEPRECATED
485 m_printer->setOrientation(page->orientation()); // DEPRECATED
486
487 QPainter painter;
488 painter.begin(m_printer);
489 painter.setRenderHint(QPainter::Antialiasing, true);
490
491 for (int p = 0 ; p < totalPages ;) {
492 int paperWidth = PaperSizes::width(page->paperSize(), page->orientation());
493 int paperHeight = PaperSizes::height(page->paperSize(), page->orientation());
494
495 painter.setWindow(0, 0, paperWidth, paperHeight);
496
497 page->render(m_document, &painter);
498
499 if (++p < totalPages) {
500 page = (m_printer->pageOrder() == QPrinter::FirstPageFirst)?pages.takeFirst():pages.takeLast();
501
502 m_printer->setPaperSize(page->paperSize()); // DEPRECATED
503 m_printer->setOrientation(page->orientation()); // DEPRECATED
504
505 m_printer->newPage();
506 }
507 }
508
509 painter.end();
510 }
511
512
fileImportImage()513 void MainWindow::fileImportImage()
514 {
515 MainWindow *window;
516 bool docEmpty = ((m_document->undoStack().isClean()) && (m_document->url().toString() == i18n("Untitled")));
517 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)"));
518
519 if (url.isValid()) {
520 QTemporaryFile tmpFile;
521
522 if (tmpFile.open()) {
523 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
524
525 if (job->exec()) {
526 if (docEmpty) {
527 convertImage(tmpFile.fileName());
528 this->findChild<QDockWidget *>(QStringLiteral("ImportedImage#"))->show();
529 } else {
530 window = new MainWindow(tmpFile.fileName());
531 window->show();
532 }
533 } else {
534 KMessageBox::error(nullptr, job->errorString());
535 }
536 }
537 }
538 }
539
540
convertImage(const QString & source)541 void MainWindow::convertImage(const QString &source)
542 {
543 Magick::Image image(source.toStdString());
544
545 QMap<int, QColor> documentFlosses;
546 QList<qint16> symbolIndexes = SymbolManager::library(Configuration::palette_DefaultSymbolLibrary())->indexes();
547
548 QPointer<ImportImageDlg> importImageDlg = new ImportImageDlg(this, image);
549
550 if (importImageDlg->exec()) {
551 Magick::Image convertedImage = importImageDlg->convertedImage();
552
553 int imageWidth = convertedImage.columns();
554 int imageHeight = convertedImage.rows();
555 int documentWidth = imageWidth;
556 int documentHeight = imageHeight;
557
558 bool useFractionals = importImageDlg->useFractionals();
559
560 /*
561 * ImageMagick prior to V7 used matte (opacity) to determine if an image has transparency.
562 * 0.0 for transparent to 1.0 for opaque
563 *
564 * ImageMagick V7 now uses alpha (transparency).
565 * 1.0 for transparent to 0.0 for opaque
566 *
567 * Access to pixels has changed too, V7 can use pixelColor to access the color of a particular
568 * pixel, but although this was available in V6, it doesn't appear to produce the same result
569 * and has resulted in black images when importing.
570 */
571 #if MagickLibVersion < 0x700
572 bool hasTransparency = convertedImage.matte();
573 double transparent = 1.0;
574 const Magick::PixelPacket *pixels = convertedImage.getConstPixels(0, 0, imageWidth, imageHeight);
575 #else
576 bool hasTransparency = convertedImage.alpha();
577 double transparent = 0.0;
578 #endif
579
580 bool ignoreColor = importImageDlg->ignoreColor();
581 Magick::Color ignoreColorValue = importImageDlg->ignoreColorValue();
582
583 int pixelCount = imageWidth * imageHeight;
584
585 if (useFractionals) {
586 documentWidth /= 2;
587 documentHeight /= 2;
588 }
589
590 QString schemeName = importImageDlg->flossScheme();
591 FlossScheme *flossScheme = SchemeManager::scheme(schemeName);
592
593 QUndoCommand *importImageCommand = new ImportImageCommand(m_document);
594 new ResizeDocumentCommand(m_document, documentWidth, documentHeight, importImageCommand);
595 new ChangeSchemeCommand(m_document, schemeName, importImageCommand);
596
597 QProgressDialog progress(i18n("Converting to stitches"), i18n("Cancel"), 0, pixelCount, this);
598 progress.setWindowModality(Qt::WindowModal);
599
600 for (int dy = 0 ; dy < imageHeight ; dy++) {
601 progress.setValue(dy * imageWidth);
602 QApplication::processEvents();
603
604 if (progress.wasCanceled()) {
605 delete importImageDlg;
606 delete importImageCommand;
607 return;
608 }
609
610 for (int dx = 0 ; dx < imageWidth ; dx++) {
611 #if MagickLibVersion < 0x700
612 Magick::ColorRGB rgb = Magick::Color(*pixels++);
613 #else
614 Magick::ColorRGB rgb = convertedImage.pixelColor(dx, dy);
615 #endif
616
617 if (hasTransparency && (rgb.alpha() == transparent)) {
618 // ignore this pixel as it is transparent
619 } else {
620 if (!(ignoreColor && (rgb == ignoreColorValue))) {
621 int flossIndex;
622 QColor color((int)(255*rgb.red()), (int)(255*rgb.green()), (int)(255*rgb.blue()));
623
624 for (flossIndex = 0 ; flossIndex < documentFlosses.count() ; ++flossIndex) {
625 if (documentFlosses[flossIndex] == color) {
626 break;
627 }
628 }
629
630 if (flossIndex == documentFlosses.count()) { // reached the end of the list
631 qint16 stitchSymbol = symbolIndexes.takeFirst();
632 Qt::PenStyle backstitchSymbol(Qt::SolidLine);
633 Floss *floss = flossScheme->find(color);
634
635 DocumentFloss *documentFloss = new DocumentFloss(floss->name(), stitchSymbol, backstitchSymbol, Configuration::palette_StitchStrands(), Configuration::palette_BackstitchStrands());
636 documentFloss->setFlossColor(floss->color());
637 new AddDocumentFlossCommand(m_document, flossIndex, documentFloss, importImageCommand);
638 documentFlosses.insert(flossIndex, color);
639 }
640
641 // at this point
642 // flossIndex will be the index for the found color
643 if (useFractionals) {
644 int zone = (dy % 2) * 2 + (dx % 2);
645 new AddStitchCommand(m_document, QPoint(dx / 2, dy / 2), stitchMap[0][zone], flossIndex, importImageCommand);
646 } else {
647 new AddStitchCommand(m_document, QPoint(dx, dy), Stitch::Full, flossIndex, importImageCommand);
648 }
649 }
650 }
651 }
652 }
653
654 new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), importImageDlg->horizontalClothCount(), importImageCommand);
655 new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), importImageDlg->verticalClothCount(), importImageCommand);
656 m_document->undoStack().push(importImageCommand);
657
658 convertPreview(source, importImageDlg->croppedArea());
659 }
660
661 delete importImageDlg;
662 }
663
664
convertPreview(const QString & source,const QRect & croppedArea)665 void MainWindow::convertPreview(const QString &source, const QRect &croppedArea)
666 {
667 QPixmap pixmap;
668 pixmap.load(source);
669 pixmap = pixmap.copy(croppedArea);
670 m_imageLabel->setPixmap(pixmap);
671 }
672
673
fileProperties()674 void MainWindow::fileProperties()
675 {
676 QPointer<FilePropertiesDlg> filePropertiesDlg = new FilePropertiesDlg(this, m_document);
677
678 if (filePropertiesDlg->exec()) {
679 QUndoCommand *cmd = new FilePropertiesCommand(m_document);
680
681 if ((filePropertiesDlg->documentWidth() != m_document->pattern()->stitches().width()) || (filePropertiesDlg->documentHeight() != m_document->pattern()->stitches().height())) {
682 new ResizeDocumentCommand(m_document, filePropertiesDlg->documentWidth(), filePropertiesDlg->documentHeight(), cmd);
683 }
684
685 if (filePropertiesDlg->unitsFormat() != static_cast<Configuration::EnumDocument_UnitsFormat::type>(m_document->property(QStringLiteral("unitsFormat")).toInt())) {
686 new SetPropertyCommand(m_document, QStringLiteral("unitsFormat"), QVariant(filePropertiesDlg->unitsFormat()), cmd);
687 }
688
689 if (filePropertiesDlg->horizontalClothCount() != m_document->property(QStringLiteral("horizontalClothCount")).toDouble()) {
690 new SetPropertyCommand(m_document, QStringLiteral("horizontalClothCount"), QVariant(filePropertiesDlg->horizontalClothCount()), cmd);
691 }
692
693 if (filePropertiesDlg->clothCountLink() != m_document->property(QStringLiteral("clothCountLink")).toBool()) {
694 new SetPropertyCommand(m_document, QStringLiteral("clothCountLink"), QVariant(filePropertiesDlg->clothCountLink()), cmd);
695 }
696
697 if (filePropertiesDlg->verticalClothCount() != m_document->property(QStringLiteral("verticalClothCount")).toDouble()) {
698 new SetPropertyCommand(m_document, QStringLiteral("verticalClothCount"), QVariant(filePropertiesDlg->verticalClothCount()), cmd);
699 }
700
701 if (filePropertiesDlg->clothCountUnits() != static_cast<Configuration::EnumEditor_ClothCountUnits::type>(m_document->property(QStringLiteral("clothCountUnits")).toInt())) {
702 new SetPropertyCommand(m_document, QStringLiteral("clothCountUnits"), QVariant(filePropertiesDlg->clothCountUnits()), cmd);
703 }
704
705 if (filePropertiesDlg->title() != m_document->property(QStringLiteral("title")).toString()) {
706 new SetPropertyCommand(m_document, QStringLiteral("title"), QVariant(filePropertiesDlg->title()), cmd);
707 }
708
709 if (filePropertiesDlg->author() != m_document->property(QStringLiteral("author")).toString()) {
710 new SetPropertyCommand(m_document, QStringLiteral("author"), QVariant(filePropertiesDlg->author()), cmd);
711 }
712
713 if (filePropertiesDlg->copyright() != m_document->property(QStringLiteral("copyright")).toString()) {
714 new SetPropertyCommand(m_document, QStringLiteral("copyright"), QVariant(filePropertiesDlg->copyright()), cmd);
715 }
716
717 if (filePropertiesDlg->fabric() != m_document->property(QStringLiteral("fabric")).toString()) {
718 new SetPropertyCommand(m_document, QStringLiteral("fabric"), QVariant(filePropertiesDlg->fabric()), cmd);
719 }
720
721 if (filePropertiesDlg->fabricColor() != m_document->property(QStringLiteral("fabricColor")).value<QColor>()) {
722 new SetPropertyCommand(m_document, QStringLiteral("fabricColor"), QVariant(filePropertiesDlg->fabricColor()), cmd);
723 }
724
725 if (filePropertiesDlg->instructions() != m_document->property(QStringLiteral("instructions")).toString()) {
726 new SetPropertyCommand(m_document, QStringLiteral("instructions"), QVariant(filePropertiesDlg->instructions()), cmd);
727 }
728
729 if (filePropertiesDlg->flossScheme() != m_document->pattern()->palette().schemeName()) {
730 new ChangeSchemeCommand(m_document, filePropertiesDlg->flossScheme(), cmd);
731 }
732
733 if (cmd->childCount()) {
734 m_document->undoStack().push(cmd);
735 } else {
736 delete cmd;
737 }
738 }
739
740 delete filePropertiesDlg;
741 }
742
743
fileAddBackgroundImage()744 void MainWindow::fileAddBackgroundImage()
745 {
746 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)"));
747
748 if (!url.isEmpty()) {
749 QRect patternArea(0, 0, m_document->pattern()->stitches().width(), m_document->pattern()->stitches().height());
750 QRect selectionArea = m_editor->selectionArea();
751 QSharedPointer<BackgroundImage> backgroundImage(new BackgroundImage(url, (selectionArea.isValid() ? selectionArea : patternArea)));
752
753 if (backgroundImage->isValid()) {
754 m_document->undoStack().push(new AddBackgroundImageCommand(m_document, backgroundImage, this));
755 }
756 }
757 }
758
759
fileRemoveBackgroundImage()760 void MainWindow::fileRemoveBackgroundImage()
761 {
762 QAction *action = qobject_cast<QAction *>(sender());
763 m_document->undoStack().push(new RemoveBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), this));
764 }
765
766
fileClose()767 void MainWindow::fileClose()
768 {
769 if (queryClose()) {
770 m_document->initialiseNew();
771 setupActionsFromDocument();
772 m_editor->readDocumentSettings();
773 m_preview->readDocumentSettings();
774 }
775
776 close();
777 }
778
779
fileQuit()780 void MainWindow::fileQuit()
781 {
782 close();
783 }
784
785
editUndo()786 void MainWindow::editUndo()
787 {
788 m_document->undoStack().undo();
789 }
790
791
editRedo()792 void MainWindow::editRedo()
793 {
794 m_document->undoStack().redo();
795 }
796
797
undoTextChanged(const QString & text)798 void MainWindow::undoTextChanged(const QString &text)
799 {
800 actionCollection()->action(QStringLiteral("edit_undo"))->setText(i18n("Undo %1", text));
801 }
802
803
redoTextChanged(const QString & text)804 void MainWindow::redoTextChanged(const QString &text)
805 {
806 actionCollection()->action(QStringLiteral("edit_redo"))->setText(i18n("Redo %1", text));
807 }
808
809
clipboardDataChanged()810 void MainWindow::clipboardDataChanged()
811 {
812 actionCollection()->action(QStringLiteral("edit_paste"))->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QStringLiteral("application/kxstitch")));
813 }
814
815
paletteManager()816 void MainWindow::paletteManager()
817 {
818 QPointer<PaletteManagerDlg> paletteManagerDlg = new PaletteManagerDlg(this, m_document);
819
820 if (paletteManagerDlg->exec()) {
821 DocumentPalette palette = paletteManagerDlg->palette();
822
823 if (palette != m_document->pattern()->palette()) {
824 m_document->undoStack().push(new UpdateDocumentPaletteCommand(m_document, palette));
825 }
826 }
827
828 delete paletteManagerDlg;
829 }
830
831
paletteShowSymbols(bool show)832 void MainWindow::paletteShowSymbols(bool show)
833 {
834 m_palette->showSymbols(show);
835 }
836
837
paletteClearUnused()838 void MainWindow::paletteClearUnused()
839 {
840 QMap<int, FlossUsage> flossUsage = m_document->pattern()->stitches().flossUsage();
841 QMapIterator<int, DocumentFloss *> flosses(m_document->pattern()->palette().flosses());
842 ClearUnusedFlossesCommand *clearUnusedFlossesCommand = new ClearUnusedFlossesCommand(m_document);
843
844 while (flosses.hasNext()) {
845 flosses.next();
846
847 if (flossUsage[flosses.key()].totalStitches() == 0) {
848 new RemoveDocumentFlossCommand(m_document, flosses.key(), flosses.value(), clearUnusedFlossesCommand);
849 }
850 }
851
852 if (clearUnusedFlossesCommand->childCount()) {
853 m_document->undoStack().push(clearUnusedFlossesCommand);
854 } else {
855 delete clearUnusedFlossesCommand;
856 }
857 }
858
859
paletteCalibrateScheme()860 void MainWindow::paletteCalibrateScheme()
861 {
862 }
863
864
paletteSwapColors(int originalIndex,int replacementIndex)865 void MainWindow::paletteSwapColors(int originalIndex, int replacementIndex)
866 {
867 if (originalIndex != replacementIndex) {
868 m_document->undoStack().push(new PaletteSwapColorCommand(m_document, originalIndex, replacementIndex));
869 }
870 }
871
872
paletteReplaceColor(int originalIndex,int replacementIndex)873 void MainWindow::paletteReplaceColor(int originalIndex, int replacementIndex)
874 {
875 if (originalIndex != replacementIndex) {
876 m_document->undoStack().push(new PaletteReplaceColorCommand(m_document, originalIndex, replacementIndex));
877 }
878 }
879
880
viewFitBackgroundImage()881 void MainWindow::viewFitBackgroundImage()
882 {
883 QAction *action = qobject_cast<QAction *>(sender());
884 m_document->undoStack().push(new FitBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), m_editor->selectionArea()));
885 }
886
887
paletteContextMenu(const QPoint & pos)888 void MainWindow::paletteContextMenu(const QPoint &pos)
889 {
890 static_cast<QMenu *>(guiFactory()->container(QStringLiteral("PalettePopup"), this))->popup(qobject_cast<QWidget *>(sender())->mapToGlobal(pos));
891 }
892
893
viewShowBackgroundImage()894 void MainWindow::viewShowBackgroundImage()
895 {
896 QAction *action = qobject_cast<QAction *>(sender());
897 m_document->undoStack().push(new ShowBackgroundImageCommand(m_document, action->data().value<QSharedPointer<BackgroundImage>>(), action->isChecked()));
898 }
899
900
patternExtend()901 void MainWindow::patternExtend()
902 {
903 QPointer<ExtendPatternDlg> extendPatternDlg = new ExtendPatternDlg(this);
904
905 if (extendPatternDlg->exec()) {
906 int top = extendPatternDlg->top();
907 int left = extendPatternDlg->left();
908 int right = extendPatternDlg->right();
909 int bottom = extendPatternDlg->bottom();
910
911 if (top || left || right || bottom) {
912 m_document->undoStack().push(new ExtendPatternCommand(m_document, top, left, right, bottom));
913 }
914 }
915
916 delete extendPatternDlg;
917 }
918
919
patternCentre()920 void MainWindow::patternCentre()
921 {
922 m_document->undoStack().push(new CentrePatternCommand(m_document));
923 }
924
925
patternCrop()926 void MainWindow::patternCrop()
927 {
928 m_document->undoStack().push(new CropToPatternCommand(m_document));
929 }
930
931
patternCropToSelection()932 void MainWindow::patternCropToSelection()
933 {
934 m_document->undoStack().push(new CropToSelectionCommand(m_document, m_editor->selectionArea()));
935 }
936
937
insertColumns()938 void MainWindow::insertColumns()
939 {
940 m_document->undoStack().push(new InsertColumnsCommand(m_document, m_editor->selectionArea()));
941 }
942
943
insertRows()944 void MainWindow::insertRows()
945 {
946 m_document->undoStack().push(new InsertRowsCommand(m_document, m_editor->selectionArea()));
947 }
948
949
preferences()950 void MainWindow::preferences()
951 {
952 if (KConfigDialog::showDialog(QStringLiteral("preferences"))) {
953 return;
954 }
955
956 KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("preferences"), Configuration::self());
957 dialog->setFaceType(KPageDialog::List);
958
959 dialog->addPage(new EditorConfigPage(0, QStringLiteral("EditorConfigPage")), i18nc("The Editor config page", "Editor"), QStringLiteral("preferences-desktop"));
960 dialog->addPage(new PatternConfigPage(0, QStringLiteral("PatternConfigPage")), i18n("Pattern"), QStringLiteral("ksnapshot"));
961 PaletteConfigPage *paletteConfigPage = new PaletteConfigPage(0, QStringLiteral("PaletteConfigPage"));
962 dialog->addPage(paletteConfigPage, i18n("Palette"), QStringLiteral("preferences-desktop-color"));
963 dialog->addPage(new ImportConfigPage(0, QStringLiteral("ImportConfigPage")), i18n("Import"), QStringLiteral("insert-image"));
964 dialog->addPage(new LibraryConfigPage(0, QStringLiteral("LibraryConfigPage")), i18n("Library"), QStringLiteral("accessories-dictionary"));
965 dialog->addPage(new PrinterConfigPage(0, QStringLiteral("PrinterConfigPage")), i18n("Printer Configuration"), QStringLiteral("preferences-desktop-printer"));
966
967 connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::settingsChanged);
968
969 dialog->show();
970 }
971
972
settingsChanged()973 void MainWindow::settingsChanged()
974 {
975 QList<QUndoCommand *> documentChanges;
976 ConfigurationCommand *configurationCommand = new ConfigurationCommand(this);
977
978 if (m_document->property(QStringLiteral("cellHorizontalGrouping")) != Configuration::editor_CellHorizontalGrouping()) {
979 documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("cellHorizontalGrouping"), Configuration::editor_CellHorizontalGrouping(), configurationCommand));
980 }
981
982 if (m_document->property(QStringLiteral("cellVerticalGrouping")) != Configuration::editor_CellVerticalGrouping()) {
983 documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("cellVerticalGrouping"), Configuration::editor_CellVerticalGrouping(), configurationCommand));
984 }
985
986 if (m_document->property(QStringLiteral("thickLineColor")) != Configuration::editor_ThickLineColor()) {
987 documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("thickLineColor"), Configuration::editor_ThickLineColor(), configurationCommand));
988 }
989
990 if (m_document->property(QStringLiteral("thinLineColor")) != Configuration::editor_ThinLineColor()) {
991 documentChanges.append(new SetPropertyCommand(m_document, QStringLiteral("thinLineColor"), Configuration::editor_ThinLineColor(), configurationCommand));
992 }
993
994 if (documentChanges.count()) {
995 m_document->undoStack().push(configurationCommand);
996 } else {
997 delete configurationCommand;
998 }
999
1000 loadSettings();
1001 }
1002
1003
loadSettings()1004 void MainWindow::loadSettings()
1005 {
1006 m_horizontalScale->setMinimumSize(0, Configuration::editor_HorizontalScaleHeight());
1007 m_verticalScale->setMinimumSize(Configuration::editor_VerticalScaleWidth(), 0);
1008 m_horizontalScale->setCellGrouping(Configuration::editor_CellHorizontalGrouping());
1009 m_verticalScale->setCellGrouping(Configuration::editor_CellVerticalGrouping());
1010
1011 m_editor->loadSettings();
1012 m_preview->loadSettings();
1013 m_palette->loadSettings();
1014
1015 KActionCollection *actions = actionCollection();
1016
1017 actions->action(QStringLiteral("makesCopies"))->setChecked(Configuration::tool_MakesCopies());
1018
1019 actions->action(QStringLiteral("colorHighlight"))->setChecked(Configuration::renderer_ColorHilight());
1020
1021 actions->action(QStringLiteral("renderStitches"))->setChecked(Configuration::renderer_RenderStitches());
1022 actions->action(QStringLiteral("renderBackstitches"))->setChecked(Configuration::renderer_RenderBackstitches());
1023 actions->action(QStringLiteral("renderFrenchKnots"))->setChecked(Configuration::renderer_RenderFrenchKnots());
1024 actions->action(QStringLiteral("renderGrid"))->setChecked(Configuration::renderer_RenderGrid());
1025 actions->action(QStringLiteral("renderBackgroundImages"))->setChecked(Configuration::renderer_RenderBackgroundImages());
1026
1027 switch (Configuration::editor_FormatScalesAs()) {
1028 case Configuration::EnumEditor_FormatScalesAs::Stitches:
1029 actions->action(QStringLiteral("formatScalesAsStitches"))->trigger();
1030 break;
1031
1032 case Configuration::EnumEditor_FormatScalesAs::Inches:
1033 actions->action(QStringLiteral("formatScalesAsInches"))->trigger();
1034 break;
1035
1036 case Configuration::EnumEditor_FormatScalesAs::Centimeters:
1037 actions->action(QStringLiteral("formatScalesAsCentimeters"))->trigger();
1038 break;
1039
1040 default:
1041 break;
1042 }
1043
1044 switch (Configuration::renderer_RenderStitchesAs()) {
1045 case Configuration::EnumRenderer_RenderStitchesAs::Stitches:
1046 actions->action(QStringLiteral("renderStitchesAsRegularStitches"))->trigger();
1047 break;
1048
1049 case Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols:
1050 actions->action(QStringLiteral("renderStitchesAsBlackWhiteSymbols"))->trigger();
1051 break;
1052
1053 case Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols:
1054 actions->action(QStringLiteral("renderStitchesAsColorSymbols"))->trigger();
1055 break;
1056
1057 case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks:
1058 actions->action(QStringLiteral("renderStitchesAsColorBlocks"))->trigger();
1059 break;
1060
1061 case Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols:
1062 actions->action(QStringLiteral("renderStitchesAsColorBlocksSymbols"))->trigger();
1063 break;
1064
1065 default:
1066 break;
1067 }
1068
1069 switch (Configuration::renderer_RenderBackstitchesAs()) {
1070 case Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines:
1071 actions->action(QStringLiteral("renderBackstitchesAsColorLines"))->trigger();
1072 break;
1073
1074 case Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols:
1075 actions->action(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"))->trigger();
1076 break;
1077
1078 default:
1079 break;
1080 }
1081
1082 switch (Configuration::renderer_RenderKnotsAs()) {
1083 case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks:
1084 actions->action(QStringLiteral("renderKnotsAsColorBlocks"))->trigger();
1085 break;
1086
1087 case Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols:
1088 actions->action(QStringLiteral("renderKnotsAsColorBlocksSymbols"))->trigger();
1089 break;
1090
1091 case Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols:
1092 actions->action(QStringLiteral("renderKnotsAsColorSymbols"))->trigger();
1093 break;
1094
1095 case Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols:
1096 actions->action(QStringLiteral("renderKnotsAsBlackWhiteSymbols"))->trigger();
1097 break;
1098
1099 default:
1100 break;
1101 }
1102
1103 actions->action(QStringLiteral("paletteShowSymbols"))->setChecked(Configuration::palette_ShowSymbols());
1104 }
1105
1106
documentModified(bool clean)1107 void MainWindow::documentModified(bool clean)
1108 {
1109 setCaption(m_document->url().fileName(), !clean);
1110 }
1111
1112
setupActions()1113 void MainWindow::setupActions()
1114 {
1115 QAction *action;
1116 QActionGroup *actionGroup;
1117
1118 KActionCollection *actions = actionCollection();
1119
1120 // File menu
1121 KStandardAction::openNew(this, &MainWindow::fileNew, actions);
1122 KStandardAction::open(this, static_cast<void (MainWindow::*)()>(&MainWindow::fileOpen), actions);
1123 KStandardAction::openRecent(this, static_cast<void (MainWindow::*)(const QUrl &)>(&MainWindow::fileOpen), actions)->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
1154 // Edit menu
1155 KStandardAction::undo(this, &MainWindow::editUndo, actions);
1156 KStandardAction::redo(this, &MainWindow::editRedo, actions);
1157 KStandardAction::cut(m_editor, &Editor::editCut, actions);
1158 actions->action(QStringLiteral("edit_cut"))->setEnabled(false);
1159 KStandardAction::copy(m_editor, &Editor::editCopy, actions);
1160 actions->action(QStringLiteral("edit_copy"))->setEnabled(false);
1161 KStandardAction::paste(m_editor, &Editor::editPaste, actions);
1162
1163 action = new QAction(this);
1164 action->setText(i18n("Mirror/Rotate makes copies"));
1165 action->setCheckable(true);
1166 connect(action, &QAction::triggered, m_editor, &Editor::setMakesCopies);
1167 actions->addAction(QStringLiteral("makesCopies"), action);
1168
1169 action = new QAction(this);
1170 action->setText(i18n("Horizontally"));
1171 action->setData(Qt::Horizontal);
1172 connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection);
1173 action->setEnabled(false);
1174 actions->addAction(QStringLiteral("mirrorHorizontal"), action);
1175
1176 action = new QAction(this);
1177 action->setText(i18n("Vertically"));
1178 action->setData(Qt::Vertical);
1179 connect(action, &QAction::triggered, m_editor, &Editor::mirrorSelection);
1180 action->setEnabled(false);
1181 actions->addAction(QStringLiteral("mirrorVertical"), action);
1182
1183 action = new QAction(this);
1184 action->setText(i18n("90 Degrees"));
1185 action->setData(StitchData::Rotate90);
1186 connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1187 action->setEnabled(false);
1188 actions->addAction(QStringLiteral("rotate90"), action);
1189
1190 action = new QAction(this);
1191 action->setText(i18n("180 Degrees"));
1192 action->setData(StitchData::Rotate180);
1193 connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1194 action->setEnabled(false);
1195 actions->addAction(QStringLiteral("rotate180"), action);
1196
1197 action = new QAction(this);
1198 action->setText(i18n("270 Degrees"));
1199 action->setData(StitchData::Rotate270);
1200 connect(action, &QAction::triggered, m_editor, &Editor::rotateSelection);
1201 action->setEnabled(false);
1202 actions->addAction(QStringLiteral("rotate270"), action);
1203
1204 // Selection mask sub menu
1205 action = new QAction(this);
1206 action->setText(i18n("Stitch Mask"));
1207 action->setCheckable(true);
1208 connect(action, &QAction::triggered, m_editor, &Editor::setMaskStitch);
1209 actions->addAction(QStringLiteral("maskStitch"), action);
1210
1211 action = new QAction(this);
1212 action->setText(i18n("Color Mask"));
1213 action->setCheckable(true);
1214 connect(action, &QAction::triggered, m_editor, &Editor::setMaskColor);
1215 actions->addAction(QStringLiteral("maskColor"), action);
1216
1217 action = new QAction(this);
1218 action->setText(i18n("Exclude Backstitches"));
1219 action->setCheckable(true);
1220 connect(action, &QAction::triggered, m_editor, &Editor::setMaskBackstitch);
1221 actions->addAction(QStringLiteral("maskBackstitch"), action);
1222
1223 action = new QAction(this);
1224 action->setText(i18n("Exclude Knots"));
1225 action->setCheckable(true);
1226 connect(action, &QAction::triggered, m_editor, &Editor::setMaskKnot);
1227 actions->addAction(QStringLiteral("maskKnot"), action);
1228
1229
1230 // View menu
1231 KStandardAction::zoomIn(m_editor, &Editor::zoomIn, actions);
1232 KStandardAction::zoomOut(m_editor, &Editor::zoomOut, actions);
1233 KStandardAction::actualSize(m_editor, &Editor::actualSize, actions);
1234 action = KStandardAction::fitToPage(m_editor, &Editor::fitToPage, actions);
1235 action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best")));
1236 action = KStandardAction::fitToWidth(m_editor, &Editor::fitToWidth, actions);
1237 action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-width")));
1238 action = KStandardAction::fitToHeight(m_editor, &Editor::fitToHeight, actions);
1239 action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-height")));
1240
1241 // Entries for Show/Hide Preview and Palette dock windows are added dynamically
1242 // Entries for Show/Hide and Remove background images are added dynamically
1243
1244
1245 // Stitches Menu
1246 actionGroup = new QActionGroup(this);
1247 actionGroup->setExclusive(true);
1248
1249 action = new QAction(this);
1250 action->setText(i18n("Quarter Stitch"));
1251 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-quarter-stitch")));
1252 action->setCheckable(true);
1253 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchQuarter); });
1254 actions->addAction(QStringLiteral("stitchQuarter"), action);
1255 actionGroup->addAction(action);
1256
1257 action = new QAction(this);
1258 action->setText(i18n("Half Stitch"));
1259 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-half-stitch")));
1260 action->setCheckable(true);
1261 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchHalf); });
1262 actions->addAction(QStringLiteral("stitchHalf"), action);
1263 actionGroup->addAction(action);
1264
1265 action = new QAction(this);
1266 action->setText(i18n("3 Quarter Stitch"));
1267 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-3quarter-stitch")));
1268 action->setCheckable(true);
1269 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::Stitch3Quarter); });
1270 actions->addAction(QStringLiteral("stitch3Quarter"), action);
1271 actionGroup->addAction(action);
1272
1273 action = new QAction(this);
1274 action->setText(i18n("Full Stitch"));
1275 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-full-stitch")));
1276 action->setCheckable(true);
1277 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchFull); });
1278 actions->addAction(QStringLiteral("stitchFull"), action);
1279 actionGroup->addAction(action);
1280
1281 action = new QAction(this);
1282 action->setText(i18n("Small Half Stitch"));
1283 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-small-half-stitch")));
1284 action->setCheckable(true);
1285 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchSmallHalf); });
1286 actions->addAction(QStringLiteral("stitchSmallHalf"), action);
1287 actionGroup->addAction(action);
1288
1289 action = new QAction(this);
1290 action->setText(i18n("Small Full Stitch"));
1291 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-small-full-stitch")));
1292 action->setCheckable(true);
1293 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchSmallFull); });
1294 actions->addAction(QStringLiteral("stitchSmallFull"), action);
1295 actionGroup->addAction(action);
1296
1297 action = new QAction(this);
1298 action->setText(i18n("French Knot"));
1299 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-frenchknot")));
1300 action->setCheckable(true);
1301 connect(action, &QAction::triggered, m_editor, [=]() { m_editor->selectStitch(Editor::StitchFrenchKnot); });
1302 actions->addAction(QStringLiteral("stitchFrenchKnot"), action);
1303 actionGroup->addAction(action);
1304
1305
1306 // Tools Menu
1307 actionGroup = new QActionGroup(this);
1308 actionGroup->setExclusive(true);
1309
1310 action = new QAction(this);
1311 action->setText(i18n("Paint"));
1312 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-brush")));
1313 action->setCheckable(true);
1314 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolPaint);});
1315 actions->addAction(QStringLiteral("toolPaint"), action);
1316 actionGroup->addAction(action);
1317
1318 action = new QAction(this);
1319 action->setText(i18n("Draw"));
1320 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-freehand")));
1321 action->setCheckable(true);
1322 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolDraw);});
1323 actions->addAction(QStringLiteral("toolDraw"), action);
1324 actionGroup->addAction(action);
1325
1326 action = new QAction(this);
1327 action->setText(i18n("Erase"));
1328 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-eraser")));
1329 action->setCheckable(true);
1330 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolErase);});
1331 actions->addAction(QStringLiteral("toolErase"), action);
1332 actionGroup->addAction(action);
1333
1334 action = new QAction(this);
1335 action->setText(i18n("Draw Rectangle"));
1336 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-rectangle")));
1337 action->setCheckable(true);
1338 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolRectangle);});
1339 actions->addAction(QStringLiteral("toolRectangle"), action);
1340 actionGroup->addAction(action);
1341
1342 action = new QAction(this);
1343 action->setText(i18n("Fill Rectangle"));
1344 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-draw-rectangle-filled")));
1345 action->setCheckable(true);
1346 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillRectangle);});
1347 actions->addAction(QStringLiteral("toolFillRectangle"), action);
1348 actionGroup->addAction(action);
1349
1350 action = new QAction(this);
1351 action->setText(i18n("Draw Ellipse"));
1352 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-ellipse")));
1353 action->setCheckable(true);
1354 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolEllipse);});
1355 actions->addAction(QStringLiteral("toolEllipse"), action);
1356 actionGroup->addAction(action);
1357
1358 action = new QAction(this);
1359 action->setText(i18n("Fill Ellipse"));
1360 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-draw-ellipse-filled")));
1361 action->setCheckable(true);
1362 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillEllipse);});
1363 actions->addAction(QStringLiteral("toolFillEllipse"), action);
1364 actionGroup->addAction(action);
1365
1366 action = new QAction(this);
1367 action->setText(i18n("Fill Polygon"));
1368 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-polyline")));
1369 action->setCheckable(true);
1370 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolFillPolygon);});
1371 actions->addAction(QStringLiteral("toolFillPolygon"), action);
1372 actionGroup->addAction(action);
1373
1374 action = new QAction(this);
1375 action->setText(i18n("Text"));
1376 action->setIcon(QIcon::fromTheme(QStringLiteral("draw-text")));
1377 action->setCheckable(true);
1378 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolText);});
1379 actions->addAction(QStringLiteral("toolText"), action);
1380 actionGroup->addAction(action);
1381
1382 action = new QAction(this);
1383 action->setText(i18n("Alphabet"));
1384 action->setIcon(QIcon::fromTheme(QStringLiteral("text-field")));
1385 action->setCheckable(true);
1386 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolAlphabet);});
1387 actions->addAction(QStringLiteral("toolAlphabet"), action);
1388 actionGroup->addAction(action);
1389
1390 action = new QAction(this);
1391 action->setText(i18nc("Select an area of the pattern", "Select"));
1392 action->setIcon(QIcon::fromTheme(QStringLiteral("select-rectangular")));
1393 action->setCheckable(true);
1394 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolSelect);});
1395 actions->addAction(QStringLiteral("toolSelectRectangle"), action);
1396 actionGroup->addAction(action);
1397
1398 action = new QAction(this);
1399 action->setText(i18n("Backstitch"));
1400 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-backstitch")));
1401 action->setCheckable(true);
1402 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolBackstitch);});
1403 actions->addAction(QStringLiteral("toolBackstitch"), action);
1404 actionGroup->addAction(action);
1405
1406 action = new QAction(this);
1407 action->setText(i18n("Color Picker"));
1408 action->setIcon(QIcon::fromTheme(QStringLiteral("color-picker")));
1409 action->setCheckable(true);
1410 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->selectTool(Editor::ToolColorPicker);});
1411 actions->addAction(QStringLiteral("toolColorPicker"), action);
1412 actionGroup->addAction(action);
1413
1414
1415 // Palette Menu
1416 action = new QAction(this);
1417 action->setText(i18n("Palette Manager..."));
1418 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-color-add")));
1419 connect(action, &QAction::triggered, this, &MainWindow::paletteManager);
1420 actions->addAction(QStringLiteral("paletteManager"), action);
1421
1422 action = new QAction(this);
1423 action->setText(i18n("Show Symbols"));
1424 action->setCheckable(true);
1425 connect(action, &QAction::toggled, this, &MainWindow::paletteShowSymbols);
1426 actions->addAction(QStringLiteral("paletteShowSymbols"), action);
1427
1428 action = new QAction(this);
1429 action->setText(i18n("Clear Unused"));
1430 connect(action, &QAction::triggered, this, &MainWindow::paletteClearUnused);
1431 actions->addAction(QStringLiteral("paletteClearUnused"), action);
1432
1433 action = new QAction(this);
1434 action->setText(i18n("Calibrate Scheme..."));
1435 connect(action, &QAction::triggered, this, &MainWindow::paletteCalibrateScheme);
1436 actions->addAction(QStringLiteral("paletteCalibrateScheme"), action);
1437
1438 action = new QAction(this);
1439 action->setText(i18n("Swap Colors"));
1440 connect(action, &QAction::triggered, m_palette, static_cast<void (Palette::*)()>(&Palette::swapColors));
1441 actions->addAction(QStringLiteral("paletteSwapColors"), action);
1442
1443 action = new QAction(this);
1444 action->setText(i18n("Replace Colors"));
1445 connect(action, &QAction::triggered, m_palette, static_cast<void (Palette::*)()>(&Palette::replaceColor));
1446 actions->addAction(QStringLiteral("paletteReplaceColor"), action);
1447
1448
1449 // Pattern Menu
1450 action = new QAction(this);
1451 action->setText(i18n("Extend Pattern..."));
1452 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-extend-pattern")));
1453 connect(action, &QAction::triggered, this, &MainWindow::patternExtend);
1454 actions->addAction(QStringLiteral("patternExtend"), action);
1455
1456 action = new QAction(this);
1457 action->setText(i18n("Center Pattern"));
1458 action->setIcon(QIcon::fromTheme(QStringLiteral("kxstitch-center-pattern")));
1459 connect(action, &QAction::triggered, this, &MainWindow::patternCentre);
1460 actions->addAction(QStringLiteral("patternCentre"), action);
1461
1462 action = new QAction(this);
1463 action->setText(i18n("Crop Canvas to Pattern"));
1464 connect(action, &QAction::triggered, this, &MainWindow::patternCrop);
1465 actions->addAction(QStringLiteral("patternCrop"), action);
1466
1467 action = new QAction(this);
1468 action->setText(i18n("Crop Canvas to Selection"));
1469 action->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop")));
1470 connect(action, &QAction::triggered, this, &MainWindow::patternCropToSelection);
1471 action->setEnabled(false);
1472 actions->addAction(QStringLiteral("patternCropToSelection"), action);
1473
1474 action = new QAction(this);
1475 action->setText(i18n("Insert Rows"));
1476 connect(action, &QAction::triggered, this, &MainWindow::insertRows);
1477 action->setEnabled(false);
1478 actions->addAction(QStringLiteral("insertRows"), action);
1479
1480 action = new QAction(this);
1481 action->setText(i18n("Insert Columns"));
1482 connect(action, &QAction::triggered, this, &MainWindow::insertColumns);
1483 action->setEnabled(false);
1484 actions->addAction(QStringLiteral("insertColumns"), action);
1485
1486
1487 // Library Menu
1488 action = new QAction(this);
1489 action->setText(i18n("Library Manager..."));
1490 connect(action, &QAction::triggered, m_editor, &Editor::libraryManager);
1491 actions->addAction(QStringLiteral("libraryManager"), action);
1492
1493 // Settings Menu
1494 KStandardAction::preferences(this, &MainWindow::preferences, actions);
1495 // formatScalesAs
1496 actionGroup = new QActionGroup(this);
1497 actionGroup->setExclusive(true);
1498
1499 action = new QAction(this);
1500 action->setText(i18n("Stitches"));
1501 action->setCheckable(true);
1502 connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsStitches);
1503 actions->addAction(QStringLiteral("formatScalesAsStitches"), action);
1504 actionGroup->addAction(action);
1505
1506 action = new QAction(this);
1507 action->setText(i18n("Centimeters"));
1508 action->setCheckable(true);
1509 connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsCentimeters);
1510 actions->addAction(QStringLiteral("formatScalesAsCentimeters"), action);
1511 actionGroup->addAction(action);
1512
1513 action = new QAction(this);
1514 action->setText(i18n("Inches"));
1515 action->setCheckable(true);
1516 connect(action, &QAction::triggered, m_editor, &Editor::formatScalesAsInches);
1517 actions->addAction(QStringLiteral("formatScalesAsInches"), action);
1518 actionGroup->addAction(action);
1519
1520 // ShowStitchesAs
1521 actionGroup = new QActionGroup(this);
1522 actionGroup->setExclusive(true);
1523
1524 action = new QAction(this);
1525 action->setText(i18n("Regular Stitches"));
1526 action->setCheckable(true);
1527 action->setChecked(true);
1528 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::Stitches);});
1529 actions->addAction(QStringLiteral("renderStitchesAsRegularStitches"), action);
1530 actionGroup->addAction(action);
1531
1532 action = new QAction(this);
1533 action->setText(i18n("Black & White Symbols"));
1534 action->setCheckable(true);
1535 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::BlackWhiteSymbols);});
1536 actions->addAction(QStringLiteral("renderStitchesAsBlackWhiteSymbols"), action);
1537 actionGroup->addAction(action);
1538
1539 action = new QAction(this);
1540 action->setText(i18n("Color Symbols"));
1541 action->setCheckable(true);
1542 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorSymbols);});
1543 actions->addAction(QStringLiteral("renderStitchesAsColorSymbols"), action);
1544 actionGroup->addAction(action);
1545
1546 action = new QAction(this);
1547 action->setText(i18n("Color Blocks"));
1548 action->setCheckable(true);
1549 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocks);});
1550 actions->addAction(QStringLiteral("renderStitchesAsColorBlocks"), action);
1551 actionGroup->addAction(action);
1552
1553 action = new QAction(this);
1554 action->setText(i18n("Color Blocks & Symbols"));
1555 action->setCheckable(true);
1556 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderStitchesAs(Configuration::EnumRenderer_RenderStitchesAs::ColorBlocksSymbols);});
1557 actions->addAction(QStringLiteral("renderStitchesAsColorBlocksSymbols"), action);
1558 actionGroup->addAction(action);
1559
1560 // ShowBackstitchesAs
1561 actionGroup = new QActionGroup(this);
1562 actionGroup->setExclusive(true);
1563
1564 action = new QAction(this);
1565 action->setText(i18n("Color Lines"));
1566 action->setCheckable(true);
1567 action->setChecked(true);
1568 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::ColorLines);});
1569 actions->addAction(QStringLiteral("renderBackstitchesAsColorLines"), action);
1570 actionGroup->addAction(action);
1571
1572 action = new QAction(this);
1573 action->setText(i18n("Black & White Symbols"));
1574 action->setCheckable(true);
1575 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderBackstitchesAs(Configuration::EnumRenderer_RenderBackstitchesAs::BlackWhiteSymbols);});
1576 actions->addAction(QStringLiteral("renderBackstitchesAsBlackWhiteSymbols"), action);
1577 actionGroup->addAction(action);
1578
1579 // ShowKnotsAs
1580 actionGroup = new QActionGroup(this);
1581 actionGroup->setExclusive(true);
1582
1583 action = new QAction(this);
1584 action->setText(i18n("Color Blocks"));
1585 action->setCheckable(true);
1586 action->setChecked(true);
1587 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocks);});
1588 actions->addAction(QStringLiteral("renderKnotsAsColorBlocks"), action);
1589 actionGroup->addAction(action);
1590
1591 action = new QAction(this);
1592 action->setText(i18n("Color Blocks & Symbols"));
1593 action->setCheckable(true);
1594 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorBlocksSymbols);});
1595 actions->addAction(QStringLiteral("renderKnotsAsColorBlocksSymbols"), action);
1596 actionGroup->addAction(action);
1597
1598 action = new QAction(this);
1599 action->setText(i18n("Color Symbols"));
1600 action->setCheckable(true);
1601 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::ColorSymbols);});
1602 actions->addAction(QStringLiteral("renderKnotsAsColorSymbols"), action);
1603 actionGroup->addAction(action);
1604
1605 action = new QAction(this);
1606 action->setText(i18n("Black & White Symbols"));
1607 action->setCheckable(true);
1608 connect(action, &QAction::triggered, m_editor, [=]() {m_editor->renderKnotsAs(Configuration::EnumRenderer_RenderKnotsAs::BlackWhiteSymbols);});
1609 actions->addAction(QStringLiteral("renderKnotsAsBlackWhiteSymbols"), action);
1610 actionGroup->addAction(action);
1611
1612
1613 action = new QAction(this);
1614 action->setText(i18n("Color Highlight"));
1615 action->setCheckable(true);
1616 connect(action, &QAction::toggled, m_editor, &Editor::colorHighlight);
1617 actions->addAction(QStringLiteral("colorHighlight"), action);
1618
1619 action = new QAction(this);
1620 action->setText(i18n("Show Stitches"));
1621 action->setCheckable(true);
1622 connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderStitches));
1623 actions->addAction(QStringLiteral("renderStitches"), action);
1624
1625 action = new QAction(this);
1626 action->setText(i18n("Show Backstitches"));
1627 action->setCheckable(true);
1628 connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderBackstitches));
1629 actions->addAction(QStringLiteral("renderBackstitches"), action);
1630
1631 action = new QAction(this);
1632 action->setText(i18n("Show French Knots"));
1633 action->setCheckable(true);
1634 connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderFrenchKnots));
1635 actions->addAction(QStringLiteral("renderFrenchKnots"), action);
1636
1637 action = new QAction(this);
1638 action->setText(i18n("Show Grid"));
1639 action->setCheckable(true);
1640 connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderGrid));
1641 actions->addAction(QStringLiteral("renderGrid"), action);
1642
1643 action = new QAction(this);
1644 action->setText(i18n("Show Background Images"));
1645 action->setCheckable(true);
1646 connect(action, &QAction::toggled, m_editor, static_cast<void (Editor::*)(bool)>(&Editor::renderBackgroundImages));
1647 actions->addAction(QStringLiteral("renderBackgroundImages"), action);
1648
1649 m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches")));
1650 m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters")));
1651 m_horizontalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches")));
1652
1653 m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsStitches")));
1654 m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsCentimeters")));
1655 m_verticalScale->addAction(actions->action(QStringLiteral("formatScalesAsInches")));
1656
1657 setupGUI(KXmlGuiWindow::Default, QStringLiteral("kxstitchui.rc"));
1658 }
1659
1660
updateBackgroundImageActionLists()1661 void MainWindow::updateBackgroundImageActionLists()
1662 {
1663 auto backgroundImages = m_document->backgroundImages().backgroundImages();
1664
1665 unplugActionList(QStringLiteral("removeBackgroundImageActions"));
1666 unplugActionList(QStringLiteral("fitBackgroundImageActions"));
1667 unplugActionList(QStringLiteral("showBackgroundImageActions"));
1668
1669 QList<QAction *> removeBackgroundImageActions;
1670 QList<QAction *> fitBackgroundImageActions;
1671 QList<QAction *> showBackgroundImageActions;
1672
1673 while (backgroundImages.hasNext()) {
1674 QSharedPointer<BackgroundImage> backgroundImage = backgroundImages.next();
1675
1676 QAction *action = new QAction(backgroundImage->url().fileName(), this);
1677 action->setData(QVariant::fromValue(backgroundImage));
1678 action->setIcon(backgroundImage->icon());
1679 connect(action, &QAction::triggered, this, &MainWindow::fileRemoveBackgroundImage);
1680 removeBackgroundImageActions.append(action);
1681
1682 action = new QAction(backgroundImage->url().fileName(), this);
1683 action->setData(QVariant::fromValue(backgroundImage));
1684 action->setIcon(backgroundImage->icon());
1685 connect(action, &QAction::triggered, this, &MainWindow::viewFitBackgroundImage);
1686 fitBackgroundImageActions.append(action);
1687
1688 action = new QAction(backgroundImage->url().fileName(), this);
1689 action->setData(QVariant::fromValue(backgroundImage));
1690 action->setIcon(backgroundImage->icon());
1691 action->setCheckable(true);
1692 action->setChecked(backgroundImage->isVisible());
1693 connect(action, &QAction::triggered, this, &MainWindow::viewShowBackgroundImage);
1694 showBackgroundImageActions.append(action);
1695 }
1696
1697 plugActionList(QStringLiteral("removeBackgroundImageActions"), removeBackgroundImageActions);
1698 plugActionList(QStringLiteral("fitBackgroundImageActions"), fitBackgroundImageActions);
1699 plugActionList(QStringLiteral("showBackgroundImageActions"), showBackgroundImageActions);
1700 }
1701
1702
setupDockWindows()1703 void MainWindow::setupDockWindows()
1704 {
1705 QDockWidget *dock = new QDockWidget(i18n("Preview"), this);
1706 dock->setObjectName(QStringLiteral("PreviewDock#"));
1707 dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1708 QScrollArea *scrollArea = new QScrollArea();
1709 m_preview = new Preview(scrollArea);
1710 scrollArea->setWidget(m_preview);
1711 scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1712 scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1713 scrollArea->setMinimumSize(std::min(300, m_preview->width()), std::min(400, m_preview->height()));
1714 dock->setWidget(scrollArea);
1715 addDockWidget(Qt::LeftDockWidgetArea, dock);
1716 actionCollection()->addAction(QStringLiteral("showPreviewDockWidget"), dock->toggleViewAction());
1717
1718 dock = new QDockWidget(i18n("Palette"), this);
1719 dock->setObjectName(QStringLiteral("PaletteDock#"));
1720 dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1721 m_palette = new Palette(this);
1722 m_palette->setContextMenuPolicy(Qt::CustomContextMenu);
1723 dock->setWidget(m_palette);
1724 addDockWidget(Qt::LeftDockWidgetArea, dock);
1725 actionCollection()->addAction(QStringLiteral("showPaletteDockWidget"), dock->toggleViewAction());
1726
1727 dock = new QDockWidget(i18n("History"), this);
1728 dock->setObjectName(QStringLiteral("HistoryDock#"));
1729 dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1730 m_history = new QUndoView(this);
1731 dock->setWidget(m_history);
1732 addDockWidget(Qt::LeftDockWidgetArea, dock);
1733 actionCollection()->addAction(QStringLiteral("showHistoryDockWidget"), dock->toggleViewAction());
1734
1735 dock = new QDockWidget(i18n("Imported Image"), this);
1736 dock->setObjectName(QStringLiteral("ImportedImage#"));
1737 dock->setAllowedAreas(Qt::AllDockWidgetAreas);
1738 m_imageLabel = new ScaledPixmapLabel(this);
1739 m_imageLabel->setScaledContents(false);
1740 dock->setWidget(m_imageLabel);
1741 addDockWidget(Qt::LeftDockWidgetArea, dock);
1742 actionCollection()->addAction(QStringLiteral("showImportedDockWidget"), dock->toggleViewAction());
1743 }
1744