KParts

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

KDE's Doxygen guidelines are available online.