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