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 }