• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

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 #include "group.h"
00019 
00020 #include "workspace.h"
00021 #include "client.h"
00022 
00023 #include <assert.h>
00024 #include <tdestartupinfo.h>
00025 
00026 
00027 /*
00028  TODO
00029  Rename as many uses of 'transient' as possible (hasTransient->hasSubwindow,etc.),
00030  or I'll get it backwards in half of the cases again.
00031 */
00032 
00033 namespace KWinInternal
00034 {
00035 
00036 /*
00037  Consistency checks for window relations. Since transients are determinated
00038  using Client::transiency_list and main windows are determined using Client::transientFor()
00039  or the group for group transients, these have to match both ways.
00040 */
00041 //#define ENABLE_TRANSIENCY_CHECK
00042 
00043 #ifdef NDEBUG
00044 #undef ENABLE_TRANSIENCY_CHECK
00045 #endif
00046 
00047 #ifdef ENABLE_TRANSIENCY_CHECK
00048 static bool transiencyCheckNonExistent = false;
00049 
00050 bool performTransiencyCheck()
00051     {
00052     bool ret = true;
00053     ClientList clients = Workspace::self()->clients;
00054     for( ClientList::ConstIterator it1 = clients.begin();
00055          it1 != clients.end();
00056          ++it1 )
00057         {
00058         if( (*it1)->deleting )
00059             continue;
00060         if( (*it1)->in_group == NULL )
00061             {
00062             kdDebug() << "TC: " << *it1 << " in not in a group" << endl;
00063             ret = false;
00064             }
00065         else if( !(*it1)->in_group->members().contains( *it1 ))
00066             {
00067             kdDebug() << "TC: " << *it1 << " has a group " << (*it1)->in_group << " but group does not contain it" << endl;
00068             ret = false;
00069             }
00070         if( !(*it1)->isTransient())
00071             {
00072             if( !(*it1)->mainClients().isEmpty())
00073                 {
00074                 kdDebug() << "TC: " << *it1 << " is not transient, has main clients:" << (*it1)->mainClients() << endl;
00075                 ret = false;
00076                 }
00077             }
00078         else
00079             {
00080             ClientList mains = (*it1)->mainClients();
00081             for( ClientList::ConstIterator it2 = mains.begin();
00082                  it2 != mains.end();
00083                  ++it2 )
00084                 {
00085                 if( transiencyCheckNonExistent
00086                     && !Workspace::self()->clients.contains( *it2 )
00087                     && !Workspace::self()->desktops.contains( *it2 ))
00088                     {
00089                     kdDebug() << "TC:" << *it1 << " has non-existent main client " << endl;
00090                     kdDebug() << "TC2:" << *it2 << endl; // this may crash
00091                     ret = false;
00092                     continue;
00093                     }
00094                 if( !(*it2)->transients_list.contains( *it1 ))
00095                     {
00096                     kdDebug() << "TC:" << *it1 << " has main client " << *it2 << " but main client does not have it as a transient" << endl;
00097                     ret = false;
00098                     }
00099                 }
00100             }
00101         ClientList trans = (*it1)->transients_list;
00102         for( ClientList::ConstIterator it2 = trans.begin();
00103              it2 != trans.end();
00104              ++it2 )
00105             {
00106             if( transiencyCheckNonExistent
00107                 && !Workspace::self()->clients.contains( *it2 )
00108                 && !Workspace::self()->desktops.contains( *it2 ))
00109                 {
00110                 kdDebug() << "TC:" << *it1 << " has non-existent transient " << endl;
00111                 kdDebug() << "TC2:" << *it2 << endl; // this may crash
00112                 ret = false;
00113                 continue;
00114                 }
00115             if( !(*it2)->mainClients().contains( *it1 ))
00116                 {
00117                 kdDebug() << "TC:" << *it1 << " has transient " << *it2 << " but transient does not have it as a main client" << endl;
00118                 ret = false;
00119                 }
00120             }
00121         }
00122     GroupList groups = Workspace::self()->groups;
00123     for( GroupList::ConstIterator it1 = groups.begin();
00124          it1 != groups.end();
00125          ++it1 )
00126         {
00127         ClientList members = (*it1)->members();
00128         for( ClientList::ConstIterator it2 = members.begin();
00129              it2 != members.end();
00130              ++it2 )
00131             {
00132             if( (*it2)->in_group != *it1 )
00133                 {
00134                 kdDebug() << "TC: Group " << *it1 << " contains client " << *it2 << " but client is not in that group" << endl;
00135                 ret = false;
00136                 }
00137             }
00138         }
00139     return ret;
00140     }
00141 
00142 static TQString transiencyCheckStartBt;
00143 static const Client* transiencyCheckClient;
00144 static int transiencyCheck = 0;
00145 
00146 static void startTransiencyCheck( const TQString& bt, const Client* c, bool ne )
00147     {
00148     if( ++transiencyCheck == 1 )
00149         {
00150         transiencyCheckStartBt = bt;
00151         transiencyCheckClient = c;
00152         }
00153     if( ne )
00154         transiencyCheckNonExistent = true;
00155     }
00156 static void checkTransiency()
00157     {
00158     if( --transiencyCheck == 0 )
00159         {
00160         if( !performTransiencyCheck())
00161             {
00162             kdDebug() << "BT:" << transiencyCheckStartBt << endl;
00163             kdDebug() << "CLIENT:" << transiencyCheckClient << endl;
00164             assert( false );
00165             }
00166         transiencyCheckNonExistent = false;
00167         }
00168     }
00169 class TransiencyChecker
00170     {
00171     public:
00172         TransiencyChecker( const TQString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); }
00173         ~TransiencyChecker() { checkTransiency(); }
00174     };
00175 
00176 void checkNonExistentClients()
00177     {
00178     startTransiencyCheck( kdBacktrace(), NULL, true );
00179     checkTransiency();
00180     }
00181 
00182 #define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c )
00183 
00184 #else
00185 
00186 #define TRANSIENCY_CHECK( c )
00187 
00188 void checkNonExistentClients()
00189     {
00190     }
00191 
00192 #endif
00193 
00194 //********************************************
00195 // Group
00196 //********************************************
00197 
00198 Group::Group( Window leader_P, Workspace* workspace_P )
00199     :   leader_client( NULL ),
00200         leader_wid( leader_P ),
00201         _workspace( workspace_P ),
00202         leader_info( NULL ),
00203         user_time( -1U ),
00204         refcount( 0 )
00205     {
00206     if( leader_P != None )
00207         {
00208         leader_client = workspace_P->findClient( WindowMatchPredicate( leader_P ));
00209         unsigned long properties[ 2 ] = { 0, NET::WM2StartupId };
00210         leader_info = new NETWinInfo( tqt_xdisplay(), leader_P, workspace()->rootWin(),
00211             properties, 2 );
00212         }
00213     workspace()->addGroup( this, Allowed );
00214     }
00215 
00216 Group::~Group()
00217     {
00218     delete leader_info;
00219     }
00220 
00221 TQPixmap Group::icon() const
00222     {
00223     if( leader_client != NULL )
00224         return leader_client->icon();
00225     else if( leader_wid != None )
00226         {
00227         TQPixmap ic;
00228         Client::readIcons( leader_wid, &ic, NULL );
00229         return ic;
00230         }
00231     return TQPixmap();
00232     }
00233 
00234 TQPixmap Group::miniIcon() const
00235     {
00236     if( leader_client != NULL )
00237         return leader_client->miniIcon();
00238     else if( leader_wid != None )
00239         {
00240         TQPixmap ic;
00241         Client::readIcons( leader_wid, NULL, &ic );
00242         return ic;
00243         }
00244     return TQPixmap();
00245     }
00246 
00247 void Group::addMember( Client* member_P )
00248     {
00249     TRANSIENCY_CHECK( member_P );
00250     _members.append( member_P );
00251 //    kdDebug() << "GROUPADD:" << this << ":" << member_P << endl;
00252 //    kdDebug() << kdBacktrace() << endl;
00253     }
00254 
00255 void Group::removeMember( Client* member_P )
00256     {
00257     TRANSIENCY_CHECK( member_P );
00258 //    kdDebug() << "GROUPREMOVE:" << this << ":" << member_P << endl;
00259 //    kdDebug() << kdBacktrace() << endl;
00260     Q_ASSERT( _members.contains( member_P ));
00261     _members.remove( member_P );
00262 // there are cases when automatic deleting of groups must be delayed,
00263 // e.g. when removing a member and doing some operation on the possibly
00264 // other members of the group (which would be however deleted already
00265 // if there were no other members)
00266     if( refcount == 0 && _members.isEmpty())
00267         {
00268         workspace()->removeGroup( this, Allowed );
00269         delete this;
00270         }
00271     }
00272 
00273 void Group::ref()
00274     {
00275     ++refcount;
00276     }
00277 
00278 void Group::deref()
00279     {
00280     if( --refcount == 0 && _members.isEmpty())
00281         {
00282         workspace()->removeGroup( this, Allowed );
00283         delete this;
00284         }
00285     }
00286 
00287 void Group::gotLeader( Client* leader_P )
00288     {
00289     assert( leader_P->window() == leader_wid );
00290     leader_client = leader_P;
00291     }
00292 
00293 void Group::lostLeader()
00294     {
00295     assert( !_members.contains( leader_client ));
00296     leader_client = NULL;
00297     if( _members.isEmpty())
00298         {
00299         workspace()->removeGroup( this, Allowed );
00300         delete this;
00301         }
00302     }
00303 
00304 void Group::getIcons()
00305     {
00306     // TODO - also needs adding the flag to NETWinInfo
00307     }
00308 
00309 //***************************************
00310 // Workspace
00311 //***************************************
00312 
00313 Group* Workspace::findGroup( Window leader ) const
00314     {
00315     assert( leader != None );
00316     for( GroupList::ConstIterator it = groups.begin();
00317          it != groups.end();
00318          ++it )
00319         if( (*it)->leader() == leader )
00320             return *it;
00321     return NULL;
00322     }
00323 
00324 // Client is group transient, but has no group set. Try to find
00325 // group with windows with the same client leader.
00326 Group* Workspace::findClientLeaderGroup( const Client* c ) const
00327     {
00328     TRANSIENCY_CHECK( c );
00329     Group* ret = NULL;
00330     for( ClientList::ConstIterator it = clients.begin();
00331          it != clients.end();
00332          ++it )
00333         {
00334         if( *it == c )
00335             continue;
00336         if( (*it)->wmClientLeader() == c->wmClientLeader())
00337             {
00338             if( ret == NULL || ret == (*it)->group())
00339                 ret = (*it)->group();
00340             else
00341                 {
00342                 // There are already two groups with the same client leader.
00343                 // This most probably means the app uses group transients without
00344                 // setting group for its windows. Merging the two groups is a bad
00345                 // hack, but there's no really good solution for this case.
00346                 ClientList old_group = (*it)->group()->members();
00347                 // old_group autodeletes when being empty
00348                 for( unsigned int pos = 0;
00349                      pos < old_group.count();
00350                      ++pos )
00351                     {
00352                     Client* tmp = old_group[ pos ];
00353                     if( tmp != c )
00354                         tmp->changeClientLeaderGroup( ret );
00355                     }
00356                 }
00357             }
00358         }
00359     return ret;
00360     }
00361 
00362 void Workspace::updateMinimizedOfTransients( Client* c )
00363     {
00364     // if mainwindow is minimized or shaded, minimize transients too
00365     if ( c->isMinimized() || c->isShade() )
00366         {
00367         for( ClientList::ConstIterator it = c->transients().begin();
00368              it != c->transients().end();
00369              ++it )
00370             {
00371             if( !(*it)->isMinimized()
00372                  && !(*it)->isTopMenu() ) // topmenus are not minimized, they're hidden
00373                 {
00374                 (*it)->minimize( true ); // avoid animation
00375                 updateMinimizedOfTransients( (*it) );
00376                 }
00377             }
00378         }
00379     else
00380         { // else unmiminize the transients
00381         for( ClientList::ConstIterator it = c->transients().begin();
00382              it != c->transients().end();
00383              ++it )
00384             {
00385             if( (*it)->isMinimized()
00386                 && !(*it)->isTopMenu())
00387                 {
00388                 (*it)->unminimize( true ); // avoid animation
00389                 updateMinimizedOfTransients( (*it) );
00390                 }
00391             }
00392         }
00393     }
00394 
00395 
00399 void Workspace::updateOnAllDesktopsOfTransients( Client* c )
00400     {
00401     for( ClientList::ConstIterator it = c->transients().begin();
00402          it != c->transients().end();
00403          ++it)
00404         {
00405         if( (*it)->isOnAllDesktops() != c->isOnAllDesktops())
00406             (*it)->setOnAllDesktops( c->isOnAllDesktops());
00407         }
00408     }
00409 
00410 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
00411 void Workspace::checkTransients( Window w )
00412     {
00413     TRANSIENCY_CHECK( NULL );
00414     for( ClientList::ConstIterator it = clients.begin();
00415          it != clients.end();
00416          ++it )
00417         (*it)->checkTransient( w );
00418     }
00419 
00420 
00421 
00422 //****************************************
00423 // Client
00424 //****************************************
00425 
00426 // hacks for broken apps here
00427 // all resource classes are forced to be lowercase
00428 bool Client::resourceMatch( const Client* c1, const Client* c2 )
00429     {
00430     // xv has "xv" as resource name, and different strings starting with "XV" as resource class
00431     if( tqstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
00432          return tqstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv";
00433     // Mozilla has "Mozilla" as resource name, and different strings as resource class
00434     if( c1->resourceName() == "mozilla" )
00435         return c2->resourceName() == "mozilla";
00436     return c1->resourceClass() == c2->resourceClass();
00437     }
00438 
00439 bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack )
00440     {
00441     bool same_app = false;
00442 
00443     // tests that definitely mean they belong together
00444     if( c1 == c2 )
00445         same_app = true;
00446     else if( c1->isTransient() && c2->hasTransient( c1, true ))
00447         same_app = true; // c1 has c2 as mainwindow
00448     else if( c2->isTransient() && c1->hasTransient( c2, true ))
00449         same_app = true; // c2 has c1 as mainwindow
00450     else if( c1->group() == c2->group())
00451         same_app = true; // same group
00452     else if( c1->wmClientLeader() == c2->wmClientLeader()
00453         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00454         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00455         same_app = true; // same client leader
00456 
00457     // tests that mean they most probably don't belong together
00458     else if( c1->pid() != c2->pid()
00459         || c1->wmClientMachine( false ) != c2->wmClientMachine( false ))
00460         ; // different processes
00461     else if( c1->wmClientLeader() != c2->wmClientLeader()
00462         && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
00463         && c2->wmClientLeader() != c2->window()) // don't use in this test then
00464         ; // different client leader
00465     else if( !resourceMatch( c1, c2 ))
00466         ; // different apps
00467     else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
00468         ; // "different" apps
00469     else if( c1->pid() == 0 || c2->pid() == 0 )
00470         ; // old apps that don't have _NET_WM_PID, consider them different
00471           // if they weren't found to match above
00472     else
00473         same_app = true; // looks like it's the same app
00474 
00475     return same_app;
00476     }
00477 
00478 // Non-transient windows with window role containing '#' are always
00479 // considered belonging to different applications (unless
00480 // the window role is exactly the same). TDEMainWindow sets
00481 // window role this way by default, and different TDEMainWindow
00482 // usually "are" different application from user's point of view.
00483 // This help with no-focus-stealing for e.g. konqy reusing.
00484 // On the other hand, if one of the windows is active, they are
00485 // considered belonging to the same application. This is for
00486 // the cases when opening new mainwindow directly from the application,
00487 // e.g. 'Open New Window' in konqy ( active_hack == true ).
00488 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack )
00489     {
00490     if( c1->isTransient())
00491         {
00492         while( c1->transientFor() != NULL )
00493             c1 = c1->transientFor();
00494         if( c1->groupTransient())
00495             return c1->group() == c2->group();
00496 #if 0
00497                 // if a group transient is in its own group, it didn't possibly have a group,
00498                 // and therefore should be considered belonging to the same app like
00499                 // all other windows from the same app
00500                 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00501 #endif
00502         }
00503     if( c2->isTransient())
00504         {
00505         while( c2->transientFor() != NULL )
00506             c2 = c2->transientFor();
00507         if( c2->groupTransient())
00508             return c1->group() == c2->group();
00509 #if 0
00510                 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2;
00511 #endif
00512         }
00513     int pos1 = c1->windowRole().find( '#' );
00514     int pos2 = c2->windowRole().find( '#' );
00515     if(( pos1 >= 0 && pos2 >= 0 )
00516         ||
00517     // hacks here
00518         // Mozilla has resourceName() and resourceClass() swapped
00519         ((c1->resourceName() == "mozilla") && (c2->resourceName() == "mozilla")) )
00520         {
00521         if( !active_hack )   // without the active hack for focus stealing prevention,
00522             return c1 == c2; // different mainwindows are always different apps
00523         if( !c1->isActive() && !c2->isActive())
00524             return c1 == c2;
00525         else
00526             return true;
00527         }
00528     return true;
00529     }
00530 
00531 /*
00532 
00533  Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3
00534 
00535  WM_TRANSIENT_FOR is basically means "this is my mainwindow".
00536  For NET::Unknown windows, transient windows are considered to be NET::Dialog
00537  windows, for compatibility with non-NETWM clients. KWin may adjust the value
00538  of this property in some cases (window pointing to itself or creating a loop,
00539  keeping NET::Splash windows above other windows from the same app, etc.).
00540 
00541  Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after
00542  possibly being adjusted by KWin. Client::transient_for points to the Client
00543  this Client is transient for, or is NULL. If Client::transient_for_id is
00544  poiting to the root window, the window is considered to be transient
00545  for the whole window group, as suggested in NETWM 7.3.
00546 
00547  In the case of group transient window, Client::transient_for is NULL,
00548  and Client::groupTransient() returns true. Such window is treated as
00549  if it were transient for every window in its window group that has been
00550  mapped _before_ it (or, to be exact, was added to the same group before it).
00551  Otherwise two group transients can create loops, which can lead very very
00552  nasty things (bug #67914 and all its dupes).
00553 
00554  Client::original_transient_for_id is the value of the property, which
00555  may be different if Client::transient_for_id if e.g. forcing NET::Splash
00556  to be kept on top of its window group, or when the mainwindow is not mapped
00557  yet, in which case the window is temporarily made group transient,
00558  and when the mainwindow is mapped, transiency is re-evaluated.
00559 
00560  This can get a bit complicated with with e.g. two Konqueror windows created
00561  by the same process. They should ideally appear like two independent applications
00562  to the user. This should be accomplished by all windows in the same process
00563  having the same window group (needs to be changed in Qt at the moment), and
00564  using non-group transients poiting to their relevant mainwindow for toolwindows
00565  etc. KWin should handle both group and non-group transient dialogs well.
00566 
00567  In other words:
00568  - non-transient windows     : isTransient() == false
00569  - normal transients         : transientFor() != NULL
00570  - group transients          : groupTransient() == true
00571 
00572  - list of mainwindows       : mainClients()  (call once and loop over the result)
00573  - list of transients        : transients()
00574  - every window in the group : group()->members()
00575 */
00576 
00577 void Client::readTransient()
00578     {
00579     TRANSIENCY_CHECK( this );
00580     Window new_transient_for_id;
00581     if( XGetTransientForHint( tqt_xdisplay(), window(), &new_transient_for_id ))
00582         {
00583         original_transient_for_id = new_transient_for_id;
00584         new_transient_for_id = verifyTransientFor( new_transient_for_id, true );
00585         }
00586     else
00587         {
00588         original_transient_for_id = None;
00589         new_transient_for_id = verifyTransientFor( None, false );
00590         }
00591     setTransient( new_transient_for_id );
00592     }
00593 
00594 void Client::setTransient( Window new_transient_for_id )
00595     {
00596     TRANSIENCY_CHECK( this );
00597     if( new_transient_for_id != transient_for_id )
00598         {
00599         removeFromMainClients();
00600         transient_for = NULL;
00601         transient_for_id = new_transient_for_id;
00602         if( transient_for_id != None && !groupTransient())
00603             {
00604             transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id ));
00605             assert( transient_for != NULL ); // verifyTransient() had to check this
00606             transient_for->addTransient( this );
00607             } // checkGroup() will check 'check_active_modal'
00608         checkGroup( NULL, true ); // force, because transiency has changed
00609         if( isTopMenu())
00610             workspace()->updateCurrentTopMenu();
00611         workspace()->updateClientLayer( this );
00612         }
00613     }
00614 
00615 void Client::removeFromMainClients()
00616     {
00617     TRANSIENCY_CHECK( this );
00618     if( transientFor() != NULL )
00619         transientFor()->removeTransient( this );
00620     if( groupTransient())
00621         {
00622         for( ClientList::ConstIterator it = group()->members().begin();
00623              it != group()->members().end();
00624              ++it )
00625             (*it)->removeTransient( this );
00626         }
00627     }
00628 
00629 // *sigh* this transiency handling is madness :(
00630 // This one is called when destroying/releasing a window.
00631 // It makes sure this client is removed from all grouping
00632 // related lists.
00633 void Client::cleanGrouping()
00634     {
00635     TRANSIENCY_CHECK( this );
00636 //    kdDebug() << "CLEANGROUPING:" << this << endl;
00637 //    for( ClientList::ConstIterator it = group()->members().begin();
00638 //         it != group()->members().end();
00639 //         ++it )
00640 //        kdDebug() << "CL:" << *it << endl;
00641 //    ClientList mains;
00642 //    mains = mainClients();
00643 //    for( ClientList::ConstIterator it = mains.begin();
00644 //         it != mains.end();
00645 //         ++it )
00646 //        kdDebug() << "MN:" << *it << endl;
00647     removeFromMainClients();
00648 //    kdDebug() << "CLEANGROUPING2:" << this << endl;
00649 //    for( ClientList::ConstIterator it = group()->members().begin();
00650 //         it != group()->members().end();
00651 //         ++it )
00652 //        kdDebug() << "CL2:" << *it << endl;
00653 //    mains = mainClients();
00654 //    for( ClientList::ConstIterator it = mains.begin();
00655 //         it != mains.end();
00656 //         ++it )
00657 //        kdDebug() << "MN2:" << *it << endl;
00658     for( ClientList::ConstIterator it = transients_list.begin();
00659          it != transients_list.end();
00660          )
00661         {
00662         if( (*it)->transientFor() == this )
00663             {
00664             ClientList::ConstIterator it2 = it++;
00665             removeTransient( *it2 );
00666             }
00667         else
00668             ++it;
00669         }
00670 //    kdDebug() << "CLEANGROUPING3:" << this << endl;
00671 //    for( ClientList::ConstIterator it = group()->members().begin();
00672 //         it != group()->members().end();
00673 //         ++it )
00674 //        kdDebug() << "CL3:" << *it << endl;
00675 //    mains = mainClients();
00676 //    for( ClientList::ConstIterator it = mains.begin();
00677 //         it != mains.end();
00678 //         ++it )
00679 //        kdDebug() << "MN3:" << *it << endl;
00680     // HACK
00681     // removeFromMainClients() did remove 'this' from transient
00682     // lists of all group members, but then made windows that
00683     // were transient for 'this' group transient, which again
00684     // added 'this' to those transient lists :(
00685     ClientList group_members = group()->members();
00686     group()->removeMember( this );
00687     in_group = NULL;
00688     for( ClientList::ConstIterator it = group_members.begin();
00689          it != group_members.end();
00690          ++it )
00691         (*it)->removeTransient( this );
00692 //    kdDebug() << "CLEANGROUPING4:" << this << endl;
00693 //    for( ClientList::ConstIterator it = group_members.begin();
00694 //         it != group_members.end();
00695 //         ++it )
00696 //        kdDebug() << "CL4:" << *it << endl;
00697     }
00698 
00699 // Make sure that no group transient is considered transient
00700 // for a window that is (directly or indirectly) transient for it
00701 // (including another group transients).
00702 // Non-group transients not causing loops are checked in verifyTransientFor().
00703 void Client::checkGroupTransients()
00704     {
00705     TRANSIENCY_CHECK( this );
00706     for( ClientList::ConstIterator it1 = group()->members().begin();
00707          it1 != group()->members().end();
00708          ++it1 )
00709         {
00710         if( !(*it1)->groupTransient()) // check all group transients in the group
00711             continue;                  // TODO optimize to check only the changed ones?
00712         for( ClientList::ConstIterator it2 = group()->members().begin();
00713              it2 != group()->members().end();
00714              ++it2 ) // group transients can be transient only for others in the group,
00715             {        // so don't make them transient for the ones that are transient for it
00716             if( *it1 == *it2 )
00717                 continue;
00718             for( Client* cl = (*it2)->transientFor();
00719                  cl != NULL;
00720                  cl = cl->transientFor())
00721                 {
00722                 if( cl == *it1 )
00723                     { // don't use removeTransient(), that would modify *it2 too
00724                     (*it2)->transients_list.remove( *it1 );
00725                     continue;
00726                     }
00727                 }
00728             // if *it1 and *it2 are both group transients, and are transient for each other,
00729             // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later,
00730             // and should be therefore on top of *it1
00731             // TODO This could possibly be optimized, it also requires hasTransient() to check for loops.
00732             if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true ))
00733                 (*it2)->transients_list.remove( *it1 );
00734             // if there are already windows W1 and W2, W2 being transient for W1, and group transient W3
00735             // is added, make it transient only for W2, not for W1, because it's already indirectly
00736             // transient for it - the indirect transiency actually shouldn't break anything,
00737             // but it can lead to exponentially expensive operations (#95231)
00738             // TODO this is pretty slow as well
00739             for( ClientList::ConstIterator it3 = group()->members().begin();
00740                  it3 != group()->members().end();
00741                  ++it3 )
00742                 {
00743                 if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 )
00744                     continue;
00745                 if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false ))
00746                     {
00747                     if( (*it2)->hasTransient( *it3, true ))
00748                         (*it2)->transients_list.remove( *it1 );
00749                     if( (*it3)->hasTransient( *it2, true ))
00750                         (*it3)->transients_list.remove( *it1 );
00751                     }
00752                 }
00753             }
00754         }
00755     }
00756 
00760 Window Client::verifyTransientFor( Window new_transient_for, bool defined )
00761     {
00762     Window new_property_value = new_transient_for;
00763     // make sure splashscreens are shown above all their app's windows, even though
00764     // they're in Normal layer
00765     if( isSplash() && new_transient_for == None )
00766         new_transient_for = workspace()->rootWin();
00767     if( new_transient_for == None )
00768         if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window
00769             new_property_value = new_transient_for = workspace()->rootWin();
00770         else
00771             return None;
00772     if( new_transient_for == window()) // pointing to self
00773         { // also fix the property itself
00774         kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl;
00775         new_property_value = new_transient_for = workspace()->rootWin();
00776         }
00777 //  The transient_for window may be embedded in another application,
00778 //  so twin cannot see it. Try to find the managed client for the
00779 //  window and fix the transient_for property if possible.
00780     WId before_search = new_transient_for;
00781     while( new_transient_for != None
00782            && new_transient_for != workspace()->rootWin()
00783            && !workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00784         {
00785         Window root_return, parent_return;
00786         Window* wins = NULL;
00787         unsigned int nwins;
00788         int r = XQueryTree(tqt_xdisplay(), new_transient_for, &root_return, &parent_return,  &wins, &nwins);
00789         if ( wins )
00790             XFree((void *) wins);
00791         if ( r == 0)
00792             break;
00793         new_transient_for = parent_return;
00794         }
00795     if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for )))
00796         {
00797         if( new_transient_for != before_search )
00798             {
00799             kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window "
00800                 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl;
00801             new_property_value = new_transient_for; // also fix the property
00802             }
00803         }
00804     else
00805         new_transient_for = before_search; // nice try
00806 // loop detection
00807 // group transients cannot cause loops, because they're considered transient only for non-transient
00808 // windows in the group
00809     int count = 20;
00810     Window loop_pos = new_transient_for;
00811     while( loop_pos != None && loop_pos != workspace()->rootWin())
00812         {
00813         Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos ));
00814         if( pos == NULL )
00815             break;
00816         loop_pos = pos->transient_for_id;
00817         if( --count == 0 || pos == this )
00818             {
00819             kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl;
00820             new_transient_for = workspace()->rootWin();
00821             }
00822         }
00823     if( new_transient_for != workspace()->rootWin()
00824         && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL )
00825         { // it's transient for a specific window, but that window is not mapped
00826         new_transient_for = workspace()->rootWin();
00827         }
00828     if( new_property_value != original_transient_for_id )
00829         XSetTransientForHint( tqt_xdisplay(), window(), new_property_value );
00830     return new_transient_for;
00831     }
00832 
00833 void Client::addTransient( Client* cl )
00834     {
00835     TRANSIENCY_CHECK( this );
00836     assert( !transients_list.contains( cl ));
00837 //    assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
00838     assert( cl != this );
00839     transients_list.append( cl );
00840     if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal())        
00841         check_active_modal = true;
00842 //    kdDebug() << "ADDTRANS:" << this << ":" << cl << endl;
00843 //    kdDebug() << kdBacktrace() << endl;
00844 //    for( ClientList::ConstIterator it = transients_list.begin();
00845 //         it != transients_list.end();
00846 //         ++it )
00847 //        kdDebug() << "AT:" << (*it) << endl;
00848     }
00849 
00850 void Client::removeTransient( Client* cl )
00851     {
00852     TRANSIENCY_CHECK( this );
00853 //    kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
00854 //    kdDebug() << kdBacktrace() << endl;
00855     transients_list.remove( cl );
00856     // cl is transient for this, but this is going away
00857     // make cl group transient
00858     if( cl->transientFor() == this )
00859         {
00860         cl->transient_for_id = None;
00861         cl->transient_for = NULL; // SELI
00862 // SELI       cl->setTransient( workspace()->rootWin());
00863         cl->setTransient( None );
00864         }
00865     }
00866 
00867 // A new window has been mapped. Check if it's not a mainwindow for this already existing window.
00868 void Client::checkTransient( Window w )
00869     {
00870     TRANSIENCY_CHECK( this );
00871     if( original_transient_for_id != w )
00872         return;
00873     w = verifyTransientFor( w, true );
00874     setTransient( w );
00875     }
00876 
00877 // returns true if cl is the transient_for window for this client,
00878 // or recursively the transient_for window
00879 bool Client::hasTransient( const Client* cl, bool indirect ) const
00880     {
00881     // checkGroupTransients() uses this to break loops, so hasTransient() must detect them
00882     ConstClientList set;
00883     return hasTransientInternal( cl, indirect, set );
00884     }
00885 
00886 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const
00887     {
00888     if( cl->transientFor() != NULL )
00889         {
00890         if( cl->transientFor() == this )
00891             return true;
00892         if( !indirect )
00893             return false;
00894         if( set.contains( cl ))
00895             return false;
00896         set.append( cl );
00897         return hasTransientInternal( cl->transientFor(), indirect, set );
00898         }
00899     if( !cl->isTransient())
00900         return false;
00901     if( group() != cl->group())
00902         return false;
00903     // cl is group transient, search from top
00904     if( transients().contains( const_cast< Client* >( cl )))
00905         return true;
00906     if( !indirect )
00907         return false;
00908     if( set.contains( this ))
00909         return false;
00910     set.append( this );
00911     for( ClientList::ConstIterator it = transients().begin();
00912          it != transients().end();
00913          ++it )
00914         if( (*it)->hasTransientInternal( cl, indirect, set ))
00915             return true;
00916     return false;
00917     }
00918 
00919 ClientList Client::mainClients() const
00920     {
00921     if( !isTransient())
00922         return ClientList();
00923     if( transientFor() != NULL )
00924         return ClientList() << const_cast< Client* >( transientFor());
00925     ClientList result;
00926     for( ClientList::ConstIterator it = group()->members().begin();
00927          it != group()->members().end();
00928          ++it )
00929         if((*it)->hasTransient( this, false ))
00930             result.append( *it );
00931     return result;
00932     }
00933 
00934 Client* Client::findModal()
00935     {
00936     for( ClientList::ConstIterator it = transients().begin();
00937          it != transients().end();
00938          ++it )
00939         if( Client* ret = (*it)->findModal())
00940             return ret;
00941     if( isModal())
00942         return this;
00943     return NULL;
00944     }
00945 
00946 // Client::window_group only holds the contents of the hint,
00947 // but it should be used only to find the group, not for anything else
00948 // Argument is only when some specific group needs to be set.
00949 void Client::checkGroup( Group* set_group, bool force )
00950     {
00951     TRANSIENCY_CHECK( this );
00952     Group* old_group = in_group;
00953     if( old_group != NULL )
00954         old_group->ref(); // turn off automatic deleting
00955     if( set_group != NULL )
00956         {
00957         if( set_group != in_group )
00958             {
00959             if( in_group != NULL )
00960                 in_group->removeMember( this );
00961             in_group = set_group;
00962             in_group->addMember( this );
00963             }
00964         }
00965     else if( window_group != None )
00966         {
00967         Group* new_group = workspace()->findGroup( window_group );
00968         if( transientFor() != NULL && transientFor()->group() != new_group )
00969             { // move the window to the right group (e.g. a dialog provided
00970               // by different app, but transient for this one, so make it part of that group)
00971             new_group = transientFor()->group();
00972             }
00973         if( new_group == NULL ) // doesn't exist yet
00974             new_group = new Group( window_group, workspace());
00975         if( new_group != in_group )
00976             {
00977             if( in_group != NULL )
00978                 in_group->removeMember( this );
00979             in_group = new_group;
00980             in_group->addMember( this );
00981             }
00982         }
00983     else
00984         {
00985         if( transientFor() != NULL )
00986             { // doesn't have window group set, but is transient for something
00987           // so make it part of that group
00988             Group* new_group = transientFor()->group();
00989             if( new_group != in_group )
00990                 {
00991                 if( in_group != NULL )
00992                     in_group->removeMember( this );
00993                 in_group = transientFor()->group();
00994                 in_group->addMember( this );
00995                 }
00996             }
00997         else if( groupTransient())
00998             { // group transient which actually doesn't have a group :(
00999               // try creating group with other windows with the same client leader
01000             Group* new_group = workspace()->findClientLeaderGroup( this );
01001             if( new_group == NULL )
01002                 new_group = new Group( None, workspace());
01003             if( new_group != in_group )
01004                 {
01005                 if( in_group != NULL )
01006                     in_group->removeMember( this );
01007                 in_group = new_group;
01008                 in_group->addMember( this );
01009                 }
01010             }
01011         else // Not transient without a group, put it in its client leader group.
01012             { // This might be stupid if grouping was used for e.g. taskbar grouping
01013               // or minimizing together the whole group, but as long as its used
01014               // only for dialogs it's better to keep windows from one app in one group.
01015             Group* new_group = workspace()->findClientLeaderGroup( this );
01016             if( in_group != NULL && in_group != new_group )
01017                 {
01018                 in_group->removeMember( this );            
01019                 in_group = NULL;
01020                 }
01021             if( new_group == NULL )
01022                 new_group = new Group( None, workspace() );
01023             if( in_group != new_group )
01024                 {
01025                 in_group = new_group;
01026                 in_group->addMember( this );
01027                 }
01028             }
01029         }
01030     if( in_group != old_group || force )
01031         {
01032         for( ClientList::Iterator it = transients_list.begin();
01033              it != transients_list.end();
01034              )
01035             { // group transients in the old group are no longer transient for it
01036             if( (*it)->groupTransient() && (*it)->group() != group())
01037                 it = transients_list.remove( it );
01038             else
01039                 ++it;
01040             }
01041         if( groupTransient())
01042             {
01043             // no longer transient for ones in the old group
01044             if( old_group != NULL )
01045                 {
01046                 for( ClientList::ConstIterator it = old_group->members().begin();
01047                      it != old_group->members().end();
01048                      ++it )
01049                     (*it)->removeTransient( this );
01050                 }
01051             // and make transient for all in the new group
01052             for( ClientList::ConstIterator it = group()->members().begin();
01053                  it != group()->members().end();
01054                  ++it )
01055                 {
01056                 if( *it == this )
01057                     break; // this means the window is only transient for windows mapped before it
01058                 (*it)->addTransient( this );
01059                 }
01060             }
01061         // group transient splashscreens should be transient even for windows
01062         // in group mapped later
01063         for( ClientList::ConstIterator it = group()->members().begin();
01064              it != group()->members().end();
01065              ++it )
01066             {
01067             if( !(*it)->isSplash())
01068                 continue;
01069             if( !(*it)->groupTransient())
01070                 continue;
01071             if( *it == this || hasTransient( *it, true )) // TODO indirect?
01072                 continue;
01073             addTransient( *it );
01074         }
01075         }
01076     if( old_group != NULL )
01077         old_group->deref(); // can be now deleted if empty
01078     checkGroupTransients();
01079     checkActiveModal();
01080     workspace()->updateClientLayer( this );
01081     }
01082 
01083 // used by Workspace::findClientLeaderGroup()
01084 void Client::changeClientLeaderGroup( Group* gr )
01085     {
01086     // transientFor() != NULL are in the group of their mainwindow, so keep them there
01087     if( transientFor() != NULL )
01088         return;
01089     // also don't change the group for window which have group set
01090     if( window_group )
01091         return;
01092     checkGroup( gr ); // change group
01093     }
01094 
01095 bool Client::check_active_modal = false;
01096 
01097 void Client::checkActiveModal()
01098     {
01099     // if the active window got new modal transient, activate it.
01100     // cannot be done in AddTransient(), because there may temporarily
01101     // exist loops, breaking findModal
01102     Client* check_modal = workspace()->mostRecentlyActivatedClient();
01103     if( check_modal != NULL && check_modal->check_active_modal )
01104         {
01105         Client* new_modal = check_modal->findModal();
01106         if( new_modal != NULL && new_modal != check_modal )
01107             {
01108             if( !new_modal->isManaged())
01109                 return; // postpone check until end of manage()
01110             workspace()->activateClient( new_modal );
01111             }
01112         check_modal->check_active_modal = false;
01113         }
01114     }
01115 
01116 } // namespace

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.6.3
This website is maintained by Timothy Pearson.