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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 13 21:47:05 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003