layers.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 // SELI zmenit doc
00013 
00014 /*
00015 
00016  This file contains things relevant to stacking order and layers.
00017 
00018  Design:
00019 
00020  Normal unconstrained stacking order, as requested by the user (by clicking
00021  on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order.
00022  That list shouldn't be used at all, except for building
00023  Workspace::stacking_order. The building is done
00024  in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should
00025  be used to get the stacking order, because it also checks the stacking order
00026  is up to date.
00027  All clients are also stored in Workspace::clients (except for isDesktop() clients,
00028  as those are very special, and are stored in Workspace::desktops), in the order
00029  the clients were created.
00030 
00031  Every window has one layer assigned in which it is. There are 6 layers,
00032  from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer
00033  and ActiveLayer (see also NETWM sect.7.10.). The layer a window is in depends
00034  on the window type, and on other things like whether the window is active.
00035 
00036  NET::Splash clients belong to the Normal layer. NET::TopMenu clients
00037  belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow
00038  are in the Normal layer in order to keep the 'allow window to cover
00039  the panel' Kicker setting to work as intended (this may look like a slight
00040  spec violation, but a) I have no better idea, b) the spec allows adjusting
00041  the stacking order if the WM thinks it's a good idea . We put all
00042  NET::KeepAbove above all Docks too, even though the spec suggests putting
00043  them in the same layer.
00044 
00045  Most transients are in the same layer as their mainwindow,
00046  see Workspace::constrainedStackingOrder(), they may also be in higher layers, but
00047  they should never be below their mainwindow.
00048 
00049  When some client attribute changes (above/below flag, transiency...),
00050  Workspace::updateClientLayer() should be called in order to make
00051  sure it's moved to the appropriate layer ClientList if needed.
00052 
00053  Currently the things that affect client in which layer a client
00054  belongs: KeepAbove/Keep Below flags, window type, fullscreen
00055  state and whether the client is active, mainclient (transiency).
00056 
00057  Make sure updateStackingOrder() is called in order to make
00058  Workspace::stackingOrder() up to date and propagated to the world.
00059  Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker
00060  helper class) it's possible to temporarily disable updates
00061  and the stacking order will be updated once after it's allowed again.
00062 
00063 */
00064 
00065 #include <assert.h>
00066 
00067 #include <kdebug.h>
00068 
00069 #include "utils.h"
00070 #include "client.h"
00071 #include "workspace.h"
00072 #include "tabbox.h"
00073 #include "group.h"
00074 #include "rules.h"
00075 
00076 extern Time qt_x_time;
00077 
00078 namespace KWinInternal
00079 {
00080 
00081 //*******************************
00082 // Workspace
00083 //*******************************
00084 
00085 void Workspace::updateClientLayer( Client* c )
00086     {
00087     if( c == NULL )
00088         return;
00089     if( c->layer() == c->belongsToLayer())
00090         return;
00091     StackingUpdatesBlocker blocker( this );
00092     c->invalidateLayer(); // invalidate, will be updated when doing restacking
00093     for( ClientList::ConstIterator it = c->transients().begin();
00094          it != c->transients().end();
00095          ++it )
00096         updateClientLayer( *it );
00097     }
00098 
00099 void Workspace::updateStackingOrder( bool propagate_new_clients )
00100     {
00101     if( block_stacking_updates > 0 )
00102         {
00103         blocked_propagating_new_clients = blocked_propagating_new_clients || propagate_new_clients;
00104         return;
00105         }
00106     ClientList new_stacking_order = constrainedStackingOrder();
00107     bool changed = ( new_stacking_order != stacking_order );
00108     stacking_order = new_stacking_order;
00109 #if 0
00110     kdDebug() << "stacking:" << changed << endl;
00111     if( changed || propagate_new_clients )
00112         {
00113         for( ClientList::ConstIterator it = stacking_order.begin();
00114              it != stacking_order.end();
00115              ++it )
00116             kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00117         }
00118 #endif
00119     if( changed || propagate_new_clients )
00120         propagateClients( propagate_new_clients );
00121     }
00122 
00127 void Workspace::propagateClients( bool propagate_new_clients )
00128     {
00129     Window *cl; // MW we should not assume WId and Window to be compatible
00130                                 // when passig pointers around.
00131 
00132     // restack the windows according to the stacking order
00133     Window* new_stack = new Window[ stacking_order.count() + 2 ];
00134     int pos = 0;
00135     // Stack all windows under the support window. The support window is
00136     // not used for anything (besides the NETWM property), and it's not shown,
00137     // but it was lowered after kwin startup. Stacking all clients below
00138     // it ensures that no client will be ever shown above override-redirect
00139     // windows (e.g. popups).
00140     new_stack[ pos++ ] = supportWindow->winId();
00141     int topmenu_space_pos = 1; // not 0, that's supportWindow !!!
00142     for( ClientList::ConstIterator it = stacking_order.fromLast();
00143          it != stacking_order.end();
00144          --it )
00145         {
00146         new_stack[ pos++ ] = (*it)->frameId();
00147         if( (*it)->belongsToLayer() >= DockLayer )
00148             topmenu_space_pos = pos;
00149         }
00150     if( topmenu_space != NULL )
00151         { // make sure the topmenu space is below all topmenus, fullscreens, etc.
00152         for( int i = pos;
00153              i > topmenu_space_pos;
00154              --i )
00155             new_stack[ i ] = new_stack[ i - 1 ];
00156         new_stack[ topmenu_space_pos ] = topmenu_space->winId();
00157         ++pos;
00158         }
00159     // TODO isn't it too inefficient to restart always all clients?
00160     // TODO don't restack not visible windows?
00161     assert( new_stack[ 0 ] = supportWindow->winId());
00162     XRestackWindows(qt_xdisplay(), new_stack, pos);
00163     delete [] new_stack;
00164 
00165     if ( propagate_new_clients )
00166         {
00167         cl = new Window[ desktops.count() + clients.count()];
00168         pos = 0;
00169     // TODO this is still not completely in the map order
00170         for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it )
00171             cl[pos++] =  (*it)->window();
00172         for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it )
00173             cl[pos++] =  (*it)->window();
00174         rootInfo->setClientList( cl, pos );
00175         delete [] cl;
00176         }
00177 
00178     cl = new Window[ stacking_order.count()];
00179     pos = 0;
00180     for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
00181         cl[pos++] =  (*it)->window();
00182     rootInfo->setClientListStacking( cl, pos );
00183     delete [] cl;
00184     }
00185 
00186 
00192 // TODO misleading name for this method
00193 Client* Workspace::topClientOnDesktop( int desktop, bool unconstrained ) const
00194     {
00195 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00196     ClientList::ConstIterator begin, end;
00197     if( !unconstrained )
00198         {
00199         begin = stacking_order.fromLast();
00200         end = stacking_order.end();
00201         }
00202     else
00203         {
00204         begin = unconstrained_stacking_order.fromLast();
00205         end = unconstrained_stacking_order.end();
00206         }
00207     for( ClientList::ConstIterator it = begin;
00208         it != end;
00209         --it )
00210         {
00211         if ( (*it)->isOnDesktop( desktop ) && !(*it)->isSpecialWindow()
00212             && (*it)->isShown( false ) && (*it)->wantsTabFocus())
00213             return *it;
00214         }
00215     return 0;
00216     }
00217 
00218 Client* Workspace::findDesktop( bool topmost, int desktop ) const
00219     {
00220 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00221     if( topmost )
00222         {
00223         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
00224             {
00225             if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop()
00226                 && (*it)->isShown( true ))
00227                 return *it;
00228             }
00229         }
00230     else // bottom-most
00231         {
00232         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
00233             {
00234             if ( (*it)->isOnDesktop( desktop ) && (*it)->isDesktop()
00235                 && (*it)->isShown( true ))
00236                 return *it;
00237             }
00238         }
00239     return NULL;
00240     }
00241 
00242 void Workspace::raiseOrLowerClient( Client *c)
00243     {
00244     if (!c) return;
00245     Client* topmost = NULL;
00246 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00247     if ( most_recently_raised && stacking_order.contains( most_recently_raised ) &&
00248          most_recently_raised->isShown( true ) && c->isOnCurrentDesktop())
00249         topmost = most_recently_raised;
00250     else
00251         topmost = topClientOnDesktop( c->isOnAllDesktops() ? currentDesktop() : c->desktop());
00252 
00253     if( c == topmost)
00254         lowerClient(c);
00255     else
00256         raiseClient(c);
00257     }
00258 
00259 
00260 void Workspace::lowerClient( Client* c )
00261     {
00262     if ( !c )
00263         return;
00264     if( c->isTopMenu())
00265         return;
00266 
00267     c->cancelAutoRaise();
00268 
00269     StackingUpdatesBlocker blocker( this );
00270 
00271     unconstrained_stacking_order.remove( c );
00272     unconstrained_stacking_order.prepend( c );
00273     if( c->isTransient())
00274         {
00275         // lower also mainclients, in their reversed stacking order
00276         ClientList mainclients = ensureStackingOrder( c->mainClients());
00277         for( ClientList::ConstIterator it = mainclients.fromLast();
00278              it != mainclients.end();
00279              ++it )
00280             lowerClient( *it );
00281         }
00282 
00283     if ( c == most_recently_raised )
00284         most_recently_raised = 0;
00285     }
00286 
00287 void Workspace::lowerClientWithinApplication( Client* c )
00288     {
00289     if ( !c )
00290         return;
00291     if( c->isTopMenu())
00292         return;
00293 
00294     c->cancelAutoRaise();
00295 
00296     StackingUpdatesBlocker blocker( this );
00297 
00298     unconstrained_stacking_order.remove( c );
00299     bool lowered = false;
00300     // first try to put it below the bottom-most window of the application
00301     for( ClientList::Iterator it = unconstrained_stacking_order.begin();
00302          it != unconstrained_stacking_order.end();
00303          ++it )
00304         if( Client::belongToSameApplication( *it, c ))
00305             {
00306             unconstrained_stacking_order.insert( it, c );
00307             lowered = true;
00308             break;
00309             }
00310     if( !lowered )
00311         unconstrained_stacking_order.prepend( c );
00312     // ignore mainwindows
00313     }
00314 
00315 void Workspace::raiseClient( Client* c )
00316     {
00317     if ( !c )
00318         return;
00319     if( c->isTopMenu())
00320         return;
00321 
00322     c->cancelAutoRaise();
00323 
00324     StackingUpdatesBlocker blocker( this );
00325 
00326     if( c->isTransient())
00327         {
00328         ClientList mainclients = ensureStackingOrder( c->mainClients());
00329         for( ClientList::ConstIterator it = mainclients.begin();
00330              it != mainclients.end();
00331              ++it )
00332             raiseClient( *it );
00333         }
00334 
00335     unconstrained_stacking_order.remove( c );
00336     unconstrained_stacking_order.append( c );
00337 
00338     if( !c->isSpecialWindow())
00339         {
00340         most_recently_raised = c;
00341         pending_take_activity = NULL;
00342         }
00343     }
00344 
00345 void Workspace::raiseClientWithinApplication( Client* c )
00346     {
00347     if ( !c )
00348         return;
00349     if( c->isTopMenu())
00350         return;
00351 
00352     c->cancelAutoRaise();
00353 
00354     StackingUpdatesBlocker blocker( this );
00355     // ignore mainwindows
00356     
00357     // first try to put it above the top-most window of the application
00358     for( ClientList::Iterator it = unconstrained_stacking_order.fromLast();
00359          it != unconstrained_stacking_order.end();
00360          --it )
00361         {
00362         if( *it == c ) // don't lower it just because it asked to be raised
00363             return;
00364         if( Client::belongToSameApplication( *it, c ))
00365             {
00366             unconstrained_stacking_order.remove( c );
00367             ++it; // insert after the found one
00368             unconstrained_stacking_order.insert( it, c );
00369             return;
00370             }
00371         }
00372     }
00373 
00374 void Workspace::raiseClientRequest( Client* c, NET::RequestSource src, Time timestamp )
00375     {
00376     if( src == NET::FromTool || allowFullClientRaising( c, timestamp ))
00377         raiseClient( c );
00378     else
00379         {
00380         raiseClientWithinApplication( c );
00381         c->demandAttention();
00382         }
00383     }
00384 
00385 void Workspace::lowerClientRequest( Client* c, NET::RequestSource src, Time /*timestamp*/ )
00386     {
00387     // If the client has support for all this focus stealing prevention stuff,
00388     // do only lowering within the application, as that's the more logical
00389     // variant of lowering when application requests it.
00390     // No demanding of attention here of course.
00391     if( src == NET::FromTool || !c->hasUserTimeSupport())
00392         lowerClient( c );
00393     else
00394         lowerClientWithinApplication( c );
00395     }
00396 
00397 void Workspace::restackClientUnderActive( Client* c )
00398     {
00399     if( c->isTopMenu())
00400         return;
00401     if( !active_client || active_client == c )
00402         {
00403         raiseClient( c );
00404         return;
00405         }
00406 
00407     // put in the stacking order below _all_ windows belonging to the active application
00408     assert( unconstrained_stacking_order.contains( active_client ));
00409     for( ClientList::Iterator it = unconstrained_stacking_order.begin();
00410          it != unconstrained_stacking_order.end();
00411          ++it )
00412         { // TODO ignore topmenus?
00413         if( Client::belongToSameApplication( active_client, *it ))
00414             {
00415             if( *it != c )
00416                 {
00417                 unconstrained_stacking_order.remove( c );
00418                 unconstrained_stacking_order.insert( it, c );
00419                 }
00420             break;
00421             }
00422         }
00423     assert( unconstrained_stacking_order.contains( c ));
00424     for( int desktop = 1;
00425          desktop <= numberOfDesktops();
00426          ++desktop )
00427         { // do for every virtual desktop to handle the case of onalldesktop windows
00428         if( c->wantsTabFocus() && c->isOnDesktop( desktop ) && focus_chain[ desktop ].contains( active_client ))
00429             {
00430             // also put in focus_chain[currentDesktop()] after all windows belonging to the active applicationa
00431             focus_chain[ desktop ].remove( c );
00432             for( ClientList::Iterator it = focus_chain[ desktop ].fromLast();
00433                  it != focus_chain[ desktop ].end();
00434                  --it )
00435                 {
00436                 if( Client::belongToSameApplication( active_client, *it ))
00437                     {
00438                     focus_chain[ desktop ].insert( it, c );
00439                     break;
00440                     }
00441                 }
00442             }
00443         }
00444     // the same for global_focus_chain
00445     if( c->wantsTabFocus() && global_focus_chain.contains( active_client ))
00446         {
00447         global_focus_chain.remove( c );
00448         for( ClientList::Iterator it = global_focus_chain.fromLast();
00449              it != global_focus_chain.end();
00450              --it )
00451             {
00452             if( Client::belongToSameApplication( active_client, *it ))
00453                 {
00454                 global_focus_chain.insert( it, c );
00455                 break;
00456                 }
00457             }
00458         }
00459     updateStackingOrder();
00460     }
00461 
00462 void Workspace::circulateDesktopApplications()
00463     {
00464     if ( desktops.count() > 1 )
00465         {
00466         bool change_active = activeClient()->isDesktop();
00467         raiseClient( findDesktop( false, currentDesktop()));
00468         if( change_active ) // if the previously topmost Desktop was active, activate this new one
00469             activateClient( findDesktop( true, currentDesktop()));
00470         }
00471     // if there's no active client, make desktop the active one
00472     if( desktops.count() > 0 && activeClient() == NULL && should_get_focus.count() == 0 )
00473         activateClient( findDesktop( true, currentDesktop()));
00474     }
00475 
00476 
00480 ClientList Workspace::constrainedStackingOrder()
00481     {
00482     ClientList layer[ NumLayers ];
00483 
00484 #if 0
00485     kdDebug() << "stacking1:" << endl;
00486 #endif
00487     // build the order from layers
00488     QMap< Group*, Layer > minimum_layer;
00489     for( ClientList::ConstIterator it = unconstrained_stacking_order.begin();
00490          it != unconstrained_stacking_order.end();
00491          ++it )
00492         {
00493         Layer l = (*it)->layer();
00494         // If a window is raised above some other window in the same window group
00495         // which is in the ActiveLayer (i.e. it's fulscreened), make sure it stays
00496         // above that window (see #95731).
00497         if( minimum_layer.contains( (*it)->group())
00498             && minimum_layer[ (*it)->group() ] == ActiveLayer
00499             && ( l == NormalLayer || l == AboveLayer ))
00500             {
00501             l = minimum_layer[ (*it)->group() ];
00502             }
00503         minimum_layer[ (*it)->group() ] = l;
00504         layer[ l ].append( *it );
00505         }
00506     ClientList stacking;    
00507     for( Layer lay = FirstLayer;
00508          lay < NumLayers;
00509          ++lay )    
00510         stacking += layer[ lay ];
00511 #if 0
00512     kdDebug() << "stacking2:" << endl;
00513     for( ClientList::ConstIterator it = stacking.begin();
00514          it != stacking.end();
00515          ++it )
00516         kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00517 #endif
00518     // now keep transients above their mainwindows
00519     // TODO this could(?) use some optimization
00520     for( ClientList::Iterator it = stacking.fromLast();
00521          it != stacking.end();
00522          )
00523         {
00524         if( !(*it)->isTransient())
00525             {
00526             --it;
00527             continue;
00528             }
00529         ClientList::Iterator it2 = stacking.end();
00530         if( (*it)->groupTransient())
00531             {
00532             if( (*it)->group()->members().count() > 0 )
00533                 { // find topmost client this one is transient for
00534                 for( it2 = stacking.fromLast();
00535                      it2 != stacking.end();
00536                      --it2 )
00537                     {
00538                     if( *it2 == *it )
00539                         {
00540                         it2 = stacking.end(); // don't reorder
00541                         break;
00542                         }
00543                     if( (*it2)->hasTransient( *it, true ) && keepTransientAbove( *it2, *it ))
00544                         break;
00545                     }
00546                 } // else it2 remains pointing at stacking.end()
00547             }
00548         else
00549             {
00550             for( it2 = stacking.fromLast();
00551                  it2 != stacking.end();
00552                  --it2 )
00553                 {
00554                 if( *it2 == *it )
00555                     {
00556                     it2 = stacking.end(); // don't reorder
00557                     break;
00558                     }
00559                 if( *it2 == (*it)->transientFor() && keepTransientAbove( *it2, *it ))
00560                     break;
00561                 }
00562             }
00563 //        kdDebug() << "STACK:" << (*it) << ":" << ( it2 == stacking.end() ? ((Client*)0) : (*it2)) << endl;
00564         if( it2 == stacking.end())
00565             {
00566             --it;
00567             continue;
00568             }
00569         Client* current = *it;
00570         ClientList::Iterator remove_it = it;
00571         --it;
00572         stacking.remove( remove_it );
00573         if( !current->transients().isEmpty())  // this one now can be possibly above its transients,
00574             it = it2; // so go again higher in the stack order and possibly move those transients again
00575         ++it2; // insert after the mainwindow, it's ok if it2 is now stacking.end()
00576         stacking.insert( it2, current );
00577         }
00578 #if 0
00579     kdDebug() << "stacking3:" << endl;
00580     for( ClientList::ConstIterator it = stacking.begin();
00581          it != stacking.end();
00582          ++it )
00583         kdDebug() << (void*)(*it) << *it << ":" << (*it)->layer() << endl;
00584     kdDebug() << "\n\n" << endl;
00585 #endif
00586     return stacking;
00587     }
00588 
00589 void Workspace::blockStackingUpdates( bool block )
00590     {
00591     if( block )
00592         {
00593         if( block_stacking_updates == 0 )
00594             blocked_propagating_new_clients = false;
00595         ++block_stacking_updates;
00596         }
00597     else // !block
00598         if( --block_stacking_updates == 0 )
00599             updateStackingOrder( blocked_propagating_new_clients );
00600     }
00601 
00602 // Ensure list is in stacking order
00603 ClientList Workspace::ensureStackingOrder( const ClientList& list ) const
00604     {
00605 // TODO    Q_ASSERT( block_stacking_updates == 0 );
00606     if( list.count() < 2 )
00607         return list;
00608     // TODO is this worth optimizing?
00609     ClientList result = list;
00610     for( ClientList::ConstIterator it = stacking_order.begin();
00611          it != stacking_order.end();
00612          ++it )
00613         if( result.remove( *it ) != 0 )
00614             result.append( *it );
00615     return result;
00616     }
00617 
00618 // check whether a transient should be actually kept above its mainwindow
00619 // there may be some special cases where this rule shouldn't be enfored
00620 bool Workspace::keepTransientAbove( const Client* mainwindow, const Client* transient )
00621     {
00622     // When topmenu's mainwindow becomes active, topmenu is raised and shown.
00623     // They also belong to the Dock layer. This makes them to be very high.
00624     // Therefore don't keep group transients above them, otherwise this would move
00625     // group transients way too high.
00626     if( mainwindow->isTopMenu() && transient->groupTransient())
00627         return false;
00628     // #93832 - don't keep splashscreens above dialogs
00629     if( transient->isSplash() && mainwindow->isDialog())
00630         return false;
00631     // This is rather a hack for #76026. Don't keep non-modal dialogs above
00632     // the mainwindow, but only if they're group transient (since only such dialogs
00633     // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker)
00634     // needs to be found.
00635     if( transient->isDialog() && !transient->isModal() && transient->groupTransient())
00636         return false;
00637     // #63223 - don't keep transients above docks, because the dock is kept high,
00638     // and e.g. dialogs for them would be too high too
00639     if( mainwindow->isDock())
00640         return false;
00641     return true;
00642     }
00643 
00644 //*******************************
00645 // Client
00646 //*******************************
00647 
00648 void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSource src, Time timestamp, bool send_event )
00649     {
00650     switch ( detail )
00651         {
00652         case Above:
00653         case TopIf:
00654             workspace()->raiseClientRequest( this, src, timestamp );
00655           break;
00656         case Below:
00657         case BottomIf:
00658             workspace()->lowerClientRequest( this, src, timestamp );
00659           break;
00660         case Opposite:
00661         default:
00662             break;
00663         }
00664     if( send_event )
00665         sendSyntheticConfigureNotify();
00666     }
00667     
00668 void Client::setKeepAbove( bool b )
00669     {
00670     b = rules()->checkKeepAbove( b );
00671     if( b && !rules()->checkKeepBelow( false ))
00672         setKeepBelow( false );
00673     if ( b == keepAbove())
00674         { // force hint change if different
00675         if( bool( info->state() & NET::KeepAbove ) != keepAbove())
00676             info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove );
00677         return;
00678         }
00679     keep_above = b;
00680     info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove );
00681     if( decoration != NULL )
00682         decoration->emitKeepAboveChanged( keepAbove());
00683     workspace()->updateClientLayer( this );
00684     updateWindowRules();
00685     }
00686 
00687 void Client::setKeepBelow( bool b )
00688     {
00689     b = rules()->checkKeepBelow( b );
00690     if( b && !rules()->checkKeepAbove( false ))
00691         setKeepAbove( false );
00692     if ( b == keepBelow())
00693         { // force hint change if different
00694         if( bool( info->state() & NET::KeepBelow ) != keepBelow())
00695             info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow );
00696         return;
00697         }
00698     keep_below = b;
00699     info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow );
00700     if( decoration != NULL )
00701         decoration->emitKeepBelowChanged( keepBelow());
00702     workspace()->updateClientLayer( this );
00703     updateWindowRules();
00704     }
00705 
00706 Layer Client::layer() const
00707     {
00708     if( in_layer == UnknownLayer )
00709         const_cast< Client* >( this )->in_layer = belongsToLayer();
00710     return in_layer;
00711     }
00712 
00713 Layer Client::belongsToLayer() const
00714     {
00715     if( isDesktop())
00716         return DesktopLayer;
00717     if( isSplash())         // no damn annoying splashscreens
00718         return NormalLayer; // getting in the way of everything else
00719     if( isDock() && keepBelow())
00720         // slight hack for the 'allow window to cover panel' Kicker setting
00721         // don't move keepbelow docks below normal window, but only to the same
00722         // layer, so that both may be raised to cover the other
00723         return NormalLayer;
00724     if( keepBelow())
00725         return BelowLayer;
00726     if( isDock() && !keepBelow())
00727         return DockLayer;
00728     if( isTopMenu())
00729         return DockLayer;
00730     // only raise fullscreen above docks if it's the topmost window in unconstrained stacking order,
00731     // i.e. the window set to be topmost by the user (also includes transients of the fullscreen window)
00732     const Client* ac = workspace()->mostRecentlyActivatedClient(); // instead of activeClient() - avoids flicker
00733     const Client* top = workspace()->topClientOnDesktop( desktop(), true );
00734     if( isFullScreen() && ac != NULL && top != NULL
00735         && ( ac == this || this->group() == ac->group())
00736         && ( top == this || this->group() == top->group()))
00737         return ActiveLayer;
00738     if( keepAbove())
00739         return AboveLayer;
00740     return NormalLayer;
00741     }
00742 
00743 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys