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