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