File indexing completed on 2024-05-19 05:42:19

0001 // ct_lvtqtw_itool.cpp                                               -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtqtc_itool.h>
0021 
0022 #include <ct_lvtqtc_graphicsscene.h>
0023 #include <ct_lvtqtc_graphicsview.h>
0024 
0025 #include <QAction>
0026 #include <QDebug>
0027 #include <QEvent>
0028 #include <QGraphicsSceneMouseEvent>
0029 #include <QKeyEvent>
0030 #include <QToolButton>
0031 
0032 // in one source file
0033 Q_LOGGING_CATEGORY(LogTool, "log.itool")
0034 
0035 namespace Codethink::lvtqtc {
0036 struct ITool::Private {
0037     QString name;
0038     QString tooltip;
0039     QIcon icon;
0040     GraphicsView *graphicsView = nullptr;
0041     QAction *action = nullptr;
0042     lvtldr::NodeStorage *nodeStorage = nullptr;
0043 };
0044 
0045 ITool::ITool(const QString& name, const QString& tooltip, const QIcon& icon, GraphicsView *gv):
0046     d(std::make_unique<ITool::Private>())
0047 {
0048     d->name = name;
0049     d->tooltip = tooltip;
0050     d->icon = icon;
0051     d->graphicsView = gv;
0052 
0053     d->action = new QAction();
0054     d->action->setIcon(d->icon);
0055     d->action->setText(d->name);
0056     d->action->setToolTip(d->tooltip);
0057     d->action->setCheckable(true);
0058 
0059     connect(d->action, &QAction::toggled, this, [this](bool value) {
0060         value ? activate() : deactivate();
0061 
0062         // this won't re-trigger the toggled signal, and it's needed
0063         // to actually select the button on click, because of the
0064         // invisible button on the button group being selected after
0065         // one is deactivated. see this example:
0066         // btn1 is active, we click in btn2.
0067         // btn2 get's active, this disables btn1
0068         // because btn1 got deactivated, invisibleButton will get active, deactivating btn2
0069         // so we need to check the 2nd button twice.
0070         d->action->setChecked(value);
0071     });
0072 
0073     connect(this, &lvtqtc::ITool::undoCommandCreated, d->graphicsView, &lvtqtc::GraphicsView::undoCommandReceived);
0074 }
0075 
0076 ITool::~ITool() noexcept
0077 {
0078     d->action->deleteLater();
0079 }
0080 
0081 QAction *ITool::action() const
0082 {
0083     return d->action;
0084 }
0085 
0086 QString ITool::name() const
0087 {
0088     return d->name;
0089 }
0090 
0091 QString ITool::toolTip() const
0092 {
0093     return d->tooltip;
0094 }
0095 
0096 QIcon ITool::icon() const
0097 {
0098     return d->icon;
0099 }
0100 
0101 void ITool::setEnabled(bool enabled)
0102 {
0103     d->action->setEnabled(enabled);
0104 }
0105 
0106 bool ITool::eventFilter(QObject *obj, QEvent *ev)
0107 {
0108     if (ev->type() == QEvent::KeyPress) {
0109         auto *keyEv = static_cast<QKeyEvent *>(ev); // NOLINT
0110 
0111         // If the user manually cancels the action by pressing esc, also clean the message message.
0112         if (keyEv->key() == Qt::Key_Escape) {
0113             d->action->setChecked(false);
0114             Q_EMIT sendMessage(QString(), KMessageWidget::Information);
0115         }
0116     }
0117 
0118     if (obj == d->graphicsView) {
0119         switch (ev->type()) {
0120         case QEvent::KeyRelease:
0121             keyReleaseEvent(static_cast<QKeyEvent *>(ev)); // NOLINT
0122             return ev->isAccepted();
0123         case QEvent::KeyPress:
0124             keyPressEvent(static_cast<QKeyEvent *>(ev)); // NOLINT
0125             return ev->isAccepted();
0126         default:
0127             return false;
0128         }
0129         return false;
0130     }
0131 
0132     if (obj == d->graphicsView->viewport()) {
0133         switch (ev->type()) {
0134         case QEvent::MouseMove:
0135             mouseMoveEvent(static_cast<QMouseEvent *>(ev)); // NOLINT
0136             return ev->isAccepted();
0137         case QEvent::MouseButtonPress:
0138             mousePressEvent(static_cast<QMouseEvent *>(ev)); // NOLINT
0139             return ev->isAccepted();
0140         case QEvent::MouseButtonRelease:
0141             mouseReleaseEvent(static_cast<QMouseEvent *>(ev)); // NOLINT
0142             return ev->isAccepted();
0143         default:
0144             return false;
0145         }
0146         return true;
0147     }
0148 
0149     if (obj == d->graphicsView->scene()) {
0150         switch (ev->type()) {
0151         case QEvent::GraphicsSceneMouseMove:
0152             sceneMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(ev)); // NOLINT
0153             return ev->isAccepted();
0154         case QEvent::GraphicsSceneMousePress:
0155             sceneMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(ev)); // NOLINT
0156             return ev->isAccepted();
0157         case QEvent::GraphicsSceneMouseRelease:
0158             sceneMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(ev)); // NOLINT
0159             return ev->isAccepted();
0160         default:
0161             return false;
0162         }
0163         return true;
0164     }
0165     return false;
0166 }
0167 
0168 GraphicsView *ITool::graphicsView() const
0169 {
0170     return d->graphicsView;
0171 }
0172 
0173 GraphicsScene *ITool::graphicsScene() const
0174 {
0175     return qobject_cast<GraphicsScene *>(d->graphicsView->scene());
0176 }
0177 
0178 void ITool::setupInfra(bool install)
0179 {
0180     if (install) {
0181         d->graphicsView->installEventFilter(this);
0182         d->graphicsView->viewport()->installEventFilter(this);
0183         d->graphicsView->scene()->installEventFilter(this);
0184         d->graphicsView->setCurrentTool(this);
0185         d->graphicsView->setFocus(Qt::OtherFocusReason);
0186     } else {
0187         d->graphicsView->removeEventFilter(this);
0188         d->graphicsView->viewport()->removeEventFilter(this);
0189         d->graphicsView->scene()->removeEventFilter(this);
0190         d->graphicsView->setCurrentTool(nullptr);
0191     }
0192 }
0193 
0194 void ITool::activate()
0195 {
0196     qCDebug(LogTool) << name() << "Activated";
0197     // because of the double selection needed - for the invisibleButton
0198     // we need to, before activating anything, making sure this anything
0199     // is not already connected to the view.
0200     setupInfra(false);
0201     setupInfra(true);
0202     Q_EMIT activated();
0203 }
0204 
0205 void ITool::deactivate()
0206 {
0207     qCDebug(LogTool) << name() << "Deactivate";
0208     setupInfra(false);
0209     Q_EMIT deactivated();
0210 }
0211 
0212 void ITool::mousePressEvent(QMouseEvent *event)
0213 {
0214     qCDebug(LogTool) << name() << "Mouse Press Event ignored";
0215     event->setAccepted(false);
0216     Q_UNUSED(event);
0217 }
0218 
0219 void ITool::mouseMoveEvent(QMouseEvent *event)
0220 {
0221     event->setAccepted(false);
0222     Q_UNUSED(event);
0223 }
0224 
0225 void ITool::mouseReleaseEvent(QMouseEvent *event)
0226 {
0227     qCDebug(LogTool) << name() << "Mouse Press Release ignored";
0228     event->setAccepted(false);
0229     Q_UNUSED(event);
0230 }
0231 
0232 void ITool::keyPressEvent(QKeyEvent *event)
0233 {
0234     qCDebug(LogTool) << name() << "Key Press Event ignored";
0235     event->setAccepted(false);
0236     Q_UNUSED(event);
0237 }
0238 
0239 void ITool::keyReleaseEvent(QKeyEvent *event)
0240 {
0241     qCDebug(LogTool) << name() << "Key Release ignored";
0242     event->setAccepted(false);
0243     Q_UNUSED(event);
0244 }
0245 
0246 void ITool::drawForeground(QPainter *painter, const QRectF& rect)
0247 {
0248     Q_UNUSED(painter);
0249     Q_UNUSED(rect);
0250 }
0251 
0252 // those acts on the scene
0253 void ITool::sceneMousePressEvent(QGraphicsSceneMouseEvent *event)
0254 {
0255     qCDebug(LogTool) << name() << "Scene Mouse Press Event Ignored";
0256     event->setAccepted(false);
0257     Q_UNUSED(event);
0258 }
0259 
0260 void ITool::sceneMouseMoveEvent(QGraphicsSceneMouseEvent *event)
0261 {
0262     event->setAccepted(false);
0263     Q_UNUSED(event);
0264 }
0265 
0266 void ITool::sceneMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
0267 {
0268     qCDebug(LogTool) << name() << "Scene Mouse Release Event Ignored";
0269     event->setAccepted(false);
0270     Q_UNUSED(event);
0271 }
0272 
0273 } // namespace Codethink::lvtqtc