File indexing completed on 2024-05-12 04:06:22

0001 /*
0002     SPDX-FileCopyrightText: 2009, 2010 Stefan Majewsky <majewsky@gmx.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "puzzlecreator.h"
0008 #include "slicerconfwidget.h"
0009 #include "file-io/components.h"
0010 #include "file-io/puzzle.h"
0011 #include "file-io/puzzlestructs.h"
0012 
0013 #include <Pala/Slicer>
0014 #include <Pala/SlicerJob>
0015 #include <Pala/SlicerMode>
0016 
0017 #include <QFormLayout>
0018 #include <QLabel>
0019 #include <QStackedWidget>
0020 #include <QUuid>
0021 #include <QImageReader>
0022 
0023 #include <KLineEdit>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 #include <KUrlRequester>
0027 
0028 Palapeli::PuzzleCreatorDialog::PuzzleCreatorDialog()
0029     : m_result(nullptr)
0030     , m_imageSelector(new KUrlRequester)
0031     , m_nameEdit(new KLineEdit)
0032     , m_commentEdit(new KLineEdit)
0033     , m_authorEdit(new KLineEdit)
0034     , m_slicerSelector(new Palapeli::SlicerSelector)
0035     , m_slicerConfigMasterWidget(new QStackedWidget)
0036 {
0037     //setup dialog
0038     setWindowTitle(i18nc("@title:window", "Create New Puzzle"));
0039     buttonBox()->button(QDialogButtonBox::Help)->setVisible(false);
0040     //setup image selector
0041     m_imageSelector->setMode(KFile::File | KFile::LocalOnly | KFile::ExistingOnly);
0042     const auto supportedImageTypes = QImageReader::supportedMimeTypes();
0043     QStringList mimeTypeFilters;
0044     mimeTypeFilters.reserve(supportedImageTypes.size());
0045     for (const auto& imageType : supportedImageTypes) {
0046         mimeTypeFilters.append(QString::fromUtf8(imageType));
0047     }
0048     m_imageSelector->setMimeTypeFilters(mimeTypeFilters);
0049     //build sublayouts
0050     QFormLayout* sourceLayout = new QFormLayout;
0051     sourceLayout->addRow(i18nc("@label:chooser", "Image file:"), m_imageSelector);
0052     sourceLayout->addRow(new QLabel(i18nc("@info", "Please describe below the image which you have chosen.")));
0053     sourceLayout->addRow(i18nc("@label:textbox", "Image name:"), m_nameEdit);
0054     sourceLayout->addRow(i18nc("@label:textbox (like in: This comment is optional.)", "Optional comment:"), m_commentEdit);
0055     sourceLayout->addRow(i18nc("@label:textbox", "Name of image author:"), m_authorEdit);
0056         const auto slicers = m_slicerSelector->slicers();
0057     for (const Pala::Slicer* slicer : slicers)
0058     {
0059         m_slicerConfigWidgets[slicer] = new Palapeli::SlicerConfigWidget(slicer);
0060         m_slicerConfigMasterWidget->addWidget(m_slicerConfigWidgets[slicer]);
0061     }
0062     //build page widget items
0063     m_sourcePage = addPage(new QWidget, i18nc("@item:inlistbox (page name in an assistant dialog)", "Choose image"));
0064     m_sourcePage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Specify the source image to be sliced into pieces"));
0065     m_sourcePage->widget()->setLayout(sourceLayout);
0066     m_slicerPage = addPage(m_slicerSelector, i18nc("@item:inlistbox (page name in an assistant dialog)", "Choose slicer"));
0067     m_slicerPage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Choose a slicing method"));
0068     m_slicerConfigPage = addPage(m_slicerConfigMasterWidget, i18nc("@item:inlistbox (page name in an assistant dialog)", "Configure slicer"));
0069     m_slicerConfigPage->setHeader(i18nc("@title:tab (page header in an assistant dialog)", "Tweak the parameters of the chosen slicing method"));
0070     //wire up stuff
0071     connect(this, &PuzzleCreatorDialog::accepted, this, &PuzzleCreatorDialog::createPuzzle);
0072     connect(m_imageSelector, &KUrlRequester::urlSelected, this, &PuzzleCreatorDialog::checkData);
0073     connect(m_nameEdit, &KLineEdit::textChanged, this, &PuzzleCreatorDialog::checkData);
0074     connect(m_authorEdit, &KLineEdit::textChanged, this, &PuzzleCreatorDialog::checkData);
0075     checkData(); //to invalidate first page
0076     setValid(m_slicerPage, false);
0077     connect(m_slicerSelector, &Palapeli::SlicerSelector::currentSelectionChanged, this, &PuzzleCreatorDialog::updateSlicerSelection);
0078 }
0079 
0080 Palapeli::Puzzle* Palapeli::PuzzleCreatorDialog::result() const
0081 {
0082     return m_result;
0083 }
0084 
0085 void Palapeli::PuzzleCreatorDialog::checkData()
0086 {
0087     bool sourceValid = !m_imageSelector->url().isEmpty();
0088     sourceValid &= !m_nameEdit->text().isEmpty();
0089     sourceValid &= !m_authorEdit->text().isEmpty();
0090     setValid(m_sourcePage, sourceValid);
0091 }
0092 
0093 void Palapeli::PuzzleCreatorDialog::updateSlicerSelection(const Palapeli::SlicerSelection& selection)
0094 {
0095     setValid(m_slicerPage, (bool) selection.slicer);
0096     if (!selection.slicer) return;
0097     //update slicer configuration widget
0098     Palapeli::SlicerConfigWidget* scWidget = m_slicerConfigWidgets.value(selection.slicer);
0099     scWidget->setMode(selection.mode);
0100     m_slicerConfigMasterWidget->setCurrentWidget(scWidget);
0101 }
0102 
0103 void Palapeli::PuzzleCreatorDialog::createPuzzle()
0104 {
0105     if (m_result)
0106         return; //We won't create the puzzle if there is one already.
0107     //assemble data for creation context
0108     const Palapeli::SlicerSelection selection = m_slicerSelector->currentSelection();
0109     const Pala::Slicer* slicer = selection.slicer;
0110     if (!slicer)
0111     {
0112         KMessageBox::error(this, i18n("Puzzle cannot be created: The slicer plugin could not be loaded."));
0113         return;
0114     }
0115     QMap<QByteArray, QVariant> slicerArgs = m_slicerConfigWidgets[selection.slicer]->arguments();
0116     QImage image;
0117     if (!image.load(m_imageSelector->url().toLocalFile()))
0118     {
0119         KMessageBox::error(this, i18n("Puzzle cannot be created: The file you selected is not an image."));
0120         return;
0121     }
0122     image = image.convertToFormat(image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
0123     Pala::SlicerJob job(image, slicerArgs);
0124     job.setMode(selection.mode);
0125     if (!const_cast<Pala::Slicer*>(slicer)->process(&job)) //BIC: make Pala::Slicer::process() and run() const
0126     {
0127         KMessageBox::error(this, i18n("Puzzle cannot be created: Slicing failed because of undetermined problems."));
0128         return;
0129     }
0130     //create puzzle creation context
0131     Palapeli::PuzzleCreationContext creationContext;
0132     creationContext.name = m_nameEdit->text();
0133     creationContext.comment = m_commentEdit->text();
0134     creationContext.author = m_authorEdit->text();
0135     creationContext.pieceCount = job.pieces().count();
0136     creationContext.image = image;
0137     creationContext.thumbnail = image.scaled(Palapeli::PuzzleMetadata::ThumbnailBaseSize, Qt::KeepAspectRatio);
0138     creationContext.modifyProtection = false; //only enabled for default puzzles
0139     creationContext.slicer = selection.slicerPluginName;
0140     creationContext.slicerMode = selection.mode ? selection.mode->key() : QByteArray();
0141     creationContext.slicerArgs = slicerArgs;
0142     //create puzzle
0143     m_result = new Palapeli::Puzzle(new Palapeli::CreationContextComponent(creationContext), QString(), QUuid::createUuid().toString());
0144 }
0145 
0146 #include "moc_puzzlecreator.cpp"