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 else 00807 { 00808 xSizeHint.max_width = QMAX( xSizeHint.max_width, 1 ); 00809 xSizeHint.max_height = QMAX( xSizeHint.max_height, 1 ); 00810 } 00811 if( xSizeHint.flags & PResizeInc ) 00812 { 00813 xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 ); 00814 xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 ); 00815 } 00816 else 00817 { 00818 xSizeHint.width_inc = 1; 00819 xSizeHint.height_inc = 1; 00820 } 00821 if( xSizeHint.flags & PAspect ) 00822 { // no dividing by zero 00823 xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 ); 00824 xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 ); 00825 } 00826 else 00827 { 00828 xSizeHint.min_aspect.x = 1; 00829 xSizeHint.min_aspect.y = INT_MAX; 00830 xSizeHint.max_aspect.x = INT_MAX; 00831 xSizeHint.max_aspect.y = 1; 00832 } 00833 if( ! ( xSizeHint.flags & PWinGravity )) 00834 xSizeHint.win_gravity = NorthWestGravity; 00835 if( isManaged()) 00836 { // update to match restrictions 00837 QSize new_size = adjustedSize( size()); 00838 if( new_size != size() && !isShade()) // SHADE 00839 resizeWithChecks( new_size ); 00840 } 00841 updateAllowedActions(); // affects isResizeable() 00842 } 00843 00849 void Client::sendSyntheticConfigureNotify() 00850 { 00851 XConfigureEvent c; 00852 c.type = ConfigureNotify; 00853 c.send_event = True; 00854 c.event = window(); 00855 c.window = window(); 00856 c.x = x() + clientPos().x(); 00857 c.y = y() + clientPos().y(); 00858 c.width = clientSize().width(); 00859 c.height = clientSize().height(); 00860 c.border_width = 0; 00861 c.above = None; 00862 c.override_redirect = 0; 00863 XSendEvent( qt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c ); 00864 } 00865 00866 const QPoint Client::calculateGravitation( bool invert, int gravity ) const 00867 { 00868 int dx, dy; 00869 dx = dy = 0; 00870 00871 if( gravity == 0 ) // default (nonsense) value for the argument 00872 gravity = xSizeHint.win_gravity; 00873 00874 // dx, dy specify how the client window moves to make space for the frame 00875 switch (gravity) 00876 { 00877 case NorthWestGravity: // move down right 00878 default: 00879 dx = border_left; 00880 dy = border_top; 00881 break; 00882 case NorthGravity: // move right 00883 dx = 0; 00884 dy = border_top; 00885 break; 00886 case NorthEastGravity: // move down left 00887 dx = -border_right; 00888 dy = border_top; 00889 break; 00890 case WestGravity: // move right 00891 dx = border_left; 00892 dy = 0; 00893 break; 00894 case CenterGravity: 00895 break; // will be handled specially 00896 case StaticGravity: // don't move 00897 dx = 0; 00898 dy = 0; 00899 break; 00900 case EastGravity: // move left 00901 dx = -border_right; 00902 dy = 0; 00903 break; 00904 case SouthWestGravity: // move up right 00905 dx = border_left ; 00906 dy = -border_bottom; 00907 break; 00908 case SouthGravity: // move up 00909 dx = 0; 00910 dy = -border_bottom; 00911 break; 00912 case SouthEastGravity: // move up left 00913 dx = -border_right; 00914 dy = -border_bottom; 00915 break; 00916 } 00917 if( gravity != CenterGravity ) 00918 { // translate from client movement to frame movement 00919 dx -= border_left; 00920 dy -= border_top; 00921 } 00922 else 00923 { // center of the frame will be at the same position client center without frame would be 00924 dx = - ( border_left + border_right ) / 2; 00925 dy = - ( border_top + border_bottom ) / 2; 00926 } 00927 if( !invert ) 00928 return QPoint( x() + dx, y() + dy ); 00929 else 00930 return QPoint( x() - dx, y() - dy ); 00931 } 00932 00933 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity ) 00934 { 00935 if( gravity == 0 ) // default (nonsense) value for the argument 00936 gravity = xSizeHint.win_gravity; 00937 if( value_mask & ( CWX | CWY )) 00938 { 00939 QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation 00940 if ( value_mask & CWX ) 00941 new_pos.setX( rx ); 00942 if ( value_mask & CWY ) 00943 new_pos.setY( ry ); 00944 00945 // clever(??) workaround for applications like xv that want to set 00946 // the location to the current location but miscalculate the 00947 // frame size due to kwin being a double-reparenting window 00948 // manager 00949 if ( new_pos.x() == x() + clientPos().x() && 00950 new_pos.y() == y() + clientPos().y() && gravity == NorthWestGravity ) 00951 { 00952 new_pos.setX( x()); 00953 new_pos.setY( y()); 00954 } 00955 00956 int nw = clientSize().width(); 00957 int nh = clientSize().height(); 00958 if ( value_mask & CWWidth ) 00959 nw = rw; 00960 if ( value_mask & CWHeight ) 00961 nh = rh; 00962 QSize ns = sizeForClientSize( QSize( nw, nh ) ); 00963 00964 // TODO what to do with maximized windows? 00965 if ( maximizeMode() != MaximizeFull 00966 || ns != size()) 00967 { 00968 resetMaximize(); 00969 ++block_geometry; 00970 move( new_pos ); 00971 plainResize( ns ); // TODO must(?) resize before gravitating? 00972 --block_geometry; 00973 setGeometry( QRect( calculateGravitation( false, gravity ), size()), ForceGeometrySet ); 00974 } 00975 } 00976 00977 if ( value_mask & (CWWidth | CWHeight ) 00978 && ! ( value_mask & ( CWX | CWY )) ) // pure resize 00979 { 00980 if ( isShade()) // SELI SHADE 00981 setShade( ShadeNone ); 00982 00983 int nw = clientSize().width(); 00984 int nh = clientSize().height(); 00985 if ( value_mask & CWWidth ) 00986 nw = rw; 00987 if ( value_mask & CWHeight ) 00988 nh = rh; 00989 QSize ns = sizeForClientSize( QSize( nw, nh ) ); 00990 00991 if( ns != size()) // don't restore if some app sets its own size again 00992 { 00993 resetMaximize(); 00994 int save_gravity = xSizeHint.win_gravity; 00995 xSizeHint.win_gravity = gravity; 00996 resizeWithChecks( ns ); 00997 xSizeHint.win_gravity = save_gravity; 00998 } 00999 } 01000 // No need to send synthetic configure notify event here, either it's sent together 01001 // with geometry change, or there's no need to send it. 01002 // Handling of the real ConfigureRequest event forces sending it, as there it's necessary. 01003 } 01004 01005 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force ) 01006 { 01007 int newx = x(); 01008 int newy = y(); 01009 QRect area = workspace()->clientArea( WorkArea, this ); 01010 // don't allow growing larger than workarea 01011 if( w > area.width()) 01012 w = area.width(); 01013 if( h > area.height()) 01014 h = area.height(); 01015 QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size 01016 w = tmp.width(); 01017 h = tmp.height(); 01018 switch( xSizeHint.win_gravity ) 01019 { 01020 case NorthWestGravity: // top left corner doesn't move 01021 default: 01022 break; 01023 case NorthGravity: // middle of top border doesn't move 01024 newx = ( newx + width() / 2 ) - ( w / 2 ); 01025 break; 01026 case NorthEastGravity: // top right corner doesn't move 01027 newx = newx + width() - w; 01028 break; 01029 case WestGravity: // middle of left border doesn't move 01030 newy = ( newy + height() / 2 ) - ( h / 2 ); 01031 break; 01032 case CenterGravity: // middle point doesn't move 01033 newx = ( newx + width() / 2 ) - ( w / 2 ); 01034 newy = ( newy + height() / 2 ) - ( h / 2 ); 01035 break; 01036 case StaticGravity: // top left corner of _client_ window doesn't move 01037 // since decoration doesn't change, equal to NorthWestGravity 01038 break; 01039 case EastGravity: // // middle of right border doesn't move 01040 newx = newx + width() - w; 01041 newy = ( newy + height() / 2 ) - ( h / 2 ); 01042 break; 01043 case SouthWestGravity: // bottom left corner doesn't move 01044 newy = newy + height() - h; 01045 break; 01046 case SouthGravity: // middle of bottom border doesn't move 01047 newx = ( newx + width() / 2 ) - ( w / 2 ); 01048 newy = newy + height() - h; 01049 break; 01050 case SouthEastGravity: // bottom right corner doesn't move 01051 newx = newx + width() - w; 01052 newy = newy + height() - h; 01053 break; 01054 } 01055 // if it would be moved outside of workarea, keep it inside, 01056 // see also Client::computeWorkareaDiff() 01057 if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit 01058 { 01059 if( newx < area.left()) 01060 newx = area.left(); 01061 if( newx + w > area.right() + 1 ) 01062 newx = area.right() + 1 - w; 01063 assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above 01064 } 01065 if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit 01066 { 01067 if( newy < area.top()) 01068 newy = area.top(); 01069 if( newy + h > area.bottom() + 1 ) 01070 newy = area.bottom() + 1 - h; 01071 assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above 01072 } 01073 setGeometry( newx, newy, w, h, force ); 01074 } 01075 01076 // _NET_MOVERESIZE_WINDOW 01077 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height ) 01078 { 01079 int gravity = flags & 0xff; 01080 int value_mask = 0; 01081 if( flags & ( 1 << 8 )) 01082 value_mask |= CWX; 01083 if( flags & ( 1 << 9 )) 01084 value_mask |= CWY; 01085 if( flags & ( 1 << 10 )) 01086 value_mask |= CWWidth; 01087 if( flags & ( 1 << 11 )) 01088 value_mask |= CWHeight; 01089 configureRequest( value_mask, x, y, width, height, gravity ); 01090 } 01091 01095 bool Client::isResizable() const 01096 { 01097 if ( !isMovable() || !motif_may_resize || isSplash()) 01098 return FALSE; 01099 01100 if ( ( xSizeHint.flags & PMaxSize) == 0 || (xSizeHint.flags & PMinSize ) == 0 ) 01101 return TRUE; 01102 return ( xSizeHint.min_width < xSizeHint.max_width ) || 01103 ( xSizeHint.min_height < xSizeHint.max_height ); 01104 } 01105 01106 /* 01107 Returns whether the window is maximizable or not 01108 */ 01109 bool Client::isMaximizable() const 01110 { 01111 if ( maximizeMode() != MaximizeRestore ) 01112 return TRUE; 01113 if( !isResizable() || isToolbar()) // SELI isToolbar() ? 01114 return false; 01115 if( xSizeHint.max_height < 32767 || xSizeHint.max_width < 32767 ) // sizes are 16bit with X 01116 return false; 01117 return true; 01118 } 01119 01120 01124 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) 01125 { 01126 if( force == NormalGeometrySet && frame_geometry == QRect( x, y, w, h )) 01127 return; 01128 frame_geometry = QRect( x, y, w, h ); 01129 if( !isShade()) 01130 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); 01131 else 01132 { 01133 // check that the frame is not resized to full size when it should be shaded 01134 if( !shade_geometry_change && h != border_top + border_bottom ) 01135 { 01136 kdDebug() << "h:" << h << ":t:" << border_top << ":b:" << border_bottom << endl; 01137 assert( false ); 01138 } 01139 client_size = QSize( w - border_left - border_right, client_size.height()); 01140 } 01141 updateWorkareaDiffs(); 01142 if( block_geometry == 0 ) 01143 { 01144 XMoveResizeWindow( qt_xdisplay(), frameId(), x, y, w, h ); 01145 resizeDecoration( QSize( w, h )); 01146 if( !isShade()) 01147 { 01148 QSize cs = clientSize(); 01149 XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(), 01150 cs.width(), cs.height()); 01151 // FRAME tady poradi tak, at neni flicker 01152 XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height()); 01153 } 01154 if( shape()) 01155 updateShape(); 01156 // SELI TODO won't this be too expensive? 01157 updateWorkareaDiffs(); 01158 sendSyntheticConfigureNotify(); // TODO optimize this? 01159 } 01160 } 01161 01162 void Client::plainResize( int w, int h, ForceGeometry_t force ) 01163 { // TODO make this deffered with isResize() ? old kwin did 01164 if( force == NormalGeometrySet && frame_geometry.size() == QSize( w, h )) 01165 return; 01166 frame_geometry.setSize( QSize( w, h )); 01167 if( !isShade()) 01168 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom ); 01169 else 01170 { 01171 // check that the frame is not resized to full size when it should be shaded 01172 if( !shade_geometry_change && h != border_top + border_bottom ) 01173 { 01174 kdDebug() << "h:" << h << ":t:" << border_top << ":b:" << border_bottom << endl; 01175 assert( false ); 01176 } 01177 client_size = QSize( w - border_left - border_right, client_size.height()); 01178 } 01179 updateWorkareaDiffs(); 01180 if( block_geometry == 0 ) 01181 { 01182 // FRAME tady poradi tak, at neni flicker 01183 XResizeWindow( qt_xdisplay(), frameId(), w, h ); 01184 resizeDecoration( QSize( w, h )); 01185 if( !isShade()) 01186 { 01187 QSize cs = clientSize(); 01188 XMoveResizeWindow( qt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(), 01189 cs.width(), cs.height()); 01190 XMoveResizeWindow( qt_xdisplay(), window(), 0, 0, cs.width(), cs.height()); 01191 } 01192 if( shape()) 01193 updateShape(); 01194 updateWorkareaDiffs(); 01195 sendSyntheticConfigureNotify(); 01196 } 01197 } 01198 01202 void Client::move( int x, int y, ForceGeometry_t force ) 01203 { 01204 if( force == NormalGeometrySet && frame_geometry.topLeft() == QPoint( x, y )) 01205 return; 01206 frame_geometry.moveTopLeft( QPoint( x, y )); 01207 updateWorkareaDiffs(); 01208 if( block_geometry == 0 ) 01209 { 01210 XMoveWindow( qt_xdisplay(), frameId(), x, y ); 01211 sendSyntheticConfigureNotify(); 01212 } 01213 } 01214 01215 01216 void Client::maximize( MaximizeMode m ) 01217 { 01218 setMaximize( m & MaximizeVertical, m & MaximizeHorizontal ); 01219 } 01220 01224 void Client::setMaximize( bool vertically, bool horizontally ) 01225 { // changeMaximize() flips the state, so change from set->flip 01226 changeMaximize( 01227 max_mode & MaximizeVertical ? !vertically : vertically, 01228 max_mode & MaximizeHorizontal ? !horizontally : horizontally, 01229 false ); 01230 } 01231 01232 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust ) 01233 { 01234 if( !isMaximizable()) 01235 return; 01236 01237 ++block_geometry; // TODO GeometryBlocker class? 01238 01239 if( isShade()) // SELI SHADE 01240 setShade( ShadeNone ); 01241 01242 MaximizeMode old_mode = max_mode; 01243 // 'adjust == true' means to update the size only, e.g. after changing workspace size 01244 if( !adjust ) 01245 { 01246 if( vertical ) 01247 max_mode = MaximizeMode( max_mode ^ MaximizeVertical ); 01248 if( horizontal ) 01249 max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal ); 01250 } 01251 01252 // maximing one way and unmaximizing the other way shouldn't happen 01253 Q_ASSERT( !( vertical && horizontal ) 01254 || (( max_mode & MaximizeVertical != 0 ) == ( max_mode & MaximizeHorizontal != 0 ))); 01255 01256 // save sizes for restoring, if maximalizing 01257 bool maximalizing = false; 01258 if( vertical && !(old_mode & MaximizeVertical )) 01259 { 01260 geom_restore.setTop( y()); 01261 geom_restore.setHeight( height()); 01262 maximalizing = true; 01263 } 01264 if( horizontal && !( old_mode & MaximizeHorizontal )) 01265 { 01266 geom_restore.setLeft( x()); 01267 geom_restore.setWidth( width()); 01268 maximalizing = true; 01269 } 01270 01271 if( !adjust ) 01272 { 01273 if( maximalizing ) 01274 Notify::raise( Notify::Maximize ); 01275 else 01276 Notify::raise( Notify::UnMaximize ); 01277 } 01278 01279 if( decoration != NULL ) // decorations may turn off some borders when maximized 01280 decoration->borders( border_left, border_right, border_top, border_bottom ); 01281 01282 QRect clientArea = workspace()->clientArea( MaximizeArea, this ); 01283 01284 switch (max_mode) 01285 { 01286 01287 case MaximizeVertical: 01288 { 01289 if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull 01290 { 01291 if( geom_restore.width() == 0 ) 01292 { // needs placement 01293 plainResize( adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )); 01294 workspace()->placeSmart( this, clientArea ); 01295 } 01296 else 01297 setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()), 01298 adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH ))); 01299 } 01300 else 01301 setGeometry( QRect(QPoint(x(), clientArea.top()), 01302 adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH ))); 01303 info->setState( NET::MaxVert, NET::Max ); 01304 break; 01305 } 01306 01307 case MaximizeHorizontal: 01308 { 01309 if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull 01310 { 01311 if( geom_restore.height() == 0 ) 01312 { // needs placement 01313 plainResize( adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )); 01314 workspace()->placeSmart( this, clientArea ); 01315 } 01316 else 01317 setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()), 01318 adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW ))); 01319 } 01320 else 01321 setGeometry( QRect( QPoint(clientArea.left(), y()), 01322 adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW ))); 01323 info->setState( NET::MaxHoriz, NET::Max ); 01324 break; 01325 } 01326 01327 case MaximizeRestore: 01328 { 01329 QRect restore = geometry(); 01330 // when only partially maximized, geom_restore may not have the other dimension remembered 01331 if( old_mode & MaximizeVertical ) 01332 { 01333 restore.setTop( geom_restore.top()); 01334 restore.setBottom( geom_restore.bottom()); 01335 } 01336 if( old_mode & MaximizeHorizontal ) 01337 { 01338 restore.setLeft( geom_restore.left()); 01339 restore.setRight( geom_restore.right()); 01340 } 01341 if( !restore.isValid()) 01342 { 01343 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 ); 01344 if( geom_restore.width() > 0 ) 01345 s.setWidth( geom_restore.width()); 01346 if( geom_restore.height() > 0 ) 01347 s.setHeight( geom_restore.height()); 01348 plainResize( adjustedSize( s )); 01349 workspace()->placeSmart( this, clientArea ); 01350 restore = geometry(); 01351 if( geom_restore.width() > 0 ) 01352 restore.moveLeft( geom_restore.x()); 01353 if( geom_restore.height() > 0 ) 01354 restore.moveTop( geom_restore.y()); 01355 } 01356 setGeometry( restore ); 01357 info->setState( 0, NET::Max ); 01358 break; 01359 } 01360 01361 case MaximizeFull: 01362 { 01363 QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax ); 01364 QRect r = QRect(clientArea.topLeft(), adjSize); 01365 setGeometry( r ); 01366 info->setState( NET::Max, NET::Max ); 01367 break; 01368 } 01369 default: 01370 break; 01371 } 01372 01373 --block_geometry; 01374 setGeometry( geometry(), ForceGeometrySet ); 01375 01376 updateAllowedActions(); 01377 if( decoration != NULL ) 01378 decoration->maximizeChange(); 01379 } 01380 01381 void Client::resetMaximize() 01382 { 01383 if( max_mode == MaximizeRestore ) 01384 return; 01385 max_mode = MaximizeRestore; 01386 Notify::raise( Notify::UnMaximize ); 01387 info->setState( 0, NET::Max ); 01388 updateAllowedActions(); 01389 if( decoration != NULL ) 01390 decoration->borders( border_left, border_right, border_top, border_bottom ); 01391 setGeometry( geometry(), ForceGeometrySet ); 01392 if( decoration != NULL ) 01393 decoration->maximizeChange(); 01394 } 01395 01396 bool Client::isFullScreenable( bool fullscreen_hack ) const 01397 { 01398 if( fullscreen_hack ) 01399 return isNormalWindow() || isOverride(); 01400 else // don't check size constrains - some apps request fullscreen despite requesting fixed size 01401 return !isSpecialWindow(); // also better disallow only weird types to go fullscreen 01402 } 01403 01404 bool Client::userCanSetFullScreen() const 01405 { 01406 return isNormalWindow() && fullscreen_mode != FullScreenHack 01407 && ( isMaximizable() || isFullScreen()); // isMaximizable() is false for isFullScreen() 01408 } 01409 01410 void Client::setFullScreen( bool set, bool user ) 01411 { 01412 if( !isFullScreen() && !set ) 01413 return; 01414 if( fullscreen_mode == FullScreenHack ) 01415 return; 01416 if( user && !userCanSetFullScreen()) 01417 return; 01418 setShade( ShadeNone ); 01419 bool was_fs = isFullScreen(); 01420 if( !was_fs ) 01421 geom_fs_restore = geometry(); 01422 fullscreen_mode = set ? FullScreenNormal : FullScreenNone; 01423 if( was_fs == isFullScreen()) 01424 return; 01425 StackingUpdatesBlocker blocker( workspace()); 01426 workspace()->updateClientLayer( this ); // active fullscreens get different layer 01427 info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen ); 01428 updateDecoration( false, false ); 01429 if( isFullScreen()) 01430 setGeometry( workspace()->clientArea( MaximizeFullArea, this )); 01431 else 01432 { 01433 if( maximizeMode() != MaximizeRestore ) 01434 changeMaximize( false, false, true ); // adjust size 01435 else if( !geom_fs_restore.isNull()) 01436 setGeometry( geom_fs_restore ); 01437 // TODO isShaded() ? 01438 else 01439 { // does this ever happen? 01440 setGeometry( workspace()->clientArea( MaximizeArea, this )); 01441 } 01442 } 01443 } 01444 01445 01446 static QRect* visible_bound = 0; 01447 static GeometryTip* geometryTip = 0; 01448 01449 void Client::drawbound( const QRect& geom ) 01450 { 01451 assert( visible_bound == NULL ); 01452 visible_bound = new QRect( geom ); 01453 doDrawbound( *visible_bound, false ); 01454 } 01455 01456 void Client::clearbound() 01457 { 01458 if( visible_bound == NULL ) 01459 return; 01460 doDrawbound( *visible_bound, true ); 01461 delete visible_bound; 01462 visible_bound = 0; 01463 } 01464 01465 void Client::doDrawbound( const QRect& geom, bool clear ) 01466 { 01467 if( decoration != NULL && decoration->drawbound( geom, clear )) 01468 return; // done by decoration 01469 QPainter p ( workspace()->desktopWidget() ); 01470 p.setPen( QPen( Qt::white, 5 ) ); 01471 p.setRasterOp( Qt::XorROP ); 01472 p.drawRect( geom ); 01473 } 01474 01475 void Client::positionGeometryTip() 01476 { 01477 assert( isMove() || isResize()); 01478 // Position and Size display 01479 if (options->showGeometryTip()) 01480 { 01481 if( !geometryTip ) 01482 { // save under is not necessary with opaque, and seem to make things slower 01483 bool save_under = ( isMove() && options->moveMode != Options::Opaque ) 01484 || ( isResize() && options->resizeMode != Options::Opaque ); 01485 geometryTip = new GeometryTip( &xSizeHint, save_under ); 01486 } 01487 QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself 01488 wgeom.setWidth( wgeom.width() - ( width() - clientSize().width())); 01489 wgeom.setHeight( wgeom.height() - ( height() - clientSize().height())); 01490 if( isShade()) 01491 wgeom.setHeight( 0 ); 01492 geometryTip->setGeometry( wgeom ); 01493 if( !geometryTip->isVisible()) 01494 { 01495 geometryTip->show(); 01496 geometryTip->raise(); 01497 } 01498 } 01499 } 01500 01501 class EatAllPaintEvents 01502 : public QObject 01503 { 01504 protected: 01505 virtual bool eventFilter( QObject* o, QEvent* e ) 01506 { return e->type() == QEvent::Paint && o != geometryTip; } 01507 }; 01508 01509 static EatAllPaintEvents* eater = 0; 01510 01511 bool Client::startMoveResize() 01512 { 01513 assert( !moveResizeMode ); 01514 assert( QWidget::keyboardGrabber() == NULL ); 01515 assert( QWidget::mouseGrabber() == NULL ); 01516 if( QApplication::activePopupWidget() != NULL ) 01517 return false; // popups have grab 01518 bool has_grab = false; 01519 // This reportedly improves smoothness of the moveresize operation, 01520 // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* 01521 // (http://lists.kde.org/?t=107302193400001&r=1&w=2) 01522 XSetWindowAttributes attrs; 01523 QRect r = workspace()->clientArea( FullArea, this ); 01524 move_resize_grab_window = XCreateWindow( qt_xdisplay(), workspace()->rootWin(), r.x(), r.y(), 01525 r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs ); 01526 XMapRaised( qt_xdisplay(), move_resize_grab_window ); 01527 if( XGrabPointer( qt_xdisplay(), move_resize_grab_window, False, 01528 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, 01529 GrabModeAsync, GrabModeAsync, None, cursor.handle(), qt_x_time ) == Success ) 01530 has_grab = true; 01531 if( XGrabKeyboard( qt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, qt_x_time ) == Success ) 01532 has_grab = true; 01533 if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize 01534 { 01535 XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); 01536 move_resize_grab_window = None; 01537 return false; 01538 } 01539 if ( maximizeMode() != MaximizeRestore ) 01540 resetMaximize(); 01541 moveResizeMode = true; 01542 workspace()->setClientIsMoving(this); 01543 initialMoveResizeGeom = moveResizeGeom = geometry(); 01544 checkUnrestrictedMoveResize(); 01545 if ( ( isMove() && options->moveMode != Options::Opaque ) 01546 || ( isResize() && options->resizeMode != Options::Opaque ) ) 01547 { 01548 grabXServer(); 01549 kapp->sendPostedEvents(); 01550 // we have server grab -> nothing should cause paint events 01551 // unfortunately, that's not completely true, Qt may generate 01552 // paint events on some widgets due to FocusIn(?) 01553 // eat them, otherwise XOR painting will be broken (#58054) 01554 // paint events for the geometrytip need to be allowed, though 01555 eater = new EatAllPaintEvents; 01556 kapp->installEventFilter( eater ); 01557 } 01558 Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart ); 01559 return true; 01560 } 01561 01562 void Client::finishMoveResize( bool cancel ) 01563 { 01564 leaveMoveResize(); 01565 if( cancel ) 01566 setGeometry( initialMoveResizeGeom ); 01567 else 01568 setGeometry( moveResizeGeom ); 01569 // FRAME update(); 01570 Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); 01571 } 01572 01573 void Client::leaveMoveResize() 01574 { 01575 clearbound(); 01576 if (geometryTip) 01577 { 01578 geometryTip->hide(); 01579 delete geometryTip; 01580 geometryTip = NULL; 01581 } 01582 if ( ( isMove() && options->moveMode != Options::Opaque ) 01583 || ( isResize() && options->resizeMode != Options::Opaque ) ) 01584 ungrabXServer(); 01585 XUngrabKeyboard( qt_xdisplay(), qt_x_time ); 01586 XUngrabPointer( qt_xdisplay(), qt_x_time ); 01587 XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); 01588 move_resize_grab_window = None; 01589 workspace()->setClientIsMoving(0); 01590 if( move_faked_activity ) 01591 workspace()->unfakeActivity( this ); 01592 move_faked_activity = false; 01593 moveResizeMode = false; 01594 delete eater; 01595 eater = 0; 01596 } 01597 01598 // This function checks if it actually makes sense to perform a restricted move/resize. 01599 // If e.g. the titlebar is already outside of the workarea, there's no point in performing 01600 // a restricted move resize, because then e.g. resize would also move the window (#74555). 01601 // NOTE: Most of it is duplicated from handleMoveResize(). 01602 void Client::checkUnrestrictedMoveResize() 01603 { 01604 if( unrestrictedMoveResize ) 01605 return; 01606 QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop()); 01607 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; 01608 // restricted move/resize - keep at least part of the titlebar always visible 01609 // how much must remain visible when moved away in that direction 01610 left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); 01611 right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); 01612 // width/height change with opaque resizing, use the initial ones 01613 titlebar_marge = initialMoveResizeGeom.height(); 01614 top_marge = border_bottom; 01615 bottom_marge = border_top; 01616 if( isResize()) 01617 { 01618 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) 01619 unrestrictedMoveResize = true; 01620 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01621 unrestrictedMoveResize = true; 01622 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01623 unrestrictedMoveResize = true; 01624 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01625 unrestrictedMoveResize = true; 01626 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out 01627 unrestrictedMoveResize = true; 01628 } 01629 if( isMove()) 01630 { 01631 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out 01632 unrestrictedMoveResize = true; 01633 // no need to check top_marge, titlebar_marge already handles it 01634 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01635 unrestrictedMoveResize = true; 01636 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01637 unrestrictedMoveResize = true; 01638 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01639 unrestrictedMoveResize = true; 01640 } 01641 } 01642 01643 void Client::handleMoveResize( int x, int y, int x_root, int y_root ) 01644 { 01645 if(( mode == PositionCenter && !isMovable()) 01646 || ( mode != PositionCenter && ( isShade() || !isResizable()))) 01647 return; 01648 01649 if ( !moveResizeMode ) 01650 { 01651 QPoint p( QPoint( x, y ) - moveOffset ); 01652 if (p.manhattanLength() >= 6) 01653 { 01654 if( !startMoveResize()) 01655 { 01656 buttonDown = false; 01657 setCursor( mode ); 01658 return; 01659 } 01660 } 01661 else 01662 return; 01663 } 01664 01665 // ShadeHover or ShadeActive, ShadeNormal was already avoided above 01666 if ( mode != PositionCenter && shade_mode != ShadeNone ) // SHADE 01667 setShade( ShadeNone ); 01668 01669 QPoint globalPos( x_root, y_root ); 01670 // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done, 01671 // the bottomleft corner should be at is at (topleft.x(), bottomright().y()) 01672 QPoint topleft = globalPos - moveOffset; 01673 QPoint bottomright = globalPos + invertedMoveOffset; 01674 QRect previousMoveResizeGeom = moveResizeGeom; 01675 01676 // TODO move whole group when moving its leader or when the leader is not mapped? 01677 01678 // compute bounds 01679 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01680 QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop()); 01681 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; 01682 if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely 01683 left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5; 01684 else // restricted move/resize - keep at least part of the titlebar always visible 01685 { 01686 // how much must remain visible when moved away in that direction 01687 left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); 01688 right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); 01689 // width/height change with opaque resizing, use the initial ones 01690 titlebar_marge = initialMoveResizeGeom.height(); 01691 top_marge = border_bottom; 01692 bottom_marge = border_top; 01693 } 01694 01695 bool update = false; 01696 if( isResize()) 01697 { 01698 // first resize (without checking constrains), then snap, then check bounds, then check constrains 01699 QRect orig = initialMoveResizeGeom; 01700 Sizemode sizemode = SizemodeAny; 01701 switch ( mode ) 01702 { 01703 case PositionTopLeft: 01704 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; 01705 break; 01706 case PositionBottomRight: 01707 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; 01708 break; 01709 case PositionBottomLeft: 01710 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; 01711 break; 01712 case PositionTopRight: 01713 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01714 break; 01715 case PositionTop: 01716 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ; 01717 sizemode = SizemodeFixedH; // try not to affect height 01718 break; 01719 case PositionBottom: 01720 moveResizeGeom = QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ; 01721 sizemode = SizemodeFixedH; 01722 break; 01723 case PositionLeft: 01724 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ; 01725 sizemode = SizemodeFixedW; 01726 break; 01727 case PositionRight: 01728 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ; 01729 sizemode = SizemodeFixedW; 01730 break; 01731 case PositionCenter: 01732 default: 01733 assert( false ); 01734 break; 01735 } 01736 // TODO snap 01737 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01738 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) 01739 moveResizeGeom.setBottom( desktopArea.top() + top_marge ); 01740 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01741 moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge ); 01742 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01743 moveResizeGeom.setRight( desktopArea.left() + left_marge ); 01744 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01745 moveResizeGeom.setLeft(desktopArea.right() - right_marge ); 01746 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out 01747 moveResizeGeom.setTop( desktopArea.top()); 01748 01749 QSize size = adjustedSize( moveResizeGeom.size(), sizemode ); 01750 // the new topleft and bottomright corners (after checking size constrains), if they'll be needed 01751 topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 ); 01752 bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 ); 01753 orig = moveResizeGeom; 01754 switch ( mode ) 01755 { // these 4 corners ones are copied from above 01756 case PositionTopLeft: 01757 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; 01758 break; 01759 case PositionBottomRight: 01760 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; 01761 break; 01762 case PositionBottomLeft: 01763 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; 01764 break; 01765 case PositionTopRight: 01766 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01767 break; 01768 // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change. 01769 // Therefore grow to the right/bottom if needed. 01770 // TODO it should probably obey gravity rather than always using right/bottom ? 01771 case PositionTop: 01772 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01773 break; 01774 case PositionBottom: 01775 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; 01776 break; 01777 case PositionLeft: 01778 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y())); 01779 break; 01780 case PositionRight: 01781 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; 01782 break; 01783 case PositionCenter: 01784 default: 01785 assert( false ); 01786 break; 01787 } 01788 if( moveResizeGeom.size() != previousMoveResizeGeom.size()) 01789 update = true; 01790 } 01791 else if( isMove()) 01792 { 01793 assert( mode == PositionCenter ); 01794 // first move, then snap, then check bounds 01795 moveResizeGeom.moveTopLeft( topleft ); 01796 moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) ); 01797 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01798 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out 01799 moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 ); 01800 // no need to check top_marge, titlebar_marge already handles it 01801 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01802 moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge ); 01803 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01804 moveResizeGeom.moveRight( desktopArea.left() + left_marge ); 01805 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01806 moveResizeGeom.moveLeft(desktopArea.right() - right_marge ); 01807 if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) 01808 update = true; 01809 } 01810 else 01811 assert( false ); 01812 01813 if( update ) 01814 { 01815 if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) 01816 { 01817 setGeometry( moveResizeGeom ); 01818 positionGeometryTip(); 01819 } 01820 else if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent ) 01821 { 01822 clearbound(); // it's necessary to move the geometry tip when there's no outline 01823 positionGeometryTip(); // shown, otherwise it would cause repaint problems in case 01824 drawbound( moveResizeGeom ); // they overlap; the paint event will come after this, 01825 } // so the geometry tip will be painted above the outline 01826 } 01827 if ( isMove() ) 01828 workspace()->clientMoved(globalPos, qt_x_time); 01829 } 01830 01831 01832 } // 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 Tue Aug 31 00:02:13 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003