File indexing completed on 2025-03-09 03:52:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-09-09
0007  * Description : a presentation tool.
0008  *
0009  * SPDX-FileCopyrightText: 2008-2009 by Valerio Fuoglio <valerio dot fuoglio at gmail dot com>
0010  * SPDX-FileCopyrightText:      2009 by Andi Clemens <andi dot clemens at googlemail dot com>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  * SPDX-FileCopyrightText:      2021 by Phuoc Khanh Le <phuockhanhnk94 at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "presentation_mainpage.h"
0019 
0020 // Qt includes
0021 
0022 #include <QFile>
0023 #include <QFileInfo>
0024 #include <QMap>
0025 #include <QString>
0026 #include <QStringList>
0027 #include <QTime>
0028 #include <QHeaderView>
0029 #include <QPainter>
0030 #include <QVBoxLayout>
0031 #include <QUrl>
0032 #include <QIcon>
0033 #include <QMessageBox>
0034 
0035 // Local includes
0036 
0037 #include "digikam_config.h"
0038 #include "digikam_debug.h"
0039 #include "presentationcontainer.h"
0040 #include "presentation_advpage.h"
0041 #include "presentation_captionpage.h"
0042 #include "thumbnailloadthread.h"
0043 #include "ditemslist.h"
0044 #include "dmetadata.h"
0045 #include "presentationwidget.h"
0046 
0047 #ifdef HAVE_OPENGL
0048 #   include "presentationgl.h"
0049 #   include "presentationkb.h"
0050 #endif
0051 
0052 using namespace Digikam;
0053 
0054 namespace DigikamGenericPresentationPlugin
0055 {
0056 
0057 class Q_DECL_HIDDEN PresentationMainPage::Private
0058 {
0059 
0060 public:
0061 
0062     explicit Private()
0063       : sharedData        (nullptr),
0064         imagesFilesListBox(nullptr),
0065         ICON_SIZE         (256)
0066     {
0067     }
0068 
0069     PresentationContainer* sharedData;
0070     QTime                  totalTime;
0071     DItemsList*            imagesFilesListBox;
0072 
0073     const int              ICON_SIZE;
0074 };
0075 
0076 PresentationMainPage::PresentationMainPage(QWidget* const parent,
0077                                            PresentationContainer* const sharedData)
0078     : QWidget(parent),
0079       d      (new Private)
0080 {
0081     setupUi(this);
0082 
0083     d->sharedData = sharedData;
0084 
0085     // --------------------------------------------------------
0086 
0087     QVBoxLayout* const listBoxContainerLayout = new QVBoxLayout;
0088     d->imagesFilesListBox                     = new DItemsList(m_ImagesFilesListBoxContainer);
0089     d->imagesFilesListBox->setIconSize(32);
0090     d->imagesFilesListBox->setObjectName(QLatin1String("Presentation ImagesList"));
0091     d->imagesFilesListBox->listView()->header()->hide();
0092     d->imagesFilesListBox->enableControlButtons(true);
0093     d->imagesFilesListBox->enableDragAndDrop(true);
0094 
0095     listBoxContainerLayout->addWidget(d->imagesFilesListBox);
0096     listBoxContainerLayout->setContentsMargins(QMargins());
0097     listBoxContainerLayout->setSpacing(0);
0098     m_ImagesFilesListBoxContainer->setLayout(listBoxContainerLayout);
0099 
0100     // --------------------------------------------------------
0101 
0102     m_previewLabel->setMinimumWidth(d->ICON_SIZE);
0103     m_previewLabel->setMinimumHeight(d->ICON_SIZE);
0104 
0105 #ifdef HAVE_OPENGL
0106 
0107     m_openglCheckBox->setEnabled(true);
0108 
0109 #else
0110 
0111     m_openglCheckBox->setEnabled(false);
0112 
0113 #endif
0114 
0115 }
0116 
0117 PresentationMainPage::~PresentationMainPage()
0118 {
0119     delete d;
0120 }
0121 
0122 void PresentationMainPage::readSettings()
0123 {
0124 
0125 #ifdef HAVE_OPENGL
0126 
0127     m_openglCheckBox->setChecked(d->sharedData->opengl);
0128 
0129 #endif
0130 
0131     m_delaySpinBox->setValue(d->sharedData->delay);
0132     m_printNameCheckBox->setChecked(d->sharedData->printFileName);
0133     m_printProgressCheckBox->setChecked(d->sharedData->printProgress);
0134     m_printCommentsCheckBox->setChecked(d->sharedData->printFileComments);
0135     m_loopCheckBox->setChecked(d->sharedData->loop);
0136     m_shuffleCheckBox->setChecked(d->sharedData->shuffle);
0137     m_offAutoDelayCheckBox->setChecked(d->sharedData->offAutoDelay);
0138 
0139     m_delaySpinBox->setValue(d->sharedData->useMilliseconds ? d->sharedData->delay
0140                                                             : d->sharedData->delay / 1000);
0141 
0142     slotUseMillisecondsToggled();
0143 
0144     // --------------------------------------------------------
0145 
0146     setupConnections();
0147     slotOpenGLToggled();
0148     slotPrintCommentsToggled();
0149     slotEffectChanged();
0150 
0151     addItems(d->sharedData->urlList);
0152 }
0153 
0154 void PresentationMainPage::saveSettings()
0155 {
0156 
0157 #ifdef HAVE_OPENGL
0158 
0159     d->sharedData->opengl                = m_openglCheckBox->isChecked();
0160 
0161 #endif
0162 
0163     d->sharedData->delay                 = d->sharedData->useMilliseconds ? m_delaySpinBox->value()
0164                                                                           : m_delaySpinBox->value() * 1000;
0165 
0166     d->sharedData->printFileName         = m_printNameCheckBox->isChecked();
0167     d->sharedData->printProgress         = m_printProgressCheckBox->isChecked();
0168     d->sharedData->printFileComments     = m_printCommentsCheckBox->isChecked();
0169     d->sharedData->loop                  = m_loopCheckBox->isChecked();
0170     d->sharedData->shuffle               = m_shuffleCheckBox->isChecked();
0171     d->sharedData->offAutoDelay          = m_offAutoDelayCheckBox->isChecked();
0172 
0173     if (!m_openglCheckBox->isChecked())
0174     {
0175 
0176         QString effect;
0177         QMap<QString, QString> effectNames = PresentationWidget::effectNamesI18N();
0178         QMap<QString, QString>::ConstIterator it;
0179 
0180         for (it = effectNames.constBegin() ; it != effectNames.constEnd() ; ++it)
0181         {
0182             if (it.value() == m_effectsComboBox->currentText())
0183             {
0184                 effect = it.key();
0185                 break;
0186             }
0187         }
0188 
0189         d->sharedData->effectName = effect;
0190     }
0191 
0192 #ifdef HAVE_OPENGL
0193 
0194     else
0195     {
0196         QMap<QString, QString> effects;
0197         QMap<QString, QString> effectNames;
0198         QMap<QString, QString>::ConstIterator it;
0199 
0200         // Load slideshowgl effects
0201 
0202         effectNames = PresentationGL::effectNamesI18N();
0203 
0204         for (it = effectNames.constBegin() ; it != effectNames.constEnd() ; ++it)
0205         {
0206             effects.insert(it.key(), it.value());
0207         }
0208 
0209         // Load Ken Burns effect
0210 
0211         effectNames = PresentationKB::effectNamesI18N();
0212 
0213         for (it = effectNames.constBegin() ; it != effectNames.constEnd() ; ++it)
0214         {
0215             effects.insert(it.key(), it.value());
0216         }
0217 
0218         QString effect;
0219 
0220         for (it = effects.constBegin() ; it != effects.constEnd() ; ++it)
0221         {
0222             if (it.value() == m_effectsComboBox->currentText())
0223             {
0224                 effect = it.key();
0225                 break;
0226             }
0227         }
0228 
0229         d->sharedData->effectNameGL = effect;
0230     }
0231 
0232 #endif
0233 
0234 }
0235 
0236 void PresentationMainPage::showNumberImages()
0237 {
0238     int numberOfImages = d->imagesFilesListBox->imageUrls().count();
0239     QTime totalDuration(0, 0, 0);
0240 
0241     int transitionDuration = 2000;
0242 
0243 #ifdef HAVE_OPENGL
0244 
0245     if (m_openglCheckBox->isChecked())
0246     {
0247         transitionDuration += 500;
0248     }
0249 
0250 #endif
0251 
0252     if (numberOfImages != 0)
0253     {
0254         if (d->sharedData->useMilliseconds)
0255         {
0256             totalDuration = totalDuration.addMSecs(numberOfImages * m_delaySpinBox->text().toInt());
0257         }
0258         else
0259         {
0260             totalDuration = totalDuration.addSecs(numberOfImages * m_delaySpinBox->text().toInt());
0261         }
0262 
0263         totalDuration = totalDuration.addMSecs((numberOfImages - 1) * transitionDuration);
0264     }
0265 
0266     d->totalTime = totalDuration;
0267 
0268     // Notify total time is changed
0269 
0270     Q_EMIT signalTotalTimeChanged(d->totalTime);
0271 
0272     if (m_offAutoDelayCheckBox->isChecked() == false)
0273     {
0274         m_label6->setText(i18np("%1 image [%2]", "%1 images [%2]", numberOfImages, totalDuration.toString()));
0275     }
0276     else
0277     {
0278         m_label6->setText(i18np("%1 image", "%1 images", numberOfImages));
0279     }
0280 }
0281 
0282 void PresentationMainPage::loadEffectNames()
0283 {
0284     m_effectsComboBox->clear();
0285 
0286     QMap<QString, QString> effectNames = PresentationWidget::effectNamesI18N();
0287     QStringList effects;
0288 
0289     QMap<QString, QString>::Iterator it;
0290 
0291     for (it = effectNames.begin() ; it != effectNames.end() ; ++it)
0292     {
0293         effects.append(it.value());
0294     }
0295 
0296     m_effectsComboBox->insertItems(0, effects);
0297 
0298     for (int i = 0 ; i < m_effectsComboBox->count() ; ++i)
0299     {
0300         if (effectNames[d->sharedData->effectName] == m_effectsComboBox->itemText(i))
0301         {
0302             m_effectsComboBox->setCurrentIndex(i);
0303             break;
0304         }
0305     }
0306 }
0307 
0308 void PresentationMainPage::slotOffAutoDelay()
0309 {
0310     m_delaySpinBox->setEnabled(!m_offAutoDelayCheckBox->isChecked());
0311     m_delayLabel->setEnabled(!m_offAutoDelayCheckBox->isChecked());
0312     m_openglCheckBox->setEnabled(!m_offAutoDelayCheckBox->isChecked());
0313     m_openglCheckBox->setChecked(!m_offAutoDelayCheckBox->isChecked());
0314     d->sharedData->advancedPage->m_useMillisecondsCheckBox->setEnabled(!m_offAutoDelayCheckBox->isChecked());
0315     slotDelayChanged(0);
0316 }
0317 
0318 void PresentationMainPage::loadEffectNamesGL()
0319 {
0320 
0321 #ifdef HAVE_OPENGL
0322 
0323     m_effectsComboBox->clear();
0324 
0325     QStringList effects;
0326     QMap<QString, QString> effectNames;
0327     QMap<QString, QString>::Iterator it;
0328 
0329     // Load slideshowgl effects
0330 
0331     effectNames = PresentationGL::effectNamesI18N();
0332 
0333     // Add Ken Burns effect
0334 
0335     QMap<QString, QString> effectNameKB = PresentationKB::effectNamesI18N();
0336     for (it = effectNameKB.begin(); it != effectNameKB.end(); ++it)
0337     {
0338         effectNames.insert(it.key(), it.value());
0339     }
0340 
0341     for (it = effectNames.begin() ; it != effectNames.end() ; ++it)
0342     {
0343         effects.append(it.value());
0344     }
0345 
0346     // Update GUI
0347 
0348     effects.sort();
0349 
0350     m_effectsComboBox->insertItems(0, effects);
0351 
0352     for (int i = 0 ; i < m_effectsComboBox->count() ; ++i)
0353     {
0354         if (effectNames[d->sharedData->effectNameGL] == m_effectsComboBox->itemText(i))
0355         {
0356             m_effectsComboBox->setCurrentIndex(i);
0357             break;
0358         }
0359     }
0360 
0361 #endif
0362 
0363 }
0364 
0365 bool PresentationMainPage::updateUrlList()
0366 {
0367     d->sharedData->urlList.clear();
0368     QTreeWidgetItemIterator it(d->imagesFilesListBox->listView());
0369 
0370     while (*it)
0371     {
0372         DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it);
0373 
0374         if (!item)
0375         {
0376             continue;
0377         }
0378 
0379         if (!QFile::exists(item->url().toLocalFile()))
0380         {
0381             QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Cannot access file %1. Please check the path is correct.",
0382                                                             item->url().toLocalFile()));
0383             return false;
0384         }
0385 
0386         d->sharedData->urlList.append(item->url());  // Input images files.
0387         ++it;
0388     }
0389 
0390     return true;
0391 }
0392 
0393 void PresentationMainPage::slotImagesFilesSelected(QTreeWidgetItem* item)
0394 {
0395     if (!item || d->imagesFilesListBox->imageUrls().isEmpty())
0396     {
0397         m_previewLabel->setPixmap(QPixmap());
0398         m_label7->setText(QLatin1String(""));
0399         return;
0400     }
0401 
0402     DItemsListViewItem* const pitem = dynamic_cast<DItemsListViewItem*>(item);
0403 
0404     if (!pitem)
0405     {
0406         return;
0407     }
0408 
0409     connect(ThumbnailLoadThread::defaultThread(), SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
0410             this, SLOT(slotThumbnail(LoadingDescription,QPixmap)));
0411 
0412     ThumbnailLoadThread::defaultThread()->find(ThumbnailIdentifier(pitem->url().toLocalFile()));
0413 
0414     QModelIndex index = d->imagesFilesListBox->listView()->currentIndex();
0415 
0416     if (index.isValid())
0417     {
0418         int rowindex = index.row();
0419         m_label7->setText(i18nc("Image number %1", "Image #%1", rowindex + 1));
0420     }
0421 }
0422 
0423 void PresentationMainPage::addItems(const QList<QUrl>& fileList)
0424 {
0425     if (fileList.isEmpty())
0426     {
0427         return;
0428     }
0429 
0430     QList<QUrl> files = fileList;
0431 
0432     d->imagesFilesListBox->slotAddImages(files);
0433     slotImagesFilesSelected(d->imagesFilesListBox->listView()->currentItem());
0434 }
0435 
0436 void PresentationMainPage::slotOpenGLToggled()
0437 {
0438     if (m_openglCheckBox->isChecked())
0439     {
0440         loadEffectNamesGL();
0441     }
0442     else
0443     {
0444         loadEffectNames();
0445     }
0446 
0447     showNumberImages();
0448     slotEffectChanged();
0449 }
0450 
0451 void PresentationMainPage::slotEffectChanged()
0452 {
0453     bool isKB = m_effectsComboBox->currentText() == i18n("Ken Burns");
0454 
0455     m_printNameCheckBox->setEnabled(!isKB);
0456     m_printProgressCheckBox->setEnabled(!isKB);
0457     m_printCommentsCheckBox->setEnabled(!isKB);
0458 
0459 #ifdef HAVE_OPENGL
0460 
0461     d->sharedData->advancedPage->m_openGlFullScale->setEnabled(!isKB && m_openglCheckBox->isChecked());
0462 
0463 #endif
0464 
0465     d->sharedData->captionPage->setEnabled((!isKB) && m_printCommentsCheckBox->isChecked());
0466 }
0467 
0468 void PresentationMainPage::slotDelayChanged(int delay)
0469 {
0470     d->sharedData->delay = d->sharedData->useMilliseconds ? delay : delay * 1000;
0471     showNumberImages();
0472 }
0473 
0474 void PresentationMainPage::slotUseMillisecondsToggled()
0475 {
0476     int delay = d->sharedData->delay;
0477 
0478     if (d->sharedData->useMilliseconds)
0479     {
0480         m_delayLabel->setText(i18n("Delay between images (ms):"));
0481 
0482         m_delaySpinBox->setRange(d->sharedData->delayMsMinValue, d->sharedData->delayMsMaxValue);
0483         m_delaySpinBox->setSingleStep(d->sharedData->delayMsLineStep);
0484     }
0485     else
0486     {
0487         m_delayLabel->setText(i18n("Delay between images (s):"));
0488 
0489         m_delaySpinBox->setRange(d->sharedData->delayMsMinValue / 100, d->sharedData->delayMsMaxValue / 1000);
0490         m_delaySpinBox->setSingleStep(d->sharedData->delayMsLineStep / 100);
0491         delay /= 1000;
0492     }
0493 
0494     m_delaySpinBox->setValue(delay);
0495 }
0496 
0497 void PresentationMainPage::slotPortfolioDurationChanged(int)
0498 {
0499     showNumberImages();
0500     Q_EMIT signalTotalTimeChanged(d->totalTime);
0501 }
0502 
0503 void PresentationMainPage::slotThumbnail(const LoadingDescription& /*desc*/, const QPixmap& pix)
0504 {
0505     if (pix.isNull())
0506     {
0507         m_previewLabel->setPixmap(QIcon::fromTheme(QLatin1String("view-preview")).pixmap(d->ICON_SIZE, QIcon::Disabled));
0508     }
0509     else
0510     {
0511         m_previewLabel->setPixmap(pix.scaled(d->ICON_SIZE, d->ICON_SIZE, Qt::KeepAspectRatio));
0512     }
0513 
0514     disconnect(ThumbnailLoadThread::defaultThread(), nullptr,
0515                this, nullptr);
0516 }
0517 
0518 void PresentationMainPage::slotPrintCommentsToggled()
0519 {
0520     d->sharedData->printFileComments = m_printCommentsCheckBox->isChecked();
0521     d->sharedData->captionPage->setEnabled(m_printCommentsCheckBox->isChecked());
0522 }
0523 
0524 void PresentationMainPage::slotImageListChanged()
0525 {
0526     showNumberImages();
0527     slotImagesFilesSelected(d->imagesFilesListBox->listView()->currentItem());
0528 }
0529 
0530 void PresentationMainPage::removeImageFromList(const QUrl& url)
0531 {
0532     d->imagesFilesListBox->removeItemByUrl(url);
0533 }
0534 
0535 void PresentationMainPage::setupConnections()
0536 {
0537     connect(d->sharedData->advancedPage, SIGNAL(useMillisecondsToggled()),
0538             this, SLOT(slotUseMillisecondsToggled()));
0539 
0540     connect(m_printCommentsCheckBox, SIGNAL(toggled(bool)),
0541             this, SLOT(slotPrintCommentsToggled()));
0542 
0543     connect(m_openglCheckBox, SIGNAL(toggled(bool)),
0544             this, SLOT(slotOpenGLToggled()));
0545 
0546     connect(m_delaySpinBox, SIGNAL(valueChanged(int)),
0547             this, SLOT(slotDelayChanged(int)));
0548 
0549     connect(m_offAutoDelayCheckBox, SIGNAL(toggled(bool)),
0550             this, SLOT(slotOffAutoDelay()));
0551 
0552     connect(m_effectsComboBox, SIGNAL(activated(int)),
0553             this, SLOT(slotEffectChanged()));
0554 
0555     connect(d->imagesFilesListBox, SIGNAL(signalImageListChanged()),
0556             this, SLOT(slotImageListChanged()));
0557 
0558     connect(d->imagesFilesListBox, SIGNAL(signalItemClicked(QTreeWidgetItem*)),
0559             this, SLOT(slotImagesFilesSelected(QTreeWidgetItem*)));
0560 }
0561 
0562 } // namespace DigikamGenericPresentationPlugin
0563 
0564 #include "moc_presentation_mainpage.cpp"