File indexing completed on 2025-01-05 03:53:11

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2011-05-23
0007  * Description : a tool to create panorama by fusion of several images.
0008  * Acknowledge : based on the expoblending tool
0009  *
0010  * SPDX-FileCopyrightText: 2011-2016 by Benjamin Girault <benjamin dot girault at gmail dot com>
0011  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "panolastpage.h"
0018 
0019 // Qt includes
0020 
0021 #include <QUrl>
0022 #include <QFile>
0023 #include <QDir>
0024 #include <QLabel>
0025 #include <QPixmap>
0026 #include <QGroupBox>
0027 #include <QVBoxLayout>
0028 #include <QCheckBox>
0029 #include <QStandardPaths>
0030 #include <QLineEdit>
0031 
0032 // KDE includes
0033 
0034 #include <klocalizedstring.h>
0035 #include <ksharedconfig.h>
0036 #include <kconfiggroup.h>
0037 
0038 // Local includes
0039 
0040 #include "digikam_debug.h"
0041 #include "panomanager.h"
0042 #include "panoactionthread.h"
0043 #include "dlayoutbox.h"
0044 
0045 namespace DigikamGenericPanoramaPlugin
0046 {
0047 
0048 class Q_DECL_HIDDEN PanoLastPage::Private
0049 {
0050 public:
0051 
0052     explicit Private()
0053       : copyDone             (false),
0054         title                (nullptr),
0055         saveSettingsGroupBox (nullptr),
0056         fileTemplateQLineEdit(nullptr),
0057         savePtoCheckBox      (nullptr),
0058         warningLabel         (nullptr),
0059         errorLabel           (nullptr),
0060         mngr                 (nullptr)
0061     {
0062     }
0063 
0064     bool         copyDone;
0065 
0066     QLabel*      title;
0067 
0068     QGroupBox*   saveSettingsGroupBox;
0069     QLineEdit*   fileTemplateQLineEdit;
0070     QCheckBox*   savePtoCheckBox;
0071     QLabel*      warningLabel;
0072     QLabel*      errorLabel;
0073 
0074     PanoManager* mngr;
0075 };
0076 
0077 PanoLastPage::PanoLastPage(PanoManager* const mngr, QWizard* const dlg)
0078      : DWizardPage(dlg, QString::fromLatin1("<b>%1</b>").arg(i18nc("@title:window", "Panorama Stitched"))),
0079        d          (new Private)
0080 {
0081     KSharedConfigPtr config         = KSharedConfig::openConfig();
0082     KConfigGroup group              = config->group(QLatin1String("Panorama Settings"));
0083 
0084     d->mngr                         = mngr;
0085 
0086     DVBox* const vbox               = new DVBox(this);
0087 
0088     d->title                        = new QLabel(vbox);
0089     d->title->setOpenExternalLinks(true);
0090     d->title->setWordWrap(true);
0091 
0092     QVBoxLayout* const formatVBox   = new QVBoxLayout();
0093 
0094     d->saveSettingsGroupBox         = new QGroupBox(i18nc("@title:group", "Save Settings"), vbox);
0095     d->saveSettingsGroupBox->setLayout(formatVBox);
0096     formatVBox->addStretch(1);
0097 
0098     QLabel* const fileTemplateLabel = new QLabel(i18nc("@label:textbox", "File name template:"), d->saveSettingsGroupBox);
0099     formatVBox->addWidget(fileTemplateLabel);
0100 
0101     d->fileTemplateQLineEdit        = new QLineEdit(QLatin1String("panorama"), d->saveSettingsGroupBox);
0102     d->fileTemplateQLineEdit->setToolTip(i18nc("@info:tooltip", "Name of the panorama file (without its extension)."));
0103     d->fileTemplateQLineEdit->setWhatsThis(i18nc("@info:whatsthis", "\"File name template\": Set here the base name of the files that "
0104                                                 "will be saved. For example, if your template is \"panorama\" and if "
0105                                                 "you chose a JPEG output, then your panorama will be saved with the "
0106                                                 "name \"panorama.jpg\". If you choose to save also the project file, "
0107                                                 "it will have the name \"panorama.pto\"."));
0108     formatVBox->addWidget(d->fileTemplateQLineEdit);
0109 
0110     d->savePtoCheckBox              = new QCheckBox(i18nc("@option:check", "Save project file"), d->saveSettingsGroupBox);
0111     d->savePtoCheckBox->setChecked(group.readEntry("Save PTO", false));
0112     d->savePtoCheckBox->setToolTip(i18nc("@info:tooltip", "Save the project file for further processing within Hugin GUI."));
0113     d->savePtoCheckBox->setWhatsThis(i18nc("@info:whatsthis", "\"Save project file\": You can keep the project file generated to stitch "
0114                                           "your panorama for further tweaking within %1 by checking this. "
0115                                           "This is useful if you want a different projection, modify the horizon or "
0116                                           "the center of the panorama, or modify the control points to get better results.",
0117                                           QLatin1String("<a href=\"http://hugin.sourceforge.net/\">Hugin</a>")));        // krazy:exclude=insecurenet
0118     formatVBox->addWidget(d->savePtoCheckBox);
0119 
0120     d->warningLabel                 = new QLabel(d->saveSettingsGroupBox);
0121     d->warningLabel->hide();
0122     formatVBox->addWidget(d->warningLabel);
0123 
0124     d->errorLabel                   = new QLabel(d->saveSettingsGroupBox);
0125     d->errorLabel->hide();
0126     formatVBox->addWidget(d->errorLabel);
0127 
0128     vbox->setStretchFactor(new QWidget(vbox), 2);
0129 
0130     setPageWidget(vbox);
0131 
0132     QPixmap leftPix(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/data/assistant-hugin.png")));
0133     setLeftBottomPix(leftPix.scaledToWidth(128, Qt::SmoothTransformation));
0134 
0135     connect(d->fileTemplateQLineEdit, SIGNAL(textChanged(QString)),
0136             this, SLOT(slotTemplateChanged(QString)));
0137 
0138     connect(d->savePtoCheckBox, SIGNAL(stateChanged(int)),
0139             this, SLOT(slotPtoCheckBoxChanged(int)));
0140 }
0141 
0142 PanoLastPage::~PanoLastPage()
0143 {
0144     KSharedConfigPtr config = KSharedConfig::openConfig();
0145     KConfigGroup group      = config->group(QLatin1String("Panorama Settings"));
0146     group.writeEntry("Save PTO", d->savePtoCheckBox->isChecked());
0147     config->sync();
0148 
0149     delete d;
0150 }
0151 
0152 void PanoLastPage::copyFiles()
0153 {
0154     connect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)),
0155             this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData)));
0156 
0157     QUrl panoUrl = d->mngr->preProcessedMap().begin().key().adjusted(QUrl::RemoveFilename);
0158     panoUrl.setPath(panoUrl.path() + panoFileName(d->fileTemplateQLineEdit->text()));
0159 
0160     d->mngr->thread()->copyFiles(
0161                                  d->mngr->panoPtoUrl(),
0162                                  d->mngr->panoUrl(),
0163                                  panoUrl,
0164                                  d->mngr->preProcessedMap(),
0165                                  d->savePtoCheckBox->isChecked(),
0166                                  d->mngr->gPano()
0167                                 );
0168 }
0169 
0170 QString PanoLastPage::panoFileName(const QString& fileTemplate) const
0171 {
0172     switch (d->mngr->format())
0173     {
0174         default:
0175         case JPEG:
0176             return fileTemplate + QLatin1String(".jpg");
0177 
0178         case TIFF:
0179             return fileTemplate + QLatin1String(".tif");
0180     }
0181 }
0182 
0183 void PanoLastPage::checkFiles()
0184 {
0185     QString dir = d->mngr->preProcessedMap().begin().key().toString(QUrl::RemoveFilename);
0186     QUrl panoUrl(dir + panoFileName(d->fileTemplateQLineEdit->text()));
0187     QUrl ptoUrl(dir + d->fileTemplateQLineEdit->text() + QLatin1String(".pto"));
0188     QFile panoFile(panoUrl.toString(QUrl::PreferLocalFile));
0189     QFile ptoFile(ptoUrl.toString(QUrl::PreferLocalFile));
0190 
0191     bool rawsOk = true;
0192 
0193     if (d->savePtoCheckBox->isChecked())
0194     {
0195         for (auto& input : d->mngr->preProcessedMap().keys())
0196         {
0197             if (input != d->mngr->preProcessedMap()[input].preprocessedUrl)
0198             {
0199                 QString dir2 = input.toString(QUrl::RemoveFilename);
0200                 QUrl derawUrl(dir2 + d->mngr->preProcessedMap()[input].preprocessedUrl.fileName());
0201                 QFile derawFile(derawUrl.toString(QUrl::PreferLocalFile));
0202                 rawsOk      &= !derawFile.exists();
0203             }
0204         }
0205     }
0206 
0207     if      (panoFile.exists() || (d->savePtoCheckBox->isChecked() && ptoFile.exists()))
0208     {
0209         setComplete(false);
0210         Q_EMIT completeChanged();
0211         d->warningLabel->setText(QString::fromUtf8("<qt><p><font color=\"red\"><b>%1:</b> %2.</font></p></qt>")
0212                                  .arg(i18nc("@title: dialog", "Warning"))
0213                                  .arg(i18nc("@label", "This file already exists")));
0214         d->warningLabel->show();
0215     }
0216     else if (!rawsOk)
0217     {
0218         setComplete(true);
0219         Q_EMIT completeChanged();
0220         d->warningLabel->setText(QString::fromUtf8("<qt><p><font color=\"orange\"><b>:</b> %2.</font></p></qt>")
0221                                  .arg(i18nc("@title: dialog", "Warning"))
0222                                  .arg(i18nc("@label", "One or more converted raw files already exists (they will be skipped during the copying process)")));
0223         d->warningLabel->show();
0224     }
0225     else
0226     {
0227         setComplete(true);
0228         Q_EMIT completeChanged();
0229         d->warningLabel->hide();
0230     }
0231 }
0232 
0233 void PanoLastPage::initializePage()
0234 {
0235     QString first = d->mngr->itemsList().first().fileName();
0236     QString last  = d->mngr->itemsList().last().fileName();
0237     QString file  = QString::fromLatin1("%1-%2")
0238         .arg(first.left(first.lastIndexOf(QLatin1Char('.'))))
0239         .arg(last.left(last.lastIndexOf(QLatin1Char('.'))));
0240     d->fileTemplateQLineEdit->setText(file);
0241 
0242     checkFiles();
0243 }
0244 
0245 bool PanoLastPage::validatePage()
0246 {
0247     if (d->copyDone)
0248     {
0249         return true;
0250     }
0251 
0252     setComplete(false);
0253     copyFiles();
0254 
0255     return false;
0256 }
0257 
0258 void PanoLastPage::slotTemplateChanged(const QString&)
0259 {
0260     d->title->setText(QString::fromUtf8("<qt>"
0261                                         "<p><h1><b>%1</b></h1></p>"
0262                                         "<p>%2</p>"
0263                                         "<p>%3</p>"
0264                                         "<p>%4<br /><b>%5</b><br /></p>"
0265                                         "<p>%6</p>"
0266                                         "</qt>")
0267                    .arg(i18nc("@info", "Panorama Stitching is Done"))
0268                    .arg(i18nc("@info", "Congratulations. Your images are stitched into a panorama."))
0269                    .arg(i18nc("@info", "Your panorama will be created to the directory:"))
0270                    .arg(QDir::toNativeSeparators(d->mngr->preProcessedMap().begin().key().toString(QUrl::RemoveFilename | QUrl::PreferLocalFile)))
0271                    .arg(i18nc("@info", "once you press the \"Finish\" button, with the name set below."))
0272                    .arg(i18nc("@info", "If you choose to save the project file, and "
0273                                        "if your images were raw images then the converted images used during "
0274                                        "the stitching process will be copied at the same time (those are "
0275                                        "TIFF files that can be big).")));
0276     checkFiles();
0277 }
0278 
0279 void PanoLastPage::slotPtoCheckBoxChanged(int)
0280 {
0281     checkFiles();
0282 }
0283 
0284 void PanoLastPage::slotPanoAction(const DigikamGenericPanoramaPlugin::PanoActionData& ad)
0285 {
0286     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "SlotPanoAction (lastPage)";
0287     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "starting, success, action:" << ad.starting << ad.success << ad.action;
0288 
0289     if (!ad.starting)           // Something is complete...
0290     {
0291         if (!ad.success)        // Something is failed...
0292         {
0293             switch (ad.action)
0294             {
0295                 case PANO_COPY:
0296                 {
0297                     disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)),
0298                                this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData)));
0299 
0300                     d->errorLabel->setText(QString::fromUtf8("<qt><p><font color=\"red\"><b>%1:</b> %2</font></p></qt>")
0301                                            .arg(i18nc("@label", "Error"))
0302                                            .arg(ad.message));
0303                     d->errorLabel->show();
0304                     break;
0305                 }
0306 
0307                 default:
0308                 {
0309                     qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (last) " << ad.action;
0310                     break;
0311                 }
0312             }
0313         }
0314         else                    // Something is done...
0315         {
0316             switch (ad.action)
0317             {
0318                 case PANO_COPY:
0319                 {
0320                     disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)),
0321                                this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData)));
0322 
0323                     d->copyDone = true;
0324                     Q_EMIT signalCopyFinished();
0325                     break;
0326                 }
0327 
0328                 default:
0329                 {
0330                     qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (last) " << ad.action;
0331                     break;
0332                 }
0333             }
0334         }
0335     }
0336 }
0337 
0338 } // namespace DigikamGenericPanoramaPlugin
0339 
0340 #include "moc_panolastpage.cpp"