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