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 for( ClientList::ConstIterator it = clients.begin(); 00152 it != clients.end(); 00153 ++it ) 00154 { 00155 if( *it == c ) 00156 continue; 00157 if( (*it)->wmClientLeader() == c->wmClientLeader()) 00158 return (*it)->group(); 00159 } 00160 return NULL; 00161 } 00162 00163 void Workspace::updateMinimizedOfTransients( Client* c ) 00164 { 00165 // if mainwindow is minimized or shaded, minimize transients too 00166 if ( c->isMinimized() || c->isShade() ) 00167 { 00168 for( ClientList::ConstIterator it = c->transients().begin(); 00169 it != c->transients().end(); 00170 ++it ) 00171 { 00172 if( !(*it)->isMinimized() 00173 && !(*it)->isShade() 00174 && !(*it)->isTopMenu() ) // topmenus are not minimized, they're hidden 00175 { 00176 (*it)->minimize(); 00177 updateMinimizedOfTransients( (*it) ); 00178 } 00179 } 00180 } 00181 else 00182 { // else unmiminize the transients 00183 for( ClientList::ConstIterator it = c->transients().begin(); 00184 it != c->transients().end(); 00185 ++it ) 00186 { 00187 if( (*it)->isMinimized() 00188 && !(*it)->isTopMenu()) 00189 { 00190 (*it)->unminimize(); 00191 updateMinimizedOfTransients( (*it) ); 00192 } 00193 } 00194 } 00195 } 00196 00197 00201 void Workspace::updateOnAllDesktopsOfTransients( Client* c ) 00202 { 00203 for( ClientList::ConstIterator it = c->transients().begin(); 00204 it != c->transients().end(); 00205 ++it) 00206 { 00207 if( (*it)->isOnAllDesktops() != c->isOnAllDesktops()) 00208 (*it)->setOnAllDesktops( c->isOnAllDesktops()); 00209 } 00210 } 00211 00212 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window. 00213 void Workspace::checkTransients( Window w ) 00214 { 00215 for( ClientList::ConstIterator it = clients.begin(); 00216 it != clients.end(); 00217 ++it ) 00218 (*it)->checkTransient( w ); 00219 } 00220 00221 00222 00223 //**************************************** 00224 // Client 00225 //**************************************** 00226 00227 // hacks for broken apps here 00228 // all resource classes are forced to be lowercase 00229 bool Client::resourceMatch( const Client* c1, const Client* c2 ) 00230 { 00231 // xv has "xv" as resource name, and different strings starting with "XV" as resource class 00232 if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" ) 00233 return qstrncmp( c2->resourceClass(), "xv", 2 ) == 0 && c2->resourceName() == "xv"; 00234 // Mozilla has "Mozilla" as resource name, and different strings as resource class 00235 if( c1->resourceName() == "mozilla" ) 00236 return c2->resourceName() == "mozilla"; 00237 return c1->resourceClass() == c2->resourceClass(); 00238 } 00239 00240 bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack ) 00241 { 00242 bool same_app = false; 00243 if( c1 == c2 ) 00244 same_app = true; 00245 else if( c1->isTransient() && c2->hasTransient( c1, true )) 00246 same_app = true; // c1 has c2 as mainwindow 00247 else if( c2->isTransient() && c1->hasTransient( c2, true )) 00248 same_app = true; // c2 has c1 as mainwindow 00249 else if( c1->pid() != c2->pid() 00250 || c1->wmClientMachine() != c2->wmClientMachine()) 00251 ; // different processes 00252 else if( c1->wmClientLeader() != c2->wmClientLeader() 00253 && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), 00254 && c2->wmClientLeader() != c2->window()) // don't use in this test then 00255 ; // different client leader 00256 else if( !resourceMatch( c1, c2 )) 00257 ; // different apps 00258 else if( !sameAppWindowRoleMatch( c1, c2, active_hack )) 00259 ; // "different" apps 00260 else if( c1->wmClientLeader() == c2->wmClientLeader() 00261 && c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(), 00262 && c2->wmClientLeader() != c2->window()) // don't use in this test then 00263 same_app = true; // same client leader 00264 else if( c1->group() == c2->group()) 00265 same_app = true; // same group 00266 else if( c1->pid() == 0 || c2->pid() == 0 ) 00267 ; // old apps that don't have _NET_WM_PID, consider them different 00268 // if they weren't found to match above 00269 else 00270 same_app = true; // looks like it's the same app 00271 return same_app; 00272 } 00273 00274 // Non-transient windows with window role containing '#' are always 00275 // considered belonging to different applications (unless 00276 // the window role is exactly the same). KMainWindow sets 00277 // window role this way by default, and different KMainWindow 00278 // usually "are" different application from user's point of view. 00279 // This help with no-focus-stealing for e.g. konqy reusing. 00280 // On the other hand, if one of the windows is active, they are 00281 // considered belonging to the same application. This is for 00282 // the cases when opening new mainwindow directly from the application, 00283 // e.g. 'Open New Window' in konqy ( active_hack == true ). 00284 bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ) 00285 { 00286 if( c1->isTransient()) 00287 { 00288 while( c1->transientFor() != NULL ) 00289 c1 = c1->transientFor(); 00290 if( c1->groupTransient()) 00291 return c1->group() == c2->group(); 00292 #if 0 00293 // if a group transient is in its own group, it didn't possibly have a group, 00294 // and therefore should be considered belonging to the same app like 00295 // all other windows from the same app 00296 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; 00297 #endif 00298 } 00299 if( c2->isTransient()) 00300 { 00301 while( c2->transientFor() != NULL ) 00302 c2 = c2->transientFor(); 00303 if( c2->groupTransient()) 00304 return c1->group() == c2->group(); 00305 #if 0 00306 || c1->group()->leaderClient() == c1 || c2->group()->leaderClient() == c2; 00307 #endif 00308 } 00309 int pos1 = c1->windowRole().find( '#' ); 00310 int pos2 = c2->windowRole().find( '#' ); 00311 if(( pos1 >= 0 && pos2 >= 0 ) 00312 || 00313 // hacks here 00314 // Mozilla has resourceName() and resourceClass() swapped 00315 c1->resourceName() == "mozilla" && c2->resourceName() == "mozilla" ) 00316 { 00317 if( !active_hack ) // without the active hack for focus stealing prevention, 00318 return c1 == c2; // different mainwindows are always different apps 00319 if( !c1->isActive() && !c2->isActive()) 00320 return c1 == c2; 00321 else 00322 return true; 00323 } 00324 return true; 00325 } 00326 00327 /* 00328 00329 Transiency stuff: ICCCM 4.1.2.6, NETWM 7.3 00330 00331 WM_TRANSIENT_FOR is basically means "this is my mainwindow". 00332 For NET::Unknown windows, transient windows are considered to be NET::Dialog 00333 windows, for compatibility with non-NETWM clients. KWin may adjust the value 00334 of this property in some cases (window pointing to itself or creating a loop, 00335 keeping NET::Splash windows above other windows from the same app, etc.). 00336 00337 Client::transient_for_id is the value of the WM_TRANSIENT_FOR property, after 00338 possibly being adjusted by KWin. Client::transient_for points to the Client 00339 this Client is transient for, or is NULL. If Client::transient_for_id is 00340 poiting to the root window, the window is considered to be transient 00341 for the whole window group, as suggested in NETWM 7.3. 00342 00343 In the case of group transient window, Client::transient_for is NULL, 00344 and Client::groupTransient() returns true. Such window is treated as 00345 if it were transient for every window in its window group that has been 00346 mapped _before_ it (or, to be exact, was added to the same group before it). 00347 Otherwise two group transients can create loops, which can lead very very 00348 nasty things (bug #67914 and all its dupes). 00349 00350 Client::original_transient_for_id is the value of the property, which 00351 may be different if Client::transient_for_id if e.g. forcing NET::Splash 00352 to be kept on top of its window group, or when the mainwindow is not mapped 00353 yet, in which case the window is temporarily made group transient, 00354 and when the mainwindow is mapped, transiency is re-evaluated. 00355 00356 This can get a bit complicated with with e.g. two Konqueror windows created 00357 by the same process. They should ideally appear like two independent applications 00358 to the user. This should be accomplished by all windows in the same process 00359 having the same window group (needs to be changed in Qt at the moment), and 00360 using non-group transients poiting to their relevant mainwindow for toolwindows 00361 etc. KWin should handle both group and non-group transient dialogs well. 00362 00363 In other words: 00364 - non-transient windows : isTransient() == false 00365 - normal transients : transientFor() != NULL 00366 - group transients : groupTransient() == true 00367 00368 - list of mainwindows : mainClients() (call once and loop over the result) 00369 - list of transients : transients() 00370 - every window in the group : group()->members() 00371 */ 00372 00373 void Client::readTransient() 00374 { 00375 Window new_transient_for_id; 00376 if( XGetTransientForHint( qt_xdisplay(), window(), &new_transient_for_id )) 00377 { 00378 original_transient_for_id = new_transient_for_id; 00379 new_transient_for_id = verifyTransientFor( new_transient_for_id, true ); 00380 } 00381 else 00382 { 00383 original_transient_for_id = None; 00384 new_transient_for_id = verifyTransientFor( None, false ); 00385 } 00386 setTransient( new_transient_for_id ); 00387 } 00388 00389 void Client::setTransient( Window new_transient_for_id ) 00390 { 00391 if( new_transient_for_id != transient_for_id ) 00392 { 00393 removeFromMainClients(); 00394 transient_for = NULL; 00395 transient_for_id = new_transient_for_id; 00396 if( transient_for_id != None && !groupTransient()) 00397 { 00398 transient_for = workspace()->findClient( WindowMatchPredicate( transient_for_id )); 00399 assert( transient_for != NULL ); // verifyTransient() had to check this 00400 transient_for->addTransient( this ); 00401 } 00402 checkGroup(); // first check group 00403 if( groupTransient()) 00404 { // and make transient for all in the group 00405 for( ClientList::ConstIterator it = group()->members().begin(); 00406 it != group()->members().end(); 00407 ++it ) 00408 { 00409 if( *it == this ) 00410 break; // this means the window is only transient for windows mapped before it 00411 (*it)->addTransient( this ); 00412 } 00413 } 00414 checkGroupTransients(); 00415 workspace()->updateClientLayer( this ); 00416 } 00417 } 00418 00419 void Client::removeFromMainClients() 00420 { 00421 if( transientFor() != NULL ) 00422 transientFor()->removeTransient( this ); 00423 if( groupTransient()) 00424 { 00425 for( ClientList::ConstIterator it = group()->members().begin(); 00426 it != group()->members().end(); 00427 ++it ) 00428 (*it)->removeTransient( this ); 00429 } 00430 } 00431 00432 // *sigh* this transiency handling is madness :( 00433 // This one is called when destroying/releasing a window. 00434 // It makes sure this client is removed from all grouping 00435 // related lists. 00436 void Client::cleanGrouping() 00437 { 00438 // kdDebug() << "CLEANGROUPING:" << this << endl; 00439 // for( ClientList::ConstIterator it = group()->members().begin(); 00440 // it != group()->members().end(); 00441 // ++it ) 00442 // kdDebug() << "CL:" << *it << endl; 00443 // ClientList mains; 00444 // mains = mainClients(); 00445 // for( ClientList::ConstIterator it = mains.begin(); 00446 // it != mains.end(); 00447 // ++it ) 00448 // kdDebug() << "MN:" << *it << endl; 00449 removeFromMainClients(); 00450 // kdDebug() << "CLEANGROUPING2:" << this << endl; 00451 // for( ClientList::ConstIterator it = group()->members().begin(); 00452 // it != group()->members().end(); 00453 // ++it ) 00454 // kdDebug() << "CL2:" << *it << endl; 00455 // mains = mainClients(); 00456 // for( ClientList::ConstIterator it = mains.begin(); 00457 // it != mains.end(); 00458 // ++it ) 00459 // kdDebug() << "MN2:" << *it << endl; 00460 for( ClientList::ConstIterator it = transients_list.begin(); 00461 it != transients_list.end(); 00462 ) 00463 { 00464 if( (*it)->transientFor() == this ) 00465 { 00466 ClientList::ConstIterator it2 = it++; 00467 removeTransient( *it2 ); 00468 } 00469 else 00470 ++it; 00471 } 00472 // kdDebug() << "CLEANGROUPING3:" << this << endl; 00473 // for( ClientList::ConstIterator it = group()->members().begin(); 00474 // it != group()->members().end(); 00475 // ++it ) 00476 // kdDebug() << "CL3:" << *it << endl; 00477 // mains = mainClients(); 00478 // for( ClientList::ConstIterator it = mains.begin(); 00479 // it != mains.end(); 00480 // ++it ) 00481 // kdDebug() << "MN3:" << *it << endl; 00482 // HACK 00483 // removeFromMainClients() did remove 'this' from transient 00484 // lists of all group members, but then made windows that 00485 // were transient for 'this' group transient, which again 00486 // added 'this' to those transient lists :( 00487 ClientList group_members = group()->members(); 00488 group()->removeMember( this ); 00489 in_group = NULL; 00490 for( ClientList::ConstIterator it = group_members.begin(); 00491 it != group_members.end(); 00492 ++it ) 00493 (*it)->removeTransient( this ); 00494 // kdDebug() << "CLEANGROUPING4:" << this << endl; 00495 // for( ClientList::ConstIterator it = group_members.begin(); 00496 // it != group_members.end(); 00497 // ++it ) 00498 // kdDebug() << "CL4:" << *it << endl; 00499 } 00500 00501 // Make sure that no group transient is considered transient 00502 // for a window that is (directly or indirectly) transient for it 00503 // (including another group transients). 00504 // Non-group transients not causing loops are checked in verifyTransientFor(). 00505 void Client::checkGroupTransients() 00506 { 00507 for( ClientList::ConstIterator it1 = group()->members().begin(); 00508 it1 != group()->members().end(); 00509 ++it1 ) 00510 { 00511 if( !(*it1)->groupTransient()) // check all group transients in the group 00512 continue; // TODO optimize to check only the changed ones? 00513 for( ClientList::ConstIterator it2 = group()->members().begin(); 00514 it2 != group()->members().end(); 00515 ++it2 ) // group transients can be transient only for others in the group, 00516 { // so don't make them transient for the ones that are transient for it 00517 if( *it1 == *it2 ) 00518 continue; 00519 for( Client* cl = (*it2)->transientFor(); 00520 cl != NULL; 00521 cl = cl->transientFor()) 00522 { 00523 if( cl == *it1 ) 00524 { // don't use removeTransient(), that would modify *it2 too 00525 (*it2)->transients_list.remove( *it1 ); 00526 continue; 00527 } 00528 } 00529 // if *it1 and *it2 are both group transients, and are transient for each other, 00530 // make only *it2 transient for *it1 (i.e. subwindow), as *it2 came later, 00531 // and should be therefore on top of *it1 00532 // TODO This could possibly be optimized, it also requires hasTransient() to check for loops. 00533 if( (*it2)->groupTransient() && (*it1)->hasTransient( *it2, true ) && (*it2)->hasTransient( *it1, true )) 00534 (*it2)->transients_list.remove( *it1 ); 00535 } 00536 } 00537 } 00538 00542 Window Client::verifyTransientFor( Window new_transient_for, bool defined ) 00543 { 00544 Window new_property_value = new_transient_for; 00545 // make sure splashscreens are shown above all their app's windows, even though 00546 // they're in Normal layer 00547 if( isSplash() && new_transient_for == None ) 00548 new_transient_for = workspace()->rootWin(); 00549 if( new_transient_for == None ) 00550 if( defined ) // sometimes WM_TRANSIENT_FOR is set to None, instead of root window 00551 new_property_value = new_transient_for = workspace()->rootWin(); 00552 else 00553 return None; 00554 if( new_transient_for == window()) // pointing to self 00555 { // also fix the property itself 00556 kdWarning( 1216 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to itself." << endl; 00557 new_property_value = new_transient_for = workspace()->rootWin(); 00558 } 00559 // The transient_for window may be embedded in another application, 00560 // so kwin cannot see it. Try to find the managed client for the 00561 // window and fix the transient_for property if possible. 00562 WId before_search = new_transient_for; 00563 while( new_transient_for != None 00564 && new_transient_for != workspace()->rootWin() 00565 && !workspace()->findClient( WindowMatchPredicate( new_transient_for ))) 00566 { 00567 Window root_return, parent_return; 00568 Window* wins = NULL; 00569 unsigned int nwins; 00570 int r = XQueryTree(qt_xdisplay(), new_transient_for, &root_return, &parent_return, &wins, &nwins); 00571 if ( wins ) 00572 XFree((void *) wins); 00573 if ( r == 0) 00574 break; 00575 new_transient_for = parent_return; 00576 } 00577 if( Client* new_transient_for_client = workspace()->findClient( WindowMatchPredicate( new_transient_for ))) 00578 { 00579 if( new_transient_for != before_search ) 00580 { 00581 kdDebug( 1212 ) << "Client " << this << " has WM_TRANSIENT_FOR poiting to non-toplevel window " 00582 << before_search << ", child of " << new_transient_for_client << ", adjusting." << endl; 00583 new_property_value = new_transient_for; // also fix the property 00584 } 00585 } 00586 else 00587 new_transient_for = before_search; // nice try 00588 // loop detection 00589 // group transients cannot cause loops, because they're considered transient only for non-transient 00590 // windows in the group 00591 int count = 20; 00592 Window loop_pos = new_transient_for; 00593 while( loop_pos != None && loop_pos != workspace()->rootWin()) 00594 { 00595 Client* pos = workspace()->findClient( WindowMatchPredicate( loop_pos )); 00596 if( pos == NULL ) 00597 break; 00598 loop_pos = pos->transient_for_id; 00599 if( --count == 0 ) 00600 { 00601 kdWarning( 1216 ) << "Client " << this << " caused WM_TRANSIENT_FOR loop." << endl; 00602 new_transient_for = workspace()->rootWin(); 00603 } 00604 } 00605 if( new_transient_for != workspace()->rootWin() 00606 && workspace()->findClient( WindowMatchPredicate( new_transient_for )) == NULL ) 00607 { // it's transient for a specific window, but that window is not mapped 00608 new_transient_for = workspace()->rootWin(); 00609 } 00610 if( new_property_value != original_transient_for_id ) 00611 XSetTransientForHint( qt_xdisplay(), window(), new_property_value ); 00612 return new_transient_for; 00613 } 00614 00615 void Client::addTransient( Client* cl ) 00616 { 00617 assert( !transients_list.contains( cl )); 00618 // assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients() 00619 assert( cl != this ); 00620 transients_list.append( cl ); 00621 if( workspace()->mostRecentlyActivatedClient() == this && cl->isModal()) 00622 workspace()->activateClient( findModal()); 00623 // kdDebug() << "ADDTRANS:" << this << ":" << cl << endl; 00624 // kdDebug() << kdBacktrace() << endl; 00625 // for( ClientList::ConstIterator it = transients_list.begin(); 00626 // it != transients_list.end(); 00627 // ++it ) 00628 // kdDebug() << "AT:" << (*it) << endl; 00629 } 00630 00631 void Client::removeTransient( Client* cl ) 00632 { 00633 // kdDebug() << "REMOVETRANS:" << this << ":" << cl << endl; 00634 // kdDebug() << kdBacktrace() << endl; 00635 transients_list.remove( cl ); 00636 // cl is transient for this, but this is going away 00637 // make cl group transient 00638 if( cl->transientFor() == this ) 00639 { 00640 cl->transient_for_id = None; 00641 cl->transient_for = NULL; // SELI 00642 // SELI cl->setTransient( workspace()->rootWin()); 00643 cl->setTransient( None ); 00644 } 00645 } 00646 00647 // A new window has been mapped. Check if it's not a mainwindow for this already existing window. 00648 void Client::checkTransient( Window w ) 00649 { 00650 if( original_transient_for_id != w ) 00651 return; 00652 w = verifyTransientFor( w, true ); 00653 setTransient( w ); 00654 } 00655 00656 // returns true if cl is the transient_for window for this client, 00657 // or recursively the transient_for window 00658 bool Client::hasTransient( const Client* cl, bool indirect ) const 00659 { 00660 // checkGroupTransients() uses this to break loops, so hasTransient() must detect them 00661 ConstClientList set; 00662 return hasTransientInternal( cl, indirect, set ); 00663 } 00664 00665 bool Client::hasTransientInternal( const Client* cl, bool indirect, ConstClientList& set ) const 00666 { 00667 if( set.contains( this )) 00668 return false; 00669 set.append( this ); 00670 if( cl->transientFor() != NULL ) 00671 { 00672 if( cl->transientFor() == this ) 00673 return true; 00674 if( !indirect ) 00675 return false; 00676 return hasTransientInternal( cl->transientFor(), indirect, set ); 00677 } 00678 if( !cl->isTransient()) 00679 return false; 00680 if( group() != cl->group()) 00681 return false; 00682 // cl is group transient, search from top 00683 if( transients().contains( const_cast< Client* >( cl ))) 00684 return true; 00685 if( !indirect ) 00686 return false; 00687 for( ClientList::ConstIterator it = transients().begin(); 00688 it != transients().end(); 00689 ++it ) 00690 if( (*it)->hasTransientInternal( cl, indirect, set )) 00691 return true; 00692 return false; 00693 } 00694 00695 ClientList Client::mainClients() const 00696 { 00697 if( !isTransient()) 00698 return ClientList(); 00699 if( transientFor() != NULL ) 00700 return ClientList() << const_cast< Client* >( transientFor()); 00701 ClientList result; 00702 for( ClientList::ConstIterator it = group()->members().begin(); 00703 it != group()->members().end(); 00704 ++it ) 00705 if((*it)->hasTransient( this, false )) 00706 result.append( *it ); 00707 return result; 00708 } 00709 00710 Client* Client::findModal() 00711 { 00712 for( ClientList::ConstIterator it = transients().begin(); 00713 it != transients().end(); 00714 ++it ) 00715 if( Client* ret = (*it)->findModal()) 00716 return ret; 00717 if( isModal()) 00718 return this; 00719 return NULL; 00720 } 00721 00722 // Client::window_group only holds the contents of the hint, 00723 // but it should be used only to find the group, not for anything else 00724 void Client::checkGroup() 00725 { 00726 Group* old_group = in_group; 00727 if( window_group != None ) 00728 { 00729 Group* new_group = workspace()->findGroup( window_group ); 00730 if( transientFor() != NULL && transientFor()->group() != new_group ) 00731 { // move the window to the right group (e.g. a dialog provided 00732 // by different app, but transient for this one, so make it part of that group) 00733 new_group = transientFor()->group(); 00734 } 00735 if( new_group == NULL ) // doesn't exist yet 00736 new_group = new Group( window_group, workspace()); 00737 if( new_group != in_group ) 00738 { 00739 if( in_group != NULL ) 00740 in_group->removeMember( this ); 00741 in_group = new_group; 00742 in_group->addMember( this ); 00743 } 00744 } 00745 else 00746 { 00747 if( transientFor() != NULL ) 00748 { // doesn't have window group set, but is transient for something 00749 // so make it part of that group 00750 Group* new_group = transientFor()->group(); 00751 if( new_group != in_group ) 00752 { 00753 if( in_group != NULL ) 00754 in_group->removeMember( this ); 00755 in_group = transientFor()->group(); 00756 in_group->addMember( this ); 00757 } 00758 } 00759 else if( groupTransient()) 00760 { // group transient which actually doesn't have a group :( 00761 // try creating group with other windows with the same client leader 00762 Group* new_group = workspace()->findClientLeaderGroup( this ); 00763 if( new_group == NULL ) 00764 new_group = new Group( None, workspace()); 00765 if( new_group != in_group ) 00766 { 00767 if( in_group != NULL ) 00768 in_group->removeMember( this ); 00769 in_group = new_group; 00770 in_group->addMember( this ); 00771 } 00772 } 00773 else // not transient without a group, put it in its own group 00774 { 00775 if( in_group != NULL && in_group->leader() != window()) 00776 { 00777 in_group->removeMember( this ); 00778 in_group = NULL; 00779 } 00780 if( in_group == NULL ) 00781 { 00782 in_group = new Group( None, workspace()); 00783 in_group->addMember( this ); 00784 } 00785 } 00786 } 00787 if( in_group != old_group ) 00788 { 00789 for( ClientList::Iterator it = transients_list.begin(); 00790 it != transients_list.end(); 00791 ) 00792 { // group transients in the old group are no longer transient for it 00793 if( (*it)->groupTransient() && (*it)->group() != group()) 00794 it = transients_list.remove( it ); 00795 else 00796 ++it; 00797 } 00798 #if 0 // TODO 00799 if( groupTransient()) 00800 { 00801 if( workspace()->findGroup( old_group )) // if it still exists 00802 { // it's no longer transient for windows in the old group 00803 for( ClientList::ConstIterator it = old_group->members().begin(); 00804 it != old_group->members().end(); 00805 ++it ) 00806 (*it)->removeTransient( this ); 00807 } 00808 // and it's transiet for all windows in the new group (this one is the most recent 00809 // in the group, so it is transient only for all previous windows) 00810 // loops are checked in checkGroupTransients() 00811 for( ClientList::ConstIterator it = group()->members().begin(); 00812 it != group()->members().end(); 00813 ++it ) 00814 (*it)->addTransient( this ); 00815 } 00816 #endif 00817 #if 0 // this would make group transients transient for window that were mapped later 00818 for( ClientList::ConstIterator it = group()->members().begin(); 00819 it != group()->members().end(); 00820 ++it ) 00821 { 00822 if( !(*it)->groupTransient()) // and group transients in the new group are transient for it 00823 continue; 00824 if( *it == this ) 00825 continue; 00826 addTransient( *it ); 00827 } 00828 checkGroupTransients(); 00829 #endif 00830 } 00831 } 00832 00833 00834 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Dec 16 19:08:41 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003