kwin Library API Documentation

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 
00031 #include "plugins.h"
00032 #include "client.h"
00033 #include "popupinfo.h"
00034 #include "tabbox.h"
00035 #include "atoms.h"
00036 #include "placement.h"
00037 #include "notifications.h"
00038 #include "group.h"
00039 #include "rules.h"
00040 
00041 #include <X11/extensions/shape.h>
00042 #include <X11/keysym.h>
00043 #include <X11/keysymdef.h>
00044 #include <X11/cursorfont.h>
00045 
00046 extern Time qt_x_time;
00047 
00048 namespace KWinInternal
00049 {
00050 
00051 extern int screen_number;
00052 
00053 Workspace *Workspace::_self = 0;
00054 
00055 // Rikkus: This class is too complex. It needs splitting further.
00056 // It's a nightmare to understand, especially with so few comments :(
00057 
00058 // Matthias: Feel free to ask me questions about it. Feel free to add
00059 // comments. I dissagree that further splittings makes it easier. 2500
00060 // lines are not too much. It's the task that is complex, not the
00061 // code.
00062 Workspace::Workspace( bool restore )
00063   : DCOPObject        ("KWinInterface"),
00064     QObject           (0, "workspace"),
00065     current_desktop   (0),
00066     number_of_desktops(0),
00067     popup_client      (0),
00068     desktop_widget    (0),
00069     temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ),
00070     active_client     (0),
00071     last_active_client     (0),
00072     most_recently_raised (0),
00073     movingClient(0),
00074     pending_take_activity ( NULL ),
00075     delayfocus_client (0),
00076     was_user_interaction (false),
00077     session_saving    (false),
00078     control_grab      (false),
00079     tab_grab          (false),
00080     mouse_emulation   (false),
00081     block_focus       (0),
00082     tab_box           (0),
00083     popupinfo         (0),
00084     popup             (0),
00085     advanced_popup    (0),
00086     desk_popup        (0),
00087     desk_popup_index  (0),
00088     keys              (0),
00089     root              (0),
00090     workspaceInit     (true),
00091     startup(0), electric_have_borders(false),
00092     electric_current_border(0),
00093     electric_top_border(None),
00094     electric_bottom_border(None),
00095     electric_left_border(None),
00096     electric_right_border(None),
00097     layoutOrientation(Qt::Vertical),
00098     layoutX(-1),
00099     layoutY(2),
00100     workarea(NULL),
00101     screenarea(NULL),
00102     topmenu_selection( NULL ),
00103     topmenu_watcher( NULL ),
00104     topmenu_height( 0 ),
00105     managing_topmenus( false ),
00106     topmenu_space( NULL ),
00107     set_active_client_recursion( 0 ),
00108     block_stacking_updates( 0 ),
00109     forced_global_mouse_grab( false )
00110     {
00111     _self = this;
00112     mgr = new PluginMgr;
00113     root = qt_xrootwin();
00114     default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() );
00115     installed_colormap = default_colormap;
00116     session.setAutoDelete( TRUE );
00117 
00118     connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )),
00119         this, SLOT( gotTemporaryRulesMessage( const QString& )));
00120     connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules()));
00121 
00122     updateXTime(); // needed for proper initialization of user_time in Client ctor
00123 
00124     delayFocusTimer = 0; 
00125     
00126     electric_time_first = qt_x_time;
00127     electric_time_last = qt_x_time;
00128 
00129     if ( restore )
00130       loadSessionInfo();
00131 
00132     loadWindowRules();
00133 
00134     (void) QApplication::desktop(); // trigger creation of desktop widget
00135 
00136     desktop_widget =
00137       new QWidget(
00138         0,
00139         "desktop_widget",
00140         Qt::WType_Desktop | Qt::WPaintUnclipped
00141     );
00142 
00143     kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later
00144     // call this before XSelectInput() on the root window
00145     startup = new KStartupInfo(
00146         KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this );
00147 
00148     // select windowmanager privileges
00149     XSelectInput(qt_xdisplay(), root,
00150                  KeyPressMask |
00151                  PropertyChangeMask |
00152                  ColormapChangeMask |
00153                  SubstructureRedirectMask |
00154                  SubstructureNotifyMask |
00155                  FocusChangeMask // for NotifyDetailNone
00156                  );
00157 
00158     Shape::init();
00159 
00160     // compatibility
00161     long data = 1;
00162 
00163     XChangeProperty(
00164       qt_xdisplay(),
00165       qt_xrootwin(),
00166       atoms->kwin_running,
00167       atoms->kwin_running,
00168       32,
00169       PropModeAppend,
00170       (unsigned char*) &data,
00171       1
00172     );
00173 
00174     initShortcuts();
00175     tab_box = new TabBox( this );
00176     popupinfo = new PopupInfo( );
00177 
00178     init();
00179 
00180 #if (QT_VERSION-0 >= 0x030200) // XRANDR support
00181     connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized()));
00182 #endif
00183     }
00184 
00185 
00186 void Workspace::init()
00187     {
00188     checkElectricBorders();
00189 
00190     supportWindow = new QWidget;
00191     XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp
00192 
00193     XSetWindowAttributes attr;
00194     attr.override_redirect = 1;
00195     null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent,
00196         InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
00197     XMapWindow(qt_xdisplay(), null_focus_window);
00198 
00199     unsigned long protocols[ 5 ] =
00200         {
00201         NET::Supported |
00202         NET::SupportingWMCheck |
00203         NET::ClientList |
00204         NET::ClientListStacking |
00205         NET::DesktopGeometry |
00206         NET::NumberOfDesktops |
00207         NET::CurrentDesktop |
00208         NET::ActiveWindow |
00209         NET::WorkArea |
00210         NET::CloseWindow |
00211         NET::DesktopNames |
00212         NET::KDESystemTrayWindows |
00213         NET::WMName |
00214         NET::WMVisibleName |
00215         NET::WMDesktop |
00216         NET::WMWindowType |
00217         NET::WMState |
00218         NET::WMStrut |
00219         NET::WMIconGeometry |
00220         NET::WMIcon |
00221         NET::WMPid |
00222         NET::WMMoveResize |
00223         NET::WMKDESystemTrayWinFor |
00224         NET::WMKDEFrameStrut |
00225         NET::WMPing
00226         ,
00227         NET::NormalMask |
00228         NET::DesktopMask |
00229         NET::DockMask |
00230         NET::ToolbarMask |
00231         NET::MenuMask |
00232         NET::DialogMask |
00233         NET::OverrideMask |
00234         NET::TopMenuMask |
00235         NET::UtilityMask |
00236         NET::SplashMask |
00237         0
00238         ,
00239         NET::Modal |
00240 //        NET::Sticky |  // large desktops not supported (and probably never will be)
00241         NET::MaxVert |
00242         NET::MaxHoriz |
00243         NET::Shaded |
00244         NET::SkipTaskbar |
00245         NET::KeepAbove |
00246 //        NET::StaysOnTop |  the same like KeepAbove
00247         NET::SkipPager |
00248         NET::Hidden |
00249         NET::FullScreen |
00250         NET::KeepBelow |
00251         NET::DemandsAttention |
00252         0
00253         ,
00254         NET::WM2UserTime |
00255         NET::WM2StartupId |
00256         NET::WM2AllowedActions |
00257         NET::WM2RestackWindow |
00258         NET::WM2MoveResizeWindow |
00259         NET::WM2ExtendedStrut |
00260         NET::WM2KDETemporaryRules |
00261         0
00262         ,
00263         NET::ActionMove |
00264         NET::ActionResize |
00265         NET::ActionMinimize |
00266         NET::ActionShade |
00267 //        NET::ActionStick | // Sticky state is not supported
00268         NET::ActionMaxVert |
00269         NET::ActionMaxHoriz |
00270         NET::ActionFullScreen |
00271         NET::ActionChangeDesktop |
00272         NET::ActionClose |
00273         0
00274         ,
00275         };
00276 
00277     rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin",
00278         protocols, 5, qt_xscreen() );
00279 
00280     loadDesktopSettings();
00281     // extra NETRootInfo instance in Client mode is needed to get the values of the properties
00282     NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop );
00283     int initial_desktop;
00284     if( !kapp->isSessionRestored())
00285         initial_desktop = client_info.currentDesktop();
00286     else
00287         {
00288         KConfigGroupSaver saver( kapp->sessionConfig(), "Session" );
00289         initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 );
00290         }
00291     if( !setCurrentDesktop( initial_desktop ))
00292         setCurrentDesktop( 1 );
00293 
00294     // now we know how many desktops we'll, thus, we initialise the positioning object
00295     initPositioning = new Placement(this);
00296 
00297     connect(&reconfigureTimer, SIGNAL(timeout()), this,
00298             SLOT(slotReconfigure()));
00299     connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows()));
00300 
00301     connect(kapp, SIGNAL(appearanceChanged()), this,
00302             SLOT(slotReconfigure()));
00303     connect(kapp, SIGNAL(settingsChanged(int)), this,
00304             SLOT(slotSettingsChanged(int)));
00305 
00306     active_client = NULL;
00307     rootInfo->setActiveWindow( None );
00308     focusToNull();
00309     if( !kapp->isSessionRestored())
00310         ++block_focus; // because it will be set below
00311 
00312     char nm[ 100 ];
00313     sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay()));
00314     Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False );
00315     topmenu_selection = new KSelectionOwner( topmenu_atom );
00316     topmenu_watcher = new KSelectionWatcher( topmenu_atom );
00317 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
00318 
00319         { // begin updates blocker block
00320         StackingUpdatesBlocker blocker( this );
00321 
00322         if( options->topMenuEnabled() && topmenu_selection->claim( false ))
00323             setupTopMenuHandling(); // this can call updateStackingOrder()
00324         else
00325             lostTopMenuSelection();
00326 
00327         unsigned int i, nwins;
00328         Window root_return, parent_return, *wins;
00329         XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins);
00330         for (i = 0; i < nwins; i++) 
00331             {
00332             XWindowAttributes attr;
00333             XGetWindowAttributes(qt_xdisplay(), wins[i], &attr);
00334             if (attr.override_redirect )
00335                 continue;
00336             if( topmenu_space && topmenu_space->winId() == wins[ i ] )
00337                 continue;
00338             if (attr.map_state != IsUnmapped) 
00339                 {
00340                 if ( addSystemTrayWin( wins[i] ) )
00341                     continue;
00342                 Client* c = createClient( wins[i], true );
00343                 if ( c != NULL && root != qt_xrootwin() ) 
00344                     { // TODO what is this?
00345                 // TODO may use QWidget:.create
00346                     XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 );
00347                     c->move(0,0);
00348                     }
00349                 }
00350             }
00351         if ( wins )
00352             XFree((void *) wins);
00353     // propagate clients, will really happen at the end of the updates blocker block
00354         updateStackingOrder( true );
00355 
00356         updateClientArea();
00357         raiseElectricBorders();
00358 
00359     // NETWM spec says we have to set it to (0,0) if we don't support it
00360         NETPoint* viewports = new NETPoint[ number_of_desktops ];
00361         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
00362         delete[] viewports;
00363         QRect geom = QApplication::desktop()->geometry();
00364         NETSize desktop_geometry;
00365         desktop_geometry.width = geom.width();
00366         desktop_geometry.height = geom.height();
00367     // TODO update also after gaining XRANDR support
00368         rootInfo->setDesktopGeometry( -1, desktop_geometry );
00369 
00370         } // end updates blocker block
00371 
00372     Client* new_active_client = NULL;
00373     if( !kapp->isSessionRestored())
00374         {
00375         --block_focus;
00376         new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
00377         }
00378     if( new_active_client == NULL
00379         && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
00380         {
00381         if( new_active_client == NULL )
00382             new_active_client = topClientOnDesktop( currentDesktop());
00383         if( new_active_client == NULL && !desktops.isEmpty() )
00384             new_active_client = findDesktop( true, currentDesktop());
00385         }
00386     if( new_active_client != NULL )
00387         activateClient( new_active_client );
00388     // SELI TODO this won't work with unreasonable focus policies,
00389     // and maybe in rare cases also if the selected client doesn't
00390     // want focus
00391     workspaceInit = false;
00392 // TODO ungrabXServer()
00393     }
00394 
00395 Workspace::~Workspace()
00396     {
00397     blockStackingUpdates( true );
00398 // TODO    grabXServer();
00399     // use stacking_order, so that kwin --replace keeps stacking order
00400     for( ClientList::ConstIterator it = stacking_order.begin();
00401          it != stacking_order.end();
00402          ++it )
00403         {
00404     // only release the window
00405         (*it)->releaseWindow( true );
00406         // no removeClient() is called !
00407         }
00408     delete desktop_widget;
00409     delete tab_box;
00410     delete popupinfo;
00411     delete popup;
00412     if ( root == qt_xrootwin() )
00413         XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running);
00414 
00415     writeWindowRules();
00416     KGlobal::config()->sync();
00417 
00418     delete rootInfo;
00419     delete supportWindow;
00420     delete mgr;
00421     delete[] workarea;
00422     delete[] screenarea;
00423     delete startup;
00424     delete initPositioning;
00425     delete topmenu_watcher;
00426     delete topmenu_selection;
00427     delete topmenu_space;
00428     while( !rules.isEmpty())
00429         {
00430         delete rules.front();
00431         rules.pop_front();
00432         }
00433     XDestroyWindow( qt_xdisplay(), null_focus_window );
00434 // TODO    ungrabXServer();
00435     _self = 0;
00436     }
00437 
00438 Client* Workspace::createClient( Window w, bool is_mapped )
00439     {
00440     StackingUpdatesBlocker blocker( this );
00441     Client* c = new Client( this );
00442     if( !c->manage( w, is_mapped ))
00443         {
00444         Client::deleteClient( c, Allowed );
00445         return NULL;
00446         }
00447     addClient( c, Allowed );
00448     return c;
00449     }
00450 
00451 void Workspace::addClient( Client* c, allowed_t )
00452     {
00453     Group* grp = findGroup( c->window());
00454     if( grp != NULL )
00455         grp->gotLeader( c );
00456 
00457     if ( c->isDesktop() )
00458         {
00459         desktops.append( c );
00460         if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
00461             requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
00462         }
00463     else
00464         {
00465         if ( c->wantsTabFocus() && !focus_chain.contains( c ))
00466             focus_chain.append( c );
00467         clients.append( c );
00468         }
00469     if( !unconstrained_stacking_order.contains( c ))
00470         unconstrained_stacking_order.append( c );
00471     if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires
00472         stacking_order.append( c );    // c to be in stacking_order
00473     if( c->isTopMenu())
00474         addTopMenu( c );
00475     updateClientArea(); // this cannot be in manage(), because the client got added only now
00476     updateClientLayer( c );
00477     if( c->isDesktop())
00478         {
00479         raiseClient( c );
00480     // if there's no active client, make this desktop the active one
00481         if( activeClient() == NULL && should_get_focus.count() == 0 )
00482             activateClient( findDesktop( true, currentDesktop()));
00483         }
00484     checkTransients( c->window()); // SELI does this really belong here?
00485     updateStackingOrder( true ); // propagate new client
00486     if( c->isUtility() || c->isMenu() || c->isToolbar())
00487         updateToolWindows( true );
00488     }
00489 
00490 /*
00491   Destroys the client \a c
00492  */
00493 void Workspace::removeClient( Client* c, allowed_t )
00494     {
00495     if (c == active_client && popup)
00496         popup->close();
00497     if( c == popup_client )
00498         popup_client = 0;
00499 
00500     if( c->isDialog())
00501         Notify::raise( Notify::TransDelete );
00502     if( c->isNormalWindow())
00503         Notify::raise( Notify::Delete );
00504 
00505     Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
00506     clients.remove( c );
00507     desktops.remove( c );
00508     unconstrained_stacking_order.remove( c );
00509     stacking_order.remove( c );
00510     focus_chain.remove( c );
00511     attention_chain.remove( c );
00512     if( c->isTopMenu())
00513         removeTopMenu( c );
00514     Group* group = findGroup( c->window());
00515     if( group != NULL )
00516         group->lostLeader();
00517 
00518     if ( c == most_recently_raised )
00519         most_recently_raised = 0;
00520     should_get_focus.remove( c );
00521     Q_ASSERT( c != active_client );
00522     if ( c == last_active_client )
00523         last_active_client = 0;
00524     if( c == pending_take_activity )
00525         pending_take_activity = NULL;
00526     if( c == delayfocus_client )
00527         cancelDelayFocus();
00528 
00529     updateStackingOrder( true );
00530 
00531     if (tab_grab)
00532        tab_box->repaint();
00533 
00534     updateClientArea();
00535     }
00536 
00537 void Workspace::updateCurrentTopMenu()
00538     {
00539     if( !managingTopMenus())
00540         return;
00541     // toplevel menubar handling
00542     Client* menubar = 0;
00543     bool block_desktop_menubar = false;
00544     if( active_client )
00545         {
00546         // show the new menu bar first...
00547         Client* menu_client = active_client;
00548         for(;;)
00549             {
00550             if( menu_client->isFullScreen())
00551                 block_desktop_menubar = true;
00552             for( ClientList::ConstIterator it = menu_client->transients().begin();
00553                  it != menu_client->transients().end();
00554                  ++it )
00555                 if( (*it)->isTopMenu())
00556                     {
00557                     menubar = *it;
00558                     break;
00559                     }
00560             if( menubar != NULL || !menu_client->isTransient())
00561                 break;
00562             if( menu_client->isModal() || menu_client->transientFor() == NULL )
00563                 break; // don't use mainwindow's menu if this is modal or group transient
00564             menu_client = menu_client->transientFor();
00565             }
00566         if( !menubar )
00567             { // try to find any topmenu from the application (#72113)
00568             for( ClientList::ConstIterator it = active_client->group()->members().begin();
00569                  it != active_client->group()->members().end();
00570                  ++it )
00571                 if( (*it)->isTopMenu())
00572                     {
00573                     menubar = *it;
00574                     break;
00575                     }
00576             }
00577         }
00578     if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
00579         {
00580         // Find the menubar of the desktop
00581         Client* desktop = findDesktop( true, currentDesktop());
00582         if( desktop != NULL )
00583             {
00584             for( ClientList::ConstIterator it = desktop->transients().begin();
00585                  it != desktop->transients().end();
00586                  ++it )
00587                 if( (*it)->isTopMenu())
00588                     {
00589                     menubar = *it;
00590                     break;
00591                     }
00592             }
00593         // TODO to be cleaned app with window grouping
00594         // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
00595         // thus the topmenu is not transient for it :-/.
00596         if( menubar == NULL )
00597             {
00598             for( ClientList::ConstIterator it = topmenus.begin();
00599                  it != topmenus.end();
00600                  ++it )
00601                 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
00602                     {                                     // set pointing to the root window
00603                     menubar = *it;                        // to recognize it here
00604                     break;                                // Also, with the xroot hack in kdesktop,
00605                     }                                     // there's no NET::Desktop window to be transient for
00606             }
00607         }
00608 
00609 //    kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
00610     if ( menubar )
00611         {
00612         if( active_client && !menubar->isOnDesktop( active_client->desktop()))
00613             menubar->setDesktop( active_client->desktop());
00614         menubar->hideClient( false );
00615         topmenu_space->hide();
00616         // make it appear like it's been raised manually - it's in the Dock layer anyway,
00617         // and not raising it could mess up stacking order of topmenus within one application,
00618         // and thus break raising of mainclients in raiseClient()
00619         unconstrained_stacking_order.remove( menubar );
00620         unconstrained_stacking_order.append( menubar );
00621         }
00622     else if( !block_desktop_menubar )
00623         { // no topmenu active - show the space window, so that there's not empty space
00624         topmenu_space->show();
00625         }
00626 
00627     // ... then hide the other ones. Avoids flickers.
00628     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 
00629         {
00630         if( (*it)->isTopMenu() && (*it) != menubar )
00631             (*it)->hideClient( true );
00632         }
00633     }
00634 
00635 
00636 void Workspace::updateToolWindows( bool also_hide )
00637     {
00638     // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
00639     const Group* group = NULL;
00640     const Client* client = active_client;
00641 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
00642 // will be shown; if a group transient is group, all tools in the group will be shown
00643     while( client != NULL )
00644         {
00645         if( !client->isTransient())
00646             break;
00647         if( client->groupTransient())
00648             {
00649             group = client->group();
00650             break;
00651             }
00652         client = client->transientFor();
00653         }
00654     // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
00655     // i.e. if it's not up to date
00656 
00657     // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
00658     ClientList to_show, to_hide;
00659     for( ClientList::ConstIterator it = stacking_order.begin();
00660          it != stacking_order.end();
00661          ++it )
00662         {
00663         if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
00664             {
00665             bool show = true;
00666             if( !(*it)->isTransient())
00667                 {
00668                 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
00669                     show = true;
00670                 else if( client != NULL && (*it)->group() == client->group())
00671                     show = true;
00672                 else
00673                     show = false;
00674                 }
00675             else
00676                 {
00677                 if( group != NULL && (*it)->group() == group )
00678                     show = true;
00679                 else if( client != NULL && client->hasTransient( (*it), true ))
00680                     show = true;
00681                 else
00682                     show = false;
00683                 }
00684             if( !show && also_hide )
00685                 {
00686                 const ClientList mainclients = (*it)->mainClients();
00687                 // don't hide utility windows which are standalone(?) or
00688                 // have e.g. kicker as mainwindow
00689                 if( mainclients.isEmpty())
00690                     show = true;
00691                 for( ClientList::ConstIterator it2 = mainclients.begin();
00692                      it2 != mainclients.end();
00693                      ++it2 )
00694                     {
00695                     if( (*it2)->isSpecialWindow())
00696                         show = true;
00697                     }
00698                 if( !show )
00699                     to_hide.append( *it );
00700                 }
00701             if( show )
00702                 to_show.append( *it );
00703             }
00704         } // first show new ones, then hide
00705     for( ClientList::ConstIterator it = to_show.fromLast();
00706          it != to_show.end();
00707          --it ) // from topmost
00708         // TODO since this is in stacking order, the order of taskbar entries changes :(
00709         (*it)->hideClient( false );
00710     if( also_hide )
00711         {
00712         for( ClientList::ConstIterator it = to_hide.begin();
00713              it != to_hide.end();
00714              ++it ) // from bottommost
00715             (*it)->hideClient( true );
00716         updateToolWindowsTimer.stop();
00717         }
00718     else // setActiveClient() is after called with NULL client, quickly followed
00719         {    // by setting a new client, which would result in flickering
00720         updateToolWindowsTimer.start( 50, true );
00721         }
00722     }
00723 
00724 void Workspace::slotUpdateToolWindows()
00725     {
00726     updateToolWindows( true );
00727     }
00728 
00732 void Workspace::updateColormap()
00733     {
00734     Colormap cmap = default_colormap;
00735     if ( activeClient() && activeClient()->colormap() != None )
00736         cmap = activeClient()->colormap();
00737     if ( cmap != installed_colormap ) 
00738         {
00739         XInstallColormap(qt_xdisplay(), cmap );
00740         installed_colormap = cmap;
00741         }
00742     }
00743 
00744 void Workspace::reconfigure()
00745     {
00746     reconfigureTimer.start(200, true);
00747     }
00748 
00749 
00750 void Workspace::slotSettingsChanged(int category)
00751     {
00752     kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl;
00753     if( category == (int) KApplication::SETTINGS_SHORTCUTS )
00754         readShortcuts();
00755     }
00756 
00760 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() );
00761 KWIN_PROCEDURE( ResetupRulesProcedure, cl->setupWindowRules( true ) );
00762 
00763 void Workspace::slotReconfigure()
00764     {
00765     kdDebug(1212) << "Workspace::slotReconfigure()" << endl;
00766     reconfigureTimer.stop();
00767 
00768     KGlobal::config()->reparseConfiguration();
00769     unsigned long changed = options->updateSettings();
00770     tab_box->reconfigure();
00771     popupinfo->reconfigure();
00772     readShortcuts();
00773     forEachClient( CheckIgnoreFocusStealingProcedure());
00774 
00775     if( mgr->reset( changed ))
00776         { // decorations need to be recreated
00777 #if 0 // This actually seems to make things worse now
00778         QWidget curtain;
00779         curtain.setBackgroundMode( NoBackground );
00780         curtain.setGeometry( QApplication::desktop()->geometry() );
00781         curtain.show();
00782 #endif
00783         for( ClientList::ConstIterator it = clients.begin();
00784                 it != clients.end();
00785                 ++it )
00786             {
00787             (*it)->updateDecoration( true, true );
00788             }
00789         mgr->destroyPreviousPlugin();
00790         }
00791     else
00792         {
00793         forEachClient( CheckBorderSizesProcedure());
00794         }
00795 
00796     checkElectricBorders();
00797 
00798     if( options->topMenuEnabled() && !managingTopMenus())
00799         {
00800         if( topmenu_selection->claim( false ))
00801             setupTopMenuHandling();
00802         else
00803             lostTopMenuSelection();
00804         }
00805     else if( !options->topMenuEnabled() && managingTopMenus())
00806         {
00807         topmenu_selection->release();
00808         lostTopMenuSelection();
00809         }
00810     topmenu_height = 0; // invalidate used menu height
00811     if( managingTopMenus())
00812         {
00813         updateTopMenuGeometry();
00814         updateCurrentTopMenu();
00815         }
00816 
00817     loadWindowRules();
00818     forEachClient( ResetupRulesProcedure());
00819     }
00820 
00821 void Workspace::loadDesktopSettings()
00822     {
00823     KConfig* c = KGlobal::config();
00824     QCString groupname;
00825     if (screen_number == 0)
00826         groupname = "Desktops";
00827     else
00828         groupname.sprintf("Desktops-screen-%d", screen_number);
00829     KConfigGroupSaver saver(c,groupname);
00830 
00831     int n = c->readNumEntry("Number", 2);
00832     number_of_desktops = n;
00833     delete workarea;
00834     workarea = new QRect[ n + 1 ];
00835     delete screenarea;
00836     screenarea = NULL;
00837     rootInfo->setNumberOfDesktops( number_of_desktops );
00838     desktop_focus_chain.resize( n );
00839     for(int i = 1; i <= n; i++) 
00840         {
00841         QString s = c->readEntry(QString("Name_%1").arg(i),
00842                                 i18n("Desktop %1").arg(i));
00843         rootInfo->setDesktopName( i, s.utf8().data() );
00844         desktop_focus_chain[i-1] = i;
00845         }
00846     }
00847 
00848 void Workspace::saveDesktopSettings()
00849     {
00850     KConfig* c = KGlobal::config();
00851     QCString groupname;
00852     if (screen_number == 0)
00853         groupname = "Desktops";
00854     else
00855         groupname.sprintf("Desktops-screen-%d", screen_number);
00856     KConfigGroupSaver saver(c,groupname);
00857 
00858     c->writeEntry("Number", number_of_desktops );
00859     for(int i = 1; i <= number_of_desktops; i++) 
00860         {
00861         QString s = desktopName( i );
00862         QString defaultvalue = i18n("Desktop %1").arg(i);
00863         if ( s.isEmpty() ) 
00864             {
00865             s = defaultvalue;
00866             rootInfo->setDesktopName( i, s.utf8().data() );
00867             }
00868 
00869         if (s != defaultvalue) 
00870             {
00871             c->writeEntry( QString("Name_%1").arg(i), s );
00872             }
00873         else 
00874             {
00875             QString currentvalue = c->readEntry(QString("Name_%1").arg(i));
00876             if (currentvalue != defaultvalue)
00877                 c->writeEntry( QString("Name_%1").arg(i), "" );
00878             }
00879         }
00880     }
00881 
00882 QStringList Workspace::configModules(bool controlCenter)
00883     {
00884     QStringList args;
00885     //if( KStandardDirs::default_menu_type_by_version()=="kde")
00886     //{
00887     //args <<  "kde-kwindecoration.desktop";
00888     //if (controlCenter)
00889         //args << "kde-kwinoptions.desktop";
00890     //else if (kapp->authorizeControlModule("kde-kwinoptions.desktop"))
00891         //args  << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced" << "kwinrules";
00892     //}
00893     //else
00894     {
00895     args <<  "kwindecoration.desktop";
00896     if (controlCenter)
00897         args << "kwinoptions.desktop";
00898     else if (kapp->authorizeControlModule("kwinoptions.desktop"))
00899         args  << "kwinactions" << "kwinfocus" <<  "kwinmoving" << "kwinadvanced" << "kwinrules";
00900     }
00901 
00902     return args;
00903     }
00904 
00905 void Workspace::configureWM()
00906     {
00907     KApplication::kdeinitExec( "kcmshell", configModules(false) );
00908     }
00909 
00913 void Workspace::doNotManage( QString title )
00914     {
00915     doNotManageList.append( title );
00916     }
00917 
00921 bool Workspace::isNotManaged( const QString& title )
00922     {
00923     for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 
00924         {
00925         QRegExp r( (*it) );
00926         if (r.search(title) != -1) 
00927             {
00928             doNotManageList.remove( it );
00929             return TRUE;
00930             }
00931         }
00932     return FALSE;
00933     }
00934 
00938 void Workspace::refresh() 
00939     {
00940     QWidget w;
00941     w.setGeometry( QApplication::desktop()->geometry() );
00942     w.show();
00943     w.hide();
00944     QApplication::flushX();
00945     }
00946 
00954 class ObscuringWindows
00955     {
00956     public:
00957         ~ObscuringWindows();
00958         void create( Client* c );
00959     private:
00960         QValueList<Window> obscuring_windows;
00961         static QValueList<Window>* cached;
00962         static unsigned int max_cache_size;
00963     };
00964 
00965 QValueList<Window>* ObscuringWindows::cached = 0;
00966 unsigned int ObscuringWindows::max_cache_size = 0;
00967 
00968 void ObscuringWindows::create( Client* c )
00969     {
00970     if( cached == 0 )
00971         cached = new QValueList<Window>;
00972     Window obs_win;
00973     XWindowChanges chngs;
00974     int mask = CWSibling | CWStackMode;
00975     if( cached->count() > 0 ) 
00976         {
00977         cached->remove( obs_win = cached->first());
00978         chngs.x = c->x();
00979         chngs.y = c->y();
00980         chngs.width = c->width();
00981         chngs.height = c->height();
00982         mask |= CWX | CWY | CWWidth | CWHeight;
00983         }
00984     else 
00985         {
00986         XSetWindowAttributes a;
00987         a.background_pixmap = None;
00988         a.override_redirect = True;
00989         obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(),
00990             c->width(), c->height(), 0, CopyFromParent, InputOutput,
00991             CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
00992         }
00993     chngs.sibling = c->frameId();
00994     chngs.stack_mode = Below;
00995     XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs );
00996     XMapWindow( qt_xdisplay(), obs_win );
00997     obscuring_windows.append( obs_win );
00998     }
00999 
01000 ObscuringWindows::~ObscuringWindows()
01001     {
01002     max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1;
01003     for( QValueList<Window>::ConstIterator it = obscuring_windows.begin();
01004          it != obscuring_windows.end();
01005          ++it ) 
01006         {
01007         XUnmapWindow( qt_xdisplay(), *it );
01008         if( cached->count() < max_cache_size )
01009             cached->prepend( *it );
01010         else
01011             XDestroyWindow( qt_xdisplay(), *it );
01012         }
01013     }
01014 
01015 
01022 bool Workspace::setCurrentDesktop( int new_desktop )
01023     {
01024     if (new_desktop < 1 || new_desktop > number_of_desktops )
01025         return false;
01026 
01027     if( popup )
01028         popup->close();
01029     ++block_focus;
01030 // TODO    Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
01031     StackingUpdatesBlocker blocker( this );
01032 
01033     if (new_desktop != current_desktop) 
01034         {
01035         /*
01036           optimized Desktop switching: unmapping done from back to front
01037           mapping done from front to back => less exposure events
01038         */
01039         Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
01040 
01041         ObscuringWindows obs_wins;
01042 
01043         int old_desktop = current_desktop;
01044         current_desktop = new_desktop; // change the desktop (so that Client::virtualDesktopChange() works)
01045 
01046         for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it)
01047             if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
01048                 {
01049                 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop ))
01050                     obs_wins.create( *it );
01051                 (*it)->virtualDesktopChange();
01052                 }
01053 
01054         rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
01055 
01056         if( movingClient && !movingClient->isOnDesktop( new_desktop ))
01057             movingClient->setDesktop( new_desktop );
01058 
01059         for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it)
01060             if ( (*it)->isOnDesktop( new_desktop ) )
01061                 (*it)->virtualDesktopChange();
01062         }
01063 
01064     // restore the focus on this desktop
01065     --block_focus;
01066     Client* c = 0;
01067 
01068     if ( options->focusPolicyIsReasonable()) 
01069         {
01070         // Search in focus chain
01071 
01072         if ( focus_chain.contains( active_client ) && active_client->isShown( true )
01073             && active_client->isOnCurrentDesktop())
01074             {
01075             c = active_client; // the requestFocus below will fail, as the client is already active
01076             }
01077 
01078         if ( !c ) 
01079             {
01080             for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 
01081                 {
01082                 if ( (*it)->isShown( false ) && !(*it)->isOnAllDesktops() && (*it)->isOnCurrentDesktop()) 
01083                     {
01084                     c = *it;
01085                     break;
01086                     }
01087                 }
01088             }
01089 
01090         if ( !c ) 
01091             {
01092             for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 
01093                 {
01094                 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) 
01095                     {
01096                     c = *it;
01097                     break;
01098                     }
01099                 }
01100             }
01101         }
01102 
01103     //if "unreasonable focus policy"
01104     // and active_client is on_all_desktops and under mouse (hence == old_active_client),
01105     // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
01106     else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
01107       c= active_client;
01108 
01109     if( c != active_client )
01110         setActiveClient( NULL, Allowed );
01111 
01112     if ( c ) 
01113         requestFocus( c );
01114     else 
01115         focusToNull();
01116 
01117     if( !desktops.isEmpty() ) 
01118         {
01119         Window w_tmp;
01120         int i_tmp;
01121         XGetInputFocus( qt_xdisplay(), &w_tmp, &i_tmp );
01122         if( w_tmp == null_focus_window ) // CHECKME?
01123             requestFocus( findDesktop( true, currentDesktop()));
01124         }
01125 
01126     updateCurrentTopMenu();
01127 
01128     // Update focus chain:
01129     //  If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
01130     //   Output: chain = { 3, 1, 2, 4 }.
01131 //    kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n")
01132 //      .arg(current_desktop).arg(desktop_focus_chain.find( current_desktop ));
01133     for( int i = desktop_focus_chain.find( current_desktop ); i > 0; i-- )
01134         desktop_focus_chain[i] = desktop_focus_chain[i-1];
01135     desktop_focus_chain[0] = current_desktop;
01136 
01137 //    QString s = "desktop_focus_chain[] = { ";
01138 //    for( uint i = 0; i < desktop_focus_chain.size(); i++ )
01139 //        s += QString::number(desktop_focus_chain[i]) + ", ";
01140 //    kdDebug(1212) << s << "}\n";
01141     return true;
01142     }
01143 
01144 void Workspace::nextDesktop()
01145     {
01146     int desktop = currentDesktop() + 1;
01147     setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
01148     popupinfo->showInfo( desktopName(currentDesktop()) );
01149     }
01150 
01151 void Workspace::previousDesktop()
01152     {
01153     int desktop = currentDesktop() - 1;
01154     setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
01155     popupinfo->showInfo( desktopName(currentDesktop()) );
01156     }
01157 
01161 void Workspace::setNumberOfDesktops( int n )
01162     {
01163     if ( n == number_of_desktops )
01164         return;
01165     int old_number_of_desktops = number_of_desktops;
01166     number_of_desktops = n;
01167 
01168     if( currentDesktop() > numberOfDesktops())
01169         setCurrentDesktop( numberOfDesktops());
01170 
01171     // if increasing the number, do the resizing now,
01172     // otherwise after the moving of windows to still existing desktops
01173     if( old_number_of_desktops < number_of_desktops ) 
01174         {
01175         rootInfo->setNumberOfDesktops( number_of_desktops );
01176         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01177         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01178         delete[] viewports;
01179         updateClientArea( true );
01180         }
01181 
01182     // if the number of desktops decreased, move all
01183     // windows that would be hidden to the last visible desktop
01184     if( old_number_of_desktops > number_of_desktops ) 
01185         {
01186         for( ClientList::ConstIterator it = clients.begin();
01187               it != clients.end();
01188               ++it) 
01189             {
01190             if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
01191                 sendClientToDesktop( *it, numberOfDesktops(), true );
01192             }
01193         }
01194     if( old_number_of_desktops > number_of_desktops ) 
01195         {
01196         rootInfo->setNumberOfDesktops( number_of_desktops );
01197         NETPoint* viewports = new NETPoint[ number_of_desktops ];
01198         rootInfo->setDesktopViewport( number_of_desktops, *viewports );
01199         delete[] viewports;
01200         updateClientArea( true );
01201         }
01202 
01203     saveDesktopSettings();
01204 
01205     // Resize and reset the desktop focus chain.
01206     desktop_focus_chain.resize( n );
01207     for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
01208         desktop_focus_chain[i] = i+1;
01209     }
01210 
01216 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
01217     {
01218     bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
01219     c->setDesktop( desk );
01220     if ( c->desktop() != desk ) // no change or desktop forced
01221         return;
01222     desk = c->desktop(); // Client did range checking
01223 
01224     if ( c->isOnDesktop( currentDesktop() ) )
01225         {
01226         if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
01227             && !was_on_desktop // for stickyness changes
01228             && !dont_activate )
01229             requestFocus( c );
01230         else
01231             restackClientUnderActive( c );
01232         }
01233     else 
01234         {
01235         raiseClient( c );
01236         focus_chain.remove( c );
01237         if ( c->wantsTabFocus() )
01238             focus_chain.append( c );
01239         }
01240 
01241     ClientList transients_stacking_order = ensureStackingOrder( c->transients());
01242     for( ClientList::ConstIterator it = transients_stacking_order.begin();
01243          it != transients_stacking_order.end();
01244          ++it )
01245         sendClientToDesktop( *it, desk, dont_activate );
01246     updateClientArea();
01247     }
01248 
01249 void Workspace::setDesktopLayout(int o, int x, int y)
01250     {
01251     layoutOrientation = (Qt::Orientation) o;
01252     layoutX = x;
01253     layoutY = y;
01254     }
01255 
01256 void Workspace::calcDesktopLayout(int &x, int &y)
01257     {
01258     x = layoutX;
01259     y = layoutY;
01260     if ((x == -1) && (y > 0))
01261        x = (numberOfDesktops()+y-1) / y;
01262     else if ((y == -1) && (x > 0))
01263        y = (numberOfDesktops()+x-1) / x;
01264 
01265     if (x == -1)
01266        x = 1;
01267     if (y == -1)
01268        y = 1;
01269     }
01270 
01275 bool Workspace::addSystemTrayWin( WId w )
01276     {
01277     if ( systemTrayWins.contains( w ) )
01278         return TRUE;
01279 
01280     NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor );
01281     WId trayWinFor = ni.kdeSystemTrayWinFor();
01282     if ( !trayWinFor )
01283         return FALSE;
01284     systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) );
01285     XSelectInput( qt_xdisplay(), w,
01286                   StructureNotifyMask
01287                   );
01288     XAddToSaveSet( qt_xdisplay(), w );
01289     propagateSystemTrayWins();
01290     return TRUE;
01291     }
01292 
01297 bool Workspace::removeSystemTrayWin( WId w, bool check )
01298     {
01299     if ( !systemTrayWins.contains( w ) )
01300         return FALSE;
01301     if( check )
01302         {
01303     // When getting UnmapNotify, it's not clear if it's the systray
01304     // reparenting the window into itself, or if it's the window
01305     // going away. This is obviously a flaw in the design, and we were
01306     // just lucky it worked for so long. Kicker's systray temporarily
01307     // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while
01308     // embedding it, allowing KWin to figure out. Kicker just mustn't
01309     // crash before removing it again ... *shrug* .
01310         int num_props;
01311         Atom* props = XListProperties( qt_xdisplay(), w, &num_props );
01312         if( props != NULL )
01313             {
01314             for( int i = 0;
01315                  i < num_props;
01316                  ++i )
01317                 if( props[ i ] == atoms->kde_system_tray_embedding )
01318                     {
01319                     XFree( props );
01320                     return false;
01321                     }
01322             XFree( props );
01323             }
01324         }
01325     systemTrayWins.remove( w );
01326     propagateSystemTrayWins();
01327     return TRUE;
01328     }
01329 
01330 
01334 void Workspace::propagateSystemTrayWins()
01335     {
01336     Window *cl = new Window[ systemTrayWins.count()];
01337 
01338     int i = 0;
01339     for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 
01340         {
01341         cl[i++] =  (*it).win;
01342         }
01343 
01344     rootInfo->setKDESystemTrayWindows( cl, i );
01345     delete [] cl;
01346     }
01347 
01348 
01349 void Workspace::killWindowId( Window window_to_kill )
01350     {
01351     if( window_to_kill == None )
01352         return;
01353     Window window = window_to_kill;
01354     Client* client = NULL;
01355     for(;;) 
01356         {
01357         client = findClient( FrameIdMatchPredicate( window ));
01358         if( client != NULL ) // found the client
01359             break;
01360         Window parent, root;
01361         Window* children;
01362         unsigned int children_count;
01363         XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count );
01364         if( children != NULL )
01365             XFree( children );
01366         if( window == root ) // we didn't find the client, probably an override-redirect window
01367             break;
01368         window = parent; // go up
01369         }
01370     if( client != NULL )
01371         client->killWindow();
01372     else
01373         XKillClient( qt_xdisplay(), window_to_kill );
01374     }
01375 
01376 
01377 void Workspace::sendPingToWindow( Window window, Time timestamp )
01378     {
01379     rootInfo->sendPing( window, timestamp );
01380     }
01381 
01382 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
01383     {
01384     rootInfo->takeActivity( c->window(), timestamp, flags );
01385     pending_take_activity = c;
01386     }
01387 
01388 
01392 void Workspace::slotGrabWindow()
01393     {
01394     if ( active_client ) 
01395         {
01396         QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() );
01397 
01398     //No XShape - no work.
01399         if( Shape::available()) 
01400             {
01401         //As the first step, get the mask from XShape.
01402             int count, order;
01403             XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(),
01404                                                      ShapeBounding, &count, &order);
01405         //The ShapeBounding region is the outermost shape of the window;
01406         //ShapeBounding - ShapeClipping is defined to be the border.
01407         //Since the border area is part of the window, we use bounding
01408         // to limit our work region
01409             if (rects) 
01410                 {
01411         //Create a QRegion from the rectangles describing the bounding mask.
01412                 QRegion contents;
01413                 for (int pos = 0; pos < count; pos++)
01414                     contents += QRegion(rects[pos].x, rects[pos].y,
01415                                         rects[pos].width, rects[pos].height);
01416                 XFree(rects);
01417 
01418         //Create the bounding box.
01419                 QRegion bbox(0, 0, snapshot.width(), snapshot.height());
01420 
01421         //Get the masked away area.
01422                 QRegion maskedAway = bbox - contents;
01423                 QMemArray<QRect> maskedAwayRects = maskedAway.rects();
01424 
01425         //Construct a bitmap mask from the rectangles
01426                 QBitmap mask( snapshot.width(), snapshot.height());
01427                 QPainter p(&mask);
01428                 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1);
01429                 for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
01430                     p.fillRect(maskedAwayRects[pos], Qt::color0);
01431                 p.end();
01432                 snapshot.setMask(mask);
01433                 }
01434             }
01435 
01436         QClipboard *cb = QApplication::clipboard();
01437         cb->setPixmap( snapshot );
01438         }
01439     else
01440         slotGrabDesktop();
01441     }
01442 
01446 void Workspace::slotGrabDesktop()
01447     {
01448     QPixmap p = QPixmap::grabWindow( qt_xrootwin() );
01449     QClipboard *cb = QApplication::clipboard();
01450     cb->setPixmap( p );
01451     }
01452 
01453 
01457 void Workspace::slotMouseEmulation()
01458     {
01459 
01460     if ( mouse_emulation ) 
01461         {
01462         XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01463         mouse_emulation = FALSE;
01464         return;
01465         }
01466 
01467     if ( XGrabKeyboard(qt_xdisplay(),
01468                        root, FALSE,
01469                        GrabModeAsync, GrabModeAsync,
01470                        qt_x_time) == GrabSuccess ) 
01471         {
01472         mouse_emulation = TRUE;
01473         mouse_emulation_state = 0;
01474         mouse_emulation_window = 0;
01475         }
01476     }
01477 
01484 WId Workspace::getMouseEmulationWindow()
01485     {
01486     Window root;
01487     Window child = qt_xrootwin();
01488     int root_x, root_y, lx, ly;
01489     uint state;
01490     Window w;
01491     Client * c = 0;
01492     do 
01493         {
01494         w = child;
01495         if (!c)
01496             c = findClient( FrameIdMatchPredicate( w ));
01497         XQueryPointer( qt_xdisplay(), w, &root, &child,
01498                        &root_x, &root_y, &lx, &ly, &state );
01499         } while  ( child != None && child != w );
01500 
01501     if ( c && !c->isActive() )
01502         activateClient( c );
01503     return (WId) w;
01504     }
01505 
01509 unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state )
01510     {
01511     if ( !w )
01512         return state;
01513     QWidget* widget = QWidget::find( w );
01514     if ( (!widget ||  widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) 
01515         {
01516         int x, y;
01517         Window xw;
01518         XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw );
01519         if ( type == EmuMove ) 
01520             { // motion notify events
01521             XMotionEvent e;
01522             e.type = MotionNotify;
01523             e.window = w;
01524             e.root = qt_xrootwin();
01525             e.subwindow = w;
01526             e.time = qt_x_time;
01527             e.x = x;
01528             e.y = y;
01529             e.x_root = pos.x();
01530             e.y_root = pos.y();
01531             e.state = state;
01532             e.is_hint = NotifyNormal;
01533             XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, (XEvent*)&e );
01534             }
01535         else 
01536             {
01537             XButtonEvent e;
01538             e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
01539             e.window = w;
01540             e.root = qt_xrootwin();
01541             e.subwindow = w;
01542             e.time = qt_x_time;
01543             e.x = x;
01544             e.y = y;
01545             e.x_root = pos.x();
01546             e.y_root = pos.y();
01547             e.state = state;
01548             e.button = button;
01549             XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, (XEvent*)&e );
01550 
01551             if ( type == EmuPress ) 
01552                 {
01553                 switch ( button ) 
01554                     {
01555                     case 2:
01556                         state |= Button2Mask;
01557                         break;
01558                     case 3:
01559                         state |= Button3Mask;
01560                         break;
01561                     default: // 1
01562                         state |= Button1Mask;
01563                         break;
01564                     }
01565                 }
01566             else 
01567                 {
01568                 switch ( button ) 
01569                     {
01570                     case 2:
01571                         state &= ~Button2Mask;
01572                         break;
01573                     case 3:
01574                         state &= ~Button3Mask;
01575                         break;
01576                     default: // 1
01577                         state &= ~Button1Mask;
01578                         break;
01579                     }
01580                 }
01581             }
01582         }
01583     return state;
01584     }
01585 
01589 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
01590     {
01591     if ( root != qt_xrootwin() )
01592         return FALSE;
01593     int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0);
01594     int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
01595 
01596     bool is_control = km & ControlMask;
01597     bool is_alt = km & Mod1Mask;
01598     bool is_shift = km & ShiftMask;
01599     int delta = is_control?1:is_alt?32:8;
01600     QPoint pos = QCursor::pos();
01601 
01602     switch ( kc ) 
01603         {
01604         case XK_Left:
01605         case XK_KP_Left:
01606             pos.rx() -= delta;
01607             break;
01608         case XK_Right:
01609         case XK_KP_Right:
01610             pos.rx() += delta;
01611             break;
01612         case XK_Up:
01613         case XK_KP_Up:
01614             pos.ry() -= delta;
01615             break;
01616         case XK_Down:
01617         case XK_KP_Down:
01618             pos.ry() += delta;
01619             break;
01620         case XK_F1:
01621             if ( !mouse_emulation_state )
01622                 mouse_emulation_window = getMouseEmulationWindow();
01623             if ( (mouse_emulation_state & Button1Mask) == 0 )
01624                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01625             if ( !is_shift )
01626                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01627             break;
01628         case XK_F2:
01629             if ( !mouse_emulation_state )
01630                 mouse_emulation_window = getMouseEmulationWindow();
01631             if ( (mouse_emulation_state & Button2Mask) == 0 )
01632                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
01633             if ( !is_shift )
01634                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01635             break;
01636         case XK_F3:
01637             if ( !mouse_emulation_state )
01638                 mouse_emulation_window = getMouseEmulationWindow();
01639             if ( (mouse_emulation_state & Button3Mask) == 0 )
01640                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
01641             if ( !is_shift )
01642                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01643             break;
01644         case XK_Return:
01645         case XK_space:
01646         case XK_KP_Enter:
01647         case XK_KP_Space: 
01648             {
01649             if ( !mouse_emulation_state ) 
01650                 {
01651             // nothing was pressed, fake a LMB click
01652                 mouse_emulation_window = getMouseEmulationWindow();
01653                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
01654                 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01655                 }
01656             else 
01657                 { // release all
01658                 if ( mouse_emulation_state & Button1Mask )
01659                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
01660                 if ( mouse_emulation_state & Button2Mask )
01661                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
01662                 if ( mouse_emulation_state & Button3Mask )
01663                     mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
01664                 }
01665             }
01666     // fall through
01667         case XK_Escape:
01668             XUngrabKeyboard(qt_xdisplay(), qt_x_time);
01669             mouse_emulation = FALSE;
01670             return TRUE;
01671         default:
01672             return FALSE;
01673         }
01674 
01675     QCursor::setPos( pos );
01676     if ( mouse_emulation_state )
01677         mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0,  mouse_emulation_state );
01678     return TRUE;
01679 
01680     }
01681 
01687 QWidget* Workspace::desktopWidget()
01688     {
01689     return desktop_widget;
01690     }
01691 
01692 //Delayed focus functions
01693 void Workspace::delayFocus()
01694     {
01695     requestFocus( delayfocus_client );
01696     cancelDelayFocus();
01697     }
01698     
01699 void Workspace::requestDelayFocus( Client* c )
01700     {
01701     delayfocus_client = c;
01702     delete delayFocusTimer;
01703     delayFocusTimer = new QTimer( this );
01704     connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) );
01705     delayFocusTimer->start( options->delayFocusInterval, TRUE  );
01706     }
01707     
01708 void Workspace::cancelDelayFocus()
01709     {
01710     delete delayFocusTimer;
01711     delayFocusTimer = 0;
01712     }
01713 
01714 // Electric Borders
01715 //========================================================================//
01716 // Electric Border Window management. Electric borders allow a user
01717 // to change the virtual desktop by moving the mouse pointer to the
01718 // borders. Technically this is done with input only windows. Since
01719 // electric borders can be switched on and off, we have these two
01720 // functions to create and destroy them.
01721 void Workspace::checkElectricBorders( bool force )
01722     {
01723     if( force )
01724         destroyBorderWindows();
01725     
01726     electric_current_border = 0;
01727 
01728     QRect r = QApplication::desktop()->geometry();
01729     electricTop = r.top();
01730     electricBottom = r.bottom();
01731     electricLeft = r.left();
01732     electricRight = r.right();
01733 
01734     if (options->electricBorders() == Options::ElectricAlways)
01735        createBorderWindows();
01736     else
01737        destroyBorderWindows();
01738     }
01739 
01740 void Workspace::createBorderWindows()
01741     {
01742     if ( electric_have_borders )
01743         return;
01744 
01745     electric_have_borders = true;
01746 
01747     QRect r = QApplication::desktop()->geometry();
01748     XSetWindowAttributes attributes;
01749     unsigned long valuemask;
01750     attributes.override_redirect = True;
01751     attributes.event_mask =  (EnterWindowMask | LeaveWindowMask |
01752                               VisibilityChangeMask);
01753     valuemask=  (CWOverrideRedirect | CWEventMask | CWCursor );
01754     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01755                                           XC_sb_up_arrow);
01756     electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01757                                 0,0,
01758                                 r.width(),1,
01759                                 0,
01760                                 CopyFromParent, InputOnly,
01761                                 CopyFromParent,
01762                                 valuemask, &attributes);
01763     XMapWindow(qt_xdisplay(), electric_top_border);
01764 
01765     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01766                                           XC_sb_down_arrow);
01767     electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01768                                    0,r.height()-1,
01769                                    r.width(),1,
01770                                    0,
01771                                    CopyFromParent, InputOnly,
01772                                    CopyFromParent,
01773                                    valuemask, &attributes);
01774     XMapWindow(qt_xdisplay(), electric_bottom_border);
01775 
01776     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01777                                           XC_sb_left_arrow);
01778     electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01779                                  0,0,
01780                                  1,r.height(),
01781                                  0,
01782                                  CopyFromParent, InputOnly,
01783                                  CopyFromParent,
01784                                  valuemask, &attributes);
01785     XMapWindow(qt_xdisplay(), electric_left_border);
01786 
01787     attributes.cursor = XCreateFontCursor(qt_xdisplay(),
01788                                           XC_sb_right_arrow);
01789     electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(),
01790                                   r.width()-1,0,
01791                                   1,r.height(),
01792                                   0,
01793                                   CopyFromParent, InputOnly,
01794                                   CopyFromParent,
01795                                   valuemask, &attributes);
01796     XMapWindow(qt_xdisplay(),  electric_right_border);
01797     }
01798 
01799 
01800 // Electric Border Window management. Electric borders allow a user
01801 // to change the virtual desktop by moving the mouse pointer to the
01802 // borders. Technically this is done with input only windows. Since
01803 // electric borders can be switched on and off, we have these two
01804 // functions to create and destroy them.
01805 void Workspace::destroyBorderWindows()
01806     {
01807     if( !electric_have_borders)
01808       return;
01809 
01810     electric_have_borders = false;
01811 
01812     if(electric_top_border)
01813       XDestroyWindow(qt_xdisplay(),electric_top_border);
01814     if(electric_bottom_border)
01815       XDestroyWindow(qt_xdisplay(),electric_bottom_border);
01816     if(electric_left_border)
01817       XDestroyWindow(qt_xdisplay(),electric_left_border);
01818     if(electric_right_border)
01819       XDestroyWindow(qt_xdisplay(),electric_right_border);
01820 
01821     electric_top_border    = None;
01822     electric_bottom_border = None;
01823     electric_left_border   = None;
01824     electric_right_border  = None;
01825     }
01826 
01827 void Workspace::clientMoved(const QPoint &pos, Time now)
01828     {
01829     if (options->electricBorders() == Options::ElectricDisabled)
01830        return;
01831 
01832     if ((pos.x() != electricLeft) &&
01833         (pos.x() != electricRight) &&
01834         (pos.y() != electricTop) &&
01835         (pos.y() != electricBottom))
01836        return;
01837 
01838     Time treshold_set = options->electricBorderDelay(); // set timeout
01839     Time treshold_reset = 250; // reset timeout
01840     int distance_reset = 10; // Mouse should not move more than this many pixels
01841 
01842     int border = 0;
01843     if (pos.x() == electricLeft)
01844        border = 1;
01845     else if (pos.x() == electricRight)
01846        border = 2;
01847     else if (pos.y() == electricTop)
01848        border = 3;
01849     else if (pos.y() == electricBottom)
01850        border = 4;
01851 
01852     if ((electric_current_border == border) &&
01853         (timestampDiff(electric_time_last, now) < treshold_reset) &&
01854         ((pos-electric_push_point).manhattanLength() < distance_reset))
01855         {
01856         electric_time_last = now;
01857 
01858         if (timestampDiff(electric_time_first, now) > treshold_set)
01859             {
01860             electric_current_border = 0;
01861 
01862             QRect r = QApplication::desktop()->geometry();
01863             int offset;
01864 
01865             int desk_before = currentDesktop();
01866             switch(border)
01867                 {
01868                 case 1:
01869                  slotSwitchDesktopLeft();
01870                  if (currentDesktop() != desk_before) 
01871                     {
01872                     offset = r.width() / 5;
01873                     QCursor::setPos(r.width() - offset, pos.y());
01874                     }
01875                 break;
01876 
01877                case 2:
01878                 slotSwitchDesktopRight();
01879                 if (currentDesktop() != desk_before) 
01880                     {
01881                     offset = r.width() / 5;
01882                     QCursor::setPos(offset, pos.y());
01883                     }
01884                 break;
01885 
01886                case 3:
01887                 slotSwitchDesktopUp();
01888                 if (currentDesktop() != desk_before) 
01889                     {
01890                     offset = r.height() / 5;
01891                     QCursor::setPos(pos.x(), r.height() - offset);
01892                     }
01893                 break;
01894 
01895                case 4:
01896                 slotSwitchDesktopDown();
01897                 if (currentDesktop() != desk_before) 
01898                     {
01899                     offset = r.height() / 5;
01900                     QCursor::setPos(pos.x(), offset);
01901                     }
01902                 break;
01903                 }
01904             return;
01905             }
01906         }
01907     else 
01908         {
01909         electric_current_border = border;
01910         electric_time_first = now;
01911         electric_time_last = now;
01912         electric_push_point = pos;
01913         }
01914 
01915     int mouse_warp = 1;
01916 
01917   // reset the pointer to find out wether the user is really pushing
01918     switch( border)
01919         {
01920         case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break;
01921         case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break;
01922         case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break;
01923         case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break;
01924         }
01925     }
01926 
01927 // this function is called when the user entered an electric border
01928 // with the mouse. It may switch to another virtual desktop
01929 void Workspace::electricBorder(XEvent *e)
01930     {
01931     Time now = e->xcrossing.time;
01932     QPoint p(e->xcrossing.x_root, e->xcrossing.y_root);
01933 
01934     clientMoved(p, now);
01935     }
01936 
01937 // electric borders (input only windows) have to be always on the
01938 // top. For that reason kwm calls this function always after some
01939 // windows have been raised.
01940 void Workspace::raiseElectricBorders()
01941     {
01942 
01943     if(electric_have_borders)
01944         {
01945         XRaiseWindow(qt_xdisplay(), electric_top_border);
01946         XRaiseWindow(qt_xdisplay(), electric_left_border);
01947         XRaiseWindow(qt_xdisplay(), electric_bottom_border);
01948         XRaiseWindow(qt_xdisplay(), electric_right_border);
01949         }
01950     }
01951 
01952 void Workspace::addTopMenu( Client* c )
01953     {
01954     assert( c->isTopMenu());
01955     assert( !topmenus.contains( c ));
01956     topmenus.append( c );
01957     if( managingTopMenus())
01958         {
01959         int minsize = c->minSize().height();
01960         if( minsize > topMenuHeight())
01961             {
01962             topmenu_height = minsize;
01963             updateTopMenuGeometry();
01964             }
01965         updateTopMenuGeometry( c );
01966         updateCurrentTopMenu();
01967         }
01968 //        kdDebug() << "NEW TOPMENU:" << c << endl;
01969     }
01970 
01971 void Workspace::removeTopMenu( Client* c )
01972     {
01973 //    if( c->isTopMenu())
01974 //        kdDebug() << "REMOVE TOPMENU:" << c << endl;
01975     assert( c->isTopMenu());
01976     assert( topmenus.contains( c ));
01977     topmenus.remove( c );
01978     updateCurrentTopMenu();
01979     // TODO reduce topMenuHeight() if possible?
01980     }
01981 
01982 void Workspace::lostTopMenuSelection()
01983     {
01984 //    kdDebug() << "lost TopMenu selection" << endl;
01985     // make sure this signal is always set when not owning the selection
01986     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01987     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01988     if( !managing_topmenus )
01989         return;
01990     connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
01991     disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
01992     managing_topmenus = false;
01993     delete topmenu_space;
01994     topmenu_space = NULL;
01995     updateClientArea();
01996     for( ClientList::ConstIterator it = topmenus.begin();
01997          it != topmenus.end();
01998          ++it )
01999         (*it)->checkWorkspacePosition();
02000     }
02001 
02002 void Workspace::lostTopMenuOwner()
02003     {
02004     if( !options->topMenuEnabled())
02005         return;
02006 //    kdDebug() << "TopMenu selection lost owner" << endl;
02007     if( !topmenu_selection->claim( false ))
02008         {
02009 //        kdDebug() << "Failed to claim TopMenu selection" << endl;
02010         return;
02011         }
02012 //    kdDebug() << "claimed TopMenu selection" << endl;
02013     setupTopMenuHandling();
02014     }
02015 
02016 void Workspace::setupTopMenuHandling()
02017     {
02018     if( managing_topmenus )
02019         return;
02020     connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection()));
02021     disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner()));
02022     managing_topmenus = true;
02023     topmenu_space = new QWidget;
02024     Window stack[ 2 ];
02025     stack[ 0 ] = supportWindow->winId();
02026     stack[ 1 ] = topmenu_space->winId();
02027     XRestackWindows(qt_xdisplay(), stack, 2);
02028     updateTopMenuGeometry();
02029     topmenu_space->show();
02030     updateClientArea();
02031     updateCurrentTopMenu();
02032     }
02033 
02034 int Workspace::topMenuHeight() const
02035     {
02036     if( topmenu_height == 0 )
02037         { // simply create a dummy menubar and use its preffered height as the menu height
02038         KMenuBar tmpmenu;
02039         tmpmenu.insertItem( "dummy" );
02040         topmenu_height = tmpmenu.sizeHint().height();
02041         }
02042     return topmenu_height;
02043     }
02044 
02045 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
02046     {
02047     return mgr->createDecoration( bridge );
02048     }
02049 
02050 QString Workspace::desktopName( int desk ) const
02051     {
02052     return QString::fromUtf8( rootInfo->desktopName( desk ) );
02053     }
02054 
02055 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data )
02056     {
02057     return startup->checkStartup( w, id, data ) == KStartupInfo::Match;
02058     }
02059 
02064 void Workspace::focusToNull()
02065     {
02066     XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time );
02067     }
02068 
02069 void Workspace::helperDialog( const QString& message, const Client* c )
02070     {
02071     QStringList args;
02072     QString type;
02073     if( message == "noborderaltf3" )
02074         {
02075         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02076             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02077         args << "--msgbox" <<
02078               i18n( "You have selected to show a window without its border.\n"
02079                     "Without the border, you will not be able to enable the border "
02080                     "again using the mouse: use the window operations menu instead, "
02081                     "activated using the %1 keyboard shortcut." )
02082                 .arg( shortcut );
02083         type = "altf3warning";
02084         }
02085     else if( message == "fullscreenaltf3" )
02086         {
02087         QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
02088             .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
02089         args << "--msgbox" <<
02090               i18n( "You have selected to show a window in fullscreen mode.\n"
02091                     "If the application itself does not have an option to turn the fullscreen "
02092                     "mode off you will not be able to disable it "
02093                     "again using the mouse: use the window operations menu instead, "
02094                     "activated using the %1 keyboard shortcut." )
02095                 .arg( shortcut );
02096         type = "altf3warning";
02097         }
02098     else
02099         assert( false );
02100     KProcess proc;
02101     proc << "kdialog" << args;
02102     if( !type.isEmpty())
02103         {
02104         KConfig cfg( "kwin_dialogsrc" );
02105         cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox
02106         if( !cfg.readBoolEntry( type, true )) // has don't show again checked
02107             return;                           // save launching kdialog
02108         proc << "--dontagain" << "kwin_dialogsrc:" + type;
02109         }
02110     if( c != NULL )
02111         proc << "--embed" << QString::number( c->window());
02112     proc.start( KProcess::DontCare );
02113     }
02114 
02115 } // namespace
02116 
02117 #include "workspace.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Apr 4 11:22:00 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003