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