File indexing completed on 2025-01-05 03:59:40
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-08-20 0007 * Description : editor tool template class. 0008 * 0009 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "editortool.h" 0016 0017 // Qt includes 0018 0019 #include <QWidget> 0020 #include <QTimer> 0021 #include <QIcon> 0022 #include <QApplication> 0023 0024 // KDE includes 0025 0026 #include <kconfiggroup.h> 0027 #include <ksharedconfig.h> 0028 #include <klocalizedstring.h> 0029 0030 // Local includes 0031 0032 #include "digikam_debug.h" 0033 #include "dimgthreadedfilter.h" 0034 #include "dimgthreadedanalyser.h" 0035 #include "imageguidewidget.h" 0036 #include "imageregionwidget.h" 0037 #include "histogramwidget.h" 0038 #include "histogrambox.h" 0039 #include "editortoolsettings.h" 0040 #include "editortooliface.h" 0041 0042 namespace Digikam 0043 { 0044 0045 class Q_DECL_HIDDEN EditorTool::Private 0046 { 0047 0048 public: 0049 0050 explicit Private() 0051 : initPreview (false), 0052 version (0), 0053 view (nullptr), 0054 timer (nullptr), 0055 settings (nullptr), 0056 category (FilterAction::ReproducibleFilter), 0057 plugin (nullptr) 0058 { 0059 } 0060 0061 static const QString configGroupName; 0062 static const QString configRestoreSettingsEntry; 0063 0064 bool initPreview; 0065 QString helpAnchor; 0066 QString name; 0067 int version; 0068 0069 QWidget* view; 0070 QIcon icon; 0071 QTimer* timer; 0072 0073 EditorToolSettings* settings; 0074 0075 FilterAction::Category category; 0076 0077 DPlugin* plugin; 0078 }; 0079 0080 const QString EditorTool::Private::configGroupName(QLatin1String("ImageViewer Settings")); 0081 const QString EditorTool::Private::configRestoreSettingsEntry(QLatin1String("RestoreToolSettings")); 0082 0083 // -------------------------------------------------------- 0084 0085 EditorTool::EditorTool(QObject* const parent) 0086 : QObject(parent), 0087 d (new Private) 0088 { 0089 d->timer = new QTimer(this); 0090 0091 // --- NOTE: use dynamic binding as slotPreview() is a virtual method which can be re-implemented in derived classes. 0092 0093 connect(d->timer, &QTimer::timeout, 0094 this, &EditorTool::slotPreview); 0095 } 0096 0097 EditorTool::~EditorTool() 0098 { 0099 delete d->settings; 0100 delete d->view; 0101 delete d; 0102 } 0103 0104 void EditorTool::setPlugin(DPlugin* const plugin) 0105 { 0106 d->plugin = plugin; 0107 setToolName(d->plugin->name()); 0108 setToolIcon(d->plugin->icon()); 0109 d->settings->setTool(this); 0110 } 0111 0112 DPlugin* EditorTool::plugin() const 0113 { 0114 return d->plugin; 0115 } 0116 0117 void EditorTool::init() 0118 { 0119 QTimer::singleShot(0, this, SLOT(slotInit())); 0120 } 0121 0122 void EditorTool::setInitPreview(bool b) 0123 { 0124 d->initPreview = b; 0125 } 0126 0127 QIcon EditorTool::toolIcon() const 0128 { 0129 return d->icon; 0130 } 0131 0132 void EditorTool::setToolIcon(const QIcon& icon) 0133 { 0134 d->icon = icon; 0135 } 0136 0137 QString EditorTool::toolName() const 0138 { 0139 return d->name; 0140 } 0141 0142 void EditorTool::setToolName(const QString& name) 0143 { 0144 d->name = name; 0145 } 0146 0147 int EditorTool::toolVersion() const 0148 { 0149 return d->version; 0150 } 0151 0152 void EditorTool::setToolVersion(const int version) 0153 { 0154 d->version = version; 0155 } 0156 0157 FilterAction::Category EditorTool::toolCategory() const 0158 { 0159 return d->category; 0160 } 0161 0162 void EditorTool::setToolCategory(const FilterAction::Category category) 0163 { 0164 d->category = category; 0165 } 0166 0167 void EditorTool::setPreviewModeMask(int mask) 0168 { 0169 EditorToolIface::editorToolIface()->setPreviewModeMask(mask); 0170 } 0171 0172 QWidget* EditorTool::toolView() const 0173 { 0174 return d->view; 0175 } 0176 0177 void EditorTool::setToolView(QWidget* const view) 0178 { 0179 d->view = view; 0180 0181 // Will be unblocked in slotInit() 0182 // This will prevent resize event signals Q_EMIT during tool init. 0183 0184 d->view->blockSignals(true); 0185 0186 ImageGuideWidget* const wgt = dynamic_cast<ImageGuideWidget*>(d->view); 0187 0188 if (wgt) 0189 { 0190 connect(d->view, SIGNAL(spotPositionChangedFromOriginal(Digikam::DColor,QPoint)), 0191 this, SLOT(slotUpdateSpotInfo(Digikam::DColor,QPoint))); 0192 0193 connect(d->view, SIGNAL(spotPositionChangedFromTarget(Digikam::DColor,QPoint)), 0194 this, SLOT(slotUpdateSpotInfo(Digikam::DColor,QPoint))); 0195 } 0196 } 0197 0198 EditorToolSettings* EditorTool::toolSettings() const 0199 { 0200 return d->settings; 0201 } 0202 0203 void EditorTool::setToolSettings(EditorToolSettings* const settings) 0204 { 0205 d->settings = settings; 0206 d->settings->setTool(this); 0207 0208 connect(d->settings, SIGNAL(signalOkClicked()), 0209 this, SLOT(slotOk())); 0210 0211 connect(d->settings, SIGNAL(signalCancelClicked()), 0212 this, SLOT(slotCancel())); 0213 0214 connect(d->settings, SIGNAL(signalDefaultClicked()), 0215 this, SLOT(slotResetSettings())); 0216 0217 connect(d->settings, SIGNAL(signalSaveAsClicked()), 0218 this, SLOT(slotSaveAsSettings())); 0219 0220 connect(d->settings, SIGNAL(signalLoadClicked()), 0221 this, SLOT(slotLoadSettings())); 0222 0223 connect(d->settings, SIGNAL(signalTryClicked()), 0224 this, SLOT(slotPreview())); 0225 0226 connect(d->settings, SIGNAL(signalChannelChanged()), 0227 this, SLOT(slotChannelChanged())); 0228 0229 connect(d->settings, SIGNAL(signalScaleChanged()), 0230 this, SLOT(slotScaleChanged())); 0231 0232 // Will be unblocked in slotInit() 0233 // This will prevent signals Q_EMIT during tool init. 0234 0235 d->settings->blockSignals(true); 0236 } 0237 0238 void EditorTool::slotInit() 0239 { 0240 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0241 KConfigGroup group = config->group(d->configGroupName); 0242 0243 // We always have to call readSettings(), some tools need it. 0244 0245 if (group.readEntry(d->configRestoreSettingsEntry, true)) 0246 { 0247 readSettings(); 0248 } 0249 else 0250 { 0251 slotResetSettings(); 0252 } 0253 0254 // Unlock signals from preview and settings widgets when init is done. 0255 0256 d->view->blockSignals(false); 0257 d->settings->blockSignals(false); 0258 0259 if (d->initPreview) 0260 { 0261 slotTimer(); 0262 } 0263 } 0264 0265 void EditorTool::setToolHelp(const QString& anchor) 0266 { 0267 d->helpAnchor = anchor; 0268 0269 // TODO: use this anchor with editor Help menu 0270 } 0271 0272 QString EditorTool::toolHelp() const 0273 { 0274 if (d->helpAnchor.isEmpty()) 0275 { 0276 return (objectName() + QLatin1String(".anchor")); 0277 } 0278 0279 return d->helpAnchor; 0280 } 0281 0282 void EditorTool::setBusy(bool state) 0283 { 0284 d->settings->setBusy(state); 0285 } 0286 0287 void EditorTool::readSettings() 0288 { 0289 d->settings->readSettings(); 0290 } 0291 0292 void EditorTool::writeSettings() 0293 { 0294 d->settings->writeSettings(); 0295 } 0296 0297 void EditorTool::slotResetSettings() 0298 { 0299 d->settings->resetSettings(); 0300 } 0301 0302 void EditorTool::slotTimer() 0303 { 0304 d->timer->setSingleShot(true); 0305 d->timer->start(500); 0306 } 0307 0308 void EditorTool::slotOk() 0309 { 0310 writeSettings(); 0311 finalRendering(); 0312 0313 Q_EMIT okClicked(); 0314 } 0315 0316 void EditorTool::slotCancel() 0317 { 0318 writeSettings(); 0319 0320 Q_EMIT cancelClicked(); 0321 } 0322 0323 void EditorTool::slotCloseTool() 0324 { 0325 slotCancel(); 0326 } 0327 0328 void EditorTool::slotApplyTool() 0329 { 0330 slotOk(); 0331 } 0332 0333 void EditorTool::slotPreviewModeChanged() 0334 { 0335 slotPreview(); 0336 } 0337 0338 void EditorTool::setBackgroundColor(const QColor& bg) 0339 { 0340 ImageGuideWidget* const view = dynamic_cast<ImageGuideWidget*>(d->view); 0341 QPalette palette; 0342 0343 if (view) 0344 { 0345 palette.setColor(view->backgroundRole(), bg); 0346 view->setPalette(palette); 0347 } 0348 0349 ImageRegionWidget* const view2 = dynamic_cast<ImageRegionWidget*>(d->view); 0350 0351 if (view2) 0352 { 0353 palette.setColor(view2->backgroundRole(), bg); 0354 view2->setPalette(palette); 0355 } 0356 } 0357 0358 void EditorTool::ICCSettingsChanged() 0359 { 0360 ImageGuideWidget* const view = dynamic_cast<ImageGuideWidget*>(d->view); 0361 0362 if (view) 0363 { 0364 view->ICCSettingsChanged(); 0365 } 0366 0367 ImageRegionWidget* const view2 = dynamic_cast<ImageRegionWidget*>(d->view); 0368 0369 if (view2) 0370 { 0371 view2->ICCSettingsChanged(); 0372 } 0373 } 0374 0375 void EditorTool::exposureSettingsChanged() 0376 { 0377 ImageGuideWidget* const view = dynamic_cast<ImageGuideWidget*>(d->view); 0378 0379 if (view) 0380 { 0381 view->exposureSettingsChanged(); 0382 } 0383 0384 ImageRegionWidget* const view2 = dynamic_cast<ImageRegionWidget*>(d->view); 0385 0386 if (view2) 0387 { 0388 view2->exposureSettingsChanged(); 0389 } 0390 } 0391 0392 void EditorTool::setToolInfoMessage(const QString& txt) 0393 { 0394 EditorToolIface::editorToolIface()->setToolInfoMessage(txt); 0395 } 0396 0397 void EditorTool::slotUpdateSpotInfo(const DColor& col, const QPoint& point) 0398 { 0399 DColor color = col; 0400 setToolInfoMessage(i18n("(%1,%2) RGBA:%3,%4,%5,%6", 0401 point.x(), point.y(), 0402 color.red(), color.green(), 0403 color.blue(), color.alpha())); 0404 } 0405 0406 // ---------------------------------------------------------------- 0407 0408 class Q_DECL_HIDDEN EditorToolThreaded::Private 0409 { 0410 0411 public: 0412 0413 explicit Private() 0414 : delFilter (true), 0415 currentRenderingMode(EditorToolThreaded::NoneRendering), 0416 threadedFilter (nullptr), 0417 threadedAnalyser (nullptr) 0418 { 0419 } 0420 0421 bool delFilter; 0422 0423 EditorToolThreaded::RenderingMode currentRenderingMode; 0424 0425 QString progressMess; 0426 0427 DImgThreadedFilter* threadedFilter; 0428 DImgThreadedAnalyser* threadedAnalyser; 0429 }; 0430 0431 EditorToolThreaded::EditorToolThreaded(QObject* const parent) 0432 : EditorTool(parent), 0433 d (new Private) 0434 { 0435 } 0436 0437 EditorToolThreaded::~EditorToolThreaded() 0438 { 0439 delete d->threadedFilter; 0440 delete d; 0441 } 0442 0443 EditorToolThreaded::RenderingMode EditorToolThreaded::renderingMode() const 0444 { 0445 return d->currentRenderingMode; 0446 } 0447 0448 void EditorToolThreaded::setProgressMessage(const QString& mess) 0449 { 0450 d->progressMess = mess; 0451 } 0452 0453 DImgThreadedFilter* EditorToolThreaded::filter() const 0454 { 0455 return d->threadedFilter; 0456 } 0457 0458 void EditorToolThreaded::slotInit() 0459 { 0460 EditorTool::slotInit(); 0461 0462 QWidget* const view = toolView(); 0463 0464 if (dynamic_cast<ImageGuideWidget*>(view)) 0465 { 0466 connect(view, SIGNAL(signalResized()), 0467 this, SLOT(slotResized())); 0468 } 0469 0470 if (dynamic_cast<ImageRegionWidget*>(view)) 0471 { 0472 connect(view, SIGNAL(signalOriginalClipFocusChanged()), 0473 this, SLOT(slotTimer())); 0474 } 0475 } 0476 0477 void EditorToolThreaded::setFilter(DImgThreadedFilter* const filter) 0478 { 0479 delete d->threadedFilter; 0480 d->threadedFilter = filter; 0481 0482 connect(d->threadedFilter, SIGNAL(started()), 0483 this, SLOT(slotFilterStarted())); 0484 0485 connect(d->threadedFilter, SIGNAL(finished(bool)), 0486 this, SLOT(slotFilterFinished(bool))); 0487 0488 connect(d->threadedFilter, SIGNAL(progress(int)), 0489 this, SLOT(slotProgress(int))); 0490 0491 d->threadedFilter->startFilter(); 0492 } 0493 0494 DImgThreadedAnalyser* EditorToolThreaded::analyser() const 0495 { 0496 return d->threadedAnalyser; 0497 } 0498 0499 void EditorToolThreaded::setAnalyser(DImgThreadedAnalyser* const analyser) 0500 { 0501 qCDebug(DIGIKAM_GENERAL_LOG) << "Analys " << toolName() << " started..."; 0502 0503 toolSettings()->enableButton(EditorToolSettings::Ok, false); 0504 toolSettings()->enableButton(EditorToolSettings::SaveAs, false); 0505 toolSettings()->enableButton(EditorToolSettings::Load, false); 0506 toolSettings()->enableButton(EditorToolSettings::Default, false); 0507 toolSettings()->enableButton(EditorToolSettings::Try, false); 0508 toolView()->setEnabled(false); 0509 0510 EditorToolIface::editorToolIface()->setToolStartProgress(d->progressMess.isEmpty() ? toolName() : d->progressMess); 0511 qApp->setOverrideCursor(Qt::WaitCursor); 0512 0513 delete d->threadedAnalyser; 0514 d->threadedAnalyser = analyser; 0515 0516 connect(d->threadedAnalyser, SIGNAL(started()), 0517 this, SLOT(slotAnalyserStarted())); 0518 0519 connect(d->threadedAnalyser, SIGNAL(finished(bool)), 0520 this, SLOT(slotAnalyserFinished(bool))); 0521 0522 connect(d->threadedAnalyser, SIGNAL(progress(int)), 0523 this, SLOT(slotProgress(int))); 0524 0525 d->threadedAnalyser->startFilter(); 0526 } 0527 0528 void EditorToolThreaded::slotResized() 0529 { 0530 if (d->currentRenderingMode == EditorToolThreaded::FinalRendering) 0531 { 0532 toolView()->update(); 0533 return; 0534 } 0535 else if (d->currentRenderingMode == EditorToolThreaded::PreviewRendering) 0536 { 0537 if (filter()) 0538 { 0539 filter()->cancelFilter(); 0540 } 0541 } 0542 0543 QTimer::singleShot(0, this, SLOT(slotPreview())); 0544 } 0545 0546 void EditorToolThreaded::slotAbort() 0547 { 0548 d->currentRenderingMode = EditorToolThreaded::NoneRendering; 0549 0550 if (analyser()) 0551 { 0552 analyser()->cancelFilter(); 0553 } 0554 0555 if (filter()) 0556 { 0557 filter()->cancelFilter(); 0558 } 0559 0560 EditorToolIface::editorToolIface()->setToolStopProgress(); 0561 0562 toolSettings()->enableButton(EditorToolSettings::Ok, true); 0563 toolSettings()->enableButton(EditorToolSettings::Load, true); 0564 toolSettings()->enableButton(EditorToolSettings::SaveAs, true); 0565 toolSettings()->enableButton(EditorToolSettings::Try, true); 0566 toolSettings()->enableButton(EditorToolSettings::Default, true); 0567 toolView()->setEnabled(true); 0568 0569 qApp->restoreOverrideCursor(); 0570 0571 renderingFinished(); 0572 } 0573 0574 void EditorToolThreaded::slotFilterStarted() 0575 { 0576 } 0577 0578 void EditorToolThreaded::slotFilterFinished(bool success) 0579 { 0580 if (success) // Computation Completed ! 0581 { 0582 switch (d->currentRenderingMode) 0583 { 0584 case EditorToolThreaded::PreviewRendering: 0585 { 0586 qCDebug(DIGIKAM_GENERAL_LOG) << "Preview " << toolName() << " completed..."; 0587 setPreviewImage(); 0588 slotAbort(); 0589 break; 0590 } 0591 0592 case EditorToolThreaded::FinalRendering: 0593 { 0594 qCDebug(DIGIKAM_GENERAL_LOG) << "Final" << toolName() << " completed..."; 0595 setFinalImage(); 0596 EditorToolIface::editorToolIface()->setToolStopProgress(); 0597 qApp->restoreOverrideCursor(); 0598 Q_EMIT okClicked(); 0599 break; 0600 } 0601 0602 default: 0603 { 0604 break; 0605 } 0606 } 0607 } 0608 else // Computation Failed ! 0609 { 0610 switch (d->currentRenderingMode) 0611 { 0612 case EditorToolThreaded::PreviewRendering: 0613 { 0614 qCDebug(DIGIKAM_GENERAL_LOG) << "Preview " << toolName() << " failed..."; 0615 slotAbort(); 0616 break; 0617 } 0618 0619 case EditorToolThreaded::FinalRendering: 0620 default: 0621 { 0622 break; 0623 } 0624 } 0625 } 0626 } 0627 0628 void EditorToolThreaded::slotProgress(int progress) 0629 { 0630 EditorToolIface::editorToolIface()->setToolProgress(progress); 0631 } 0632 0633 void EditorToolThreaded::slotAnalyserStarted() 0634 { 0635 } 0636 0637 void EditorToolThreaded::slotAnalyserFinished(bool success) 0638 { 0639 if (success) 0640 { 0641 qCDebug(DIGIKAM_GENERAL_LOG) << "Analys " << toolName() << " completed..."; 0642 analyserCompleted(); 0643 } 0644 else 0645 { 0646 qCDebug(DIGIKAM_GENERAL_LOG) << "Analys " << toolName() << " failed..."; 0647 slotAbort(); 0648 } 0649 } 0650 0651 void EditorToolThreaded::slotOk() 0652 { 0653 // Computation already in process. 0654 0655 if (d->currentRenderingMode != EditorToolThreaded::PreviewRendering) 0656 { 0657 // See bug #305916 : cancel preview before. 0658 0659 slotAbort(); 0660 } 0661 0662 writeSettings(); 0663 0664 d->currentRenderingMode = EditorToolThreaded::FinalRendering; 0665 qCDebug(DIGIKAM_GENERAL_LOG) << "Final " << toolName() << " started..."; 0666 0667 toolSettings()->enableButton(EditorToolSettings::Ok, false); 0668 toolSettings()->enableButton(EditorToolSettings::SaveAs, false); 0669 toolSettings()->enableButton(EditorToolSettings::Load, false); 0670 toolSettings()->enableButton(EditorToolSettings::Default, false); 0671 toolSettings()->enableButton(EditorToolSettings::Try, false); 0672 toolView()->setEnabled(false); 0673 0674 EditorToolIface::editorToolIface()->setToolStartProgress(d->progressMess.isEmpty() ? toolName() : d->progressMess); 0675 qApp->setOverrideCursor(Qt::WaitCursor); 0676 0677 if (d->delFilter && d->threadedFilter) 0678 { 0679 delete d->threadedFilter; 0680 d->threadedFilter = nullptr; 0681 } 0682 0683 prepareFinal(); 0684 } 0685 0686 void EditorToolThreaded::slotPreview() 0687 { 0688 // Computation already in process. 0689 0690 if (d->currentRenderingMode != EditorToolThreaded::NoneRendering) 0691 { 0692 return; 0693 } 0694 0695 d->currentRenderingMode = EditorToolThreaded::PreviewRendering; 0696 qCDebug(DIGIKAM_GENERAL_LOG) << "Preview " << toolName() << " started..."; 0697 0698 toolSettings()->enableButton(EditorToolSettings::Ok, false); 0699 toolSettings()->enableButton(EditorToolSettings::SaveAs, false); 0700 toolSettings()->enableButton(EditorToolSettings::Load, false); 0701 toolSettings()->enableButton(EditorToolSettings::Default, false); 0702 toolSettings()->enableButton(EditorToolSettings::Try, false); 0703 toolView()->setEnabled(false); 0704 0705 EditorToolIface::editorToolIface()->setToolStartProgress(d->progressMess.isEmpty() ? toolName() : d->progressMess); 0706 qApp->setOverrideCursor(Qt::WaitCursor); 0707 0708 if (d->delFilter && d->threadedFilter) 0709 { 0710 delete d->threadedFilter; 0711 d->threadedFilter = nullptr; 0712 } 0713 0714 preparePreview(); 0715 } 0716 0717 void EditorToolThreaded::slotCancel() 0718 { 0719 writeSettings(); 0720 slotAbort(); 0721 Q_EMIT cancelClicked(); 0722 } 0723 0724 void EditorToolThreaded::deleteFilterInstance(bool b) 0725 { 0726 d->delFilter = b; 0727 } 0728 0729 } // namespace Digikam 0730 0731 #include "moc_editortool.cpp"