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