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