kwin Library API Documentation

geometry.cpp

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