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     if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())
00622         workspace()->activateClient( findModal());
00623 //    kdDebug() << "ADDTRANS:" << this << ":" << cl << endl;
00624 //    kdDebug() << kdBacktrace() << endl;
00625 //    for( ClientList::ConstIterator it = transients_list.begin();
00626 //         it != transients_list.end();
00627 //         ++it )
00628 //        kdDebug() << "AT:" << (*it) << endl;
00629     }
00630 
00631 void Client::removeTransient( Client* cl )
00632     {
00633 //    kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
00634 //    kdDebug() << kdBacktrace() << endl;
00635     transients_list.remove( cl );
00636     // cl is transient for this, but this is going away
00637     // make cl group transient
00638     if( cl->transientFor() == this )
00639         {
00640         cl->transient_for_id = None;
00641         cl->transient_for = NULL; // SELI
00642 // SELI       cl->setTransient( workspace()->rootWin());
00643         cl->setTransient( None );
00644         }
00645     }
00646 
00647 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
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 // returns true if cl is the transient_for window for this client,
00657 // or recursively the transient_for window
00658 bool Client::hasTransient( const Client* cl, bool indirect ) const
00659     {
00660     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
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     // cl is group transient, search from top
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 // Client::window_group only holds the contents of the hint,
00723 // but it should be used only to find the group, not for anything else
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             { // move the window to the right group (e.g. a dialog provided
00732               // by different app, but transient for this one, so make it part of that group)
00733             new_group = transientFor()->group();
00734             }
00735         if( new_group == NULL ) // doesn't exist yet
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             { // doesn't have window group set, but is transient for something
00749           // so make it part of that group
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             { // group transient which actually doesn't have a group :(
00761               // try creating group with other windows with the same client leader
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 // not transient without a group, put it in its own group
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             { // group transients in the old group are no longer transient for it
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 )) // if it still exists
00802                 { // it's no longer transient for windows in the old group
00803                 for( ClientList::ConstIterator it = old_group->members().begin();
00804                      it != old_group->members().end();
00805                      ++it )
00806                     (*it)->removeTransient( this );
00807                 }
00808             // and it's transiet for all windows in the new group (this one is the most recent
00809             // in the group, so it is transient only for all previous windows)
00810             // loops are checked in checkGroupTransients()
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())  // and group transients in the new group are transient for it
00823                 continue;
00824             if( *it == this )
00825                 continue;
00826             addTransient( *it );
00827         }
00828         checkGroupTransients();
00829 #endif
00830         }
00831     }
00832 
00833 
00834 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 29 21:20:50 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003