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