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"