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 // This reportedly improves smoothness of the moveresize operation, 01519 // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* 01520 // (http://lists.kde.org/?t=107302193400001&r=1&w=2) 01521 XSetWindowAttributes attrs; 01522 QRect r = workspace()->clientArea( FullArea, this ); 01523 move_resize_grab_window = XCreateWindow( qt_xdisplay(), workspace()->rootWin(), r.x(), r.y(), 01524 r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs ); 01525 XMapRaised( qt_xdisplay(), move_resize_grab_window ); 01526 if( XGrabPointer( qt_xdisplay(), move_resize_grab_window, False, 01527 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, 01528 GrabModeAsync, GrabModeAsync, None, cursor.handle(), qt_x_time ) == Success ) 01529 has_grab = true; 01530 if( XGrabKeyboard( qt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, qt_x_time ) == Success ) 01531 has_grab = true; 01532 if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize 01533 { 01534 XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); 01535 move_resize_grab_window = None; 01536 return false; 01537 } 01538 if ( maximizeMode() != MaximizeRestore ) 01539 resetMaximize(); 01540 moveResizeMode = true; 01541 workspace()->setClientIsMoving(this); 01542 initialMoveResizeGeom = moveResizeGeom = geometry(); 01543 checkUnrestrictedMoveResize(); 01544 if ( ( isMove() && options->moveMode != Options::Opaque ) 01545 || ( isResize() && options->resizeMode != Options::Opaque ) ) 01546 { 01547 grabXServer(); 01548 kapp->sendPostedEvents(); 01549 // we have server grab -> nothing should cause paint events 01550 // unfortunately, that's not completely true, Qt may generate 01551 // paint events on some widgets due to FocusIn(?) 01552 // eat them, otherwise XOR painting will be broken (#58054) 01553 // paint events for the geometrytip need to be allowed, though 01554 eater = new EatAllPaintEvents; 01555 kapp->installEventFilter( eater ); 01556 } 01557 Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart ); 01558 return true; 01559 } 01560 01561 void Client::finishMoveResize( bool cancel ) 01562 { 01563 leaveMoveResize(); 01564 if( cancel ) 01565 setGeometry( initialMoveResizeGeom ); 01566 else 01567 setGeometry( moveResizeGeom ); 01568 // FRAME update(); 01569 Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); 01570 } 01571 01572 void Client::leaveMoveResize() 01573 { 01574 clearbound(); 01575 if (geometryTip) 01576 { 01577 geometryTip->hide(); 01578 delete geometryTip; 01579 geometryTip = NULL; 01580 } 01581 if ( ( isMove() && options->moveMode != Options::Opaque ) 01582 || ( isResize() && options->resizeMode != Options::Opaque ) ) 01583 ungrabXServer(); 01584 XUngrabKeyboard( qt_xdisplay(), qt_x_time ); 01585 XUngrabPointer( qt_xdisplay(), qt_x_time ); 01586 XDestroyWindow( qt_xdisplay(), move_resize_grab_window ); 01587 move_resize_grab_window = None; 01588 workspace()->setClientIsMoving(0); 01589 if( move_faked_activity ) 01590 workspace()->unfakeActivity( this ); 01591 move_faked_activity = false; 01592 moveResizeMode = false; 01593 delete eater; 01594 eater = 0; 01595 } 01596 01597 // This function checks if it actually makes sense to perform a restricted move/resize. 01598 // If e.g. the titlebar is already outside of the workarea, there's no point in performing 01599 // a restricted move resize, because then e.g. resize would also move the window (#74555). 01600 // NOTE: Most of it is duplicated from handleMoveResize(). 01601 void Client::checkUnrestrictedMoveResize() 01602 { 01603 if( unrestrictedMoveResize ) 01604 return; 01605 QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop()); 01606 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; 01607 // restricted move/resize - keep at least part of the titlebar always visible 01608 // how much must remain visible when moved away in that direction 01609 left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); 01610 right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); 01611 // width/height change with opaque resizing, use the initial ones 01612 titlebar_marge = initialMoveResizeGeom.height(); 01613 top_marge = border_bottom; 01614 bottom_marge = border_top; 01615 if( isResize()) 01616 { 01617 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) 01618 unrestrictedMoveResize = true; 01619 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01620 unrestrictedMoveResize = true; 01621 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01622 unrestrictedMoveResize = true; 01623 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01624 unrestrictedMoveResize = true; 01625 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out 01626 unrestrictedMoveResize = true; 01627 } 01628 if( isMove()) 01629 { 01630 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out 01631 unrestrictedMoveResize = true; 01632 // no need to check top_marge, titlebar_marge already handles it 01633 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01634 unrestrictedMoveResize = true; 01635 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01636 unrestrictedMoveResize = true; 01637 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01638 unrestrictedMoveResize = true; 01639 } 01640 } 01641 01642 void Client::handleMoveResize( int x, int y, int x_root, int y_root ) 01643 { 01644 if(( mode == PositionCenter && !isMovable()) 01645 || ( mode != PositionCenter && ( isShade() || !isResizable()))) 01646 return; 01647 01648 if ( !moveResizeMode ) 01649 { 01650 QPoint p( QPoint( x, y ) - moveOffset ); 01651 if (p.manhattanLength() >= 6) 01652 { 01653 if( !startMoveResize()) 01654 { 01655 buttonDown = false; 01656 setCursor( mode ); 01657 return; 01658 } 01659 } 01660 else 01661 return; 01662 } 01663 01664 // ShadeHover or ShadeActive, ShadeNormal was already avoided above 01665 if ( mode != PositionCenter && shade_mode != ShadeNone ) // SHADE 01666 setShade( ShadeNone ); 01667 01668 QPoint globalPos( x_root, y_root ); 01669 // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done, 01670 // the bottomleft corner should be at is at (topleft.x(), bottomright().y()) 01671 QPoint topleft = globalPos - moveOffset; 01672 QPoint bottomright = globalPos + invertedMoveOffset; 01673 QRect previousMoveResizeGeom = moveResizeGeom; 01674 01675 // TODO move whole group when moving its leader or when the leader is not mapped? 01676 01677 // compute bounds 01678 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01679 QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop()); 01680 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; 01681 if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely 01682 left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5; 01683 else // restricted move/resize - keep at least part of the titlebar always visible 01684 { 01685 // how much must remain visible when moved away in that direction 01686 left_marge = KMIN( 100 + border_right, moveResizeGeom.width()); 01687 right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); 01688 // width/height change with opaque resizing, use the initial ones 01689 titlebar_marge = initialMoveResizeGeom.height(); 01690 top_marge = border_bottom; 01691 bottom_marge = border_top; 01692 } 01693 01694 bool update = false; 01695 if( isResize()) 01696 { 01697 // first resize (without checking constrains), then snap, then check bounds, then check constrains 01698 QRect orig = initialMoveResizeGeom; 01699 Sizemode sizemode = SizemodeAny; 01700 switch ( mode ) 01701 { 01702 case PositionTopLeft: 01703 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; 01704 break; 01705 case PositionBottomRight: 01706 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; 01707 break; 01708 case PositionBottomLeft: 01709 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; 01710 break; 01711 case PositionTopRight: 01712 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01713 break; 01714 case PositionTop: 01715 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ; 01716 sizemode = SizemodeFixedH; // try not to affect height 01717 break; 01718 case PositionBottom: 01719 moveResizeGeom = QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ; 01720 sizemode = SizemodeFixedH; 01721 break; 01722 case PositionLeft: 01723 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ; 01724 sizemode = SizemodeFixedW; 01725 break; 01726 case PositionRight: 01727 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ; 01728 sizemode = SizemodeFixedW; 01729 break; 01730 case PositionCenter: 01731 default: 01732 assert( false ); 01733 break; 01734 } 01735 // TODO snap 01736 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01737 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) 01738 moveResizeGeom.setBottom( desktopArea.top() + top_marge ); 01739 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01740 moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge ); 01741 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01742 moveResizeGeom.setRight( desktopArea.left() + left_marge ); 01743 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01744 moveResizeGeom.setLeft(desktopArea.right() - right_marge ); 01745 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out 01746 moveResizeGeom.setTop( desktopArea.top()); 01747 01748 QSize size = adjustedSize( moveResizeGeom.size(), sizemode ); 01749 // the new topleft and bottomright corners (after checking size constrains), if they'll be needed 01750 topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 ); 01751 bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 ); 01752 orig = moveResizeGeom; 01753 switch ( mode ) 01754 { // these 4 corners ones are copied from above 01755 case PositionTopLeft: 01756 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ; 01757 break; 01758 case PositionBottomRight: 01759 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ; 01760 break; 01761 case PositionBottomLeft: 01762 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ; 01763 break; 01764 case PositionTopRight: 01765 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01766 break; 01767 // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change. 01768 // Therefore grow to the right/bottom if needed. 01769 // TODO it should probably obey gravity rather than always using right/bottom ? 01770 case PositionTop: 01771 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ; 01772 break; 01773 case PositionBottom: 01774 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; 01775 break; 01776 case PositionLeft: 01777 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y())); 01778 break; 01779 case PositionRight: 01780 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ; 01781 break; 01782 case PositionCenter: 01783 default: 01784 assert( false ); 01785 break; 01786 } 01787 if( moveResizeGeom.size() != previousMoveResizeGeom.size()) 01788 update = true; 01789 } 01790 else if( isMove()) 01791 { 01792 assert( mode == PositionCenter ); 01793 // first move, then snap, then check bounds 01794 moveResizeGeom.moveTopLeft( topleft ); 01795 moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) ); 01796 // NOTE: This is duped in checkUnrestrictedMoveResize(). 01797 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out 01798 moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 ); 01799 // no need to check top_marge, titlebar_marge already handles it 01800 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) 01801 moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge ); 01802 if( moveResizeGeom.right() < desktopArea.left() + left_marge ) 01803 moveResizeGeom.moveRight( desktopArea.left() + left_marge ); 01804 if( moveResizeGeom.left() > desktopArea.right() - right_marge ) 01805 moveResizeGeom.moveLeft(desktopArea.right() - right_marge ); 01806 if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) 01807 update = true; 01808 } 01809 else 01810 assert( false ); 01811 01812 if( update ) 01813 { 01814 if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) 01815 { 01816 setGeometry( moveResizeGeom ); 01817 positionGeometryTip(); 01818 } 01819 else if(( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent ) 01820 { 01821 clearbound(); // it's necessary to move the geometry tip when there's no outline 01822 positionGeometryTip(); // shown, otherwise it would cause repaint problems in case 01823 drawbound( moveResizeGeom ); // they overlap; the paint event will come after this, 01824 } // so the geometry tip will be painted above the outline 01825 } 01826 if ( isMove() ) 01827 workspace()->clientMoved(globalPos, qt_x_time); 01828 } 01829 01830 01831 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Dec 16 19:08:41 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003