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();
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();
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() != c2->wmClientMachine())
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 workspace()->updateClientLayer( this );
00423 }
00424 }
00425
00426 void Client::removeFromMainClients()
00427 {
00428 if( transientFor() != NULL )
00429 transientFor()->removeTransient( this );
00430 if( groupTransient())
00431 {
00432 for( ClientList::ConstIterator it = group()->members().begin();
00433 it != group()->members().end();
00434 ++it )
00435 (*it)->removeTransient( this );
00436 }
00437 }
00438
00439
00440
00441
00442
00443 void Client::cleanGrouping()
00444 {
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456 removeFromMainClients();
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 for( ClientList::ConstIterator it = transients_list.begin();
00468 it != transients_list.end();
00469 )
00470 {
00471 if( (*it)->transientFor() == this )
00472 {
00473 ClientList::ConstIterator it2 = it++;
00474 removeTransient( *it2 );
00475 }
00476 else
00477 ++it;
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494 ClientList group_members = group()->members();
00495 group()->removeMember( this );
00496 in_group = NULL;
00497 for( ClientList::ConstIterator it = group_members.begin();
00498 it != group_members.end();
00499 ++it )
00500 (*it)->removeTransient( this );
00501
00502
00503
00504
00505
00506 }
00507
00508
00509
00510
00511
00512 void Client::checkGroupTransients()
00513 {
00514 for( ClientList::ConstIterator it1 = group()->members().begin();
00515 it1 != group()->members().end();
00516 ++it1 )
00517 {
00518 if( !(*it1)->groupTransient())
00519 continue;
00520 for( ClientList::ConstIterator it2 = group()->members().begin();
00521 it2 != group()->members().end();
00522 ++it2 )
00523 {
00524 if( *it1 == *it2 )
00525 continue;
00526 for( Client* cl = (*it2)->transientFor();
00527 cl != NULL;
00528 cl = cl->transientFor())
00529 {
00530 if( cl == *it1 )
00531 {
00532 (*it2)->transients_list.remove( *it1 );
00533 continue;
00534 }
00535 }
00536
00537
00538
00539
00540 if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00541 (*it2)->transients_list.remove( *it1 );
00542 }
00543 }
00544 }
00545
00549 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00550 {
00551 Window new_property_value = new_transient_for;
00552
00553
00554 if( isSplash() && new_transient_for == None )
00555 new_transient_for = workspace()->rootWin();
00556 if( new_transient_for == None )
00557 if( defined )
00558 new_property_value = new_transient_for = workspace()->rootWin();
00559 else
00560 return None;
00561 if( new_transient_for == window())
00562 {
00563 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00564 new_property_value = new_transient_for = workspace()->rootWin();
00565 }
00566
00567
00568
00569 WId before_search = new_transient_for;
00570 while( new_transient_for != None
00571 && new_transient_for != workspace()->rootWin()
00572 && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00573 {
00574 Window root_return, parent_return;
00575 Window* wins = NULL;
00576 unsigned int nwins;
00577 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00578 if ( wins )
00579 XFree((void *) wins);
00580 if ( r == 0)
00581 break;
00582 new_transient_for = parent_return;
00583 }
00584 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00585 {
00586 if( new_transient_for != before_search )
00587 {
00588 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00589 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00590 new_property_value = new_transient_for;
00591 }
00592 }
00593 else
00594 new_transient_for = before_search;
00595
00596
00597
00598 int count = 20;
00599 Window loop_pos = new_transient_for;
00600 while( loop_pos != None && loop_pos != workspace()->rootWin())
00601 {
00602 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00603 if( pos == NULL )
00604 break;
00605 loop_pos = pos->transient_for_id;
00606 if( --count == 0 )
00607 {
00608 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00609 new_transient_for = workspace()->rootWin();
00610 }
00611 }
00612 if( new_transient_for != workspace()->rootWin()
00613 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00614 {
00615 new_transient_for = workspace()->rootWin();
00616 }
00617 if( new_property_value != original_transient_for_id )
00618 XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00619 return new_transient_for;
00620 }
00621
00622 void Client::addTransient( Client* cl )
00623 {
00624 assert( !transients_list.contains( cl ));
00625
00626 assert( cl != this );
00627 transients_list.append( cl );
00628 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00629 check_active_modal = true;
00630
00631
00632
00633
00634
00635
00636 }
00637
00638 void Client::removeTransient( Client* cl )
00639 {
00640
00641
00642 transients_list.remove( cl );
00643
00644
00645 if( cl->transientFor() == this )
00646 {
00647 cl->transient_for_id = None;
00648 cl->transient_for = NULL;
00649
00650 cl->setTransient( None );
00651 }
00652 }
00653
00654
00655 void Client::checkTransient( Window w )
00656 {
00657 if( original_transient_for_id != w )
00658 return;
00659 w = verifyTransientFor( w, true );
00660 setTransient( w );
00661 }
00662
00663
00664
00665 bool Client::hasTransient( const Client* cl, bool indirect ) const
00666 {
00667
00668 ConstClientList set;
00669 return hasTransientInternal( cl, indirect, set );
00670 }
00671
00672 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00673 {
00674 if( set.contains( this ))
00675 return false;
00676 set.append( this );
00677 if( cl->transientFor() != NULL )
00678 {
00679 if( cl->transientFor() == this )
00680 return true;
00681 if( !indirect )
00682 return false;
00683 return hasTransientInternal( cl->transientFor(), indirect, set );
00684 }
00685 if( !cl->isTransient())
00686 return false;
00687 if( group() != cl->group())
00688 return false;
00689
00690 if( transients().contains( const_cast< Client* >( cl )))
00691 return true;
00692 if( !indirect )
00693 return false;
00694 for( ClientList::ConstIterator it = transients().begin();
00695 it != transients().end();
00696 ++it )
00697 if( (*it)->hasTransientInternal( cl, indirect, set ))
00698 return true;
00699 return false;
00700 }
00701
00702 ClientList Client::mainClients() const
00703 {
00704 if( !isTransient())
00705 return ClientList();
00706 if( transientFor() != NULL )
00707 return ClientList() << const_cast< Client* >( transientFor());
00708 ClientList result;
00709 for( ClientList::ConstIterator it = group()->members().begin();
00710 it != group()->members().end();
00711 ++it )
00712 if((*it)->hasTransient( this, false ))
00713 result.append( *it );
00714 return result;
00715 }
00716
00717 Client* Client::findModal()
00718 {
00719 for( ClientList::ConstIterator it = transients().begin();
00720 it != transients().end();
00721 ++it )
00722 if( Client* ret = (*it)->findModal())
00723 return ret;
00724 if( isModal())
00725 return this;
00726 return NULL;
00727 }
00728
00729
00730
00731
00732 void Client::checkGroup( Group* set_group, bool force )
00733 {
00734 Group* old_group = in_group;
00735 if( set_group != NULL )
00736 {
00737 if( set_group != in_group )
00738 {
00739 if( in_group != NULL )
00740 in_group->removeMember( this );
00741 in_group = set_group;
00742 in_group->addMember( this );
00743 }
00744 }
00745 else if( window_group != None )
00746 {
00747 Group* new_group = workspace()->findGroup( window_group );
00748 if( transientFor() != NULL && transientFor()->group() != new_group )
00749 {
00750
00751 new_group = transientFor()->group();
00752 }
00753 if( new_group == NULL )
00754 new_group = new Group( window_group, workspace());
00755 if( new_group != in_group )
00756 {
00757 if( in_group != NULL )
00758 in_group->removeMember( this );
00759 in_group = new_group;
00760 in_group->addMember( this );
00761 }
00762 }
00763 else
00764 {
00765 if( transientFor() != NULL )
00766 {
00767
00768 Group* new_group = transientFor()->group();
00769 if( new_group != in_group )
00770 {
00771 if( in_group != NULL )
00772 in_group->removeMember( this );
00773 in_group = transientFor()->group();
00774 in_group->addMember( this );
00775 }
00776 }
00777 else if( groupTransient())
00778 {
00779
00780 Group* new_group = workspace()->findClientLeaderGroup( this );
00781 if( new_group == NULL )
00782 new_group = new Group( None, workspace());
00783 if( new_group != in_group )
00784 {
00785 if( in_group != NULL )
00786 in_group->removeMember( this );
00787 in_group = new_group;
00788 in_group->addMember( this );
00789 }
00790 }
00791 else
00792 {
00793 if( in_group != NULL && in_group->leader() != window())
00794 {
00795 in_group->removeMember( this );
00796 in_group = NULL;
00797 }
00798 if( in_group == NULL )
00799 {
00800 in_group = new Group( None, workspace());
00801 in_group->addMember( this );
00802 }
00803 }
00804 }
00805 if( in_group != old_group || force )
00806 {
00807 for( ClientList::Iterator it = transients_list.begin();
00808 it != transients_list.end();
00809 )
00810 {
00811 if( (*it)->groupTransient() && (*it)->group() != group())
00812 it = transients_list.remove( it );
00813 else
00814 ++it;
00815 }
00816 if( groupTransient())
00817 {
00818 for( ClientList::ConstIterator it = group()->members().begin();
00819 it != group()->members().end();
00820 ++it )
00821 {
00822 if( *it == this )
00823 break;
00824 (*it)->addTransient( this );
00825 }
00826 }
00827 #if 0 // TODO
00828 if( groupTransient())
00829 {
00830 if( workspace()->findGroup( old_group ))
00831 {
00832 for( ClientList::ConstIterator it = old_group->members().begin();
00833 it != old_group->members().end();
00834 ++it )
00835 (*it)->removeTransient( this );
00836 }
00837
00838
00839
00840 for( ClientList::ConstIterator it = group()->members().begin();
00841 it != group()->members().end();
00842 ++it )
00843 (*it)->addTransient( this );
00844 }
00845 #endif
00846
00847
00848 for( ClientList::ConstIterator it = group()->members().begin();
00849 it != group()->members().end();
00850 ++it )
00851 {
00852 if( !(*it)->isSplash())
00853 continue;
00854 if( !(*it)->groupTransient())
00855 continue;
00856 if( *it == this || hasTransient( *it, true ))
00857 continue;
00858 addTransient( *it );
00859 }
00860 }
00861 checkGroupTransients();
00862
00863
00864
00865 Client* check_modal = workspace()->mostRecentlyActivatedClient();
00866 if( check_modal != NULL && check_modal->check_active_modal )
00867 {
00868 Client* new_modal = check_modal->findModal();
00869 if( new_modal != NULL && new_modal != check_modal )
00870 workspace()->activateClient( new_modal );
00871 check_modal->check_active_modal = false;
00872 }
00873 workspace()->updateClientLayer( this );
00874 }
00875
00876
00877 }