• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

geometry.cpp

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

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal