workspace.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 //#define QT_CLEAN_NAMESPACE
00013 
00014 #include "workspace.h"
00015 
00016 #include <kapplication.h>
00017 #include <kstartupinfo.h>
00018 #include <fixx11h.h>
00019 #include <kconfig.h>
00020 #include <kglobal.h>
00021 #include <qpopupmenu.h>
00022 #include <klocale.h>
00023 #include <qregexp.h>
00024 #include <qpainter.h>
00025 #include <qbitmap.h>
00026 #include <qclipboard.h>
00027 #include <kmenubar.h>
00028 #include <kprocess.h>
00029 #include <kglobalaccel.h>
00030 #include <dcopclient.h>
00031 #include <kipc.h>
00032 
00033 #include "plugins.h"
00034 #include "client.h"
00035 #include "popupinfo.h"
00036 #include "tabbox.h"
00037 #include "atoms.h"
00038 #include "placement.h"
00039 #include "notifications.h"
00040 #include "group.h"
00041 #include "rules.h"
00042 
00043 #include <X11/extensions/shape.h>
00044 #include <X11/keysym.h>
00045 #include <X11/keysymdef.h>
00046 #include <X11/cursorfont.h>
00047 
00048 extern Time qt_x_time;
00049 
00050 namespace KWinInternal
00051 {
00052 
00053 extern int screen_number;
00054 
00055 Workspace *Workspace::_self = 0;
00056 
00057 KProcess* kompmgr = 0;
00058 KSelectionOwner* kompmgr_selection;
00059 
00060 bool allowKompmgrRestart = TRUE;
00061 
00062 // Rikkus: This class is too complex. It needs splitting further.
00063 // It's a nightmare to understand, especially with so few comments :(
00064 
00065 // Matthias: Feel free to ask me questions about it. Feel free to add
00066 // comments. I disagree that further splittings makes it easier. 2500
00067 // lines are not too much. It's the task that is complex, not the
00068 // code.
00069 Workspace::Workspace( bool restore )
00070   : DCOPObject        ("KWinInterface"),
00071     QObject           (0, "workspace"),
00072     current_desktop   (0),
00073     number_of_desktops(0),
00074     active_popup( NULL ),
00075     active_popup_client( NULL ),
00076     desktop_widget    (0),
00077     temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ),
00078     active_client     (0),
00079     last_active_client     (0),
00080     most_recently_raised (0),
00081     movingClient(0),
00082     pending_take_activity ( NULL ),
00083     delayfocus_client (0),
00084     showing_desktop( false ),
00085     block_showing_desktop( 0 ),
00086     was_user_interaction (false),
00087     session_saving    (false),
00088     control_grab      (false),
00089     tab_grab          (false),
00090     mouse_emulation   (false),
00091     block_focus       (0),
00092     tab_box           (0),
00093     popupinfo         (0),
00094     popup             (0),
00095     advanced_popup    (0),
00096     desk_popup        (0),
00097     desk_popup_index  (0),
00098     keys              (0),
00099     client_keys       ( NULL ),
00100     client_keys_dialog ( NULL ),
00101     client_keys_client ( NULL ),
00102     disable_shortcuts_keys ( NULL ),
00103     global_shortcuts_disabled( false ),
00104     global_shortcuts_disabled_for_client( false ),
00105     root              (0),
00106     workspaceInit     (true),
00107     startup(0), electric_have_borders(false),
00108     electric_current_border(0),
00109     electric_top_border(None),
00110     electric_bottom_border(None),
00111     electric_left_border(None),
00112     electric_right_border(None),
00113     layoutOrientation(Qt::Vertical),
00114     layoutX(-1),
00115     layoutY(2),
00116     workarea(NULL),
00117     screenarea(NULL),
00118     managing_topmenus( false ),
00119     topmenu_selection( NULL ),
00120     topmenu_watcher( NULL ),
00121     topmenu_height( 0 ),
00122     topmenu_space( NULL ),
00123     set_active_client_recursion( 0 ),
00124     block_stacking_updates( 0 ),
00125     forced_global_mouse_grab( false )
00126     {
00127     _self = this;
00128     mgr = new PluginMgr;
00129     root = qt_xrootwin();
00130     default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() );
00131     installed_colormap = default_colormap;
00132     session.setAutoDelete( TRUE );
00133 
00134     connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )),
00135         this, SLOT( gotTemporaryRulesMessage( const QString& )));
00136     connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules()));
00137 
00138     updateXTime(); // needed for proper initialization of user_time in Client ctor
00139 
00140     delayFocusTimer = 0; 
00141     
00142     electric_time_first = qt_x_time;
00143     electric_time_last = qt_x_time;
00144 
00145     if ( restore )
00146       loadSessionInfo();
00147 
00148     loadWindowRules();
00149 
00150     (void) QApplication::desktop(); // trigger creation of desktop widget
00151 
00152     desktop_widget =
00153       new QWidget(
00154         0,
00155         "desktop_widget",
00156         Qt::WType_Desktop | Qt::WPaintUnclipped
00157     );
00158 
00159     kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later
00160     // call this before XSelectInput() on the root window
00161     startup = new KStartupInfo(
00162         KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
00163 
00164     // select windowmanager privileges
00165     XSelectInput(qt_xdisplay(), root,
00166                  KeyPressMask |
00167                  PropertyChangeMask |
00168                  ColormapChangeMask |
00169                  SubstructureRedirectMask |
00170                  SubstructureNotifyMask |
00171                  FocusChangeMask // for NotifyDetailNone
00172                  );
00173 
00174     Shape::init();
00175 
00176     // compatibility
00177     long data = 1;
00178 
00179     XChangeProperty(
00180       qt_xdisplay(),
00181       qt_xrootwin(),
00182       atoms->kwin_running,
00183       atoms->kwin_running,
00184       32,
00185       PropModeAppend,
00186       (unsigned char*) &data,
00187       1
00188     );
00189 
00190     client_keys = new KGlobalAccel( this );
00191     initShortcuts();
00192     tab_box = new TabBox( this );
00193     popupinfo = new PopupInfo( );
00194 
00195     init();
00196 
00197 #if (QT_VERSION-0 >= 0x030200) // XRANDR support
00198     connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized()));
00199 #endif
00200 
00201     // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object
00202     if (options->useTranslucency)
00203         {
00204         kompmgr = new KProcess;
00205         connect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), SLOT(handleKompmgrOutput(KProcess*, char*, int)));
00206         *kompmgr << "kompmgr";
00207         startKompmgr();
00208         }
00209     }
00210 
00211 
00212 void Workspace::init()
00213     {
00214     checkElectricBorders();
00215 
00216 // not used yet
00217 //     topDock = 0L;
00218 //     maximizedWindowCounter = 0;
00219     
00220     supportWindow = new QWidget;
00221     XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp
00222 
00223     XSetWindowAttributes attr;
00224     attr.override_redirect = 1;
00225     null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent,
00226         InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
00227     XMapWindow(qt_xdisplay(), null_focus_window);
00228 
00229     unsigned long protocols[ 5 ] =
00230         {
00231         NET::Supported |
00232         NET::SupportingWMCheck |
00233         NET::ClientList |
00234         NET::ClientListStacking |
00235         NET::DesktopGeometry |
00236         NET::NumberOfDesktops |
00237         NET::CurrentDesktop |
00238         NET::ActiveWindow |
00239         NET::WorkArea |
00240         NET::CloseWindow |
00241         NET::DesktopNames |
00242         NET::KDESystemTrayWindows |
00243         NET::WMName |
00244         NET::WMVisibleName |
00245         NET::WMDesktop |
00246         NET::WMWindowType |
00247         NET::WMState |
00248         NET::WMStrut |
00249         NET::WMIconGeometry |
00250         NET::WMIcon |
00251         NET::WMPid |
00252         NET::WMMoveResize |
00253         NET::WMKDESystemTrayWinFor |
00254         NET::WMFrameExtents |
00255         NET::WMPing
00256         ,
00257         NET::NormalMask |
00258         NET::DesktopMask |
00259         NET::DockMask |
00260         NET::ToolbarMask |
00261         NET::MenuMask |
00262         NET::DialogMask |
00263         NET::OverrideMask |
00264         NET::TopMenuMask |
00265         NET::UtilityMask |
00266         NET::SplashMask |
00267         0
00268         ,
00269         NET::Modal |
00270 //        NET::Sticky |  // large desktops not supported (and probably never will be)
00271         NET::MaxVert |
00272         NET::MaxHoriz |
00273         NET::Shaded |
00274         NET::SkipTaskbar |
00275         NET::KeepAbove |
00276 //        NET::StaysOnTop |  the same like KeepAbove
00277         NET::SkipPager |
00278         NET::Hidden |
00279         NET::FullScreen |
00280         NET::KeepBelow |
00281         NET::DemandsAttention |
00282         0
00283         ,
00284         NET::WM2UserTime |
00285         NET::WM2StartupId |
00286         NET::WM2AllowedActions |
00287         NET::WM2RestackWindow |
00288         NET::WM2MoveResizeWindow |
00289         NET::WM2ExtendedStrut |
00290         NET::WM2KDETemporaryRules |
00291         NET::WM2ShowingDesktop |
00292         0
00293         ,
00294         NET::ActionMove |
00295         NET::ActionResize |
00296         NET::ActionMinimize |
00297         NET::ActionShade |
00298 //        NET::ActionStick | // Sticky state is not supported
00299         NET::ActionMaxVert |
00300         NET::ActionMaxHoriz |
00301         NET::ActionFullScreen |
00302         NET::ActionChangeDesktop |
00303         NET::ActionClose |
00304         0
00305         ,
00306         };
00307 
00308     rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin",
00309         protocols, 5, qt_xscreen() );
00310 
00311     loadDesktopSettings();
00312     // extra NETRootInfo instance in Client mode is needed to get the values of the properties
00313     NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop );
00314     int initial_desktop;
00315     if( !kapp->isSessionRestored())
00316         initial_desktop = client_info.currentDesktop();
00317     else
00318         {
00319         KConfigGroupSaver saver( kapp->sessionConfig(), "Session" );
00320         initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 );
00321         }
00322     if( !setCurrentDesktop( initial_desktop ))
00323         setCurrentDesktop( 1 );
00324 
00325     // now we know how many desktops we'll, thus, we initialise the positioning object
00326     initPositioning = new Placement(this);
00327 
00328     connect(&reconfigureTimer, SIGNAL(timeout()), this,
00329             SLOT(slotReconfigure()));
00330     connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows()));
00331 
00332     connect(kapp, SIGNAL(appearanceChanged()), this,
00333             SLOT(slotReconfigure()));
00334     connect(kapp, SIGNAL(settingsChanged(int)), this,
00335             SLOT(slotSettingsChanged(int)));
00336     connect(kapp, SIGNAL( kipcMessage( int, int )), this, SLOT( kipcMessage( int, int )));
00337 
00338     active_client = NULL;
00339     rootInfo->setActiveWindow( None );
00340     focusToNull();
00341     if( !kapp->isSessionRestored())
00342         ++block_focus; // because it will be set below
00343 
00344     char nm[ 100 ];
00345     sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay()));
00346     Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False );
00347     topmenu_selection = new KSelectionOwner( topmenu_atom );
00348     topmenu_watcher = new KSelectionWatcher( topmenu_atom );
00349 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
00350 
00351         { // begin updates blocker block
00352         StackingUpdatesBlocker blocker( this );
00353 
00354         if( options->topMenuEnabled() && topmenu_selection->claim( false ))
00355             setupTopMenuHandling(); // this can call updateStackingOrder()
00356         else
00357             lostTopMenuSelection();
00358 
00359         unsigned int i, nwins;
00360         Window root_return, parent_return, *wins;
00361         XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins);
00362         for (i = 0; i < nwins; i++) 
00363             {
00364             XWindowAttributes attr;
00365             XGetWindowAttributes(qt_xdisplay(), wins[i], &attr);
00366             if (attr.override_redirect )
00367                 continue;
00368             if( topmenu_space && topmenu_space->winId() == wins[ i ] )
00369                 continue;
00370             if (attr.map_state != IsUnmapped) 
00371                 {
00372                 if ( addSystemTrayWin( wins[i] ) )
00373                     continue;
00374                 Client* c = createClient( wins[i], true );
00375                 if ( c != NULL && root != qt_xrootwin() ) 
00376                     { // TODO what is this?
00377                 // TODO may use QWidget:.create
00378                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00379                     c->move(0,0);
00380                     }
00381                 }
00382             }
00383         if ( wins )
00384             XFree((void *) wins);
00385     // propagate clients, will really happen at the end of the updates blocker block
00386         updateStackingOrder( true );
00387 
00388         updateClientArea();
00389         raiseElectricBorders();
00390 
00391     // NETWM spec says we have to set it to (0,0) if we don't support it
00392         NETPoint* viewports = new NETPoint[ number_of_desktops ];
00393         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
00394         delete[] viewports;
00395         QRect geom = QApplication::desktop()->geometry();
00396         NETSize desktop_geometry;
00397         desktop_geometry.width = geom.width();
00398         desktop_geometry.height = geom.height();
00399     // TODO update also after gaining XRANDR support
00400         rootInfo->setDesktopGeometry( -1, desktop_geometry );
00401         setShowingDesktop( false );
00402 
00403         } // end updates blocker block
00404 
00405     Client* new_active_client = NULL;
00406     if( !kapp->isSessionRestored())
00407         {
00408         --block_focus;
00409         new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
00410         }
00411     if( new_active_client == NULL
00412         && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
00413         {
00414         if( new_active_client == NULL )
00415             new_active_client = topClientOnDesktop( currentDesktop());
00416         if( new_active_client == NULL && !desktops.isEmpty() )
00417             new_active_client = findDesktop( true, currentDesktop());
00418         }
00419     if( new_active_client != NULL )
00420         activateClient( new_active_client );
00421     // SELI TODO this won't work with unreasonable focus policies,
00422     // and maybe in rare cases also if the selected client doesn't
00423     // want focus
00424     workspaceInit = false;
00425 // TODO ungrabXServer()
00426     }
00427 
00428 Workspace::~Workspace()
00429     {
00430     if (kompmgr)
00431         delete kompmgr;
00432     blockStackingUpdates( true );
00433 // TODO    grabXServer();
00434     // use stacking_order, so that kwin --replace keeps stacking order
00435     for( ClientList::ConstIterator it = stacking_order.begin();
00436          it != stacking_order.end();
00437          ++it )
00438         {
00439     // only release the window
00440         (*it)->releaseWindow( true );
00441         // No removeClient() is called, it does more than just removing.
00442         // However, remove from some lists to e.g. prevent performTransiencyCheck()
00443         // from crashing.
00444         clients.remove( *it );
00445         desktops.remove( *it );
00446         }
00447     delete desktop_widget;
00448     delete tab_box;
00449     delete popupinfo;
00450     delete popup;
00451     if ( root == qt_xrootwin() )
00452         XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running);
00453 
00454     writeWindowRules();
00455     KGlobal::config()->sync();
00456 
00457     delete rootInfo;
00458     delete supportWindow;
00459     delete mgr;
00460     delete[] workarea;
00461     delete[] screenarea;
00462     delete startup;
00463     delete initPositioning;
00464     delete topmenu_watcher;
00465     delete topmenu_selection;
00466     delete topmenu_space;
00467     delete client_keys_dialog;
00468     while( !rules.isEmpty())
00469         {
00470         delete rules.front();
00471         rules.pop_front();
00472         }
00473     XDestroyWindow( qt_xdisplay(), null_focus_window );
00474 // TODO    ungrabXServer();
00475     _self = 0;
00476     }
00477 
00478 Client* Workspace::createClient( Window w, bool is_mapped )
00479     {
00480     StackingUpdatesBlocker blocker( this );
00481     Client* c = new Client( this );
00482     if( !c->manage( w, is_mapped ))
00483         {
00484         Client::deleteClient( c, Allowed );
00485         return NULL;
00486         }
00487     addClient( c, Allowed );
00488     return c;
00489     }
00490 
00491 void Workspace::addClient( Client* c, allowed_t )
00492     {
00493     // waited with trans settings until window figured out if active or not ;)
00494 //     qWarning("%s", (const char*)(c->resourceClass()));
00495     c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None);
00496     // first check if the window has it's own opinion of it's translucency ;)
00497     c->getWindowOpacity();
00498     if (c->isDock())
00499         {
00500 //         if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c;
00501         if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future
00502             {
00503             c->setShadowSize(options->dockShadowSize);
00504             c->setOpacity(options->translucentDocks, options->dockOpacity);
00505             }
00506         }
00507 //------------------------------------------------        
00508     Group* grp = findGroup( c->window());
00509     if( grp != NULL )
00510         grp->gotLeader( c );
00511 
00512     if ( c->isDesktop() )
00513         {
00514         desktops.append( c );
00515         if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
00516             requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
00517         }
00518     else
00519         {
00520         updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there
00521         clients.append( c );
00522         }
00523     if( !unconstrained_stacking_order.contains( c ))
00524         unconstrained_stacking_order.append( c );
00525     if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires
00526         stacking_order.append( c );    // c to be in stacking_order
00527     if( c->isTopMenu())
00528         addTopMenu( c );
00529     updateClientArea(); // this cannot be in manage(), because the client got added only now
00530     updateClientLayer( c );
00531     if( c->isDesktop())
00532         {
00533         raiseClient( c );
00534     // if there's no active client, make this desktop the active one
00535         if( activeClient() == NULL && should_get_focus.count() == 0 )
00536             activateClient( findDesktop( true, currentDesktop()));
00537         }
00538     c->checkActiveModal();
00539     checkTransients( c->window()); // SELI does this really belong here?
00540     updateStackingOrder( true ); // propagate new client
00541     if( c->isUtility() || c->isMenu() || c->isToolbar())
00542         updateToolWindows( true );
00543     checkNonExistentClients();
00544     }
00545 
00546 /*
00547   Destroys the client \a c
00548  */
00549 void Workspace::removeClient( Client* c, allowed_t )
00550     {
00551     if (c == active_popup_client)
00552         closeActivePopup();
00553 
00554     if( client_keys_client == c )
00555         setupWindowShortcutDone( false );
00556     if( !c->shortcut().isNull())
00557         c->setShortcut( QString::null ); // remove from client_keys
00558 
00559     if( c->isDialog())
00560         Notify::raise( Notify::TransDelete );
00561     if( c->isNormalWindow())
00562         Notify::raise( Notify::Delete );
00563 
00564     Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
00565     clients.remove( c );
00566     desktops.remove( c );
00567     unconstrained_stacking_order.remove( c );
00568     stacking_order.remove( c );
00569     for( int i = 1;
00570          i <= numberOfDesktops();
00571          ++i )
00572         focus_chain[ i ].remove( c );
00573     global_focus_chain.remove( c );
00574     attention_chain.remove( c );
00575     if( c->isTopMenu())
00576         removeTopMenu( c );
00577     Group* group = findGroup( c->window());
00578     if( group != NULL )
00579         group->lostLeader();
00580 
00581     if ( c == most_recently_raised )
00582         most_recently_raised = 0;
00583     should_get_focus.remove( c );
00584     Q_ASSERT( c != active_client );
00585     if ( c == last_active_client )
00586         last_active_client = 0;
00587     if( c == pending_take_activity )
00588         pending_take_activity = NULL;
00589     if( c == delayfocus_client )
00590         cancelDelayFocus();
00591 
00592     updateStackingOrder( true );
00593 
00594     if (tab_grab)
00595        tab_box->repaint();
00596 
00597     updateClientArea();
00598     }
00599 
00600 void Workspace::updateFocusChains( Client* c, FocusChainChange change )
00601     {
00602     if( !c->wantsTabFocus()) // doesn't want tab focus, remove
00603         {
00604         for( int i=1;
00605              i<= numberOfDesktops();
00606              ++i )
00607             focus_chain[i].remove(c);
00608         global_focus_chain.remove( c );
00609         return;
00610         }
00611     if(c->desktop() == NET::OnAllDesktops)
00612         { //now on all desktops, add it to focus_chains it is not already in
00613         for( int i=1; i<= numberOfDesktops(); i++)
00614             { // making first/last works only on current desktop, don't affect all desktops
00615             if( i == currentDesktop()
00616                 && ( change == FocusChainMakeFirst || change == FocusChainMakeLast ))
00617                 {
00618                 focus_chain[ i ].remove( c );
00619                 if( change == FocusChainMakeFirst )
00620                     focus_chain[ i ].append( c );
00621                 else
00622                     focus_chain[ i ].prepend( c );
00623                 }
00624             else if( !focus_chain[ i ].contains( c ))
00625                 { // add it after the active one
00626                 if( active_client != NULL && active_client != c
00627                     && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
00628                     focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
00629                 else
00630                     focus_chain[ i ].append( c ); // otherwise add as the first one
00631                 }
00632             }
00633         }
00634     else    //now only on desktop, remove it anywhere else
00635         {
00636         for( int i=1; i<= numberOfDesktops(); i++)
00637             {
00638             if( i == c->desktop())
00639                 {
00640                 if( change == FocusChainMakeFirst )
00641                     {
00642                     focus_chain[ i ].remove( c );
00643                     focus_chain[ i ].append( c );
00644                     }
00645                 else if( change == FocusChainMakeLast )
00646                     {
00647                     focus_chain[ i ].remove( c );
00648                     focus_chain[ i ].prepend( c );
00649                     }
00650                 else if( !focus_chain[ i ].contains( c ))
00651                     {
00652                     if( active_client != NULL && active_client != c
00653                         && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
00654                         focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
00655                     else
00656                         focus_chain[ i ].append( c ); // otherwise add as the first one
00657                     }
00658                 }
00659             else
00660                 focus_chain[ i ].remove( c );
00661             }
00662         }
00663     if( change == FocusChainMakeFirst )
00664         {
00665         global_focus_chain.remove( c );
00666         global_focus_chain.append( c );
00667         }
00668     else if( change == FocusChainMakeLast )
00669         {
00670         global_focus_chain.remove( c );
00671         global_focus_chain.prepend( c );
00672         }
00673     else if( !global_focus_chain.contains( c ))
00674         {
00675         if( active_client != NULL && active_client != c
00676             && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client )
00677             global_focus_chain.insert( global_focus_chain.fromLast(), c );
00678         else
00679             global_focus_chain.append( c ); // otherwise add as the first one
00680         }
00681     }
00682 
00683 void Workspace::updateCurrentTopMenu()
00684     {
00685     if( !managingTopMenus())
00686         return;
00687     // toplevel menubar handling
00688     Client* menubar = 0;
00689     bool block_desktop_menubar = false;
00690     if( active_client )
00691         {
00692         // show the new menu bar first...
00693         Client* menu_client = active_client;
00694         for(;;)
00695             {
00696             if( menu_client->isFullScreen())
00697                 block_desktop_menubar = true;
00698             for( ClientList::ConstIterator it = menu_client->transients().begin();
00699                  it != menu_client->transients().end();
00700                  ++it )
00701                 if( (*it)->isTopMenu())
00702                     {
00703                     menubar = *it;
00704                     break;
00705                     }
00706             if( menubar != NULL || !menu_client->isTransient())
00707                 break;
00708             if( menu_client->isModal() || menu_client->transientFor() == NULL )
00709                 break; // don't use mainwindow's menu if this is modal or group transient
00710             menu_client = menu_client->transientFor();
00711             }
00712         if( !menubar )
00713             { // try to find any topmenu from the application (#72113)
00714             for( ClientList::ConstIterator it = active_client->group()->members().begin();
00715                  it != active_client->group()->members().end();
00716                  ++it )
00717                 if( (*it)->isTopMenu())
00718                     {
00719                     menubar = *it;
00720                     break;
00721                     }
00722             }
00723         }
00724     if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
00725         {
00726         // Find the menubar of the desktop
00727         Client* desktop = findDesktop( true, currentDesktop());
00728         if( desktop != NULL )
00729             {
00730             for( ClientList::ConstIterator it = desktop->transients().begin();
00731                  it != desktop->transients().end();
00732                  ++it )
00733                 if( (*it)->isTopMenu())
00734                     {
00735                     menubar = *it;
00736                     break;
00737                     }
00738             }
00739         // TODO to be cleaned app with window grouping
00740         // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
00741         // thus the topmenu is not transient for it :-/.
00742         if( menubar == NULL )
00743             {
00744             for( ClientList::ConstIterator it = topmenus.begin();
00745                  it != topmenus.end();
00746                  ++it )
00747                 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
00748                     {                                     // set pointing to the root window
00749                     menubar = *it;                        // to recognize it here
00750                     break;                                // Also, with the xroot hack in kdesktop,
00751                     }                                     // there's no NET::Desktop window to be transient for
00752             }
00753         }
00754 
00755 //    kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
00756     if ( menubar )
00757         {
00758         if( active_client && !menubar->isOnDesktop( active_client->desktop()))
00759             menubar->setDesktop( active_client->desktop());
00760         menubar->hideClient( false );
00761         topmenu_space->hide();
00762         // make it appear like it's been raised manually - it's in the Dock layer anyway,
00763         // and not raising it could mess up stacking order of topmenus within one application,
00764         // and thus break raising of mainclients in raiseClient()
00765         unconstrained_stacking_order.remove( menubar );
00766         unconstrained_stacking_order.append( menubar );
00767         }
00768     else if( !block_desktop_menubar )
00769         { // no topmenu active - show the space window, so that there's not empty space
00770         topmenu_space->show();
00771         }
00772 
00773     // ... then hide the other ones. Avoids flickers.
00774     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 
00775         {
00776         if( (*it)->isTopMenu() && (*it) != menubar )
00777             (*it)->hideClient( true );
00778         }
00779     }
00780 
00781 
00782 void Workspace::updateToolWindows( bool also_hide )
00783     {
00784     // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
00785     if( !options->hideUtilityWindowsForInactive )
00786         {
00787         for( ClientList::ConstIterator it = clients.begin();
00788              it != clients.end();
00789              ++it )
00790             (*it)->hideClient( false );
00791         return;
00792         }
00793     const Group* group = NULL;
00794     const Client* client = active_client;
00795 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
00796 // will be shown; if a group transient is group, all tools in the group will be shown
00797     while( client != NULL )
00798         {
00799         if( !client->isTransient())
00800             break;
00801         if( client->groupTransient())
00802             {
00803             group = client->group();
00804             break;
00805             }
00806         client = client->transientFor();
00807         }
00808     // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
00809     // i.e. if it's not up to date
00810 
00811     // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
00812     ClientList to_show, to_hide;
00813     for( ClientList::ConstIterator it = stacking_order.begin();
00814          it != stacking_order.end();
00815          ++it )
00816         {
00817         if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
00818             {
00819             bool show = true;
00820             if( !(*it)->isTransient())
00821                 {
00822                 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
00823                     show = true;
00824                 else if( client != NULL && (*it)->group() == client->group())
00825                     show = true;
00826                 else
00827                     show = false;
00828                 }
00829             else
00830                 {
00831                 if( group != NULL && (*it)->group() == group )
00832                     show = true;
00833                 else if( client != NULL && client->hasTransient( (*it), true ))
00834                     show = true;
00835                 else
00836                     show = false;
00837                 }
00838             if( !show && also_hide )
00839                 {
00840                 const ClientList mainclients = (*it)->mainClients();
00841                 // don't hide utility windows which are standalone(?) or
00842                 // have e.g. kicker as mainwindow
00843                 if( mainclients.isEmpty())
00844                     show = true;
00845                 for( ClientList::ConstIterator it2 = mainclients.begin();
00846                      it2 != mainclients.end();
00847                      ++it2 )
00848                     {
00849                     if( (*it2)->isSpecialWindow())
00850                         show = true;
00851                     }
00852                 if( !show )
00853                     to_hide.append( *it );
00854                 }
00855             if( show )
00856                 to_show.append( *it );
00857             }
00858         } // first show new ones, then hide
00859     for( ClientList::ConstIterator it = to_show.fromLast();
00860          it != to_show.end();
00861          --it ) // from topmost
00862         // TODO since this is in stacking order, the order of taskbar entries changes :(
00863         (*it)->hideClient( false );
00864     if( also_hide )
00865         {
00866         for( ClientList::ConstIterator it = to_hide.begin();
00867              it != to_hide.end();
00868              ++it ) // from bottommost
00869             (*it)->hideClient( true );
00870         updateToolWindowsTimer.stop();
00871         }
00872     else // setActiveClient() is after called with NULL client, quickly followed
00873         {    // by setting a new client, which would result in flickering
00874         updateToolWindowsTimer.start( 50, true );
00875         }
00876     }
00877 
00878 void Workspace::slotUpdateToolWindows()
00879     {
00880     updateToolWindows( true );
00881     }
00882 
00886 void Workspace::updateColormap()
00887     {
00888     Colormap cmap = default_colormap;
00889     if ( activeClient() && activeClient()->colormap() != None )
00890         cmap = activeClient()->colormap();
00891     if ( cmap != installed_colormap ) 
00892         {
00893         XInstallColormap(qt_xdisplay(), cmap );
00894         installed_colormap = cmap;
00895         }
00896     }
00897 
00898 void Workspace::reconfigure()
00899     {
00900     reconfigureTimer.start(200, true);
00901     }
00902 
00903 
00904 void Workspace::slotSettingsChanged(int category)
00905     {
00906     kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl;
00907     if( category == (int) KApplication::SETTINGS_SHORTCUTS )
00908         readShortcuts();
00909     }
00910 
00914 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() );
00915 
00916 void Workspace::slotReconfigure()
00917     {
00918     kdDebug(1212) << "Workspace::slotReconfigure()" << endl;
00919     reconfigureTimer.stop();
00920 
00921     KGlobal::config()->reparseConfiguration();
00922     unsigned long changed = options->updateSettings();
00923     tab_box->reconfigure();
00924     popupinfo->reconfigure();
00925     initPositioning->reinitCascading( 0 );
00926     readShortcuts();
00927     forEachClient( CheckIgnoreFocusStealingProcedure());
00928     updateToolWindows( true );
00929 
00930     if( mgr->reset( changed ))
00931         { // decorations need to be recreated
00932 #if 0 // This actually seems to make things worse now
00933         QWidget curtain;
00934         curtain.setBackgroundMode( NoBackground );
00935         curtain.setGeometry( QApplication::desktop()->geometry() );
00936         curtain.show();
00937 #endif
00938         for( ClientList::ConstIterator it = clients.begin();
00939                 it != clients.end();
00940                 ++it )
00941             {
00942             (*it)->updateDecoration( true, true );
00943             }
00944         mgr->destroyPreviousPlugin();
00945         }
00946     else
00947         {
00948         forEachClient( CheckBorderSizesProcedure());
00949         }
00950 
00951     checkElectricBorders();
00952 
00953     if( options->topMenuEnabled() && !managingTopMenus())
00954         {
00955         if( topmenu_selection->claim( false ))
00956             setupTopMenuHandling();
00957         else
00958             lostTopMenuSelection();
00959         }
00960     else if( !options->topMenuEnabled() && managingTopMenus())
00961         {
00962         topmenu_selection->release();
00963         lostTopMenuSelection();
00964         }
00965     topmenu_height = 0; // invalidate used menu height
00966     if( managingTopMenus())
00967         {
00968         updateTopMenuGeometry();
00969         updateCurrentTopMenu();
00970         }
00971 
00972     loadWindowRules();
00973     for( ClientList::Iterator it = clients.begin();
00974          it != clients.end();
00975          ++it )
00976         {
00977         (*it)->setupWindowRules( true );
00978         (*it)->applyWindowRules();
00979         discardUsedWindowRules( *it, false );
00980         }
00981 
00982     if (options->resetKompmgr) // need restart
00983         {
00984         bool tmp = options->useTranslucency;
00985         stopKompmgr();
00986         if (tmp)
00987             QTimer::singleShot( 200, this, SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart
00988         }
00989     }
00990 
00991 void Workspace::loadDesktopSettings()
00992     {
00993     KConfig* c = KGlobal::config();
00994     QCString groupname;
00995     if (screen_number == 0)
00996         groupname = "Desktops";
00997     else
00998         groupname.sprintf("Desktops-screen-%d", screen_number);
00999     KConfigGroupSaver saver(c,groupname);
01000 
01001     int n = c->readNumEntry("Number", 4);
01002     number_of_desktops = n;
01003     delete workarea;
01004     workarea = new QRect[ n + 1 ];
01005     delete screenarea;
01006     screenarea = NULL;
01007     rootInfo->setNumberOfDesktops( number_of_desktops );
01008     desktop_focus_chain.resize( n );
01009     // make it +1, so that it can be accessed as [1..numberofdesktops]
01010     focus_chain.resize( n + 1 );
01011     for(int i = 1; i <= n; i++) 
01012         {
01013         QString s = c->readEntry(QString("Name_%1").arg(i),
01014                                 i18n("Desktop %1").arg(i));
01015         rootInfo->setDesktopName( i, s.utf8().data() );
01016         desktop_focus_chain[i-1] = i;
01017         }
01018     }
01019 
01020 void Workspace::saveDesktopSettings()
01021     {
01022     KConfig* c = KGlobal::config();
01023     QCString groupname;
01024     if (screen_number == 0)
01025         groupname = "Desktops";
01026     else
01027         groupname.sprintf("Desktops-screen-%d", screen_number);
01028     KConfigGroupSaver saver(c,groupname);
01029 
01030     c->writeEntry("Number", number_of_desktops );
01031     for(int i = 1; i <= number_of_desktops; i++) 
01032         {
01033         QString s = desktopName( i );
01034         QString defaultvalue = i18n("Desktop %1").arg(i);
01035         if ( s.isEmpty() ) 
01036             {
01037             s = defaultvalue;
01038             rootInfo->setDesktopName( i, s.utf8().data() );
01039             }
01040 
01041         if (s != defaultvalue) 
01042             {
01043             c->writeEntry( QString("Name_%1").arg(i), s );
01044             }
01045         else 
01046             {
01047             QString currentvalue = c->readEntry(QString("Name_%1").arg(i));
01048             if (currentvalue != defaultvalue)
01049                 c->writeEntry( QString("Name_%1").arg(i), "" );
01050             }
01051         }
01052     }
01053 
01054 QStringList Workspace::configModules(bool controlCenter)
01055     {
01056     QStringList args;
01057     args <<  "kde-kwindecoration.desktop";
01058     if (controlCenter)
01059         args << "kde-kwinoptions.desktop";
01060     else if (kapp->authorizeControlModule("kde-kwinoptions.desktop"))
01061         args  << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwintranslucency";
01062     return args;
01063     }
01064 
01065 void Workspace::configureWM()
01066     {
01067     KApplication::kdeinitExec( "kcmshell", configModules(false) );
01068     }
01069 
01073 void Workspace::doNotManage( QString title )
01074     {
01075     doNotManageList.append( title );
01076     }
01077 
01081 bool Workspace::isNotManaged( const QString& title )
01082     {
01083     for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 
01084         {
01085         QRegExp r( (*it) );
01086         if (r.search(title) != -1) 
01087             {
01088             doNotManageList.remove( it );
01089             return TRUE;
01090             }
01091         }
01092     return FALSE;
01093     }
01094 
01098 void Workspace::refresh() 
01099     {
01100     QWidget w;
01101     w.setGeometry( QApplication::desktop()->geometry() );
01102     w.show();
01103     w.hide();
01104     QApplication::flushX();
01105     }
01106 
01114 class ObscuringWindows
01115     {
01116     public:
01117         ~ObscuringWindows();
01118         void create( Client* c );
01119     private:
01120         QValueList<Window> obscuring_windows;
01121         static QValueList<Window>* cached;
01122         static unsigned int max_cache_size;
01123     };
01124 
01125 QValueList<Window>* ObscuringWindows::cached = 0;
01126 unsigned int ObscuringWindows::max_cache_size = 0;
01127 
01128 void ObscuringWindows::create( Client* c )
01129     {
01130     if( cached == 0 )
01131         cached = new QValueList<Window>;
01132     Window obs_win;
01133     XWindowChanges chngs;
01134     int mask = CWSibling | CWStackMode;
01135     if( cached->count() > 0 ) 
01136         {
01137         cached->remove( obs_win = cached->first());
01138         chngs.x = c->x();
01139         chngs.y = c->y();
01140         chngs.width = c->width();
01141         chngs.height = c->height();
01142         mask |= CWX | CWY | CWWidth | CWHeight;
01143         }
01144     else 
01145         {
01146         XSetWindowAttributes a;
01147         a.background_pixmap = None;
01148         a.override_redirect = True;
01149         obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(),
01150             c->width(), c->height(), 0, CopyFromParent, InputOutput,
01151             CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
01152         }
01153     chngs.sibling = c->frameId();
01154     chngs.stack_mode = Below;
01155     XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs );
01156     XMapWindow( qt_xdisplay(), obs_win );
01157     obscuring_windows.append( obs_win );
01158     }
01159 
01160 ObscuringWindows::~ObscuringWindows()
01161     {
01162     max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1;
01163     for( QValueList<Window>::ConstIterator it = obscuring_windows.begin();
01164          it != obscuring_windows.end();
01165          ++it ) 
01166         {
01167         XUnmapWindow( qt_xdisplay(), *it );
01168         if( cached->count() < max_cache_size )
01169             cached->prepend( *it );
01170         else
01171             XDestroyWindow( qt_xdisplay(), *it );
01172         }
01173     }
01174 
01175 
01182 bool Workspace::setCurrentDesktop( int new_desktop )
01183     {
01184     if (new_desktop < 1 || new_desktop > number_of_desktops )
01185         return false;
01186 
01187     closeActivePopup();
01188     ++block_focus;
01189 // TODO    Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
01190     StackingUpdatesBlocker blocker( this );
01191 
01192     int old_desktop = current_desktop;
01193     if (new_desktop != current_desktop) 
01194         {
01195         ++block_showing_desktop;
01196         /*
01197           optimized Desktop switching: unmapping done from back to front
01198           mapping done from front to back => less exposure events
01199         */
01200         Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
01201 
01202         ObscuringWindows obs_wins;
01203 
01204         current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works)
01205 
01206         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
01207             if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
01208                 {
01209                 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
01210                     obs_wins.create( *it );
01211                 (*it)->updateVisibility();
01212                 }
01213 
01214         rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
01215 
01216         if( movingClient && !movingClient->isOnDesktop( new_desktop ))
01217             movingClient->setDesktop( new_desktop );
01218 
01219         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
01220             if ( (*it)->isOnDesktop( new_desktop ) )
01221                 (*it)->updateVisibility();
01222 
01223         --block_showing_desktop;
01224         if( showingDesktop()) // do this only after desktop change to avoid flicker
01225             resetShowingDesktop( false );
01226         }
01227 
01228     // restore the focus on this desktop
01229     --block_focus;
01230     Client* c = 0;
01231 
01232     if ( options->focusPolicyIsReasonable()) 
01233         {
01234         // Search in focus chain
01235         if ( movingClient != NULL && active_client == movingClient
01236             && focus_chain[currentDesktop()].contains( active_client )
01237             && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01238             {
01239             c = active_client; // the requestFocus below will fail, as the client is already active
01240             }
01241         if ( !c ) 
01242             {
01243             for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
01244                  it != focus_chain[currentDesktop()].end();
01245                  --it )
01246                 {
01247                 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop())
01248                     {
01249                     c = *it;
01250                     break;
01251                     }
01252                 }
01253             }
01254         }
01255 
01256     //if "unreasonable focus policy"
01257     // and active_client is on_all_desktops and under mouse (hence == old_active_client),
01258     // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
01259     else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01260       c= active_client;
01261 
01262     if( c != active_client )
01263         setActiveClient( NULL, Allowed );
01264 
01265     if ( c ) 
01266         requestFocus( c );
01267     else if( !desktops.isEmpty() ) 
01268         requestFocus( findDesktop( true, currentDesktop()));
01269     else 
01270         focusToNull();
01271 
01272     updateCurrentTopMenu();
01273 
01274     // Update focus chain:
01275     //  If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
01276     //   Output: chain = { 3, 1, 2, 4 }.
01277 //    kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n")
01278 //      .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
01279     for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- )
01280         desktop_focus_chain[i] = desktop_focus_chain[i-1];
01281     desktop_focus_chain[0] = currentDesktop();
01282 
01283 //    QString s = "desktop_focus_chain[] = { ";
01284 //    for( uint i = 0; i < desktop_focus_chain.size(); i++ )
01285 //        s += QString::number(desktop_focus_chain[i]) + ", ";
01286 //    kdDebug(1212) << s << "}\n";
01287 
01288     if( old_desktop != 0 )  // not for the very first time
01289         popupinfo->showInfo( desktopName(currentDesktop()) );
01290     return true;
01291     }
01292 
01293 // called only from DCOP
01294 void Workspace::nextDesktop()
01295     {
01296     int desktop = currentDesktop() + 1;
01297     setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
01298     }
01299 
01300 // called only from DCOP
01301 void Workspace::previousDesktop()
01302     {
01303     int desktop = currentDesktop() - 1;
01304     setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
01305     }
01306 
01307 int Workspace::desktopToRight( int desktop ) const
01308     {
01309     int x,y;
01310     calcDesktopLayout(x,y);
01311     int dt = desktop-1;
01312     if (layoutOrientation == Qt::Vertical)
01313         {
01314         dt += y;
01315         if ( dt >= numberOfDesktops() ) 
01316             {
01317             if ( options->rollOverDesktops )
01318               dt -= numberOfDesktops();
01319             else
01320               return desktop;
01321             }
01322         }
01323     else
01324         {
01325         int d = (dt % x) + 1;
01326         if ( d >= x ) 
01327             {
01328             if ( options->rollOverDesktops )
01329               d -= x;
01330             else
01331               return desktop;
01332             }
01333         dt = dt - (dt % x) + d;
01334         }
01335     return dt+1;
01336     }
01337 
01338 int Workspace::desktopToLeft( int desktop ) const
01339     {
01340     int x,y;
01341     calcDesktopLayout(x,y);
01342     int dt = desktop-1;
01343     if (layoutOrientation == Qt::Vertical)
01344         {
01345         dt -= y;
01346         if ( dt < 0 ) 
01347             {
01348             if ( options->rollOverDesktops )
01349               dt += numberOfDesktops();
01350             else
01351               return desktop;
01352             }
01353         }
01354     else
01355         {
01356         int d = (dt % x) - 1;
01357         if ( d < 0 ) 
01358             {
01359             if ( options->rollOverDesktops )
01360               d += x;
01361             else
01362               return desktop;
01363             }
01364         dt = dt - (dt % x) + d;
01365         }
01366     return dt+1;
01367     }
01368 
01369 int Workspace::desktopUp( int desktop ) const
01370     {
01371     int x,y;
01372     calcDesktopLayout(x,y);
01373     int dt = desktop-1;
01374     if (layoutOrientation == Qt::Horizontal)
01375         {
01376         dt -= x;
01377         if ( dt < 0 ) 
01378             {
01379             if ( options->rollOverDesktops )
01380               dt += numberOfDesktops();
01381             else
01382               return desktop;
01383             }
01384         }
01385     else
01386         {
01387         int d = (dt % y) - 1;
01388         if ( d < 0 ) 
01389             {
01390             if ( options->rollOverDesktops )
01391               d += y;
01392             else
01393               return desktop;
01394             }
01395         dt = dt - (dt % y) + d;
01396         }
01397     return dt+1;
01398     }
01399 
01400 int Workspace::desktopDown( int desktop ) const
01401     {
01402     int x,y;
01403     calcDesktopLayout(x,y);
01404     int dt = desktop-1;
01405     if (layoutOrientation == Qt::Horizontal)
01406         {
01407         dt += x;
01408         if ( dt >= numberOfDesktops() ) 
01409             {
01410             if ( options->rollOverDesktops )
01411               dt -= numberOfDesktops();
01412             else
01413               return desktop;
01414             }
01415         }
01416     else
01417         {
01418         int d = (dt % y) + 1;
01419         if ( d >= y ) 
01420             {
01421             if ( options->rollOverDesktops )
01422               d -= y;
01423             else
01424               return desktop;
01425             }
01426         dt = dt - (dt % y) + d;
01427         }
01428     return dt+1;
01429     }
01430 
01431 
01435 void Workspace::setNumberOfDesktops( int n )
01436     {
01437     if ( n == number_of_desktops )
01438         return;
01439     int old_number_of_desktops = number_of_desktops;
01440     number_of_desktops = n;
01441 
01442     if( currentDesktop() > numberOfDesktops())
01443         setCurrentDesktop( numberOfDesktops());
01444 
01445     // if increasing the number, do the resizing now,
01446     // otherwise after the moving of windows to still existing desktops
01447     if( old_number_of_desktops < number_of_desktops ) 
01448         {
01449         rootInfo->setNumberOfDesktops( number_of_desktops );
01450         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01451         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01452         delete[] viewports;
01453         updateClientArea( true );
01454         focus_chain.resize( number_of_desktops + 1 );
01455         }
01456 
01457     // if the number of desktops decreased, move all
01458     // windows that would be hidden to the last visible desktop
01459     if( old_number_of_desktops > number_of_desktops ) 
01460         {
01461         for( ClientList::ConstIterator it = clients.begin();
01462               it != clients.end();
01463               ++it) 
01464             {
01465             if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
01466                 sendClientToDesktop( *it, numberOfDesktops(), true );
01467             }
01468         }
01469     if( old_number_of_desktops > number_of_desktops ) 
01470         {
01471         rootInfo->setNumberOfDesktops( number_of_desktops );
01472         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01473         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01474         delete[] viewports;
01475         updateClientArea( true );
01476         focus_chain.resize( number_of_desktops + 1 );
01477         }
01478 
01479     saveDesktopSettings();
01480 
01481     // Resize and reset the desktop focus chain.
01482     desktop_focus_chain.resize( n );
01483     for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
01484         desktop_focus_chain[i] = i+1;
01485     }
01486 
01492 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
01493     {
01494     bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
01495     c->setDesktop( desk );
01496     if ( c->desktop() != desk ) // no change or desktop forced
01497         return;
01498     desk = c->desktop(); // Client did range checking
01499 
01500     if ( c->isOnDesktop( currentDesktop() ) )
01501         {
01502         if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
01503             && !was_on_desktop // for stickyness changes
01504             && !dont_activate )
01505             requestFocus( c );
01506         else
01507             restackClientUnderActive( c );
01508         }
01509     else 
01510         {
01511         raiseClient( c );
01512         }
01513 
01514     ClientList transients_stacking_order = ensureStackingOrder( c->transients());
01515     for( ClientList::ConstIterator it = transients_stacking_order.begin();
01516          it != transients_stacking_order.end();
01517          ++it )
01518         sendClientToDesktop( *it, desk, dont_activate );
01519     updateClientArea();
01520     }
01521 
01522 void Workspace::setDesktopLayout(int o, int x, int y)
01523     {
01524     layoutOrientation = (Qt::Orientation) o;
01525     layoutX = x;
01526     layoutY = y;
01527     }
01528 
01529 void Workspace::calcDesktopLayout(int &x, int &y) const
01530     {
01531     x = layoutX;
01532     y = layoutY;
01533     if ((x == -1) && (y > 0))
01534        x = (numberOfDesktops()+y-1) / y;
01535     else if ((y == -1) && (x > 0))
01536        y = (numberOfDesktops()+x-1) / x;
01537 
01538     if (x == -1)
01539        x = 1;
01540     if (y == -1)
01541        y = 1;
01542     }
01543 
01548 bool Workspace::addSystemTrayWin( WId w )
01549     {
01550     if ( systemTrayWins.contains( w ) )
01551         return TRUE;
01552 
01553     NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor );
01554     WId trayWinFor = ni.kdeSystemTrayWinFor();
01555     if ( !trayWinFor )
01556         return FALSE;
01557     systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) );
01558     XSelectInput( qt_xdisplay(), w,
01559                   StructureNotifyMask
01560                   );
01561     XAddToSaveSet( qt_xdisplay(), w );
01562     propagateSystemTrayWins();
01563     return TRUE;
01564     }
01565 
01570 bool Workspace::removeSystemTrayWin( WId w, bool check )
01571     {
01572     if ( !systemTrayWins.contains( w ) )
01573         return FALSE;
01574     if( check )
01575         {
01576     // When getting UnmapNotify, it's not clear if it's the systray
01577     // reparenting the window into itself, or if it's the window
01578     // going away. This is obviously a flaw in the design, and we were
01579     // just lucky it worked for so long. Kicker's systray temporarily
01580     // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while
01581     // embedding it, allowing KWin to figure out. Kicker just mustn't
01582     // crash before removing it again ... *shrug* .
01583         int num_props;
01584         Atom* props = XListProperties( qt_xdisplay(), w, &num_props );
01585         if( props != NULL )
01586             {
01587             for( int i = 0;
01588                  i < num_props;
01589                  ++i )
01590                 if( props[ i ] == atoms->kde_system_tray_embedding )
01591                     {
01592                     XFree( props );
01593                     return false;
01594                     }
01595             XFree( props );
01596             }
01597         }
01598     systemTrayWins.remove( w );
01599     propagateSystemTrayWins();
01600     return TRUE;
01601     }
01602 
01603 
01607 void Workspace::propagateSystemTrayWins()
01608     {
01609     Window *cl = new Window[ systemTrayWins.count()];
01610 
01611     int i = 0;
01612     for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 
01613         {
01614         cl[i++] =  (*it).win;
01615         }
01616 
01617     rootInfo->setKDESystemTrayWindows( cl, i );
01618     delete [] cl;
01619     }
01620 
01621 
01622 void Workspace::killWindowId( Window window_to_kill )
01623     {
01624     if( window_to_kill == None )
01625         return;
01626     Window window = window_to_kill;
01627     Client* client = NULL;
01628     for(;;) 
01629         {
01630         client = findClient( FrameIdMatchPredicate( window ));
01631         if( client != NULL ) // found the client
01632             break;
01633         Window parent, root;
01634         Window* children;
01635         unsigned int children_count;
01636         XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count );
01637         if( children != NULL )
01638             XFree( children );
01639         if( window == root ) // we didn't find the client, probably an override-redirect window
01640             break;
01641         window = parent; // go up
01642         }
01643     if( client != NULL )
01644         client->killWindow();
01645     else
01646         XKillClient( qt_xdisplay(), window_to_kill );
01647     }
01648 
01649 
01650 void Workspace::sendPingToWindow( Window window, Time timestamp )
01651     {
01652     rootInfo->sendPing( window, timestamp );
01653     }
01654 
01655 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
01656     {
01657     rootInfo->takeActivity( c->window(), timestamp, flags );
01658     pending_take_activity = c;
01659     }
01660 
01661 
01665 void Workspace::slotGrabWindow()
01666     {
01667     if ( active_client ) 
01668         {
01669         QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
01670 
01671     //No XShape - no work.
01672         if( Shape::available()) 
01673             {
01674         //As the first step, get the mask from XShape.
01675             int count, order;
01676             XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(),
01677                                                      ShapeBounding, &count, &order);
01678         //The ShapeBounding region is the outermost shape of the window;
01679         //ShapeBounding - ShapeClipping is defined to be the border.
01680         //Since the border area is part of the window, we use bounding
01681         // to limit our work region
01682             if (rects) 
01683                 {
01684         //Create a QRegion from the rectangles describing the bounding mask.
01685                 QRegion contents;
01686                 for (int pos = 0; pos < count; pos++)
01687                     contents += QRegion(rects[pos].x, rects[pos].y,
01688                                         rects[pos].width, rects[pos].height);
01689                 XFree(rects);
01690 
01691         //Create the bounding box.
01692                 QRegion bbox(0, 0, snapshot.width(), snapshot.height());
01693 
01694         //Get the masked away area.
01695                 QRegion maskedAway = bbox - contents;
01696                 QMemArray<QRect> maskedAwayRects = maskedAway.rects();
01697 
01698         //Construct a bitmap mask from the rectangles
01699                 QBitmap mask( snapshot.width(), snapshot.height());
01700                 QPainter p(&mask);
01701                 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1);
01702                 for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
01703                     p.fillRect(maskedAwayRects[pos], Qt::color0);
01704                 p.end();
01705                 snapshot.setMask(mask);
01706                 }
01707             }
01708 
01709         QClipboard *cb = QApplication::clipboard();
01710         cb->setPixmap( snapshot );
01711         }
01712     else
01713         slotGrabDesktop();
01714     }
01715 
01719 void Workspace::slotGrabDesktop()
01720     {
01721     QPixmap p = QPixmap::grabWindow( qt_xrootwin() );
01722     QClipboard *cb = QApplication::clipboard();
01723     cb->setPixmap( p );
01724     }
01725 
01726 
01730 void Workspace::slotMouseEmulation()
01731     {
01732 
01733     if ( mouse_emulation ) 
01734         {
01735         XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01736         mouse_emulation = FALSE;
01737         return;
01738         }
01739 
01740     if ( XGrabKeyboard(qt_xdisplay(),
01741                        root, FALSE,
01742                        GrabModeAsync, GrabModeAsync,
01743                        qt_x_time) == GrabSuccess ) 
01744         {
01745         mouse_emulation = TRUE;
01746         mouse_emulation_state = 0;
01747         mouse_emulation_window = 0;
01748         }
01749     }
01750 
01757 WId Workspace::getMouseEmulationWindow()
01758     {
01759     Window root;
01760     Window child = qt_xrootwin();
01761     int root_x, root_y, lx, ly;
01762     uint state;
01763     Window w;
01764     Client * c = 0;
01765     do 
01766         {
01767         w = child;
01768         if (!c)
01769             c = findClient( FrameIdMatchPredicate( w ));
01770         XQueryPointer( qt_xdisplay(), w, &root, &child,
01771                        &root_x, &root_y, &lx, &ly, &state );
01772         } while  ( child != None && child != w );
01773 
01774     if ( c && !c->isActive() )
01775         activateClient( c );
01776     return (WId) w;
01777     }
01778 
01782 unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state )
01783     {
01784     if ( !w )
01785         return state;
01786     QWidget* widget = QWidget::find( w );
01787     if ( (!widget ||  widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) 
01788         {
01789         int x, y;
01790         Window xw;
01791         XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw );
01792         if ( type == EmuMove ) 
01793             { // motion notify events
01794             XEvent e;
01795             e.type = MotionNotify;
01796             e.xmotion.window = w;
01797             e.xmotion.root = qt_xrootwin();
01798             e.xmotion.subwindow = w;
01799             e.xmotion.time = qt_x_time;
01800             e.xmotion.x = x;
01801             e.xmotion.y = y;
01802             e.xmotion.x_root = pos.x();
01803             e.xmotion.y_root = pos.y();
01804             e.xmotion.state = state;
01805             e.xmotion.is_hint = NotifyNormal;
01806             XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, &e );
01807             }
01808         else 
01809             {
01810             XEvent e;
01811             e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
01812             e.xbutton.window = w;
01813             e.xbutton.root = qt_xrootwin();
01814             e.xbutton.subwindow = w;
01815             e.xbutton.time = qt_x_time;
01816             e.xbutton.x = x;
01817             e.xbutton.y = y;
01818             e.xbutton.x_root = pos.x();
01819             e.xbutton.y_root = pos.y();
01820             e.xbutton.state = state;
01821             e.xbutton.button = button;
01822             XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, &e );
01823 
01824             if ( type == EmuPress ) 
01825                 {
01826                 switch ( button ) 
01827                     {
01828                     case 2:
01829                         state |= Button2Mask;
01830                         break;
01831                     case 3:
01832                         state |= Button3Mask;
01833                         break;
01834                     default: // 1
01835                         state |= Button1Mask;
01836                         break;
01837                     }
01838                 }
01839             else 
01840                 {
01841                 switch ( button ) 
01842                     {
01843                     case 2:
01844                         state &= ~Button2Mask;
01845                         break;
01846                     case 3:
01847                         state &= ~Button3Mask;
01848                         break;
01849                     default: // 1
01850                         state &= ~Button1Mask;
01851                         break;
01852                     }
01853                 }
01854             }
01855         }
01856     return state;
01857     }
01858 
01862 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
01863     {
01864     if ( root != qt_xrootwin() )
01865         return FALSE;
01866     int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0);
01867     int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
01868 
01869     bool is_control = km & ControlMask;
01870     bool is_alt = km & Mod1Mask;
01871     bool is_shift = km & ShiftMask;
01872     int delta = is_control?1:is_alt?32:8;
01873     QPoint pos = QCursor::pos();
01874 
01875     switch ( kc ) 
01876         {
01877         case XK_Left:
01878         case XK_KP_Left:
01879             pos.rx() -= delta;
01880             break;
01881         case XK_Right:
01882         case XK_KP_Right:
01883             pos.rx() += delta;
01884             break;
01885         case XK_Up:
01886         case XK_KP_Up:
01887             pos.ry() -= delta;
01888             break;
01889         case XK_Down:
01890         case XK_KP_Down:
01891             pos.ry() += delta;
01892             break;
01893         case XK_F1:
01894             if ( !mouse_emulation_state )
01895                 mouse_emulation_window = getMouseEmulationWindow();
01896             if ( (mouse_emulation_state & Button1Mask) == 0 )
01897                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01898             if ( !is_shift )
01899                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01900             break;
01901         case XK_F2:
01902             if ( !mouse_emulation_state )
01903                 mouse_emulation_window = getMouseEmulationWindow();
01904             if ( (mouse_emulation_state & Button2Mask) == 0 )
01905                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
01906             if ( !is_shift )
01907                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01908             break;
01909         case XK_F3:
01910             if ( !mouse_emulation_state )
01911                 mouse_emulation_window = getMouseEmulationWindow();
01912             if ( (mouse_emulation_state & Button3Mask) == 0 )
01913                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
01914             if ( !is_shift )
01915                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01916             break;
01917         case XK_Return:
01918         case XK_space:
01919         case XK_KP_Enter:
01920         case XK_KP_Space: 
01921             {
01922             if ( !mouse_emulation_state ) 
01923                 {
01924             // nothing was pressed, fake a LMB click
01925                 mouse_emulation_window = getMouseEmulationWindow();
01926                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01927                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01928                 }
01929             else 
01930                 { // release all
01931                 if ( mouse_emulation_state & Button1Mask )
01932                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01933                 if ( mouse_emulation_state & Button2Mask )
01934                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01935                 if ( mouse_emulation_state & Button3Mask )
01936                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01937                 }
01938             }
01939     // fall through
01940         case XK_Escape:
01941             XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01942             mouse_emulation = FALSE;
01943             return TRUE;
01944         default:
01945             return FALSE;
01946         }
01947 
01948     QCursor::setPos( pos );
01949     if ( mouse_emulation_state )
01950         mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0,  mouse_emulation_state );
01951     return TRUE;
01952 
01953     }
01954 
01960 QWidget* Workspace::desktopWidget()
01961     {
01962     return desktop_widget;
01963     }
01964 
01965 //Delayed focus functions
01966 void Workspace::delayFocus()
01967     {
01968     requestFocus( delayfocus_client );
01969     cancelDelayFocus();
01970     }
01971     
01972 void Workspace::requestDelayFocus( Client* c )
01973     {
01974     delayfocus_client = c;
01975     delete delayFocusTimer;
01976     delayFocusTimer = new QTimer( this );
01977     connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) );
01978     delayFocusTimer->start( options->delayFocusInterval, TRUE  );
01979     }
01980     
01981 void Workspace::cancelDelayFocus()
01982     {
01983     delete delayFocusTimer;
01984     delayFocusTimer = 0;
01985     }
01986 
01987 // Electric Borders
01988 //========================================================================//
01989 // Electric Border Window management. Electric borders allow a user
01990 // to change the virtual desktop by moving the mouse pointer to the
01991 // borders. Technically this is done with input only windows. Since
01992 // electric borders can be switched on and off, we have these two
01993 // functions to create and destroy them.
01994 void Workspace::checkElectricBorders( bool force )
01995     {
01996     if( force )
01997         destroyBorderWindows();
01998     
01999     electric_current_border = 0;
02000 
02001     QRect r = QApplication::desktop()->geometry();
02002     electricTop = r.top();
02003     electricBottom = r.bottom();
02004     electricLeft = r.left();
02005     electricRight = r.right();
02006 
02007     if (options->electricBorders() == Options::ElectricAlways)
02008        createBorderWindows();
02009     else
02010        destroyBorderWindows();
02011     }
02012 
02013 void Workspace::createBorderWindows()
02014     {
02015     if ( electric_have_borders )
02016         return;
02017 
02018     electric_have_borders = true;
02019 
02020     QRect r = QApplication::desktop()->geometry();
02021     XSetWindowAttributes attributes;
02022     unsigned long valuemask;
02023     attributes.override_redirect = True;
02024     attributes.event_mask =  ( EnterWindowMask | LeaveWindowMask );
02025     valuemask=  (CWOverrideRedirect | CWEventMask | CWCursor );
02026     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02027                                           XC_sb_up_arrow);
02028     electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02029                                 0,0,
02030                                 r.width(),1,
02031                                 0,
02032                                 CopyFromParent, InputOnly,
02033                                 CopyFromParent,
02034                                 valuemask, &attributes);
02035     XMapWindow(qt_xdisplay(), electric_top_border);
02036 
02037     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02038                                           XC_sb_down_arrow);
02039     electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02040                                    0,r.height()-1,
02041                                    r.width(),1,
02042                                    0,
02043                                    CopyFromParent, InputOnly,
02044                                    CopyFromParent,
02045                                    valuemask, &attributes);
02046     XMapWindow(qt_xdisplay(), electric_bottom_border);
02047 
02048     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02049                                           XC_sb_left_arrow);
02050     electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02051                                  0,0,
02052                                  1,r.height(),
02053                                  0,
02054                                  CopyFromParent, InputOnly,
02055                                  CopyFromParent,
02056                                  valuemask, &attributes);
02057     XMapWindow(qt_xdisplay(), electric_left_border);
02058 
02059     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
02060                                           XC_sb_right_arrow);
02061     electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
02062                                   r.width()-1,0,
02063                                   1,r.height(),
02064                                   0,
02065                                   CopyFromParent, InputOnly,
02066                                   CopyFromParent,
02067                                   valuemask, &attributes);
02068     XMapWindow(qt_xdisplay(),  electric_right_border);
02069     // Set XdndAware on the windows, so that DND enter events are received (#86998)
02070     Atom version = 4; // XDND version
02071     XChangeProperty( qt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM,
02072         32, PropModeReplace, ( unsigned char* )&version, 1 );
02073     XChangeProperty( qt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM,
02074         32, PropModeReplace, ( unsigned char* )&version, 1 );
02075     XChangeProperty( qt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM,
02076         32, PropModeReplace, ( unsigned char* )&version, 1 );
02077     XChangeProperty( qt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM,
02078         32, PropModeReplace, ( unsigned char* )&version, 1 );
02079     }
02080 
02081 
02082 // Electric Border Window management. Electric borders allow a user
02083 // to change the virtual desktop by moving the mouse pointer to the
02084 // borders. Technically this is done with input only windows. Since
02085 // electric borders can be switched on and off, we have these two
02086 // functions to create and destroy them.
02087 void Workspace::destroyBorderWindows()
02088     {
02089     if( !electric_have_borders)
02090       return;
02091 
02092     electric_have_borders = false;
02093 
02094     if(electric_top_border)
02095       XDestroyWindow(qt_xdisplay(),electric_top_border);
02096     if(electric_bottom_border)
02097       XDestroyWindow(qt_xdisplay(),electric_bottom_border);
02098     if(electric_left_border)
02099       XDestroyWindow(qt_xdisplay(),electric_left_border);
02100     if(electric_right_border)
02101       XDestroyWindow(qt_xdisplay(),electric_right_border);
02102 
02103     electric_top_border    = None;
02104     electric_bottom_border = None;
02105     electric_left_border   = None;
02106     electric_right_border  = None;
02107     }
02108 
02109 void Workspace::clientMoved(const QPoint &pos, Time now)
02110     {
02111     if (options->electricBorders() == Options::ElectricDisabled)
02112        return;
02113 
02114     if ((pos.x() != electricLeft) &&
02115         (pos.x() != electricRight) &&
02116         (pos.y() != electricTop) &&
02117         (pos.y() != electricBottom))
02118        return;
02119 
02120     Time treshold_set = options->electricBorderDelay(); // set timeout
02121     Time treshold_reset = 250; // reset timeout
02122     int distance_reset = 30; // Mouse should not move more than this many pixels
02123 
02124     int border = 0;
02125     if (pos.x() == electricLeft)
02126        border = 1;
02127     else if (pos.x() == electricRight)
02128        border = 2;
02129     else if (pos.y() == electricTop)
02130        border = 3;
02131     else if (pos.y() == electricBottom)
02132        border = 4;
02133 
02134     if ((electric_current_border == border) &&
02135         (timestampDiff(electric_time_last, now) < treshold_reset) &&
02136         ((pos-electric_push_point).manhattanLength() < distance_reset))
02137         {
02138         electric_time_last = now;
02139 
02140         if (timestampDiff(electric_time_first, now) > treshold_set)
02141             {
02142             electric_current_border = 0;
02143 
02144             QRect r = QApplication::desktop()->geometry();
02145             int offset;
02146 
02147             int desk_before = currentDesktop();
02148             switch(border)
02149                 {
02150                 case 1:
02151                  slotSwitchDesktopLeft();
02152                  if (currentDesktop() != desk_before) 
02153                     {
02154                     offset = r.width() / 5;
02155                     QCursor::setPos(r.width() - offset, pos.y());
02156                     }
02157                 break;
02158 
02159                case 2:
02160                 slotSwitchDesktopRight();
02161                 if (currentDesktop() != desk_before) 
02162                     {
02163                     offset = r.width() / 5;
02164                     QCursor::setPos(offset, pos.y());
02165                     }
02166                 break;
02167 
02168                case 3:
02169                 slotSwitchDesktopUp();
02170                 if (currentDesktop() != desk_before) 
02171                     {
02172                     offset = r.height() / 5;
02173                     QCursor::setPos(pos.x(), r.height() - offset);
02174                     }
02175                 break;
02176 
02177                case 4:
02178                 slotSwitchDesktopDown();
02179                 if (currentDesktop() != desk_before) 
02180                     {
02181                     offset = r.height() / 5;
02182                     QCursor::setPos(pos.x(), offset);
02183                     }
02184                 break;
02185                 }
02186             return;
02187             }
02188         }
02189     else 
02190         {
02191         electric_current_border = border;
02192         electric_time_first = now;
02193         electric_time_last = now;
02194         electric_push_point = pos;
02195         }
02196 
02197     int mouse_warp = 1;
02198 
02199   // reset the pointer to find out wether the user is really pushing
02200     switch( border)
02201         {
02202         case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break;
02203         case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break;
02204         case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break;
02205         case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break;
02206         }
02207     }
02208 
02209 // this function is called when the user entered an electric border
02210 // with the mouse. It may switch to another virtual desktop
02211 bool Workspace::electricBorder(XEvent *e)
02212     {
02213     if( !electric_have_borders )
02214         return false;
02215     if( e->type == EnterNotify )
02216         {
02217         if( e->xcrossing.window == electric_top_border ||
02218             e->xcrossing.window == electric_left_border ||
02219             e->xcrossing.window == electric_bottom_border ||
02220             e->xcrossing.window == electric_right_border)
02221             // the user entered an electric border
02222             {
02223             clientMoved( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time );
02224             return true;
02225             }
02226         }
02227     if( e->type == ClientMessage )
02228         {
02229         if( e->xclient.message_type == atoms->xdnd_position
02230             && ( e->xclient.window == electric_top_border
02231                  || e->xclient.window == electric_bottom_border
02232                  || e->xclient.window == electric_left_border
02233                  || e->xclient.window == electric_right_border ))
02234             {
02235             updateXTime();
02236             clientMoved( QPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), qt_x_time );
02237             return true;
02238             }
02239         }
02240     return false;
02241     }
02242 
02243 // electric borders (input only windows) have to be always on the
02244 // top. For that reason kwm calls this function always after some
02245 // windows have been raised.
02246 void Workspace::raiseElectricBorders()
02247     {
02248 
02249     if(electric_have_borders)
02250         {
02251         XRaiseWindow(qt_xdisplay(), electric_top_border);
02252         XRaiseWindow(qt_xdisplay(), electric_left_border);
02253         XRaiseWindow(qt_xdisplay(), electric_bottom_border);
02254         XRaiseWindow(qt_xdisplay(), electric_right_border);
02255         }
02256     }
02257 
02258 void Workspace::addTopMenu( Client* c )
02259     {
02260     assert( c->isTopMenu());
02261     assert( !topmenus.contains( c ));
02262     topmenus.append( c );
02263     if( managingTopMenus())
02264         {
02265         int minsize = c->minSize().height();
02266         if( minsize > topMenuHeight())
02267             {
02268             topmenu_height = minsize;
02269             updateTopMenuGeometry();
02270             }
02271         updateTopMenuGeometry( c );
02272         updateCurrentTopMenu();
02273         }
02274 //        kdDebug() << "NEW TOPMENU:" << c << endl;
02275     }
02276 
02277 void Workspace::removeTopMenu( Client* c )
02278     {
02279 //    if( c->isTopMenu())
02280 //        kdDebug() << "REMOVE TOPMENU:" << c << endl;
02281     assert( c->isTopMenu());
02282     assert( topmenus.contains( c ));
02283     topmenus.remove( c );
02284     updateCurrentTopMenu();
02285     // TODO reduce topMenuHeight() if possible?
02286     }
02287 
02288 void Workspace::lostTopMenuSelection()
02289     {
02290 //    kdDebug() << "lost TopMenu selection" << endl;
02291     // make sure this signal is always set when not owning the selection
02292     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02293     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02294     if( !managing_topmenus )
02295         return;
02296     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02297     disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
02298     managing_topmenus = false;
02299     delete topmenu_space;
02300     topmenu_space = NULL;
02301     updateClientArea();
02302     for( ClientList::ConstIterator it = topmenus.begin();
02303          it != topmenus.end();
02304          ++it )
02305         (*it)->checkWorkspacePosition();
02306     }
02307 
02308 void Workspace::lostTopMenuOwner()
02309     {
02310     if( !options->topMenuEnabled())
02311         return;
02312 //    kdDebug() << "TopMenu selection lost owner" << endl;
02313     if( !topmenu_selection->claim( false ))
02314         {
02315 //        kdDebug() << "Failed to claim TopMenu selection" << endl;
02316         return;
02317         }
02318 //    kdDebug() << "claimed TopMenu selection" << endl;
02319     setupTopMenuHandling();
02320     }
02321 
02322 void Workspace::setupTopMenuHandling()
02323     {
02324     if( managing_topmenus )
02325         return;
02326     connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
02327     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02328     managing_topmenus = true;
02329     topmenu_space = new QWidget;
02330     Window stack[ 2 ];
02331     stack[ 0 ] = supportWindow->winId();
02332     stack[ 1 ] = topmenu_space->winId();
02333     XRestackWindows(qt_xdisplay(), stack, 2);
02334     updateTopMenuGeometry();
02335     topmenu_space->show();
02336     updateClientArea();
02337     updateCurrentTopMenu();
02338     }
02339 
02340 int Workspace::topMenuHeight() const
02341     {
02342     if( topmenu_height == 0 )
02343         { // simply create a dummy menubar and use its preffered height as the menu height
02344         KMenuBar tmpmenu;
02345         tmpmenu.insertItem( "dummy" );
02346         topmenu_height = tmpmenu.sizeHint().height();
02347         }
02348     return topmenu_height;
02349     }
02350 
02351 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
02352     {
02353     return mgr->createDecoration( bridge );
02354     }
02355 
02356 QString Workspace::desktopName( int desk ) const
02357     {
02358     return QString::fromUtf8( rootInfo->desktopName( desk ) );
02359     }
02360 
02361 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data )
02362     {
02363     return startup->checkStartup( w, id, data ) == KStartupInfo::Match;
02364     }
02365 
02370 void Workspace::focusToNull()
02371     {
02372     XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time );
02373     }
02374 
02375 void Workspace::helperDialog( const QString& message, const Client* c )
02376     {
02377     QStringList args;
02378     QString type;
02379     if( message == "noborderaltf3" )
02380         {
02381         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02382             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02383         args << "--msgbox" <<
02384               i18n( "You have selected to show a window without its border.\n"
02385                     "Without the border, you will not be able to enable the border "
02386                     "again using the mouse: use the window operations menu instead, "
02387                     "activated using the %1 keyboard shortcut." )
02388                 .arg( shortcut );
02389         type = "altf3warning";
02390         }
02391     else if( message == "fullscreenaltf3" )
02392         {
02393         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02394             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02395         args << "--msgbox" <<
02396               i18n( "You have selected to show a window in fullscreen mode.\n"
02397                     "If the application itself does not have an option to turn the fullscreen "
02398                     "mode off you will not be able to disable it "
02399                     "again using the mouse: use the window operations menu instead, "
02400                     "activated using the %1 keyboard shortcut." )
02401                 .arg( shortcut );
02402         type = "altf3warning";
02403         }
02404     else
02405         assert( false );
02406     KProcess proc;
02407     proc << "kdialog" << args;
02408     if( !type.isEmpty())
02409         {
02410         KConfig cfg( "kwin_dialogsrc" );
02411         cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox
02412         if( !cfg.readBoolEntry( type, true )) // has don't show again checked
02413             return;                           // save launching kdialog
02414         proc << "--dontagain" << "kwin_dialogsrc:" + type;
02415         }
02416     if( c != NULL )
02417         proc << "--embed" << QString::number( c->window());
02418     proc.start( KProcess::DontCare );
02419     }
02420 
02421 
02422 // kompmgr stuff
02423     
02424 void Workspace::startKompmgr()
02425 {
02426     if (!kompmgr || kompmgr->isRunning())
02427         return;
02428     if (!kompmgr->start(KProcess::OwnGroup, KProcess::Stderr))
02429         {
02430         options->useTranslucency = FALSE;
02431         KProcess proc;
02432         proc << "kdialog" << "--error"
02433             << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.")
02434             << "--title" << "Composite Manager Failure";
02435         proc.start(KProcess::DontCare);
02436         }
02437     else
02438         {
02439         delete kompmgr_selection;
02440         char selection_name[ 100 ];
02441         sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( qt_xdisplay()));
02442         kompmgr_selection = new KSelectionOwner( selection_name );
02443         connect( kompmgr_selection, SIGNAL( lostOwnership()), SLOT( stopKompmgr()));
02444         kompmgr_selection->claim( true );
02445         connect(kompmgr, SIGNAL(processExited(KProcess*)), SLOT(restartKompmgr()));
02446         options->useTranslucency = TRUE;
02447         allowKompmgrRestart = FALSE;
02448         QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) );
02449         QByteArray ba;
02450         QDataStream arg(ba, IO_WriteOnly);
02451         arg << "";
02452         kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba);
02453         }
02454         if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
02455 }
02456 
02457 void Workspace::stopKompmgr()
02458 {
02459     if (!kompmgr  || !kompmgr->isRunning())
02460         return;
02461     delete kompmgr_selection;
02462     kompmgr_selection = NULL;
02463     kompmgr->disconnect(this, SLOT(restartKompmgr()));
02464     options->useTranslucency = FALSE;
02465     if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
02466     kompmgr->kill();
02467     QByteArray ba;
02468     QDataStream arg(ba, IO_WriteOnly);
02469     arg << "";
02470     kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba);
02471 }
02472 
02473 bool Workspace::kompmgrIsRunning()
02474 {
02475    return kompmgr && kompmgr->isRunning();
02476 }
02477 
02478 void Workspace::unblockKompmgrRestart()
02479 {
02480     allowKompmgrRestart = TRUE;
02481 }
02482 
02483 void Workspace::restartKompmgr()
02484 // this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); QTimer::singleShot(200, workspace, SLOT(startKompmgr()));
02485 {
02486     if (!allowKompmgrRestart) // uh-ohh
02487         {
02488         delete kompmgr_selection;
02489         kompmgr_selection = NULL;
02490         options->useTranslucency = FALSE;
02491         KProcess proc;
02492         proc << "kdialog" << "--error"
02493             << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.")
02494             << "--title" << i18n("Composite Manager Failure");
02495         proc.start(KProcess::DontCare);
02496         return;
02497         }
02498     if (!kompmgr)
02499         return;
02500 // this should be useless, i keep it for maybe future need
02501 //     if (!kcompmgr)
02502 //         {
02503 //         kompmgr = new KProcess;
02504 //         kompmgr->clearArguments();
02505 //         *kompmgr << "kompmgr";
02506 //         }
02507 // -------------------
02508     if (!kompmgr->start(KProcess::NotifyOnExit, KProcess::Stderr))
02509         {
02510         delete kompmgr_selection;
02511         kompmgr_selection = NULL;
02512         options->useTranslucency = FALSE;
02513         KProcess proc;
02514         proc << "kdialog" << "--error"
02515             << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.")
02516             << "--title" << i18n("Composite Manager Failure");
02517         proc.start(KProcess::DontCare);
02518         }
02519     else
02520         {
02521         allowKompmgrRestart = FALSE;
02522         QTimer::singleShot( 60000, this, SLOT(unblockKompmgrRestart()) );
02523         }
02524 }
02525 
02526 void Workspace::handleKompmgrOutput( KProcess* , char *buffer, int buflen)
02527 {
02528     QString message;
02529     QString output = QString::fromLocal8Bit( buffer, buflen );
02530     if (output.contains("Started",false))
02531         ; // don't do anything, just pass to the connection release
02532     else if (output.contains("Can't open display",false))
02533         message = i18n("<qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt>");
02534     else if (output.contains("No render extension",false))
02535         message = i18n("<qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg &ge; 6.8 from www.freedesktop.org.<br></qt>");
02536     else if (output.contains("No composite extension",false))
02537         message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>"
02538         "<i>Section \"Extensions\"<br>"
02539         "Option \"Composite\" \"Enable\"<br>"
02540         "EndSection</i></qt>");
02541     else if (output.contains("No damage extension",false))
02542         message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
02543     else if (output.contains("No XFixes extension",false))
02544         message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
02545     else return; //skip others
02546     // kompmgr startup failed or succeeded, release connection
02547     kompmgr->closeStderr();
02548     disconnect(kompmgr, SIGNAL(receivedStderr(KProcess*, char*, int)), this, SLOT(handleKompmgrOutput(KProcess*, char*, int)));
02549     if( !message.isEmpty())
02550         {
02551         KProcess proc;
02552         proc << "kdialog" << "--error"
02553             << message
02554             << "--title" << i18n("Composite Manager Failure");
02555         proc.start(KProcess::DontCare);
02556         }
02557 }
02558     
02559         
02560 void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent)
02561 {
02562     if (opacityPercent > 100) opacityPercent = 100;
02563     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02564         if (winId == (*it)->window())
02565             {
02566             (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF));
02567             return;
02568             }
02569 }
02570 
02571 void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent)
02572 {
02573     //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400%
02574     if (shadowSizePercent > 400) shadowSizePercent = 400;
02575     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02576         if (winId == (*it)->window())
02577             {
02578             (*it)->setShadowSize(shadowSizePercent);
02579             return;
02580             }
02581 }
02582 
02583 void Workspace::setUnshadowed(unsigned long winId)
02584 {
02585     for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
02586         if (winId == (*it)->window())
02587             {
02588             (*it)->setShadowSize(0);
02589             return;
02590             }
02591 }
02592 
02593 void Workspace::setShowingDesktop( bool showing )
02594     {
02595     rootInfo->setShowingDesktop( showing );
02596     showing_desktop = showing;
02597     ++block_showing_desktop;
02598     if( showing_desktop )
02599         {
02600         showing_desktop_clients.clear();
02601         ++block_focus;
02602         ClientList cls = stackingOrder();
02603         // find them first, then minimize, otherwise transients may get minimized with the window
02604         // they're transient for
02605         for( ClientList::ConstIterator it = cls.begin();
02606              it != cls.end();
02607              ++it )
02608             {
02609             if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow())
02610                 showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker
02611             }
02612         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02613              it != showing_desktop_clients.end();
02614              ++it )
02615             (*it)->minimize(true);
02616         --block_focus;
02617         if( Client* desk = findDesktop( true, currentDesktop()))
02618             requestFocus( desk );
02619         }
02620     else
02621         {
02622         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02623              it != showing_desktop_clients.end();
02624              ++it )
02625             (*it)->unminimize(true);
02626         if( showing_desktop_clients.count() > 0 )
02627             requestFocus( showing_desktop_clients.first());
02628         showing_desktop_clients.clear();
02629         }
02630     --block_showing_desktop;
02631     }
02632 
02633 // Following Kicker's behavior:
02634 // Changing a virtual desktop resets the state and shows the windows again.
02635 // Unminimizing a window resets the state but keeps the windows hidden (except
02636 // the one that was unminimized).
02637 // A new window resets the state and shows the windows again, with the new window
02638 // being active. Due to popular demand (#67406) by people who apparently
02639 // don't see a difference between "show desktop" and "minimize all", this is not
02640 // true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing
02641 // a new window resets the state but doesn't show windows.
02642 void Workspace::resetShowingDesktop( bool keep_hidden )
02643     {
02644     if( block_showing_desktop > 0 )
02645         return;
02646     rootInfo->setShowingDesktop( false );
02647     showing_desktop = false;
02648     ++block_showing_desktop;
02649     if( !keep_hidden )
02650         {
02651         for( ClientList::ConstIterator it = showing_desktop_clients.begin();
02652              it != showing_desktop_clients.end();
02653              ++it )
02654             (*it)->unminimize(true);
02655         }
02656     showing_desktop_clients.clear();
02657     --block_showing_desktop;
02658     }
02659 
02660 // Activating/deactivating this feature works like this:
02661 // When nothing is active, and the shortcut is pressed, global shortcuts are disabled
02662 //   (using global_shortcuts_disabled)
02663 // When a window that has disabling forced is activated, global shortcuts are disabled.
02664 //   (using global_shortcuts_disabled_for_client)
02665 // When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
02666 // or for a client), they are enabled again.
02667 void Workspace::slotDisableGlobalShortcuts()
02668     {
02669     if( global_shortcuts_disabled || global_shortcuts_disabled_for_client )
02670         disableGlobalShortcuts( false );
02671     else
02672         disableGlobalShortcuts( true );
02673     }
02674 
02675 static bool pending_dfc = false;
02676 
02677 void Workspace::disableGlobalShortcutsForClient( bool disable )
02678     {
02679     if( global_shortcuts_disabled_for_client == disable )
02680         return;
02681     if( !global_shortcuts_disabled )
02682         {
02683         if( disable )
02684             pending_dfc = true;
02685         KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
02686         // kwin will get the kipc message too
02687         }
02688     }
02689 
02690 void Workspace::disableGlobalShortcuts( bool disable )
02691     {
02692     KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
02693     // kwin will get the kipc message too
02694     }
02695 
02696 void Workspace::kipcMessage( int id, int data )
02697     {
02698     if( id != KIPC::BlockShortcuts )
02699         return;
02700     if( pending_dfc && data )
02701         {
02702         global_shortcuts_disabled_for_client = true;
02703         pending_dfc = false;
02704         }
02705     else
02706         {
02707         global_shortcuts_disabled = data;
02708         global_shortcuts_disabled_for_client = false;
02709         }
02710     // update also Alt+LMB actions etc.
02711     for( ClientList::ConstIterator it = clients.begin();
02712          it != clients.end();
02713          ++it )
02714         (*it)->updateMouseGrab();
02715     }
02716 
02717 } // namespace
02718 
02719 #include "workspace.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys