File indexing completed on 2024-04-28 15:29:22
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org> 0004 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "partmanager.h" 0010 0011 #include "kparts_logging.h" 0012 #include "partactivateevent.h" 0013 0014 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0015 #include "partselectevent.h" 0016 #endif 0017 0018 #include "guiactivateevent.h" 0019 #include "part.h" 0020 0021 #include <QApplication> 0022 #include <QMouseEvent> 0023 #include <QScrollBar> 0024 0025 using namespace KParts; 0026 0027 namespace KParts 0028 { 0029 class PartManagerPrivate 0030 { 0031 public: 0032 PartManagerPrivate() 0033 { 0034 m_activeWidget = nullptr; 0035 m_activePart = nullptr; 0036 0037 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0038 m_selectedPart = nullptr; 0039 m_selectedWidget = nullptr; 0040 #endif 0041 0042 m_bAllowNestedParts = false; 0043 m_bIgnoreScrollBars = false; 0044 m_activationButtonMask = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton; 0045 m_reason = PartManager::NoReason; 0046 m_bIgnoreExplicitFocusRequest = false; 0047 } 0048 ~PartManagerPrivate() 0049 { 0050 } 0051 void setReason(QEvent *ev) 0052 { 0053 switch (ev->type()) { 0054 case QEvent::MouseButtonPress: 0055 case QEvent::MouseButtonDblClick: { 0056 // clang-format off 0057 QMouseEvent *mev = static_cast<QMouseEvent *>(ev); 0058 m_reason = mev->button() == Qt::LeftButton 0059 ? PartManager::ReasonLeftClick 0060 : (mev->button() == Qt::MiddleButton 0061 ? PartManager::ReasonMidClick 0062 : PartManager::ReasonRightClick); 0063 // clang-format on 0064 break; 0065 } 0066 case QEvent::FocusIn: 0067 m_reason = static_cast<QFocusEvent *>(ev)->reason(); 0068 break; 0069 default: 0070 qCWarning(KPARTSLOG) << "PartManagerPrivate::setReason got unexpected event type" << ev->type(); 0071 break; 0072 } 0073 } 0074 0075 bool allowExplicitFocusEvent(QEvent *ev) const 0076 { 0077 if (ev->type() == QEvent::FocusIn) { 0078 QFocusEvent *fev = static_cast<QFocusEvent *>(ev); 0079 return (!m_bIgnoreExplicitFocusRequest || fev->reason() != Qt::OtherFocusReason); 0080 } 0081 return true; 0082 } 0083 0084 Part *m_activePart; 0085 QWidget *m_activeWidget; 0086 0087 QList<Part *> m_parts; 0088 0089 PartManager::SelectionPolicy m_policy; 0090 0091 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0092 Part *m_selectedPart; 0093 QWidget *m_selectedWidget; 0094 #endif 0095 0096 QList<const QWidget *> m_managedTopLevelWidgets; 0097 short int m_activationButtonMask; 0098 bool m_bIgnoreScrollBars; 0099 bool m_bAllowNestedParts; 0100 int m_reason; 0101 bool m_bIgnoreExplicitFocusRequest; 0102 }; 0103 0104 } 0105 0106 PartManager::PartManager(QWidget *parent) 0107 : QObject(parent) 0108 , d(new PartManagerPrivate) 0109 { 0110 qApp->installEventFilter(this); 0111 0112 d->m_policy = Direct; 0113 0114 addManagedTopLevelWidget(parent); 0115 } 0116 0117 PartManager::PartManager(QWidget *topLevel, QObject *parent) 0118 : QObject(parent) 0119 , d(new PartManagerPrivate) 0120 { 0121 qApp->installEventFilter(this); 0122 0123 d->m_policy = Direct; 0124 0125 addManagedTopLevelWidget(topLevel); 0126 } 0127 0128 PartManager::~PartManager() 0129 { 0130 for (const QWidget *w : std::as_const(d->m_managedTopLevelWidgets)) { 0131 disconnect(w, &QWidget::destroyed, this, &PartManager::slotManagedTopLevelWidgetDestroyed); 0132 } 0133 0134 for (Part *it : std::as_const(d->m_parts)) { 0135 it->setManager(nullptr); 0136 } 0137 0138 // core dumps ... setActivePart( 0 ); 0139 qApp->removeEventFilter(this); 0140 } 0141 0142 void PartManager::setSelectionPolicy(SelectionPolicy policy) 0143 { 0144 d->m_policy = policy; 0145 } 0146 0147 PartManager::SelectionPolicy PartManager::selectionPolicy() const 0148 { 0149 return d->m_policy; 0150 } 0151 0152 void PartManager::setAllowNestedParts(bool allow) 0153 { 0154 d->m_bAllowNestedParts = allow; 0155 } 0156 0157 bool PartManager::allowNestedParts() const 0158 { 0159 return d->m_bAllowNestedParts; 0160 } 0161 0162 void PartManager::setIgnoreScrollBars(bool ignore) 0163 { 0164 d->m_bIgnoreScrollBars = ignore; 0165 } 0166 0167 bool PartManager::ignoreScrollBars() const 0168 { 0169 return d->m_bIgnoreScrollBars; 0170 } 0171 0172 void PartManager::setActivationButtonMask(short int buttonMask) 0173 { 0174 d->m_activationButtonMask = buttonMask; 0175 } 0176 0177 short int PartManager::activationButtonMask() const 0178 { 0179 return d->m_activationButtonMask; 0180 } 0181 0182 bool PartManager::eventFilter(QObject *obj, QEvent *ev) 0183 { 0184 if (ev->type() != QEvent::MouseButtonPress && ev->type() != QEvent::MouseButtonDblClick && ev->type() != QEvent::FocusIn) { 0185 return false; 0186 } 0187 0188 if (!obj->isWidgetType()) { 0189 return false; 0190 } 0191 0192 QWidget *w = static_cast<QWidget *>(obj); 0193 0194 if (((w->windowFlags().testFlag(Qt::Dialog)) && w->isModal()) || (w->windowFlags().testFlag(Qt::Popup)) || (w->windowFlags().testFlag(Qt::Tool))) { 0195 return false; 0196 } 0197 0198 QMouseEvent *mev = nullptr; 0199 if (ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick) { 0200 mev = static_cast<QMouseEvent *>(ev); 0201 0202 qCDebug(KPARTSLOG) << "PartManager::eventFilter button:" << mev->button() << "d->m_activationButtonMask=" << d->m_activationButtonMask; 0203 0204 if ((mev->button() & d->m_activationButtonMask) == 0) { 0205 return false; // ignore this button 0206 } 0207 } 0208 0209 Part *part; 0210 while (w) { 0211 QPoint pos; 0212 0213 if (!d->m_managedTopLevelWidgets.contains(w->topLevelWidget())) { 0214 return false; 0215 } 0216 0217 if (d->m_bIgnoreScrollBars && ::qobject_cast<QScrollBar *>(w)) { 0218 return false; 0219 } 0220 0221 if (mev) { // mouse press or mouse double-click event 0222 pos = mev->globalPos(); 0223 part = findPartFromWidget(w, pos); 0224 } else { 0225 part = findPartFromWidget(w); 0226 } 0227 0228 // clang-format off 0229 const char *evType = (ev->type() == QEvent::MouseButtonPress) ? "MouseButtonPress" 0230 : (ev->type() == QEvent::MouseButtonDblClick) ? "MouseButtonDblClick" 0231 : (ev->type() == QEvent::FocusIn) ? "FocusIn" : "OTHER! ERROR!"; 0232 // clang-format on 0233 if (part) { // We found a part whose widget is w 0234 if (d->m_policy == PartManager::TriState) { 0235 if (ev->type() == QEvent::MouseButtonDblClick) { 0236 if (part == d->m_activePart && w == d->m_activeWidget) { 0237 return false; 0238 } 0239 0240 qCDebug(KPARTSLOG) << "PartManager::eventFilter dblclick -> setActivePart" << part; 0241 0242 d->setReason(ev); 0243 setActivePart(part, w); 0244 d->m_reason = NoReason; 0245 return true; 0246 } 0247 0248 if ( 0249 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0250 (d->m_selectedWidget != w || d->m_selectedPart != part) && 0251 #endif 0252 (d->m_activeWidget != w || d->m_activePart != part)) { 0253 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0254 if (part->isSelectable()) { 0255 setSelectedPart(part, w); 0256 } else { 0257 #endif 0258 qCDebug(KPARTSLOG) << "Part" << part << "(non-selectable) made active because" << w->metaObject()->className() << "got event" << evType; 0259 0260 d->setReason(ev); 0261 setActivePart(part, w); 0262 d->m_reason = NoReason; 0263 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0264 } 0265 #endif 0266 return true; 0267 } 0268 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0269 else if (d->m_selectedWidget == w && d->m_selectedPart == part) { 0270 qCDebug(KPARTSLOG) << "Part" << part << "made active (from selected) because" << w->metaObject()->className() << "got event" << evType; 0271 0272 d->setReason(ev); 0273 setActivePart(part, w); 0274 d->m_reason = NoReason; 0275 return true; 0276 } 0277 #endif 0278 else if (d->m_activeWidget == w && d->m_activePart == part) { 0279 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0280 setSelectedPart(nullptr); 0281 #endif 0282 return false; 0283 } 0284 0285 return false; 0286 } else if (part != d->m_activePart && d->allowExplicitFocusEvent(ev)) { 0287 qCDebug(KPARTSLOG) << "Part" << part << "made active because" << w->metaObject()->className() << "got event" << evType; 0288 0289 d->setReason(ev); 0290 setActivePart(part, w); 0291 d->m_reason = NoReason; 0292 } 0293 0294 return false; 0295 } 0296 0297 w = w->parentWidget(); 0298 0299 if (w && (((w->windowFlags() & Qt::Dialog) && w->isModal()) || (w->windowFlags() & Qt::Popup) || (w->windowFlags() & Qt::Tool))) { 0300 qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() << "got event - loop aborted"; 0301 0302 return false; 0303 } 0304 } 0305 0306 qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() << "got event - loop aborted"; 0307 0308 return false; 0309 } 0310 0311 Part *PartManager::findPartFromWidget(QWidget *widget, const QPoint &pos) 0312 { 0313 for (auto *p : std::as_const(d->m_parts)) { 0314 Part *part = p->hitTest(widget, pos); 0315 if (part && d->m_parts.contains(part)) { 0316 return part; 0317 } 0318 } 0319 return nullptr; 0320 } 0321 0322 Part *PartManager::findPartFromWidget(QWidget *widget) 0323 { 0324 for (auto *part : std::as_const(d->m_parts)) { 0325 if (widget == part->widget()) { 0326 return part; 0327 } 0328 } 0329 return nullptr; 0330 } 0331 0332 void PartManager::addPart(Part *part, bool setActive) 0333 { 0334 Q_ASSERT(part); 0335 0336 // don't add parts more than once :) 0337 if (d->m_parts.contains(part)) { 0338 qCWarning(KPARTSLOG) << part << " already added"; 0339 return; 0340 } 0341 0342 d->m_parts.append(part); 0343 0344 part->setManager(this); 0345 0346 if (setActive) { 0347 setActivePart(part); 0348 0349 if (QWidget *w = part->widget()) { 0350 // Prevent focus problems 0351 if (w->focusPolicy() == Qt::NoFocus) { 0352 qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName() 0353 << "with a focus policy of NoFocus. It should have at least a" 0354 << "ClickFocus policy, for part activation to work well."; 0355 } 0356 if (part->widget() && part->widget()->focusPolicy() == Qt::TabFocus) { 0357 qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName() 0358 << "with a focus policy of TabFocus. It should have at least a" 0359 << "ClickFocus policy, for part activation to work well."; 0360 } 0361 w->setFocus(); 0362 w->show(); 0363 } 0364 } 0365 Q_EMIT partAdded(part); 0366 } 0367 0368 void PartManager::removePart(Part *part) 0369 { 0370 if (!d->m_parts.contains(part)) { 0371 return; 0372 } 0373 0374 const int nb = d->m_parts.removeAll(part); 0375 Q_ASSERT(nb == 1); 0376 Q_UNUSED(nb); // no warning in release mode 0377 part->setManager(nullptr); 0378 0379 Q_EMIT partRemoved(part); 0380 0381 if (part == d->m_activePart) { 0382 setActivePart(nullptr); 0383 } 0384 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0385 if (part == d->m_selectedPart) { 0386 setSelectedPart(nullptr); 0387 } 0388 #endif 0389 } 0390 0391 void PartManager::replacePart(Part *oldPart, Part *newPart, bool setActive) 0392 { 0393 // qCDebug(KPARTSLOG) << "replacePart" << oldPart->name() << "->" << newPart->name() << "setActive=" << setActive; 0394 // This methods does exactly removePart + addPart but without calling setActivePart(0) in between 0395 if (!d->m_parts.contains(oldPart)) { 0396 qFatal("Can't remove part %s, not in KPartManager's list.", oldPart->objectName().toLocal8Bit().constData()); 0397 return; 0398 } 0399 0400 d->m_parts.removeAll(oldPart); 0401 oldPart->setManager(nullptr); 0402 0403 Q_EMIT partRemoved(oldPart); 0404 0405 addPart(newPart, setActive); 0406 } 0407 0408 void PartManager::setActivePart(Part *part, QWidget *widget) 0409 { 0410 if (part && !d->m_parts.contains(part)) { 0411 qCWarning(KPARTSLOG) << "trying to activate a non-registered part!" << part->objectName(); 0412 return; // don't allow someone call setActivePart with a part we don't know about 0413 } 0414 0415 // check whether nested parts are disallowed and activate the top parent part then, by traversing the 0416 // tree recursively (Simon) 0417 if (part && !d->m_bAllowNestedParts) { 0418 QObject *parentPart = part->parent(); // ### this relies on people using KParts::Factory! 0419 KParts::Part *parPart = ::qobject_cast<KParts::Part *>(parentPart); 0420 if (parPart) { 0421 setActivePart(parPart, parPart->widget()); 0422 return; 0423 } 0424 } 0425 0426 qCDebug(KPARTSLOG) << "PartManager::setActivePart d->m_activePart=" << d->m_activePart << "<->part=" << part << "d->m_activeWidget=" << d->m_activeWidget 0427 << "<->widget=" << widget; 0428 0429 // don't activate twice 0430 if (d->m_activePart && part && d->m_activePart == part && (!widget || d->m_activeWidget == widget)) { 0431 return; 0432 } 0433 0434 KParts::Part *oldActivePart = d->m_activePart; 0435 QWidget *oldActiveWidget = d->m_activeWidget; 0436 0437 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0438 setSelectedPart(nullptr); 0439 #endif 0440 0441 d->m_activePart = part; 0442 d->m_activeWidget = widget; 0443 0444 if (oldActivePart) { 0445 KParts::Part *savedActivePart = part; 0446 QWidget *savedActiveWidget = widget; 0447 0448 PartActivateEvent ev(false, oldActivePart, oldActiveWidget); 0449 QApplication::sendEvent(oldActivePart, &ev); 0450 if (oldActiveWidget) { 0451 disconnect(oldActiveWidget, &QWidget::destroyed, this, &PartManager::slotWidgetDestroyed); 0452 QApplication::sendEvent(oldActiveWidget, &ev); 0453 } 0454 0455 d->m_activePart = savedActivePart; 0456 d->m_activeWidget = savedActiveWidget; 0457 } 0458 0459 if (d->m_activePart) { 0460 if (!widget) { 0461 d->m_activeWidget = part->widget(); 0462 } 0463 0464 PartActivateEvent ev(true, d->m_activePart, d->m_activeWidget); 0465 QApplication::sendEvent(d->m_activePart, &ev); 0466 if (d->m_activeWidget) { 0467 connect(d->m_activeWidget, &QWidget::destroyed, this, &PartManager::slotWidgetDestroyed); 0468 QApplication::sendEvent(d->m_activeWidget, &ev); 0469 } 0470 } 0471 // Set the new active instance 0472 // setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KComponentData::mainComponent()); 0473 0474 qCDebug(KPARTSLOG) << this << "emitting activePartChanged" << d->m_activePart; 0475 0476 Q_EMIT activePartChanged(d->m_activePart); 0477 } 0478 0479 Part *PartManager::activePart() const 0480 { 0481 return d->m_activePart; 0482 } 0483 0484 QWidget *PartManager::activeWidget() const 0485 { 0486 return d->m_activeWidget; 0487 } 0488 0489 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0490 void PartManager::setSelectedPart(Part *part, QWidget *widget) 0491 { 0492 if (part == d->m_selectedPart && widget == d->m_selectedWidget) { 0493 return; 0494 } 0495 0496 Part *oldPart = d->m_selectedPart; 0497 QWidget *oldWidget = d->m_selectedWidget; 0498 0499 d->m_selectedPart = part; 0500 d->m_selectedWidget = widget; 0501 0502 if (part && !widget) { 0503 d->m_selectedWidget = part->widget(); 0504 } 0505 0506 if (oldPart) { 0507 PartSelectEvent ev(false, oldPart, oldWidget); 0508 QApplication::sendEvent(oldPart, &ev); 0509 QApplication::sendEvent(oldWidget, &ev); 0510 } 0511 0512 if (d->m_selectedPart) { 0513 PartSelectEvent ev(true, d->m_selectedPart, d->m_selectedWidget); 0514 QApplication::sendEvent(d->m_selectedPart, &ev); 0515 QApplication::sendEvent(d->m_selectedWidget, &ev); 0516 } 0517 } 0518 #endif 0519 0520 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0521 Part *PartManager::selectedPart() const 0522 { 0523 return d->m_selectedPart; 0524 } 0525 #endif 0526 0527 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 72) 0528 QWidget *PartManager::selectedWidget() const 0529 { 0530 return d->m_selectedWidget; 0531 } 0532 #endif 0533 0534 void PartManager::slotObjectDestroyed() 0535 { 0536 // qDebug(); 0537 removePart(const_cast<Part *>(static_cast<const Part *>(sender()))); 0538 } 0539 0540 void PartManager::slotWidgetDestroyed() 0541 { 0542 // qDebug(); 0543 if (static_cast<const QWidget *>(sender()) == d->m_activeWidget) { 0544 setActivePart(nullptr); // do not remove the part because if the part's widget dies, then the 0545 } 0546 // part will delete itself anyway, invoking removePart() in its destructor 0547 } 0548 0549 const QList<Part *> PartManager::parts() const 0550 { 0551 return d->m_parts; 0552 } 0553 0554 void PartManager::addManagedTopLevelWidget(const QWidget *topLevel) 0555 { 0556 if (!topLevel->isTopLevel()) { 0557 return; 0558 } 0559 0560 if (d->m_managedTopLevelWidgets.contains(topLevel)) { 0561 return; 0562 } 0563 0564 d->m_managedTopLevelWidgets.append(topLevel); 0565 connect(topLevel, &QWidget::destroyed, this, &PartManager::slotManagedTopLevelWidgetDestroyed); 0566 } 0567 0568 void PartManager::removeManagedTopLevelWidget(const QWidget *topLevel) 0569 { 0570 d->m_managedTopLevelWidgets.removeAll(topLevel); 0571 } 0572 0573 void PartManager::slotManagedTopLevelWidgetDestroyed() 0574 { 0575 const QWidget *widget = static_cast<const QWidget *>(sender()); 0576 removeManagedTopLevelWidget(widget); 0577 } 0578 0579 int PartManager::reason() const 0580 { 0581 return d->m_reason; 0582 } 0583 0584 void PartManager::setIgnoreExplictFocusRequests(bool ignore) 0585 { 0586 d->m_bIgnoreExplicitFocusRequest = ignore; 0587 } 0588 0589 #include "moc_partmanager.cpp"