File indexing completed on 2025-01-19 03:50:33

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 1997-04-21
0007  * Description : Frame with popup menu behavior.
0008  *
0009  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 1997      by Tim D. Gilman <tdgilman at best dot org>
0011  * SPDX-FileCopyrightText: 1998-2001 by Mirko Boehm <mirko at kde dot org>
0012  * SPDX-FileCopyrightText: 2007      by John Layt <john at layt dot net>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "dpopupframe.h"
0019 
0020 // Qt includes
0021 
0022 #include <QApplication>
0023 #include <QEventLoop>
0024 #include <QKeyEvent>
0025 #include <QScreen>
0026 #include <QWindow>
0027 
0028 namespace Digikam
0029 {
0030 
0031 class Q_DECL_HIDDEN DPopupFrame::Private
0032 {
0033 public:
0034 
0035     explicit Private(DPopupFrame* const qq);
0036     ~Private();
0037 
0038 public:
0039 
0040     DPopupFrame*         q;
0041 
0042     /**
0043      * The result. It is returned from exec() when the popup window closes.
0044      */
0045     int                  result;
0046 
0047     /**
0048      * The only subwidget that uses the whole dialog window.
0049      */
0050     QWidget*             main;
0051 
0052     class OutsideClickCatcher;
0053     OutsideClickCatcher* outsideClickCatcher;
0054 };
0055 
0056 class Q_DECL_HIDDEN DPopupFrame::Private::OutsideClickCatcher : public QObject
0057 {
0058     Q_OBJECT
0059 
0060 public:
0061 
0062     explicit OutsideClickCatcher(QObject* const parent = nullptr)
0063         : QObject(parent),
0064           m_popup(nullptr)
0065     {
0066     }
0067 
0068     ~OutsideClickCatcher() override
0069     {
0070     }
0071 
0072     void setPopupFrame(DPopupFrame* const popup)
0073     {
0074         m_popup = popup;
0075         popup->installEventFilter(this);
0076     }
0077 
0078 
0079     bool eventFilter(QObject* object, QEvent* event) override
0080     {
0081         Q_UNUSED(object);
0082 
0083         // To catch outside clicks, it is sufficient to check for
0084         // hide events on Qt::Popup type widgets
0085 
0086         if (event->type() == QEvent::Hide && m_popup)
0087         {
0088             // do not set d->result here, because the popup
0089             // hides itself after leaving the event loop.
0090 
0091             Q_EMIT m_popup->leaveModality();
0092         }
0093 
0094         return false;
0095     }
0096 
0097 public:
0098 
0099     DPopupFrame* m_popup;
0100 };
0101 
0102 DPopupFrame::Private::Private(DPopupFrame* const qq)
0103     : q                  (qq),
0104       result             (0),   // rejected
0105       main               (nullptr),
0106       outsideClickCatcher(new OutsideClickCatcher)
0107 {
0108     outsideClickCatcher->setPopupFrame(q);
0109 }
0110 
0111 DPopupFrame::Private::~Private()
0112 {
0113     delete outsideClickCatcher;
0114 }
0115 
0116 DPopupFrame::DPopupFrame(QWidget* const parent)
0117     : QFrame(parent, Qt::Popup),
0118       d(new Private(this))
0119 {
0120     setFrameStyle(QFrame::Box | QFrame::Raised);
0121     setMidLineWidth(2);
0122 }
0123 
0124 DPopupFrame::~DPopupFrame()
0125 {
0126     delete d;
0127 }
0128 
0129 void DPopupFrame::keyPressEvent(QKeyEvent* e)
0130 {
0131     if (e->key() == Qt::Key_Escape)
0132     {
0133         d->result = 0; // rejected
0134         Q_EMIT leaveModality();
0135     }
0136 }
0137 
0138 void DPopupFrame::hideEvent(QHideEvent *e)
0139 {
0140     QFrame::hideEvent(e);
0141 }
0142 
0143 void DPopupFrame::close(int r)
0144 {
0145     d->result = r;
0146 
0147     Q_EMIT leaveModality();
0148 }
0149 
0150 void DPopupFrame::setMainWidget(QWidget* const m)
0151 {
0152     d->main = m;
0153 
0154     if (d->main)
0155     {
0156         resize(d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth());
0157     }
0158 }
0159 
0160 void DPopupFrame::resizeEvent(QResizeEvent* e)
0161 {
0162     Q_UNUSED(e);
0163 
0164     if (d->main)
0165     {
0166         d->main->setGeometry(frameWidth(), frameWidth(),
0167                              width() - 2 * frameWidth(), height() - 2 * frameWidth());
0168     }
0169 }
0170 
0171 void DPopupFrame::popup(const QPoint& p)
0172 {
0173     // Make sure the whole popup is visible.
0174 
0175     QScreen* screen = qApp->primaryScreen();
0176 
0177     if (QWidget* const widget = nativeParentWidget())
0178     {
0179         if (QWindow* const window = widget->windowHandle())
0180         {
0181             screen = window->screen();
0182         }
0183     }
0184 
0185     QRect desktopGeometry = screen->geometry();
0186 
0187     int x = p.x();
0188     int y = p.y();
0189     int w = width();
0190     int h = height();
0191 
0192     if ((x + w) > (desktopGeometry.x() + desktopGeometry.width()))
0193     {
0194         x = desktopGeometry.width() - w;
0195     }
0196 
0197     if ((y + h) > (desktopGeometry.y() + desktopGeometry.height()))
0198     {
0199         y = desktopGeometry.height() - h;
0200     }
0201 
0202     if (x < desktopGeometry.x())
0203     {
0204         x = 0;
0205     }
0206 
0207     if (y < desktopGeometry.y())
0208     {
0209         y = 0;
0210     }
0211 
0212     // Pop the thingy up.
0213 
0214     move(x, y);
0215     show();
0216     d->main->setFocus();
0217 }
0218 
0219 int DPopupFrame::exec(const QPoint& p)
0220 {
0221     popup(p);
0222     repaint();
0223     d->result = 0; // rejected
0224     QEventLoop eventLoop;
0225 
0226     connect(this, &DPopupFrame::leaveModality,
0227             &eventLoop, &QEventLoop::quit);
0228 
0229     eventLoop.exec();
0230 
0231     hide();
0232 
0233     return d->result;
0234 }
0235 
0236 int DPopupFrame::exec(int x, int y)
0237 {
0238     return exec(QPoint(x, y));
0239 }
0240 
0241 } // namespace Digikam
0242 
0243 #include "dpopupframe.moc"
0244 
0245 #include "moc_dpopupframe.cpp"