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 
00031 extern Time qt_x_time;
00032 
00033 namespace KWinInternal
00034 {
00035 
00036 //********************************************
00037 // Workspace
00038 //********************************************
00039 
00043 void Workspace::desktopResized()
00044     {
00045     updateClientArea();
00046     checkElectricBorders();
00047     }
00048 
00061 void Workspace::updateClientArea( bool force )
00062     {
00063     QRect* new_areas = new QRect[ numberOfDesktops() + 1 ];
00064     QRect all = QApplication::desktop()->geometry();
00065     for( int i = 1;
00066          i <= numberOfDesktops();
00067          ++i )
00068          new_areas[ i ] = all;
00069     for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
00070         {
00071         QRect r = (*it)->adjustedClientArea( all );
00072         if( r == all )
00073             continue;
00074         if( (*it)->isOnAllDesktops())
00075             for( int i = 1;
00076                  i <= numberOfDesktops();
00077                  ++i )
00078                 new_areas[ i ] = new_areas[ i ].intersect( r );
00079         else
00080             new_areas[ (*it)->desktop() ] = new_areas[ (*it)->desktop() ].intersect( r );
00081         }
00082     if( topmenu_space != NULL )
00083         {
00084         QRect topmenu_area = all;
00085         topmenu_area.setTop( topMenuHeight());
00086         for( int i = 1;
00087              i <= numberOfDesktops();
00088              ++i )
00089             new_areas[ i ] = new_areas[ i ].intersect( topmenu_area );
00090         }
00091 
00092     bool changed = force;
00093     for( int i = 1;
00094          !changed && i <= numberOfDesktops();
00095          ++i )
00096         if( workarea[ i ] != new_areas[ i ] )
00097             changed = true;
00098     if ( changed )
00099         {
00100         delete[] workarea;
00101         workarea = new_areas;
00102         new_areas = NULL;
00103         NETRect r;
00104         for( int i = 1; i <= numberOfDesktops(); i++)
00105             {
00106             r.pos.x = workarea[ i ].x();
00107             r.pos.y = workarea[ i ].y();
00108             r.size.width = workarea[ i ].width();
00109             r.size.height = workarea[ i ].height();
00110             rootInfo->setWorkArea( i, r );
00111             }
00112 
00113         updateTopMenuGeometry();
00114         for( ClientList::ConstIterator it = clients.begin();
00115              it != clients.end();
00116              ++it)
00117             (*it)->checkWorkspacePosition();
00118         }
00119     delete[] new_areas;
00120     }
00121 
00122 void Workspace::updateClientArea()
00123     {
00124     updateClientArea( false );
00125     }
00126 
00127 
00135 QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
00136     {
00137     if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
00138         desktop = currentDesktop();
00139     QRect rect = QApplication::desktop()->geometry();
00140     QDesktopWidget *desktopwidget = KApplication::desktop();
00141 
00142     switch (opt)
00143         {
00144         case MaximizeArea:
00145         case MaximizeFullArea:
00146             if (options->xineramaMaximizeEnabled)
00147                 rect = desktopwidget->screenGeometry(desktopwidget->screenNumber(p));
00148             break;
00149         case PlacementArea:
00150             if (options->xineramaPlacementEnabled)
00151                 rect = desktopwidget->screenGeometry(desktopwidget->screenNumber(p));
00152             break;
00153         case MovementArea:
00154             if (options->xineramaMovementEnabled)
00155                 rect = desktopwidget->screenGeometry(desktopwidget->screenNumber(p));
00156             break;
00157         case WorkArea:
00158         case FullArea:
00159             break; // nothing
00160         case ScreenArea:
00161             rect = desktopwidget->screenGeometry(desktopwidget->screenNumber(p));
00162             break;
00163         }
00164 
00165     if( workarea[ desktop ].isNull() || opt == FullArea || opt == MaximizeFullArea
00166         || opt == ScreenArea || opt == MovementArea )
00167         return rect;
00168 
00169     return workarea[ desktop ].intersect(rect);
00170     }
00171 
00172 QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
00173     {
00174     return clientArea( opt, c->geometry().center(), c->desktop());
00175     }
00176 
00182 QPoint Workspace::adjustClientPosition( Client* c, QPoint pos )
00183     {
00184    //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
00185    //CT adapted for kwin on 25Nov1999
00186    //aleXXX 02Nov2000 added second snapping mode
00187     if (options->windowSnapZone || options->borderSnapZone )
00188         {
00189         bool sOWO=options->snapOnlyWhenOverlapping;
00190         QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
00191         int xmin = maxRect.left();
00192         int xmax = maxRect.right()+1;               //desk size
00193         int ymin = maxRect.top();
00194         int ymax = maxRect.bottom()+1;
00195 
00196         int cx(pos.x());
00197         int cy(pos.y());
00198         int cw(c->width());
00199         int ch(c->height());
00200         int rx(cx+cw);
00201         int ry(cy+ch);                 //these don't change
00202 
00203         int nx(cx), ny(cy);                         //buffers
00204         int deltaX(xmax);
00205         int deltaY(ymax);   //minimum distance to other clients
00206 
00207         int lx, ly, lrx, lry; //coords and size for the comparison client, l
00208 
00209       // border snap
00210         int snap = options->borderSnapZone; //snap trigger
00211         if (snap)
00212             {
00213             if ((sOWO?(cx<xmin):true) && (QABS(xmin-cx)<snap))
00214                 {
00215                 deltaX = xmin-cx;
00216                 nx = xmin;
00217                 }
00218             if ((sOWO?(rx>xmax):true) && (QABS(rx-xmax)<snap) && (QABS(xmax-rx) < deltaX))
00219                 {
00220                 deltaX = rx-xmax;
00221                 nx = xmax - cw;
00222                 }
00223 
00224             if ((sOWO?(cy<ymin):true) && (QABS(ymin-cy)<snap))
00225                 {
00226                 deltaY = ymin-cy;
00227                 ny = ymin;
00228                 }
00229             if ((sOWO?(ry>ymax):true) && (QABS(ry-ymax)<snap) && (QABS(ymax-ry) < deltaY))
00230                 {
00231                 deltaY =ry-ymax;
00232                 ny = ymax - ch;
00233                 }
00234             }
00235 
00236       // windows snap
00237         snap = options->windowSnapZone;
00238         if (snap)
00239             {
00240             QValueList<Client *>::ConstIterator l;
00241             for (l = clients.begin();l != clients.end();++l )
00242                 {
00243                 if ((*l)->isOnDesktop(currentDesktop()) &&
00244                    !(*l)->isMinimized()
00245                     && (*l) != c )
00246                     {
00247                     lx = (*l)->x();
00248                     ly = (*l)->y();
00249                     lrx = lx + (*l)->width();
00250                     lry = ly + (*l)->height();
00251 
00252                     if ( (( cy <= lry ) && ( cy  >= ly  ))  ||
00253                          (( ry >= ly  ) && ( ry  <= lry ))  ||
00254                          (( cy <= ly  ) && ( ry >= lry  )) )
00255                         {
00256                         if ((sOWO?(cx<lrx):true) && (QABS(lrx-cx)<snap) && ( QABS(lrx -cx) < deltaX) )
00257                             {
00258                             deltaX = QABS( lrx - cx );
00259                             nx = lrx;
00260                             }
00261                         if ((sOWO?(rx>lx):true) && (QABS(rx-lx)<snap) && ( QABS( rx - lx )<deltaX) )
00262                             {
00263                             deltaX = QABS(rx - lx);
00264                             nx = lx - cw;
00265                             }
00266                         }
00267 
00268                     if ( (( cx <= lrx ) && ( cx  >= lx  ))  ||
00269                          (( rx >= lx  ) && ( rx  <= lrx ))  ||
00270                          (( cx <= lx  ) && ( rx >= lrx  )) )
00271                         {
00272                         if ((sOWO?(cy<lry):true) && (QABS(lry-cy)<snap) && (QABS( lry -cy ) < deltaY))
00273                             {
00274                             deltaY = QABS( lry - cy );
00275                             ny = lry;
00276                             }
00277                   //if ( (QABS( ry-ly ) < snap) && (QABS( ry - ly ) < deltaY ))
00278                         if ((sOWO?(ry>ly):true) && (QABS(ry-ly)<snap) && (QABS( ry - ly ) < deltaY ))
00279                             {
00280                             deltaY = QABS( ry - ly );
00281                             ny = ly - ch;
00282                             }
00283                         }
00284                     }
00285                 }
00286             }
00287         pos = QPoint(nx, ny);
00288         }
00289     return pos;
00290     }
00291 
00295 void Workspace::setClientIsMoving( Client *c )
00296     {
00297     Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
00298     // window while still moving the first one.
00299     movingClient = c;
00300     if (movingClient)
00301         ++block_focus;
00302     else
00303         --block_focus;
00304     }
00305 
00309 void Workspace::cascadeDesktop()
00310     {
00311 // TODO XINERAMA this probably is not right for xinerama
00312     Q_ASSERT( block_stacking_updates == 0 );
00313     ClientList::ConstIterator it(stackingOrder().begin());
00314     bool re_init_cascade_at_first_client = true;
00315     for (; it != stackingOrder().end(); ++it)
00316         {
00317         if((!(*it)->isOnDesktop(currentDesktop())) ||
00318            ((*it)->isMinimized())                  ||
00319            ((*it)->isOnAllDesktops())              ||
00320            (!(*it)->isMovable()) )
00321             continue;
00322         initPositioning->placeCascaded(*it, QRect(), re_init_cascade_at_first_client);
00323         //CT is an if faster than an attribution??
00324         if (re_init_cascade_at_first_client)
00325           re_init_cascade_at_first_client = false;
00326         }
00327     }
00328 
00333 void Workspace::unclutterDesktop()
00334     {
00335     ClientList::Iterator it(clients.fromLast());
00336     for (; it != clients.end(); --it)
00337         {
00338         if((!(*it)->isOnDesktop(currentDesktop())) ||
00339            ((*it)->isMinimized())                  ||
00340            ((*it)->isOnAllDesktops())              ||
00341            (!(*it)->isMovable()) )
00342             continue;
00343         initPositioning->placeSmart(*it, QRect());
00344         }
00345     }
00346 
00347 
00348 void Workspace::updateTopMenuGeometry( Client* c )
00349     {
00350     if( !managingTopMenus())
00351         return;
00352     if( c != NULL )
00353         {
00354         XEvent ev;
00355         ev.xclient.display = qt_xdisplay();
00356         ev.xclient.type = ClientMessage;
00357         ev.xclient.window = c->window();
00358         static Atom msg_type_atom = XInternAtom( qt_xdisplay(), "_KDE_TOPMENU_MINSIZE", False );
00359         ev.xclient.message_type = msg_type_atom;
00360         ev.xclient.format = 32;
00361         ev.xclient.data.l[0] = qt_x_time;
00362         ev.xclient.data.l[1] = topmenu_space->width();
00363         ev.xclient.data.l[2] = topmenu_space->height();
00364         ev.xclient.data.l[3] = 0;
00365         ev.xclient.data.l[4] = 0;
00366         XSendEvent( qt_xdisplay(), c->window(), False, NoEventMask, &ev );
00367         KWin::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
00368         c->checkWorkspacePosition();
00369         return;
00370         }
00371     // c == NULL - update all, including topmenu_space
00372     QRect area;
00373     area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ?
00374     area.setHeight( topMenuHeight());
00375     topmenu_space->setGeometry( area );
00376     for( ClientList::ConstIterator it = topmenus.begin();
00377          it != topmenus.end();
00378          ++it )
00379         updateTopMenuGeometry( *it );
00380     }
00381 
00382 //********************************************
00383 // Client
00384 //********************************************
00385 
00386 
00387 void Client::keepInArea( const QRect& area )
00388     {
00389     if ( geometry().right() > area.right() && width() < area.width() )
00390         move( area.right() - width(), y() );
00391     if ( geometry().bottom() > area.bottom() && height() < area.height() )
00392         move( x(), area.bottom() - height() );
00393     if( !area.contains( geometry().topLeft() ))
00394         {
00395         int tx = x();
00396         int ty = y();
00397         if ( tx < area.x() )
00398             tx = area.x();
00399         if ( ty < area.y() )
00400             ty = area.y();
00401         move( tx, ty );
00402         }
00403     }
00404 
00410 // TODO move to Workspace?
00411 QRect Client::adjustedClientArea( const QRect& area ) const
00412     {
00413     QRect r = area;
00414     // topmenu area is reserved in updateClientArea()
00415     if( isTopMenu())
00416         return r;
00417     NETStrut strut = info->strut();
00418     if ( strut.left > 0 )
00419         r.setLeft( r.left() + (int) strut.left );
00420     if ( strut.top > 0 )
00421         r.setTop( r.top() + (int) strut.top );
00422     if ( strut.right > 0  )
00423         r.setRight( r.right() - (int) strut.right );
00424     if ( strut.bottom > 0  )
00425         r.setBottom( r.bottom() - (int) strut.bottom );
00426     return r;
00427     }
00428 
00429 
00430 
00431 // updates differences to workarea edges for all directions
00432 void Client::updateWorkareaDiffs()
00433     {
00434     QRect area = workspace()->clientArea( WorkArea, this );
00435     QRect geom = geometry();
00436     workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
00437     workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
00438     }
00439 
00440 // If the client was inside workarea in the x direction, and if it was close to the left/right
00441 // edge, return the distance from the left/right edge (negative for left, positive for right)
00442 // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
00443 // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
00444 // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
00445 // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
00446 // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
00447 // the y direction is done the same, just the values will be rotated: top->left, bottom->right
00448 int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
00449     {
00450     int left_diff = left - a_left;
00451     int right_diff = a_right - right;
00452     if( left_diff < 0 || right_diff < 0 )
00453         return INT_MIN;
00454     else // fully inside workarea in this direction direction
00455         {
00456         // max distance from edge where it's still considered to be close and is kept at that distance
00457         int max_diff = ( a_right - a_left ) / 10;
00458         if( left_diff < right_diff )
00459             return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
00460         else if( left_diff > right_diff )
00461             return right_diff < max_diff ? right_diff + 1 : INT_MAX;
00462         return INT_MAX; // not close to workarea edge
00463         }
00464     }
00465 
00466 void Client::checkWorkspacePosition()
00467     {
00468     if( maximizeMode() != MaximizeRestore )
00469     // TODO update geom_restore?
00470         changeMaximize( false, false, true ); // adjust size
00471 
00472     if( isFullScreen())
00473         {
00474         QRect area = workspace()->clientArea( MaximizeFullArea, this );
00475         if( geometry() != area )
00476             setGeometry( area );
00477         return;
00478         }
00479     if( isDock())
00480         return;
00481     if( isOverride())
00482         return; // I wish I knew what to do here :(
00483     if( isTopMenu())
00484         {
00485         if( workspace()->managingTopMenus())
00486             {
00487             QRect area;
00488             ClientList mainclients = mainClients();
00489             if( mainclients.count() == 1 )
00490                 area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
00491             else
00492                 area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop());
00493             area.setHeight( workspace()->topMenuHeight());
00494 //            kdDebug() << "TOPMENU size adjust: " << area << ":" << this << endl;
00495             setGeometry( area );
00496             }
00497         return;
00498         }
00499 
00500     if( !isShade()) // TODO
00501         {
00502         int old_diff_x = workarea_diff_x;
00503         int old_diff_y = workarea_diff_y;
00504         updateWorkareaDiffs();
00505 
00506         // this can be true only if this window was mapped before KWin
00507         // was started - in such case, don't adjust position to workarea,
00508         // because the window already had its position, and if a window
00509         // with a strut altering the workarea would be managed in initialization
00510         // after this one, this window would be moved
00511         if( workspace()->initializing())
00512             return;
00513 
00514         QRect area = workspace()->clientArea( WorkArea, this );
00515         QRect new_geom = geometry();
00516         QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
00517         QRect tmp_area_x( area.left(), 0, area.width(), 0 );
00518         checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
00519         // the x<->y swapping
00520         QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
00521         QRect tmp_area_y( area.top(), 0, area.height(), 0 );
00522         checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
00523         new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
00524         QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
00525         if( final_geom != new_geom ) // size increments, or size restrictions
00526             { // adjusted size differing matters only for right and bottom edge
00527             if( old_diff_x != INT_MAX && old_diff_x > 0 )
00528                 final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
00529             if( old_diff_y != INT_MAX && old_diff_y > 0 )
00530                 final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
00531             }
00532         if( final_geom != geometry() )
00533             setGeometry( final_geom );
00534         //    updateWorkareaDiffs(); done already by setGeometry()
00535         }
00536     }
00537 
00538 // Try to be smart about keeping the clients visible.
00539 // If the client was fully inside the workspace before, try to keep
00540 // it still inside the workarea, possibly moving it or making it smaller if possible,
00541 // and try to keep the distance from the nearest workarea edge.
00542 // On the other hand, it it was partially moved outside of the workspace in some direction,
00543 // don't do anything with that direction if it's still at least partially visible. If it's
00544 // not visible anymore at all, make sure it's visible at least partially
00545 // again (not fully, as that could(?) be potentionally annoying) by
00546 // moving it slightly inside the workarea (those '+ 5').
00547 // Again, this is done for the x direction, y direction will be done by x<->y swapping
00548 void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area )
00549     {
00550     if( old_diff != INT_MIN ) // was inside workarea
00551         {
00552         if( old_diff == INT_MAX ) // was in workarea, but far from edge
00553             {
00554             if( new_diff == INT_MIN )  // is not anymore fully in workarea
00555                 {
00556                 rect.setLeft( area.left());
00557                 rect.setRight( area.right());
00558                 }
00559             return;
00560             }
00561         if( isResizable())
00562             {
00563             if( rect.width() > area.width())
00564                 rect.setWidth( area.width());
00565             if( rect.width() >= area.width() / 2 )
00566                 {
00567                 if( old_diff < 0 )
00568                     rect.setLeft( area.left() + ( -old_diff - 1 ) );
00569                 else // old_diff > 0
00570                     rect.setRight( area.right() - ( old_diff - 1 ));
00571                 }
00572             }
00573         if( isMovable())
00574             {
00575             if( old_diff < 0 ) // was in left third, keep distance from left edge
00576                 rect.moveLeft( area.left() + ( -old_diff - 1 ));
00577             else // old_diff > 0 // was in right third, keep distance from right edge
00578                 rect.moveRight( area.right() - ( old_diff - 1 ));
00579             }
00580         // this isResizable() block is copied from above, the difference is
00581         // the condition with 'area.width() / 2' - for windows that are not wide,
00582         // moving is preffered to resizing
00583         if( isResizable())
00584             {
00585             if( old_diff < 0 )
00586                 rect.setLeft( area.left() + ( -old_diff - 1 ) );
00587             else // old_diff > 0
00588                 rect.setRight( area.right() - ( old_diff - 1 ));
00589             }
00590         }
00591     if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
00592         { // not visible (almost) at all - try to make it at least partially visible
00593         if( isMovable())
00594             {
00595             if( rect.left() < area.left() + 5 )
00596                 rect.moveRight( area.left() + 5 );
00597             if( rect.right() > area.right() - 5 )
00598                 rect.moveLeft( area.right() - 5 );
00599             }
00600         }
00601     }
00602 
00606 QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const
00607     {
00608     // first, get the window size for the given frame size s
00609 
00610     QSize wsize( frame.width() - ( border_left + border_right ),
00611              frame.height() - ( border_top + border_bottom ));
00612 
00613     return sizeForClientSize( wsize, mode );
00614     }
00615 
00624 QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode ) const
00625     {
00626     int w = wsize.width();
00627     int h = wsize.height();
00628     if (w<1) w = 1;
00629     if (h<1) h = 1;
00630 
00631     // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
00632     // even if they're not set in flags - see getWmNormalHints()
00633     QSize min_size( xSizeHint.min_width, xSizeHint.min_height );
00634     QSize max_size( xSizeHint.max_width, xSizeHint.max_height );
00635     if( decoration != NULL )
00636         {
00637         QSize decominsize = decoration->minimumSize();
00638         QSize border_size( border_left + border_right, border_top + border_bottom );
00639         if( border_size.width() > decominsize.width()) // just in case
00640             decominsize.setWidth( border_size.width());
00641         if( border_size.height() > decominsize.height())
00642             decominsize.setHeight( border_size.height());
00643         if( decominsize.width() > min_size.width())
00644                 min_size.setWidth( decominsize.width());
00645         if( decominsize.height() > min_size.height())
00646                 min_size.setHeight( decominsize.height());
00647         }
00648     w = QMIN( max_size.width(), w );
00649     h = QMIN( max_size.height(), h );
00650     w = QMAX( min_size.width(), w );
00651     h = QMAX( min_size.height(), h );
00652 
00653     int width_inc = xSizeHint.width_inc;
00654     int height_inc = xSizeHint.height_inc;
00655     int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
00656     int baseh_inc = xSizeHint.min_height;
00657     w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
00658     h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
00659 // code for aspect ratios based on code from FVWM
00660     /*
00661      * The math looks like this:
00662      *
00663      * minAspectX    dwidth     maxAspectX
00664      * ---------- <= ------- <= ----------
00665      * minAspectY    dheight    maxAspectY
00666      *
00667      * If that is multiplied out, then the width and height are
00668      * invalid in the following situations:
00669      *
00670      * minAspectX * dheight > minAspectY * dwidth
00671      * maxAspectX * dheight < maxAspectY * dwidth
00672      *
00673      */
00674     if( xSizeHint.flags & PAspect )
00675         {
00676         double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
00677         double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
00678         double max_aspect_w = xSizeHint.max_aspect.x;
00679         double max_aspect_h = xSizeHint.max_aspect.y;
00680         w -= xSizeHint.base_width;
00681         h -= xSizeHint.base_height;
00682         int max_width = max_size.width() - xSizeHint.base_width;
00683         int min_width = min_size.width() - xSizeHint.base_width;
00684         int max_height = max_size.height() - xSizeHint.base_height;
00685         int min_height = min_size.height() - xSizeHint.base_height;
00686 #define ASPECT_CHECK_GROW_W \
00687         if( min_aspect_w * h > min_aspect_h * w ) \
00688             { \
00689             int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
00690             if( w + delta <= max_width ) \
00691                 w += delta; \
00692             }
00693 #define ASPECT_CHECK_SHRINK_H_GROW_W \
00694         if( min_aspect_w * h > min_aspect_h * w ) \
00695             { \
00696             int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
00697             if( h - delta >= min_height ) \
00698                 h -= delta; \
00699             else \
00700                 { \
00701                 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
00702                 if( w + delta <= max_width ) \
00703                     w += delta; \
00704                 } \
00705             }
00706 #define ASPECT_CHECK_GROW_H \
00707         if( max_aspect_w * h < max_aspect_h * w ) \
00708             { \
00709             int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
00710             if( h + delta <= max_height ) \
00711                 h += delta; \
00712             }
00713 #define ASPECT_CHECK_SHRINK_W_GROW_H \
00714         if( max_aspect_w * h < max_aspect_h * w ) \
00715             { \
00716             int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
00717             if( w - delta >= min_width ) \
00718                 w -= delta; \
00719             else \
00720                 { \
00721                 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
00722                 if( h + delta <= max_height ) \
00723                     h += delta; \
00724                 } \
00725             }
00726         switch( mode )
00727             {
00728             case SizemodeAny:
00729                 {
00730                 ASPECT_CHECK_SHRINK_H_GROW_W
00731                 ASPECT_CHECK_SHRINK_W_GROW_H
00732                 ASPECT_CHECK_GROW_H
00733                 ASPECT_CHECK_GROW_W
00734                 break;
00735                 }
00736             case SizemodeFixedW:
00737                 {
00738                 // the checks are order so that attempts to modify height are first
00739                 ASPECT_CHECK_GROW_H
00740                 ASPECT_CHECK_SHRINK_H_GROW_W
00741                 ASPECT_CHECK_SHRINK_W_GROW_H
00742                 ASPECT_CHECK_GROW_W
00743                 break;
00744                 }
00745             case SizemodeFixedH:
00746                 {
00747                 ASPECT_CHECK_GROW_W
00748                 ASPECT_CHECK_SHRINK_W_GROW_H
00749                 ASPECT_CHECK_SHRINK_H_GROW_W
00750                 ASPECT_CHECK_GROW_H
00751                 break;
00752                 }
00753             case SizemodeMax:
00754                 {
00755                 // first checks that try to shrink
00756                 ASPECT_CHECK_SHRINK_H_GROW_W
00757                 ASPECT_CHECK_SHRINK_W_GROW_H
00758                 ASPECT_CHECK_GROW_W
00759                 ASPECT_CHECK_GROW_H
00760                 break;
00761                 }
00762             case SizemodeShaded:
00763                 break;
00764             }
00765 #undef ASPECT_CHECK_SHRINK_H_GROW_W
00766 #undef ASPECT_CHECK_SHRINK_W_GROW_H
00767 #undef ASPECT_CHECK_GROW_W
00768 #undef ASPECT_CHECK_GROW_H
00769         w += xSizeHint.base_width;
00770         h += xSizeHint.base_height;
00771         }
00772 
00773     if ( mode == SizemodeShaded && wsize.height() == 0 )
00774         h = 0;
00775     return QSize( w + border_left + border_right, h + border_top + border_bottom );
00776     }
00777 
00781 void Client::getWmNormalHints()
00782     {
00783     long msize;
00784     if (XGetWMNormalHints(qt_xdisplay(), window(), &xSizeHint, &msize) == 0 )
00785         xSizeHint.flags = 0;
00786     // set defined values for the fields, even if they're not in flags
00787 
00788     // basesize is just like minsize, except for minsize is not used for aspect ratios
00789     // keep basesize only for aspect ratios, for size increments, keep the base
00790     // value in minsize - see ICCCM 4.1.2.3
00791     if( xSizeHint.flags & PBaseSize )
00792         {
00793         if( ! ( xSizeHint.flags & PMinSize )) // PBaseSize and PMinSize are equivalent
00794             {
00795             xSizeHint.flags |= PMinSize;
00796             xSizeHint.min_width = xSizeHint.base_width;
00797                 xSizeHint.min_height = xSizeHint.base_height;
00798             }
00799         }
00800     else
00801         xSizeHint.base_width = xSizeHint.base_height = 0;
00802     if( ! ( xSizeHint.flags & PMinSize ))
00803         xSizeHint.min_width = xSizeHint.min_height = 0;
00804     if( ! ( xSizeHint.flags & PMaxSize ))
00805         xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
00806     if( xSizeHint.flags & PResizeInc )
00807         {
00808         xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 );
00809         xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 );
00810         }
00811     else
00812         {
00813         xSizeHint.width_inc = 1;
00814         xSizeHint.height_inc = 1;
00815         }
00816     if( xSizeHint.flags & PAspect )
00817         { // no dividing by zero
00818         xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 );
00819         xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 );
00820         }
00821     else
00822         {
00823         xSizeHint.min_aspect.x = 1;
00824         xSizeHint.min_aspect.y = INT_MAX;
00825         xSizeHint.max_aspect.x = INT_MAX;
00826         xSizeHint.max_aspect.y = 1;
00827         }
00828     if( ! ( xSizeHint.flags & PWinGravity ))
00829         xSizeHint.win_gravity = NorthWestGravity;
00830     if( isManaged())
00831         { // update to match restrictions
00832         QSize new_size = adjustedSize( size());
00833         if( new_size != size() && !isShade()) // SHADE
00834             resizeWithChecks( new_size );
00835         }
00836     updateAllowedActions(); // affects isResizeable()
00837     }
00838 
00844 void Client::sendSyntheticConfigureNotify()
00845     {
00846     XConfigureEvent c;
00847     c.type = ConfigureNotify;
00848     c.send_event = True;
00849     c.event = window();
00850     c.window = window();
00851     c.x = x() + clientPos().x();
00852     c.y = y() + clientPos().y();
00853     c.width = clientSize().width();
00854     c.height = clientSize().height();
00855     c.border_width = 0;
00856     c.above = None;
00857     c.override_redirect = 0;
00858     XSendEvent( qt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c );
00859     }
00860 
00861 const QPoint Client::calculateGravitation( bool invert, int gravity ) const
00862     {
00863     int dx, dy;
00864     dx = dy = 0;
00865 
00866     if( gravity == 0 ) // default (nonsense) value for the argument
00867         gravity = xSizeHint.win_gravity;
00868 
00869 // dx, dy specify how the client window moves to make space for the frame
00870     switch (gravity)
00871         {
00872         case NorthWestGravity: // move down right
00873         default:
00874             dx = border_left;
00875             dy = border_top;
00876             break;
00877         case NorthGravity: // move right
00878             dx = 0;
00879             dy = border_top;
00880             break;
00881         case NorthEastGravity: // move down left
00882             dx = -border_right;
00883             dy = border_top;
00884             break;
00885         case WestGravity: // move right
00886             dx = border_left;
00887             dy = 0;
00888             break;
00889         case CenterGravity:
00890             break; // will be handled specially
00891         case StaticGravity: // don't move
00892             dx = 0;
00893             dy = 0;
00894             break;
00895         case EastGravity: // move left
00896             dx = -border_right;
00897             dy = 0;
00898             break;
00899         case SouthWestGravity: // move up right
00900             dx = border_left ;
00901             dy = -border_bottom;
00902             break;
00903         case SouthGravity: // move up
00904             dx = 0;
00905             dy = -border_bottom;
00906             break;
00907         case SouthEastGravity: // move up left
00908             dx = -border_right;
00909             dy = -border_bottom;
00910             break;
00911         }
00912     if( gravity != CenterGravity )
00913         { // translate from client movement to frame movement
00914         dx -= border_left;
00915         dy -= border_top;
00916         }
00917     else
00918         { // center of the frame will be at the same position client center without frame would be
00919         dx = - ( border_left + border_right ) / 2;
00920         dy = - ( border_top + border_bottom ) / 2;
00921         }
00922     if( !invert )
00923         return QPoint( x() + dx, y() + dy );
00924     else
00925         return QPoint( x() - dx, y() - dy );
00926     }
00927 
00928 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity )
00929     {
00930     if( gravity == 0 ) // default (nonsense) value for the argument
00931         gravity = xSizeHint.win_gravity;
00932     if( value_mask & ( CWX | CWY ))
00933         {
00934         QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
00935         if ( value_mask & CWX )
00936             new_pos.setX( rx );
00937         if ( value_mask & CWY )
00938             new_pos.setY( ry );
00939 
00940         // clever(??) workaround for applications like xv that want to set
00941         // the location to the current location but miscalculate the
00942         // frame size due to kwin being a double-reparenting window
00943         // manager
00944         if ( new_pos.x() == x() + clientPos().x() &&
00945              new_pos.y() == y() + clientPos().y() && gravity == NorthWestGravity )
00946             {
00947             new_pos.setX( x());
00948             new_pos.setY( y());
00949             }
00950 
00951         int nw = clientSize().width();
00952         int nh = clientSize().height();
00953         if ( value_mask & CWWidth )
00954             nw = rw;
00955         if ( value_mask & CWHeight )
00956             nh = rh;
00957         QSize ns = sizeForClientSize( QSize( nw, nh ) );
00958 
00959         // TODO what to do with maximized windows?
00960         if ( maximizeMode() != MaximizeFull
00961             || ns != size())
00962             {
00963             resetMaximize();
00964             ++block_geometry;
00965             move( new_pos );
00966             plainResize( ns ); // TODO must(?) resize before gravitating?
00967             --block_geometry;
00968             setGeometry( QRect( calculateGravitation( false, gravity ), size()), ForceGeometrySet );
00969             }
00970         }
00971 
00972     if ( value_mask & (CWWidth | CWHeight )
00973         && ! ( value_mask & ( CWX | CWY )) )  // pure resize
00974         {
00975         if ( isShade()) // SELI SHADE
00976             setShade( ShadeNone );
00977 
00978         int nw = clientSize().width();
00979         int nh = clientSize().height();
00980         if ( value_mask & CWWidth )
00981             nw = rw;
00982         if ( value_mask & CWHeight )
00983             nh = rh;
00984         QSize ns = sizeForClientSize( QSize( nw, nh ) );
00985 
00986         if( ns != size())  // don't restore if some app sets its own size again
00987             {
00988             resetMaximize();
00989             int save_gravity = xSizeHint.win_gravity;
00990             xSizeHint.win_gravity = gravity;
00991             resizeWithChecks( ns );
00992             xSizeHint.win_gravity = save_gravity;
00993             }
00994         }
00995     // No need to send synthetic configure notify event here, either it's sent together
00996     // with geometry change, or there's no need to send it.
00997     // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
00998     }
00999 
01000 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
01001     {
01002     int newx = x();
01003     int newy = y();
01004     QRect area = workspace()->clientArea( WorkArea, this );
01005     // don't allow growing larger than workarea
01006     if( w > area.width())
01007         w = area.width();
01008     if( h > area.height())
01009         h = area.height();
01010     QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size
01011     w = tmp.width();
01012     h = tmp.height();
01013     switch( xSizeHint.win_gravity )
01014         {
01015         case NorthWestGravity: // top left corner doesn't move
01016         default:
01017             break;
01018         case NorthGravity: // middle of top border doesn't move
01019             newx = ( newx + width() / 2 ) - ( w / 2 );
01020             break;
01021         case NorthEastGravity: // top right corner doesn't move
01022             newx = newx + width() - w;
01023             break;
01024         case WestGravity: // middle of left border doesn't move
01025             newy = ( newy + height() / 2 ) - ( h / 2 );
01026             break;
01027         case CenterGravity: // middle point doesn't move
01028             newx = ( newx + width() / 2 ) - ( w / 2 );
01029             newy = ( newy + height() / 2 ) - ( h / 2 );
01030             break;
01031         case StaticGravity: // top left corner of _client_ window doesn't move
01032             // since decoration doesn't change, equal to NorthWestGravity
01033             break;
01034         case EastGravity: // // middle of right border doesn't move
01035             newx = newx + width() - w;
01036             newy = ( newy + height() / 2 ) - ( h / 2 );
01037             break;
01038         case SouthWestGravity: // bottom left corner doesn't move
01039             newy = newy + height() - h;
01040             break;
01041         case SouthGravity: // middle of bottom border doesn't move
01042             newx = ( newx + width() / 2 ) - ( w / 2 );
01043             newy = newy + height() - h;
01044             break;
01045         case SouthEastGravity: // bottom right corner doesn't move
01046             newx = newx + width() - w;
01047             newy = newy + height() - h;
01048             break;
01049         }
01050     // if it would be moved outside of workarea, keep it inside,
01051     // see also Client::computeWorkareaDiff()
01052     if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
01053         {
01054         if( newx < area.left())
01055             newx = area.left();
01056         if( newx + w > area.right() + 1 )
01057             newx = area.right() + 1 - w;
01058         assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
01059         }
01060     if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
01061         {
01062         if( newy < area.top())
01063             newy = area.top();
01064         if( newy + h > area.bottom() + 1 )
01065             newy = area.bottom() + 1 - h;
01066         assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
01067         }
01068     setGeometry( newx, newy, w, h, force );
01069     }
01070 
01071 // _NET_MOVERESIZE_WINDOW
01072 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
01073     {
01074     int gravity = flags & 0xff;
01075     int value_mask = 0;
01076     if( flags & ( 1 << 8 ))
01077         value_mask |= CWX;
01078     if( flags & ( 1 << 9 ))
01079         value_mask |= CWY;
01080     if( flags & ( 1 << 10 ))
01081         value_mask |= CWWidth;
01082     if( flags & ( 1 << 11 ))
01083         value_mask |= CWHeight;
01084     configureRequest( value_mask, x, y, width, height, gravity );
01085     }
01086 
01090 bool Client::isResizable() const
01091     {
01092     if ( !isMovable() || !motif_may_resize || isSplash())
01093         return FALSE;
01094 
01095     if ( ( xSizeHint.flags & PMaxSize) == 0 || (xSizeHint.flags & PMinSize ) == 0 )
01096         return TRUE;
01097     return ( xSizeHint.min_width < xSizeHint.max_width  ) ||
01098           ( xSizeHint.min_height < xSizeHint.max_height  );
01099     }
01100 
01101 /*
01102   Returns whether the window is maximizable or not
01103  */
01104 bool Client::isMaximizable() const
01105     {
01106     if ( maximizeMode() != MaximizeRestore )
01107         return TRUE;
01108     if( !isResizable() || isToolbar()) // SELI isToolbar() ?
01109         return false;
01110     if( xSizeHint.max_height < 32767 || xSizeHint.max_width < 32767 ) // sizes are 16bit with X
01111         return false;
01112     return true;
01113     }
01114 
01115 
01119 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
01120     {
01121     if( force == NormalGeometrySet && frame_geometry == QRect( x, y, w, h ))
01122         return;
01123     frame_geometry = QRect( x, y, w, h );
01124     if( !isShade())
01125         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01126     else
01127         {
01128         // check that the frame is not resized to full size when it should be shaded
01129         if( !shade_geometry_change && h != border_top + border_bottom )
01130             {
01131             kdDebug() << "h:" << h << ":t:" << border_top << ":b:" << border_bottom << endl;
01132             assert( false );
01133             }
01134         client_size = QSize( w - border_left - border_right, client_size.height());
01135         }
01136     updateWorkareaDiffs();
01137     if( block_geometry == 0 )
01138         {
01139         XMoveResizeWindow( qt_xdisplay(), frameId(), x, y, w, h );
01140         resizeDecoration( QSize( w, h ));
01141         if( !isShade())
01142             {
01143             QSize cs = clientSize();
01144             XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
01145                 cs.width(), cs.height());
01146             // FRAME tady poradi tak, at neni flicker
01147             XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
01148             }
01149         if( shape())
01150             updateShape();
01151         // SELI TODO won't this be too expensive?
01152         updateWorkareaDiffs();
01153         sendSyntheticConfigureNotify(); // TODO optimize this?
01154         }
01155     }
01156 
01157 void Client::plainResize( int w, int h, ForceGeometry_t force )
01158     { // TODO make this deffered with isResize() ? old kwin did
01159     if( force == NormalGeometrySet && frame_geometry.size() == QSize( w, h ))
01160         return;
01161     frame_geometry.setSize( QSize( w, h ));
01162     if( !isShade())
01163         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01164     else
01165         {
01166         // check that the frame is not resized to full size when it should be shaded
01167         if( !shade_geometry_change && h != border_top + border_bottom )
01168             {
01169             kdDebug() << "h:" << h << ":t:" << border_top << ":b:" << border_bottom << endl;
01170             assert( false );
01171             }
01172         client_size = QSize( w - border_left - border_right, client_size.height());
01173         }
01174     updateWorkareaDiffs();
01175     if( block_geometry == 0 )
01176         {
01177         // FRAME tady poradi tak, at neni flicker
01178         XResizeWindow( qt_xdisplay(), frameId(), w, h );
01179         resizeDecoration( QSize( w, h ));
01180         if( !isShade())
01181             {
01182             QSize cs = clientSize();
01183             XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
01184                 cs.width(), cs.height());
01185             XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
01186             }
01187         if( shape())
01188             updateShape();
01189         updateWorkareaDiffs();
01190         sendSyntheticConfigureNotify();
01191         }
01192     }
01193 
01197 void Client::move( int x, int y, ForceGeometry_t force )
01198     {
01199     if( force == NormalGeometrySet && frame_geometry.topLeft() == QPoint( x, y ))
01200         return;
01201     frame_geometry.moveTopLeft( QPoint( x, y ));
01202     updateWorkareaDiffs();
01203     if( block_geometry == 0 )
01204         {
01205         XMoveWindow( qt_xdisplay(), frameId(), x, y );
01206         sendSyntheticConfigureNotify();
01207         }
01208     }
01209 
01210 
01211 void Client::maximize( MaximizeMode m )
01212     {
01213     setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
01214     }
01215 
01219 void Client::setMaximize( bool vertically, bool horizontally )
01220     {   // changeMaximize() flips the state, so change from set->flip
01221     changeMaximize(
01222         max_mode & MaximizeVertical ? !vertically : vertically,
01223         max_mode & MaximizeHorizontal ? !horizontally : horizontally,
01224         false );
01225     }
01226 
01227 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
01228     {
01229     if( !isMaximizable())
01230         return;
01231 
01232     ++block_geometry; // TODO GeometryBlocker class?
01233 
01234     if( isShade()) // SELI SHADE
01235         setShade( ShadeNone );
01236 
01237     MaximizeMode old_mode = max_mode;
01238     // 'adjust == true' means to update the size only, e.g. after changing workspace size
01239     if( !adjust )
01240         {
01241         if( vertical )
01242             max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
01243         if( horizontal )
01244             max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
01245         }
01246 
01247     // maximing one way and unmaximizing the other way shouldn't happen
01248     Q_ASSERT( !( vertical && horizontal )
01249         || (( max_mode & MaximizeVertical != 0 ) == ( max_mode & MaximizeHorizontal != 0 )));
01250 
01251     // save sizes for restoring, if maximalizing
01252     bool maximalizing = false;
01253     if( vertical && !(old_mode & MaximizeVertical ))
01254         {
01255         geom_restore.setTop( y());
01256         geom_restore.setHeight( height());
01257         maximalizing = true;
01258         }
01259     if( horizontal && !( old_mode & MaximizeHorizontal ))
01260         {
01261         geom_restore.setLeft( x());
01262         geom_restore.setWidth( width());
01263         maximalizing = true;
01264         }
01265 
01266     if( !adjust )
01267         {
01268         if( maximalizing )
01269                 Notify::raise( Notify::Maximize );
01270         else
01271             Notify::raise( Notify::UnMaximize );
01272         }
01273 
01274     if( decoration != NULL ) // decorations may turn off some borders when maximized
01275         decoration->borders( border_left, border_right, border_top, border_bottom );
01276 
01277     QRect clientArea = workspace()->clientArea( MaximizeArea, this );
01278 
01279     switch (max_mode)
01280         {
01281 
01282         case MaximizeVertical:
01283             {
01284             if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
01285                 {
01286                 if( geom_restore.width() == 0 )
01287                     { // needs placement
01288                     plainResize( adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH ));
01289                     workspace()->placeSmart( this, clientArea );
01290                     }
01291                 else
01292                     setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()),
01293                               adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )));
01294                 }
01295             else
01296                 setGeometry( QRect(QPoint(x(), clientArea.top()),
01297                               adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )));
01298             info->setState( NET::MaxVert, NET::Max );
01299             break;
01300             }
01301 
01302         case MaximizeHorizontal:
01303             {
01304             if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
01305                 {
01306                 if( geom_restore.height() == 0 )
01307                     { // needs placement
01308                     plainResize( adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW ));
01309                     workspace()->placeSmart( this, clientArea );
01310                     }
01311                 else
01312                     setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()),
01313                               adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )));
01314                 }
01315             else
01316                 setGeometry( QRect( QPoint(clientArea.left(), y()),
01317                               adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )));
01318             info->setState( NET::MaxHoriz, NET::Max );
01319             break;
01320             }
01321 
01322         case MaximizeRestore:
01323             {
01324             QRect restore = geometry();
01325     // when only partially maximized, geom_restore may not have the other dimension remembered
01326             if( old_mode & MaximizeVertical )
01327                 {
01328                 restore.setTop( geom_restore.top());
01329                 restore.setBottom( geom_restore.bottom());
01330                 }
01331             if( old_mode & MaximizeHorizontal )
01332                 {
01333                 restore.setLeft( geom_restore.left());
01334                 restore.setRight( geom_restore.right());
01335                 }
01336             if( !restore.isValid())
01337                 {
01338                 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 );
01339                 if( geom_restore.width() > 0 )
01340                     s.setWidth( geom_restore.width());
01341                 if( geom_restore.height() > 0 )
01342                     s.setHeight( geom_restore.height());
01343                 plainResize( adjustedSize( s ));
01344                 workspace()->placeSmart( this, clientArea );
01345                 restore = geometry();
01346                 if( geom_restore.width() > 0 )
01347                     restore.moveLeft( geom_restore.x());
01348                 if( geom_restore.height() > 0 )
01349                     restore.moveTop( geom_restore.y());
01350                 }
01351             setGeometry( restore );
01352             info->setState( 0, NET::Max );
01353             break;
01354             }
01355 
01356         case MaximizeFull:
01357             {
01358             QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
01359             QRect r = QRect(clientArea.topLeft(), adjSize);
01360             setGeometry( r );
01361             info->setState( NET::Max, NET::Max );
01362             break;
01363             }
01364         default:
01365             break;
01366         }
01367 
01368     --block_geometry;
01369     setGeometry( geometry(), ForceGeometrySet );
01370 
01371     updateAllowedActions();
01372     if( decoration != NULL )
01373         decoration->maximizeChange();
01374     }
01375 
01376 void Client::resetMaximize()
01377     {
01378     if( max_mode == MaximizeRestore )
01379         return;
01380     max_mode = MaximizeRestore;
01381     Notify::raise( Notify::UnMaximize );
01382     info->setState( 0, NET::Max );
01383     updateAllowedActions();
01384     if( decoration != NULL )
01385         decoration->borders( border_left, border_right, border_top, border_bottom );
01386     setGeometry( geometry(), ForceGeometrySet );
01387     if( decoration != NULL )
01388         decoration->maximizeChange();
01389     }
01390 
01391 bool Client::isFullScreenable( bool fullscreen_hack ) const
01392     {
01393     if( fullscreen_hack )
01394         return isNormalWindow() || isOverride();
01395     else // don't check size constrains - some apps request fullscreen despite requesting fixed size
01396         return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
01397     }
01398 
01399 bool Client::userCanSetFullScreen() const
01400     {
01401     return isNormalWindow() && fullscreen_mode != FullScreenHack
01402         && ( isMaximizable() || isFullScreen()); // isMaximizable() is false for isFullScreen()
01403     }
01404 
01405 void Client::setFullScreen( bool set, bool user )
01406     {
01407     if( !isFullScreen() && !set )
01408         return;
01409     if( fullscreen_mode == FullScreenHack )
01410         return;
01411     if( user && !userCanSetFullScreen())
01412         return;
01413     setShade( ShadeNone );
01414     bool was_fs = isFullScreen();
01415     if( !was_fs )
01416         geom_fs_restore = geometry();
01417     fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
01418     if( was_fs == isFullScreen())
01419         return;
01420     StackingUpdatesBlocker blocker( workspace());
01421     workspace()->updateClientLayer( this ); // active fullscreens get different layer
01422     info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
01423     updateDecoration( false, false );
01424     if( isFullScreen())
01425         setGeometry( workspace()->clientArea( MaximizeFullArea, this ));
01426     else
01427         {
01428         if( maximizeMode() != MaximizeRestore )
01429             changeMaximize( false, false, true ); // adjust size
01430         else if( !geom_fs_restore.isNull())
01431             setGeometry( geom_fs_restore );
01432         // TODO isShaded() ?
01433         else
01434             { // does this ever happen?
01435             setGeometry( workspace()->clientArea( MaximizeArea, this ));
01436             }
01437         }
01438     }
01439 
01440 
01441 static QRect*       visible_bound  = 0;
01442 static GeometryTip* geometryTip    = 0;
01443 
01444 void Client::drawbound( const QRect& geom )
01445     {
01446     assert( visible_bound == NULL );
01447     visible_bound = new QRect( geom );
01448     doDrawbound( *visible_bound, false );
01449     }
01450 
01451 void Client::clearbound()
01452     {
01453     if( visible_bound == NULL )
01454         return;
01455     doDrawbound( *visible_bound, true );
01456     delete visible_bound;
01457     visible_bound = 0;
01458     }
01459 
01460 void Client::doDrawbound( const QRect& geom, bool clear )
01461     {
01462     if( decoration != NULL && decoration->drawbound( geom, clear ))
01463         return; // done by decoration
01464     QPainter p ( workspace()->desktopWidget() );
01465     p.setPen( QPen( Qt::white, 5 ) );
01466     p.setRasterOp( Qt::XorROP );
01467     p.drawRect( geom );
01468     }
01469 
01470 void Client::positionGeometryTip()
01471     {
01472     assert( isMove() || isResize());
01473     // Position and Size display
01474     if (options->showGeometryTip())
01475         {
01476         if( !geometryTip )
01477             { // save under is not necessary with opaque, and seem to make things slower
01478             bool save_under = ( isMove() && options->moveMode != Options::Opaque )
01479                         || ( isResize() && options->resizeMode != Options::Opaque );
01480             geometryTip = new GeometryTip( &xSizeHint, save_under );
01481             }
01482         QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
01483         wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
01484         wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
01485         if( isShade())
01486             wgeom.setHeight( 0 );
01487         geometryTip->setGeometry( wgeom );
01488         if( !geometryTip->isVisible())
01489             {
01490             geometryTip->show();
01491             geometryTip->raise();
01492             }
01493         }
01494     }
01495 
01496 class EatAllPaintEvents
01497     : public QObject
01498     {
01499     protected:
01500         virtual bool eventFilter( QObject* o, QEvent* e )
01501             { return e->type() == QEvent::Paint && o != geometryTip; }
01502     };
01503 
01504 static EatAllPaintEvents* eater = 0;
01505 
01506 bool Client::startMoveResize()
01507     {
01508     assert( !moveResizeMode );
01509     assert( QWidget::keyboardGrabber() == NULL );
01510     assert( QWidget::mouseGrabber() == NULL );
01511     if( QApplication::activePopupWidget() != NULL )
01512         return false; // popups have grab
01513     bool has_grab = false;
01514     // This reportedly improves smoothness of the moveresize operation,
01515     // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
01516     // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
01517     XSetWindowAttributes attrs;
01518     QRect r = workspace()->clientArea( FullArea, this );
01519     move_resize_grab_window = XCreateWindow( qt_xdisplay(), workspace()->rootWin(), r.x(), r.y(),
01520         r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
01521     XMapRaised( qt_xdisplay(), move_resize_grab_window );
01522     if( XGrabPointer( qt_xdisplay(), move_resize_grab_window, False,
01523         ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
01524         GrabModeAsync, GrabModeAsync, None, cursor.handle(), qt_x_time ) == Success )
01525         has_grab = true;
01526     if( XGrabKeyboard( qt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, qt_x_time ) == Success )
01527         has_grab = true;
01528     if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
01529         {
01530         XDestroyWindow( qt_xdisplay(), move_resize_grab_window );
01531         move_resize_grab_window = None;
01532         return false;
01533         }
01534     if ( maximizeMode() != MaximizeRestore )
01535         resetMaximize();
01536     moveResizeMode = true;
01537     workspace()->setClientIsMoving(this);
01538     initialMoveResizeGeom = moveResizeGeom = geometry();
01539     checkUnrestrictedMoveResize();
01540     if ( ( isMove() && options->moveMode != Options::Opaque )
01541       || ( isResize() && options->resizeMode != Options::Opaque ) )
01542         {
01543         grabXServer();
01544         kapp->sendPostedEvents();
01545         // we have server grab -> nothing should cause paint events
01546         // unfortunately, that's not completely true, Qt may generate
01547         // paint events on some widgets due to FocusIn(?)
01548         // eat them, otherwise XOR painting will be broken (#58054)
01549         // paint events for the geometrytip need to be allowed, though
01550         eater = new EatAllPaintEvents;
01551         kapp->installEventFilter( eater );
01552         }
01553     Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
01554     return true;
01555     }
01556 
01557 void Client::finishMoveResize( bool cancel )
01558     {
01559     leaveMoveResize();
01560     if( cancel )
01561         setGeometry( initialMoveResizeGeom );
01562     else
01563         setGeometry( moveResizeGeom );
01564 // FRAME    update();
01565     Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
01566     }
01567 
01568 void Client::leaveMoveResize()
01569     {
01570     clearbound();
01571     if (geometryTip)
01572         {
01573         geometryTip->hide();
01574         delete geometryTip;
01575         geometryTip = NULL;
01576         }
01577     if ( ( isMove() && options->moveMode != Options::Opaque )
01578       || ( isResize() && options->resizeMode != Options::Opaque ) )
01579         ungrabXServer();
01580     XUngrabKeyboard( qt_xdisplay(), qt_x_time );
01581     XUngrabPointer( qt_xdisplay(), qt_x_time );
01582     XDestroyWindow( qt_xdisplay(), move_resize_grab_window );
01583     move_resize_grab_window = None;
01584     workspace()->setClientIsMoving(0);
01585     if( move_faked_activity )
01586         workspace()->unfakeActivity( this );
01587     move_faked_activity = false;
01588     moveResizeMode = false;
01589     delete eater;
01590     eater = 0;
01591     }
01592 
01593 // This function checks if it actually makes sense to perform a restricted move/resize.
01594 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
01595 // a restricted move resize, because then e.g. resize would also move the window (#74555).
01596 // NOTE: Most of it is duplicated from handleMoveResize().
01597 void Client::checkUnrestrictedMoveResize()
01598     {
01599     if( unrestrictedMoveResize )
01600         return;
01601     QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
01602     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
01603     // restricted move/resize - keep at least part of the titlebar always visible 
01604     // how much must remain visible when moved away in that direction
01605     left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
01606     right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
01607     // width/height change with opaque resizing, use the initial ones
01608     titlebar_marge = initialMoveResizeGeom.height();
01609     top_marge = border_bottom;
01610     bottom_marge = border_top;
01611     if( isResize())
01612         {
01613         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
01614             unrestrictedMoveResize = true;
01615         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
01616             unrestrictedMoveResize = true;
01617         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
01618             unrestrictedMoveResize = true;
01619         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
01620             unrestrictedMoveResize = true;
01621         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
01622             unrestrictedMoveResize = true;
01623         }
01624     if( isMove())
01625         {
01626         if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
01627             unrestrictedMoveResize = true;
01628         // no need to check top_marge, titlebar_marge already handles it
01629         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
01630             unrestrictedMoveResize = true;
01631         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
01632             unrestrictedMoveResize = true;
01633         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
01634             unrestrictedMoveResize = true;
01635         }
01636     }
01637 
01638 void Client::handleMoveResize( int x, int y, int x_root, int y_root )
01639     {
01640     if(( mode == PositionCenter && !isMovable())
01641         || ( mode != PositionCenter && ( isShade() || !isResizable())))
01642         return;
01643 
01644     if ( !moveResizeMode )
01645         {
01646         QPoint p( QPoint( x, y ) - moveOffset );
01647         if (p.manhattanLength() >= 6)
01648             {
01649             if( !startMoveResize())
01650                 {
01651                 buttonDown = false;
01652                 setCursor( mode );
01653                 return;
01654                 }
01655             }
01656         else
01657             return;
01658         }
01659 
01660     // ShadeHover or ShadeActive, ShadeNormal was already avoided above
01661     if ( mode != PositionCenter && shade_mode != ShadeNone ) // SHADE
01662         setShade( ShadeNone );
01663 
01664     QPoint globalPos( x_root, y_root );
01665     // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
01666     // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
01667     QPoint topleft = globalPos - moveOffset;
01668     QPoint bottomright = globalPos + invertedMoveOffset;
01669     QRect previousMoveResizeGeom = moveResizeGeom;
01670 
01671     // TODO move whole group when moving its leader or when the leader is not mapped?
01672 
01673     // compute bounds
01674     // NOTE: This is duped in checkUnrestrictedMoveResize().
01675     QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
01676     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
01677     if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
01678         left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
01679     else // restricted move/resize - keep at least part of the titlebar always visible 
01680         {        
01681         // how much must remain visible when moved away in that direction
01682         left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
01683         right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
01684         // width/height change with opaque resizing, use the initial ones
01685         titlebar_marge = initialMoveResizeGeom.height();
01686         top_marge = border_bottom;
01687         bottom_marge = border_top;
01688         }
01689 
01690     bool update = false;
01691     if( isResize())
01692         {
01693         // first resize (without checking constrains), then snap, then check bounds, then check constrains
01694         QRect orig = initialMoveResizeGeom;
01695         Sizemode sizemode = SizemodeAny;
01696         switch ( mode )
01697             {
01698             case PositionTopLeft:
01699                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
01700                 break;
01701             case PositionBottomRight:
01702                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
01703                 break;
01704             case PositionBottomLeft:
01705                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
01706                 break;
01707             case PositionTopRight:
01708                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
01709                 break;
01710             case PositionTop:
01711                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
01712                 sizemode = SizemodeFixedH; // try not to affect height
01713                 break;
01714             case PositionBottom:
01715                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ;
01716                 sizemode = SizemodeFixedH;
01717                 break;
01718             case PositionLeft:
01719                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
01720                 sizemode = SizemodeFixedW;
01721                 break;
01722             case PositionRight:
01723                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ;
01724                 sizemode = SizemodeFixedW;
01725                 break;
01726             case PositionCenter:
01727             default:
01728                 assert( false );
01729                 break;
01730             }
01731         // TODO snap
01732         // NOTE: This is duped in checkUnrestrictedMoveResize().
01733         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
01734             moveResizeGeom.setBottom( desktopArea.top() + top_marge );
01735         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
01736             moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
01737         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
01738             moveResizeGeom.setRight( desktopArea.left() + left_marge );
01739         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
01740             moveResizeGeom.setLeft(desktopArea.right() - right_marge );
01741         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
01742             moveResizeGeom.setTop( desktopArea.top());
01743 
01744         QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
01745         // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
01746         topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
01747         bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
01748         orig = moveResizeGeom;
01749         switch ( mode )
01750             { // these 4 corners ones are copied from above
01751             case PositionTopLeft:
01752                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
01753                 break;
01754             case PositionBottomRight:
01755                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
01756                 break;
01757             case PositionBottomLeft:
01758                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
01759                 break;
01760             case PositionTopRight:
01761                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
01762                 break;
01763             // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
01764             // Therefore grow to the right/bottom if needed.
01765             // TODO it should probably obey gravity rather than always using right/bottom ?
01766             case PositionTop:
01767                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
01768                 break;
01769             case PositionBottom:
01770                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
01771                 break;
01772             case PositionLeft:
01773                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y()));
01774                 break;
01775             case PositionRight:
01776                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
01777                 break;
01778             case PositionCenter:
01779             default:
01780                 assert( false );
01781                 break;
01782             }
01783         if( moveResizeGeom.size() != previousMoveResizeGeom.size())
01784             update = true;
01785         }
01786     else if( isMove())
01787         {
01788         assert( mode == PositionCenter );
01789         // first move, then snap, then check bounds
01790         moveResizeGeom.moveTopLeft( topleft );
01791         moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
01792         // NOTE: This is duped in checkUnrestrictedMoveResize().
01793         if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
01794             moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
01795         // no need to check top_marge, titlebar_marge already handles it
01796         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
01797             moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
01798         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
01799             moveResizeGeom.moveRight( desktopArea.left() + left_marge );
01800         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
01801             moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
01802         if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
01803             update = true;
01804         }
01805     else
01806         assert( false );
01807 
01808     if( update )
01809         {
01810         if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
01811             {
01812             setGeometry( moveResizeGeom );
01813             positionGeometryTip();
01814             }
01815         else if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
01816             {
01817             clearbound();  // it's necessary to move the geometry tip when there's no outline
01818             positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
01819             drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
01820             }                               // so the geometry tip will be painted above the outline
01821         }
01822     if ( isMove() )
01823       workspace()->clientMoved(globalPos, qt_x_time);
01824     }
01825 
01826 
01827 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 16 14:44:21 2004 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003