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     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                 // There are already two groups with the same client leader.
00165                 // This most probably means the app uses group transients without
00166                 // setting group for its windows. Merging the two groups is a bad
00167                 // hack, but there's no really good solution for this case.
00168                 Group* old_group = (*it)->group();
00169                 // old_group autodeletes when being empty
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 ); // change group
00176                     }
00177                 }
00178             }
00179         }
00180     return ret;
00181     }
00182 
00183 void Workspace::updateMinimizedOfTransients( Client* c )
00184     {
00185     // if mainwindow is minimized or shaded, minimize transients too
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() ) // topmenus are not minimized, they're hidden
00194                 {
00195                 (*it)->minimize();
00196                 updateMinimizedOfTransients( (*it) );
00197                 }
00198             }
00199         }
00200     else
00201         { // else unmiminize the transients
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 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
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 // Client
00244 //****************************************
00245 
00246 // hacks for broken apps here
00247 // all resource classes are forced to be lowercase
00248 bool Client::resourceMatch( const Client* c1, const Client* c2 )
00249     {
00250     // xv has "xv" as resource name, and different strings starting with "XV" as resource class
00251     if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00252          return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00253     // Mozilla has "Mozilla" as resource name, and different strings as resource class
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; // c1 has c2 as mainwindow
00266     else if( c2->isTransient() && c1->hasTransient( c2, true ))
00267         same_app = true; // c2 has c1 as mainwindow
00268     else if( c1->pid() != c2->pid()
00269         || c1->wmClientMachine() != c2->wmClientMachine())
00270         ; // different processes
00271     else if( c1->wmClientLeader() != c2->wmClientLeader()
00272         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00273         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00274         ; // different client leader
00275     else if( !resourceMatch( c1, c2 ))
00276         ; // different apps
00277     else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00278         ; // "different" apps
00279     else if( c1->wmClientLeader() == c2->wmClientLeader()
00280         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00281         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00282         same_app = true; // same client leader
00283     else if( c1->group() == c2->group())
00284         same_app = true; // same group
00285     else if( c1->pid() == 0 || c2->pid() == 0 )
00286         ; // old apps that don't have _NET_WM_PID, consider them different
00287           // if they weren't found to match above
00288     else
00289         same_app = true; // looks like it's the same app
00290     return same_app;
00291     }
00292 
00293 // Non-transient windows with window role containing '#' are always
00294 // considered belonging to different applications (unless
00295 // the window role is exactly the same). KMainWindow sets
00296 // window role this way by default, and different KMainWindow
00297 // usually "are" different application from user's point of view.
00298 // This help with no-focus-stealing for e.g. konqy reusing.
00299 // On the other hand, if one of the windows is active, they are
00300 // considered belonging to the same application. This is for
00301 // the cases when opening new mainwindow directly from the application,
00302 // e.g. 'Open New Window' in konqy ( active_hack == true ).
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                 // if a group transient is in its own group, it didn't possibly have a group,
00313                 // and therefore should be considered belonging to the same app like
00314                 // all other windows from the same app
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     // hacks here
00333         // Mozilla has resourceName() and resourceClass() swapped
00334         c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" )
00335         {
00336         if( !active_hack )   // without the active hack for focus stealing prevention,
00337             return c1 == c2; // different mainwindows are always different apps
00338         if( !c1->isActive() && !c2->isActive())
00339             return c1 == c2;
00340         else
00341             return true;
00342         }
00343     return true;
00344     }
00345 
00346 /*
00347 
00348  Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3
00349 
00350  WM_TRANSIENT_FOR is basically means "this is my mainwindow".
00351  For NET::Unknown windows, transient windows are considered to be NET::Dialog
00352  windows, for compatibility with non-NETWM clients. KWin may adjust the value
00353  of this property in some cases (window pointing to itself or creating a loop,
00354  keeping NET::Splash windows above other windows from the same app, etc.).
00355 
00356  Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after
00357  possibly being adjusted by KWin. Client::transient_for points to the Client
00358  this Client is transient for, or is NULL. If Client::transient_for_id is
00359  poiting to the root window, the window is considered to be transient
00360  for the whole window group, as suggested in NETWM 7.3.
00361 
00362  In the case of group transient window, Client::transient_for is NULL,
00363  and Client::groupTransient() returns true. Such window is treated as
00364  if it were transient for every window in its window group that has been
00365  mapped _before_ it (or, to be exact, was added to the same group before it).
00366  Otherwise two group transients can create loops, which can lead very very
00367  nasty things (bug #67914 and all its dupes).
00368 
00369  Client::original_transient_for_id is the value of the property, which
00370  may be different if Client::transient_for_id if e.g. forcing NET::Splash
00371  to be kept on top of its window group, or when the mainwindow is not mapped
00372  yet, in which case the window is temporarily made group transient,
00373  and when the mainwindow is mapped, transiency is re-evaluated.
00374 
00375  This can get a bit complicated with with e.g. two Konqueror windows created
00376  by the same process. They should ideally appear like two independent applications
00377  to the user. This should be accomplished by all windows in the same process
00378  having the same window group (needs to be changed in Qt at the moment), and
00379  using non-group transients poiting to their relevant mainwindow for toolwindows
00380  etc. KWin should handle both group and non-group transient dialogs well.
00381 
00382  In other words:
00383  - non-transient windows     : isTransient() == false
00384  - normal transients         : transientFor() != NULL
00385  - group transients          : groupTransient() == true
00386 
00387  - list of mainwindows       : mainClients()  (call once and loop over the result)
00388  - list of transients        : transients()
00389  - every window in the group : group()->members()
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 ); // verifyTransient() had to check this
00419             transient_for->addTransient( this );
00420             } // checkGroup() will check 'check_active_modal'
00421         checkGroup( NULL, true ); // force, because transiency has changed
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 // *sigh* this transiency handling is madness :(
00440 // This one is called when destroying/releasing a window.
00441 // It makes sure this client is removed from all grouping
00442 // related lists.
00443 void Client::cleanGrouping()
00444     {
00445 //    kdDebug() << "CLEANGROUPING:" << this << endl;
00446 //    for( ClientList::ConstIterator it = group()->members().begin();
00447 //         it != group()->members().end();
00448 //         ++it )
00449 //        kdDebug() << "CL:" << *it << endl;
00450 //    ClientList mains;
00451 //    mains = mainClients();
00452 //    for( ClientList::ConstIterator it = mains.begin();
00453 //         it != mains.end();
00454 //         ++it )
00455 //        kdDebug() << "MN:" << *it << endl;
00456     removeFromMainClients();
00457 //    kdDebug() << "CLEANGROUPING2:" << this << endl;
00458 //    for( ClientList::ConstIterator it = group()->members().begin();
00459 //         it != group()->members().end();
00460 //         ++it )
00461 //        kdDebug() << "CL2:" << *it << endl;
00462 //    mains = mainClients();
00463 //    for( ClientList::ConstIterator it = mains.begin();
00464 //         it != mains.end();
00465 //         ++it )
00466 //        kdDebug() << "MN2:" << *it << endl;
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 //    kdDebug() << "CLEANGROUPING3:" << this << endl;
00480 //    for( ClientList::ConstIterator it = group()->members().begin();
00481 //         it != group()->members().end();
00482 //         ++it )
00483 //        kdDebug() << "CL3:" << *it << endl;
00484 //    mains = mainClients();
00485 //    for( ClientList::ConstIterator it = mains.begin();
00486 //         it != mains.end();
00487 //         ++it )
00488 //        kdDebug() << "MN3:" << *it << endl;
00489     // HACK
00490     // removeFromMainClients() did remove 'this' from transient
00491     // lists of all group members, but then made windows that
00492     // were transient for 'this' group transient, which again
00493     // added 'this' to those transient lists :(
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 //    kdDebug() << "CLEANGROUPING4:" << this << endl;
00502 //    for( ClientList::ConstIterator it = group_members.begin();
00503 //         it != group_members.end();
00504 //         ++it )
00505 //        kdDebug() << "CL4:" << *it << endl;
00506     }
00507 
00508 // Make sure that no group transient is considered transient
00509 // for a window that is (directly or indirectly) transient for it
00510 // (including another group transients).
00511 // Non-group transients not causing loops are checked in verifyTransientFor().
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()) // check all group transients in the group
00519             continue;                  // TODO optimize to check only the changed ones?
00520         for( ClientList::ConstIterator it2 = group()->members().begin();
00521              it2 != group()->members().end();
00522              ++it2 ) // group transients can be transient only for others in the group,
00523             {        // so don't make them transient for the ones that are transient for it
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                     { // don't use removeTransient(), that would modify *it2 too
00532                     (*it2)->transients_list.remove( *it1 );
00533                     continue;
00534                     }
00535                 }
00536             // if *it1 and *it2 are both group transients, and are transient for each other,
00537             // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later,
00538             // and should be therefore on top of *it1
00539             // TODO This could possibly be optimized, it also requires hasTransient() to check for loops.
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     // make sure splashscreens are shown above all their app's windows, even though
00553     // they're in Normal layer
00554     if( isSplash() && new_transient_for == None )
00555         new_transient_for = workspace()->rootWin();
00556     if( new_transient_for == None )
00557         if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window
00558             new_property_value = new_transient_for = workspace()->rootWin();
00559         else
00560             return None;
00561     if( new_transient_for == window()) // pointing to self
00562         { // also fix the property itself
00563         kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00564         new_property_value = new_transient_for = workspace()->rootWin();
00565         }
00566 //  The transient_for window may be embedded in another application,
00567 //  so kwin cannot see it. Try to find the managed client for the
00568 //  window and fix the transient_for property if possible.
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; // also fix the property
00591             }
00592         }
00593     else
00594         new_transient_for = before_search; // nice try
00595 // loop detection
00596 // group transients cannot cause loops, because they're considered transient only for non-transient
00597 // windows in the group
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         { // it's transient for a specific window, but that window is not mapped
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 //    assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
00626     assert( cl != this );
00627     transients_list.append( cl );
00628     if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())        
00629         check_active_modal = true;
00630 //    kdDebug() << "ADDTRANS:" << this << ":" << cl << endl;
00631 //    kdDebug() << kdBacktrace() << endl;
00632 //    for( ClientList::ConstIterator it = transients_list.begin();
00633 //         it != transients_list.end();
00634 //         ++it )
00635 //        kdDebug() << "AT:" << (*it) << endl;
00636     }
00637 
00638 void Client::removeTransient( Client* cl )
00639     {
00640 //    kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
00641 //    kdDebug() << kdBacktrace() << endl;
00642     transients_list.remove( cl );
00643     // cl is transient for this, but this is going away
00644     // make cl group transient
00645     if( cl->transientFor() == this )
00646         {
00647         cl->transient_for_id = None;
00648         cl->transient_for = NULL; // SELI
00649 // SELI       cl->setTransient( workspace()->rootWin());
00650         cl->setTransient( None );
00651         }
00652     }
00653 
00654 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
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 // returns true if cl is the transient_for window for this client,
00664 // or recursively the transient_for window
00665 bool Client::hasTransient( const Client* cl, bool indirect ) const
00666     {
00667     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
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     // cl is group transient, search from top
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 // Client::window_group only holds the contents of the hint,
00730 // but it should be used only to find the group, not for anything else
00731 // Argument is only when some specific group needs to be set.
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             { // move the window to the right group (e.g. a dialog provided
00750               // by different app, but transient for this one, so make it part of that group)
00751             new_group = transientFor()->group();
00752             }
00753         if( new_group == NULL ) // doesn't exist yet
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             { // doesn't have window group set, but is transient for something
00767           // so make it part of that group
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             { // group transient which actually doesn't have a group :(
00779               // try creating group with other windows with the same client leader
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 // not transient without a group, put it in its own group
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             { // group transients in the old group are no longer transient for it
00811             if( (*it)->groupTransient() && (*it)->group() != group())
00812                 it = transients_list.remove( it );
00813             else
00814                 ++it;
00815             }
00816         if( groupTransient())
00817             { // and make transient for all in the group
00818             for( ClientList::ConstIterator it = group()->members().begin();
00819                  it != group()->members().end();
00820                  ++it )
00821                 {
00822                 if( *it == this )
00823                     break; // this means the window is only transient for windows mapped before it
00824                 (*it)->addTransient( this );
00825                 }
00826             }
00827 #if 0 // TODO
00828         if( groupTransient())
00829             {
00830             if( workspace()->findGroup( old_group )) // if it still exists
00831                 { // it's no longer transient for windows in the old group
00832                 for( ClientList::ConstIterator it = old_group->members().begin();
00833                      it != old_group->members().end();
00834                      ++it )
00835                     (*it)->removeTransient( this );
00836                 }
00837             // and it's transiet for all windows in the new group (this one is the most recent
00838             // in the group, so it is transient only for all previous windows)
00839             // loops are checked in checkGroupTransients()
00840             for( ClientList::ConstIterator it = group()->members().begin();
00841                  it != group()->members().end();
00842                  ++it )
00843                 (*it)->addTransient( this );
00844             }
00845 #endif
00846         // group transient splashscreens should be transient even for windows
00847         // in group mapped later
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 )) // TODO indirect?
00857                 continue;
00858             addTransient( *it );
00859         }
00860         }
00861     checkGroupTransients();
00862     // if the active window got new modal transient, activate it
00863     // cannot be done in AddTransient(), because there may temporarily
00864     // exist loops, breaking findModal
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 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 16 14:44:21 2004 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003