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
00263
00264 if( c1 == c2 )
00265 same_app = true;
00266 else if( c1->isTransient() && c2->hasTransient( c1, true ))
00267 same_app = true;
00268 else if( c2->isTransient() && c1->hasTransient( c2, true ))
00269 same_app = true;
00270 else if( c1->group() == c2->group())
00271 same_app = true;
00272 else if( c1->wmClientLeader() == c2->wmClientLeader()
00273 && c1->wmClientLeader() != c1->window()
00274 && c2->wmClientLeader() != c2->window())
00275 same_app = true;
00276
00277
00278 else if( c1->pid() != c2->pid()
00279 || c1->wmClientMachine( false ) != c2->wmClientMachine( false ))
00280 ;
00281 else if( c1->wmClientLeader() != c2->wmClientLeader()
00282 && c1->wmClientLeader() != c1->window()
00283 && c2->wmClientLeader() != c2->window())
00284 ;
00285 else if( !resourceMatch( c1, c2 ))
00286 ;
00287 else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00288 ;
00289 else if( c1->pid() == 0 || c2->pid() == 0 )
00290 ;
00291
00292 else
00293 same_app = true;
00294
00295 return same_app;
00296 }
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack )
00309 {
00310 if( c1->isTransient())
00311 {
00312 while( c1->transientFor() != NULL )
00313 c1 = c1->transientFor();
00314 if( c1->groupTransient())
00315 return c1->group() == c2->group();
00316 #if 0
00317
00318
00319
00320 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00321 #endif
00322 }
00323 if( c2->isTransient())
00324 {
00325 while( c2->transientFor() != NULL )
00326 c2 = c2->transientFor();
00327 if( c2->groupTransient())
00328 return c1->group() == c2->group();
00329 #if 0
00330 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00331 #endif
00332 }
00333 int pos1 = c1->windowRole().find( '#' );
00334 int pos2 = c2->windowRole().find( '#' );
00335 if(( pos1 >= 0 && pos2 >= 0 )
00336 ||
00337
00338
00339 c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" )
00340 {
00341 if( !active_hack )
00342 return c1 == c2;
00343 if( !c1->isActive() && !c2->isActive())
00344 return c1 == c2;
00345 else
00346 return true;
00347 }
00348 return true;
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
00393
00394
00395
00396
00397 void Client::readTransient()
00398 {
00399 Window new_transient_for_id;
00400 if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id ))
00401 {
00402 original_transient_for_id = new_transient_for_id;
00403 new_transient_for_id = verifyTransientFor( new_transient_for_id, true );
00404 }
00405 else
00406 {
00407 original_transient_for_id = None;
00408 new_transient_for_id = verifyTransientFor( None, false );
00409 }
00410 setTransient( new_transient_for_id );
00411 }
00412
00413 void Client::setTransient( Window new_transient_for_id )
00414 {
00415 if( new_transient_for_id != transient_for_id )
00416 {
00417 removeFromMainClients();
00418 transient_for = NULL;
00419 transient_for_id = new_transient_for_id;
00420 if( transient_for_id != None && !groupTransient())
00421 {
00422 transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id ));
00423 assert( transient_for != NULL );
00424 transient_for->addTransient( this );
00425 }
00426 checkGroup( NULL, true );
00427 if( isTopMenu())
00428 workspace()->updateCurrentTopMenu();
00429 workspace()->updateClientLayer( this );
00430 }
00431 }
00432
00433 void Client::removeFromMainClients()
00434 {
00435 if( transientFor() != NULL )
00436 transientFor()->removeTransient( this );
00437 if( groupTransient())
00438 {
00439 for( ClientList::ConstIterator it = group()->members().begin();
00440 it != group()->members().end();
00441 ++it )
00442 (*it)->removeTransient( this );
00443 }
00444 }
00445
00446
00447
00448
00449
00450 void Client::cleanGrouping()
00451 {
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463 removeFromMainClients();
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474 for( ClientList::ConstIterator it = transients_list.begin();
00475 it != transients_list.end();
00476 )
00477 {
00478 if( (*it)->transientFor() == this )
00479 {
00480 ClientList::ConstIterator it2 = it++;
00481 removeTransient( *it2 );
00482 }
00483 else
00484 ++it;
00485 }
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501 ClientList group_members = group()->members();
00502 group()->removeMember( this );
00503 in_group = NULL;
00504 for( ClientList::ConstIterator it = group_members.begin();
00505 it != group_members.end();
00506 ++it )
00507 (*it)->removeTransient( this );
00508
00509
00510
00511
00512
00513 }
00514
00515
00516
00517
00518
00519 void Client::checkGroupTransients()
00520 {
00521 for( ClientList::ConstIterator it1 = group()->members().begin();
00522 it1 != group()->members().end();
00523 ++it1 )
00524 {
00525 if( !(*it1)->groupTransient())
00526 continue;
00527 for( ClientList::ConstIterator it2 = group()->members().begin();
00528 it2 != group()->members().end();
00529 ++it2 )
00530 {
00531 if( *it1 == *it2 )
00532 continue;
00533 for( Client* cl = (*it2)->transientFor();
00534 cl != NULL;
00535 cl = cl->transientFor())
00536 {
00537 if( cl == *it1 )
00538 {
00539 (*it2)->transients_list.remove( *it1 );
00540 continue;
00541 }
00542 }
00543
00544
00545
00546
00547 if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00548 (*it2)->transients_list.remove( *it1 );
00549
00550
00551
00552
00553
00554 for( ClientList::ConstIterator it3 = group()->members().begin();
00555 it3 != group()->members().end();
00556 ++it3 )
00557 {
00558 if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 )
00559 continue;
00560 if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false ))
00561 {
00562 if( (*it2)->hasTransient( *it3, true ))
00563 (*it3)->transients_list.remove( *it1 );
00564 if( (*it3)->hasTransient( *it2, true ))
00565 (*it2)->transients_list.remove( *it1 );
00566 }
00567 }
00568 }
00569 }
00570 }
00571
00575 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00576 {
00577 Window new_property_value = new_transient_for;
00578
00579
00580 if( isSplash() && new_transient_for == None )
00581 new_transient_for = workspace()->rootWin();
00582 if( new_transient_for == None )
00583 if( defined )
00584 new_property_value = new_transient_for = workspace()->rootWin();
00585 else
00586 return None;
00587 if( new_transient_for == window())
00588 {
00589 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00590 new_property_value = new_transient_for = workspace()->rootWin();
00591 }
00592
00593
00594
00595 WId before_search = new_transient_for;
00596 while( new_transient_for != None
00597 && new_transient_for != workspace()->rootWin()
00598 && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00599 {
00600 Window root_return, parent_return;
00601 Window* wins = NULL;
00602 unsigned int nwins;
00603 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00604 if ( wins )
00605 XFree((void *) wins);
00606 if ( r == 0)
00607 break;
00608 new_transient_for = parent_return;
00609 }
00610 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00611 {
00612 if( new_transient_for != before_search )
00613 {
00614 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00615 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00616 new_property_value = new_transient_for;
00617 }
00618 }
00619 else
00620 new_transient_for = before_search;
00621
00622
00623
00624 int count = 20;
00625 Window loop_pos = new_transient_for;
00626 while( loop_pos != None && loop_pos != workspace()->rootWin())
00627 {
00628 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00629 if( pos == NULL )
00630 break;
00631 loop_pos = pos->transient_for_id;
00632 if( --count == 0 )
00633 {
00634 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00635 new_transient_for = workspace()->rootWin();
00636 }
00637 }
00638 if( new_transient_for != workspace()->rootWin()
00639 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00640 {
00641 new_transient_for = workspace()->rootWin();
00642 }
00643 if( new_property_value != original_transient_for_id )
00644 XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00645 return new_transient_for;
00646 }
00647
00648 void Client::addTransient( Client* cl )
00649 {
00650 assert( !transients_list.contains( cl ));
00651
00652 assert( cl != this );
00653 transients_list.append( cl );
00654 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00655 check_active_modal = true;
00656
00657
00658
00659
00660
00661
00662 }
00663
00664 void Client::removeTransient( Client* cl )
00665 {
00666
00667
00668 transients_list.remove( cl );
00669
00670
00671 if( cl->transientFor() == this )
00672 {
00673 cl->transient_for_id = None;
00674 cl->transient_for = NULL;
00675
00676 cl->setTransient( None );
00677 }
00678 }
00679
00680
00681 void Client::checkTransient( Window w )
00682 {
00683 if( original_transient_for_id != w )
00684 return;
00685 w = verifyTransientFor( w, true );
00686 setTransient( w );
00687 }
00688
00689
00690
00691 bool Client::hasTransient( const Client* cl, bool indirect ) const
00692 {
00693
00694 ConstClientList set;
00695 return hasTransientInternal( cl, indirect, set );
00696 }
00697
00698 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00699 {
00700 if( cl->transientFor() != NULL )
00701 {
00702 if( cl->transientFor() == this )
00703 return true;
00704 if( !indirect )
00705 return false;
00706 if( set.contains( cl ))
00707 return false;
00708 set.append( cl );
00709 return hasTransientInternal( cl->transientFor(), indirect, set );
00710 }
00711 if( !cl->isTransient())
00712 return false;
00713 if( group() != cl->group())
00714 return false;
00715
00716 if( transients().contains( const_cast< Client* >( cl )))
00717 return true;
00718 if( !indirect )
00719 return false;
00720 if( set.contains( this ))
00721 return false;
00722 set.append( this );
00723 for( ClientList::ConstIterator it = transients().begin();
00724 it != transients().end();
00725 ++it )
00726 if( (*it)->hasTransientInternal( cl, indirect, set ))
00727 return true;
00728 return false;
00729 }
00730
00731 ClientList Client::mainClients() const
00732 {
00733 if( !isTransient())
00734 return ClientList();
00735 if( transientFor() != NULL )
00736 return ClientList() << const_cast< Client* >( transientFor());
00737 ClientList result;
00738 for( ClientList::ConstIterator it = group()->members().begin();
00739 it != group()->members().end();
00740 ++it )
00741 if((*it)->hasTransient( this, false ))
00742 result.append( *it );
00743 return result;
00744 }
00745
00746 Client* Client::findModal()
00747 {
00748 for( ClientList::ConstIterator it = transients().begin();
00749 it != transients().end();
00750 ++it )
00751 if( Client* ret = (*it)->findModal())
00752 return ret;
00753 if( isModal())
00754 return this;
00755 return NULL;
00756 }
00757
00758
00759
00760
00761 void Client::checkGroup( Group* set_group, bool force )
00762 {
00763 Group* old_group = in_group;
00764 if( set_group != NULL )
00765 {
00766 if( set_group != in_group )
00767 {
00768 if( in_group != NULL )
00769 in_group->removeMember( this );
00770 in_group = set_group;
00771 in_group->addMember( this );
00772 }
00773 }
00774 else if( window_group != None )
00775 {
00776 Group* new_group = workspace()->findGroup( window_group );
00777 if( transientFor() != NULL && transientFor()->group() != new_group )
00778 {
00779
00780 new_group = transientFor()->group();
00781 }
00782 if( new_group == NULL )
00783 new_group = new Group( window_group, workspace());
00784 if( new_group != in_group )
00785 {
00786 if( in_group != NULL )
00787 in_group->removeMember( this );
00788 in_group = new_group;
00789 in_group->addMember( this );
00790 }
00791 }
00792 else
00793 {
00794 if( transientFor() != NULL )
00795 {
00796
00797 Group* new_group = transientFor()->group();
00798 if( new_group != in_group )
00799 {
00800 if( in_group != NULL )
00801 in_group->removeMember( this );
00802 in_group = transientFor()->group();
00803 in_group->addMember( this );
00804 }
00805 }
00806 else if( groupTransient())
00807 {
00808
00809 Group* new_group = workspace()->findClientLeaderGroup( this );
00810 if( new_group == NULL )
00811 new_group = new Group( None, workspace());
00812 if( new_group != in_group )
00813 {
00814 if( in_group != NULL )
00815 in_group->removeMember( this );
00816 in_group = new_group;
00817 in_group->addMember( this );
00818 }
00819 }
00820 else
00821 {
00822 if( in_group != NULL && in_group->leader() != window())
00823 {
00824 in_group->removeMember( this );
00825 in_group = NULL;
00826 }
00827 if( in_group == NULL )
00828 {
00829 in_group = new Group( None, workspace());
00830 in_group->addMember( this );
00831 }
00832 }
00833 }
00834 if( in_group != old_group || force )
00835 {
00836 for( ClientList::Iterator it = transients_list.begin();
00837 it != transients_list.end();
00838 )
00839 {
00840 if( (*it)->groupTransient() && (*it)->group() != group())
00841 it = transients_list.remove( it );
00842 else
00843 ++it;
00844 }
00845 if( groupTransient())
00846 {
00847 for( ClientList::ConstIterator it = group()->members().begin();
00848 it != group()->members().end();
00849 ++it )
00850 {
00851 if( *it == this )
00852 break;
00853 (*it)->addTransient( this );
00854 }
00855 }
00856 #if 0 // TODO
00857 if( groupTransient())
00858 {
00859 if( workspace()->findGroup( old_group ))
00860 {
00861 for( ClientList::ConstIterator it = old_group->members().begin();
00862 it != old_group->members().end();
00863 ++it )
00864 (*it)->removeTransient( this );
00865 }
00866
00867
00868
00869 for( ClientList::ConstIterator it = group()->members().begin();
00870 it != group()->members().end();
00871 ++it )
00872 (*it)->addTransient( this );
00873 }
00874 #endif
00875
00876
00877 for( ClientList::ConstIterator it = group()->members().begin();
00878 it != group()->members().end();
00879 ++it )
00880 {
00881 if( !(*it)->isSplash())
00882 continue;
00883 if( !(*it)->groupTransient())
00884 continue;
00885 if( *it == this || hasTransient( *it, true ))
00886 continue;
00887 addTransient( *it );
00888 }
00889 }
00890 checkGroupTransients();
00891 checkActiveModal();
00892 workspace()->updateClientLayer( this );
00893 }
00894
00895 bool Client::check_active_modal = false;
00896
00897 void Client::checkActiveModal()
00898 {
00899
00900
00901
00902 Client* check_modal = workspace()->mostRecentlyActivatedClient();
00903 if( check_modal != NULL && check_modal->check_active_modal )
00904 {
00905 Client* new_modal = check_modal->findModal();
00906 if( new_modal != NULL && new_modal != check_modal )
00907 {
00908 if( !new_modal->isManaged())
00909 return;
00910 workspace()->activateClient( new_modal );
00911 }
00912 check_modal->check_active_modal = false;
00913 }
00914 }
00915
00916 }