diff options
Diffstat (limited to 'kwin/group.cpp')
-rw-r--r-- | kwin/group.cpp | 1118 |
1 files changed, 0 insertions, 1118 deletions
diff --git a/kwin/group.cpp b/kwin/group.cpp deleted file mode 100644 index 8d5bd7246..000000000 --- a/kwin/group.cpp +++ /dev/null @@ -1,1118 +0,0 @@ -/***************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> -Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> - -You can Freely distribute this program under the GNU General Public -License. See the file "COPYING" for the exact licensing terms. -******************************************************************/ - -/* - - This file contains things relevant to window grouping. - -*/ - -//#define QT_CLEAN_NAMESPACE - -#include "group.h" - -#include "workspace.h" -#include "client.h" - -#include <assert.h> -#include <kstartupinfo.h> - - -/* - TODO - Rename as many uses of 'transient' as possible (hasTransient->hasSubwindow,etc.), - or I'll get it backwards in half of the cases again. -*/ - -namespace KWinInternal -{ - -/* - Consistency checks for window relations. Since transients are determinated - using Client::transiency_list and main windows are determined using Client::transientFor() - or the group for group transients, these have to match both ways. -*/ -//#define ENABLE_TRANSIENCY_CHECK - -#ifdef NDEBUG -#undef ENABLE_TRANSIENCY_CHECK -#endif - -#ifdef ENABLE_TRANSIENCY_CHECK -static bool transiencyCheckNonExistent = false; - -bool performTransiencyCheck() - { - bool ret = true; - ClientList clients = Workspace::self()->clients; - for( ClientList::ConstIterator it1 = clients.begin(); - it1 != clients.end(); - ++it1 ) - { - if( (*it1)->deleting ) - continue; - if( (*it1)->in_group == NULL ) - { - kdDebug() << "TC: " << *it1 << " in not in a group" << endl; - ret = false; - } - else if( !(*it1)->in_group->members().contains( *it1 )) - { - kdDebug() << "TC: " << *it1 << " has a group " << (*it1)->in_group << " but group does not contain it" << endl; - ret = false; - } - if( !(*it1)->isTransient()) - { - if( !(*it1)->mainClients().isEmpty()) - { - kdDebug() << "TC: " << *it1 << " is not transient, has main clients:" << (*it1)->mainClients() << endl; - ret = false; - } - } - else - { - ClientList mains = (*it1)->mainClients(); - for( ClientList::ConstIterator it2 = mains.begin(); - it2 != mains.end(); - ++it2 ) - { - if( transiencyCheckNonExistent - && !Workspace::self()->clients.contains( *it2 ) - && !Workspace::self()->desktops.contains( *it2 )) - { - kdDebug() << "TC:" << *it1 << " has non-existent main client " << endl; - kdDebug() << "TC2:" << *it2 << endl; // this may crash - ret = false; - continue; - } - if( !(*it2)->transients_list.contains( *it1 )) - { - kdDebug() << "TC:" << *it1 << " has main client " << *it2 << " but main client does not have it as a transient" << endl; - ret = false; - } - } - } - ClientList trans = (*it1)->transients_list; - for( ClientList::ConstIterator it2 = trans.begin(); - it2 != trans.end(); - ++it2 ) - { - if( transiencyCheckNonExistent - && !Workspace::self()->clients.contains( *it2 ) - && !Workspace::self()->desktops.contains( *it2 )) - { - kdDebug() << "TC:" << *it1 << " has non-existent transient " << endl; - kdDebug() << "TC2:" << *it2 << endl; // this may crash - ret = false; - continue; - } - if( !(*it2)->mainClients().contains( *it1 )) - { - kdDebug() << "TC:" << *it1 << " has transient " << *it2 << " but transient does not have it as a main client" << endl; - ret = false; - } - } - } - GroupList groups = Workspace::self()->groups; - for( GroupList::ConstIterator it1 = groups.begin(); - it1 != groups.end(); - ++it1 ) - { - ClientList members = (*it1)->members(); - for( ClientList::ConstIterator it2 = members.begin(); - it2 != members.end(); - ++it2 ) - { - if( (*it2)->in_group != *it1 ) - { - kdDebug() << "TC: Group " << *it1 << " contains client " << *it2 << " but client is not in that group" << endl; - ret = false; - } - } - } - return ret; - } - -static TQString transiencyCheckStartBt; -static const Client* transiencyCheckClient; -static int transiencyCheck = 0; - -static void startTransiencyCheck( const TQString& bt, const Client* c, bool ne ) - { - if( ++transiencyCheck == 1 ) - { - transiencyCheckStartBt = bt; - transiencyCheckClient = c; - } - if( ne ) - transiencyCheckNonExistent = true; - } -static void checkTransiency() - { - if( --transiencyCheck == 0 ) - { - if( !performTransiencyCheck()) - { - kdDebug() << "BT:" << transiencyCheckStartBt << endl; - kdDebug() << "CLIENT:" << transiencyCheckClient << endl; - assert( false ); - } - transiencyCheckNonExistent = false; - } - } -class TransiencyChecker - { - public: - TransiencyChecker( const TQString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); } - ~TransiencyChecker() { checkTransiency(); } - }; - -void checkNonExistentClients() - { - startTransiencyCheck( kdBacktrace(), NULL, true ); - checkTransiency(); - } - -#define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c ) - -#else - -#define TRANSIENCY_CHECK( c ) - -void checkNonExistentClients() - { - } - -#endif - -//******************************************** -// Group -//******************************************** - -Group::Group( Window leader_P, Workspace* workspace_P ) - : leader_client( NULL ), - leader_wid( leader_P ), - _workspace( workspace_P ), - leader_info( NULL ), - user_time( -1U ), - refcount( 0 ) - { - if( leader_P != None ) - { - leader_client = workspace_P->findClient( WindowMatchPredicate( leader_P )); - unsigned long properties[ 2 ] = { 0, NET::WM2StartupId }; - leader_info = new NETWinInfo( qt_xdisplay(), leader_P, workspace()->rootWin(), - properties, 2 ); - } - workspace()->addGroup( this, Allowed ); - } - -Group::~Group() - { - delete leader_info; - } - -TQPixmap Group::icon() const - { - if( leader_client != NULL ) - return leader_client->icon(); - else if( leader_wid != None ) - { - TQPixmap ic; - Client::readIcons( leader_wid, &ic, NULL ); - return ic; - } - return TQPixmap(); - } - -TQPixmap Group::miniIcon() const - { - if( leader_client != NULL ) - return leader_client->miniIcon(); - else if( leader_wid != None ) - { - TQPixmap ic; - Client::readIcons( leader_wid, NULL, &ic ); - return ic; - } - return TQPixmap(); - } - -void Group::addMember( Client* member_P ) - { - TRANSIENCY_CHECK( member_P ); - _members.append( member_P ); -// kdDebug() << "GROUPADD:" << this << ":" << member_P << endl; -// kdDebug() << kdBacktrace() << endl; - } - -void Group::removeMember( Client* member_P ) - { - TRANSIENCY_CHECK( member_P ); -// kdDebug() << "GROUPREMOVE:" << this << ":" << member_P << endl; -// kdDebug() << kdBacktrace() << endl; - Q_ASSERT( _members.contains( member_P )); - _members.remove( member_P ); -// there are cases when automatic deleting of groups must be delayed, -// e.g. when removing a member and doing some operation on the possibly -// other members of the group (which would be however deleted already -// if there were no other members) - if( refcount == 0 && _members.isEmpty()) - { - workspace()->removeGroup( this, Allowed ); - delete this; - } - } - -void Group::ref() - { - ++refcount; - } - -void Group::deref() - { - if( --refcount == 0 && _members.isEmpty()) - { - workspace()->removeGroup( this, Allowed ); - delete this; - } - } - -void Group::gotLeader( Client* leader_P ) - { - assert( leader_P->window() == leader_wid ); - leader_client = leader_P; - } - -void Group::lostLeader() - { - assert( !_members.contains( leader_client )); - leader_client = NULL; - if( _members.isEmpty()) - { - workspace()->removeGroup( this, Allowed ); - delete this; - } - } - -void Group::getIcons() - { - // TODO - also needs adding the flag to NETWinInfo - } - -//*************************************** -// Workspace -//*************************************** - -Group* Workspace::findGroup( Window leader ) const - { - assert( leader != None ); - for( GroupList::ConstIterator it = groups.begin(); - it != groups.end(); - ++it ) - if( (*it)->leader() == leader ) - return *it; - return NULL; - } - -// Client is group transient, but has no group set. Try to find -// group with windows with the same client leader. -Group* Workspace::findClientLeaderGroup( const Client* c ) const - { - TRANSIENCY_CHECK( c ); - Group* ret = NULL; - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it ) - { - if( *it == c ) - continue; - if( (*it)->wmClientLeader() == c->wmClientLeader()) - { - if( ret == NULL || ret == (*it)->group()) - ret = (*it)->group(); - else - { - // There are already two groups with the same client leader. - // This most probably means the app uses group transients without - // setting group for its windows. Merging the two groups is a bad - // hack, but there's no really good solution for this case. - ClientList old_group = (*it)->group()->members(); - // old_group autodeletes when being empty - for( unsigned int pos = 0; - pos < old_group.count(); - ++pos ) - { - Client* tmp = old_group[ pos ]; - if( tmp != c ) - tmp->changeClientLeaderGroup( ret ); - } - } - } - } - return ret; - } - -void Workspace::updateMinimizedOfTransients( Client* c ) - { - // if mainwindow is minimized or shaded, minimize transients too - if ( c->isMinimized() || c->isShade() ) - { - for( ClientList::ConstIterator it = c->transients().begin(); - it != c->transients().end(); - ++it ) - { - if( !(*it)->isMinimized() - && !(*it)->isTopMenu() ) // topmenus are not minimized, they're hidden - { - (*it)->minimize( true ); // avoid animation - updateMinimizedOfTransients( (*it) ); - } - } - } - else - { // else unmiminize the transients - for( ClientList::ConstIterator it = c->transients().begin(); - it != c->transients().end(); - ++it ) - { - if( (*it)->isMinimized() - && !(*it)->isTopMenu()) - { - (*it)->unminimize( true ); // avoid animation - updateMinimizedOfTransients( (*it) ); - } - } - } - } - - -/*! - Sets the client \a c's transient windows' on_all_desktops property to \a on_all_desktops. - */ -void Workspace::updateOnAllDesktopsOfTransients( Client* c ) - { - for( ClientList::ConstIterator it = c->transients().begin(); - it != c->transients().end(); - ++it) - { - if( (*it)->isOnAllDesktops() != c->isOnAllDesktops()) - (*it)->setOnAllDesktops( c->isOnAllDesktops()); - } - } - -// A new window has been mapped. Check if it's not a mainwindow for some already existing transient window. -void Workspace::checkTransients( Window w ) - { - TRANSIENCY_CHECK( NULL ); - for( ClientList::ConstIterator it = clients.begin(); - it != clients.end(); - ++it ) - (*it)->checkTransient( w ); - } - - - -//**************************************** -// Client -//**************************************** - -// hacks for broken apps here -// all resource classes are forced to be lowercase -bool Client::resourceMatch( const Client* c1, const Client* c2 ) - { - // xv has "xv" as resource name, and different strings starting with "XV" as resource class - if( tqstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" ) - return tqstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv"; - // Mozilla has "Mozilla" as resource name, and different strings as resource class - if( c1->resourceName() == "mozilla" ) - return c2->resourceName() == "mozilla"; - return c1->resourceClass() == c2->resourceClass(); - } - -bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack ) - { - bool same_app = false; - - // tests that definitely mean they belong together - if( c1 == c2 ) - same_app = true; - else if( c1->isTransient() && c2->hasTransient( c1, true )) - same_app = true; // c1 has c2 as mainwindow - else if( c2->isTransient() && c1->hasTransient( c2, true )) - same_app = true; // c2 has c1 as mainwindow - else if( c1->group() == c2->group()) - same_app = true; // same group - else if( c1->wmClientLeader() == c2->wmClientLeader() - && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), - && c2->wmClientLeader() != c2->window()) // don't use in this test then - same_app = true; // same client leader - - // tests that mean they most probably don't belong together - else if( c1->pid() != c2->pid() - || c1->wmClientMachine( false ) != c2->wmClientMachine( false )) - ; // different processes - else if( c1->wmClientLeader() != c2->wmClientLeader() - && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), - && c2->wmClientLeader() != c2->window()) // don't use in this test then - ; // different client leader - else if( !resourceMatch( c1, c2 )) - ; // different apps - else if( !sameAppWindowRoleMatch( c1, c2, active_hack )) - ; // "different" apps - else if( c1->pid() == 0 || c2->pid() == 0 ) - ; // old apps that don't have _NET_WM_PID, consider them different - // if they weren't found to match above - else - same_app = true; // looks like it's the same app - - return same_app; - } - -// Non-transient windows with window role containing '#' are always -// considered belonging to different applications (unless -// the window role is exactly the same). KMainWindow sets -// window role this way by default, and different KMainWindow -// usually "are" different application from user's point of view. -// This help with no-focus-stealing for e.g. konqy reusing. -// On the other hand, if one of the windows is active, they are -// considered belonging to the same application. This is for -// the cases when opening new mainwindow directly from the application, -// e.g. 'Open New Window' in konqy ( active_hack == true ). -bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ) - { - if( c1->isTransient()) - { - while( c1->transientFor() != NULL ) - c1 = c1->transientFor(); - if( c1->groupTransient()) - return c1->group() == c2->group(); -#if 0 - // if a group transient is in its own group, it didn't possibly have a group, - // and therefore should be considered belonging to the same app like - // all other windows from the same app - || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; -#endif - } - if( c2->isTransient()) - { - while( c2->transientFor() != NULL ) - c2 = c2->transientFor(); - if( c2->groupTransient()) - return c1->group() == c2->group(); -#if 0 - || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; -#endif - } - int pos1 = c1->windowRole().find( '#' ); - int pos2 = c2->windowRole().find( '#' ); - if(( pos1 >= 0 && pos2 >= 0 ) - || - // hacks here - // Mozilla has resourceName() and resourceClass() swapped - c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" ) - { - if( !active_hack ) // without the active hack for focus stealing prevention, - return c1 == c2; // different mainwindows are always different apps - if( !c1->isActive() && !c2->isActive()) - return c1 == c2; - else - return true; - } - return true; - } - -/* - - Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3 - - WM_TRANSIENT_FOR is basically means "this is my mainwindow". - For NET::Unknown windows, transient windows are considered to be NET::Dialog - windows, for compatibility with non-NETWM clients. KWin may adjust the value - of this property in some cases (window pointing to itself or creating a loop, - keeping NET::Splash windows above other windows from the same app, etc.). - - Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after - possibly being adjusted by KWin. Client::transient_for points to the Client - this Client is transient for, or is NULL. If Client::transient_for_id is - poiting to the root window, the window is considered to be transient - for the whole window group, as suggested in NETWM 7.3. - - In the case of group transient window, Client::transient_for is NULL, - and Client::groupTransient() returns true. Such window is treated as - if it were transient for every window in its window group that has been - mapped _before_ it (or, to be exact, was added to the same group before it). - Otherwise two group transients can create loops, which can lead very very - nasty things (bug #67914 and all its dupes). - - Client::original_transient_for_id is the value of the property, which - may be different if Client::transient_for_id if e.g. forcing NET::Splash - to be kept on top of its window group, or when the mainwindow is not mapped - yet, in which case the window is temporarily made group transient, - and when the mainwindow is mapped, transiency is re-evaluated. - - This can get a bit complicated with with e.g. two Konqueror windows created - by the same process. They should ideally appear like two independent applications - to the user. This should be accomplished by all windows in the same process - having the same window group (needs to be changed in Qt at the moment), and - using non-group transients poiting to their relevant mainwindow for toolwindows - etc. KWin should handle both group and non-group transient dialogs well. - - In other words: - - non-transient windows : isTransient() == false - - normal transients : transientFor() != NULL - - group transients : groupTransient() == true - - - list of mainwindows : mainClients() (call once and loop over the result) - - list of transients : transients() - - every window in the group : group()->members() -*/ - -void Client::readTransient() - { - TRANSIENCY_CHECK( this ); - Window new_transient_for_id; - if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id )) - { - original_transient_for_id = new_transient_for_id; - new_transient_for_id = verifyTransientFor( new_transient_for_id, true ); - } - else - { - original_transient_for_id = None; - new_transient_for_id = verifyTransientFor( None, false ); - } - setTransient( new_transient_for_id ); - } - -void Client::setTransient( Window new_transient_for_id ) - { - TRANSIENCY_CHECK( this ); - if( new_transient_for_id != transient_for_id ) - { - removeFromMainClients(); - transient_for = NULL; - transient_for_id = new_transient_for_id; - if( transient_for_id != None && !groupTransient()) - { - transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id )); - assert( transient_for != NULL ); // verifyTransient() had to check this - transient_for->addTransient( this ); - } // checkGroup() will check 'check_active_modal' - checkGroup( NULL, true ); // force, because transiency has changed - if( isTopMenu()) - workspace()->updateCurrentTopMenu(); - workspace()->updateClientLayer( this ); - } - } - -void Client::removeFromMainClients() - { - TRANSIENCY_CHECK( this ); - if( transientFor() != NULL ) - transientFor()->removeTransient( this ); - if( groupTransient()) - { - for( ClientList::ConstIterator it = group()->members().begin(); - it != group()->members().end(); - ++it ) - (*it)->removeTransient( this ); - } - } - -// *sigh* this transiency handling is madness :( -// This one is called when destroying/releasing a window. -// It makes sure this client is removed from all grouping -// related lists. -void Client::cleanGrouping() - { - TRANSIENCY_CHECK( this ); -// kdDebug() << "CLEANGROUPING:" << this << endl; -// for( ClientList::ConstIterator it = group()->members().begin(); -// it != group()->members().end(); -// ++it ) -// kdDebug() << "CL:" << *it << endl; -// ClientList mains; -// mains = mainClients(); -// for( ClientList::ConstIterator it = mains.begin(); -// it != mains.end(); -// ++it ) -// kdDebug() << "MN:" << *it << endl; - removeFromMainClients(); -// kdDebug() << "CLEANGROUPING2:" << this << endl; -// for( ClientList::ConstIterator it = group()->members().begin(); -// it != group()->members().end(); -// ++it ) -// kdDebug() << "CL2:" << *it << endl; -// mains = mainClients(); -// for( ClientList::ConstIterator it = mains.begin(); -// it != mains.end(); -// ++it ) -// kdDebug() << "MN2:" << *it << endl; - for( ClientList::ConstIterator it = transients_list.begin(); - it != transients_list.end(); - ) - { - if( (*it)->transientFor() == this ) - { - ClientList::ConstIterator it2 = it++; - removeTransient( *it2 ); - } - else - ++it; - } -// kdDebug() << "CLEANGROUPING3:" << this << endl; -// for( ClientList::ConstIterator it = group()->members().begin(); -// it != group()->members().end(); -// ++it ) -// kdDebug() << "CL3:" << *it << endl; -// mains = mainClients(); -// for( ClientList::ConstIterator it = mains.begin(); -// it != mains.end(); -// ++it ) -// kdDebug() << "MN3:" << *it << endl; - // HACK - // removeFromMainClients() did remove 'this' from transient - // lists of all group members, but then made windows that - // were transient for 'this' group transient, which again - // added 'this' to those transient lists :( - ClientList group_members = group()->members(); - group()->removeMember( this ); - in_group = NULL; - for( ClientList::ConstIterator it = group_members.begin(); - it != group_members.end(); - ++it ) - (*it)->removeTransient( this ); -// kdDebug() << "CLEANGROUPING4:" << this << endl; -// for( ClientList::ConstIterator it = group_members.begin(); -// it != group_members.end(); -// ++it ) -// kdDebug() << "CL4:" << *it << endl; - } - -// Make sure that no group transient is considered transient -// for a window that is (directly or indirectly) transient for it -// (including another group transients). -// Non-group transients not causing loops are checked in verifyTransientFor(). -void Client::checkGroupTransients() - { - TRANSIENCY_CHECK( this ); - for( ClientList::ConstIterator it1 = group()->members().begin(); - it1 != group()->members().end(); - ++it1 ) - { - if( !(*it1)->groupTransient()) // check all group transients in the group - continue; // TODO optimize to check only the changed ones? - for( ClientList::ConstIterator it2 = group()->members().begin(); - it2 != group()->members().end(); - ++it2 ) // group transients can be transient only for others in the group, - { // so don't make them transient for the ones that are transient for it - if( *it1 == *it2 ) - continue; - for( Client* cl = (*it2)->transientFor(); - cl != NULL; - cl = cl->transientFor()) - { - if( cl == *it1 ) - { // don't use removeTransient(), that would modify *it2 too - (*it2)->transients_list.remove( *it1 ); - continue; - } - } - // if *it1 and *it2 are both group transients, and are transient for each other, - // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later, - // and should be therefore on top of *it1 - // TODO This could possibly be optimized, it also requires hasTransient() to check for loops. - if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true )) - (*it2)->transients_list.remove( *it1 ); - // if there are already windows W1 and W2, W2 being transient for W1, and group transient W3 - // is added, make it transient only for W2, not for W1, because it's already indirectly - // transient for it - the indirect transiency actually shouldn't break anything, - // but it can lead to exponentially expensive operations (#95231) - // TODO this is pretty slow as well - for( ClientList::ConstIterator it3 = group()->members().begin(); - it3 != group()->members().end(); - ++it3 ) - { - if( *it1 == *it2 || *it2 == *it3 || *it1 == *it3 ) - continue; - if( (*it2)->hasTransient( *it1, false ) && (*it3)->hasTransient( *it1, false )) - { - if( (*it2)->hasTransient( *it3, true )) - (*it2)->transients_list.remove( *it1 ); - if( (*it3)->hasTransient( *it2, true )) - (*it3)->transients_list.remove( *it1 ); - } - } - } - } - } - -/*! - Check that the window is not transient for itself, and similar nonsense. - */ -Window Client::verifyTransientFor( Window new_transient_for, bool defined ) - { - Window new_property_value = new_transient_for; - // make sure splashscreens are shown above all their app's windows, even though - // they're in Normal layer - if( isSplash() && new_transient_for == None ) - new_transient_for = workspace()->rootWin(); - if( new_transient_for == None ) - if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window - new_property_value = new_transient_for = workspace()->rootWin(); - else - return None; - if( new_transient_for == window()) // pointing to self - { // also fix the property itself - kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl; - new_property_value = new_transient_for = workspace()->rootWin(); - } -// The transient_for window may be embedded in another application, -// so twin cannot see it. Try to find the managed client for the -// window and fix the transient_for property if possible. - WId before_search = new_transient_for; - while( new_transient_for != None - && new_transient_for != workspace()->rootWin() - && !workspace()->findClient( WindowMatchPredicate( new_transient_for ))) - { - Window root_return, parent_return; - Window* wins = NULL; - unsigned int nwins; - int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins); - if ( wins ) - XFree((void *) wins); - if ( r == 0) - break; - new_transient_for = parent_return; - } - if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for ))) - { - if( new_transient_for != before_search ) - { - kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window " - << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl; - new_property_value = new_transient_for; // also fix the property - } - } - else - new_transient_for = before_search; // nice try -// loop detection -// group transients cannot cause loops, because they're considered transient only for non-transient -// windows in the group - int count = 20; - Window loop_pos = new_transient_for; - while( loop_pos != None && loop_pos != workspace()->rootWin()) - { - Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos )); - if( pos == NULL ) - break; - loop_pos = pos->transient_for_id; - if( --count == 0 || pos == this ) - { - kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl; - new_transient_for = workspace()->rootWin(); - } - } - if( new_transient_for != workspace()->rootWin() - && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL ) - { // it's transient for a specific window, but that window is not mapped - new_transient_for = workspace()->rootWin(); - } - if( new_property_value != original_transient_for_id ) - XSetTransientForHint( qt_xdisplay(), window(), new_property_value ); - return new_transient_for; - } - -void Client::addTransient( Client* cl ) - { - TRANSIENCY_CHECK( this ); - assert( !transients_list.contains( cl )); -// assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients() - assert( cl != this ); - transients_list.append( cl ); - if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal()) - check_active_modal = true; -// kdDebug() << "ADDTRANS:" << this << ":" << cl << endl; -// kdDebug() << kdBacktrace() << endl; -// for( ClientList::ConstIterator it = transients_list.begin(); -// it != transients_list.end(); -// ++it ) -// kdDebug() << "AT:" << (*it) << endl; - } - -void Client::removeTransient( Client* cl ) - { - TRANSIENCY_CHECK( this ); -// kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl; -// kdDebug() << kdBacktrace() << endl; - transients_list.remove( cl ); - // cl is transient for this, but this is going away - // make cl group transient - if( cl->transientFor() == this ) - { - cl->transient_for_id = None; - cl->transient_for = NULL; // SELI -// SELI cl->setTransient( workspace()->rootWin()); - cl->setTransient( None ); - } - } - -// A new window has been mapped. Check if it's not a mainwindow for this already existing window. -void Client::checkTransient( Window w ) - { - TRANSIENCY_CHECK( this ); - if( original_transient_for_id != w ) - return; - w = verifyTransientFor( w, true ); - setTransient( w ); - } - -// returns true if cl is the transient_for window for this client, -// or recursively the transient_for window -bool Client::hasTransient( const Client* cl, bool indirect ) const - { - // checkGroupTransients() uses this to break loops, so hasTransient() must detect them - ConstClientList set; - return hasTransientInternal( cl, indirect, set ); - } - -bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const - { - if( cl->transientFor() != NULL ) - { - if( cl->transientFor() == this ) - return true; - if( !indirect ) - return false; - if( set.contains( cl )) - return false; - set.append( cl ); - return hasTransientInternal( cl->transientFor(), indirect, set ); - } - if( !cl->isTransient()) - return false; - if( group() != cl->group()) - return false; - // cl is group transient, search from top - if( transients().contains( const_cast< Client* >( cl ))) - return true; - if( !indirect ) - return false; - if( set.contains( this )) - return false; - set.append( this ); - for( ClientList::ConstIterator it = transients().begin(); - it != transients().end(); - ++it ) - if( (*it)->hasTransientInternal( cl, indirect, set )) - return true; - return false; - } - -ClientList Client::mainClients() const - { - if( !isTransient()) - return ClientList(); - if( transientFor() != NULL ) - return ClientList() << const_cast< Client* >( transientFor()); - ClientList result; - for( ClientList::ConstIterator it = group()->members().begin(); - it != group()->members().end(); - ++it ) - if((*it)->hasTransient( this, false )) - result.append( *it ); - return result; - } - -Client* Client::findModal() - { - for( ClientList::ConstIterator it = transients().begin(); - it != transients().end(); - ++it ) - if( Client* ret = (*it)->findModal()) - return ret; - if( isModal()) - return this; - return NULL; - } - -// Client::window_group only holds the contents of the hint, -// but it should be used only to find the group, not for anything else -// Argument is only when some specific group needs to be set. -void Client::checkGroup( Group* set_group, bool force ) - { - TRANSIENCY_CHECK( this ); - Group* old_group = in_group; - if( old_group != NULL ) - old_group->ref(); // turn off automatic deleting - if( set_group != NULL ) - { - if( set_group != in_group ) - { - if( in_group != NULL ) - in_group->removeMember( this ); - in_group = set_group; - in_group->addMember( this ); - } - } - else if( window_group != None ) - { - Group* new_group = workspace()->findGroup( window_group ); - if( transientFor() != NULL && transientFor()->group() != new_group ) - { // move the window to the right group (e.g. a dialog provided - // by different app, but transient for this one, so make it part of that group) - new_group = transientFor()->group(); - } - if( new_group == NULL ) // doesn't exist yet - new_group = new Group( window_group, workspace()); - if( new_group != in_group ) - { - if( in_group != NULL ) - in_group->removeMember( this ); - in_group = new_group; - in_group->addMember( this ); - } - } - else - { - if( transientFor() != NULL ) - { // doesn't have window group set, but is transient for something - // so make it part of that group - Group* new_group = transientFor()->group(); - if( new_group != in_group ) - { - if( in_group != NULL ) - in_group->removeMember( this ); - in_group = transientFor()->group(); - in_group->addMember( this ); - } - } - else if( groupTransient()) - { // group transient which actually doesn't have a group :( - // try creating group with other windows with the same client leader - Group* new_group = workspace()->findClientLeaderGroup( this ); - if( new_group == NULL ) - new_group = new Group( None, workspace()); - if( new_group != in_group ) - { - if( in_group != NULL ) - in_group->removeMember( this ); - in_group = new_group; - in_group->addMember( this ); - } - } - else // Not transient without a group, put it in its client leader group. - { // This might be stupid if grouping was used for e.g. taskbar grouping - // or minimizing together the whole group, but as long as its used - // only for dialogs it's better to keep windows from one app in one group. - Group* new_group = workspace()->findClientLeaderGroup( this ); - if( in_group != NULL && in_group != new_group ) - { - in_group->removeMember( this ); - in_group = NULL; - } - if( new_group == NULL ) - new_group = new Group( None, workspace() ); - if( in_group != new_group ) - { - in_group = new_group; - in_group->addMember( this ); - } - } - } - if( in_group != old_group || force ) - { - for( ClientList::Iterator it = transients_list.begin(); - it != transients_list.end(); - ) - { // group transients in the old group are no longer transient for it - if( (*it)->groupTransient() && (*it)->group() != group()) - it = transients_list.remove( it ); - else - ++it; - } - if( groupTransient()) - { - // no longer transient for ones in the old group - if( old_group != NULL ) - { - for( ClientList::ConstIterator it = old_group->members().begin(); - it != old_group->members().end(); - ++it ) - (*it)->removeTransient( this ); - } - // and make transient for all in the new group - for( ClientList::ConstIterator it = group()->members().begin(); - it != group()->members().end(); - ++it ) - { - if( *it == this ) - break; // this means the window is only transient for windows mapped before it - (*it)->addTransient( this ); - } - } - // group transient splashscreens should be transient even for windows - // in group mapped later - for( ClientList::ConstIterator it = group()->members().begin(); - it != group()->members().end(); - ++it ) - { - if( !(*it)->isSplash()) - continue; - if( !(*it)->groupTransient()) - continue; - if( *it == this || hasTransient( *it, true )) // TODO indirect? - continue; - addTransient( *it ); - } - } - if( old_group != NULL ) - old_group->deref(); // can be now deleted if empty - checkGroupTransients(); - checkActiveModal(); - workspace()->updateClientLayer( this ); - } - -// used by Workspace::findClientLeaderGroup() -void Client::changeClientLeaderGroup( Group* gr ) - { - // transientFor() != NULL are in the group of their mainwindow, so keep them there - if( transientFor() != NULL ) - return; - // also don't change the group for window which have group set - if( window_group ) - return; - checkGroup( gr ); // change group - } - -bool Client::check_active_modal = false; - -void Client::checkActiveModal() - { - // if the active window got new modal transient, activate it. - // cannot be done in AddTransient(), because there may temporarily - // exist loops, breaking findModal - Client* check_modal = workspace()->mostRecentlyActivatedClient(); - if( check_modal != NULL && check_modal->check_active_modal ) - { - Client* new_modal = check_modal->findModal(); - if( new_modal != NULL && new_modal != check_modal ) - { - if( !new_modal->isManaged()) - return; // postpone check until end of manage() - workspace()->activateClient( new_modal ); - } - check_modal->check_active_modal = false; - } - } - -} // namespace |