KParts

partmanager.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "partmanager.h"
10
11#include "guiactivateevent.h"
12#include "kparts_logging.h"
13#include "part.h"
14#include "partactivateevent.h"
15
16#include <QApplication>
17#include <QMouseEvent>
18#include <QScrollBar>
19
20using namespace KParts;
21
22namespace KParts
23{
24class PartManagerPrivate
25{
26public:
27 PartManagerPrivate()
28 {
29 m_activeWidget = nullptr;
30 m_activePart = nullptr;
31 m_bAllowNestedParts = false;
32 m_bIgnoreScrollBars = false;
33 m_activationButtonMask = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton;
34 m_reason = PartManager::NoReason;
35 m_bIgnoreExplicitFocusRequest = false;
36 }
37 ~PartManagerPrivate()
38 {
39 }
40 void setReason(QEvent *ev)
41 {
42 switch (ev->type()) {
45 // clang-format off
46 QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
47 m_reason = mev->button() == Qt::LeftButton
48 ? PartManager::ReasonLeftClick
49 : (mev->button() == Qt::MiddleButton
50 ? PartManager::ReasonMidClick
51 : PartManager::ReasonRightClick);
52 // clang-format on
53 break;
54 }
55 case QEvent::FocusIn:
56 m_reason = static_cast<QFocusEvent *>(ev)->reason();
57 break;
58 default:
59 qCWarning(KPARTSLOG) << "PartManagerPrivate::setReason got unexpected event type" << ev->type();
60 break;
61 }
62 }
63
64 bool allowExplicitFocusEvent(QEvent *ev) const
65 {
66 if (ev->type() == QEvent::FocusIn) {
67 QFocusEvent *fev = static_cast<QFocusEvent *>(ev);
68 return (!m_bIgnoreExplicitFocusRequest || fev->reason() != Qt::OtherFocusReason);
69 }
70 return true;
71 }
72
73 Part *m_activePart;
74 QWidget *m_activeWidget;
75
76 QList<Part *> m_parts;
77
79
80 QList<const QWidget *> m_managedTopLevelWidgets;
81 short int m_activationButtonMask;
82 bool m_bIgnoreScrollBars;
83 bool m_bAllowNestedParts;
84 int m_reason;
85 bool m_bIgnoreExplicitFocusRequest;
86};
87
88}
89
91 : QObject(parent)
92 , d(new PartManagerPrivate)
93{
94 qApp->installEventFilter(this);
95
96 d->m_policy = Direct;
97
99}
100
102 : QObject(parent)
103 , d(new PartManagerPrivate)
104{
105 qApp->installEventFilter(this);
106
107 d->m_policy = Direct;
108
109 addManagedTopLevelWidget(topLevel);
110}
111
112PartManager::~PartManager()
113{
114 for (const QWidget *w : std::as_const(d->m_managedTopLevelWidgets)) {
116 }
117
118 for (Part *it : std::as_const(d->m_parts)) {
119 it->setManager(nullptr);
120 }
121
122 // core dumps ... setActivePart( 0 );
123 qApp->removeEventFilter(this);
124}
125
127{
128 d->m_policy = policy;
129}
130
131PartManager::SelectionPolicy PartManager::selectionPolicy() const
132{
133 return d->m_policy;
134}
135
137{
138 d->m_bAllowNestedParts = allow;
139}
140
141bool PartManager::allowNestedParts() const
142{
143 return d->m_bAllowNestedParts;
144}
145
147{
148 d->m_bIgnoreScrollBars = ignore;
149}
150
151bool PartManager::ignoreScrollBars() const
152{
153 return d->m_bIgnoreScrollBars;
154}
155
156void PartManager::setActivationButtonMask(short int buttonMask)
157{
158 d->m_activationButtonMask = buttonMask;
159}
160
162{
163 return d->m_activationButtonMask;
164}
165
167{
169 return false;
170 }
171
172 if (!obj->isWidgetType()) {
173 return false;
174 }
175
176 QWidget *w = static_cast<QWidget *>(obj);
177
178 if (((w->windowFlags().testFlag(Qt::Dialog)) && w->isModal()) || (w->windowFlags().testFlag(Qt::Popup)) || (w->windowFlags().testFlag(Qt::Tool))) {
179 return false;
180 }
181
182 QMouseEvent *mev = nullptr;
184 mev = static_cast<QMouseEvent *>(ev);
185
186 qCDebug(KPARTSLOG) << "PartManager::eventFilter button:" << mev->button() << "d->m_activationButtonMask=" << d->m_activationButtonMask;
187
188 if ((mev->button() & d->m_activationButtonMask) == 0) {
189 return false; // ignore this button
190 }
191 }
192
193 Part *part;
194 while (w) {
195 QPoint pos;
196
197 if (!d->m_managedTopLevelWidgets.contains(w->topLevelWidget())) {
198 return false;
199 }
200
201 if (d->m_bIgnoreScrollBars && ::qobject_cast<QScrollBar *>(w)) {
202 return false;
203 }
204
205 if (mev) { // mouse press or mouse double-click event
206 pos = mev->globalPosition().toPoint();
207 part = findPartFromWidget(w, pos);
208 } else {
209 part = findPartFromWidget(w);
210 }
211
212 // clang-format off
213 const char *evType = (ev->type() == QEvent::MouseButtonPress) ? "MouseButtonPress"
214 : (ev->type() == QEvent::MouseButtonDblClick) ? "MouseButtonDblClick"
215 : (ev->type() == QEvent::FocusIn) ? "FocusIn" : "OTHER! ERROR!";
216 // clang-format on
217 if (part) { // We found a part whose widget is w
218 if (d->m_policy == PartManager::TriState) {
219 if (ev->type() == QEvent::MouseButtonDblClick) {
220 if (part == d->m_activePart && w == d->m_activeWidget) {
221 return false;
222 }
223
224 qCDebug(KPARTSLOG) << "PartManager::eventFilter dblclick -> setActivePart" << part;
225
226 d->setReason(ev);
227 setActivePart(part, w);
228 d->m_reason = NoReason;
229 return true;
230 }
231
232 if ((d->m_activeWidget != w || d->m_activePart != part)) {
233 qCDebug(KPARTSLOG) << "Part" << part << "(non-selectable) made active because" << w->metaObject()->className() << "got event" << evType;
234
235 d->setReason(ev);
236 setActivePart(part, w);
237 d->m_reason = NoReason;
238 return true;
239 } else if (d->m_activeWidget == w && d->m_activePart == part) {
240 return false;
241 }
242
243 return false;
244 } else if (part != d->m_activePart && d->allowExplicitFocusEvent(ev)) {
245 qCDebug(KPARTSLOG) << "Part" << part << "made active because" << w->metaObject()->className() << "got event" << evType;
246
247 d->setReason(ev);
248 setActivePart(part, w);
249 d->m_reason = NoReason;
250 }
251
252 return false;
253 }
254
255 w = w->parentWidget();
256
257 if (w && (((w->windowFlags() & Qt::Dialog) && w->isModal()) || (w->windowFlags() & Qt::Popup) || (w->windowFlags() & Qt::Tool))) {
258 qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() << "got event - loop aborted";
259
260 return false;
261 }
262 }
263
264 qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() << "got event - loop aborted";
265
266 return false;
267}
268
269Part *PartManager::findPartFromWidget(QWidget *widget, const QPoint &pos)
270{
271 for (auto *p : std::as_const(d->m_parts)) {
272 Part *part = p->hitTest(widget, pos);
273 if (part && d->m_parts.contains(part)) {
274 return part;
275 }
276 }
277 return nullptr;
278}
279
280Part *PartManager::findPartFromWidget(QWidget *widget)
281{
282 for (auto *part : std::as_const(d->m_parts)) {
283 if (widget == part->widget()) {
284 return part;
285 }
286 }
287 return nullptr;
288}
289
290void PartManager::addPart(Part *part, bool setActive)
291{
292 Q_ASSERT(part);
293
294 // don't add parts more than once :)
295 if (d->m_parts.contains(part)) {
296 qCWarning(KPARTSLOG) << part << " already added";
297 return;
298 }
299
300 d->m_parts.append(part);
301
302 part->setManager(this);
303
304 if (setActive) {
305 setActivePart(part);
306
307 if (QWidget *w = part->widget()) {
308 // Prevent focus problems
309 if (w->focusPolicy() == Qt::NoFocus) {
310 qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName()
311 << "with a focus policy of NoFocus. It should have at least a"
312 << "ClickFocus policy, for part activation to work well.";
313 }
314 if (part->widget() && part->widget()->focusPolicy() == Qt::TabFocus) {
315 qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName()
316 << "with a focus policy of TabFocus. It should have at least a"
317 << "ClickFocus policy, for part activation to work well.";
318 }
319 w->setFocus();
320 w->show();
321 }
322 }
323 Q_EMIT partAdded(part);
324}
325
327{
328 if (!d->m_parts.contains(part)) {
329 return;
330 }
331
332 const int nb = d->m_parts.removeAll(part);
333 Q_ASSERT(nb == 1);
334 Q_UNUSED(nb); // no warning in release mode
335 part->setManager(nullptr);
336
337 Q_EMIT partRemoved(part);
338
339 if (part == d->m_activePart) {
340 setActivePart(nullptr);
341 }
342}
343
344void PartManager::replacePart(Part *oldPart, Part *newPart, bool setActive)
345{
346 // qCDebug(KPARTSLOG) << "replacePart" << oldPart->name() << "->" << newPart->name() << "setActive=" << setActive;
347 // This methods does exactly removePart + addPart but without calling setActivePart(0) in between
348 if (!d->m_parts.contains(oldPart)) {
349 qFatal("Can't remove part %s, not in KPartManager's list.", oldPart->objectName().toLocal8Bit().constData());
350 return;
351 }
352
353 d->m_parts.removeAll(oldPart);
354 oldPart->setManager(nullptr);
355
356 Q_EMIT partRemoved(oldPart);
357
358 addPart(newPart, setActive);
359}
360
362{
363 if (part && !d->m_parts.contains(part)) {
364 qCWarning(KPARTSLOG) << "trying to activate a non-registered part!" << part->objectName();
365 return; // don't allow someone call setActivePart with a part we don't know about
366 }
367
368 // check whether nested parts are disallowed and activate the top parent part then, by traversing the
369 // tree recursively (Simon)
370 if (part && !d->m_bAllowNestedParts) {
371 QObject *parentPart = part->parent(); // ### this relies on people using KParts::Factory!
372 KParts::Part *parPart = ::qobject_cast<KParts::Part *>(parentPart);
373 if (parPart) {
374 setActivePart(parPart, parPart->widget());
375 return;
376 }
377 }
378
379 qCDebug(KPARTSLOG) << "PartManager::setActivePart d->m_activePart=" << d->m_activePart << "<->part=" << part << "d->m_activeWidget=" << d->m_activeWidget
380 << "<->widget=" << widget;
381
382 // don't activate twice
383 if (d->m_activePart && part && d->m_activePart == part && (!widget || d->m_activeWidget == widget)) {
384 return;
385 }
386
387 KParts::Part *oldActivePart = d->m_activePart;
388 QWidget *oldActiveWidget = d->m_activeWidget;
389
390 d->m_activePart = part;
391 d->m_activeWidget = widget;
392
393 if (oldActivePart) {
394 KParts::Part *savedActivePart = part;
395 QWidget *savedActiveWidget = widget;
396
397 PartActivateEvent ev(false, oldActivePart, oldActiveWidget);
398 QApplication::sendEvent(oldActivePart, &ev);
399 if (oldActiveWidget) {
401 QApplication::sendEvent(oldActiveWidget, &ev);
402 }
403
404 d->m_activePart = savedActivePart;
405 d->m_activeWidget = savedActiveWidget;
406 }
407
408 if (d->m_activePart) {
409 if (!widget) {
410 d->m_activeWidget = part->widget();
411 }
412
413 PartActivateEvent ev(true, d->m_activePart, d->m_activeWidget);
414 QApplication::sendEvent(d->m_activePart, &ev);
415 if (d->m_activeWidget) {
417 QApplication::sendEvent(d->m_activeWidget, &ev);
418 }
419 }
420 // Set the new active instance
421 // setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KComponentData::mainComponent());
422
423 qCDebug(KPARTSLOG) << this << "emitting activePartChanged" << d->m_activePart;
424
425 Q_EMIT activePartChanged(d->m_activePart);
426}
427
429{
430 return d->m_activePart;
431}
432
434{
435 return d->m_activeWidget;
436}
437
439{
440 // qDebug();
441 removePart(const_cast<Part *>(static_cast<const Part *>(sender())));
442}
443
445{
446 // qDebug();
447 if (static_cast<const QWidget *>(sender()) == d->m_activeWidget) {
448 setActivePart(nullptr); // do not remove the part because if the part's widget dies, then the
449 }
450 // part will delete itself anyway, invoking removePart() in its destructor
451}
452
454{
455 return d->m_parts;
456}
457
459{
460 if (!topLevel->isWindow()) {
461 return;
462 }
463
464 if (d->m_managedTopLevelWidgets.contains(topLevel)) {
465 return;
466 }
467
468 d->m_managedTopLevelWidgets.append(topLevel);
470}
471
473{
474 d->m_managedTopLevelWidgets.removeAll(topLevel);
475}
476
478{
479 const QWidget *widget = static_cast<const QWidget *>(sender());
481}
482
484{
485 return d->m_reason;
486}
487
489{
490 d->m_bIgnoreExplicitFocusRequest = ignore;
491}
492
493#include "moc_partmanager.cpp"
This event is sent by the part manager when the active part changes.
void setIgnoreScrollBars(bool ignore)
Specifies whether the partmanager should ignore mouse click events for scrollbars or not.
short int activationButtonMask() const
void partAdded(KParts::Part *part)
Emitted when a new part has been added.
virtual void removePart(Part *part)
Removes a part from the manager (this does not delete the object) .
void setActivationButtonMask(short int buttonMask)
Specifies which mouse buttons the partmanager should react upon.
virtual QWidget * activeWidget() const
Returns the active widget of the current active part (see activePart ).
bool eventFilter(QObject *obj, QEvent *ev) override
virtual Part * activePart() const
Returns the active part.
PartManager(QWidget *parent)
Constructs a part manager.
SelectionPolicy
Selection policy. The default policy of a PartManager is Direct.
Definition partmanager.h:44
virtual void replacePart(Part *oldPart, Part *newPart, bool setActive=true)
Replaces oldPart with newPart, and sets newPart as active if setActive is true.
void setSelectionPolicy(SelectionPolicy policy)
Sets the selection policy of the partmanager.
const QList< Part * > parts() const
Returns the list of parts being managed by the partmanager.
void activePartChanged(KParts::Part *newPart)
Emitted when the active part has changed.
void slotObjectDestroyed()
Removes a part when it is destroyed.
virtual void setActivePart(Part *part, QWidget *widget=nullptr)
Sets the active part.
void slotManagedTopLevelWidgetDestroyed()
void setIgnoreExplictFocusRequests(bool)
Sets whether the PartManager ignores explicit set focus requests from the part.
void removeManagedTopLevelWidget(const QWidget *topLevel)
Removes the topLevel widget from the list of managed toplevel widgets.
virtual void addPart(Part *part, bool setActive=true)
Adds a part to the manager.
void setAllowNestedParts(bool allow)
Specifies whether the partmanager should handle/allow nested parts or not.
void partRemoved(KParts::Part *part)
Emitted when a part has been removed.
void addManagedTopLevelWidget(const QWidget *topLevel)
Adds the topLevel widget to the list of managed toplevel widgets.
Base class for parts.
Definition part.h:57
virtual Part * hitTest(QWidget *widget, const QPoint &globalPos)
Returns the part (this, or a child part) at the given global position.
Definition part.cpp:101
virtual QWidget * widget()
Definition part.cpp:61
virtual void setManager(PartManager *manager)
Definition part.cpp:87
The KParts namespace,.
bool sendEvent(QObject *receiver, QEvent *event)
MouseButtonPress
Type type() const const
Qt::FocusReason reason() const const
const char * className() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
void installEventFilter(QObject *filterObj)
bool isWidgetType() const const
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
QObject * sender() const const
QPoint toPoint() const const
Qt::MouseButton button() const const
QPointF globalPosition() const const
OtherFocusReason
LeftButton
QWidget * topLevelWidget() const const
bool isWindow() const const
bool isModal() const const
QWidget * parentWidget() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:54 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.