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