kwin Library API Documentation

group.cpp

00001 /*****************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 /*
00013 
00014  This file contains things relevant to window grouping.
00015 
00016 */
00017 
00018 //#define QT_CLEAN_NAMESPACE
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  TODO
00031  Rename as many uses of 'transient' as possible (hasTransient->hasSubwindow,etc.),
00032  or I'll get it backwards in half of the cases again.
00033 */
00034 
00035 namespace KWinInternal
00036 {
00037 
00038 //********************************************
00039 // Group
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 //    kdDebug() << "GROUPADD:" << this << ":" << member_P << endl;
00094 //    kdDebug() << kdBacktrace() << endl;
00095     }
00096 
00097 void Group::removeMember( Client* member_P )
00098     {
00099 //    kdDebug() << "GROUPREMOVE:" << this << ":" << member_P << endl;
00100 //    kdDebug() << kdBacktrace() << endl;
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     // TODO - also needs adding the flag to NETWinInfo
00130     }
00131 
00132 //***************************************
00133 // Workspace
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 // Client is group transient, but has no group set. Try to find
00148 // group with windows with the same client leader.
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     // if mainwindow is minimized or shaded, minimize transients too
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() ) // topmenus are not minimized, they're hidden
00175                 {
00176                 (*it)->minimize();
00177                 updateMinimizedOfTransients( (*it) );
00178                 }
00179             }
00180         }
00181     else
00182         { // else unmiminize the transients
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 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
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 // Client
00225 //****************************************
00226 
00227 // hacks for broken apps here
00228 // all resource classes are forced to be lowercase
00229 bool Client::resourceMatch( const Client* c1, const Client* c2 )
00230     {
00231     // xv has "xv" as resource name, and different strings starting with "XV" as resource class
00232     if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00233          return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00234     // Mozilla has "Mozilla" as resource name, and different strings as resource class
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; // c1 has c2 as mainwindow
00247     else if( c2->isTransient() && c1->hasTransient( c2, true ))
00248         same_app = true; // c2 has c1 as mainwindow
00249     else if( c1->pid() != c2->pid()
00250         || c1->wmClientMachine() != c2->wmClientMachine())
00251         ; // different processes
00252     else if( c1->wmClientLeader() != c2->wmClientLeader()
00253         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00254         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00255         ; // different client leader
00256     else if( !resourceMatch( c1, c2 ))
00257         ; // different apps
00258     else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00259         ; // "different" apps
00260     else if( c1->wmClientLeader() == c2->wmClientLeader()
00261         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00262         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00263         same_app = true; // same client leader
00264     else if( c1->group() == c2->group())
00265         same_app = true; // same group
00266     else if( c1->pid() == 0 || c2->pid() == 0 )
00267         ; // old apps that don't have _NET_WM_PID, consider them different
00268           // if they weren't found to match above
00269     else
00270         same_app = true; // looks like it's the same app
00271     return same_app;
00272     }
00273 
00274 // Non-transient windows with window role containing '#' are always
00275 // considered belonging to different applications (unless
00276 // the window role is exactly the same). KMainWindow sets
00277 // window role this way by default, and different KMainWindow
00278 // usually "are" different application from user's point of view.
00279 // This help with no-focus-stealing for e.g. konqy reusing.
00280 // On the other hand, if one of the windows is active, they are
00281 // considered belonging to the same application. This is for
00282 // the cases when opening new mainwindow directly from the application,
00283 // e.g. 'Open New Window' in konqy ( active_hack == true ).
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                 // if a group transient is in its own group, it didn't possibly have a group,
00294                 // and therefore should be considered belonging to the same app like
00295                 // all other windows from the same app
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     // hacks here
00314         // Mozilla has resourceName() and resourceClass() swapped
00315         c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" )
00316         {
00317         if( !active_hack )   // without the active hack for focus stealing prevention,
00318             return c1 == c2; // different mainwindows are always different apps
00319         if( !c1->isActive() && !c2->isActive())
00320             return c1 == c2;
00321         else
00322             return true;
00323         }
00324     return true;
00325     }
00326 
00327 /*
00328 
00329  Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3
00330 
00331  WM_TRANSIENT_FOR is basically means "this is my mainwindow".
00332  For NET::Unknown windows, transient windows are considered to be NET::Dialog
00333  windows, for compatibility with non-NETWM clients. KWin may adjust the value
00334  of this property in some cases (window pointing to itself or creating a loop,
00335  keeping NET::Splash windows above other windows from the same app, etc.).
00336 
00337  Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after
00338  possibly being adjusted by KWin. Client::transient_for points to the Client
00339  this Client is transient for, or is NULL. If Client::transient_for_id is
00340  poiting to the root window, the window is considered to be transient
00341  for the whole window group, as suggested in NETWM 7.3.
00342 
00343  In the case of group transient window, Client::transient_for is NULL,
00344  and Client::groupTransient() returns true. Such window is treated as
00345  if it were transient for every window in its window group that has been
00346  mapped _before_ it (or, to be exact, was added to the same group before it).
00347  Otherwise two group transients can create loops, which can lead very very
00348  nasty things (bug #67914 and all its dupes).
00349 
00350  Client::original_transient_for_id is the value of the property, which
00351  may be different if Client::transient_for_id if e.g. forcing NET::Splash
00352  to be kept on top of its window group, or when the mainwindow is not mapped
00353  yet, in which case the window is temporarily made group transient,
00354  and when the mainwindow is mapped, transiency is re-evaluated.
00355 
00356  This can get a bit complicated with with e.g. two Konqueror windows created
00357  by the same process. They should ideally appear like two independent applications
00358  to the user. This should be accomplished by all windows in the same process
00359  having the same window group (needs to be changed in Qt at the moment), and
00360  using non-group transients poiting to their relevant mainwindow for toolwindows
00361  etc. KWin should handle both group and non-group transient dialogs well.
00362 
00363  In other words:
00364  - non-transient windows     : isTransient() == false
00365  - normal transients         : transientFor() != NULL
00366  - group transients          : groupTransient() == true
00367 
00368  - list of mainwindows       : mainClients()  (call once and loop over the result)
00369  - list of transients        : transients()
00370  - every window in the group : group()->members()
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 ); // verifyTransient() had to check this
00400             transient_for->addTransient( this );
00401             }
00402         checkGroup(); // first check group
00403         if( groupTransient())
00404             { // and make transient for all in the group
00405             for( ClientList::ConstIterator it = group()->members().begin();
00406                  it != group()->members().end();
00407                  ++it )
00408                 {
00409                 if( *it == this )
00410                     break; // this means the window is only transient for windows mapped before it
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 // *sigh* this transiency handling is madness :(
00433 // This one is called when destroying/releasing a window.
00434 // It makes sure this client is removed from all grouping
00435 // related lists.
00436 void Client::cleanGrouping()
00437     {
00438 //    kdDebug() << "CLEANGROUPING:" << this << endl;
00439 //    for( ClientList::ConstIterator it = group()->members().begin();
00440 //         it != group()->members().end();
00441 //         ++it )
00442 //        kdDebug() << "CL:" << *it << endl;
00443 //    ClientList mains;
00444 //    mains = mainClients();
00445 //    for( ClientList::ConstIterator it = mains.begin();
00446 //         it != mains.end();
00447 //         ++it )
00448 //        kdDebug() << "MN:" << *it << endl;
00449     removeFromMainClients();
00450 //    kdDebug() << "CLEANGROUPING2:" << this << endl;
00451 //    for( ClientList::ConstIterator it = group()->members().begin();
00452 //         it != group()->members().end();
00453 //         ++it )
00454 //        kdDebug() << "CL2:" << *it << endl;
00455 //    mains = mainClients();
00456 //    for( ClientList::ConstIterator it = mains.begin();
00457 //         it != mains.end();
00458 //         ++it )
00459 //        kdDebug() << "MN2:" << *it << endl;
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 //    kdDebug() << "CLEANGROUPING3:" << this << endl;
00473 //    for( ClientList::ConstIterator it = group()->members().begin();
00474 //         it != group()->members().end();
00475 //         ++it )
00476 //        kdDebug() << "CL3:" << *it << endl;
00477 //    mains = mainClients();
00478 //    for( ClientList::ConstIterator it = mains.begin();
00479 //         it != mains.end();
00480 //         ++it )
00481 //        kdDebug() << "MN3:" << *it << endl;
00482     // HACK
00483     // removeFromMainClients() did remove 'this' from transient
00484     // lists of all group members, but then made windows that
00485     // were transient for 'this' group transient, which again
00486     // added 'this' to those transient lists :(
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 //    kdDebug() << "CLEANGROUPING4:" << this << endl;
00495 //    for( ClientList::ConstIterator it = group_members.begin();
00496 //         it != group_members.end();
00497 //         ++it )
00498 //        kdDebug() << "CL4:" << *it << endl;
00499     }
00500 
00501 // Make sure that no group transient is considered transient
00502 // for a window that is (directly or indirectly) transient for it
00503 // (including another group transients).
00504 // Non-group transients not causing loops are checked in verifyTransientFor().
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()) // check all group transients in the group
00512             continue;                  // TODO optimize to check only the changed ones?
00513         for( ClientList::ConstIterator it2 = group()->members().begin();
00514              it2 != group()->members().end();
00515              ++it2 ) // group transients can be transient only for others in the group,
00516             {        // so don't make them transient for the ones that are transient for it
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                     { // don't use removeTransient(), that would modify *it2 too
00525                     (*it2)->transients_list.remove( *it1 );
00526                     continue;
00527                     }
00528                 }
00529             // if *it1 and *it2 are both group transients, and are transient for each other,
00530             // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later,
00531             // and should be therefore on top of *it1
00532             // TODO This could possibly be optimized, it also requires hasTransient() to check for loops.
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     // make sure splashscreens are shown above all their app's windows, even though
00546     // they're in Normal layer
00547     if( isSplash() && new_transient_for == None )
00548         new_transient_for = workspace()->rootWin();
00549     if( new_transient_for == None )
00550         if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window
00551             new_property_value = new_transient_for = workspace()->rootWin();
00552         else
00553             return None;
00554     if( new_transient_for == window()) // pointing to self
00555         { // also fix the property itself
00556         kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00557         new_property_value = new_transient_for = workspace()->rootWin();
00558         }
00559 //  The transient_for window may be embedded in another application,
00560 //  so kwin cannot see it. Try to find the managed client for the
00561 //  window and fix the transient_for property if possible.
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; // also fix the property
00584             }
00585         }
00586     else
00587         new_transient_for = before_search; // nice try
00588 // loop detection
00589 // group transients cannot cause loops, because they're considered transient only for non-transient
00590 // windows in the group
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         { // it's transient for a specific window, but that window is not mapped
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 //    assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
00619     assert( cl != this );
00620     transients_list.append( cl );
00621 //    kdDebug() << "ADDTRANS:" << this << ":" << cl << endl;
00622 //    kdDebug() << kdBacktrace() << endl;
00623 //    for( ClientList::ConstIterator it = transients_list.begin();
00624 //         it != transients_list.end();
00625 //         ++it )
00626 //        kdDebug() << "AT:" << (*it) << endl;
00627     }
00628 
00629 void Client::removeTransient( Client* cl )
00630     {
00631 //    kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
00632 //    kdDebug() << kdBacktrace() << endl;
00633     transients_list.remove( cl );
00634     // cl is transient for this, but this is going away
00635     // make cl group transient
00636     if( cl->transientFor() == this )
00637         {
00638         cl->transient_for_id = None;
00639         cl->transient_for = NULL; // SELI
00640 // SELI       cl->setTransient( workspace()->rootWin());
00641         cl->setTransient( None );
00642         }
00643     }
00644 
00645 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
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 // returns true if cl is the transient_for window for this client,
00655 // or recursively the transient_for window
00656 bool Client::hasTransient( const Client* cl, bool indirect ) const
00657     {
00658     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
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     // cl is group transient, search from top
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 // Client::window_group only holds the contents of the hint,
00721 // but it should be used only to find the group, not for anything else
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             { // move the window to the right group (e.g. a dialog provided
00730               // by different app, but transient for this one, so make it part of that group)
00731             new_group = transientFor()->group();
00732             }
00733         if( new_group == NULL ) // doesn't exist yet
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             { // doesn't have window group set, but is transient for something
00747           // so make it part of that group
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             { // group transient which actually doesn't have a group :(
00759               // try creating group with other windows with the same client leader
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 // not transient without a group, put it in its own group
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             { // group transients in the old group are no longer transient for it
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 )) // if it still exists
00800                 { // it's no longer transient for windows in the old group
00801                 for( ClientList::ConstIterator it = old_group->members().begin();
00802                      it != old_group->members().end();
00803                      ++it )
00804                     (*it)->removeTransient( this );
00805                 }
00806             // and it's transiet for all windows in the new group (this one is the most recent
00807             // in the group, so it is transient only for all previous windows)
00808             // loops are checked in checkGroupTransients()
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())  // and group transients in the new group are transient for it
00821                 continue;
00822             if( *it == this )
00823                 continue;
00824             addTransient( *it );
00825         }
00826         checkGroupTransients();
00827 #endif
00828         }
00829     }
00830 
00831 
00832 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 5 04:41:13 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003