• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • @topname@
  • Sitemap
  • Contact Us
 

kwin

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

kwin

Skip menu "kwin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

@topname@

Skip menu "@topname@"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for @topname@ by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal