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 | |
33 | using namespace KParts; |
34 | |
35 | namespace KParts { |
36 | |
37 | class PartManagerPrivate |
38 | { |
39 | public: |
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 | |
105 | PartManager::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 | |
116 | PartManager::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 | |
127 | PartManager::~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 | |
145 | void PartManager::setSelectionPolicy( SelectionPolicy policy ) |
146 | { |
147 | d->m_policy = policy; |
148 | } |
149 | |
150 | PartManager::SelectionPolicy PartManager::selectionPolicy() const |
151 | { |
152 | return d->m_policy; |
153 | } |
154 | |
155 | void PartManager::setAllowNestedParts( bool allow ) |
156 | { |
157 | d->m_bAllowNestedParts = allow; |
158 | } |
159 | |
160 | bool PartManager::allowNestedParts() const |
161 | { |
162 | return d->m_bAllowNestedParts; |
163 | } |
164 | |
165 | void PartManager::setIgnoreScrollBars( bool ignore ) |
166 | { |
167 | d->m_bIgnoreScrollBars = ignore; |
168 | } |
169 | |
170 | bool PartManager::ignoreScrollBars() const |
171 | { |
172 | return d->m_bIgnoreScrollBars; |
173 | } |
174 | |
175 | void PartManager::setActivationButtonMask( short int buttonMask ) |
176 | { |
177 | d->m_activationButtonMask = buttonMask; |
178 | } |
179 | |
180 | short int PartManager::activationButtonMask() const |
181 | { |
182 | return d->m_activationButtonMask; |
183 | } |
184 | |
185 | bool 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 | |
319 | Part * 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 | |
330 | Part * 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 | |
340 | void 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 | |
378 | void 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 | |
397 | void 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 | |
415 | void 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 | |
495 | void PartManager::setActiveComponent(const KComponentData &instance) |
496 | { |
497 | // It's a separate method to allow redefining this behavior |
498 | KGlobal::setActiveComponent(instance); |
499 | } |
500 | |
501 | Part *PartManager::activePart() const |
502 | { |
503 | return d->m_activePart; |
504 | } |
505 | |
506 | QWidget *PartManager::activeWidget() const |
507 | { |
508 | return d->m_activeWidget; |
509 | } |
510 | |
511 | void 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 | |
540 | Part *PartManager::selectedPart() const |
541 | { |
542 | return d->m_selectedPart; |
543 | } |
544 | |
545 | QWidget *PartManager::selectedWidget() const |
546 | { |
547 | return d->m_selectedWidget; |
548 | } |
549 | |
550 | void PartManager::slotObjectDestroyed() |
551 | { |
552 | kDebug(1000); |
553 | removePart( const_cast<Part *>( static_cast<const Part *>( sender() ) ) ); |
554 | } |
555 | |
556 | void 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 | |
564 | const QList<Part *> PartManager::parts() const |
565 | { |
566 | return d->m_parts; |
567 | } |
568 | |
569 | void 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 | |
582 | void PartManager::removeManagedTopLevelWidget( const QWidget *topLevel ) |
583 | { |
584 | if ( !topLevel->isTopLevel() ) |
585 | return; |
586 | |
587 | d->m_managedTopLevelWidgets.removeAll( topLevel ); |
588 | } |
589 | |
590 | void PartManager::slotManagedTopLevelWidgetDestroyed() |
591 | { |
592 | const QWidget *widget = static_cast<const QWidget *>( sender() ); |
593 | removeManagedTopLevelWidget( widget ); |
594 | } |
595 | |
596 | int PartManager::reason() const |
597 | { |
598 | return d->m_reason; |
599 | } |
600 | |
601 | void PartManager::setIgnoreExplictFocusRequests(bool ignore) |
602 | { |
603 | d->m_bIgnoreExplicitFocusRequest = ignore; |
604 | } |
605 | |
606 | #include "partmanager.moc" |
607 | |