File indexing completed on 2025-01-19 03:51:28
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-09-24 0007 * Description : DNG converter batch dialog 0008 * 0009 * SPDX-FileCopyrightText: 2012 by Smit Mehta <smit dot meh at gmail dot com> 0010 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2010-2011 by Jens Mueller <tschenser at gmx dot de> 0012 * SPDX-FileCopyrightText: 2011 by Veaceslav Munteanu <slavuttici at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "dngconverterdialog.h" 0019 0020 // Qt includes 0021 0022 #include <QCloseEvent> 0023 #include <QFile> 0024 #include <QFileInfo> 0025 #include <QGridLayout> 0026 #include <QPixmap> 0027 #include <QTimer> 0028 #include <QTreeWidgetItemIterator> 0029 #include <QApplication> 0030 #include <QMessageBox> 0031 #include <QMenu> 0032 #include <QPushButton> 0033 #include <QCursor> 0034 #include <QBoxLayout> 0035 0036 // KDE includes 0037 0038 #include <kconfiggroup.h> 0039 #include <ksharedconfig.h> 0040 #include <klocalizedstring.h> 0041 0042 // Local includes 0043 0044 #include "dngconverteractions.h" 0045 #include "dngconverterthread.h" 0046 #include "dngconverterlist.h" 0047 #include "filesaveconflictbox.h" 0048 #include "dngsettings.h" 0049 #include "dprogresswdg.h" 0050 #include "dngwriter.h" 0051 #include "dmetadata.h" 0052 #include "digikam_debug.h" 0053 #include "imagedialog.h" 0054 #include "dexpanderbox.h" 0055 #include "dfileoperations.h" 0056 0057 using namespace Digikam; 0058 0059 namespace DigikamGenericDNGConverterPlugin 0060 { 0061 0062 class DNGConverterDialog::Private 0063 { 0064 public: 0065 0066 Private() 0067 : busy (false), 0068 progressBar (nullptr), 0069 listView (nullptr), 0070 thread (nullptr), 0071 dngSettings (nullptr), 0072 conflictSettings(nullptr), 0073 iface (nullptr) 0074 { 0075 } 0076 0077 bool busy; 0078 0079 QStringList fileList; 0080 0081 DProgressWdg* progressBar; 0082 0083 DNGConverterList* listView; 0084 0085 DNGConverterActionThread* thread; 0086 0087 DNGSettings* dngSettings; 0088 0089 FileSaveConflictBox* conflictSettings; 0090 0091 DInfoInterface* iface; 0092 }; 0093 0094 DNGConverterDialog::DNGConverterDialog(QWidget* const parent, DInfoInterface* const iface) 0095 : DPluginDialog(parent, QLatin1String("DNG Converter Dialog")), 0096 d (new Private) 0097 { 0098 setWindowTitle(i18nc("@title:window", "DNG Converter")); 0099 setMinimumSize(900, 500); 0100 setModal(true); 0101 0102 d->iface = iface; 0103 0104 m_buttons->addButton(QDialogButtonBox::Close); 0105 m_buttons->addButton(QDialogButtonBox::Ok); 0106 m_buttons->button(QDialogButtonBox::Ok)->setText(i18nc("@action:button", "&Convert")); 0107 0108 QWidget* const mainWidget = new QWidget(this); 0109 QVBoxLayout* const vbx = new QVBoxLayout(this); 0110 vbx->addWidget(mainWidget); 0111 vbx->addWidget(m_buttons); 0112 setLayout(vbx); 0113 0114 //--------------------------------------------- 0115 0116 QGridLayout* const mainLayout = new QGridLayout(mainWidget); 0117 d->listView = new DNGConverterList(mainWidget); 0118 d->progressBar = new DProgressWdg(mainWidget); 0119 d->progressBar->reset(); 0120 d->progressBar->hide(); 0121 0122 d->listView->appendControlButtonsWidget(d->progressBar); 0123 QBoxLayout* const blay = d->listView->setControlButtonsPlacement(DItemsList::ControlButtonsBelow); 0124 blay->setStretchFactor(d->progressBar, 20); 0125 0126 d->dngSettings = new DNGSettings(this); 0127 DLineWidget* const line = new DLineWidget(Qt::Horizontal, this); 0128 d->conflictSettings = new FileSaveConflictBox(this); 0129 0130 mainLayout->addWidget(d->listView, 0, 0, 5, 1); 0131 mainLayout->addWidget(d->dngSettings, 0, 1, 1, 1); 0132 mainLayout->addWidget(line, 1, 1, 1, 1); 0133 mainLayout->addWidget(d->conflictSettings, 2, 1, 1, 1); 0134 mainLayout->setColumnStretch(0, 10); 0135 mainLayout->setRowStretch(3, 10); 0136 mainLayout->setContentsMargins(QMargins()); 0137 0138 // --------------------------------------------------------------- 0139 0140 d->thread = new DNGConverterActionThread(this); 0141 0142 connect(d->thread, SIGNAL(signalStarting(DigikamGenericDNGConverterPlugin::DNGConverterActionData)), 0143 this, SLOT(slotDNGConverterAction(DigikamGenericDNGConverterPlugin::DNGConverterActionData))); 0144 0145 connect(d->thread, SIGNAL(signalFinished(DigikamGenericDNGConverterPlugin::DNGConverterActionData)), 0146 this, SLOT(slotDNGConverterAction(DigikamGenericDNGConverterPlugin::DNGConverterActionData))); 0147 0148 connect(d->thread, SIGNAL(finished()), 0149 this, SLOT(slotThreadFinished())); 0150 0151 // --------------------------------------------------------------- 0152 0153 connect(m_buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), 0154 this, SLOT(slotStartStop())); 0155 0156 connect(m_buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), 0157 this, SLOT(slotClose())); 0158 0159 connect(d->listView, SIGNAL(signalImageListChanged()), 0160 this, SLOT(slotIdentify())); 0161 0162 connect(d->progressBar, SIGNAL(signalProgressCanceled()), 0163 this, SLOT(slotStartStop())); 0164 0165 connect(d->dngSettings, SIGNAL(signalSettingsChanged()), 0166 this, SLOT(slotIdentify())); 0167 0168 connect(d->dngSettings, SIGNAL(signalSetupExifTool()), 0169 this, SLOT(slotSetupExifTool())); 0170 0171 connect(d->conflictSettings, SIGNAL(signalConflictButtonChanged(int)), 0172 this, SLOT(slotIdentify())); 0173 0174 // --------------------------------------------------------------- 0175 0176 d->listView->setIface(d->iface); 0177 d->listView->loadImagesFromCurrentSelection(); 0178 0179 busy(false); 0180 readSettings(); 0181 } 0182 0183 DNGConverterDialog::~DNGConverterDialog() 0184 { 0185 delete d; 0186 } 0187 0188 void DNGConverterDialog::closeEvent(QCloseEvent* e) 0189 { 0190 if (!e) 0191 { 0192 return; 0193 } 0194 0195 // Stop current conversion if necessary 0196 0197 if (d->busy) 0198 { 0199 slotStartStop(); 0200 } 0201 0202 saveSettings(); 0203 d->listView->listView()->clear(); 0204 e->accept(); 0205 } 0206 0207 void DNGConverterDialog::slotSetupExifTool() 0208 { 0209 if (d->iface) 0210 { 0211 connect(d->iface, SIGNAL(signalSetupChanged()), 0212 d->dngSettings, SLOT(slotSetupChanged())); 0213 0214 d->iface->openSetupPage(DInfoInterface::ExifToolPage); 0215 } 0216 } 0217 0218 void DNGConverterDialog::slotClose() 0219 { 0220 // Stop current conversion if necessary 0221 0222 if (d->busy) 0223 { 0224 slotStartStop(); 0225 } 0226 0227 saveSettings(); 0228 d->listView->listView()->clear(); 0229 d->fileList.clear(); 0230 accept(); 0231 } 0232 0233 void DNGConverterDialog::slotDefault() 0234 { 0235 d->dngSettings->setDefaultSettings(); 0236 d->conflictSettings->resetToDefault(); 0237 } 0238 0239 void DNGConverterDialog::readSettings() 0240 { 0241 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0242 KConfigGroup group = config->group(QLatin1String("DNGConverter Settings")); 0243 0244 d->dngSettings->setBackupOriginalRawFile(group.readEntry("BackupOriginalRawFile", false)); 0245 d->dngSettings->setCompressLossLess(group.readEntry("CompressLossLess", true)); 0246 d->dngSettings->setPreviewMode(group.readEntry("PreviewMode", (int)(DNGWriter::FULL_SIZE))); 0247 d->conflictSettings->readSettings(group); 0248 } 0249 0250 void DNGConverterDialog::saveSettings() 0251 { 0252 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0253 KConfigGroup group = config->group(QLatin1String("DNGConverter Settings")); 0254 0255 group.writeEntry("BackupOriginalRawFile", d->dngSettings->backupOriginalRawFile()); 0256 group.writeEntry("CompressLossLess", d->dngSettings->compressLossLess()); 0257 group.writeEntry("PreviewMode", (int)d->dngSettings->previewMode()); 0258 d->conflictSettings->writeSettings(group); 0259 } 0260 0261 void DNGConverterDialog::slotStartStop() 0262 { 0263 if (!d->busy) 0264 { 0265 d->fileList.clear(); 0266 0267 QTreeWidgetItemIterator it(d->listView->listView()); 0268 0269 while (*it) 0270 { 0271 DNGConverterListViewItem* const lvItem = dynamic_cast<DNGConverterListViewItem*>(*it); 0272 0273 if (lvItem) 0274 { 0275 if (!lvItem->isDisabled() && (lvItem->state() != DNGConverterListViewItem::Success)) 0276 { 0277 lvItem->setIcon(1, QIcon()); 0278 lvItem->setState(DNGConverterListViewItem::Waiting); 0279 d->fileList.append(lvItem->url().path()); 0280 } 0281 } 0282 0283 ++it; 0284 } 0285 0286 if (d->fileList.empty()) 0287 { 0288 QMessageBox::information(this, i18nc("@title:window", "DNG Converter"), i18n("The list does not contain any Raw files to process.")); 0289 busy(false); 0290 slotAborted(); 0291 return; 0292 } 0293 0294 d->progressBar->setMaximum(d->fileList.count()); 0295 d->progressBar->setValue(0); 0296 d->progressBar->show(); 0297 d->progressBar->progressScheduled(i18n("DNG Converter"), true, true); 0298 d->progressBar->progressThumbnailChanged(QIcon::fromTheme(QLatin1String("image-x-adobe-dng")).pixmap(22, 22)); 0299 0300 processAll(); 0301 } 0302 else 0303 { 0304 d->fileList.clear(); 0305 d->thread->cancel(); 0306 busy(false); 0307 0308 d->listView->cancelProcess(); 0309 0310 QTimer::singleShot(500, this, SLOT(slotAborted())); 0311 } 0312 } 0313 0314 void DNGConverterDialog::addItems(const QList<QUrl>& itemList) 0315 { 0316 d->listView->slotAddImages(itemList); 0317 } 0318 0319 void DNGConverterDialog::slotAborted() 0320 { 0321 d->progressBar->setValue(0); 0322 d->progressBar->hide(); 0323 d->progressBar->progressCompleted(); 0324 } 0325 0326 void DNGConverterDialog::slotIdentify() 0327 { 0328 QList<QUrl> urlList = d->listView->imageUrls(true); 0329 0330 for (QList<QUrl>::const_iterator it = urlList.constBegin() ; it != urlList.constEnd() ; ++it) 0331 { 0332 QFileInfo fi((*it).path()); 0333 0334 if (d->conflictSettings->conflictRule() == FileSaveConflictBox::OVERWRITE) 0335 { 0336 QString dest = fi.completeBaseName() + QLatin1String(".dng"); 0337 DNGConverterListViewItem* const item = dynamic_cast<DNGConverterListViewItem*>(d->listView->listView()->findItem(*it)); 0338 0339 if (item) 0340 { 0341 item->setDestFileName(dest); 0342 } 0343 } 0344 else 0345 { 0346 QString dest = fi.absolutePath() + QLatin1String("/") + fi.completeBaseName() + QLatin1String(".dng"); 0347 QFileInfo a(dest); 0348 bool fileNotFound = (a.exists()); 0349 0350 if (!fileNotFound) 0351 { 0352 dest = fi.completeBaseName() + QLatin1String(".dng"); 0353 } 0354 0355 else 0356 { 0357 int i = 0; 0358 0359 while(fileNotFound) 0360 { 0361 a = QFileInfo(dest); 0362 0363 if (!a.exists()) 0364 { 0365 fileNotFound = false; 0366 } 0367 else 0368 { 0369 i++; 0370 dest = fi.absolutePath() + 0371 QLatin1String("/") + 0372 fi.completeBaseName() + 0373 QLatin1String("_") + 0374 QString::number(i) + 0375 QLatin1String(".dng"); 0376 } 0377 } 0378 0379 dest = fi.completeBaseName() + QLatin1String("_") + QString::number(i) + QLatin1String(".dng"); 0380 } 0381 0382 DNGConverterListViewItem* const item = dynamic_cast<DNGConverterListViewItem*>(d->listView->listView()->findItem(*it)); 0383 0384 if (item) 0385 { 0386 item->setDestFileName(dest); 0387 } 0388 } 0389 } 0390 0391 if (!urlList.empty()) 0392 { 0393 d->thread->identifyRawFiles(urlList); 0394 0395 if (!d->thread->isRunning()) 0396 { 0397 d->thread->start(); 0398 } 0399 } 0400 } 0401 0402 void DNGConverterDialog::processAll() 0403 { 0404 d->thread->setBackupOriginalRawFile(d->dngSettings->backupOriginalRawFile()); 0405 d->thread->setCompressLossLess(d->dngSettings->compressLossLess()); 0406 d->thread->setPreviewMode(d->dngSettings->previewMode()); 0407 d->thread->processRawFiles(d->listView->imageUrls(true)); 0408 0409 if (!d->thread->isRunning()) 0410 { 0411 d->thread->start(); 0412 } 0413 } 0414 0415 void DNGConverterDialog::slotThreadFinished() 0416 { 0417 busy(false); 0418 slotAborted(); 0419 } 0420 0421 void DNGConverterDialog::busy(bool busy) 0422 { 0423 d->busy = busy; 0424 0425 if (d->busy) 0426 { 0427 m_buttons->button(QDialogButtonBox::Ok)->setText(i18n("&Abort")); 0428 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18n("Abort the conversion of Raw files.")); 0429 } 0430 else 0431 { 0432 m_buttons->button(QDialogButtonBox::Ok)->setText(i18n("Con&vert")); 0433 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18n("Start converting the Raw images using the current settings.")); 0434 } 0435 0436 d->dngSettings->setEnabled(!d->busy); 0437 d->conflictSettings->setEnabled(!d->busy); 0438 d->listView->listView()->viewport()->setEnabled(!d->busy); 0439 d->busy ? setCursor(Qt::WaitCursor) : unsetCursor(); 0440 } 0441 0442 void DNGConverterDialog::processed(const QUrl& url, const QString& tmpFile) 0443 { 0444 DNGConverterListViewItem* const item = dynamic_cast<DNGConverterListViewItem*>(d->listView->listView()->findItem(url)); 0445 0446 if (!item) 0447 { 0448 return; 0449 } 0450 0451 QString destFile(item->destPath()); 0452 0453 if (d->conflictSettings->conflictRule() != FileSaveConflictBox::OVERWRITE) 0454 { 0455 if (!QFile::exists(destFile)) 0456 { 0457 item->setStatus(i18n("Failed to save image")); 0458 } 0459 } 0460 0461 if (!destFile.isEmpty()) 0462 { 0463 if (DMetadata::hasSidecar(tmpFile)) 0464 { 0465 if (!DFileOperations::renameFile(DMetadata::sidecarPath(tmpFile), 0466 DMetadata::sidecarPath(destFile))) 0467 { 0468 item->setStatus(i18n("Failed to move sidecar")); 0469 } 0470 } 0471 0472 if (!DFileOperations::renameFile(tmpFile, destFile)) 0473 { 0474 item->setStatus(i18n("Failed to save image.")); 0475 d->listView->processed(url, false); 0476 } 0477 else 0478 { 0479 item->setDestFileName(QFileInfo(destFile).fileName()); 0480 d->listView->processed(url, true); 0481 item->setStatus(i18n("Success")); 0482 } 0483 } 0484 0485 d->progressBar->setValue(d->progressBar->value()+1); 0486 } 0487 0488 void DNGConverterDialog::processingFailed(const QUrl& url, int result) 0489 { 0490 d->listView->processed(url, false); 0491 d->progressBar->setValue(d->progressBar->value()+1); 0492 0493 DNGConverterListViewItem* const item = dynamic_cast<DNGConverterListViewItem*>(d->listView->listView()->findItem(url)); 0494 0495 if (!item) 0496 { 0497 return; 0498 } 0499 0500 QString status; 0501 0502 switch (result) 0503 { 0504 case DNGWriter::PROCESS_FAILED: 0505 { 0506 status = i18n("Process failed"); 0507 break; 0508 } 0509 0510 case DNGWriter::PROCESS_CANCELED: 0511 { 0512 status = i18n("Process Canceled"); 0513 break; 0514 } 0515 0516 case DNGWriter::FILE_NOT_SUPPORTED: 0517 { 0518 status = i18n("File not supported"); 0519 break; 0520 } 0521 0522 default: 0523 { 0524 status = i18n("Internal error"); 0525 break; 0526 } 0527 } 0528 0529 item->setStatus(status); 0530 } 0531 0532 void DNGConverterDialog::slotDNGConverterAction(const DigikamGenericDNGConverterPlugin::DNGConverterActionData& ad) 0533 { 0534 QString text; 0535 0536 if (ad.starting) // Something have been started... 0537 { 0538 switch (ad.action) 0539 { 0540 case IDENTIFY: 0541 { 0542 break; 0543 } 0544 0545 case PROCESS: 0546 { 0547 busy(true); 0548 d->listView->processing(ad.fileUrl); 0549 d->progressBar->progressStatusChanged(i18n("Processing %1", ad.fileUrl.fileName())); 0550 break; 0551 } 0552 0553 default: 0554 { 0555 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "DigikamGenericDNGConverterPlugin: Unknown action"; 0556 break; 0557 } 0558 } 0559 } 0560 else 0561 { 0562 if (ad.result != DNGWriter::PROCESS_COMPLETE) // Something is failed... 0563 { 0564 switch (ad.action) 0565 { 0566 case IDENTIFY: 0567 { 0568 break; 0569 } 0570 0571 case PROCESS: 0572 { 0573 processingFailed(ad.fileUrl, ad.result); 0574 break; 0575 } 0576 0577 default: 0578 { 0579 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "DigikamGenericDNGConverterPlugin: Unknown action"; 0580 break; 0581 } 0582 } 0583 } 0584 else // Something is done... 0585 { 0586 switch (ad.action) 0587 { 0588 case IDENTIFY: 0589 { 0590 DNGConverterListViewItem* const item = dynamic_cast<DNGConverterListViewItem*>(d->listView->listView()->findItem(ad.fileUrl)); 0591 0592 if (item) 0593 { 0594 item->setIdentity(ad.message); 0595 } 0596 0597 break; 0598 } 0599 0600 case PROCESS: 0601 { 0602 processed(ad.fileUrl, ad.destPath); 0603 break; 0604 } 0605 0606 default: 0607 { 0608 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "DigikamGenericDNGConverterPlugin: Unknown action"; 0609 break; 0610 } 0611 } 0612 } 0613 } 0614 } 0615 0616 } // namespace DigikamGenericDNGConverterPlugin 0617 0618 #include "moc_dngconverterdialog.cpp"