kwin Library API Documentation

geometry.cpp

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