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