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 for( ClientList::ConstIterator it = clients.begin();
00152 it != clients.end();
00153 ++it )
00154 {
00155 if( *it == c )
00156 continue;
00157 if( (*it)->wmClientLeader() == c->wmClientLeader())
00158 return (*it)->group();
00159 }
00160 return NULL;
00161 }
00162
00163 void Workspace::updateMinimizedOfTransients( Client* c )
00164 {
00165
00166 if ( c->isMinimized() || c->isShade() )
00167 {
00168 for( ClientList::ConstIterator it = c->transients().begin();
00169 it != c->transients().end();
00170 ++it )
00171 {
00172 if( !(*it)->isMinimized()
00173 && !(*it)->isShade()
00174 && !(*it)->isTopMenu() )
00175 {
00176 (*it)->minimize();
00177 updateMinimizedOfTransients( (*it) );
00178 }
00179 }
00180 }
00181 else
00182 {
00183 for( ClientList::ConstIterator it = c->transients().begin();
00184 it != c->transients().end();
00185 ++it )
00186 {
00187 if( (*it)->isMinimized()
00188 && !(*it)->isTopMenu())
00189 {
00190 (*it)->unminimize();
00191 updateMinimizedOfTransients( (*it) );
00192 }
00193 }
00194 }
00195 }
00196
00197
00201 void Workspace::updateOnAllDesktopsOfTransients( Client* c )
00202 {
00203 for( ClientList::ConstIterator it = c->transients().begin();
00204 it != c->transients().end();
00205 ++it)
00206 {
00207 if( (*it)->isOnAllDesktops() != c->isOnAllDesktops())
00208 (*it)->setOnAllDesktops( c->isOnAllDesktops());
00209 }
00210 }
00211
00212
00213 void Workspace::checkTransients( Window w )
00214 {
00215 for( ClientList::ConstIterator it = clients.begin();
00216 it != clients.end();
00217 ++it )
00218 (*it)->checkTransient( w );
00219 }
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 bool Client::resourceMatch( const Client* c1, const Client* c2 )
00230 {
00231
00232 if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00233 return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00234
00235 if( c1->resourceName() == "mozilla" )
00236 return c2->resourceName() == "mozilla";
00237 return c1->resourceClass() == c2->resourceClass();
00238 }
00239
00240 bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack )
00241 {
00242 bool same_app = false;
00243 if( c1 == c2 )
00244 same_app = true;
00245 else if( c1->isTransient() && c2->hasTransient( c1, true ))
00246 same_app = true;
00247 else if( c2->isTransient() && c1->hasTransient( c2, true ))
00248 same_app = true;
00249 else if( c1->pid() != c2->pid()
00250 || c1->wmClientMachine() != c2->wmClientMachine())
00251 ;
00252 else if( c1->wmClientLeader() != c2->wmClientLeader()
00253 && c1->wmClientLeader() != c1->window()
00254 && c2->wmClientLeader() != c2->window())
00255 ;
00256 else if( !resourceMatch( c1, c2 ))
00257 ;
00258 else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00259 ;
00260 else if( c1->wmClientLeader() == c2->wmClientLeader()
00261 && c1->wmClientLeader() != c1->window()
00262 && c2->wmClientLeader() != c2->window())
00263 same_app = true;
00264 else if( c1->group() == c2->group())
00265 same_app = true;
00266 else if( c1->pid() == 0 || c2->pid() == 0 )
00267 ;
00268
00269 else
00270 same_app = true;
00271 return same_app;
00272 }
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack )
00285 {
00286 if( c1->isTransient())
00287 {
00288 while( c1->transientFor() != NULL )
00289 c1 = c1->transientFor();
00290 if( c1->groupTransient())
00291 return c1->group() == c2->group();
00292 #if 0
00293
00294
00295
00296 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00297 #endif
00298 }
00299 if( c2->isTransient())
00300 {
00301 while( c2->transientFor() != NULL )
00302 c2 = c2->transientFor();
00303 if( c2->groupTransient())
00304 return c1->group() == c2->group();
00305 #if 0
00306 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00307 #endif
00308 }
00309 int pos1 = c1->windowRole().find( '#' );
00310 int pos2 = c2->windowRole().find( '#' );
00311 if(( pos1 >= 0 && pos2 >= 0 )
00312 ||
00313
00314
00315 c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" )
00316 {
00317 if( !active_hack )
00318 return c1 == c2;
00319 if( !c1->isActive() && !c2->isActive())
00320 return c1 == c2;
00321 else
00322 return true;
00323 }
00324 return true;
00325 }
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
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 void Client::readTransient()
00374 {
00375 Window new_transient_for_id;
00376 if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id ))
00377 {
00378 original_transient_for_id = new_transient_for_id;
00379 new_transient_for_id = verifyTransientFor( new_transient_for_id, true );
00380 }
00381 else
00382 {
00383 original_transient_for_id = None;
00384 new_transient_for_id = verifyTransientFor( None, false );
00385 }
00386 setTransient( new_transient_for_id );
00387 }
00388
00389 void Client::setTransient( Window new_transient_for_id )
00390 {
00391 if( new_transient_for_id != transient_for_id )
00392 {
00393 removeFromMainClients();
00394 transient_for = NULL;
00395 transient_for_id = new_transient_for_id;
00396 if( transient_for_id != None && !groupTransient())
00397 {
00398 transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id ));
00399 assert( transient_for != NULL );
00400 transient_for->addTransient( this );
00401 }
00402 checkGroup();
00403 if( groupTransient())
00404 {
00405 for( ClientList::ConstIterator it = group()->members().begin();
00406 it != group()->members().end();
00407 ++it )
00408 {
00409 if( *it == this )
00410 break;
00411 (*it)->addTransient( this );
00412 }
00413 }
00414 checkGroupTransients();
00415 workspace()->updateClientLayer( this );
00416 }
00417 }
00418
00419 void Client::removeFromMainClients()
00420 {
00421 if( transientFor() != NULL )
00422 transientFor()->removeTransient( this );
00423 if( groupTransient())
00424 {
00425 for( ClientList::ConstIterator it = group()->members().begin();
00426 it != group()->members().end();
00427 ++it )
00428 (*it)->removeTransient( this );
00429 }
00430 }
00431
00432
00433
00434
00435
00436 void Client::cleanGrouping()
00437 {
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 removeFromMainClients();
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460 for( ClientList::ConstIterator it = transients_list.begin();
00461 it != transients_list.end();
00462 )
00463 {
00464 if( (*it)->transientFor() == this )
00465 {
00466 ClientList::ConstIterator it2 = it++;
00467 removeTransient( *it2 );
00468 }
00469 else
00470 ++it;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487 ClientList group_members = group()->members();
00488 group()->removeMember( this );
00489 in_group = NULL;
00490 for( ClientList::ConstIterator it = group_members.begin();
00491 it != group_members.end();
00492 ++it )
00493 (*it)->removeTransient( this );
00494
00495
00496
00497
00498
00499 }
00500
00501
00502
00503
00504
00505 void Client::checkGroupTransients()
00506 {
00507 for( ClientList::ConstIterator it1 = group()->members().begin();
00508 it1 != group()->members().end();
00509 ++it1 )
00510 {
00511 if( !(*it1)->groupTransient())
00512 continue;
00513 for( ClientList::ConstIterator it2 = group()->members().begin();
00514 it2 != group()->members().end();
00515 ++it2 )
00516 {
00517 if( *it1 == *it2 )
00518 continue;
00519 for( Client* cl = (*it2)->transientFor();
00520 cl != NULL;
00521 cl = cl->transientFor())
00522 {
00523 if( cl == *it1 )
00524 {
00525 (*it2)->transients_list.remove( *it1 );
00526 continue;
00527 }
00528 }
00529
00530
00531
00532
00533 if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00534 (*it2)->transients_list.remove( *it1 );
00535 }
00536 }
00537 }
00538
00542 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00543 {
00544 Window new_property_value = new_transient_for;
00545
00546
00547 if( isSplash() && new_transient_for == None )
00548 new_transient_for = workspace()->rootWin();
00549 if( new_transient_for == None )
00550 if( defined )
00551 new_property_value = new_transient_for = workspace()->rootWin();
00552 else
00553 return None;
00554 if( new_transient_for == window())
00555 {
00556 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00557 new_property_value = new_transient_for = workspace()->rootWin();
00558 }
00559
00560
00561
00562 WId before_search = new_transient_for;
00563 while( new_transient_for != None
00564 && new_transient_for != workspace()->rootWin()
00565 && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00566 {
00567 Window root_return, parent_return;
00568 Window* wins = NULL;
00569 unsigned int nwins;
00570 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00571 if ( wins )
00572 XFree((void *) wins);
00573 if ( r == 0)
00574 break;
00575 new_transient_for = parent_return;
00576 }
00577 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00578 {
00579 if( new_transient_for != before_search )
00580 {
00581 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00582 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00583 new_property_value = new_transient_for;
00584 }
00585 }
00586 else
00587 new_transient_for = before_search;
00588
00589
00590
00591 int count = 20;
00592 Window loop_pos = new_transient_for;
00593 while( loop_pos != None && loop_pos != workspace()->rootWin())
00594 {
00595 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00596 if( pos == NULL )
00597 break;
00598 loop_pos = pos->transient_for_id;
00599 if( --count == 0 )
00600 {
00601 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00602 new_transient_for = workspace()->rootWin();
00603 }
00604 }
00605 if( new_transient_for != workspace()->rootWin()
00606 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00607 {
00608 new_transient_for = workspace()->rootWin();
00609 }
00610 if( new_property_value != original_transient_for_id )
00611 XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00612 return new_transient_for;
00613 }
00614
00615 void Client::addTransient( Client* cl )
00616 {
00617 assert( !transients_list.contains( cl ));
00618
00619 assert( cl != this );
00620 transients_list.append( cl );
00621
00622
00623
00624
00625
00626
00627 }
00628
00629 void Client::removeTransient( Client* cl )
00630 {
00631
00632
00633 transients_list.remove( cl );
00634
00635
00636 if( cl->transientFor() == this )
00637 {
00638 cl->transient_for_id = None;
00639 cl->transient_for = NULL;
00640
00641 cl->setTransient( None );
00642 }
00643 }
00644
00645
00646 void Client::checkTransient( Window w )
00647 {
00648 if( original_transient_for_id != w )
00649 return;
00650 w = verifyTransientFor( w, true );
00651 setTransient( w );
00652 }
00653
00654
00655
00656 bool Client::hasTransient( const Client* cl, bool indirect ) const
00657 {
00658
00659 ConstClientList set;
00660 return hasTransientInternal( cl, indirect, set );
00661 }
00662
00663 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00664 {
00665 if( set.contains( this ))
00666 return false;
00667 set.append( this );
00668 if( cl->transientFor() != NULL )
00669 {
00670 if( cl->transientFor() == this )
00671 return true;
00672 if( !indirect )
00673 return false;
00674 return hasTransientInternal( cl->transientFor(), indirect, set );
00675 }
00676 if( !cl->isTransient())
00677 return false;
00678 if( group() != cl->group())
00679 return false;
00680
00681 if( transients().contains( const_cast< Client* >( cl )))
00682 return true;
00683 if( !indirect )
00684 return false;
00685 for( ClientList::ConstIterator it = transients().begin();
00686 it != transients().end();
00687 ++it )
00688 if( (*it)->hasTransientInternal( cl, indirect, set ))
00689 return true;
00690 return false;
00691 }
00692
00693 ClientList Client::mainClients() const
00694 {
00695 if( !isTransient())
00696 return ClientList();
00697 if( transientFor() != NULL )
00698 return ClientList() << const_cast< Client* >( transientFor());
00699 ClientList result;
00700 for( ClientList::ConstIterator it = group()->members().begin();
00701 it != group()->members().end();
00702 ++it )
00703 if((*it)->hasTransient( this, false ))
00704 result.append( *it );
00705 return result;
00706 }
00707
00708 Client* Client::findModal()
00709 {
00710 for( ClientList::ConstIterator it = transients().begin();
00711 it != transients().end();
00712 ++it )
00713 if( Client* ret = (*it)->findModal())
00714 return ret;
00715 if( isModal())
00716 return this;
00717 return NULL;
00718 }
00719
00720
00721
00722 void Client::checkGroup()
00723 {
00724 Group* old_group = in_group;
00725 if( window_group != None )
00726 {
00727 Group* new_group = workspace()->findGroup( window_group );
00728 if( transientFor() != NULL && transientFor()->group() != new_group )
00729 {
00730
00731 new_group = transientFor()->group();
00732 }
00733 if( new_group == NULL )
00734 new_group = new Group( window_group, workspace());
00735 if( new_group != in_group )
00736 {
00737 if( in_group != NULL )
00738 in_group->removeMember( this );
00739 in_group = new_group;
00740 in_group->addMember( this );
00741 }
00742 }
00743 else
00744 {
00745 if( transientFor() != NULL )
00746 {
00747
00748 Group* new_group = transientFor()->group();
00749 if( new_group != in_group )
00750 {
00751 if( in_group != NULL )
00752 in_group->removeMember( this );
00753 in_group = transientFor()->group();
00754 in_group->addMember( this );
00755 }
00756 }
00757 else if( groupTransient())
00758 {
00759
00760 Group* new_group = workspace()->findClientLeaderGroup( this );
00761 if( new_group == NULL )
00762 new_group = new Group( None, workspace());
00763 if( new_group != in_group )
00764 {
00765 if( in_group != NULL )
00766 in_group->removeMember( this );
00767 in_group = new_group;
00768 in_group->addMember( this );
00769 }
00770 }
00771 else
00772 {
00773 if( in_group != NULL && in_group->leader() != window())
00774 {
00775 in_group->removeMember( this );
00776 in_group = NULL;
00777 }
00778 if( in_group == NULL )
00779 {
00780 in_group = new Group( None, workspace());
00781 in_group->addMember( this );
00782 }
00783 }
00784 }
00785 if( in_group != old_group )
00786 {
00787 for( ClientList::Iterator it = transients_list.begin();
00788 it != transients_list.end();
00789 )
00790 {
00791 if( (*it)->groupTransient() && (*it)->group() != group())
00792 it = transients_list.remove( it );
00793 else
00794 ++it;
00795 }
00796 #if 0 // TODO
00797 if( groupTransient())
00798 {
00799 if( workspace()->findGroup( old_group ))
00800 {
00801 for( ClientList::ConstIterator it = old_group->members().begin();
00802 it != old_group->members().end();
00803 ++it )
00804 (*it)->removeTransient( this );
00805 }
00806
00807
00808
00809 for( ClientList::ConstIterator it = group()->members().begin();
00810 it != group()->members().end();
00811 ++it )
00812 (*it)->addTransient( this );
00813 }
00814 #endif
00815 #if 0 // this would make group transients transient for window that were mapped later
00816 for( ClientList::ConstIterator it = group()->members().begin();
00817 it != group()->members().end();
00818 ++it )
00819 {
00820 if( !(*it)->groupTransient())
00821 continue;
00822 if( *it == this )
00823 continue;
00824 addTransient( *it );
00825 }
00826 checkGroupTransients();
00827 #endif
00828 }
00829 }
00830
00831
00832 }