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