00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "group.h"
00021
00022 #include "workspace.h"
00023 #include "client.h"
00024
00025 #include <assert.h>
00026 #include <kstartupinfo.h>
00027
00028
00029
00030
00031
00032
00033
00034
00035 namespace KWinInternal
00036 {
00037
00038
00039
00040
00041
00042 Group::Group( Window leader_P, Workspace* workspace_P )
00043 : leader_client( NULL ),
00044 leader_wid( leader_P ),
00045 _workspace( workspace_P ),
00046 leader_info( NULL ),
00047 user_time( -1U )
00048 {
00049 if( leader_P != None )
00050 {
00051 leader_client = workspace_P->findClient( WindowMatchPredicate( leader_P ));
00052 unsigned long properties[ 2 ] = { 0, NET::WM2StartupId };
00053 leader_info = new NETWinInfo( qt_xdisplay(), leader_P, workspace()->rootWin(),
00054 properties, 2 );
00055 }
00056 workspace()->addGroup( this, Allowed );
00057 }
00058
00059 Group::~Group()
00060 {
00061 delete leader_info;
00062 }
00063
00064 QPixmap Group::icon() const
00065 {
00066 if( leader_client != NULL )
00067 return leader_client->icon();
00068 else if( leader_wid != None )
00069 {
00070 QPixmap ic;
00071 Client::readIcons( leader_wid, &ic, NULL );
00072 return ic;
00073 }
00074 return QPixmap();
00075 }
00076
00077 QPixmap Group::miniIcon() const
00078 {
00079 if( leader_client != NULL )
00080 return leader_client->miniIcon();
00081 else if( leader_wid != None )
00082 {
00083 QPixmap ic;
00084 Client::readIcons( leader_wid, NULL, &ic );
00085 return ic;
00086 }
00087 return QPixmap();
00088 }
00089
00090 void Group::addMember( Client* member_P )
00091 {
00092 _members.append( member_P );
00093
00094
00095 }
00096
00097 void Group::removeMember( Client* member_P )
00098 {
00099
00100
00101 Q_ASSERT( _members.contains( member_P ));
00102 _members.remove( member_P );
00103 if( _members.isEmpty())
00104 {
00105 workspace()->removeGroup( this, Allowed );
00106 delete this;
00107 }
00108 }
00109
00110 void Group::gotLeader( Client* leader_P )
00111 {
00112 assert( leader_P->window() == leader_wid );
00113 leader_client = leader_P;
00114 }
00115
00116 void Group::lostLeader()
00117 {
00118 assert( !_members.contains( leader_client ));
00119 leader_client = NULL;
00120 if( _members.isEmpty())
00121 {
00122 workspace()->removeGroup( this, Allowed );
00123 delete this;
00124 }
00125 }
00126
00127 void Group::getIcons()
00128 {
00129
00130 }
00131
00132
00133
00134
00135
00136 Group* Workspace::findGroup( Window leader ) const
00137 {
00138 assert( leader != None );
00139 for( GroupList::ConstIterator it = groups.begin();
00140 it != groups.end();
00141 ++it )
00142 if( (*it)->leader() == leader )
00143 return *it;
00144 return NULL;
00145 }
00146
00147
00148
00149 Group* Workspace::findClientLeaderGroup( const Client* c ) const
00150 {
00151 Group* ret = NULL;
00152 for( ClientList::ConstIterator it = clients.begin();
00153 it != clients.end();
00154 ++it )
00155 {
00156 if( *it == c )
00157 continue;
00158 if( (*it)->wmClientLeader() == c->wmClientLeader())
00159 {
00160 if( ret == NULL || ret == (*it)->group())
00161 ret = (*it)->group();
00162 else
00163 {
00164
00165
00166
00167
00168 Group* old_group = (*it)->group();
00169
00170 for( int cnt = old_group->members().count();
00171 cnt > 0;
00172 --cnt )
00173 {
00174 Client* tmp = old_group->members().first();
00175 tmp->checkGroup( ret );
00176 }
00177 }
00178 }
00179 }
00180 return ret;
00181 }
00182
00183 void Workspace::updateMinimizedOfTransients( Client* c )
00184 {
00185
00186 if ( c->isMinimized() || c->isShade() )
00187 {
00188 for( ClientList::ConstIterator it = c->transients().begin();
00189 it != c->transients().end();
00190 ++it )
00191 {
00192 if( !(*it)->isMinimized()
00193 && !(*it)->isTopMenu() )
00194 {
00195 (*it)->minimize( true );
00196 updateMinimizedOfTransients( (*it) );
00197 }
00198 }
00199 }
00200 else
00201 {
00202 for( ClientList::ConstIterator it = c->transients().begin();
00203 it != c->transients().end();
00204 ++it )
00205 {
00206 if( (*it)->isMinimized()
00207 && !(*it)->isTopMenu())
00208 {
00209 (*it)->unminimize( true );
00210 updateMinimizedOfTransients( (*it) );
00211 }
00212 }
00213 }
00214 }
00215
00216
00220 void Workspace::updateOnAllDesktopsOfTransients( Client* c )
00221 {
00222 for( ClientList::ConstIterator it = c->transients().begin();
00223 it != c->transients().end();
00224 ++it)
00225 {
00226 if( (*it)->isOnAllDesktops() != c->isOnAllDesktops())
00227 (*it)->setOnAllDesktops( c->isOnAllDesktops());
00228 }
00229 }
00230
00231
00232 void Workspace::checkTransients( Window w )
00233 {
00234 for( ClientList::ConstIterator it = clients.begin();
00235 it != clients.end();
00236 ++it )
00237 (*it)->checkTransient( w );
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248 bool Client::resourceMatch( const Client* c1, const Client* c2 )
00249 {
00250
00251 if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00252 return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00253
00254 if( c1->resourceName() == "mozilla" )
00255 return c2->resourceName() == "mozilla";
00256 return c1->resourceClass() == c2->resourceClass();
00257 }
00258
00259 bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack )
00260 {
00261 bool same_app = false;
00262 if( c1 == c2 )
00263 same_app = true;
00264 else if( c1->isTransient() && c2->hasTransient( c1, true ))
00265 same_app = true;
00266 else if( c2->isTransient() && c1->hasTransient( c2, true ))
00267 same_app = true;
00268 else if( c1->pid() != c2->pid()
00269 || c1->wmClientMachine( false ) != c2->wmClientMachine( false ))
00270 ;
00271 else if( c1->wmClientLeader() != c2->wmClientLeader()
00272 && c1->wmClientLeader() != c1->window()
00273 && c2->wmClientLeader() != c2->window())
00274 ;
00275 else if( !resourceMatch( c1, c2 ))
00276 ;
00277 else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00278 ;
00279 else if( c1->wmClientLeader() == c2->wmClientLeader()
00280 && c1->wmClientLeader() != c1->window()
00281 && c2->wmClientLeader() != c2->window())
00282 same_app = true;
00283 else if( c1->group() == c2->group())
00284 same_app = true;
00285 else if( c1->pid() == 0 || c2->pid() == 0 )
00286 ;
00287
00288 else
00289 same_app = true;
00290 return same_app;
00291 }
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack )
00304 {
00305 if( c1->isTransient())
00306 {
00307 while( c1->transientFor() != NULL )
00308 c1 = c1->transientFor();
00309 if( c1->groupTransient())
00310 return c1->group() == c2->group();
00311 #if 0
00312
00313
00314
00315 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00316 #endif
00317 }
00318 if( c2->isTransient())
00319 {
00320 while( c2->transientFor() != NULL )
00321 c2 = c2->transientFor();
00322 if( c2->groupTransient())
00323 return c1->group() == c2->group();
00324 #if 0
00325 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00326 #endif
00327 }
00328 int pos1 = c1->windowRole().find( '#' );
00329 int pos2 = c2->windowRole().find( '#' );
00330 if(( pos1 >= 0 && pos2 >= 0 )
00331 ||
00332
00333
00334 c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" )
00335 {
00336 if( !active_hack )
00337 return c1 == c2;
00338 if( !c1->isActive() && !c2->isActive())
00339 return c1 == c2;
00340 else
00341 return true;
00342 }
00343 return true;
00344 }
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392 void Client::readTransient()
00393 {
00394 Window new_transient_for_id;
00395 if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id ))
00396 {
00397 original_transient_for_id = new_transient_for_id;
00398 new_transient_for_id = verifyTransientFor( new_transient_for_id, true );
00399 }
00400 else
00401 {
00402 original_transient_for_id = None;
00403 new_transient_for_id = verifyTransientFor( None, false );
00404 }
00405 setTransient( new_transient_for_id );
00406 }
00407
00408 void Client::setTransient( Window new_transient_for_id )
00409 {
00410 if( new_transient_for_id != transient_for_id )
00411 {
00412 removeFromMainClients();
00413 transient_for = NULL;
00414 transient_for_id = new_transient_for_id;
00415 if( transient_for_id != None && !groupTransient())
00416 {
00417 transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id ));
00418 assert( transient_for != NULL );
00419 transient_for->addTransient( this );
00420 }
00421 checkGroup( NULL, true );
00422 if( isTopMenu())
00423 workspace()->updateCurrentTopMenu();
00424 workspace()->updateClientLayer( this );
00425 }
00426 }
00427
00428 void Client::removeFromMainClients()
00429 {
00430 if( transientFor() != NULL )
00431 transientFor()->removeTransient( this );
00432 if( groupTransient())
00433 {
00434 for( ClientList::ConstIterator it = group()->members().begin();
00435 it != group()->members().end();
00436 ++it )
00437 (*it)->removeTransient( this );
00438 }
00439 }
00440
00441
00442
00443
00444
00445 void Client::cleanGrouping()
00446 {
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458 removeFromMainClients();
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469 for( ClientList::ConstIterator it = transients_list.begin();
00470 it != transients_list.end();
00471 )
00472 {
00473 if( (*it)->transientFor() == this )
00474 {
00475 ClientList::ConstIterator it2 = it++;
00476 removeTransient( *it2 );
00477 }
00478 else
00479 ++it;
00480 }
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496 ClientList group_members = group()->members();
00497 group()->removeMember( this );
00498 in_group = NULL;
00499 for( ClientList::ConstIterator it = group_members.begin();
00500 it != group_members.end();
00501 ++it )
00502 (*it)->removeTransient( this );
00503
00504
00505
00506
00507
00508 }
00509
00510
00511
00512
00513
00514 void Client::checkGroupTransients()
00515 {
00516 for( ClientList::ConstIterator it1 = group()->members().begin();
00517 it1 != group()->members().end();
00518 ++it1 )
00519 {
00520 if( !(*it1)->groupTransient())
00521 continue;
00522 for( ClientList::ConstIterator it2 = group()->members().begin();
00523 it2 != group()->members().end();
00524 ++it2 )
00525 {
00526 if( *it1 == *it2 )
00527 continue;
00528 for( Client* cl = (*it2)->transientFor();
00529 cl != NULL;
00530 cl = cl->transientFor())
00531 {
00532 if( cl == *it1 )
00533 {
00534 (*it2)->transients_list.remove( *it1 );
00535 continue;
00536 }
00537 }
00538
00539
00540
00541
00542 if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00543 (*it2)->transients_list.remove( *it1 );
00544
00545
00546
00547
00548
00549 for( ClientList::ConstIterator it3 = group()->members().begin();
00550 it3 != group()->members().end();
00551 ++it3 )
00552 {
00553 if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 )
00554 continue;
00555 if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false ))
00556 {
00557 if( (*it2)->hasTransient( *it3, true ))
00558 (*it3)->transients_list.remove( *it1 );
00559 if( (*it3)->hasTransient( *it2, true ))
00560 (*it2)->transients_list.remove( *it1 );
00561 }
00562 }
00563 }
00564 }
00565 }
00566
00570 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00571 {
00572 Window new_property_value = new_transient_for;
00573
00574
00575 if( isSplash() && new_transient_for == None )
00576 new_transient_for = workspace()->rootWin();
00577 if( new_transient_for == None )
00578 if( defined )
00579 new_property_value = new_transient_for = workspace()->rootWin();
00580 else
00581 return None;
00582 if( new_transient_for == window())
00583 {
00584 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00585 new_property_value = new_transient_for = workspace()->rootWin();
00586 }
00587
00588
00589
00590 WId before_search = new_transient_for;
00591 while( new_transient_for != None
00592 && new_transient_for != workspace()->rootWin()
00593 && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00594 {
00595 Window root_return, parent_return;
00596 Window* wins = NULL;
00597 unsigned int nwins;
00598 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00599 if ( wins )
00600 XFree((void *) wins);
00601 if ( r == 0)
00602 break;
00603 new_transient_for = parent_return;
00604 }
00605 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00606 {
00607 if( new_transient_for != before_search )
00608 {
00609 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00610 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00611 new_property_value = new_transient_for;
00612 }
00613 }
00614 else
00615 new_transient_for = before_search;
00616
00617
00618
00619 int count = 20;
00620 Window loop_pos = new_transient_for;
00621 while( loop_pos != None && loop_pos != workspace()->rootWin())
00622 {
00623 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00624 if( pos == NULL )
00625 break;
00626 loop_pos = pos->transient_for_id;
00627 if( --count == 0 )
00628 {
00629 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00630 new_transient_for = workspace()->rootWin();
00631 }
00632 }
00633 if( new_transient_for != workspace()->rootWin()
00634 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00635 {
00636 new_transient_for = workspace()->rootWin();
00637 }
00638 if( new_property_value != original_transient_for_id )
00639 XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00640 return new_transient_for;
00641 }
00642
00643 void Client::addTransient( Client* cl )
00644 {
00645 assert( !transients_list.contains( cl ));
00646
00647 assert( cl != this );
00648 transients_list.append( cl );
00649 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00650 check_active_modal = true;
00651
00652
00653
00654
00655
00656
00657 }
00658
00659 void Client::removeTransient( Client* cl )
00660 {
00661
00662
00663 transients_list.remove( cl );
00664
00665
00666 if( cl->transientFor() == this )
00667 {
00668 cl->transient_for_id = None;
00669 cl->transient_for = NULL;
00670
00671 cl->setTransient( None );
00672 }
00673 }
00674
00675
00676 void Client::checkTransient( Window w )
00677 {
00678 if( original_transient_for_id != w )
00679 return;
00680 w = verifyTransientFor( w, true );
00681 setTransient( w );
00682 }
00683
00684
00685
00686 bool Client::hasTransient( const Client* cl, bool indirect ) const
00687 {
00688
00689 ConstClientList set;
00690 return hasTransientInternal( cl, indirect, set );
00691 }
00692
00693 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00694 {
00695 if( cl->transientFor() != NULL )
00696 {
00697 if( cl->transientFor() == this )
00698 return true;
00699 if( !indirect )
00700 return false;
00701 if( set.contains( cl ))
00702 return false;
00703 set.append( cl );
00704 return hasTransientInternal( cl->transientFor(), indirect, set );
00705 }
00706 if( !cl->isTransient())
00707 return false;
00708 if( group() != cl->group())
00709 return false;
00710
00711 if( transients().contains( const_cast< Client* >( cl )))
00712 return true;
00713 if( !indirect )
00714 return false;
00715 if( set.contains( this ))
00716 return false;
00717 set.append( this );
00718 for( ClientList::ConstIterator it = transients().begin();
00719 it != transients().end();
00720 ++it )
00721 if( (*it)->hasTransientInternal( cl, indirect, set ))
00722 return true;
00723 return false;
00724 }
00725
00726 ClientList Client::mainClients() const
00727 {
00728 if( !isTransient())
00729 return ClientList();
00730 if( transientFor() != NULL )
00731 return ClientList() << const_cast< Client* >( transientFor());
00732 ClientList result;
00733 for( ClientList::ConstIterator it = group()->members().begin();
00734 it != group()->members().end();
00735 ++it )
00736 if((*it)->hasTransient( this, false ))
00737 result.append( *it );
00738 return result;
00739 }
00740
00741 Client* Client::findModal()
00742 {
00743 for( ClientList::ConstIterator it = transients().begin();
00744 it != transients().end();
00745 ++it )
00746 if( Client* ret = (*it)->findModal())
00747 return ret;
00748 if( isModal())
00749 return this;
00750 return NULL;
00751 }
00752
00753
00754
00755
00756 void Client::checkGroup( Group* set_group, bool force )
00757 {
00758 Group* old_group = in_group;
00759 if( set_group != NULL )
00760 {
00761 if( set_group != in_group )
00762 {
00763 if( in_group != NULL )
00764 in_group->removeMember( this );
00765 in_group = set_group;
00766 in_group->addMember( this );
00767 }
00768 }
00769 else if( window_group != None )
00770 {
00771 Group* new_group = workspace()->findGroup( window_group );
00772 if( transientFor() != NULL && transientFor()->group() != new_group )
00773 {
00774
00775 new_group = transientFor()->group();
00776 }
00777 if( new_group == NULL )
00778 new_group = new Group( window_group, workspace());
00779 if( new_group != in_group )
00780 {
00781 if( in_group != NULL )
00782 in_group->removeMember( this );
00783 in_group = new_group;
00784 in_group->addMember( this );
00785 }
00786 }
00787 else
00788 {
00789 if( transientFor() != NULL )
00790 {
00791
00792 Group* new_group = transientFor()->group();
00793 if( new_group != in_group )
00794 {
00795 if( in_group != NULL )
00796 in_group->removeMember( this );
00797 in_group = transientFor()->group();
00798 in_group->addMember( this );
00799 }
00800 }
00801 else if( groupTransient())
00802 {
00803
00804 Group* new_group = workspace()->findClientLeaderGroup( this );
00805 if( new_group == NULL )
00806 new_group = new Group( None, workspace());
00807 if( new_group != in_group )
00808 {
00809 if( in_group != NULL )
00810 in_group->removeMember( this );
00811 in_group = new_group;
00812 in_group->addMember( this );
00813 }
00814 }
00815 else
00816 {
00817 if( in_group != NULL && in_group->leader() != window())
00818 {
00819 in_group->removeMember( this );
00820 in_group = NULL;
00821 }
00822 if( in_group == NULL )
00823 {
00824 in_group = new Group( None, workspace());
00825 in_group->addMember( this );
00826 }
00827 }
00828 }
00829 if( in_group != old_group || force )
00830 {
00831 for( ClientList::Iterator it = transients_list.begin();
00832 it != transients_list.end();
00833 )
00834 {
00835 if( (*it)->groupTransient() && (*it)->group() != group())
00836 it = transients_list.remove( it );
00837 else
00838 ++it;
00839 }
00840 if( groupTransient())
00841 {
00842 for( ClientList::ConstIterator it = group()->members().begin();
00843 it != group()->members().end();
00844 ++it )
00845 {
00846 if( *it == this )
00847 break;
00848 (*it)->addTransient( this );
00849 }
00850 }
00851 #if 0 // TODO
00852 if( groupTransient())
00853 {
00854 if( workspace()->findGroup( old_group ))
00855 {
00856 for( ClientList::ConstIterator it = old_group->members().begin();
00857 it != old_group->members().end();
00858 ++it )
00859 (*it)->removeTransient( this );
00860 }
00861
00862
00863
00864 for( ClientList::ConstIterator it = group()->members().begin();
00865 it != group()->members().end();
00866 ++it )
00867 (*it)->addTransient( this );
00868 }
00869 #endif
00870
00871
00872 for( ClientList::ConstIterator it = group()->members().begin();
00873 it != group()->members().end();
00874 ++it )
00875 {
00876 if( !(*it)->isSplash())
00877 continue;
00878 if( !(*it)->groupTransient())
00879 continue;
00880 if( *it == this || hasTransient( *it, true ))
00881 continue;
00882 addTransient( *it );
00883 }
00884 }
00885 checkGroupTransients();
00886 checkActiveModal();
00887 workspace()->updateClientLayer( this );
00888 }
00889
00890 bool Client::check_active_modal = false;
00891
00892 void Client::checkActiveModal()
00893 {
00894
00895
00896
00897 Client* check_modal = workspace()->mostRecentlyActivatedClient();
00898 if( check_modal != NULL && check_modal->check_active_modal )
00899 {
00900 Client* new_modal = check_modal->findModal();
00901 if( new_modal != NULL && new_modal != check_modal )
00902 {
00903 if( !new_modal->isManaged())
00904 return;
00905 workspace()->activateClient( new_modal );
00906 }
00907 check_modal->check_active_modal = false;
00908 }
00909 }
00910
00911 }