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 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00622 workspace()->activateClient( findModal());
00623
00624
00625
00626
00627
00628
00629 }
00630
00631 void Client::removeTransient( Client* cl )
00632 {
00633
00634
00635 transients_list.remove( cl );
00636
00637
00638 if( cl->transientFor() == this )
00639 {
00640 cl->transient_for_id = None;
00641 cl->transient_for = NULL;
00642
00643 cl->setTransient( None );
00644 }
00645 }
00646
00647
00648 void Client::checkTransient( Window w )
00649 {
00650 if( original_transient_for_id != w )
00651 return;
00652 w = verifyTransientFor( w, true );
00653 setTransient( w );
00654 }
00655
00656
00657
00658 bool Client::hasTransient( const Client* cl, bool indirect ) const
00659 {
00660
00661 ConstClientList set;
00662 return hasTransientInternal( cl, indirect, set );
00663 }
00664
00665 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00666 {
00667 if( set.contains( this ))
00668 return false;
00669 set.append( this );
00670 if( cl->transientFor() != NULL )
00671 {
00672 if( cl->transientFor() == this )
00673 return true;
00674 if( !indirect )
00675 return false;
00676 return hasTransientInternal( cl->transientFor(), indirect, set );
00677 }
00678 if( !cl->isTransient())
00679 return false;
00680 if( group() != cl->group())
00681 return false;
00682
00683 if( transients().contains( const_cast< Client* >( cl )))
00684 return true;
00685 if( !indirect )
00686 return false;
00687 for( ClientList::ConstIterator it = transients().begin();
00688 it != transients().end();
00689 ++it )
00690 if( (*it)->hasTransientInternal( cl, indirect, set ))
00691 return true;
00692 return false;
00693 }
00694
00695 ClientList Client::mainClients() const
00696 {
00697 if( !isTransient())
00698 return ClientList();
00699 if( transientFor() != NULL )
00700 return ClientList() << const_cast< Client* >( transientFor());
00701 ClientList result;
00702 for( ClientList::ConstIterator it = group()->members().begin();
00703 it != group()->members().end();
00704 ++it )
00705 if((*it)->hasTransient( this, false ))
00706 result.append( *it );
00707 return result;
00708 }
00709
00710 Client* Client::findModal()
00711 {
00712 for( ClientList::ConstIterator it = transients().begin();
00713 it != transients().end();
00714 ++it )
00715 if( Client* ret = (*it)->findModal())
00716 return ret;
00717 if( isModal())
00718 return this;
00719 return NULL;
00720 }
00721
00722
00723
00724 void Client::checkGroup()
00725 {
00726 Group* old_group = in_group;
00727 if( window_group != None )
00728 {
00729 Group* new_group = workspace()->findGroup( window_group );
00730 if( transientFor() != NULL && transientFor()->group() != new_group )
00731 {
00732
00733 new_group = transientFor()->group();
00734 }
00735 if( new_group == NULL )
00736 new_group = new Group( window_group, workspace());
00737 if( new_group != in_group )
00738 {
00739 if( in_group != NULL )
00740 in_group->removeMember( this );
00741 in_group = new_group;
00742 in_group->addMember( this );
00743 }
00744 }
00745 else
00746 {
00747 if( transientFor() != NULL )
00748 {
00749
00750 Group* new_group = transientFor()->group();
00751 if( new_group != in_group )
00752 {
00753 if( in_group != NULL )
00754 in_group->removeMember( this );
00755 in_group = transientFor()->group();
00756 in_group->addMember( this );
00757 }
00758 }
00759 else if( groupTransient())
00760 {
00761
00762 Group* new_group = workspace()->findClientLeaderGroup( this );
00763 if( new_group == NULL )
00764 new_group = new Group( None, workspace());
00765 if( new_group != in_group )
00766 {
00767 if( in_group != NULL )
00768 in_group->removeMember( this );
00769 in_group = new_group;
00770 in_group->addMember( this );
00771 }
00772 }
00773 else
00774 {
00775 if( in_group != NULL && in_group->leader() != window())
00776 {
00777 in_group->removeMember( this );
00778 in_group = NULL;
00779 }
00780 if( in_group == NULL )
00781 {
00782 in_group = new Group( None, workspace());
00783 in_group->addMember( this );
00784 }
00785 }
00786 }
00787 if( in_group != old_group )
00788 {
00789 for( ClientList::Iterator it = transients_list.begin();
00790 it != transients_list.end();
00791 )
00792 {
00793 if( (*it)->groupTransient() && (*it)->group() != group())
00794 it = transients_list.remove( it );
00795 else
00796 ++it;
00797 }
00798 #if 0 // TODO
00799 if( groupTransient())
00800 {
00801 if( workspace()->findGroup( old_group ))
00802 {
00803 for( ClientList::ConstIterator it = old_group->members().begin();
00804 it != old_group->members().end();
00805 ++it )
00806 (*it)->removeTransient( this );
00807 }
00808
00809
00810
00811 for( ClientList::ConstIterator it = group()->members().begin();
00812 it != group()->members().end();
00813 ++it )
00814 (*it)->addTransient( this );
00815 }
00816 #endif
00817 #if 0 // this would make group transients transient for window that were mapped later
00818 for( ClientList::ConstIterator it = group()->members().begin();
00819 it != group()->members().end();
00820 ++it )
00821 {
00822 if( !(*it)->groupTransient())
00823 continue;
00824 if( *it == this )
00825 continue;
00826 addTransient( *it );
00827 }
00828 checkGroupTransients();
00829 #endif
00830 }
00831 }
00832
00833
00834 }