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"