1/* This file is part of the KDE project
2 Copyright (C) 1999 Simon Hausmann <hausmann@kde.org>
3 (C) 1999 David Faure <faure@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "partmanager.h"
22#include <kparts/event.h>
23#include <kparts/part.h>
24#include <kglobal.h>
25#include <kdebug.h>
26
27#include <QtGui/QApplication>
28#include <QtGui/QScrollBar>
29#include <kcomponentdata.h>
30
31//#define DEBUG_PARTMANAGER
32
33using namespace KParts;
34
35namespace KParts {
36
37class PartManagerPrivate
38{
39public:
40 PartManagerPrivate()
41 {
42 m_activeWidget = 0;
43 m_activePart = 0;
44 m_selectedPart = 0;
45 m_selectedWidget = 0;
46 m_bAllowNestedParts = false;
47 m_bIgnoreScrollBars = false;
48 m_activationButtonMask = Qt::LeftButton | Qt::MidButton | Qt::RightButton;
49 m_reason = PartManager::NoReason;
50 m_bIgnoreExplicitFocusRequest = false;
51 }
52 ~PartManagerPrivate()
53 {
54 }
55 void setReason( QEvent* ev ) {
56 switch( ev->type() ) {
57 case QEvent::MouseButtonPress:
58 case QEvent::MouseButtonDblClick: {
59 QMouseEvent* mev = static_cast<QMouseEvent *>( ev );
60 m_reason = mev->button() == Qt::LeftButton
61 ? PartManager::ReasonLeftClick
62 : ( mev->button() == Qt::MidButton
63 ? PartManager::ReasonMidClick
64 : PartManager::ReasonRightClick );
65 break;
66 }
67 case QEvent::FocusIn:
68 m_reason = static_cast<QFocusEvent *>( ev )->reason();
69 break;
70 default:
71 kWarning(1000) << "PartManagerPrivate::setReason got unexpected ev type " << ev->type();
72 break;
73 }
74 }
75
76 bool allowExplicitFocusEvent(QEvent* ev) const
77 {
78 if (ev->type() == QEvent::FocusIn) {
79 QFocusEvent* fev = static_cast<QFocusEvent*>(ev);
80 return (!m_bIgnoreExplicitFocusRequest || fev->reason() != Qt::OtherFocusReason);
81 }
82 return true;
83 }
84
85 Part * m_activePart;
86 QWidget *m_activeWidget;
87
88 QList<Part *> m_parts;
89
90 PartManager::SelectionPolicy m_policy;
91
92 Part *m_selectedPart;
93 QWidget *m_selectedWidget;
94
95 QList<const QWidget *> m_managedTopLevelWidgets;
96 short int m_activationButtonMask;
97 bool m_bIgnoreScrollBars;
98 bool m_bAllowNestedParts;
99 int m_reason;
100 bool m_bIgnoreExplicitFocusRequest;
101};
102
103}
104
105PartManager::PartManager( QWidget * parent )
106 : QObject( parent ),d(new PartManagerPrivate)
107{
108
109 qApp->installEventFilter( this );
110
111 d->m_policy = Direct;
112
113 addManagedTopLevelWidget( parent );
114}
115
116PartManager::PartManager( QWidget *topLevel, QObject *parent )
117 : QObject( parent ),d(new PartManagerPrivate)
118{
119
120 qApp->installEventFilter( this );
121
122 d->m_policy = Direct;
123
124 addManagedTopLevelWidget( topLevel );
125}
126
127PartManager::~PartManager()
128{
129 foreach( const QWidget* w, d->m_managedTopLevelWidgets )
130 {
131 disconnect( w, SIGNAL(destroyed()),
132 this, SLOT(slotManagedTopLevelWidgetDestroyed()) );
133 }
134
135 foreach( Part* it, d->m_parts )
136 {
137 it->setManager( 0 );
138 }
139
140 // core dumps ... setActivePart( 0 );
141 qApp->removeEventFilter( this );
142 delete d;
143}
144
145void PartManager::setSelectionPolicy( SelectionPolicy policy )
146{
147 d->m_policy = policy;
148}
149
150PartManager::SelectionPolicy PartManager::selectionPolicy() const
151{
152 return d->m_policy;
153}
154
155void PartManager::setAllowNestedParts( bool allow )
156{
157 d->m_bAllowNestedParts = allow;
158}
159
160bool PartManager::allowNestedParts() const
161{
162 return d->m_bAllowNestedParts;
163}
164
165void PartManager::setIgnoreScrollBars( bool ignore )
166{
167 d->m_bIgnoreScrollBars = ignore;
168}
169
170bool PartManager::ignoreScrollBars() const
171{
172 return d->m_bIgnoreScrollBars;
173}
174
175void PartManager::setActivationButtonMask( short int buttonMask )
176{
177 d->m_activationButtonMask = buttonMask;
178}
179
180short int PartManager::activationButtonMask() const
181{
182 return d->m_activationButtonMask;
183}
184
185bool PartManager::eventFilter( QObject *obj, QEvent *ev )
186{
187
188 if ( ev->type() != QEvent::MouseButtonPress &&
189 ev->type() != QEvent::MouseButtonDblClick &&
190 ev->type() != QEvent::FocusIn )
191 return false;
192
193 if ( !obj->isWidgetType() )
194 return false;
195
196 QWidget *w = static_cast<QWidget *>( obj );
197
198 if ( ( ( w->windowFlags().testFlag(Qt::Dialog) ) && w->isModal() ) ||
199 ( w->windowFlags().testFlag(Qt::Popup) ) || ( w->windowFlags().testFlag(Qt::Tool) ) )
200 return false;
201
202 QMouseEvent* mev = 0;
203 if ( ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick )
204 {
205 mev = static_cast<QMouseEvent *>( ev );
206#ifdef DEBUG_PARTMANAGER
207 kDebug(1000) << "PartManager::eventFilter button: " << mev->button() << " " << "d->m_activationButtonMask=" << d->m_activationButtonMask;
208#endif
209 if ( ( mev->button() & d->m_activationButtonMask ) == 0 )
210 return false; // ignore this button
211 }
212
213 Part * part;
214 while ( w )
215 {
216 QPoint pos;
217
218 if ( !d->m_managedTopLevelWidgets.contains( w->topLevelWidget() ) )
219 return false;
220
221 if ( d->m_bIgnoreScrollBars && ::qobject_cast<QScrollBar *>(w) )
222 return false;
223
224 if ( mev ) // mouse press or mouse double-click event
225 {
226 pos = mev->globalPos();
227 part = findPartFromWidget( w, pos );
228 } else
229 part = findPartFromWidget( w );
230
231#ifdef DEBUG_PARTMANAGER
232 const char* evType = ( ev->type() == QEvent::MouseButtonPress ) ? "MouseButtonPress"
233 : ( ev->type() == QEvent::MouseButtonDblClick ) ? "MouseButtonDblClick"
234 : ( ev->type() == QEvent::FocusIn ) ? "FocusIn" : "OTHER! ERROR!";
235#endif
236 if ( part ) // We found a part whose widget is w
237 {
238 if ( d->m_policy == PartManager::TriState )
239 {
240 if ( ev->type() == QEvent::MouseButtonDblClick )
241 {
242 if ( part == d->m_activePart && w == d->m_activeWidget )
243 return false;
244
245#ifdef DEBUG_PARTMANAGER
246 kDebug(1000) << "PartManager::eventFilter dblclick -> setActivePart" << part;
247#endif
248 d->setReason( ev );
249 setActivePart( part, w );
250 d->m_reason = NoReason;
251 return true;
252 }
253
254 if ( ( d->m_selectedWidget != w || d->m_selectedPart != part ) &&
255 ( d->m_activeWidget != w || d->m_activePart != part ) )
256 {
257 if ( part->isSelectable() )
258 setSelectedPart( part, w );
259 else {
260#ifdef DEBUG_PARTMANAGER
261 kDebug(1000) << "Part " << part << " (non-selectable) made active because " << w->metaObject()->className() << " got event" << " " << evType;
262#endif
263 d->setReason( ev );
264 setActivePart( part, w );
265 d->m_reason = NoReason;
266 }
267 return true;
268 }
269 else if ( d->m_selectedWidget == w && d->m_selectedPart == part )
270 {
271#ifdef DEBUG_PARTMANAGER
272 kDebug(1000) << "Part " << part << " made active (from selected) because " << w->metaObject()->className() << " got event" << " " << evType;
273#endif
274 d->setReason( ev );
275 setActivePart( part, w );
276 d->m_reason = NoReason;
277 return true;
278 }
279 else if ( d->m_activeWidget == w && d->m_activePart == part )
280 {
281 setSelectedPart( 0 );
282 return false;
283 }
284
285 return false;
286 }
287 else if ( part != d->m_activePart && d->allowExplicitFocusEvent(ev) )
288 {
289#ifdef DEBUG_PARTMANAGER
290 kDebug(1000) << "Part " << part << " made active because " << w->metaObject()->className() << " got event" << " " << evType;
291#endif
292 d->setReason( ev );
293 setActivePart( part, w );
294 d->m_reason = NoReason;
295 }
296
297 return false;
298 }
299
300 w = w->parentWidget();
301
302 if ( w && ( ( ( w->windowFlags() & Qt::Dialog ) && w->isModal() ) ||
303 ( w->windowFlags() & Qt::Popup ) || ( w->windowFlags() & Qt::Tool ) ) )
304 {
305#ifdef DEBUG_PARTMANAGER
306 kDebug(1000) << QString("No part made active although %1/%2 got event - loop aborted").arg(obj->objectName()).arg(obj->metaObject()->className());
307#endif
308 return false;
309 }
310
311 }
312
313#ifdef DEBUG_PARTMANAGER
314 kDebug(1000) << QString("No part made active although %1/%2 got event").arg(obj->objectName()).arg(obj->metaObject()->className());
315#endif
316 return false;
317}
318
319Part * PartManager::findPartFromWidget( QWidget * widget, const QPoint &pos )
320{
321 for ( QList<Part *>::iterator it = d->m_parts.begin(), end = d->m_parts.end() ; it != end ; ++it )
322 {
323 Part *part = (*it)->hitTest( widget, pos );
324 if ( part && d->m_parts.contains( part ) )
325 return part;
326 }
327 return 0;
328}
329
330Part * PartManager::findPartFromWidget( QWidget * widget )
331{
332 for ( QList<Part *>::iterator it = d->m_parts.begin(), end = d->m_parts.end() ; it != end ; ++it )
333 {
334 if ( widget == (*it)->widget() )
335 return (*it);
336 }
337 return 0;
338}
339
340void PartManager::addPart( Part *part, bool setActive )
341{
342 Q_ASSERT( part );
343
344 // don't add parts more than once :)
345 if ( d->m_parts.contains( part ) ) {
346#ifdef DEBUG_PARTMANAGER
347 kWarning(1000) << part << " already added" << kBacktrace(5);
348#endif
349 return;
350 }
351
352 d->m_parts.append( part );
353
354 part->setManager( this );
355
356 if ( setActive ) {
357 setActivePart( part );
358
359 if ( QWidget *w = part->widget() ) {
360 // Prevent focus problems
361 if ( w->focusPolicy() == Qt::NoFocus ) {
362 kWarning(1000) << "Part '" << part->objectName() << "' has a widget "
363 << w->objectName() << " with a focus policy of NoFocus. It should have at least a"
364 << "ClickFocus policy, for part activation to work well." << endl;
365 }
366 if ( part->widget() && part->widget()->focusPolicy() == Qt::TabFocus ) {
367 kWarning(1000) << "Part '" << part->objectName() << "' has a widget "
368 << w->objectName() << " with a focus policy of TabFocus. It should have at least a"
369 << "ClickFocus policy, for part activation to work well." << endl;
370 }
371 w->setFocus();
372 w->show();
373 }
374 }
375 emit partAdded( part );
376}
377
378void PartManager::removePart( Part *part )
379{
380 if (!d->m_parts.contains(part)) {
381 return;
382 }
383
384 const int nb = d->m_parts.removeAll(part);
385 Q_ASSERT(nb == 1);
386 Q_UNUSED(nb); // no warning in release mode
387 part->setManager(0);
388
389 emit partRemoved( part );
390
391 if ( part == d->m_activePart )
392 setActivePart( 0 );
393 if ( part == d->m_selectedPart )
394 setSelectedPart( 0 );
395}
396
397void PartManager::replacePart( Part * oldPart, Part * newPart, bool setActive )
398{
399 //kDebug(1000) << "replacePart " << oldPart->name() << "-> " << newPart->name() << " setActive=" << setActive;
400 // This methods does exactly removePart + addPart but without calling setActivePart(0) in between
401 if ( !d->m_parts.contains( oldPart ) )
402 {
403 kFatal(1000) << QString("Can't remove part %1, not in KPartManager's list.").arg(oldPart->objectName());
404 return;
405 }
406
407 d->m_parts.removeAll( oldPart );
408 oldPart->setManager(0);
409
410 emit partRemoved( oldPart );
411
412 addPart( newPart, setActive );
413}
414
415void PartManager::setActivePart( Part *part, QWidget *widget )
416{
417 if ( part && !d->m_parts.contains( part ) )
418 {
419 kWarning(1000) << "trying to activate a non-registered part!" << part->objectName();
420 return; // don't allow someone call setActivePart with a part we don't know about
421 }
422
423 //check whether nested parts are disallowed and activate the top parent part then, by traversing the
424 //tree recursively (Simon)
425 if ( part && !d->m_bAllowNestedParts )
426 {
427 QObject *parentPart = part->parent(); // ### this relies on people using KParts::Factory!
428 KParts::Part *parPart = ::qobject_cast<KParts::Part *>( parentPart );
429 if ( parPart )
430 {
431 setActivePart( parPart, parPart->widget() );
432 return;
433 }
434 }
435
436#ifdef DEBUG_PARTMANAGER
437 kDebug(1000) << "PartManager::setActivePart d->m_activePart=" << d->m_activePart << "<->part=" << part
438 << " d->m_activeWidget=" << d->m_activeWidget << "<->widget=" << widget << endl;
439#endif
440
441 // don't activate twice
442 if ( d->m_activePart && part && d->m_activePart == part &&
443 (!widget || d->m_activeWidget == widget) )
444 return;
445
446 KParts::Part *oldActivePart = d->m_activePart;
447 QWidget *oldActiveWidget = d->m_activeWidget;
448
449 setSelectedPart( 0 );
450
451 d->m_activePart = part;
452 d->m_activeWidget = widget;
453
454 if ( oldActivePart )
455 {
456 KParts::Part *savedActivePart = part;
457 QWidget *savedActiveWidget = widget;
458
459 PartActivateEvent ev( false, oldActivePart, oldActiveWidget );
460 QApplication::sendEvent( oldActivePart, &ev );
461 if ( oldActiveWidget )
462 {
463 disconnect( oldActiveWidget, SIGNAL(destroyed()),
464 this, SLOT(slotWidgetDestroyed()) );
465 QApplication::sendEvent( oldActiveWidget, &ev );
466 }
467
468 d->m_activePart = savedActivePart;
469 d->m_activeWidget = savedActiveWidget;
470 }
471
472 if ( d->m_activePart )
473 {
474 if ( !widget )
475 d->m_activeWidget = part->widget();
476
477 PartActivateEvent ev( true, d->m_activePart, d->m_activeWidget );
478 QApplication::sendEvent( d->m_activePart, &ev );
479 if ( d->m_activeWidget )
480 {
481 connect( d->m_activeWidget, SIGNAL(destroyed()),
482 this, SLOT(slotWidgetDestroyed()) );
483 QApplication::sendEvent( d->m_activeWidget, &ev );
484 }
485 }
486 // Set the new active instance in KGlobal
487 setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KGlobal::mainComponent());
488
489#ifdef DEBUG_PARTMANAGER
490 kDebug(1000) << this << " emitting activePartChanged " << d->m_activePart;
491#endif
492 emit activePartChanged( d->m_activePart );
493}
494
495void PartManager::setActiveComponent(const KComponentData &instance)
496{
497 // It's a separate method to allow redefining this behavior
498 KGlobal::setActiveComponent(instance);
499}
500
501Part *PartManager::activePart() const
502{
503 return d->m_activePart;
504}
505
506QWidget *PartManager::activeWidget() const
507{
508 return d->m_activeWidget;
509}
510
511void PartManager::setSelectedPart( Part *part, QWidget *widget )
512{
513 if ( part == d->m_selectedPart && widget == d->m_selectedWidget )
514 return;
515
516 Part *oldPart = d->m_selectedPart;
517 QWidget *oldWidget = d->m_selectedWidget;
518
519 d->m_selectedPart = part;
520 d->m_selectedWidget = widget;
521
522 if ( part && !widget )
523 d->m_selectedWidget = part->widget();
524
525 if ( oldPart )
526 {
527 PartSelectEvent ev( false, oldPart, oldWidget );
528 QApplication::sendEvent( oldPart, &ev );
529 QApplication::sendEvent( oldWidget, &ev );
530 }
531
532 if ( d->m_selectedPart )
533 {
534 PartSelectEvent ev( true, d->m_selectedPart, d->m_selectedWidget );
535 QApplication::sendEvent( d->m_selectedPart, &ev );
536 QApplication::sendEvent( d->m_selectedWidget, &ev );
537 }
538}
539
540Part *PartManager::selectedPart() const
541{
542 return d->m_selectedPart;
543}
544
545QWidget *PartManager::selectedWidget() const
546{
547 return d->m_selectedWidget;
548}
549
550void PartManager::slotObjectDestroyed()
551{
552 kDebug(1000);
553 removePart( const_cast<Part *>( static_cast<const Part *>( sender() ) ) );
554}
555
556void PartManager::slotWidgetDestroyed()
557{
558 kDebug(1000);
559 if ( static_cast<const QWidget *>( sender() ) == d->m_activeWidget )
560 setActivePart( 0 ); //do not remove the part because if the part's widget dies, then the
561 //part will delete itself anyway, invoking removePart() in its destructor
562}
563
564const QList<Part *> PartManager::parts() const
565{
566 return d->m_parts;
567}
568
569void PartManager::addManagedTopLevelWidget( const QWidget *topLevel )
570{
571 if ( !topLevel->isTopLevel() )
572 return;
573
574 if ( d->m_managedTopLevelWidgets.contains( topLevel ) )
575 return;
576
577 d->m_managedTopLevelWidgets.append( topLevel );
578 connect( topLevel, SIGNAL(destroyed()),
579 this, SLOT(slotManagedTopLevelWidgetDestroyed()) );
580}
581
582void PartManager::removeManagedTopLevelWidget( const QWidget *topLevel )
583{
584 if ( !topLevel->isTopLevel() )
585 return;
586
587 d->m_managedTopLevelWidgets.removeAll( topLevel );
588}
589
590void PartManager::slotManagedTopLevelWidgetDestroyed()
591{
592 const QWidget *widget = static_cast<const QWidget *>( sender() );
593 removeManagedTopLevelWidget( widget );
594}
595
596int PartManager::reason() const
597{
598 return d->m_reason;
599}
600
601void PartManager::setIgnoreExplictFocusRequests(bool ignore)
602{
603 d->m_bIgnoreExplicitFocusRequest = ignore;
604}
605
606#include "partmanager.moc"
607