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
00551 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00552 {
00553 Window new_property_value = new_transient_for;
00554
00555
00556 if( isSplash() && new_transient_for == None )
00557 new_transient_for = workspace()->rootWin();
00558 if( new_transient_for == None )
00559 if( defined )
00560 new_property_value = new_transient_for = workspace()->rootWin();
00561 else
00562 return None;
00563 if( new_transient_for == window())
00564 {
00565 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00566 new_property_value = new_transient_for = workspace()->rootWin();
00567 }
00568
00569
00570
00571 WId before_search = new_transient_for;
00572 while( new_transient_for != None
00573 && new_transient_for != workspace()->rootWin()
00574 && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00575 {
00576 Window root_return, parent_return;
00577 Window* wins = NULL;
00578 unsigned int nwins;
00579 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins);
00580 if ( wins )
00581 XFree((void *) wins);
00582 if ( r == 0)
00583 break;
00584 new_transient_for = parent_return;
00585 }
00586 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00587 {
00588 if( new_transient_for != before_search )
00589 {
00590 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00591 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00592 new_property_value = new_transient_for;
00593 }
00594 }
00595 else
00596 new_transient_for = before_search;
00597
00598
00599
00600 int count = 20;
00601 Window loop_pos = new_transient_for;
00602 while( loop_pos != None && loop_pos != workspace()->rootWin())
00603 {
00604 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00605 if( pos == NULL )
00606 break;
00607 loop_pos = pos->transient_for_id;
00608 if( --count == 0 )
00609 {
00610 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00611 new_transient_for = workspace()->rootWin();
00612 }
00613 }
00614 if( new_transient_for != workspace()->rootWin()
00615 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00616 {
00617 new_transient_for = workspace()->rootWin();
00618 }
00619 if( new_property_value != original_transient_for_id )
00620 XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00621 return new_transient_for;
00622 }
00623
00624 void Client::addTransient( Client* cl )
00625 {
00626 assert( !transients_list.contains( cl ));
00627
00628 assert( cl != this );
00629 transients_list.append( cl );
00630 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00631 check_active_modal = true;
00632
00633
00634
00635
00636
00637
00638 }
00639
00640 void Client::removeTransient( Client* cl )
00641 {
00642
00643
00644 transients_list.remove( cl );
00645
00646
00647 if( cl->transientFor() == this )
00648 {
00649 cl->transient_for_id = None;
00650 cl->transient_for = NULL;
00651
00652 cl->setTransient( None );
00653 }
00654 }
00655
00656
00657 void Client::checkTransient( Window w )
00658 {
00659 if( original_transient_for_id != w )
00660 return;
00661 w = verifyTransientFor( w, true );
00662 setTransient( w );
00663 }
00664
00665
00666
00667 bool Client::hasTransient( const Client* cl, bool indirect ) const
00668 {
00669
00670 ConstClientList set;
00671 return hasTransientInternal( cl, indirect, set );
00672 }
00673
00674 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00675 {
00676 if( set.contains( this ))
00677 return false;
00678 set.append( this );
00679 if( cl->transientFor() != NULL )
00680 {
00681 if( cl->transientFor() == this )
00682 return true;
00683 if( !indirect )
00684 return false;
00685 return hasTransientInternal( cl->transientFor(), indirect, set );
00686 }
00687 if( !cl->isTransient())
00688 return false;
00689 if( group() != cl->group())
00690 return false;
00691
00692 if( transients().contains( const_cast< Client* >( cl )))
00693 return true;
00694 if( !indirect )
00695 return false;
00696 for( ClientList::ConstIterator it = transients().begin();
00697 it != transients().end();
00698 ++it )
00699 if( (*it)->hasTransientInternal( cl, indirect, set ))
00700 return true;
00701 return false;
00702 }
00703
00704 ClientList Client::mainClients() const
00705 {
00706 if( !isTransient())
00707 return ClientList();
00708 if( transientFor() != NULL )
00709 return ClientList() << const_cast< Client* >( transientFor());
00710 ClientList result;
00711 for( ClientList::ConstIterator it = group()->members().begin();
00712 it != group()->members().end();
00713 ++it )
00714 if((*it)->hasTransient( this, false ))
00715 result.append( *it );
00716 return result;
00717 }
00718
00719 Client* Client::findModal()
00720 {
00721 for( ClientList::ConstIterator it = transients().begin();
00722 it != transients().end();
00723 ++it )
00724 if( Client* ret = (*it)->findModal())
00725 return ret;
00726 if( isModal())
00727 return this;
00728 return NULL;
00729 }
00730
00731
00732
00733
00734 void Client::checkGroup( Group* set_group, bool force )
00735 {
00736 Group* old_group = in_group;
00737 if( set_group != NULL )
00738 {
00739 if( set_group != in_group )
00740 {
00741 if( in_group != NULL )
00742 in_group->removeMember( this );
00743 in_group = set_group;
00744 in_group->addMember( this );
00745 }
00746 }
00747 else if( window_group != None )
00748 {
00749 Group* new_group = workspace()->findGroup( window_group );
00750 if( transientFor() != NULL && transientFor()->group() != new_group )
00751 {
00752
00753 new_group = transientFor()->group();
00754 }
00755 if( new_group == NULL )
00756 new_group = new Group( window_group, workspace());
00757 if( new_group != in_group )
00758 {
00759 if( in_group != NULL )
00760 in_group->removeMember( this );
00761 in_group = new_group;
00762 in_group->addMember( this );
00763 }
00764 }
00765 else
00766 {
00767 if( transientFor() != NULL )
00768 {
00769
00770 Group* new_group = transientFor()->group();
00771 if( new_group != in_group )
00772 {
00773 if( in_group != NULL )
00774 in_group->removeMember( this );
00775 in_group = transientFor()->group();
00776 in_group->addMember( this );
00777 }
00778 }
00779 else if( groupTransient())
00780 {
00781
00782 Group* new_group = workspace()->findClientLeaderGroup( this );
00783 if( new_group == NULL )
00784 new_group = new Group( None, workspace());
00785 if( new_group != in_group )
00786 {
00787 if( in_group != NULL )
00788 in_group->removeMember( this );
00789 in_group = new_group;
00790 in_group->addMember( this );
00791 }
00792 }
00793 else
00794 {
00795 if( in_group != NULL && in_group->leader() != window())
00796 {
00797 in_group->removeMember( this );
00798 in_group = NULL;
00799 }
00800 if( in_group == NULL )
00801 {
00802 in_group = new Group( None, workspace());
00803 in_group->addMember( this );
00804 }
00805 }
00806 }
00807 if( in_group != old_group || force )
00808 {
00809 for( ClientList::Iterator it = transients_list.begin();
00810 it != transients_list.end();
00811 )
00812 {
00813 if( (*it)->groupTransient() && (*it)->group() != group())
00814 it = transients_list.remove( it );
00815 else
00816 ++it;
00817 }
00818 if( groupTransient())
00819 {
00820 for( ClientList::ConstIterator it = group()->members().begin();
00821 it != group()->members().end();
00822 ++it )
00823 {
00824 if( *it == this )
00825 break;
00826 (*it)->addTransient( this );
00827 }
00828 }
00829 #if 0 // TODO
00830 if( groupTransient())
00831 {
00832 if( workspace()->findGroup( old_group ))
00833 {
00834 for( ClientList::ConstIterator it = old_group->members().begin();
00835 it != old_group->members().end();
00836 ++it )
00837 (*it)->removeTransient( this );
00838 }
00839
00840
00841
00842 for( ClientList::ConstIterator it = group()->members().begin();
00843 it != group()->members().end();
00844 ++it )
00845 (*it)->addTransient( this );
00846 }
00847 #endif
00848
00849
00850 for( ClientList::ConstIterator it = group()->members().begin();
00851 it != group()->members().end();
00852 ++it )
00853 {
00854 if( !(*it)->isSplash())
00855 continue;
00856 if( !(*it)->groupTransient())
00857 continue;
00858 if( *it == this || hasTransient( *it, true ))
00859 continue;
00860 addTransient( *it );
00861 }
00862 }
00863 checkGroupTransients();
00864 checkActiveModal();
00865 workspace()->updateClientLayer( this );
00866 }
00867
00868 bool Client::check_active_modal = false;
00869
00870 void Client::checkActiveModal()
00871 {
00872
00873
00874
00875 Client* check_modal = workspace()->mostRecentlyActivatedClient();
00876 if( check_modal != NULL && check_modal->check_active_modal )
00877 {
00878 Client* new_modal = check_modal->findModal();
00879 if( new_modal != NULL && new_modal != check_modal )
00880 {
00881 if( !new_modal->isManaged())
00882 return;
00883 workspace()->activateClient( new_modal );
00884 }
00885 check_modal->check_active_modal = false;
00886 }
00887 }
00888
00889 }