geometry.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 /*
00013 
00014  This file contains things relevant to geometry, i.e. workspace size,
00015  window positions and window sizes.
00016 
00017 */
00018 
00019 #include "client.h"
00020 #include "workspace.h"
00021 
00022 #include <kapplication.h>
00023 #include <kglobal.h>
00024 #include <qpainter.h>
00025 #include <kwin.h>
00026 
00027 #include "placement.h"
00028 #include "notifications.h"
00029 #include "geometrytip.h"
00030 #include "rules.h"
00031 
00032 extern Time qt_x_time;
00033 
00034 namespace KWinInternal
00035 {
00036 
00037 //********************************************
00038 // Workspace
00039 //********************************************
00040 
00044 void Workspace::desktopResized()
00045     {
00046     updateClientArea();
00047     checkElectricBorders( true );
00048     }
00049 
00062 void Workspace::updateClientArea( bool force )
00063     {
00064     QDesktopWidget *desktopwidget = KApplication::desktop();
00065     int nscreens = desktopwidget -> numScreens ();
00066 //    kdDebug () << "screens: " << nscreens << endl;
00067     QRect* new_wareas = new QRect[ numberOfDesktops() + 1 ];
00068     QRect** new_sareas = new QRect*[ numberOfDesktops() + 1];
00069     QRect* screens = new QRect [ nscreens ];
00070     QRect desktopArea = desktopwidget -> geometry ();
00071     for( int iS = 0;
00072             iS < nscreens;
00073             iS ++ )
00074         {
00075             screens [iS] = desktopwidget -> screenGeometry (iS);
00076         }
00077     for( int i = 1;
00078             i <= numberOfDesktops();
00079             ++i )
00080         {
00081             new_wareas[ i ] = desktopArea;
00082             new_sareas[ i ] = new QRect [ nscreens ];
00083             for( int iS = 0;
00084                     iS < nscreens;
00085                     iS ++ )
00086                 new_sareas[ i ][ iS ] = screens[ iS ];
00087         }
00088     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
00089         {
00090             QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea );
00091             if( r == desktopArea ) // should be sufficient
00092                 continue;
00093             if( (*it)->isOnAllDesktops())
00094                 for( int i = 1;
00095                         i <= numberOfDesktops();
00096                         ++i )
00097                     {
00098                         new_wareas[ i ] = new_wareas[ i ].intersect( r );
00099                         for( int iS = 0;
00100                                 iS < nscreens;
00101                                 iS ++ )
00102                             new_sareas[ i ][ iS ] =
00103                                 new_sareas[ i ][ iS ].intersect(
00104                                         (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
00105                                     );
00106                     }
00107             else
00108                 {
00109                     new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersect( r );
00110                     for( int iS = 0;
00111                             iS < nscreens;
00112                             iS ++ )
00113                         {
00114 //                            kdDebug () << "adjusting new_sarea: " << screens[ iS ] << endl;
00115                             new_sareas[ (*it)->desktop() ][ iS ] =
00116                                 new_sareas[ (*it)->desktop() ][ iS ].intersect(
00117                                         (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
00118                                         );
00119                         }
00120                 }
00121         }
00122 #if 0
00123     for( int i = 1;
00124             i <= numberOfDesktops();
00125             ++i )
00126         {
00127             for( int iS = 0;
00128                     iS < nscreens;
00129                     iS ++ )
00130                 kdDebug () << "new_sarea: " << new_sareas[ i ][ iS ] << endl;
00131         }
00132 #endif
00133     // TODO topmenu update for screenarea changes?
00134     if( topmenu_space != NULL )
00135         {
00136         QRect topmenu_area = desktopArea;
00137         topmenu_area.setTop( topMenuHeight());
00138         for( int i = 1;
00139              i <= numberOfDesktops();
00140              ++i )
00141             new_wareas[ i ] = new_wareas[ i ].intersect( topmenu_area );
00142         }
00143 
00144     bool changed = force;
00145 
00146     if (! screenarea)
00147         changed = true;
00148 
00149     for( int i = 1;
00150          !changed && i <= numberOfDesktops();
00151          ++i )
00152         {
00153             if( workarea[ i ] != new_wareas[ i ] )
00154                 changed = true;
00155             for( int iS = 0;
00156                     iS < nscreens;
00157                     iS ++ )
00158                 if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
00159                     changed = true;
00160         }
00161 
00162     if ( changed )
00163         {
00164         delete[] workarea;
00165         workarea = new_wareas;
00166         new_wareas = NULL;
00167         delete[] screenarea;
00168         screenarea = new_sareas;
00169         new_sareas = NULL;
00170         NETRect r;
00171         for( int i = 1; i <= numberOfDesktops(); i++)
00172             {
00173             r.pos.x = workarea[ i ].x();
00174             r.pos.y = workarea[ i ].y();
00175             r.size.width = workarea[ i ].width();
00176             r.size.height = workarea[ i ].height();
00177             rootInfo->setWorkArea( i, r );
00178             }
00179 
00180         updateTopMenuGeometry();
00181         for( ClientList::ConstIterator it = clients.begin();
00182              it != clients.end();
00183              ++it)
00184             (*it)->checkWorkspacePosition();
00185         for( ClientList::ConstIterator it = desktops.begin();
00186              it != desktops.end();
00187              ++it)
00188             (*it)->checkWorkspacePosition();
00189         }
00190     delete[] screens;
00191     delete[] new_sareas;
00192     delete[] new_wareas;
00193     }
00194 
00195 void Workspace::updateClientArea()
00196     {
00197     updateClientArea( false );
00198     }
00199 
00200 
00208 QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
00209     {
00210     if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
00211         desktop = currentDesktop();
00212     QDesktopWidget *desktopwidget = KApplication::desktop();
00213     int screen = desktopwidget->screenNumber( p );
00214     if( screen < 0 )
00215         screen = desktopwidget->primaryScreen();
00216     QRect sarea = screenarea // may be NULL during KWin initialization
00217         ? screenarea[ desktop ][ screen ]
00218         : desktopwidget->screenGeometry( screen );
00219     QRect warea = workarea[ desktop ].isNull()
00220         ? QApplication::desktop()->geometry()
00221         : workarea[ desktop ];
00222     switch (opt)
00223         {
00224         case MaximizeArea:
00225             if (options->xineramaMaximizeEnabled)
00226                 return sarea;
00227             else
00228                 return warea;
00229         case MaximizeFullArea:
00230             if (options->xineramaMaximizeEnabled)
00231                 return desktopwidget->screenGeometry( screen );
00232             else
00233                 return desktopwidget->geometry();
00234         case FullScreenArea:
00235             if (options->xineramaFullscreenEnabled)
00236                 return desktopwidget->screenGeometry( screen );
00237             else
00238                 return desktopwidget->geometry();
00239         case PlacementArea:
00240             if (options->xineramaPlacementEnabled)
00241                 return sarea;
00242             else
00243                 return warea;
00244         case MovementArea:
00245             if (options->xineramaMovementEnabled)
00246                 return desktopwidget->screenGeometry( screen );
00247             else
00248                 return desktopwidget->geometry();
00249         case WorkArea:
00250             return warea;
00251         case FullXineramaArea:
00252         case FullArea:
00253             return desktopwidget->geometry();
00254         case ScreenArea:
00255             return sarea;
00256         }
00257     assert( false );
00258     return QRect();
00259     }
00260 
00261 QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
00262     {
00263     return clientArea( opt, c->geometry().center(), c->desktop());
00264     }
00265 
00271 QPoint Workspace::adjustClientPosition( Client* c, QPoint pos )
00272     {
00273    //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
00274    //CT adapted for kwin on 25Nov1999
00275    //aleXXX 02Nov2000 added second snapping mode
00276     if (options->windowSnapZone || options->borderSnapZone )
00277         {
00278         const bool sOWO=options->snapOnlyWhenOverlapping;
00279         const QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
00280         const int xmin = maxRect.left();
00281         const int xmax = maxRect.right()+1;               //desk size
00282         const int ymin = maxRect.top();
00283         const int ymax = maxRect.bottom()+1;
00284 
00285         const int cx(pos.x());
00286         const int cy(pos.y());
00287         const int cw(c->width());
00288         const int ch(c->height());
00289         const int rx(cx+cw);
00290         const int ry(cy+ch);                 //these don't change
00291 
00292         int nx(cx), ny(cy);                         //buffers
00293         int deltaX(xmax);
00294         int deltaY(ymax);   //minimum distance to other clients
00295 
00296         int lx, ly, lrx, lry; //coords and size for the comparison client, l
00297 
00298       // border snap
00299         int snap = options->borderSnapZone; //snap trigger
00300         if (snap)
00301             {
00302             if ((sOWO?(cx<xmin):true) && (QABS(xmin-cx)<snap))
00303                 {
00304                 deltaX = xmin-cx;
00305                 nx = xmin;
00306                 }
00307             if ((sOWO?(rx>xmax):true) && (QABS(rx-xmax)<snap) && (QABS(xmax-rx) < deltaX))
00308                 {
00309                 deltaX = rx-xmax;
00310                 nx = xmax - cw;
00311                 }
00312 
00313             if ((sOWO?(cy<ymin):true) && (QABS(ymin-cy)<snap))
00314                 {
00315                 deltaY = ymin-cy;
00316                 ny = ymin;
00317                 }
00318             if ((sOWO?(ry>ymax):true) && (QABS(ry-ymax)<snap) && (QABS(ymax-ry) < deltaY))
00319                 {
00320                 deltaY =ry-ymax;
00321                 ny = ymax - ch;
00322                 }
00323             }
00324 
00325       // windows snap
00326         snap = options->windowSnapZone;
00327         if (snap)
00328             {
00329             QValueList<Client *>::ConstIterator l;
00330             for (l = clients.begin();l != clients.end();++l )
00331                 {
00332                 if ((*l)->isOnDesktop(currentDesktop()) &&
00333                    !(*l)->isMinimized()
00334                     && (*l) != c )
00335                     {
00336                     lx = (*l)->x();
00337                     ly = (*l)->y();
00338                     lrx = lx + (*l)->width();
00339                     lry = ly + (*l)->height();
00340 
00341                     if ( (( cy <= lry ) && ( cy  >= ly  ))  ||
00342                          (( ry >= ly  ) && ( ry  <= lry ))  ||
00343                          (( cy <= ly  ) && ( ry >= lry  )) )
00344                         {
00345                         if ((sOWO?(cx<lrx):true) && (QABS(lrx-cx)<snap) && ( QABS(lrx -cx) < deltaX) )
00346                             {
00347                             deltaX = QABS( lrx - cx );
00348                             nx = lrx;
00349                             }
00350                         if ((sOWO?(rx>lx):true) && (QABS(rx-lx)<snap) && ( QABS( rx - lx )<deltaX) )
00351                             {
00352                             deltaX = QABS(rx - lx);
00353                             nx = lx - cw;
00354                             }
00355                         }
00356 
00357                     if ( (( cx <= lrx ) && ( cx  >= lx  ))  ||
00358                          (( rx >= lx  ) && ( rx  <= lrx ))  ||
00359                          (( cx <= lx  ) && ( rx >= lrx  )) )
00360                         {
00361                         if ((sOWO?(cy<lry):true) && (QABS(lry-cy)<snap) && (QABS( lry -cy ) < deltaY))
00362                             {
00363                             deltaY = QABS( lry - cy );
00364                             ny = lry;
00365                             }
00366                   //if ( (QABS( ry-ly ) < snap) && (QABS( ry - ly ) < deltaY ))
00367                         if ((sOWO?(ry>ly):true) && (QABS(ry-ly)<snap) && (QABS( ry - ly ) < deltaY ))
00368                             {
00369                             deltaY = QABS( ry - ly );
00370                             ny = ly - ch;
00371                             }
00372                         }
00373                     }
00374                 }
00375             }
00376         pos = QPoint(nx, ny);
00377         }
00378     return pos;
00379     }
00380 
00381 QRect Workspace::adjustClientSize( Client* c, QRect moveResizeGeom, int mode )
00382     {
00383    //adapted from adjustClientPosition on 29May2004
00384    //this function is called when resizing a window and will modify
00385    //the new dimensions to snap to other windows/borders if appropriate
00386     if ( options->windowSnapZone || options->borderSnapZone  )
00387         {
00388         const bool sOWO=options->snapOnlyWhenOverlapping;
00389 
00390         const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
00391         const int xmin = maxRect.left();
00392         const int xmax = maxRect.right();               //desk size
00393         const int ymin = maxRect.top();
00394         const int ymax = maxRect.bottom();
00395 
00396         const int cx(moveResizeGeom.left());
00397         const int cy(moveResizeGeom.top());
00398         const int rx(moveResizeGeom.right());
00399         const int ry(moveResizeGeom.bottom());
00400 
00401         int newcx(cx), newcy(cy);                         //buffers
00402         int newrx(rx), newry(ry);
00403         int deltaX(xmax);
00404         int deltaY(ymax);   //minimum distance to other clients
00405 
00406         int lx, ly, lrx, lry; //coords and size for the comparison client, l
00407 
00408       // border snap
00409         int snap = options->borderSnapZone; //snap trigger
00410         if (snap)
00411             {
00412             deltaX = int(snap);
00413             deltaY = int(snap);
00414 
00415 #define SNAP_BORDER_TOP \
00416             if ((sOWO?(newcy<ymin):true) && (QABS(ymin-newcy)<deltaY)) \
00417               { \
00418                 deltaY = QABS(ymin-newcy); \
00419                 newcy = ymin; \
00420                }
00421 
00422 #define SNAP_BORDER_BOTTOM \
00423             if ((sOWO?(newry>ymax):true) && (QABS(ymax-newry)<deltaY)) \
00424               { \
00425                 deltaY = QABS(ymax-newcy); \
00426                 newry = ymax; \
00427                }
00428 
00429 #define SNAP_BORDER_LEFT \
00430             if ((sOWO?(newcx<xmin):true) && (QABS(xmin-newcx)<deltaX)) \
00431               { \
00432                 deltaX = QABS(xmin-newcx); \
00433                 newcx = xmin; \
00434                }
00435 
00436 #define SNAP_BORDER_RIGHT \
00437             if ((sOWO?(newrx>xmax):true) && (QABS(xmax-newrx)<deltaX)) \
00438               { \
00439                 deltaX = QABS(xmax-newrx); \
00440                 newrx = xmax; \
00441                }
00442                      switch ( mode )
00443                       {
00444                       case PositionBottomRight:
00445                         SNAP_BORDER_BOTTOM
00446                         SNAP_BORDER_RIGHT
00447                         break;
00448                       case PositionRight:
00449                         SNAP_BORDER_RIGHT
00450                         break;
00451                       case PositionBottom:
00452                         SNAP_BORDER_BOTTOM
00453                         break;
00454                       case PositionTopLeft:
00455                         SNAP_BORDER_TOP
00456                         SNAP_BORDER_LEFT
00457                         break;
00458                       case PositionLeft:
00459                         SNAP_BORDER_LEFT
00460                         break;
00461                       case PositionTop:
00462                         SNAP_BORDER_TOP
00463                         break;
00464                       case PositionTopRight:
00465                         SNAP_BORDER_TOP
00466                         SNAP_BORDER_RIGHT
00467                         break;
00468                       case PositionBottomLeft:
00469                         SNAP_BORDER_BOTTOM
00470                         SNAP_BORDER_LEFT
00471                         break;
00472                       default:
00473                         assert( false );
00474                         break;
00475                       }
00476 
00477 
00478             }
00479 
00480       // windows snap
00481         snap = options->windowSnapZone;
00482         if (snap)
00483             {
00484             deltaX = int(snap);
00485             deltaY = int(snap);
00486             QValueList<Client *>::ConstIterator l;
00487             for (l = clients.begin();l != clients.end();++l )
00488                 {
00489                 if ((*l)->isOnDesktop(currentDesktop()) &&
00490                    !(*l)->isMinimized()
00491                     && (*l) != c )
00492                     {
00493                     lx = (*l)->x()-1;
00494                     ly = (*l)->y()-1;
00495                     lrx =(*l)->x() + (*l)->width();
00496                     lry =(*l)->y() + (*l)->height();
00497 
00498 #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy  >= ly  ))  || \
00499                          (( newry >= ly  ) && ( newry  <= lry ))  || \
00500                          (( newcy <= ly  ) && ( newry >= lry  )) )
00501 
00502 #define WITHIN_WIDTH  ( (( cx <= lrx ) && ( cx  >= lx  ))  || \
00503                          (( rx >= lx  ) && ( rx  <= lrx ))  || \
00504                          (( cx <= lx  ) && ( rx >= lrx  )) )
00505 
00506 #define SNAP_WINDOW_TOP  if ( (sOWO?(newcy<lry):true) \
00507                   && WITHIN_WIDTH  \
00508                   && (QABS( lry - newcy ) < deltaY) ) {  \
00509                   deltaY = QABS( lry - newcy ); \
00510                   newcy=lry; \
00511                   }
00512 
00513 #define SNAP_WINDOW_BOTTOM  if ( (sOWO?(newry>ly):true)  \
00514                      && WITHIN_WIDTH  \
00515                      && (QABS( ly - newry ) < deltaY) ) {  \
00516                      deltaY = QABS( ly - newry );  \
00517                      newry=ly;  \
00518                      }
00519 
00520 #define SNAP_WINDOW_LEFT  if ( (sOWO?(newcx<lrx):true)  \
00521                    && WITHIN_HEIGHT  \
00522                    && (QABS( lrx - newcx ) < deltaX)) {  \
00523                    deltaX = QABS( lrx - newcx );  \
00524                    newcx=lrx;  \
00525                    }
00526 
00527 #define SNAP_WINDOW_RIGHT  if ( (sOWO?(newrx>lx):true)  \
00528                     && WITHIN_HEIGHT  \
00529                     && (QABS( lx - newrx ) < deltaX))  \
00530                     {  \
00531                     deltaX = QABS( lx - newrx );  \
00532                     newrx=lx;  \
00533                     }
00534 
00535                     switch ( mode )
00536                       {
00537                       case PositionBottomRight:
00538                         SNAP_WINDOW_BOTTOM
00539                         SNAP_WINDOW_RIGHT
00540                         break;
00541                       case PositionRight:
00542                         SNAP_WINDOW_RIGHT
00543                         break;
00544                       case PositionBottom:
00545                         SNAP_WINDOW_BOTTOM
00546                         break;
00547                       case PositionTopLeft:
00548                         SNAP_WINDOW_TOP
00549                         SNAP_WINDOW_LEFT
00550                         break;
00551                       case PositionLeft:
00552                         SNAP_WINDOW_LEFT
00553                         break;
00554                       case PositionTop:
00555                         SNAP_WINDOW_TOP
00556                         break;
00557                       case PositionTopRight:
00558                         SNAP_WINDOW_TOP
00559                         SNAP_WINDOW_RIGHT
00560                         break;
00561                       case PositionBottomLeft:
00562                         SNAP_WINDOW_BOTTOM
00563                         SNAP_WINDOW_LEFT
00564                         break;
00565                       default:
00566                         assert( false );
00567                         break;
00568                       }
00569                     }
00570                 }
00571             }
00572        moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry));
00573        }
00574     return moveResizeGeom;
00575     }
00576 
00580 void Workspace::setClientIsMoving( Client *c )
00581     {
00582     Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
00583     // window while still moving the first one.
00584     movingClient = c;
00585     if (movingClient)
00586         ++block_focus;
00587     else
00588         --block_focus;
00589     }
00590 
00594 void Workspace::cascadeDesktop()
00595     {
00596 // TODO XINERAMA this probably is not right for xinerama
00597     Q_ASSERT( block_stacking_updates == 0 );
00598     ClientList::ConstIterator it(stackingOrder().begin());
00599     initPositioning->reinitCascading( currentDesktop());
00600     QRect area = clientArea( PlacementArea, QPoint( 0, 0 ), currentDesktop());
00601     for (; it != stackingOrder().end(); ++it)
00602         {
00603         if((!(*it)->isOnDesktop(currentDesktop())) ||
00604            ((*it)->isMinimized())                  ||
00605            ((*it)->isOnAllDesktops())              ||
00606            (!(*it)->isMovable()) )
00607             continue;
00608         initPositioning->placeCascaded(*it, area);
00609         }
00610     }
00611 
00616 void Workspace::unclutterDesktop()
00617     {
00618     ClientList::Iterator it(clients.fromLast());
00619     for (; it != clients.end(); --it)
00620         {
00621         if((!(*it)->isOnDesktop(currentDesktop())) ||
00622            ((*it)->isMinimized())                  ||
00623            ((*it)->isOnAllDesktops())              ||
00624            (!(*it)->isMovable()) )
00625             continue;
00626         initPositioning->placeSmart(*it, QRect());
00627         }
00628     }
00629 
00630 
00631 void Workspace::updateTopMenuGeometry( Client* c )
00632     {
00633     if( !managingTopMenus())
00634         return;
00635     if( c != NULL )
00636         {
00637         XEvent ev;
00638         ev.xclient.display = qt_xdisplay();
00639         ev.xclient.type = ClientMessage;
00640         ev.xclient.window = c->window();
00641         static Atom msg_type_atom = XInternAtom( qt_xdisplay(), "_KDE_TOPMENU_MINSIZE", False );
00642         ev.xclient.message_type = msg_type_atom;
00643         ev.xclient.format = 32;
00644         ev.xclient.data.l[0] = qt_x_time;
00645         ev.xclient.data.l[1] = topmenu_space->width();
00646         ev.xclient.data.l[2] = topmenu_space->height();
00647         ev.xclient.data.l[3] = 0;
00648         ev.xclient.data.l[4] = 0;
00649         XSendEvent( qt_xdisplay(), c->window(), False, NoEventMask, &ev );
00650         KWin::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
00651         c->checkWorkspacePosition();
00652         return;
00653         }
00654     // c == NULL - update all, including topmenu_space
00655     QRect area;
00656     area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ?
00657     area.setHeight( topMenuHeight());
00658     topmenu_space->setGeometry( area );
00659     for( ClientList::ConstIterator it = topmenus.begin();
00660          it != topmenus.end();
00661          ++it )
00662         updateTopMenuGeometry( *it );
00663     }
00664 
00665 //********************************************
00666 // Client
00667 //********************************************
00668 
00669 
00670 void Client::keepInArea( QRect area, bool partial )
00671     {
00672     if( partial )
00673         {
00674         // increase the area so that can have only 100 pixels in the area
00675         area.setLeft( QMIN( area.left() - width() + 100, area.left()));
00676         area.setTop( QMIN( area.top() - height() + 100, area.top()));
00677         area.setRight( QMAX( area.right() + width() - 100, area.right()));
00678         area.setBottom( QMAX( area.bottom() + height() - 100, area.bottom()));
00679         }
00680     if ( geometry().right() > area.right() && width() < area.width() )
00681         move( area.right() - width(), y() );
00682     if ( geometry().bottom() > area.bottom() && height() < area.height() )
00683         move( x(), area.bottom() - height() );
00684     if( !area.contains( geometry().topLeft() ))
00685         {
00686         int tx = x();
00687         int ty = y();
00688         if ( tx < area.x() )
00689             tx = area.x();
00690         if ( ty < area.y() )
00691             ty = area.y();
00692         move( tx, ty );
00693         }
00694     }
00695 
00701 // TODO move to Workspace?
00702 
00703 QRect Client::adjustedClientArea( const QRect &desktopArea, const QRect& area ) const
00704     {
00705     QRect r = area;
00706     // topmenu area is reserved in updateClientArea()
00707     if( isTopMenu())
00708         return r;
00709     NETExtendedStrut str = strut();
00710     QRect stareaL = QRect(
00711             0,
00712             str . left_start,
00713             str . left_width,
00714             str . left_end - str . left_start + 1 );
00715     QRect stareaR = QRect (
00716             desktopArea . right () - str . right_width + 1,
00717             str . right_start,
00718             str . right_width,
00719             str . right_end - str . right_start + 1 );
00720     QRect stareaT = QRect (
00721             str . top_start,
00722             0,
00723             str . top_end - str . top_start + 1,
00724             str . top_width);
00725     QRect stareaB = QRect (
00726             str . bottom_start,
00727             desktopArea . bottom () - str . bottom_width + 1,
00728             str . bottom_end - str . bottom_start + 1,
00729             str . bottom_width);
00730 
00731     NETExtendedStrut ext = info->extendedStrut();
00732     if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
00733         && ( str.left_width != 0 || str.right_width != 0 || str.top_width != 0 || str.bottom_width != 0 )) {
00734 
00735         // hack, might cause problems... this tries to guess the start/end of a
00736         // non-extended strut; only works on windows that have exact same
00737         // geometry as their strut (ie, if the geometry fits the width
00738         // exactly, we will adjust length of strut to match the geometry as well;
00739         // otherwise we use the full-edge strut)
00740 
00741         if (stareaT.top() == geometry().top() && stareaT.bottom() == geometry().bottom()) {
00742             stareaT.setLeft(geometry().left());
00743             stareaT.setRight(geometry().right());
00744 //            kdDebug () << "Trimming top-strut to geometry() to: " << stareaT << endl;
00745         }
00746         if (stareaB.top() == geometry().top() && stareaB.bottom() == geometry().bottom()) {
00747             stareaB.setLeft(geometry().left());
00748             stareaB.setRight(geometry().right());
00749 //            kdDebug () << "Trimming bottom-strut to geometry(): " << stareaB << endl;
00750         }
00751         if (stareaL.left() == geometry().left() && stareaL.right() == geometry().right()) {
00752             stareaL.setTop(geometry().top());
00753             stareaL.setBottom(geometry().bottom());
00754 //            kdDebug () << "Trimming left-strut to geometry(): " << stareaL << endl;
00755         }
00756         if (stareaR.left() == geometry().left() && stareaR.right() == geometry().right()) {
00757             stareaR.setTop(geometry().top());
00758             stareaR.setBottom(geometry().bottom());
00759 //            kdDebug () << "Trimming right-strut to geometry(): " << stareaR << endl;
00760         }
00761     }
00762     if (stareaL . intersects (area)) {
00763 //        kdDebug () << "Moving left of: " << r << " to " << stareaL.right() + 1 << endl;
00764         r . setLeft( stareaL . right() + 1 );
00765     }
00766     if (stareaR . intersects (area)) {
00767 //        kdDebug () << "Moving right of: " << r << " to " << stareaR.left() - 1 << endl;
00768         r . setRight( stareaR . left() - 1 );
00769     }
00770     if (stareaT . intersects (area)) {
00771 //        kdDebug () << "Moving top of: " << r << " to " << stareaT.bottom() + 1 << endl;
00772         r . setTop( stareaT . bottom() + 1 );
00773     }
00774     if (stareaB . intersects (area)) {
00775 //        kdDebug () << "Moving bottom of: " << r << " to " << stareaB.top() - 1 << endl;
00776         r . setBottom( stareaB . top() - 1 );
00777     }
00778     return r;
00779     }
00780 
00781 NETExtendedStrut Client::strut() const
00782     {
00783     NETExtendedStrut ext = info->extendedStrut();
00784     NETStrut str = info->strut();
00785     if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
00786         && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 ))
00787         {
00788         // build extended from simple
00789         if( str.left != 0 )
00790             {
00791             ext.left_width = str.left;
00792             ext.left_start = 0;
00793             ext.left_end = XDisplayHeight( qt_xdisplay(), DefaultScreen( qt_xdisplay()));
00794             }
00795         if( str.right != 0 )
00796             {
00797             ext.right_width = str.right;
00798             ext.right_start = 0;
00799             ext.right_end = XDisplayHeight( qt_xdisplay(), DefaultScreen( qt_xdisplay()));
00800             }
00801         if( str.top != 0 )
00802             {
00803             ext.top_width = str.top;
00804             ext.top_start = 0;
00805             ext.top_end = XDisplayWidth( qt_xdisplay(), DefaultScreen( qt_xdisplay()));
00806             }
00807         if( str.bottom != 0 )
00808             {
00809             ext.bottom_width = str.bottom;
00810             ext.bottom_start = 0;
00811             ext.bottom_end = XDisplayWidth( qt_xdisplay(), DefaultScreen( qt_xdisplay()));
00812             }
00813         }
00814     return ext;
00815     }
00816 bool Client::hasStrut() const
00817     {
00818     NETExtendedStrut ext = strut();
00819     if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 )
00820         {
00821             return false;
00822         }
00823     return true;
00824     }
00825 
00826 
00827 // updates differences to workarea edges for all directions
00828 void Client::updateWorkareaDiffs()
00829     {
00830     QRect area = workspace()->clientArea( WorkArea, this );
00831     QRect geom = geometry();
00832     workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
00833     workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
00834     }
00835 
00836 // If the client was inside workarea in the x direction, and if it was close to the left/right
00837 // edge, return the distance from the left/right edge (negative for left, positive for right)
00838 // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
00839 // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
00840 // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
00841 // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
00842 // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
00843 // the y direction is done the same, just the values will be rotated: top->left, bottom->right
00844 int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
00845     {
00846     int left_diff = left - a_left;
00847     int right_diff = a_right - right;
00848     if( left_diff < 0 || right_diff < 0 )
00849         return INT_MIN;
00850     else // fully inside workarea in this direction direction
00851         {
00852         // max distance from edge where it's still considered to be close and is kept at that distance
00853         int max_diff = ( a_right - a_left ) / 10;
00854         if( left_diff < right_diff )
00855             return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
00856         else if( left_diff > right_diff )
00857             return right_diff < max_diff ? right_diff + 1 : INT_MAX;
00858         return INT_MAX; // not close to workarea edge
00859         }
00860     }
00861 
00862 void Client::checkWorkspacePosition()
00863     {
00864     if( isDesktop())
00865         {
00866         QRect area = workspace()->clientArea( FullArea, this );
00867         if( geometry() != area )
00868             setGeometry( area );
00869         return;
00870         }
00871     if( maximizeMode() != MaximizeRestore )
00872     // TODO update geom_restore?
00873         changeMaximize( false, false, true ); // adjust size
00874 
00875     if( isFullScreen())
00876         {
00877         QRect area = workspace()->clientArea( FullScreenArea, this );
00878         if( geometry() != area )
00879             setGeometry( area );
00880         return;
00881         }
00882     if( isDock())
00883         return;
00884     if( isTopMenu())
00885         {
00886         if( workspace()->managingTopMenus())
00887             {
00888             QRect area;
00889             ClientList mainclients = mainClients();
00890             if( mainclients.count() == 1 )
00891                 area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
00892             else
00893                 area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop());
00894             area.setHeight( workspace()->topMenuHeight());
00895 //            kdDebug() << "TOPMENU size adjust: " << area << ":" << this << endl;
00896             setGeometry( area );
00897             }
00898         return;
00899         }
00900 
00901     if( !isShade()) // TODO
00902         {
00903         int old_diff_x = workarea_diff_x;
00904         int old_diff_y = workarea_diff_y;
00905         updateWorkareaDiffs();
00906 
00907         // this can be true only if this window was mapped before KWin
00908         // was started - in such case, don't adjust position to workarea,
00909         // because the window already had its position, and if a window
00910         // with a strut altering the workarea would be managed in initialization
00911         // after this one, this window would be moved
00912         if( workspace()->initializing())
00913             return;
00914 
00915         QRect area = workspace()->clientArea( WorkArea, this );
00916         QRect new_geom = geometry();
00917         QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
00918         QRect tmp_area_x( area.left(), 0, area.width(), 0 );
00919         checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
00920         // the x<->y swapping
00921         QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
00922         QRect tmp_area_y( area.top(), 0, area.height(), 0 );
00923         checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
00924         new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
00925         QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
00926         if( final_geom != new_geom ) // size increments, or size restrictions
00927             { // adjusted size differing matters only for right and bottom edge
00928             if( old_diff_x != INT_MAX && old_diff_x > 0 )
00929                 final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
00930             if( old_diff_y != INT_MAX && old_diff_y > 0 )
00931                 final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
00932             }
00933         if( final_geom != geometry() )
00934             setGeometry( final_geom );
00935         //    updateWorkareaDiffs(); done already by setGeometry()
00936         }
00937     }
00938 
00939 // Try to be smart about keeping the clients visible.
00940 // If the client was fully inside the workspace before, try to keep
00941 // it still inside the workarea, possibly moving it or making it smaller if possible,
00942 // and try to keep the distance from the nearest workarea edge.
00943 // On the other hand, it it was partially moved outside of the workspace in some direction,
00944 // don't do anything with that direction if it's still at least partially visible. If it's
00945 // not visible anymore at all, make sure it's visible at least partially
00946 // again (not fully, as that could(?) be potentionally annoying) by
00947 // moving it slightly inside the workarea (those '+ 5').
00948 // Again, this is done for the x direction, y direction will be done by x<->y swapping
00949 void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area )
00950     {
00951     if( old_diff != INT_MIN ) // was inside workarea
00952         {
00953         if( old_diff == INT_MAX ) // was in workarea, but far from edge
00954             {
00955             if( new_diff == INT_MIN )  // is not anymore fully in workarea
00956                 {
00957                 rect.setLeft( area.left());
00958                 rect.setRight( area.right());
00959                 }
00960             return;
00961             }
00962         if( isMovable())
00963             {
00964             if( old_diff < 0 ) // was in left third, keep distance from left edge
00965                 rect.moveLeft( area.left() + ( -old_diff - 1 ));
00966             else // old_diff > 0 // was in right third, keep distance from right edge
00967                 rect.moveRight( area.right() - ( old_diff - 1 ));
00968             }
00969         else if( isResizable())
00970             {
00971             if( old_diff < 0 )
00972                 rect.setLeft( area.left() + ( -old_diff - 1 ) );
00973             else // old_diff > 0
00974                 rect.setRight( area.right() - ( old_diff - 1 ));
00975             }
00976         if( rect.width() > area.width() && isResizable())
00977             rect.setWidth( area.width());
00978         if( isMovable())
00979             {
00980             if( rect.left() < area.left())
00981                 rect.moveLeft( area.left());
00982             else if( rect.right() > area.right())
00983                 rect.moveRight( area.right());
00984             }
00985         }
00986     if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
00987         { // not visible (almost) at all - try to make it at least partially visible
00988         if( isMovable())
00989             {
00990             if( rect.left() < area.left() + 5 )
00991                 rect.moveRight( area.left() + 5 );
00992             if( rect.right() > area.right() - 5 )
00993                 rect.moveLeft( area.right() - 5 );
00994             }
00995         }
00996     }
00997 
01001 QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const
01002     {
01003     // first, get the window size for the given frame size s
01004 
01005     QSize wsize( frame.width() - ( border_left + border_right ),
01006              frame.height() - ( border_top + border_bottom ));
01007 
01008     return sizeForClientSize( wsize, mode, false );
01009     }
01010 
01011 // this helper returns proper size even if the window is shaded
01012 // see also the comment in Client::setGeometry()
01013 QSize Client::adjustedSize() const
01014     {
01015     return sizeForClientSize( clientSize());
01016     }
01017 
01026 QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode, bool noframe ) const
01027     {
01028     int w = wsize.width();
01029     int h = wsize.height();
01030     if( w < 1 || h < 1 )
01031         {
01032         kdWarning() << "sizeForClientSize() with empty size!" << endl;
01033         kdWarning() << kdBacktrace() << endl;
01034         }
01035     if (w<1) w = 1;
01036     if (h<1) h = 1;
01037 
01038     // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
01039     // even if they're not set in flags - see getWmNormalHints()
01040     QSize min_size = minSize();
01041     QSize max_size = maxSize();
01042     if( decoration != NULL )
01043         {
01044         QSize decominsize = decoration->minimumSize();
01045         QSize border_size( border_left + border_right, border_top + border_bottom );
01046         if( border_size.width() > decominsize.width()) // just in case
01047             decominsize.setWidth( border_size.width());
01048         if( border_size.height() > decominsize.height())
01049             decominsize.setHeight( border_size.height());
01050         if( decominsize.width() > min_size.width())
01051                 min_size.setWidth( decominsize.width());
01052         if( decominsize.height() > min_size.height())
01053                 min_size.setHeight( decominsize.height());
01054         }
01055     w = QMIN( max_size.width(), w );
01056     h = QMIN( max_size.height(), h );
01057     w = QMAX( min_size.width(), w );
01058     h = QMAX( min_size.height(), h );
01059 
01060     int w1 = w;
01061     int h1 = h;
01062     int width_inc = xSizeHint.width_inc;
01063     int height_inc = xSizeHint.height_inc;
01064     int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
01065     int baseh_inc = xSizeHint.min_height;
01066     w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
01067     h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
01068 // code for aspect ratios based on code from FVWM
01069     /*
01070      * The math looks like this:
01071      *
01072      * minAspectX    dwidth     maxAspectX
01073      * ---------- <= ------- <= ----------
01074      * minAspectY    dheight    maxAspectY
01075      *
01076      * If that is multiplied out, then the width and height are
01077      * invalid in the following situations:
01078      *
01079      * minAspectX * dheight > minAspectY * dwidth
01080      * maxAspectX * dheight < maxAspectY * dwidth
01081      *
01082      */
01083     if( xSizeHint.flags & PAspect )
01084         {
01085         double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
01086         double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
01087         double max_aspect_w = xSizeHint.max_aspect.x;
01088         double max_aspect_h = xSizeHint.max_aspect.y;
01089         // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
01090         // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
01091         // and I have no idea how it works, let's hope nobody relies on that.
01092         w -= xSizeHint.base_width;
01093         h -= xSizeHint.base_height;
01094         int max_width = max_size.width() - xSizeHint.base_width;
01095         int min_width = min_size.width() - xSizeHint.base_width;
01096         int max_height = max_size.height() - xSizeHint.base_height;
01097         int min_height = min_size.height() - xSizeHint.base_height;
01098 #define ASPECT_CHECK_GROW_W \
01099         if( min_aspect_w * h > min_aspect_h * w ) \
01100             { \
01101             int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
01102             if( w + delta <= max_width ) \
01103                 w += delta; \
01104             }
01105 #define ASPECT_CHECK_SHRINK_H_GROW_W \
01106         if( min_aspect_w * h > min_aspect_h * w ) \
01107             { \
01108             int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
01109             if( h - delta >= min_height ) \
01110                 h -= delta; \
01111             else \
01112                 { \
01113                 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
01114                 if( w + delta <= max_width ) \
01115                     w += delta; \
01116                 } \
01117             }
01118 #define ASPECT_CHECK_GROW_H \
01119         if( max_aspect_w * h < max_aspect_h * w ) \
01120             { \
01121             int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
01122             if( h + delta <= max_height ) \
01123                 h += delta; \
01124             }
01125 #define ASPECT_CHECK_SHRINK_W_GROW_H \
01126         if( max_aspect_w * h < max_aspect_h * w ) \
01127             { \
01128             int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
01129             if( w - delta >= min_width ) \
01130                 w -= delta; \
01131             else \
01132                 { \
01133                 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
01134                 if( h + delta <= max_height ) \
01135                     h += delta; \
01136                 } \
01137             }
01138         switch( mode )
01139             {
01140             case SizemodeAny:
01141 #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
01142       // so that changing aspect ratio to a different value and back keeps the same size (#87298)
01143                 {
01144                 ASPECT_CHECK_SHRINK_H_GROW_W
01145                 ASPECT_CHECK_SHRINK_W_GROW_H
01146                 ASPECT_CHECK_GROW_H
01147                 ASPECT_CHECK_GROW_W
01148                 break;
01149                 }
01150 #endif
01151             case SizemodeFixedW:
01152                 {
01153                 // the checks are order so that attempts to modify height are first
01154                 ASPECT_CHECK_GROW_H
01155                 ASPECT_CHECK_SHRINK_H_GROW_W
01156                 ASPECT_CHECK_SHRINK_W_GROW_H
01157                 ASPECT_CHECK_GROW_W
01158                 break;
01159                 }
01160             case SizemodeFixedH:
01161                 {
01162                 ASPECT_CHECK_GROW_W
01163                 ASPECT_CHECK_SHRINK_W_GROW_H
01164                 ASPECT_CHECK_SHRINK_H_GROW_W
01165                 ASPECT_CHECK_GROW_H
01166                 break;
01167                 }
01168             case SizemodeMax:
01169                 {
01170                 // first checks that try to shrink
01171                 ASPECT_CHECK_SHRINK_H_GROW_W
01172                 ASPECT_CHECK_SHRINK_W_GROW_H
01173                 ASPECT_CHECK_GROW_W
01174                 ASPECT_CHECK_GROW_H
01175                 break;
01176                 }
01177             }
01178 #undef ASPECT_CHECK_SHRINK_H_GROW_W
01179 #undef ASPECT_CHECK_SHRINK_W_GROW_H
01180 #undef ASPECT_CHECK_GROW_W
01181 #undef ASPECT_CHECK_GROW_H
01182         w += xSizeHint.base_width;
01183         h += xSizeHint.base_height;
01184         }
01185     if( !rules()->checkStrictGeometry( false ))
01186         {
01187         // disobey increments and aspect when maximized
01188         if( maximizeMode() & MaximizeHorizontal )
01189             w = w1;
01190         if( maximizeMode() & MaximizeVertical )
01191             h = h1;
01192         }
01193 
01194     if( !noframe )
01195         {
01196         w += border_left + border_right;
01197         h += border_top + border_bottom;
01198         }
01199     return rules()->checkSize( QSize( w, h ));
01200     }
01201 
01205 void Client::getWmNormalHints()
01206     {
01207     long msize;
01208     if (XGetWMNormalHints(qt_xdisplay(), window(), &xSizeHint, &msize) == 0 )
01209         xSizeHint.flags = 0;
01210     // set defined values for the fields, even if they're not in flags
01211 
01212     if( xSizeHint.flags & PBaseSize )
01213         {
01214         // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
01215         // The other way around PMinSize is not a complete fallback for PBaseSize,
01216         // so that's not handled here.
01217         if( ! ( xSizeHint.flags & PMinSize ))
01218             {
01219             xSizeHint.min_width = xSizeHint.base_width;
01220             xSizeHint.min_height = xSizeHint.base_height;
01221             }
01222         }
01223     else
01224         xSizeHint.base_width = xSizeHint.base_height = 0;
01225     if( ! ( xSizeHint.flags & PMinSize ))
01226         xSizeHint.min_width = xSizeHint.min_height = 0;
01227     if( ! ( xSizeHint.flags & PMaxSize ))
01228         xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
01229     else
01230         {
01231         xSizeHint.max_width = QMAX( xSizeHint.max_width, 1 );
01232         xSizeHint.max_height = QMAX( xSizeHint.max_height, 1 );
01233         }
01234     if( xSizeHint.flags & PResizeInc )
01235         {
01236         xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 );
01237         xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 );
01238         }
01239     else
01240         {
01241         xSizeHint.width_inc = 1;
01242         xSizeHint.height_inc = 1;
01243         }
01244     if( xSizeHint.flags & PAspect )
01245         { // no dividing by zero
01246         xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 );
01247         xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 );
01248         }
01249     else
01250         {
01251         xSizeHint.min_aspect.x = 1;
01252         xSizeHint.min_aspect.y = INT_MAX;
01253         xSizeHint.max_aspect.x = INT_MAX;
01254         xSizeHint.max_aspect.y = 1;
01255         }
01256     if( ! ( xSizeHint.flags & PWinGravity ))
01257         xSizeHint.win_gravity = NorthWestGravity;
01258     if( isManaged())
01259         { // update to match restrictions
01260         QSize new_size = adjustedSize();
01261         if( new_size != size() && !isFullScreen())
01262             resizeWithChecks( new_size );
01263         }
01264     updateAllowedActions(); // affects isResizeable()
01265     }
01266 
01267 QSize Client::minSize() const
01268     {
01269     return rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height ));
01270     }
01271 
01272 QSize Client::maxSize() const
01273     {
01274     return rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height ));
01275     }
01276 
01282 void Client::sendSyntheticConfigureNotify()
01283     {
01284     XConfigureEvent c;
01285     c.type = ConfigureNotify;
01286     c.send_event = True;
01287     c.event = window();
01288     c.window = window();
01289     c.x = x() + clientPos().x();
01290     c.y = y() + clientPos().y();
01291     c.width = clientSize().width();
01292     c.height = clientSize().height();
01293     c.border_width = 0;
01294     c.above = None;
01295     c.override_redirect = 0;
01296     XSendEvent( qt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c );
01297     }
01298 
01299 const QPoint Client::calculateGravitation( bool invert, int gravity ) const
01300     {
01301     int dx, dy;
01302     dx = dy = 0;
01303 
01304     if( gravity == 0 ) // default (nonsense) value for the argument
01305         gravity = xSizeHint.win_gravity;
01306 
01307 // dx, dy specify how the client window moves to make space for the frame
01308     switch (gravity)
01309         {
01310         case NorthWestGravity: // move down right
01311         default:
01312             dx = border_left;
01313             dy = border_top;
01314             break;
01315         case NorthGravity: // move right
01316             dx = 0;
01317             dy = border_top;
01318             break;
01319         case NorthEastGravity: // move down left
01320             dx = -border_right;
01321             dy = border_top;
01322             break;
01323         case WestGravity: // move right
01324             dx = border_left;
01325             dy = 0;
01326             break;
01327         case CenterGravity:
01328             break; // will be handled specially
01329         case StaticGravity: // don't move
01330             dx = 0;
01331             dy = 0;
01332             break;
01333         case EastGravity: // move left
01334             dx = -border_right;
01335             dy = 0;
01336             break;
01337         case SouthWestGravity: // move up right
01338             dx = border_left ;
01339             dy = -border_bottom;
01340             break;
01341         case SouthGravity: // move up
01342             dx = 0;
01343             dy = -border_bottom;
01344             break;
01345         case SouthEastGravity: // move up left
01346             dx = -border_right;
01347             dy = -border_bottom;
01348             break;
01349         }
01350     if( gravity != CenterGravity )
01351         { // translate from client movement to frame movement
01352         dx -= border_left;
01353         dy -= border_top;
01354         }
01355     else
01356         { // center of the frame will be at the same position client center without frame would be
01357         dx = - ( border_left + border_right ) / 2;
01358         dy = - ( border_top + border_bottom ) / 2;
01359         }
01360     if( !invert )
01361         return QPoint( x() + dx, y() + dy );
01362     else
01363         return QPoint( x() - dx, y() - dy );
01364     }
01365 
01366 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
01367     {
01368     if( gravity == 0 ) // default (nonsense) value for the argument
01369         gravity = xSizeHint.win_gravity;
01370     if( value_mask & ( CWX | CWY ))
01371         {
01372         QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
01373         if ( value_mask & CWX )
01374             new_pos.setX( rx );
01375         if ( value_mask & CWY )
01376             new_pos.setY( ry );
01377 
01378         // clever(?) workaround for applications like xv that want to set
01379         // the location to the current location but miscalculate the
01380         // frame size due to kwin being a double-reparenting window
01381         // manager
01382         if ( new_pos.x() == x() + clientPos().x() &&
01383              new_pos.y() == y() + clientPos().y() && gravity == NorthWestGravity )
01384             {
01385             new_pos.setX( x());
01386             new_pos.setY( y());
01387             }
01388 
01389         int nw = clientSize().width();
01390         int nh = clientSize().height();
01391         if ( value_mask & CWWidth )
01392             nw = rw;
01393         if ( value_mask & CWHeight )
01394             nh = rh;
01395         QSize ns = sizeForClientSize( QSize( nw, nh ) );
01396 
01397         // TODO what to do with maximized windows?
01398         if ( maximizeMode() != MaximizeFull
01399             || ns != size())
01400             {
01401             QRect orig_geometry = geometry();
01402             GeometryUpdatesPostponer blocker( this );
01403             move( new_pos );
01404             plainResize( ns );
01405             setGeometry( QRect( calculateGravitation( false, gravity ), size()));
01406             updateFullScreenHack( QRect( new_pos, QSize( nw, nh )));
01407             QRect area = workspace()->clientArea( WorkArea, this );
01408             if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
01409                 && area.contains( orig_geometry ))
01410                 keepInArea( area );
01411 
01412             // this is part of the kicker-xinerama-hack... it should be
01413             // safe to remove when kicker gets proper ExtendedStrut support;
01414             // see Workspace::updateClientArea() and
01415             // Client::adjustedClientArea()
01416             if (hasStrut ())
01417                 workspace() -> updateClientArea ();
01418             }
01419         }
01420 
01421     if ( value_mask & (CWWidth | CWHeight )
01422         && ! ( value_mask & ( CWX | CWY )) )  // pure resize
01423         {
01424         int nw = clientSize().width();
01425         int nh = clientSize().height();
01426         if ( value_mask & CWWidth )
01427             nw = rw;
01428         if ( value_mask & CWHeight )
01429             nh = rh;
01430         QSize ns = sizeForClientSize( QSize( nw, nh ) );
01431 
01432         if( ns != size())  // don't restore if some app sets its own size again
01433             {
01434             QRect orig_geometry = geometry();
01435             GeometryUpdatesPostponer blocker( this );
01436             int save_gravity = xSizeHint.win_gravity;
01437             xSizeHint.win_gravity = gravity;
01438             resizeWithChecks( ns );
01439             xSizeHint.win_gravity = save_gravity;
01440             updateFullScreenHack( QRect( calculateGravitation( true, xSizeHint.win_gravity ), QSize( nw, nh )));
01441             QRect area = workspace()->clientArea( WorkArea, this );
01442             if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
01443                 && area.contains( orig_geometry ))
01444                 keepInArea( area );
01445             }
01446         }
01447     // No need to send synthetic configure notify event here, either it's sent together
01448     // with geometry change, or there's no need to send it.
01449     // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
01450     }
01451 
01452 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
01453     {
01454     if( shade_geometry_change )
01455         assert( false );
01456     else if( isShade())
01457         {
01458         if( h == border_top + border_bottom )
01459             {
01460             kdWarning() << "Shaded geometry passed for size:" << endl;
01461             kdWarning() << kdBacktrace() << endl;
01462             }
01463         }
01464     int newx = x();
01465     int newy = y();
01466     QRect area = workspace()->clientArea( WorkArea, this );
01467     // don't allow growing larger than workarea
01468     if( w > area.width())
01469         w = area.width();
01470     if( h > area.height())
01471         h = area.height();
01472     QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size
01473     w = tmp.width();
01474     h = tmp.height();
01475     switch( xSizeHint.win_gravity )
01476         {
01477         case NorthWestGravity: // top left corner doesn't move
01478         default:
01479             break;
01480         case NorthGravity: // middle of top border doesn't move
01481             newx = ( newx + width() / 2 ) - ( w / 2 );
01482             break;
01483         case NorthEastGravity: // top right corner doesn't move
01484             newx = newx + width() - w;
01485             break;
01486         case WestGravity: // middle of left border doesn't move
01487             newy = ( newy + height() / 2 ) - ( h / 2 );
01488             break;
01489         case CenterGravity: // middle point doesn't move
01490             newx = ( newx + width() / 2 ) - ( w / 2 );
01491             newy = ( newy + height() / 2 ) - ( h / 2 );
01492             break;
01493         case StaticGravity: // top left corner of _client_ window doesn't move
01494             // since decoration doesn't change, equal to NorthWestGravity
01495             break;
01496         case EastGravity: // // middle of right border doesn't move
01497             newx = newx + width() - w;
01498             newy = ( newy + height() / 2 ) - ( h / 2 );
01499             break;
01500         case SouthWestGravity: // bottom left corner doesn't move
01501             newy = newy + height() - h;
01502             break;
01503         case SouthGravity: // middle of bottom border doesn't move
01504             newx = ( newx + width() / 2 ) - ( w / 2 );
01505             newy = newy + height() - h;
01506             break;
01507         case SouthEastGravity: // bottom right corner doesn't move
01508             newx = newx + width() - w;
01509             newy = newy + height() - h;
01510             break;
01511         }
01512     // if it would be moved outside of workarea, keep it inside,
01513     // see also Client::computeWorkareaDiff()
01514     if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
01515         {
01516         if( newx < area.left())
01517             newx = area.left();
01518         if( newx + w > area.right() + 1 )
01519             newx = area.right() + 1 - w;
01520         assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
01521         }
01522     if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
01523         {
01524         if( newy < area.top())
01525             newy = area.top();
01526         if( newy + h > area.bottom() + 1 )
01527             newy = area.bottom() + 1 - h;
01528         assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
01529         }
01530     setGeometry( newx, newy, w, h, force );
01531     }
01532 
01533 // _NET_MOVERESIZE_WINDOW
01534 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
01535     {
01536     int gravity = flags & 0xff;
01537     int value_mask = 0;
01538     if( flags & ( 1 << 8 ))
01539         value_mask |= CWX;
01540     if( flags & ( 1 << 9 ))
01541         value_mask |= CWY;
01542     if( flags & ( 1 << 10 ))
01543         value_mask |= CWWidth;
01544     if( flags & ( 1 << 11 ))
01545         value_mask |= CWHeight;
01546     configureRequest( value_mask, x, y, width, height, gravity, true );
01547     }
01548 
01553 bool Client::isMovable() const
01554     {
01555     if( !motif_may_move || isFullScreen())
01556         return false;
01557     if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
01558         return false;
01559     if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
01560         return false;
01561     if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
01562         return false;
01563     return true;
01564     }
01565 
01569 bool Client::isResizable() const
01570     {
01571     if( !motif_may_resize || isFullScreen())
01572         return false;
01573     if( isSpecialWindow() )
01574         return false;
01575     if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
01576         return false;
01577     if( rules()->checkSize( QSize()).isValid()) // forced size
01578         return false;
01579 
01580     QSize min = minSize();
01581     QSize max = maxSize();
01582     return min.width() < max.width() || min.height() < max.height();
01583     }
01584 
01585 /*
01586   Returns whether the window is maximizable or not
01587  */
01588 bool Client::isMaximizable() const
01589     {
01590         { // isMovable() and isResizable() may be false for maximized windows
01591           // with moving/resizing maximized windows disabled
01592         TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
01593         if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
01594             return false;
01595         }
01596     if ( maximizeMode() != MaximizeRestore )
01597         return TRUE;
01598     QSize max = maxSize();
01599 #if 0
01600     if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
01601         return false;
01602 #else
01603     // apparently there are enough apps which specify some arbitrary value
01604     // for their maximum size just for the fun of it
01605     QSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
01606     if( max.width() < areasize.width() || max.height() < areasize.height())
01607         return false;
01608 #endif
01609     return true;
01610     }
01611 
01612 
01616 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
01617     {
01618     // this code is also duplicated in Client::plainResize()
01619     // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
01620     // simply because there are too many places dealing with geometry. Those places
01621     // ignore shaded state and use normal geometry, which they usually should get
01622     // from adjustedSize(). Such geometry comes here, and if the window is shaded,
01623     // the geometry is used only for client_size, since that one is not used when
01624     // shading. Then the frame geometry is adjusted for the shaded geometry.
01625     // This gets more complicated in the case the code does only something like
01626     // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
01627     // Such code is wrong and should be changed to handle the case when the window is shaded,
01628     // for example using Client::clientSize().
01629     if( shade_geometry_change )
01630         ; // nothing
01631     else if( isShade())
01632         {
01633         if( h == border_top + border_bottom )
01634             {
01635             kdDebug() << "Shaded geometry passed for size:" << endl;
01636             kdDebug() << kdBacktrace() << endl;
01637             }
01638         else
01639             {
01640             client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01641             h = border_top + border_bottom;
01642             }
01643         }
01644     else
01645         {
01646         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01647         }
01648     if( force == NormalGeometrySet && frame_geometry == QRect( x, y, w, h ))
01649         return;
01650     frame_geometry = QRect( x, y, w, h );
01651     updateWorkareaDiffs();
01652     if( postpone_geometry_updates != 0 )
01653         {
01654         pending_geometry_update = true;
01655         return;
01656         }
01657     resizeDecoration( QSize( w, h ));
01658     XMoveResizeWindow( qt_xdisplay(), frameId(), x, y, w, h );
01659 //     resizeDecoration( QSize( w, h ));
01660     if( !isShade())
01661         {
01662         QSize cs = clientSize();
01663         XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
01664             cs.width(), cs.height());
01665         XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
01666         }
01667     if( shape())
01668         updateShape();
01669     // SELI TODO won't this be too expensive?
01670     updateWorkareaDiffs();
01671     sendSyntheticConfigureNotify();
01672     updateWindowRules();
01673     checkMaximizeGeometry();
01674     }
01675 
01676 void Client::plainResize( int w, int h, ForceGeometry_t force )
01677     {
01678     // this code is also duplicated in Client::setGeometry(), and it's also commented there
01679     if( shade_geometry_change )
01680         ; // nothing
01681     else if( isShade())
01682         {
01683         if( h == border_top + border_bottom )
01684             {
01685             kdDebug() << "Shaded geometry passed for size:" << endl;
01686             kdDebug() << kdBacktrace() << endl;
01687             }
01688         else
01689             {
01690             client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01691             h = border_top + border_bottom;
01692             }
01693         }
01694     else
01695         {
01696         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01697         }
01698     if( QSize( w, h ) != rules()->checkSize( QSize( w, h )))
01699         {
01700         kdDebug() << "forced size fail:" << QSize( w,h ) << ":" << rules()->checkSize( QSize( w, h )) << endl;
01701         kdDebug() << kdBacktrace() << endl;
01702         }
01703     if( force == NormalGeometrySet && frame_geometry.size() == QSize( w, h ))
01704         return;
01705     frame_geometry.setSize( QSize( w, h ));
01706     updateWorkareaDiffs();
01707     if( postpone_geometry_updates != 0 )
01708         {
01709         pending_geometry_update = true;
01710         return;
01711         }
01712     resizeDecoration( QSize( w, h ));
01713     XResizeWindow( qt_xdisplay(), frameId(), w, h );
01714 //     resizeDecoration( QSize( w, h ));
01715     if( !isShade())
01716         {
01717         QSize cs = clientSize();
01718         XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
01719             cs.width(), cs.height());
01720         XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
01721         }
01722     if( shape())
01723         updateShape();
01724     updateWorkareaDiffs();
01725     sendSyntheticConfigureNotify();
01726     updateWindowRules();
01727     checkMaximizeGeometry();
01728     }
01729 
01733 void Client::move( int x, int y, ForceGeometry_t force )
01734     {
01735     if( force == NormalGeometrySet && frame_geometry.topLeft() == QPoint( x, y ))
01736         return;
01737     frame_geometry.moveTopLeft( QPoint( x, y ));
01738     updateWorkareaDiffs();
01739     if( postpone_geometry_updates != 0 )
01740         {
01741         pending_geometry_update = true;
01742         return;
01743         }
01744     XMoveWindow( qt_xdisplay(), frameId(), x, y );
01745     sendSyntheticConfigureNotify();
01746     updateWindowRules();
01747     checkMaximizeGeometry();
01748     }
01749 
01750 
01751 void Client::postponeGeometryUpdates( bool postpone )
01752     {
01753     if( postpone )
01754         {
01755         if( postpone_geometry_updates == 0 )
01756             pending_geometry_update = false;
01757         ++postpone_geometry_updates;
01758         }
01759     else
01760         {
01761         if( --postpone_geometry_updates == 0 )
01762             {
01763             if( pending_geometry_update )
01764                 {
01765                 if( isShade())
01766                     setGeometry( QRect( pos(), adjustedSize()), ForceGeometrySet );
01767                 else
01768                     setGeometry( geometry(), ForceGeometrySet );
01769                 pending_geometry_update = false;
01770                 }
01771             }
01772         }
01773     }
01774 
01775 void Client::maximize( MaximizeMode m )
01776     {
01777     setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
01778     }
01779 
01783 void Client::setMaximize( bool vertically, bool horizontally )
01784     {   // changeMaximize() flips the state, so change from set->flip
01785     changeMaximize(
01786         max_mode & MaximizeVertical ? !vertically : vertically,
01787         max_mode & MaximizeHorizontal ? !horizontally : horizontally,
01788         false );
01789     }
01790 
01791 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
01792     {
01793     if( !isMaximizable())
01794         return;
01795 
01796     MaximizeMode old_mode = max_mode;
01797     // 'adjust == true' means to update the size only, e.g. after changing workspace size
01798     if( !adjust )
01799         {
01800         if( vertical )
01801             max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
01802         if( horizontal )
01803             max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
01804         }
01805         
01806     max_mode = rules()->checkMaximize( max_mode );
01807     if( !adjust && max_mode == old_mode )
01808         return;
01809 
01810     GeometryUpdatesPostponer blocker( this );
01811 
01812     // maximing one way and unmaximizing the other way shouldn't happen
01813     Q_ASSERT( !( vertical && horizontal )
01814         || (( max_mode & MaximizeVertical != 0 ) == ( max_mode & MaximizeHorizontal != 0 )));
01815 
01816     QRect clientArea = workspace()->clientArea( MaximizeArea, this );
01817 
01818     // save sizes for restoring, if maximalizing
01819     if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
01820         {
01821         geom_restore.setTop( y());
01822         geom_restore.setHeight( height());
01823         }
01824     if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
01825         {
01826         geom_restore.setLeft( x());
01827         geom_restore.setWidth( width());
01828         }
01829 
01830     if( !adjust )
01831         {
01832         if(( vertical && !(old_mode & MaximizeVertical ))
01833             || ( horizontal && !( old_mode & MaximizeHorizontal )))
01834             Notify::raise( Notify::Maximize );
01835         else
01836             Notify::raise( Notify::UnMaximize );
01837         }
01838 
01839     if( decoration != NULL ) // decorations may turn off some borders when maximized
01840         decoration->borders( border_left, border_right, border_top, border_bottom );
01841 
01842     // restore partial maximizations
01843     if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
01844         {
01845         if ( maximizeModeRestore()==MaximizeVertical )
01846         {
01847         max_mode = MaximizeVertical;
01848         maxmode_restore = MaximizeRestore;
01849         }
01850     if ( maximizeModeRestore()==MaximizeHorizontal )
01851         {
01852         max_mode = MaximizeHorizontal;
01853         maxmode_restore = MaximizeRestore;
01854         }   
01855     }
01856     
01857     switch (max_mode)
01858         {
01859 
01860         case MaximizeVertical:
01861             {
01862             if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
01863                 {
01864                 if( geom_restore.width() == 0 )
01865                     { // needs placement
01866                     plainResize( adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH ));
01867                     workspace()->placeSmart( this, clientArea );
01868                     }
01869                 else
01870                     setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()),
01871                               adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
01872                 }
01873             else
01874                 setGeometry( QRect(QPoint(x(), clientArea.top()),
01875                               adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
01876             info->setState( NET::MaxVert, NET::Max );
01877             break;
01878             }
01879 
01880         case MaximizeHorizontal:
01881             {
01882             if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
01883                 {
01884                 if( geom_restore.height() == 0 )
01885                     { // needs placement
01886                     plainResize( adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW ));
01887                     workspace()->placeSmart( this, clientArea );
01888                     }
01889                 else
01890                     setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()),
01891                               adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), ForceGeometrySet);
01892                 }
01893             else
01894                 setGeometry( QRect( QPoint(clientArea.left(), y()),
01895                               adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )), ForceGeometrySet);
01896             info->setState( NET::MaxHoriz, NET::Max );
01897             break;
01898             }
01899 
01900         case MaximizeRestore:
01901             {
01902             QRect restore = geometry();
01903     // when only partially maximized, geom_restore may not have the other dimension remembered
01904             if( old_mode & MaximizeVertical )
01905                 {
01906                 restore.setTop( geom_restore.top());
01907                 restore.setBottom( geom_restore.bottom());
01908                 }
01909             if( old_mode & MaximizeHorizontal )
01910                 {
01911                 restore.setLeft( geom_restore.left());
01912                 restore.setRight( geom_restore.right());
01913                 }
01914             if( !restore.isValid())
01915                 {
01916                 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 );
01917                 if( geom_restore.width() > 0 )
01918                     s.setWidth( geom_restore.width());
01919                 if( geom_restore.height() > 0 )
01920                     s.setHeight( geom_restore.height());
01921                 plainResize( adjustedSize( s ));
01922                 workspace()->placeSmart( this, clientArea );
01923                 restore = geometry();
01924                 if( geom_restore.width() > 0 )
01925                     restore.moveLeft( geom_restore.x());
01926                 if( geom_restore.height() > 0 )
01927                     restore.moveTop( geom_restore.y());
01928                 }
01929             setGeometry( restore, ForceGeometrySet );
01930             info->setState( 0, NET::Max );
01931             break;
01932             }
01933 
01934         case MaximizeFull:
01935             {
01936             if( !adjust )
01937                 {
01938                 if( old_mode & MaximizeVertical )
01939                     maxmode_restore = MaximizeVertical;
01940                 if( old_mode & MaximizeHorizontal )
01941                     maxmode_restore = MaximizeHorizontal;
01942                 }
01943             QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
01944             QRect r = QRect(clientArea.topLeft(), adjSize);
01945             setGeometry( r, ForceGeometrySet );
01946             info->setState( NET::Max, NET::Max );
01947             break;
01948             }
01949         default:
01950             break;
01951         }
01952 
01953     updateAllowedActions();
01954     if( decoration != NULL )
01955         decoration->maximizeChange();
01956     updateWindowRules();
01957     }
01958 
01959 void Client::resetMaximize()
01960     {
01961     if( max_mode == MaximizeRestore )
01962         return;
01963     max_mode = MaximizeRestore;
01964     Notify::raise( Notify::UnMaximize );
01965     info->setState( 0, NET::Max );
01966     updateAllowedActions();
01967     if( decoration != NULL )
01968         decoration->borders( border_left, border_right, border_top, border_bottom );
01969     if( isShade())
01970         setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
01971     else
01972         setGeometry( geometry(), ForceGeometrySet );
01973     if( decoration != NULL )
01974         decoration->maximizeChange();
01975     }
01976 
01977 void Client::checkMaximizeGeometry()
01978     {
01979     // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
01980     // when after the condition is no longer true
01981     if( isShade())
01982         return;
01983     if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
01984         return;
01985     // Just in case.
01986     static int recursion_protection = 0;
01987     if( recursion_protection > 3 )
01988         {
01989         kdWarning( 1212 ) << "Check maximize overflow - you loose!" << endl;
01990         kdWarning( 1212 ) << kdBacktrace() << endl;
01991         return;
01992         }
01993     ++recursion_protection;
01994     QRect max_area = workspace()->clientArea( MaximizeArea, this );
01995     if( geometry() == max_area )
01996         {
01997         if( max_mode != MaximizeFull )
01998             maximize( MaximizeFull );
01999         }
02000     else if( x() == max_area.left() && width() == max_area.width())
02001         {
02002         if( max_mode != MaximizeHorizontal )
02003             maximize( MaximizeHorizontal );
02004         }
02005     else if( y() == max_area.top() && height() == max_area.height())
02006         {
02007         if( max_mode != MaximizeVertical )
02008             maximize( MaximizeVertical );
02009         }
02010     else if( max_mode != MaximizeRestore )
02011         {
02012         resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
02013         }
02014     --recursion_protection;
02015     }
02016 
02017 bool Client::isFullScreenable( bool fullscreen_hack ) const
02018     {
02019     if( !rules()->checkFullScreen( true ))
02020         return false;
02021     if( fullscreen_hack )
02022         return isNormalWindow();
02023     if( rules()->checkStrictGeometry( false ))
02024         {
02025         // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
02026         QRect fsarea = workspace()->clientArea( FullScreenArea, this );
02027         if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
02028             return false;
02029         }
02030      // don't check size constrains - some apps request fullscreen despite requesting fixed size
02031     return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
02032     }
02033 
02034 bool Client::userCanSetFullScreen() const
02035     {
02036     if( fullscreen_mode == FullScreenHack )
02037         return false;
02038     if( !isFullScreenable( false ))
02039         return false;
02040     // isMaximizable() returns false if fullscreen
02041     TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
02042     return isNormalWindow() && isMaximizable();
02043     }
02044 
02045 void Client::setFullScreen( bool set, bool user )
02046     {
02047     if( !isFullScreen() && !set )
02048         return;
02049     if( fullscreen_mode == FullScreenHack )
02050         return;
02051     if( user && !userCanSetFullScreen())
02052         return;
02053     set = rules()->checkFullScreen( set );
02054     setShade( ShadeNone );
02055     bool was_fs = isFullScreen();
02056     if( !was_fs )
02057         geom_fs_restore = geometry();
02058     fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
02059     if( was_fs == isFullScreen())
02060         return;
02061     StackingUpdatesBlocker blocker1( workspace());
02062     GeometryUpdatesPostponer blocker2( this );
02063     workspace()->updateClientLayer( this ); // active fullscreens get different layer
02064     info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
02065     updateDecoration( false, false );
02066     // XINERAMA
02067     if( isFullScreen() && xSizeHint.width > workspace()->clientArea(FullScreenArea, this).width() )
02068         setGeometry( workspace()->clientArea( FullXineramaArea, this ));
02069     else if( isFullScreen() && xSizeHint.height > workspace()->clientArea(FullScreenArea, this).height() )
02070         setGeometry( workspace()->clientArea( FullXineramaArea, this ));
02071     else if( isFullScreen())
02072         setGeometry( workspace()->clientArea( FullScreenArea, this ));
02073     else
02074         {
02075         if( !geom_fs_restore.isNull())
02076             setGeometry( QRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
02077         // TODO isShaded() ?
02078         else
02079             { // does this ever happen?
02080             setGeometry( workspace()->clientArea( MaximizeArea, this ));
02081             }
02082         }
02083     updateWindowRules();
02084     }
02085 
02086 bool Client::checkFullScreenHack( const QRect& geom ) const
02087     {
02088     // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
02089     return (( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size()
02090             || geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
02091         && noBorder() && !isUserNoBorder() && isFullScreenable( true ));
02092     }
02093 
02094 void Client::updateFullScreenHack( const QRect& geom )
02095     {
02096     bool is_hack = checkFullScreenHack( geom );
02097     if( fullscreen_mode == FullScreenNone && is_hack )
02098         {
02099         fullscreen_mode = FullScreenHack;
02100         updateDecoration( false, false );
02101         setGeometry( workspace()->clientArea( FullScreenArea, this ));
02102         }
02103     else if( fullscreen_mode == FullScreenHack && !is_hack )
02104         {
02105         fullscreen_mode = FullScreenNone;
02106         updateDecoration( false, false );
02107         // whoever called this must setup correct geometry
02108         }
02109     StackingUpdatesBlocker blocker( workspace());
02110     workspace()->updateClientLayer( this ); // active fullscreens get different layer
02111     }
02112 
02113 static QRect*       visible_bound  = 0;
02114 static GeometryTip* geometryTip    = 0;
02115 
02116 void Client::drawbound( const QRect& geom )
02117     {
02118     assert( visible_bound == NULL );
02119     visible_bound = new QRect( geom );
02120     doDrawbound( *visible_bound, false );
02121     }
02122 
02123 void Client::clearbound()
02124     {
02125     if( visible_bound == NULL )
02126         return;
02127     doDrawbound( *visible_bound, true );
02128     delete visible_bound;
02129     visible_bound = 0;
02130     }
02131 
02132 void Client::doDrawbound( const QRect& geom, bool clear )
02133     {
02134     if( decoration != NULL && decoration->drawbound( geom, clear ))
02135         return; // done by decoration
02136     QPainter p ( workspace()->desktopWidget() );
02137     p.setPen( QPen( Qt::white, 5 ) );
02138     p.setRasterOp( Qt::XorROP );
02139     // the line is 5 pixel thick, so compensate for the extra two pixels
02140     // on outside (#88657)
02141     QRect g = geom;
02142     if( g.width() > 5 )
02143         {
02144         g.setLeft( g.left() + 2 );
02145         g.setRight( g.right() - 2 );
02146         }
02147     if( g.height() > 5 )
02148         {
02149         g.setTop( g.top() + 2 );
02150         g.setBottom( g.bottom() - 2 );
02151         }
02152     p.drawRect( g );
02153     }
02154 
02155 void Client::positionGeometryTip()
02156     {
02157     assert( isMove() || isResize());
02158     // Position and Size display
02159     if (options->showGeometryTip())
02160         {
02161         if( !geometryTip )
02162             { // save under is not necessary with opaque, and seem to make things slower
02163             bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02164                         || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
02165             geometryTip = new GeometryTip( &xSizeHint, save_under );
02166             }
02167         QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
02168         wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
02169         wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
02170         if( isShade())
02171             wgeom.setHeight( 0 );
02172         geometryTip->setGeometry( wgeom );
02173         if( !geometryTip->isVisible())
02174             {
02175             geometryTip->show();
02176             geometryTip->raise();
02177             }
02178         }
02179     }
02180 
02181 class EatAllPaintEvents
02182     : public QObject
02183     {
02184     protected:
02185         virtual bool eventFilter( QObject* o, QEvent* e )
02186             { return e->type() == QEvent::Paint && o != geometryTip; }
02187     };
02188 
02189 static EatAllPaintEvents* eater = 0;
02190 
02191 bool Client::startMoveResize()
02192     {
02193     assert( !moveResizeMode );
02194     assert( QWidget::keyboardGrabber() == NULL );
02195     assert( QWidget::mouseGrabber() == NULL );
02196     if( QApplication::activePopupWidget() != NULL )
02197         return false; // popups have grab
02198     bool has_grab = false;
02199     // This reportedly improves smoothness of the moveresize operation,
02200     // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
02201     // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
02202     XSetWindowAttributes attrs;
02203     QRect r = workspace()->clientArea( FullArea, this );
02204     move_resize_grab_window = XCreateWindow( qt_xdisplay(), workspace()->rootWin(), r.x(), r.y(),
02205         r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
02206     XMapRaised( qt_xdisplay(), move_resize_grab_window );
02207     if( XGrabPointer( qt_xdisplay(), move_resize_grab_window, False,
02208         ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
02209         GrabModeAsync, GrabModeAsync, None, cursor.handle(), qt_x_time ) == Success )
02210         has_grab = true;
02211     if( XGrabKeyboard( qt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, qt_x_time ) == Success )
02212         has_grab = true;
02213     if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
02214         {
02215         XDestroyWindow( qt_xdisplay(), move_resize_grab_window );
02216         move_resize_grab_window = None;
02217         return false;
02218         }
02219     if ( maximizeMode() != MaximizeRestore )
02220         resetMaximize();
02221     moveResizeMode = true;
02222     workspace()->setClientIsMoving(this);
02223     initialMoveResizeGeom = moveResizeGeom = geometry();
02224     checkUnrestrictedMoveResize();
02225     // rule out non opaque windows from useless translucency settings, maybe resizes?
02226     if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
02227         setShadowSize(0);
02228     if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque){
02229         savedOpacity_ = opacity_;
02230         setOpacity(options->translucentMovingWindows, options->movingWindowOpacity);
02231     }
02232     if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02233       || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
02234         {
02235         grabXServer();
02236         kapp->sendPostedEvents();
02237         // we have server grab -> nothing should cause paint events
02238         // unfortunately, that's not completely true, Qt may generate
02239         // paint events on some widgets due to FocusIn(?)
02240         // eat them, otherwise XOR painting will be broken (#58054)
02241         // paint events for the geometrytip need to be allowed, though
02242         eater = new EatAllPaintEvents;
02243 // not needed anymore?        kapp->installEventFilter( eater );
02244         }
02245     Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
02246     return true;
02247     }
02248 
02249 void Client::finishMoveResize( bool cancel )
02250     {
02251     leaveMoveResize();
02252     if( cancel )
02253         setGeometry( initialMoveResizeGeom );
02254     else
02255         setGeometry( moveResizeGeom );
02256     checkMaximizeGeometry();
02257 // FRAME    update();
02258     Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
02259     }
02260 
02261 void Client::leaveMoveResize()
02262     {
02263     // rule out non opaque windows from useless translucency settings, maybe resizes?
02264     if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
02265         setOpacity(true, savedOpacity_);
02266     if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
02267         updateShadowSize();
02268     clearbound();
02269     if (geometryTip)
02270         {
02271         geometryTip->hide();
02272         delete geometryTip;
02273         geometryTip = NULL;
02274         }
02275     if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02276       || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
02277         ungrabXServer();
02278     XUngrabKeyboard( qt_xdisplay(), qt_x_time );
02279     XUngrabPointer( qt_xdisplay(), qt_x_time );
02280     XDestroyWindow( qt_xdisplay(), move_resize_grab_window );
02281     move_resize_grab_window = None;
02282     workspace()->setClientIsMoving(0);
02283     if( move_faked_activity )
02284         workspace()->unfakeActivity( this );
02285     move_faked_activity = false;
02286     moveResizeMode = false;
02287     delete eater;
02288     eater = 0;
02289     }
02290 
02291 // This function checks if it actually makes sense to perform a restricted move/resize.
02292 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
02293 // a restricted move resize, because then e.g. resize would also move the window (#74555).
02294 // NOTE: Most of it is duplicated from handleMoveResize().
02295 void Client::checkUnrestrictedMoveResize()
02296     {
02297     if( unrestrictedMoveResize )
02298         return;
02299     QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
02300     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
02301     // restricted move/resize - keep at least part of the titlebar always visible 
02302     // how much must remain visible when moved away in that direction
02303     left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
02304     right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
02305     // width/height change with opaque resizing, use the initial ones
02306     titlebar_marge = initialMoveResizeGeom.height();
02307     top_marge = border_bottom;
02308     bottom_marge = border_top;
02309     if( isResize())
02310         {
02311         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
02312             unrestrictedMoveResize = true;
02313         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02314             unrestrictedMoveResize = true;
02315         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02316             unrestrictedMoveResize = true;
02317         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02318             unrestrictedMoveResize = true;
02319         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
02320             unrestrictedMoveResize = true;
02321         }
02322     if( isMove())
02323         {
02324         if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
02325             unrestrictedMoveResize = true;
02326         // no need to check top_marge, titlebar_marge already handles it
02327         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02328             unrestrictedMoveResize = true;
02329         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02330             unrestrictedMoveResize = true;
02331         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02332             unrestrictedMoveResize = true;
02333         }
02334     }
02335 
02336 void Client::handleMoveResize( int x, int y, int x_root, int y_root )
02337     {
02338     if(( mode == PositionCenter && !isMovable())
02339         || ( mode != PositionCenter && ( isShade() || !isResizable())))
02340         return;
02341 
02342     if ( !moveResizeMode )
02343         {
02344         QPoint p( QPoint( x, y ) - moveOffset );
02345         if (p.manhattanLength() >= 6)
02346             {
02347             if( !startMoveResize())
02348                 {
02349                 buttonDown = false;
02350                 setCursor( mode );
02351                 return;
02352                 }
02353             }
02354         else
02355             return;
02356         }
02357 
02358     // ShadeHover or ShadeActive, ShadeNormal was already avoided above
02359     if ( mode != PositionCenter && shade_mode != ShadeNone )
02360         setShade( ShadeNone );
02361 
02362     QPoint globalPos( x_root, y_root );
02363     // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
02364     // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
02365     QPoint topleft = globalPos - moveOffset;
02366     QPoint bottomright = globalPos + invertedMoveOffset;
02367     QRect previousMoveResizeGeom = moveResizeGeom;
02368 
02369     // TODO move whole group when moving its leader or when the leader is not mapped?
02370 
02371     // compute bounds
02372     // NOTE: This is duped in checkUnrestrictedMoveResize().
02373     QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
02374     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
02375     if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
02376         left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
02377     else // restricted move/resize - keep at least part of the titlebar always visible 
02378         {        
02379         // how much must remain visible when moved away in that direction
02380         left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
02381         right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
02382         // width/height change with opaque resizing, use the initial ones
02383         titlebar_marge = initialMoveResizeGeom.height();
02384         top_marge = border_bottom;
02385         bottom_marge = border_top;
02386         }
02387 
02388     bool update = false;
02389     if( isResize())
02390         {
02391         // first resize (without checking constrains), then snap, then check bounds, then check constrains
02392         QRect orig = initialMoveResizeGeom;
02393         Sizemode sizemode = SizemodeAny;
02394         switch ( mode )
02395             {
02396             case PositionTopLeft:
02397                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
02398                 break;
02399             case PositionBottomRight:
02400                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
02401                 break;
02402             case PositionBottomLeft:
02403                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
02404                 break;
02405             case PositionTopRight:
02406                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02407                 break;
02408             case PositionTop:
02409                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
02410                 sizemode = SizemodeFixedH; // try not to affect height
02411                 break;
02412             case PositionBottom:
02413                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ;
02414                 sizemode = SizemodeFixedH;
02415                 break;
02416             case PositionLeft:
02417                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
02418                 sizemode = SizemodeFixedW;
02419                 break;
02420             case PositionRight:
02421                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ;
02422                 sizemode = SizemodeFixedW;
02423                 break;
02424             case PositionCenter:
02425             default:
02426                 assert( false );
02427                 break;
02428             }
02429 
02430         // adjust new size to snap to other windows/borders
02431         moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
02432 
02433         // NOTE: This is duped in checkUnrestrictedMoveResize().
02434         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
02435             moveResizeGeom.setBottom( desktopArea.top() + top_marge );
02436         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02437             moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
02438         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02439             moveResizeGeom.setRight( desktopArea.left() + left_marge );
02440         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02441             moveResizeGeom.setLeft(desktopArea.right() - right_marge );
02442         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
02443             moveResizeGeom.setTop( desktopArea.top());
02444 
02445         QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
02446         // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
02447         topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
02448         bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
02449         orig = moveResizeGeom;
02450         switch ( mode )
02451             { // these 4 corners ones are copied from above
02452             case PositionTopLeft:
02453                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
02454                 break;
02455             case PositionBottomRight:
02456                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
02457                 break;
02458             case PositionBottomLeft:
02459                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
02460                 break;
02461             case PositionTopRight:
02462                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02463                 break;
02464             // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
02465             // Therefore grow to the right/bottom if needed.
02466             // TODO it should probably obey gravity rather than always using right/bottom ?
02467             case PositionTop:
02468                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02469                 break;
02470             case PositionBottom:
02471                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
02472                 break;
02473             case PositionLeft:
02474                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y()));
02475                 break;
02476             case PositionRight:
02477                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
02478                 break;
02479             case PositionCenter:
02480             default:
02481                 assert( false );
02482                 break;
02483             }
02484         if( moveResizeGeom.size() != previousMoveResizeGeom.size())
02485             update = true;
02486         }
02487     else if( isMove())
02488         {
02489         assert( mode == PositionCenter );
02490         // first move, then snap, then check bounds
02491         moveResizeGeom.moveTopLeft( topleft );
02492         moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
02493         // NOTE: This is duped in checkUnrestrictedMoveResize().
02494         if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
02495             moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
02496         // no need to check top_marge, titlebar_marge already handles it
02497         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02498             moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
02499         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02500             moveResizeGeom.moveRight( desktopArea.left() + left_marge );
02501         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02502             moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
02503         if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
02504             update = true;
02505         }
02506     else
02507         assert( false );
02508 
02509     if( update )
02510         {
02511         if( rules()->checkMoveResizeMode
02512             ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
02513             {
02514             setGeometry( moveResizeGeom );
02515             positionGeometryTip();
02516             }
02517         else if( rules()->checkMoveResizeMode
02518             ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
02519             {
02520             clearbound();  // it's necessary to move the geometry tip when there's no outline
02521             positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
02522             drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
02523             }                               // so the geometry tip will be painted above the outline
02524         }
02525     if ( isMove() )
02526       workspace()->clientMoved(globalPos, qt_x_time);
02527     }
02528 
02529 
02530 } // namespace
KDE Home | KDE Accessibility Home | Description of Access Keys