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( true ); // avoid animation
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( true ); // avoid animation
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( false ) != c2->wmClientMachine( false ))
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         if( isTopMenu())
00423             workspace()->updateCurrentTopMenu();
00424         workspace()->updateClientLayer( this );
00425         }
00426     }
00427 
00428 void Client::removeFromMainClients()
00429     {
00430     if( transientFor() != NULL )
00431         transientFor()->removeTransient( this );
00432     if( groupTransient())
00433         {
00434         for( ClientList::ConstIterator it = group()->members().begin();
00435              it != group()->members().end();
00436              ++it )
00437             (*it)->removeTransient( this );
00438         }
00439     }
00440 
00441 // *sigh* this transiency handling is madness :(
00442 // This one is called when destroying/releasing a window.
00443 // It makes sure this client is removed from all grouping
00444 // related lists.
00445 void Client::cleanGrouping()
00446     {
00447 //    kdDebug() << "CLEANGROUPING:" << this << endl;
00448 //    for( ClientList::ConstIterator it = group()->members().begin();
00449 //         it != group()->members().end();
00450 //         ++it )
00451 //        kdDebug() << "CL:" << *it << endl;
00452 //    ClientList mains;
00453 //    mains = mainClients();
00454 //    for( ClientList::ConstIterator it = mains.begin();
00455 //         it != mains.end();
00456 //         ++it )
00457 //        kdDebug() << "MN:" << *it << endl;
00458     removeFromMainClients();
00459 //    kdDebug() << "CLEANGROUPING2:" << this << endl;
00460 //    for( ClientList::ConstIterator it = group()->members().begin();
00461 //         it != group()->members().end();
00462 //         ++it )
00463 //        kdDebug() << "CL2:" << *it << endl;
00464 //    mains = mainClients();
00465 //    for( ClientList::ConstIterator it = mains.begin();
00466 //         it != mains.end();
00467 //         ++it )
00468 //        kdDebug() << "MN2:" << *it << endl;
00469     for( ClientList::ConstIterator it = transients_list.begin();
00470          it != transients_list.end();
00471          )
00472         {
00473         if( (*it)->transientFor() == this )
00474             {
00475             ClientList::ConstIterator it2 = it++;
00476             removeTransient( *it2 );
00477             }
00478         else
00479             ++it;
00480         }
00481 //    kdDebug() << "CLEANGROUPING3:" << this << endl;
00482 //    for( ClientList::ConstIterator it = group()->members().begin();
00483 //         it != group()->members().end();
00484 //         ++it )
00485 //        kdDebug() << "CL3:" << *it << endl;
00486 //    mains = mainClients();
00487 //    for( ClientList::ConstIterator it = mains.begin();
00488 //         it != mains.end();
00489 //         ++it )
00490 //        kdDebug() << "MN3:" << *it << endl;
00491     // HACK
00492     // removeFromMainClients() did remove 'this' from transient
00493     // lists of all group members, but then made windows that
00494     // were transient for 'this' group transient, which again
00495     // added 'this' to those transient lists :(
00496     ClientList group_members = group()->members();
00497     group()->removeMember( this );
00498     in_group = NULL;
00499     for( ClientList::ConstIterator it = group_members.begin();
00500          it != group_members.end();
00501          ++it )
00502         (*it)->removeTransient( this );
00503 //    kdDebug() << "CLEANGROUPING4:" << this << endl;
00504 //    for( ClientList::ConstIterator it = group_members.begin();
00505 //         it != group_members.end();
00506 //         ++it )
00507 //        kdDebug() << "CL4:" << *it << endl;
00508     }
00509 
00510 // Make sure that no group transient is considered transient
00511 // for a window that is (directly or indirectly) transient for it
00512 // (including another group transients).
00513 // Non-group transients not causing loops are checked in verifyTransientFor().
00514 void Client::checkGroupTransients()
00515     {
00516     for( ClientList::ConstIterator it1 = group()->members().begin();
00517          it1 != group()->members().end();
00518          ++it1 )
00519         {
00520         if( !(*it1)->groupTransient()) // check all group transients in the group
00521             continue;                  // TODO optimize to check only the changed ones?
00522         for( ClientList::ConstIterator it2 = group()->members().begin();
00523              it2 != group()->members().end();
00524              ++it2 ) // group transients can be transient only for others in the group,
00525             {        // so don't make them transient for the ones that are transient for it
00526             if( *it1 == *it2 )
00527                 continue;
00528             for( Client* cl = (*it2)->transientFor();
00529                  cl != NULL;
00530                  cl = cl->transientFor())
00531                 {
00532                 if( cl == *it1 )
00533                     { // don't use removeTransient(), that would modify *it2 too
00534                     (*it2)->transients_list.remove( *it1 );
00535                     continue;
00536                     }
00537                 }
00538             // if *it1 and *it2 are both group transients, and are transient for each other,
00539             // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later,
00540             // and should be therefore on top of *it1
00541             // TODO This could possibly be optimized, it also requires hasTransient() to check for loops.
00542             if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00543                 (*it2)->transients_list.remove( *it1 );
00544             }
00545         }
00546     }
00547 
00551 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00552     {
00553     Window new_property_value = new_transient_for;
00554     // make sure splashscreens are shown above all their app's windows, even though
00555     // they're in Normal layer
00556     if( isSplash() && new_transient_for == None )
00557         new_transient_for = workspace()->rootWin();
00558     if( new_transient_for == None )
00559         if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window
00560             new_property_value = new_transient_for = workspace()->rootWin();
00561         else
00562             return None;
00563     if( new_transient_for == window()) // pointing to self
00564         { // also fix the property itself
00565         kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00566         new_property_value = new_transient_for = workspace()->rootWin();
00567         }
00568 //  The transient_for window may be embedded in another application,
00569 //  so kwin cannot see it. Try to find the managed client for the
00570 //  window and fix the transient_for property if possible.
00571     WId before_search = new_transient_for;
00572     while( new_transient_for != None
00573            && new_transient_for != workspace()->rootWin()
00574            && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00575         {
00576         Window root_return, parent_return;
00577         Window* wins = NULL;
00578         unsigned int nwins;
00579         int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return,  &wins, &nwins);
00580         if ( wins )
00581             XFree((void *) wins);
00582         if ( r == 0)
00583             break;
00584         new_transient_for = parent_return;
00585         }
00586     if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00587         {
00588         if( new_transient_for != before_search )
00589             {
00590             kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00591                 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00592             new_property_value = new_transient_for; // also fix the property
00593             }
00594         }
00595     else
00596         new_transient_for = before_search; // nice try
00597 // loop detection
00598 // group transients cannot cause loops, because they're considered transient only for non-transient
00599 // windows in the group
00600     int count = 20;
00601     Window loop_pos = new_transient_for;
00602     while( loop_pos != None && loop_pos != workspace()->rootWin())
00603         {
00604         Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00605         if( pos == NULL )
00606             break;
00607         loop_pos = pos->transient_for_id;
00608         if( --count == 0 )
00609             {
00610             kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00611             new_transient_for = workspace()->rootWin();
00612             }
00613         }
00614     if( new_transient_for != workspace()->rootWin()
00615         && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00616         { // it's transient for a specific window, but that window is not mapped
00617         new_transient_for = workspace()->rootWin();
00618         }
00619     if( new_property_value != original_transient_for_id )
00620         XSetTransientForHint( qt_xdisplay(), window(), new_property_value );
00621     return new_transient_for;
00622     }
00623 
00624 void Client::addTransient( Client* cl )
00625     {
00626     assert( !transients_list.contains( cl ));
00627 //    assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
00628     assert( cl != this );
00629     transients_list.append( cl );
00630     if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())        
00631         check_active_modal = true;
00632 //    kdDebug() << "ADDTRANS:" << this << ":" << cl << endl;
00633 //    kdDebug() << kdBacktrace() << endl;
00634 //    for( ClientList::ConstIterator it = transients_list.begin();
00635 //         it != transients_list.end();
00636 //         ++it )
00637 //        kdDebug() << "AT:" << (*it) << endl;
00638     }
00639 
00640 void Client::removeTransient( Client* cl )
00641     {
00642 //    kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
00643 //    kdDebug() << kdBacktrace() << endl;
00644     transients_list.remove( cl );
00645     // cl is transient for this, but this is going away
00646     // make cl group transient
00647     if( cl->transientFor() == this )
00648         {
00649         cl->transient_for_id = None;
00650         cl->transient_for = NULL; // SELI
00651 // SELI       cl->setTransient( workspace()->rootWin());
00652         cl->setTransient( None );
00653         }
00654     }
00655 
00656 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
00657 void Client::checkTransient( Window w )
00658     {
00659     if( original_transient_for_id != w )
00660         return;
00661     w = verifyTransientFor( w, true );
00662     setTransient( w );
00663     }
00664 
00665 // returns true if cl is the transient_for window for this client,
00666 // or recursively the transient_for window
00667 bool Client::hasTransient( const Client* cl, bool indirect ) const
00668     {
00669     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
00670     ConstClientList set;
00671     return hasTransientInternal( cl, indirect, set );
00672     }
00673 
00674 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00675     {
00676     if( set.contains( this ))
00677         return false;
00678     set.append( this );
00679     if( cl->transientFor() != NULL )
00680         {
00681         if( cl->transientFor() == this )
00682             return true;
00683         if( !indirect )
00684             return false;
00685         return hasTransientInternal( cl->transientFor(), indirect, set );
00686         }
00687     if( !cl->isTransient())
00688         return false;
00689     if( group() != cl->group())
00690         return false;
00691     // cl is group transient, search from top
00692     if( transients().contains( const_cast< Client* >( cl )))
00693         return true;
00694     if( !indirect )
00695         return false;
00696     for( ClientList::ConstIterator it = transients().begin();
00697          it != transients().end();
00698          ++it )
00699         if( (*it)->hasTransientInternal( cl, indirect, set ))
00700             return true;
00701     return false;
00702     }
00703 
00704 ClientList Client::mainClients() const
00705     {
00706     if( !isTransient())
00707         return ClientList();
00708     if( transientFor() != NULL )
00709         return ClientList() << const_cast< Client* >( transientFor());
00710     ClientList result;
00711     for( ClientList::ConstIterator it = group()->members().begin();
00712          it != group()->members().end();
00713          ++it )
00714         if((*it)->hasTransient( this, false ))
00715             result.append( *it );
00716     return result;
00717     }
00718 
00719 Client* Client::findModal()
00720     {
00721     for( ClientList::ConstIterator it = transients().begin();
00722          it != transients().end();
00723          ++it )
00724         if( Client* ret = (*it)->findModal())
00725             return ret;
00726     if( isModal())
00727         return this;
00728     return NULL;
00729     }
00730 
00731 // Client::window_group only holds the contents of the hint,
00732 // but it should be used only to find the group, not for anything else
00733 // Argument is only when some specific group needs to be set.
00734 void Client::checkGroup( Group* set_group, bool force )
00735     {
00736     Group* old_group = in_group;
00737     if( set_group != NULL )
00738         {
00739         if( set_group != in_group )
00740             {
00741             if( in_group != NULL )
00742                 in_group->removeMember( this );
00743             in_group = set_group;
00744             in_group->addMember( this );
00745             }
00746         }
00747     else if( window_group != None )
00748         {
00749         Group* new_group = workspace()->findGroup( window_group );
00750         if( transientFor() != NULL && transientFor()->group() != new_group )
00751             { // move the window to the right group (e.g. a dialog provided
00752               // by different app, but transient for this one, so make it part of that group)
00753             new_group = transientFor()->group();
00754             }
00755         if( new_group == NULL ) // doesn't exist yet
00756             new_group = new Group( window_group, workspace());
00757         if( new_group != in_group )
00758             {
00759             if( in_group != NULL )
00760                 in_group->removeMember( this );
00761             in_group = new_group;
00762             in_group->addMember( this );
00763             }
00764         }
00765     else
00766         {
00767         if( transientFor() != NULL )
00768             { // doesn't have window group set, but is transient for something
00769           // so make it part of that group
00770             Group* new_group = transientFor()->group();
00771             if( new_group != in_group )
00772                 {
00773                 if( in_group != NULL )
00774                     in_group->removeMember( this );
00775                 in_group = transientFor()->group();
00776                 in_group->addMember( this );
00777                 }
00778             }
00779         else if( groupTransient())
00780             { // group transient which actually doesn't have a group :(
00781               // try creating group with other windows with the same client leader
00782             Group* new_group = workspace()->findClientLeaderGroup( this );
00783             if( new_group == NULL )
00784                 new_group = new Group( None, workspace());
00785             if( new_group != in_group )
00786                 {
00787                 if( in_group != NULL )
00788                     in_group->removeMember( this );
00789                 in_group = new_group;
00790                 in_group->addMember( this );
00791                 }
00792             }
00793         else // not transient without a group, put it in its own group
00794             {
00795             if( in_group != NULL && in_group->leader() != window())
00796                 {
00797                 in_group->removeMember( this );            
00798                 in_group = NULL;
00799                 }
00800             if( in_group == NULL )
00801                 {
00802                 in_group = new Group( None, workspace());
00803                 in_group->addMember( this );
00804                 }
00805             }
00806         }
00807     if( in_group != old_group || force )
00808         {
00809         for( ClientList::Iterator it = transients_list.begin();
00810              it != transients_list.end();
00811              )
00812             { // group transients in the old group are no longer transient for it
00813             if( (*it)->groupTransient() && (*it)->group() != group())
00814                 it = transients_list.remove( it );
00815             else
00816                 ++it;
00817             }
00818         if( groupTransient())
00819             { // and make transient for all in the group
00820             for( ClientList::ConstIterator it = group()->members().begin();
00821                  it != group()->members().end();
00822                  ++it )
00823                 {
00824                 if( *it == this )
00825                     break; // this means the window is only transient for windows mapped before it
00826                 (*it)->addTransient( this );
00827                 }
00828             }
00829 #if 0 // TODO
00830         if( groupTransient())
00831             {
00832             if( workspace()->findGroup( old_group )) // if it still exists
00833                 { // it's no longer transient for windows in the old group
00834                 for( ClientList::ConstIterator it = old_group->members().begin();
00835                      it != old_group->members().end();
00836                      ++it )
00837                     (*it)->removeTransient( this );
00838                 }
00839             // and it's transiet for all windows in the new group (this one is the most recent
00840             // in the group, so it is transient only for all previous windows)
00841             // loops are checked in checkGroupTransients()
00842             for( ClientList::ConstIterator it = group()->members().begin();
00843                  it != group()->members().end();
00844                  ++it )
00845                 (*it)->addTransient( this );
00846             }
00847 #endif
00848         // group transient splashscreens should be transient even for windows
00849         // in group mapped later
00850         for( ClientList::ConstIterator it = group()->members().begin();
00851              it != group()->members().end();
00852              ++it )
00853             {
00854             if( !(*it)->isSplash())
00855                 continue;
00856             if( !(*it)->groupTransient())
00857                 continue;
00858             if( *it == this || hasTransient( *it, true )) // TODO indirect?
00859                 continue;
00860             addTransient( *it );
00861         }
00862         }
00863     checkGroupTransients();
00864     checkActiveModal();
00865     workspace()->updateClientLayer( this );
00866     }
00867 
00868 bool Client::check_active_modal = false;
00869 
00870 void Client::checkActiveModal()
00871     {
00872     // if the active window got new modal transient, activate it.
00873     // cannot be done in AddTransient(), because there may temporarily
00874     // exist loops, breaking findModal
00875     Client* check_modal = workspace()->mostRecentlyActivatedClient();
00876     if( check_modal != NULL && check_modal->check_active_modal )
00877         {
00878         Client* new_modal = check_modal->findModal();
00879         if( new_modal != NULL && new_modal != check_modal )
00880             {
00881             if( !new_modal->isManaged())
00882                 return; // postpone check until end of manage()
00883             workspace()->activateClient( new_modal );
00884             }
00885         check_modal->check_active_modal = false;
00886         }
00887     }
00888 
00889 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Aug 20 13:39:11 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003