kwin Library API Documentation

client.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 #include "client.h"
00013 
00014 #include <qapplication.h>
00015 #include <qpainter.h>
00016 #include <qdatetime.h>
00017 #include <kprocess.h>
00018 #include <unistd.h>
00019 #include <kstandarddirs.h>
00020 #include <qwhatsthis.h>
00021 #include <kwin.h>
00022 #include <kiconloader.h>
00023 #include <stdlib.h>
00024 
00025 #include "bridge.h"
00026 #include "group.h"
00027 #include "workspace.h"
00028 #include "atoms.h"
00029 #include "notifications.h"
00030 
00031 #include <X11/extensions/shape.h>
00032 
00033 // put all externs before the namespace statement to allow the linker
00034 // to resolve them properly
00035 
00036 extern Atom qt_wm_state;
00037 extern Time qt_x_time;
00038 extern Atom qt_window_role;
00039 extern Atom qt_sm_client_id;
00040 
00041 namespace KWinInternal
00042 {
00043 
00044 /*
00045 
00046  Creating a client:
00047      - only by calling Workspace::createClient()
00048          - it creates a new client and calls manage() for it
00049 
00050  Destroying a client:
00051      - destroyClient() - only when the window itself has been destroyed
00052      - releaseWindow() - the window is kept, only the client itself is destroyed
00053 
00054 */
00055 
00056 
00068 Client::Client( Workspace *ws )
00069     :   QObject( NULL ),
00070         client( None ),
00071         wrapper( None ),
00072         frame( None ),
00073         decoration( NULL ),
00074         wspace( ws ),
00075         bridge( new Bridge( this )),
00076         move_faked_activity( false ),
00077         move_resize_grab_window( None ),
00078         transient_for( NULL ),
00079         transient_for_id( None ),
00080         original_transient_for_id( None ),
00081         in_group( NULL ),
00082         window_group( None ),
00083         in_layer( UnknownLayer ),
00084         ping_timer( NULL ),
00085         process_killer( NULL ),
00086         user_time( CurrentTime ), // not known yet
00087         allowed_actions( 0 ),
00088         block_geometry( 0 ),
00089         shade_geometry_change( false ),
00090         border_left( 0 ),
00091         border_right( 0 ),
00092         border_top( 0 ),
00093         border_bottom( 0 )
00094 // SELI do all as initialization
00095     {
00096     autoRaiseTimer = 0;
00097     shadeHoverTimer = 0;
00098 
00099     // set the initial mapping state
00100     mapping_state = WithdrawnState;
00101     desk = 0; // no desktop yet
00102 
00103     mode = PositionCenter;
00104     buttonDown = FALSE;
00105     moveResizeMode = FALSE;
00106 
00107     info = NULL;
00108 
00109     shade_mode = ShadeNone;
00110     active = FALSE;
00111     keep_above = FALSE;
00112     keep_below = FALSE;
00113     is_shape = FALSE;
00114     motif_may_move = TRUE;
00115     motif_may_resize = TRUE;
00116     motif_may_close = TRUE;
00117     fullscreen_mode = FullScreenNone;
00118     skip_taskbar = FALSE;
00119     original_skip_taskbar = false;
00120     minimized = false;
00121     hidden = false;
00122     modal = false;
00123     noborder = false;
00124     user_noborder = false;
00125     not_obscured = false;
00126     urgency = false;
00127     ignore_focus_stealing = false;
00128 
00129     Pdeletewindow = 0;
00130     Ptakefocus = 0;
00131     Pcontexthelp = 0;
00132     Pping = 0;
00133     input = FALSE;
00134     store_settings = FALSE;
00135     skip_pager = FALSE;
00136 
00137 
00138     max_mode = MaximizeRestore;
00139 
00140     cmap = None;
00141     
00142     frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
00143     client_size = QSize( 100, 100 );
00144 
00145     // SELI initialize xsizehints??
00146     }
00147 
00151 Client::~Client()
00152     {
00153     assert(!moveResizeMode);
00154     assert( client == None );
00155     assert( frame == None && wrapper == None );
00156     assert( decoration == NULL );
00157     assert( block_geometry == 0 );
00158     delete info;
00159     delete bridge;
00160     }
00161 
00162 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
00163 void Client::deleteClient( Client* c, allowed_t )
00164     {
00165     delete c;
00166     }
00167 
00171 void Client::releaseWindow( bool on_shutdown )
00172     {
00173     if (moveResizeMode)
00174        leaveMoveResize();
00175     setModal( false ); // otherwise its mainwindow wouldn't get focus
00176     hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags)
00177     if( !on_shutdown )
00178         workspace()->clientHidden( this );
00179     XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect
00180     destroyDecoration();
00181     cleanGrouping();
00182     if( !on_shutdown )
00183         {
00184         workspace()->removeClient( this, Allowed );
00185         // only when the window is being unmapped, not when closing down KWin
00186         // (NETWM sections 5.5,5.7)
00187         info->setDesktop( 0 );
00188         desk = 0;
00189         info->setState( 0, info->state()); // reset all state flags
00190         }
00191     XDeleteProperty( qt_xdisplay(),  client, atoms->kde_net_wm_user_creation_time);
00192     // TODO remove KDEFrameStrut property
00193     XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y());
00194     XRemoveFromSaveSet( qt_xdisplay(), client );
00195     XSelectInput( qt_xdisplay(), client, NoEventMask );
00196     if( on_shutdown )
00197         { // map the window, so it can be found after another WM is started
00198         XMapWindow( qt_xdisplay(), client );
00199     // TODO preserve minimized, shaded etc. state?
00200         }
00201     else
00202         {
00203         // Make sure it's not mapped if the app unmapped it (#65279). The app
00204         // may do map+unmap before we initially map the window by calling rawShow() from manage().
00205         XUnmapWindow( qt_xdisplay(), client ); 
00206         }
00207     setMappingState( WithdrawnState ); // after all is done, tell the app
00208     client = None;
00209     XDestroyWindow( qt_xdisplay(), wrapper );
00210     wrapper = None;
00211     XDestroyWindow( qt_xdisplay(), frame );
00212     frame = None;
00213     deleteClient( this, Allowed );
00214     }
00215 
00216 // like releaseWindow(), but this one is called when the window has been already destroyed
00217 // (e.g. the application closed it)
00218 void Client::destroyClient()
00219     {
00220     if (moveResizeMode)
00221        leaveMoveResize();
00222     ++block_geometry;
00223     setModal( false );
00224     hidden = true; // so that it's not considered visible anymore
00225     workspace()->clientHidden( this );
00226     destroyDecoration();
00227     cleanGrouping();
00228     workspace()->removeClient( this, Allowed );
00229     client = None; // invalidate
00230     XDestroyWindow( qt_xdisplay(), wrapper );
00231     wrapper = None;
00232     XDestroyWindow( qt_xdisplay(), frame );
00233     frame = None;
00234     --block_geometry;
00235     deleteClient( this, Allowed );
00236     }
00237 
00238 void Client::updateDecoration( bool check_workspace_pos, bool force )
00239     {
00240     if( !force && (( decoration == NULL && noBorder())
00241                     || ( decoration != NULL && !noBorder())))
00242         return;
00243     bool do_show = false;
00244     ++block_geometry;
00245     if( force )
00246         destroyDecoration();
00247     if( !noBorder())
00248         {
00249         decoration = workspace()->createDecoration( bridge );
00250         // TODO check decoration's minimum size?
00251         decoration->init();
00252         decoration->widget()->installEventFilter( this );
00253         XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
00254         decoration->widget()->lower();
00255         decoration->borders( border_left, border_right, border_top, border_bottom );
00256         int save_workarea_diff_x = workarea_diff_x;
00257         int save_workarea_diff_y = workarea_diff_y;
00258         move( calculateGravitation( false ));
00259         if( !isShade())
00260             plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00261         else
00262             plainResize( sizeForClientSize( QSize( clientSize().width(), 0 ), SizemodeShaded ), ForceGeometrySet );
00263         workarea_diff_x = save_workarea_diff_x;
00264         workarea_diff_y = save_workarea_diff_y;
00265         do_show = true;
00266         }
00267     else
00268         destroyDecoration();
00269     if( check_workspace_pos )
00270         checkWorkspacePosition();
00271     --block_geometry;
00272     setGeometry( geometry(), ForceGeometrySet );
00273     if( do_show )
00274         decoration->widget()->show();
00275     updateFrameStrut();
00276     }
00277 
00278 void Client::destroyDecoration()
00279     {
00280     if( decoration != NULL )
00281         {
00282         delete decoration;
00283         decoration = NULL;
00284         QPoint grav = calculateGravitation( true );
00285         border_left = border_right = border_top = border_bottom = 0;
00286         setMask( QRegion()); // reset shape mask
00287         int save_workarea_diff_x = workarea_diff_x;
00288         int save_workarea_diff_y = workarea_diff_y;
00289         if( !isShade())
00290             plainResize( clientSize(), ForceGeometrySet );
00291         else
00292             plainResize( QSize( clientSize().width(), 0 ), ForceGeometrySet );
00293         move( grav );
00294         workarea_diff_x = save_workarea_diff_x;
00295         workarea_diff_y = save_workarea_diff_y;
00296         }
00297     }
00298 
00299 void Client::checkBorderSizes()
00300     {
00301     if( decoration == NULL )
00302         return;
00303     int new_left, new_right, new_top, new_bottom;
00304     decoration->borders( new_left, new_right, new_top, new_bottom );
00305     if( new_left == border_left && new_right == border_right
00306         && new_top == border_top && new_bottom == border_bottom )
00307         return;
00308     ++block_geometry;
00309     move( calculateGravitation( true ));
00310     border_left = new_left;
00311     border_right = new_right;
00312     border_top = new_top;
00313     border_bottom = new_bottom;
00314     move( calculateGravitation( false ));
00315     plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00316     checkWorkspacePosition();
00317     --block_geometry;
00318     setGeometry( geometry(), ForceGeometrySet );
00319     }
00320 
00321 void Client::detectNoBorder()
00322     {
00323     if( Shape::hasShape( window()) || Motif::noBorder( window()))
00324         {
00325         noborder = true; // TODO for all window types?
00326         return;
00327         }
00328     switch( windowType())
00329         {
00330         case NET::Desktop :
00331         case NET::Dock :
00332         case NET::TopMenu :
00333         case NET::Override :
00334         case NET::Splash :
00335             noborder = true;
00336           break;
00337         case NET::Unknown :
00338         case NET::Normal :
00339         case NET::Toolbar :
00340         case NET::Menu :
00341         case NET::Dialog :
00342         case NET::Utility :
00343             noborder = false;
00344           break;
00345         default:
00346             assert( false );
00347         }
00348     }
00349 
00350 void Client::updateFrameStrut()
00351     {
00352 // TODO KDEFrameStrut je ale pitome jmeno
00353     NETStrut strut;
00354     strut.left = border_left;
00355     strut.right = border_right;
00356     strut.top = border_top;
00357     strut.bottom = border_bottom;
00358     info->setKDEFrameStrut( strut );
00359     }
00360 
00361 // Resizes the decoration, and makes sure the decoration widget gets resize event
00362 // even if the size hasn't changed. This is needed to make sure the decoration
00363 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
00364 // the decoration may turn on/off some borders, but the actual size
00365 // of the decoration stays the same).
00366 void Client::resizeDecoration( const QSize& s )
00367     {
00368     if( decoration == NULL )
00369         return;
00370     QSize oldsize = decoration->widget()->size();
00371     decoration->resize( s );
00372     if( oldsize == s )
00373         {
00374         QResizeEvent e( s, oldsize );
00375         QApplication::sendEvent( decoration->widget(), &e );
00376         }
00377     }
00378 
00379 bool Client::noBorder() const
00380     {
00381     return noborder || isFullScreen() || user_noborder;
00382     }
00383 
00384 bool Client::userCanSetNoBorder() const
00385     {
00386     return !noborder && !isFullScreen() && !isShade();
00387     }
00388 
00389 bool Client::isUserNoBorder() const
00390     {
00391     return user_noborder;
00392     }
00393 
00394 void Client::setUserNoBorder( bool set )
00395     {
00396     if( !userCanSetNoBorder())
00397         return;
00398     if( user_noborder == set )
00399         return;
00400     user_noborder = set;
00401     updateDecoration( true, false );
00402     }
00403 
00404 void Client::updateShape()
00405     {
00406     if ( shape() )
00407         XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
00408                            clientPos().x(), clientPos().y(),
00409                            window(), ShapeBounding, ShapeSet);
00410     else
00411         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00412                            None, ShapeSet);
00413     // workaround for #19644 - shaped windows shouldn't have decoration
00414     if( shape() && !noBorder()) 
00415         {
00416         noborder = true;
00417         updateDecoration( true );
00418         }
00419     }
00420 
00421 void Client::setMask( const QRegion& reg, int mode )
00422     {
00423     _mask = reg;
00424     if( reg.isNull())
00425         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00426             None, ShapeSet );
00427     else if( mode == X::Unsorted )
00428         XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00429             reg.handle(), ShapeSet );
00430     else
00431         {
00432         QMemArray< QRect > rects = reg.rects();
00433         XRectangle* xrects = new XRectangle[ rects.count() ];
00434         for( unsigned int i = 0;
00435              i < rects.count();
00436              ++i )
00437             {
00438             xrects[ i ].x = rects[ i ].x();
00439             xrects[ i ].y = rects[ i ].y();
00440             xrects[ i ].width = rects[ i ].width();
00441             xrects[ i ].height = rects[ i ].height();
00442             }
00443         XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00444             xrects, rects.count(), ShapeSet, mode );
00445         delete[] xrects;
00446         }
00447     }
00448 
00449 QRegion Client::mask() const
00450     {
00451     if( _mask.isEmpty())
00452         return QRegion( 0, 0, width(), height());
00453     return _mask;
00454     }
00455 
00456 void Client::hideClient( bool hide )
00457     {
00458     if( hidden == hide )
00459         return;
00460     hidden = hide;
00461     info->setState( hidden ? NET::Hidden : 0, NET::Hidden );
00462     if( hidden )
00463         {
00464         setMappingState( IconicState );
00465         rawHide();
00466         setSkipTaskbar( true, false ); // also hide from taskbar
00467         }
00468     else // !hidden
00469         {
00470         setSkipTaskbar( original_skip_taskbar, false );
00471         if( isOnCurrentDesktop())
00472             {
00473             if( isShown( false ))
00474                 setMappingState( NormalState );
00475             rawShow(); // is either visible or shaded
00476             }
00477         }
00478     }
00479 
00480 /*
00481   Returns whether the window is minimizable or not
00482  */
00483 bool Client::isMinimizable() const
00484     {
00485     if( !wantsTabFocus() // SELI co NET::Utility? a proc wantsTabFocus() - skiptaskbar? ?
00486         || ( isSpecialWindow() && !isOverride()))
00487         return false;
00488     if( isTransient())
00489         { // transients may be minimized only if mainwindow is not shown
00490         ClientList mainclients = mainClients();
00491         for( ClientList::ConstIterator it = mainclients.begin();
00492              it != mainclients.end();
00493              ++it )
00494             if( (*it)->isShown( true ))
00495                 return false;
00496         }
00497     return true;
00498     }
00499 
00503 void Client::minimize()
00504     {
00505     if ( !isMinimizable() || isMinimized())
00506         return;
00507 
00508     minimized = true;
00509 
00510     Notify::raise( Notify::Minimize );
00511 
00512     // SELI mainClients().isEmpty() ??? - and in unminimize() too
00513     if ( mainClients().isEmpty() && isOnCurrentDesktop())
00514         animateMinimizeOrUnminimize( true ); // was visible or shaded
00515 
00516     setMappingState( IconicState );
00517     info->setState( NET::Hidden, NET::Hidden );
00518     rawHide();
00519     updateAllowedActions();
00520     workspace()->updateMinimizedOfTransients( this );
00521     }
00522 
00523 void Client::unminimize()
00524     {
00525     if( !isMinimized())
00526         return;
00527 
00528     Notify::raise( Notify::UnMinimize );
00529     minimized = false;    
00530     info->setState( 0, NET::Hidden );
00531     if( isOnCurrentDesktop())
00532         {
00533         if( mainClients().isEmpty())
00534             animateMinimizeOrUnminimize( FALSE );
00535         if( isShown( false ))
00536             setMappingState( NormalState );
00537         rawShow(); // is either visible or shaded
00538         }
00539     updateAllowedActions();
00540     workspace()->updateMinimizedOfTransients( this );
00541     }
00542 
00543 extern bool         blockAnimation;
00544 
00545 void Client::animateMinimizeOrUnminimize( bool minimize )
00546     {
00547     if ( blockAnimation )
00548         return;
00549     if ( !options->animateMinimize )
00550         return;
00551 
00552     if( decoration != NULL && decoration->animateMinimize( minimize ))
00553         return; // decoration did it
00554 
00555     // the function is a bit tricky since it will ensure that an
00556     // animation action needs always the same time regardless of the
00557     // performance of the machine or the X-Server.
00558 
00559     float lf,rf,tf,bf,step;
00560 
00561     int speed = options->animateMinimizeSpeed;
00562     if ( speed > 10 )
00563         speed = 10;
00564     if ( speed < 0 )
00565         speed = 0;
00566 
00567     step = 40. * (11 - speed );
00568 
00569     NETRect r = info->iconGeometry();
00570     QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00571     if ( !icongeom.isValid() )
00572         return;
00573 
00574     QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00575 
00576     QRect before, after;
00577     if ( minimize ) 
00578         {
00579         before = QRect( x(), y(), width(), pm.height() );
00580         after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00581         }
00582     else 
00583         {
00584         before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00585         after = QRect( x(), y(), width(), pm.height() );
00586         }
00587 
00588     lf = (after.left() - before.left())/step;
00589     rf = (after.right() - before.right())/step;
00590     tf = (after.top() - before.top())/step;
00591     bf = (after.bottom() - before.bottom())/step;
00592 
00593     grabXServer();
00594 
00595     QRect area = before;
00596     QRect area2;
00597     QPixmap pm2;
00598 
00599     QTime t;
00600     t.start();
00601     float diff;
00602 
00603     QPainter p ( workspace()->desktopWidget() );
00604     bool need_to_clear = FALSE;
00605     QPixmap pm3;
00606     do 
00607         {
00608         if (area2 != area)
00609             {
00610             pm = animationPixmap( area.width() );
00611             pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00612             p.drawPixmap( area.x(), area.y(), pm );
00613             if ( need_to_clear ) 
00614                 {
00615                 p.drawPixmap( area2.x(), area2.y(), pm3 );
00616                 need_to_clear = FALSE;
00617                 }
00618             area2 = area;
00619             }
00620         XFlush(qt_xdisplay());
00621         XSync( qt_xdisplay(), FALSE );
00622         diff = t.elapsed();
00623         if (diff > step)
00624             diff = step;
00625         area.setLeft(before.left() + int(diff*lf));
00626         area.setRight(before.right() + int(diff*rf));
00627         area.setTop(before.top() + int(diff*tf));
00628         area.setBottom(before.bottom() + int(diff*bf));
00629         if (area2 != area ) 
00630             {
00631             if ( area2.intersects( area ) )
00632                 p.drawPixmap( area2.x(), area2.y(), pm2 );
00633             else 
00634                 { // no overlap, we can clear later to avoid flicker
00635                 pm3 = pm2;
00636                 need_to_clear = TRUE;
00637                 }
00638             }
00639         } while ( t.elapsed() < step);
00640     if (area2 == area || need_to_clear )
00641         p.drawPixmap( area2.x(), area2.y(), pm2 );
00642 
00643     p.end();
00644     ungrabXServer();
00645     }
00646 
00647 
00651 QPixmap Client::animationPixmap( int w )
00652     {
00653     QFont font = options->font(isActive());
00654     QFontMetrics fm( font );
00655     QPixmap pm( w, fm.lineSpacing() );
00656     pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00657     QPainter p( &pm );
00658     p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00659     p.setFont(options->font(isActive()));
00660     p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00661     return pm;
00662     }
00663 
00664 
00665 bool Client::isShadeable() const
00666     {
00667     return !isSpecialWindow() && !noBorder();
00668     }
00669 
00670 void Client::setShade( ShadeMode mode )
00671     {
00672     if( !isShadeable())
00673         return;
00674     if( shade_mode == mode )
00675         return;
00676     bool was_shade = isShade();
00677     ShadeMode was_shade_mode = shade_mode;
00678     shade_mode = mode;
00679     if( was_shade == isShade())
00680         return; // no real change in shaded state
00681 
00682     if( shade_mode == ShadeNormal )
00683         {
00684         if ( isShown( true ) && isOnCurrentDesktop())
00685                 Notify::raise( Notify::ShadeUp );
00686         }
00687     else if( shade_mode == ShadeNone )
00688         {
00689         if( isShown( true ) && isOnCurrentDesktop())
00690                 Notify::raise( Notify::ShadeDown );
00691         }
00692 
00693     assert( decoration != NULL ); // noborder windows can't be shaded
00694     ++block_geometry;
00695     // decorations may turn off some borders when shaded
00696     decoration->borders( border_left, border_right, border_top, border_bottom );
00697 
00698     int as = options->animateShade? 10 : 1;
00699 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
00700     if ( isShade()) 
00701         { // shade_mode == ShadeNormal
00702         int h = height();
00703         shade_geometry_change = true;
00704         QSize s( sizeForClientSize( QSize( clientSize().width(), 0), SizemodeShaded ) );
00705         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00706         XUnmapWindow( qt_xdisplay(), wrapper );
00707         XUnmapWindow( qt_xdisplay(), client );
00708         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00709 // FRAME       repaint( FALSE );
00710 //        bool wasStaticContents = testWFlags( WStaticContents );
00711 //        setWFlags( WStaticContents );
00712         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00713         do 
00714             {
00715             h -= step;
00716             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00717             resizeDecoration( QSize( s.width(), h ));
00718             QApplication::syncX();
00719             } while ( h > s.height() + step );
00720 //        if ( !wasStaticContents )
00721 //            clearWFlags( WStaticContents );
00722         shade_geometry_change = false;
00723         plainResize( s );
00724         if( isActive())
00725             {
00726             if( was_shade_mode == ShadeHover )
00727                 workspace()->activateNextClient( this );
00728             else
00729                 workspace()->focusToNull();
00730             }
00731         }
00732     else 
00733         {
00734         int h = height();
00735         shade_geometry_change = true;
00736         QSize s( sizeForClientSize( clientSize(), SizemodeShaded ));
00737 // FRAME       bool wasStaticContents = testWFlags( WStaticContents );
00738 //        setWFlags( WStaticContents );
00739         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00740         do 
00741             {
00742             h += step;
00743             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00744             resizeDecoration( QSize( s.width(), h ));
00745             // assume a border
00746             // we do not have time to wait for X to send us paint events
00747 // FRAME           repaint( 0, h - step-5, width(), step+5, TRUE);
00748             QApplication::syncX();
00749             } while ( h < s.height() - step );
00750 //        if ( !wasStaticContents )
00751 //            clearWFlags( WStaticContents );
00752         shade_geometry_change = false;
00753         plainResize( s );
00754         if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00755             setActive( TRUE );
00756         XMapWindow( qt_xdisplay(), wrapperId());
00757         XMapWindow( qt_xdisplay(), window());
00758         if ( isActive() )
00759             workspace()->requestFocus( this );
00760         }
00761     --block_geometry;
00762     setGeometry( geometry(), ForceGeometrySet );
00763     info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00764     info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00765     setMappingState( isShown( false ) && isOnCurrentDesktop() ? NormalState : IconicState );
00766     updateAllowedActions();
00767     workspace()->updateMinimizedOfTransients( this );
00768     decoration->shadeChange();
00769     }
00770 
00771 void Client::shadeHover()
00772     {
00773     setShade( ShadeHover );
00774     delete shadeHoverTimer;
00775     shadeHoverTimer = 0;
00776     }
00777 
00778 void Client::toggleShade()
00779     {
00780     // if the mode is ShadeHover or ShadeActive, cancel shade too
00781     setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00782     }
00783 
00784 void Client::virtualDesktopChange()
00785     {
00786     if( hidden || minimized )
00787         return; // no visibility change
00788     // from here it can be only shaded or normally shown
00789     if( isOnCurrentDesktop())
00790         {
00791         if( !isShade())
00792             setMappingState( NormalState );
00793         rawShow();
00794         }
00795     else
00796         {
00797         if( !isShade())
00798             setMappingState( IconicState );
00799         rawHide();
00800         }
00801     }
00802 
00807 void Client::setMappingState(int s)
00808     {
00809     assert( client != None );
00810     if( mapping_state == s )
00811         return;
00812     bool was_unmanaged = ( mapping_state == WithdrawnState );
00813     mapping_state = s;
00814     if( mapping_state == WithdrawnState )
00815         {
00816         XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00817         return;
00818         }
00819     assert( s == NormalState || s == IconicState );
00820 
00821     unsigned long data[2];
00822     data[0] = (unsigned long) s;
00823     data[1] = (unsigned long) None;
00824     XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00825         PropModeReplace, (unsigned char *)data, 2);
00826 
00827     if( was_unmanaged ) // force setting the geometry, manage() did block_geometry = 1
00828         {
00829         assert( block_geometry == 1 );
00830         --block_geometry;
00831         setGeometry( frame_geometry, ForceGeometrySet );
00832         }
00833     }
00834 
00839 void Client::rawShow()
00840     {
00841     if( decoration != NULL )
00842         decoration->widget()->show(); // not really necessary, but let it know the state
00843     XMapWindow( qt_xdisplay(), frame );
00844     if( !isShade())
00845         {
00846         XMapWindow( qt_xdisplay(), wrapper );
00847         XMapWindow( qt_xdisplay(), client );
00848         }
00849     }
00850 
00856 void Client::rawHide()
00857     {
00858 // Here it may look like a race condition, as some other client might try to unmap
00859 // the window between these two XSelectInput() calls. However, they're supposed to
00860 // use XWithdrawWindow(), which also sends a synthetic event to the root window,
00861 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
00862 // will be missed is also very minimal, so I don't think it's needed to grab the server
00863 // here.
00864     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00865     XUnmapWindow( qt_xdisplay(), frame );
00866     XUnmapWindow( qt_xdisplay(), wrapper );
00867     XUnmapWindow( qt_xdisplay(), client );
00868     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00869     if( decoration != NULL )
00870         decoration->widget()->hide(); // not really necessary, but let it know the state
00871     workspace()->clientHidden( this );
00872     }
00873 
00874 static void sendClientMessage(Window w, Atom a, long x)
00875     {
00876     XEvent ev;
00877     long mask;
00878 
00879     memset(&ev, 0, sizeof(ev));
00880     ev.xclient.type = ClientMessage;
00881     ev.xclient.window = w;
00882     ev.xclient.message_type = a;
00883     ev.xclient.format = 32;
00884     ev.xclient.data.l[0] = x;
00885     ev.xclient.data.l[1] = qt_x_time;
00886     mask = 0L;
00887     if (w == qt_xrootwin())
00888       mask = SubstructureRedirectMask;        /* magic! */
00889     XSendEvent(qt_xdisplay(), w, False, mask, &ev);
00890     }
00891 
00892 /*
00893   Returns whether the window may be closed (have a close button)
00894  */
00895 bool Client::isCloseable() const
00896     {
00897     return motif_may_close && ( !isSpecialWindow() || isOverride()); // TODO is NET::Override special?
00898     }
00899 
00904 void Client::closeWindow()
00905     {
00906     if( !isCloseable())
00907         return;
00908     // Update user time, needed for whole group, because the window may create a confirming dialog,
00909     // and this window's user time wouldn't apply to it
00910     // This is needed even for apps without support for user timestamp (e.g. nedit), so updating
00911     // user timestamp in apps on WM_DELETE_WINDOW is not an option (and I'm not sure if it would be right)
00912     group()->updateUserTime(); 
00913     if ( Pdeletewindow )
00914         {
00915         Notify::raise( Notify::Close );
00916         sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
00917         pingWindow();
00918         }
00919     else 
00920         {
00921         // client will not react on wm_delete_window. We have not choice
00922         // but destroy his connection to the XServer.
00923         killWindow();
00924         }
00925     }
00926 
00927 
00931 void Client::killWindow()
00932     {
00933     kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
00934     // not sure if we need an Notify::Kill or not.. until then, use
00935     // Notify::Close
00936     Notify::raise( Notify::Close );
00937 
00938     if( isDialog())
00939         Notify::raise( Notify::TransDelete );
00940     if( isNormalWindow())
00941         Notify::raise( Notify::Delete );
00942     killProcess( false );
00943     // always kill this client at the server
00944     XKillClient(qt_xdisplay(), window() );
00945     destroyClient();
00946     }
00947 
00948 // send a ping to the window using _NET_WM_PING if possible
00949 // if it doesn't respond within a reasonable time, it will be
00950 // killed
00951 void Client::pingWindow()
00952     {
00953     if( !Pping )
00954         return; // can't ping :(
00955     if( ping_timer != NULL )
00956         return; // pinging already
00957     ping_timer = new QTimer( this );
00958     connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
00959     ping_timer->start( 5000, true ); // give it 5 seconds
00960     ping_timestamp = qt_x_time;
00961     workspace()->sendPingToWindow( window(), ping_timestamp );
00962     }
00963 
00964 void Client::gotPing( Time timestamp )
00965     {
00966     if( timestamp != ping_timestamp )
00967         return;
00968     delete ping_timer;
00969     ping_timer = NULL;
00970     if( process_killer != NULL )
00971         {
00972         process_killer->kill();
00973         delete process_killer;
00974         process_killer = NULL;
00975         }
00976     }
00977 
00978 void Client::pingTimeout()
00979     {
00980     kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
00981     delete ping_timer;
00982     ping_timer = NULL;
00983     killProcess( true, ping_timestamp );
00984     }
00985 
00986 void Client::killProcess( bool ask, Time timestamp )
00987     {
00988     if( process_killer != NULL )
00989         return;
00990     Q_ASSERT( !ask || timestamp != CurrentTime );
00991     QCString machine = wmClientMachine();
00992     pid_t pid = info->pid();
00993     if( pid <= 0 || machine.isEmpty()) // needed properties missing
00994         return;
00995     kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
00996     if( !ask )
00997         {
00998         if( machine != "localhost" )
00999             {
01000             KProcess proc;
01001             proc << "xon" << machine << "kill" << pid;
01002             proc.start( KProcess::DontCare );
01003             }
01004         else
01005             ::kill( pid, SIGTERM );
01006         }
01007     else
01008         { // SELI TODO handle the window created by handler specially (on top,urgent?)
01009         process_killer = new KProcess( this );
01010         *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01011             << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01012             << "--windowname" << caption().utf8()
01013             << "--applicationname" << resourceClass()
01014             << "--wid" << QCString().setNum( window())
01015             << "--timestamp" << QCString().setNum( timestamp );
01016         connect( process_killer, SIGNAL( processExited( KProcess* )),
01017             SLOT( processKillerExited()));
01018         if( !process_killer->start( KProcess::NotifyOnExit ))
01019             {
01020             delete process_killer;
01021             process_killer = NULL;
01022             return;
01023             }
01024         }
01025     }
01026 
01027 void Client::processKillerExited()
01028     {
01029     kdDebug( 1212 ) << "Killer exited" << endl;
01030     delete process_killer;
01031     process_killer = NULL;
01032     }
01033 
01034 void Client::setSkipTaskbar( bool b, bool from_outside )
01035     {
01036     if( from_outside )
01037         original_skip_taskbar = b;
01038     if ( b == skipTaskbar() )
01039         return;
01040     skip_taskbar = b;
01041     info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01042     }
01043 
01044 void Client::setSkipPager( bool b )
01045     {
01046     if ( b == skipPager() )
01047         return;
01048     skip_pager = b;
01049     info->setState( b?NET::SkipPager:0, NET::SkipPager );
01050     }
01051 
01052 void Client::setModal( bool m )
01053     { // Qt-3.2 can have even modal normal windows :(
01054     if( modal == m )
01055         return;
01056     modal = m;
01057     if( !modal )
01058         return;
01059     // changing modality for a mapped window is weird (?)
01060     // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
01061     }
01062 
01063 void Client::toggleOnAllDesktops()
01064     {
01065     setOnAllDesktops( !isOnAllDesktops());
01066     }
01067 
01068 void Client::setDesktop( int desktop )
01069     {
01070     if( desktop != NET::OnAllDesktops ) // do range check
01071         desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
01072     if( desk == desktop )
01073         return;
01074     int was_desk = desk;
01075     desk = desktop;
01076     info->setDesktop( desktop );
01077     if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01078         { // onAllDesktops changed
01079         if ( isShown( true ))
01080             Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01081         workspace()->updateOnAllDesktopsOfTransients( this );
01082         }
01083     if( decoration != NULL )
01084         decoration->desktopChange();
01085     virtualDesktopChange(); // hide/show if needed
01086     }
01087 
01088 void Client::setOnAllDesktops( bool b )
01089     {
01090     if(( b && isOnAllDesktops())
01091         || ( !b && !isOnAllDesktops()))
01092         return;
01093     if( b )
01094         setDesktop( NET::OnAllDesktops );
01095     else
01096         setDesktop( workspace()->currentDesktop());
01097     }
01098 
01099 bool Client::isOnCurrentDesktop() const
01100     {
01101     return isOnDesktop( workspace()->currentDesktop());
01102     }
01103 
01108 void Client::takeFocus( bool force, allowed_t )
01109     {
01110     if ( !force && ( isTopMenu() || isDock() || isSplash()) )
01111         return; // toplevel menus and dock windows don't take focus if not forced
01112 
01113 #ifndef NDEBUG
01114     static Time previous_focus_timestamp;
01115     static Client* previous_client;
01116     if( previous_focus_timestamp == qt_x_time && previous_client != this )
01117         {
01118         kdWarning( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
01119         kdDebug( 1212 ) << kdBacktrace() << endl;
01120         }
01121     previous_focus_timestamp = qt_x_time;
01122     previous_client = this;
01123 #endif
01124     if ( input ) 
01125         {
01126         XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time );
01127         }
01128     if ( Ptakefocus )
01129         sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01130     }
01131 
01139 bool Client::providesContextHelp() const
01140     {
01141     return Pcontexthelp;
01142     }
01143 
01144 
01151 void Client::showContextHelp()
01152     {
01153     if ( Pcontexthelp ) 
01154         {
01155         sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01156         QWhatsThis::enterWhatsThisMode(); // SELI?
01157         }
01158     }
01159 
01160 
01165 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01166 
01167 void Client::fetchName()
01168     {
01169     QString s;
01170 
01171     if ( info->name() && info->name()[ 0 ] != '\0' ) 
01172         s = QString::fromUtf8( info->name() );
01173     else 
01174         s = KWin::readNameProperty( window(), XA_WM_NAME );
01175     if ( s != cap_normal ) 
01176         {
01177         bool reset_name = cap_normal.isEmpty();
01178         for( unsigned int i = 0;
01179              i < s.length();
01180              ++i )
01181             if( !s[ i ].isPrint())
01182                 s[ i ] = ' ';
01183         cap_normal = s;
01184         bool was_suffix = ( !cap_suffix.isEmpty());
01185         cap_suffix = QString::null;
01186         if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 
01187             {
01188             int i = 2;
01189             do 
01190                 {
01191                 cap_suffix = " <" + QString::number(i) + ">";
01192                 i++;
01193                 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01194             info->setVisibleName( caption().utf8() );
01195             reset_name = false;
01196             }
01197         if(( was_suffix && cap_suffix.isEmpty()
01198             || reset_name )) // if it was new window, it may have old value still set, if the window is reused
01199             {
01200             info->setVisibleName( "" ); // remove
01201             info->setVisibleIconName( "" ); // remove
01202             }
01203         else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01204             info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
01205 
01206         if( isManaged() && decoration != NULL )
01207                 decoration->captionChange();
01208         }
01209     }
01210 
01211 void Client::fetchIconicName()
01212     {
01213     QString s;
01214 
01215     if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 
01216         s = QString::fromUtf8( info->iconName() );
01217     else 
01218         s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
01219     if ( s != cap_iconic ) 
01220         {
01221         cap_iconic = s;
01222         if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01223             info->setVisibleIconName( ( s + cap_suffix ).utf8() );
01224         }
01225     }
01226 
01229 QString Client::caption() const
01230     {
01231     return cap_normal + cap_suffix;
01232     }
01233 
01234 void Client::getWMHints()
01235     {
01236     XWMHints *hints = XGetWMHints(qt_xdisplay(), window() );
01237     input = true;
01238     window_group = None;
01239     urgency = false;
01240     if ( hints )
01241         {
01242         if( hints->flags & InputHint )
01243             input = hints->input;
01244         if( hints->flags & WindowGroupHint )
01245             window_group = hints->window_group;
01246         urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield
01247         XFree( (char*)hints );
01248         }
01249     checkGroup();
01250     updateUrgency();
01251     updateAllowedActions(); // group affects isMinimizable()
01252     }
01253 
01254 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01255     {    
01256     // get the icons, allow scaling
01257     if( icon != NULL )
01258         *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
01259     if( miniicon != NULL )
01260         if( icon == NULL || !icon->isNull())
01261             *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
01262         else
01263             *miniicon = QPixmap();
01264     }
01265 
01266 void Client::getIcons()
01267     {
01268     // first read icons from the window itself
01269     readIcons( window(), &icon_pix, &miniicon_pix );
01270     if( icon_pix.isNull())
01271         { // then try window group
01272         icon_pix = group()->icon();
01273         miniicon_pix = group()->miniIcon();
01274         }
01275     if( icon_pix.isNull() && isTransient())
01276         { // then mainclients
01277         ClientList mainclients = mainClients();
01278         for( ClientList::ConstIterator it = mainclients.begin();
01279              it != mainclients.end() && icon_pix.isNull();
01280              ++it )
01281             {
01282             icon_pix = (*it)->icon();
01283             miniicon_pix = (*it)->miniIcon();
01284             }
01285         }
01286     if( icon_pix.isNull())
01287         { // and if nothing else, load icon from classhint or xapp icon
01288         icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
01289         miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
01290         }
01291     if( isManaged() && decoration != NULL )
01292         decoration->iconChange();
01293     }
01294 
01295 void Client::getWindowProtocols()
01296     {
01297     Atom *p;
01298     int i,n;
01299 
01300     Pdeletewindow = 0;
01301     Ptakefocus = 0;
01302     Pcontexthelp = 0;
01303     Pping = 0;
01304 
01305     if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n))
01306         {
01307         for (i = 0; i < n; i++)
01308             if (p[i] == atoms->wm_delete_window)
01309                 Pdeletewindow = 1;
01310             else if (p[i] == atoms->wm_take_focus)
01311                 Ptakefocus = 1;
01312             else if (p[i] == atoms->net_wm_context_help)
01313                 Pcontexthelp = 1;
01314             else if (p[i] == atoms->net_wm_ping)
01315                 Pping = 1;
01316         if (n>0)
01317             XFree(p);
01318         }
01319     }
01320 
01321 static int nullErrorHandler(Display *, XErrorEvent *)
01322     {
01323     return 0;
01324     }
01325 
01329 QCString Client::staticWindowRole(WId w)
01330     {
01331     return getStringProperty(w, qt_window_role);
01332     }
01333 
01337 QCString Client::staticSessionId(WId w)
01338     {
01339     return getStringProperty(w, qt_sm_client_id);
01340     }
01341 
01345 QCString Client::staticWmCommand(WId w)
01346     {
01347     return getStringProperty(w, XA_WM_COMMAND, ' ');
01348     }
01349 
01354 QCString Client::staticWmClientMachine(WId w)
01355     {
01356     QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE);
01357     if (result.isEmpty()) 
01358         {
01359         result = "localhost";
01360         }
01361     else 
01362         {
01363         // special name for the local machine (localhost)
01364         char hostnamebuf[80];
01365         if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) 
01366             {
01367             hostnamebuf[sizeof(hostnamebuf)-1] = 0;
01368             if (result == hostnamebuf)
01369                 result = "localhost";
01370             char *dot = strchr(hostnamebuf, '.');
01371             if (dot && !(*dot = 0) && result == hostnamebuf)
01372                 result = "localhost";
01373             }
01374         }
01375     return result;
01376     }
01377 
01381 Window Client::staticWmClientLeader(WId w)
01382     {
01383     Atom type;
01384     int format, status;
01385     unsigned long nitems = 0;
01386     unsigned long extra = 0;
01387     unsigned char *data = 0;
01388     Window result = w;
01389     XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
01390     status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
01391                                  FALSE, XA_WINDOW, &type, &format,
01392                                  &nitems, &extra, &data );
01393     XSetErrorHandler(oldHandler);
01394     if (status  == Success ) 
01395         {
01396         if (data && nitems > 0)
01397             result = *((Window*) data);
01398         XFree(data);
01399         }
01400     return result;
01401     }
01402 
01403 
01404 void Client::getWmClientLeader()
01405     {
01406     wmClientLeaderWin = staticWmClientLeader(window());
01407     }
01408 
01413 QCString Client::sessionId()
01414     {
01415     QCString result = staticSessionId(window());
01416     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01417         result = staticSessionId(wmClientLeaderWin);
01418     return result;
01419     }
01420 
01425 QCString Client::wmCommand()
01426     {
01427     QCString result = staticWmCommand(window());
01428     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01429         result = staticWmCommand(wmClientLeaderWin);
01430     return result;
01431     }
01432 
01437 QCString Client::wmClientMachine() const
01438     {
01439     QCString result = staticWmClientMachine(window());
01440     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01441         result = staticWmClientMachine(wmClientLeaderWin);
01442     return result;
01443     }
01444 
01449 Window Client::wmClientLeader() const
01450     {
01451     if (wmClientLeaderWin)
01452         return wmClientLeaderWin;
01453     return window();
01454     }
01455 
01456 bool Client::wantsTabFocus() const
01457     {
01458     return ( isNormalWindow() || isDialog() || isOverride())
01459                           && ( input || Ptakefocus ) && !skip_taskbar;
01460     }
01461 
01462 
01463 bool Client::wantsInput() const
01464     {
01465     return input;
01466     }
01467 
01472 bool Client::isMovable() const
01473     {
01474     return motif_may_move && !isFullScreen() &&
01475         ( !isSpecialWindow() || isOverride() || isSplash() || isToolbar()) && // allow moving of splashscreens :)
01476         ( maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows() );
01477     }
01478 
01479 bool Client::isDesktop() const
01480     {
01481     return windowType() == NET::Desktop;
01482     }
01483 
01484 bool Client::isDock() const
01485     {
01486     return windowType() == NET::Dock;
01487     }
01488 
01489 bool Client::isTopMenu() const
01490     {
01491     return windowType() == NET::TopMenu;
01492     }
01493 
01494 
01495 bool Client::isMenu() const
01496     {
01497     return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp.
01498     }
01499 
01500 bool Client::isToolbar() const
01501     {
01502     return windowType() == NET::Toolbar;
01503     }
01504 
01505 bool Client::isOverride() const
01506     {
01507     return windowType() == NET::Override;
01508     }
01509 
01510 bool Client::isSplash() const
01511     {
01512     return windowType() == NET::Splash;
01513     }
01514 
01515 bool Client::isUtility() const
01516     {
01517     return windowType() == NET::Utility;
01518     }
01519 
01520 bool Client::isDialog() const
01521     {
01522     return windowType() == NET::Dialog;
01523     }
01524 
01525 bool Client::isNormalWindow() const
01526     {
01527     return windowType() == NET::Normal;
01528     }
01529 
01530 bool Client::isSpecialWindow() const
01531     {
01532     return isDesktop() || isDock() || isSplash() || isTopMenu()
01533         || ( isOverride() && !isFullScreen())// SELI is NET::Override special or not?
01534         || isToolbar(); // TODO
01535     }
01536 
01537 NET::WindowType Client::windowType( bool strict, int supported_types ) const
01538     {
01539     NET::WindowType wt = info->windowType( supported_types );
01540     // TODO is this 'strict' really needed (and used?)
01541     if( !strict )
01542         { // hacks here
01543         if( wt == NET::Menu )
01544             {
01545             // ugly hack to support the times when NET::Menu meant NET::TopMenu
01546             // if it's as wide as the screen, not very high and has its upper-left
01547             // corner a bit above the screen's upper-left cornet, it's a topmenu
01548             if( x() == 0 && y() < 0 && y() > -10 && height() < 100
01549                 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
01550                 wt = NET::TopMenu;
01551             }
01552         const char* const oo_prefix = "openoffice.org"; // QCString has no startsWith()
01553         // oo_prefix is lowercase, because resourceClass() is forced to be lowercase
01554         if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
01555             wt = NET::Normal; // see bug #66065
01556         if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec
01557             wt = isTransient() ? NET::Dialog : NET::Normal;
01558         }
01559     return wt;
01560     }
01561 
01566 void Client::setCursor( Position m )
01567     {
01568     if ( !isResizable() || isShade() || noBorder())
01569         {
01570         setCursor( arrowCursor );
01571         return;
01572         }
01573     switch ( m ) 
01574         {
01575         case PositionTopLeft:
01576         case PositionBottomRight:
01577             setCursor( sizeFDiagCursor );
01578             break;
01579         case PositionBottomLeft:
01580         case PositionTopRight:
01581             setCursor( sizeBDiagCursor );
01582             break;
01583         case PositionTop:
01584         case PositionBottom:
01585             setCursor( sizeVerCursor );
01586             break;
01587         case PositionLeft:
01588         case PositionRight:
01589             setCursor( sizeHorCursor );
01590             break;
01591         default:
01592             if( buttonDown )
01593                 setCursor( sizeAllCursor );
01594             else
01595                 setCursor( arrowCursor );
01596             break;
01597         }
01598     }
01599 
01600 // TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit?
01601 void Client::setCursor( const QCursor& c )
01602     {
01603     if( c.handle() == cursor.handle())
01604         return;
01605     cursor = c;
01606     if( decoration != NULL )
01607         decoration->widget()->setCursor( cursor );
01608     XDefineCursor( qt_xdisplay(), frameId(), cursor.handle());
01609     }
01610 
01611 Client::Position Client::mousePosition( const QPoint& p ) const
01612     {
01613     if( decoration != NULL )
01614         return decoration->mousePosition( p );
01615     return PositionCenter;
01616     }
01617 
01618 void Client::updateAllowedActions( bool force )
01619     {
01620     if( !isManaged() && !force )
01621         return;
01622     unsigned long old_allowed_actions = allowed_actions;
01623     allowed_actions = 0;
01624     if( isMovable())
01625         allowed_actions |= NET::ActionMove;
01626     if( isResizable())
01627         allowed_actions |= NET::ActionResize;
01628     if( isMinimizable())
01629         allowed_actions |= NET::ActionMinimize;
01630     if( isShadeable())
01631         allowed_actions |= NET::ActionShade;
01632     // sticky state not supported
01633     if( isMaximizable())
01634         allowed_actions |= NET::ActionMax;
01635     if( userCanSetFullScreen())
01636         allowed_actions |= NET::ActionFullScreen;
01637     allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.)
01638     if( isCloseable())
01639         allowed_actions |= NET::ActionClose;
01640     if( old_allowed_actions == allowed_actions )
01641         return;
01642     // TODO this could be delayed and compressed - it's only for pagers etc. anyway
01643     info->setAllowedActions( allowed_actions );
01644     // TODO this should also tell the decoration, so that it can update the buttons
01645     }
01646 
01647 void Client::autoRaise()
01648     {
01649     workspace()->raiseClient( this );
01650     delete autoRaiseTimer;
01651     autoRaiseTimer = 0;
01652     }
01653 
01654 void Client::cancelAutoRaise()
01655     {
01656     delete autoRaiseTimer;
01657     autoRaiseTimer = 0;
01658     }
01659 
01660 #ifdef NDEBUG
01661 kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; }
01662 kndbgstream& operator<<( kndbgstream& stream, const ClientList&  ) { return stream; }
01663 kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; }
01664 #else
01665 kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
01666     {
01667     if( cl == NULL )
01668         return stream << "\'NULL_CLIENT\'";
01669     return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
01670     }
01671 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
01672     {
01673     stream << "LIST:(";
01674     bool first = true;
01675     for( ClientList::ConstIterator it = list.begin();
01676          it != list.end();
01677          ++it )
01678         {
01679         if( !first )
01680             stream << ":";
01681         first = false;
01682         stream << *it;
01683         }
01684     stream << ")";
01685     return stream;
01686     }
01687 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
01688     {
01689     stream << "LIST:(";
01690     bool first = true;
01691     for( ConstClientList::ConstIterator it = list.begin();
01692          it != list.end();
01693          ++it )
01694         {
01695         if( !first )
01696             stream << ":";
01697         first = false;
01698         stream << *it;
01699         }
01700     stream << ")";
01701     return stream;
01702     }
01703 #endif
01704 
01705 QPixmap * kwin_get_menu_pix_hack()
01706     {
01707     static QPixmap p;
01708     if ( p.isNull() )
01709         p = SmallIcon( "bx2" );
01710     return &p;
01711     }
01712 
01713 } // namespace
01714 
01715 #include "client.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 29 21:20:49 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003