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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-11-14
0007  * Description : a presentation tool.
0008  *
0009  * SPDX-FileCopyrightText: 2007-2009 by Valerio Fuoglio <valerio dot fuoglio at gmail dot com>
0010  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * Parts of this code are based on
0013  * smoothslidesaver by Carsten Weinhold <carsten dot weinhold at gmx dot de>
0014  * and slideshowgl by Renchi Raju <renchi dot raju at gmail dot com>
0015  *
0016  * SPDX-License-Identifier: GPL-2.0-or-later
0017  *
0018  * ============================================================ */
0019 
0020 #include "presentationkb_p.h"
0021 
0022 // C++ includes
0023 
0024 #include <cmath>
0025 
0026 // Qt includes
0027 
0028 #include <QImage>
0029 #include <QPainter>
0030 #include <QFont>
0031 #include <QCursor>
0032 #include <QPixmap>
0033 #include <QApplication>
0034 #include <QScreen>
0035 #include <QWindow>
0036 
0037 // KDE includes
0038 
0039 #include <klocalizedstring.h>
0040 #include <ksharedconfig.h>
0041 #include <kconfiggroup.h>
0042 
0043 namespace DigikamGenericPresentationPlugin
0044 {
0045 
0046 KBViewTrans::KBViewTrans(bool zoomIn, float relAspect)
0047     : m_deltaX      (0.0),
0048       m_deltaY      (0.0),
0049       m_deltaScale  (0.0),
0050       m_baseScale   (0.0),
0051       m_baseX       (0.0),
0052       m_baseY       (0.0),
0053       m_xScale      (0.0),
0054       m_yScale      (0.0)
0055 {
0056     int i        = 0;
0057 
0058     // randomly select sizes of start and end viewport
0059 
0060     double s[2]  = { 0.0 };
0061 
0062     do
0063     {
0064         s[0]  = 0.3 * rnd() + 1.0;
0065         s[1]  = 0.3 * rnd() + 1.0;
0066     }
0067     while ((fabs(s[0] - s[1]) < 0.15) && (++i < 10));
0068 
0069     if (zoomIn ^ (s[0] > s[1]))
0070     {
0071         double tmp = s[0];
0072         s[0]       = s[1];
0073         s[1]       = tmp;
0074     }
0075 
0076     m_deltaScale = s[1] / s[0] - 1.0;
0077     m_baseScale  = s[0];
0078 
0079     // additional scale factors to ensure proper m_aspect of the displayed image
0080 
0081     double x[2]       = { 0.0 };
0082     double y[2]       = { 0.0 };
0083     double xMargin[2] = { 0.0 };
0084     double yMargin[2] = { 0.0 };
0085     double bestDist   = 0.0;;
0086     double sx         = 0.0;
0087     double sy         = 0.0;
0088 
0089     if (relAspect > 1.0)
0090     {
0091         sx = 1.0;
0092         sy = relAspect;
0093     }
0094     else
0095     {
0096         sx = 1.0 / relAspect;
0097         sy = 1.0;
0098     }
0099 
0100     m_xScale   = sx;
0101     m_yScale   = sy;
0102 
0103     // calculate path
0104 
0105     xMargin[0] = (s[0] * sx - 1.0) / 2.0;
0106     yMargin[0] = (s[0] * sy - 1.0) / 2.0;
0107     xMargin[1] = (s[1] * sx - 1.0) / 2.0;
0108     yMargin[1] = (s[1] * sy - 1.0) / 2.0;
0109 
0110     i        = 0;
0111     bestDist = 0.0;
0112 
0113     do
0114     {
0115         double sign = rndSign();
0116         x[0]        = xMargin[0] * (0.2 * rnd() + 0.8) *  sign;
0117         y[0]        = yMargin[0] * (0.2 * rnd() + 0.8) * -sign;
0118         x[1]        = xMargin[1] * (0.2 * rnd() + 0.8) * -sign;
0119         y[1]        = yMargin[1] * (0.2 * rnd() + 0.8) *  sign;
0120 
0121         if (fabs(x[1] - x[0]) + fabs(y[1] - y[0]) > bestDist)
0122         {
0123             m_baseX  = x[0];
0124             m_baseY  = y[0];
0125             m_deltaX = x[1] - x[0];
0126             m_deltaY = y[1] - y[0];
0127             bestDist = fabs(m_deltaX) + fabs(m_deltaY);
0128         }
0129 
0130     }
0131     while ((bestDist < 0.3) && (++i < 10));
0132 }
0133 
0134 KBViewTrans::KBViewTrans()
0135     : m_deltaX      (0.0),
0136       m_deltaY      (0.0),
0137       m_deltaScale  (0.0),
0138       m_baseScale   (0.0),
0139       m_baseX       (0.0),
0140       m_baseY       (0.0),
0141       m_xScale      (0.0),
0142       m_yScale      (0.0)
0143 {
0144 }
0145 
0146 KBViewTrans::~KBViewTrans()
0147 {
0148 }
0149 
0150 float KBViewTrans::transX(float pos) const
0151 {
0152     return (m_baseX + m_deltaX * pos);
0153 }
0154 
0155 float KBViewTrans::transY(float pos) const
0156 {
0157     return (m_baseY + m_deltaY * pos);
0158 }
0159 
0160 float KBViewTrans::scale (float pos) const
0161 {
0162     return (m_baseScale * (1.0 + m_deltaScale * pos));
0163 }
0164 
0165 float KBViewTrans::xScaleCorrect() const
0166 {
0167     return m_xScale;
0168 }
0169 
0170 float KBViewTrans::yScaleCorrect() const
0171 {
0172     return m_yScale;
0173 }
0174 
0175 double KBViewTrans::rnd() const
0176 {
0177     return QRandomGenerator::global()->generateDouble();
0178 }
0179 
0180 double KBViewTrans::rndSign() const
0181 {
0182     return ((QRandomGenerator::global()->bounded(2U) == 0) ? 1.0 : -1.0);
0183 }
0184 
0185 // -------------------------------------------------------------------------
0186 
0187 KBImage::KBImage(KBViewTrans* const viewTrans, float aspect)
0188     : m_viewTrans   (viewTrans),
0189       m_aspect      (aspect),
0190       m_pos         (0.0),
0191       m_opacity     (0.0),
0192       m_texture     (nullptr)
0193 {
0194     m_paint = (m_viewTrans) ? true : false;
0195 }
0196 
0197 KBImage::~KBImage()
0198 {
0199     if (m_texture)
0200     {
0201         m_texture->destroy();
0202     }
0203 
0204     delete m_viewTrans;
0205     delete m_texture;
0206 }
0207 
0208 // -------------------------------------------------------------------------
0209 
0210 PresentationKB::PresentationKB(PresentationContainer* const sharedData)
0211     : QOpenGLWidget(),
0212       d            (new Private)
0213 {
0214     setAttribute(Qt::WA_DeleteOnClose);
0215     setContextMenuPolicy(Qt::PreventContextMenu);
0216 
0217 #ifdef Q_OS_WIN
0218 
0219     setWindowFlags(Qt::Popup               |
0220                    Qt::FramelessWindowHint |
0221                    Qt::WindowStaysOnTopHint);
0222 
0223 #else
0224 
0225     setWindowState(windowState() | Qt::WindowFullScreen);
0226 
0227 #endif
0228 
0229     QScreen* screen = qApp->primaryScreen();
0230 
0231     if (QWidget* const widget = qApp->activeWindow())
0232     {
0233         if (QWindow* const window = widget->windowHandle())
0234         {
0235             screen = window->screen();
0236         }
0237     }
0238 
0239     QRect deskRect = screen->geometry();
0240     d->deskX       = deskRect.x();
0241     d->deskY       = deskRect.y();
0242     d->deskWidth   = deskRect.width();
0243     d->deskHeight  = deskRect.height();
0244 
0245     move(d->deskX, d->deskY);
0246     resize(d->deskWidth, d->deskHeight);
0247 
0248     d->sharedData          = sharedData;
0249     d->sharedData->display = this;
0250 
0251     readSettings();
0252 
0253     unsigned frameRate;
0254 
0255     if (d->forceFrameRate == 0)
0256     {
0257         frameRate = qRound(screen->refreshRate() * 2);
0258     }
0259     else
0260     {
0261         frameRate = d->forceFrameRate;
0262     }
0263 
0264     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Frame Rate : " << frameRate;
0265 
0266     d->image[0]        = new KBImage(nullptr);
0267     d->image[1]        = new KBImage(nullptr);
0268     d->step            = 1.0 / ((float)(d->delay * frameRate));
0269     d->enableSameSpeed = d->sharedData->kbEnableSameSpeed;
0270     d->imageLoadThread = new KBImageLoader(d->sharedData, width(), height());
0271     d->timer           = new QTimer(this);
0272 
0273     connect(d->timer, SIGNAL(timeout()),
0274             this, SLOT(moveSlot()));
0275 
0276     // -- playback widget -------------------------------
0277 
0278 #ifdef HAVE_MEDIAPLAYER
0279 
0280     d->playbackWidget = new PresentationAudioWidget(this, d->sharedData->soundtrackUrls, d->sharedData);
0281     d->playbackWidget->hide();
0282     d->playbackWidget->move(0, 0);
0283 
0284 #endif
0285 
0286     // -- hide cursor when not moved --------------------
0287 
0288     d->mouseMoveTimer = new QTimer(this);
0289     d->mouseMoveTimer->setSingleShot(true);
0290 
0291     connect(d->mouseMoveTimer, SIGNAL(timeout()),
0292             this, SLOT(slotMouseMoveTimeOut()));
0293 
0294     setMouseTracking(true);
0295     slotMouseMoveTimeOut();
0296 
0297     // -- load image and let's start
0298 
0299     d->imageLoadThread->start();
0300     d->timer->start(1000 / frameRate);
0301 
0302 #ifdef HAVE_MEDIAPLAYER
0303 
0304     if (d->sharedData->soundtrackPlay)
0305     {
0306         d->playbackWidget->slotPlay();
0307     }
0308 
0309 #endif
0310 
0311 }
0312 
0313 PresentationKB::~PresentationKB()
0314 {
0315 
0316 #ifdef HAVE_MEDIAPLAYER
0317 
0318     d->playbackWidget->slotStop();
0319 
0320 #endif
0321 
0322     d->timer->stop();
0323     d->mouseMoveTimer->stop();
0324 
0325     delete d->effect;
0326     delete d->image[0];
0327     delete d->image[1];
0328 
0329     if (d->endTexture)
0330     {
0331         d->endTexture->destroy();
0332     }
0333 
0334     delete d->endTexture;
0335 
0336     d->imageLoadThread->quit();
0337     bool terminated = d->imageLoadThread->wait(10000);
0338 
0339     if (!terminated)
0340     {
0341         d->imageLoadThread->terminate();
0342         d->imageLoadThread->wait(3000);
0343     }
0344 
0345     delete d->imageLoadThread;
0346     delete d;
0347 }
0348 
0349 float PresentationKB::aspect() const
0350 {
0351     return ((float)width() / (float)height());
0352 }
0353 
0354 void PresentationKB::setNewKBEffect()
0355 {
0356     KBEffect::Type type;
0357     bool needFadeIn = ((d->effect == nullptr) || (d->effect->type() == KBEffect::Fade));
0358 
0359     // we currently only have two effects
0360 
0361     if      (d->disableFadeInOut)
0362     {
0363         type = KBEffect::Blend;
0364     }
0365     else if (d->disableCrossFade)
0366     {
0367         type = KBEffect::Fade;
0368     }
0369     else
0370     {
0371         type = KBEffect::chooseKBEffect((d->effect) ? d->effect->type() : KBEffect::Fade);
0372     }
0373 
0374     delete d->effect;
0375 
0376     switch (type)
0377     {
0378         case KBEffect::Fade:
0379         {
0380             d->effect = new FadeKBEffect(this, needFadeIn);
0381             break;
0382         }
0383 
0384         case KBEffect::Blend:
0385         {
0386             d->effect = new BlendKBEffect(this, needFadeIn);
0387             break;
0388         }
0389 
0390         default:
0391         {
0392             qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown transition effect, falling back to crossfade";
0393             d->effect = new BlendKBEffect(this, needFadeIn);
0394             break;
0395         }
0396     }
0397 }
0398 
0399 void PresentationKB::moveSlot()
0400 {
0401     if (d->initialized)
0402     {
0403         if (d->effect->done())
0404         {
0405             setNewKBEffect();
0406             d->imageLoadThread->requestNewImage();
0407             d->endOfShow = !d->haveImages;
0408         }
0409 
0410         if (d->enableSameSpeed)
0411         {
0412             d->effect->advanceTime(d->stepSameSpeed);
0413         }
0414         else
0415         {
0416             d->effect->advanceTime(d->step);
0417         }
0418     }
0419 
0420     update();
0421 }
0422 
0423 bool PresentationKB::setupNewImage(int idx)
0424 {
0425     Q_ASSERT(idx >= 0 && idx < 2);
0426 
0427     if (!d->haveImages)
0428     {
0429         return false;
0430     }
0431 
0432     bool ok   = false;
0433     d->zoomIn = !d->zoomIn;
0434 
0435     if (d->imageLoadThread->grabImage())
0436     {
0437         delete d->image[idx];
0438 
0439         // we have the image lock and there is an image
0440 
0441         float imageAspect            = d->imageLoadThread->imageAspect();
0442         KBViewTrans* const viewTrans = new KBViewTrans(d->zoomIn, aspect() / imageAspect);
0443         d->image[idx]                = new KBImage(viewTrans, imageAspect);
0444 
0445         applyTexture(d->image[idx], d->imageLoadThread->image());
0446         ok                           = true;
0447 
0448     }
0449     else
0450     {
0451         d->haveImages = false;
0452     }
0453 
0454     // don't forget to release the lock on the copy of the image
0455     // owned by the image loader thread
0456 
0457     d->imageLoadThread->ungrabImage();
0458 
0459     return ok;
0460 }
0461 
0462 void PresentationKB::startSlideShowOnce()
0463 {
0464     // when the image loader thread is ready, it will already have loaded
0465     // the first image
0466     if ((d->initialized == false) && d->imageLoadThread->ready())
0467     {
0468         setupNewImage(0);                      // setup the first image and
0469         d->imageLoadThread->requestNewImage(); // load the next one in background
0470         setNewKBEffect();                      // set the initial effect
0471         if (d->enableSameSpeed)
0472         {
0473             d->stepSameSpeed = d->step / d->imageLoadThread->imageAspect();
0474         }
0475 
0476         d->initialized = true;
0477     }
0478 }
0479 
0480 void PresentationKB::swapImages()
0481 {
0482     KBImage* const tmp = d->image[0];
0483     d->image[0]        = d->image[1];
0484     d->image[1]        = tmp;
0485 }
0486 
0487 void PresentationKB::initializeGL()
0488 {
0489     // Enable Texture Mapping
0490 
0491     glEnable(GL_TEXTURE_2D);
0492 
0493     // Clear The Background Color
0494 
0495     glClearColor(0.0, 0.0, 0.0, 1.0f);
0496 
0497     glEnable(GL_TEXTURE_2D);
0498     glShadeModel(GL_SMOOTH);
0499 
0500     // Turn Blending On
0501 
0502     glEnable(GL_BLEND);
0503 
0504     // Blending Function For Translucency Based On Source Alpha Value
0505 
0506     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
0507 
0508     // Enable perspective vision
0509 
0510     glClearDepth(1.0f);
0511 }
0512 
0513 void PresentationKB::paintGL()
0514 {
0515     startSlideShowOnce();
0516 
0517     glDisable(GL_DEPTH_TEST);
0518     glDepthMask(GL_FALSE);
0519 
0520     // only clear the color buffer, if none of the active images is fully opaque
0521 
0522     if (!((d->image[0]->m_paint && (d->image[0]->m_opacity == 1.0)) ||
0523         (d->image[1]->m_paint && (d->image[1]->m_opacity == 1.0))))
0524     {
0525         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
0526     }
0527 
0528     glLoadIdentity();
0529     glMatrixMode(GL_PROJECTION);
0530     glLoadIdentity();
0531     glMatrixMode(GL_MODELVIEW);
0532     glLoadIdentity();
0533 
0534     if (d->endOfShow)
0535     {
0536         endOfShow();
0537         d->timer->stop();
0538     }
0539     else
0540     {
0541         if (d->image[1]->m_paint)
0542         {
0543             paintTexture(d->image[1]);
0544         }
0545 
0546         if (d->image[0]->m_paint)
0547         {
0548             paintTexture(d->image[0]);
0549         }
0550     }
0551 
0552     glFlush();
0553 }
0554 
0555 void PresentationKB::resizeGL(int w, int h)
0556 {
0557     glViewport(0, 0, (GLint)w, (GLint)h);
0558 }
0559 
0560 void PresentationKB::applyTexture(KBImage* const img, const QImage &texture)
0561 {
0562     /* create the texture */
0563 
0564     img->m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
0565     img->m_texture->setData(texture.mirrored());
0566     img->m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
0567     img->m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
0568     img->m_texture->bind();
0569 }
0570 
0571 void PresentationKB::paintTexture(KBImage* const img)
0572 {
0573     glMatrixMode(GL_MODELVIEW);
0574     glLoadIdentity();
0575 
0576     float sx = img->m_viewTrans->xScaleCorrect();
0577     float sy = img->m_viewTrans->yScaleCorrect();
0578 
0579     glTranslatef(img->m_viewTrans->transX(img->m_pos) * 2.0, img->m_viewTrans->transY(img->m_pos) * 2.0, 0.0);
0580     glScalef(img->m_viewTrans->scale(img->m_pos), img->m_viewTrans->scale(img->m_pos), 0.0);
0581 
0582     img->m_texture->bind();
0583 
0584     glBegin(GL_QUADS);
0585     {
0586         glColor4f(1.0, 1.0, 1.0, img->m_opacity);
0587         glTexCoord2f(0, 0);
0588         glVertex3f(-sx, -sy, 0);
0589 
0590         glTexCoord2f(1, 0);
0591         glVertex3f(sx, -sy, 0);
0592 
0593         glTexCoord2f(1, 1);
0594         glVertex3f(sx, sy, 0);
0595 
0596         glTexCoord2f(0, 1);
0597         glVertex3f(-sx, sy, 0);
0598     }
0599     glEnd();
0600 }
0601 
0602 void PresentationKB::readSettings()
0603 {
0604     KSharedConfigPtr config = KSharedConfig::openConfig();
0605     KConfigGroup group      = config->group(QLatin1String("Presentation Settings"));
0606 
0607     d->delay                = group.readEntry("Delay", 8000) / 1000;
0608     d->disableFadeInOut     = group.readEntry("KB Disable FadeInOut", false);
0609     d->disableCrossFade     = group.readEntry("KB Disable Crossfade", false);
0610     d->forceFrameRate       = group.readEntry("KB Force Framerate", 0);
0611 
0612     if (d->delay < 5)
0613     {
0614         d->delay = 5;
0615     }
0616 
0617     if (d->forceFrameRate > 120)
0618     {
0619         d->forceFrameRate = 120;
0620     }
0621 }
0622 
0623 void PresentationKB::endOfShow()
0624 {
0625     QPixmap pix(512, 512);
0626     pix.fill(Qt::black);
0627 
0628     QFont fn(font());
0629     fn.setPointSize(fn.pointSize() + 10);
0630     fn.setBold(true);
0631 
0632     QPainter p(&pix);
0633     p.setPen(Qt::white);
0634     p.setFont(fn);
0635     p.drawText(20, 50,  i18n("SlideShow Completed"));
0636     p.drawText(20, 100, i18n("Click to Exit..."));
0637     p.end();
0638 
0639     /* create the texture */
0640 
0641     d->endTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
0642     d->endTexture->setData(pix.toImage().mirrored());
0643     d->endTexture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
0644     d->endTexture->setMagnificationFilter(QOpenGLTexture::Linear);
0645     d->endTexture->bind();
0646 
0647     /* paint the texture */
0648 
0649     glMatrixMode(GL_MODELVIEW);
0650     glLoadIdentity();
0651 
0652     glBegin(GL_QUADS);
0653     {
0654         glColor4f(1.0, 1.0, 1.0, 1.0);
0655         glTexCoord2f(0, 0);
0656         glVertex3f(-1.0, -1.0, 0);
0657 
0658         glTexCoord2f(1, 0);
0659         glVertex3f(1.0, -1.0, 0);
0660 
0661         glTexCoord2f(1, 1);
0662         glVertex3f(1.0, 1.0, 0);
0663 
0664         glTexCoord2f(0, 1);
0665         glVertex3f(-1.0, 1.0, 0);
0666     }
0667     glEnd();
0668 
0669     d->showingEnd = true;
0670 }
0671 
0672 QStringList PresentationKB::effectNames()
0673 {
0674     QStringList effects;
0675     effects.append(QLatin1String("Ken Burns"));
0676 
0677     return effects;
0678 }
0679 
0680 QMap<QString, QString> PresentationKB::effectNamesI18N()
0681 {
0682     QMap<QString, QString> effects;
0683     effects[QLatin1String("Ken Burns")] = i18n("Ken Burns");
0684 
0685     return effects;
0686 }
0687 
0688 void PresentationKB::keyPressEvent(QKeyEvent* event)
0689 {
0690     if (!event)
0691     {
0692         return;
0693     }
0694 
0695 #ifdef HAVE_MEDIAPLAYER
0696 
0697     d->playbackWidget->keyPressEvent(event);
0698 
0699 #endif
0700 
0701     if (event->key() == Qt::Key_Escape)
0702     {
0703         close();
0704     }
0705 }
0706 
0707 void PresentationKB::mousePressEvent(QMouseEvent* e)
0708 {
0709     if (!e)
0710     {
0711         return;
0712     }
0713 
0714     if (d->endOfShow && d->showingEnd)
0715     {
0716         slotClose();
0717     }
0718 }
0719 
0720 void PresentationKB::mouseMoveEvent(QMouseEvent* e)
0721 {
0722     setCursor(QCursor(Qt::ArrowCursor));
0723     d->mouseMoveTimer->start(1000);
0724 
0725 #ifdef HAVE_MEDIAPLAYER
0726 
0727     if (!d->playbackWidget->canHide())
0728     {
0729         return;
0730     }
0731 
0732     QPoint pos(e->pos());
0733 
0734     if ((pos.y() > 20)                     &&
0735         (pos.y() < (d->deskHeight - 20 - 1)))
0736     {
0737         if (d->playbackWidget->isHidden())
0738         {
0739             return;
0740         }
0741         else
0742         {
0743             d->playbackWidget->hide();
0744             setFocus();
0745         }
0746 
0747         return;
0748     }
0749 
0750     d->playbackWidget->show();
0751 
0752 #else
0753 
0754     Q_UNUSED(e);
0755 
0756 #endif
0757 }
0758 
0759 void PresentationKB::slotMouseMoveTimeOut()
0760 {
0761     QPoint pos(QCursor::pos());
0762 
0763     if ((pos.y() < 20)                    ||
0764         (pos.y() > (d->deskHeight - 20 - 1))
0765 
0766 #ifdef HAVE_MEDIAPLAYER
0767 
0768         || d->playbackWidget->underMouse()
0769 
0770 #endif
0771 
0772        )
0773     {
0774         return;
0775     }
0776 
0777     setCursor(QCursor(Qt::BlankCursor));
0778 }
0779 
0780 bool PresentationKB::checkOpenGL() const
0781 {
0782     // No OpenGL context is found. Are the drivers ok?
0783 
0784     if (!isValid())
0785     {
0786         return false;
0787     }
0788 
0789     // GL_EXT_texture3D is not supported
0790 
0791     QString s = QString::fromLatin1(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
0792 
0793     if (!s.contains(QString::fromLatin1("GL_EXT_texture3D"), Qt::CaseInsensitive))
0794     {
0795         return false;
0796     }
0797 
0798     // Everything is ok!
0799 
0800     return true;
0801 }
0802 
0803 void PresentationKB::slotClose()
0804 {
0805     close();
0806 }
0807 
0808 } // namespace DigikamGenericPresentationPlugin
0809 
0810 #include "moc_presentationkb.cpp"