1 /*
2 SPDX-FileCopyrightText: 2009, 2010 Stefan Majewsky <majewsky@gmx.net>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "puzzlecreator.h"
8 #include "slicerconfwidget.h"
9 #include "file-io/components.h"
10 #include "file-io/puzzle.h"
11 #include "file-io/puzzlestructs.h"
12
13 #include <Pala/Slicer>
14 #include <Pala/SlicerJob>
15 #include <Pala/SlicerMode>
16
17 #include <QFormLayout>
18 #include <QLabel>
19 #include <QStackedWidget>
20 #include <QUuid>
21 #include <QImageReader>
22
23 #include <KLineEdit>
24 #include <KLocalizedString>
25 #include <KMessageBox>
26 #include <KUrlRequester>
27
PuzzleCreatorDialog()28 Palapeli::PuzzleCreatorDialog::PuzzleCreatorDialog()
29 : m_result(nullptr)
30 , m_imageSelector(new KUrlRequester)
31 , m_nameEdit(new KLineEdit)
32 , m_commentEdit(new KLineEdit)
33 , m_authorEdit(new KLineEdit)
34 , m_slicerSelector(new Palapeli::SlicerSelector)
35 , m_slicerConfigMasterWidget(new QStackedWidget)
36 {
37 //setup dialog
38 setWindowTitle(i18nc("@title:window", "Create New Puzzle"));
39 buttonBox()->button(QDialogButtonBox::Help)->setVisible(false);
40 //setup image selector
41 m_imageSelector->setMode(KFile::File | KFile::LocalOnly | KFile::ExistingOnly);
42 const auto supportedImageTypes = QImageReader::supportedMimeTypes();
43 QStringList mimeTypeFilters;
44 mimeTypeFilters.reserve(supportedImageTypes.size());
45 for (const auto& imageType : supportedImageTypes) {
46 mimeTypeFilters.append(QString::fromUtf8(imageType));
47 }
48 m_imageSelector->setMimeTypeFilters(mimeTypeFilters);
49 //build sublayouts
50 QFormLayout* sourceLayout = new QFormLayout;
51 sourceLayout->addRow(i18nc("@label:chooser", "Image file:"), m_imageSelector);
52 sourceLayout->addRow(new QLabel(i18nc("@info", "Please describe below the image which you have chosen.")));
53 sourceLayout->addRow(i18nc("@label:textbox", "Image name:"), m_nameEdit);
54 sourceLayout->addRow(i18nc("@label:textbox (like in: This comment is optional.)", "Optional comment:"), m_commentEdit);
55 sourceLayout->addRow(i18nc("@label:textbox", "Name of image author:"), m_authorEdit);
56 const auto slicers = m_slicerSelector->slicers();
57 for (const Pala::Slicer* slicer : slicers)
58 {
59 m_slicerConfigWidgets[slicer] = new Palapeli::SlicerConfigWidget(slicer);
60 m_slicerConfigMasterWidget->addWidget(m_slicerConfigWidgets[slicer]);
61 }
62 //build page widget items
63 m_sourcePage = addPage(new QWidget, i18nc("@item:inlistbox (page name in an assistant dialog)", "Choose image"));
64 m_sourcePage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Specify the source image to be sliced into pieces"));
65 m_sourcePage->widget()->setLayout(sourceLayout);
66 m_slicerPage = addPage(m_slicerSelector, i18nc("@item:inlistbox (page name in an assistant dialog)", "Choose slicer"));
67 m_slicerPage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Choose a slicing method"));
68 m_slicerConfigPage = addPage(m_slicerConfigMasterWidget, i18nc("@item:inlistbox (page name in an assistant dialog)", "Configure slicer"));
69 m_slicerConfigPage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Tweak the parameters of the chosen slicing method"));
70 //wire up stuff
71 connect(this, &PuzzleCreatorDialog::accepted, this, &PuzzleCreatorDialog::createPuzzle);
72 connect(m_imageSelector, &KUrlRequester::urlSelected, this, &PuzzleCreatorDialog::checkData);
73 connect(m_nameEdit, &KLineEdit::textChanged, this, &PuzzleCreatorDialog::checkData);
74 connect(m_authorEdit, &KLineEdit::textChanged, this, &PuzzleCreatorDialog::checkData);
75 checkData(); //to invalidate first page
76 setValid(m_slicerPage, false);
77 connect(m_slicerSelector, &Palapeli::SlicerSelector::currentSelectionChanged, this, &PuzzleCreatorDialog::updateSlicerSelection);
78 }
79
result() const80 Palapeli::Puzzle* Palapeli::PuzzleCreatorDialog::result() const
81 {
82 return m_result;
83 }
84
checkData()85 void Palapeli::PuzzleCreatorDialog::checkData()
86 {
87 bool sourceValid = !m_imageSelector->url().isEmpty();
88 sourceValid &= !m_nameEdit->text().isEmpty();
89 sourceValid &= !m_authorEdit->text().isEmpty();
90 setValid(m_sourcePage, sourceValid);
91 }
92
updateSlicerSelection(const Palapeli::SlicerSelection & selection)93 void Palapeli::PuzzleCreatorDialog::updateSlicerSelection(const Palapeli::SlicerSelection& selection)
94 {
95 setValid(m_slicerPage, (bool) selection.slicer);
96 if (!selection.slicer) return;
97 //update slicer configuration widget
98 Palapeli::SlicerConfigWidget* scWidget = m_slicerConfigWidgets.value(selection.slicer);
99 scWidget->setMode(selection.mode);
100 m_slicerConfigMasterWidget->setCurrentWidget(scWidget);
101 }
102
createPuzzle()103 void Palapeli::PuzzleCreatorDialog::createPuzzle()
104 {
105 if (m_result)
106 return; //We won't create the puzzle if there is one already.
107 //assemble data for creation context
108 const Palapeli::SlicerSelection selection = m_slicerSelector->currentSelection();
109 const Pala::Slicer* slicer = selection.slicer;
110 if (!slicer)
111 {
112 KMessageBox::sorry(this, i18n("Puzzle cannot be created: The slicer plugin could not be loaded."));
113 return;
114 }
115 QMap<QByteArray, QVariant> slicerArgs = m_slicerConfigWidgets[selection.slicer]->arguments();
116 QImage image;
117 if (!image.load(m_imageSelector->url().toLocalFile()))
118 {
119 KMessageBox::sorry(this, i18n("Puzzle cannot be created: The file you selected is not an image."));
120 return;
121 }
122 image = image.convertToFormat(image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
123 Pala::SlicerJob job(image, slicerArgs);
124 job.setMode(selection.mode);
125 if (!const_cast<Pala::Slicer*>(slicer)->process(&job)) //BIC: make Pala::Slicer::process() and run() const
126 {
127 KMessageBox::sorry(this, i18n("Puzzle cannot be created: Slicing failed because of undetermined problems."));
128 return;
129 }
130 //create puzzle creation context
131 Palapeli::PuzzleCreationContext creationContext;
132 creationContext.name = m_nameEdit->text();
133 creationContext.comment = m_commentEdit->text();
134 creationContext.author = m_authorEdit->text();
135 creationContext.pieceCount = job.pieces().count();
136 creationContext.image = image;
137 creationContext.thumbnail = image.scaled(Palapeli::PuzzleMetadata::ThumbnailBaseSize, Qt::KeepAspectRatio);
138 creationContext.modifyProtection = false; //only enabled for default puzzles
139 creationContext.slicer = selection.slicerPluginName;
140 creationContext.slicerMode = selection.mode ? selection.mode->key() : QByteArray();
141 creationContext.slicerArgs = slicerArgs;
142 //create puzzle
143 m_result = new Palapeli::Puzzle(new Palapeli::CreationContextComponent(creationContext), QString(), QUuid::createUuid().toString());
144 }
145
146
147