TDE base libraries and programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

geometry.cpp 98KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649
  1. /*****************************************************************
  2. KWin - the KDE window manager
  3. This file is part of the KDE project.
  4. Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
  5. Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
  6. You can Freely distribute this program under the GNU General Public
  7. License. See the file "COPYING" for the exact licensing terms.
  8. ******************************************************************/
  9. /*
  10. This file contains things relevant to geometry, i.e. workspace size,
  11. window positions and window sizes.
  12. */
  13. #include "client.h"
  14. #include "workspace.h"
  15. #include <tdeapplication.h>
  16. #include <tdeglobal.h>
  17. #include <tqpainter.h>
  18. #include <twin.h>
  19. #include "placement.h"
  20. #include "notifications.h"
  21. #include "geometrytip.h"
  22. #include "rules.h"
  23. namespace KWinInternal
  24. {
  25. //********************************************
  26. // Workspace
  27. //********************************************
  28. /*!
  29. Resizes the workspace after an XRANDR screen size change
  30. */
  31. void Workspace::desktopResized()
  32. {
  33. //printf("Workspace::desktopResized()\n");
  34. TQRect geom = TDEApplication::desktop()->geometry();
  35. NETSize desktop_geometry;
  36. desktop_geometry.width = geom.width();
  37. desktop_geometry.height = geom.height();
  38. rootInfo->setDesktopGeometry( -1, desktop_geometry );
  39. updateClientArea( true );
  40. checkElectricBorders( true );
  41. }
  42. /*!
  43. Resizes the workspace after kdesktop signals a desktop resize
  44. */
  45. void Workspace::kDestopResized()
  46. {
  47. //printf("Workspace::kDesktopResized()\n");
  48. TQRect geom = TDEApplication::desktop()->geometry();
  49. NETSize desktop_geometry;
  50. desktop_geometry.width = geom.width();
  51. desktop_geometry.height = geom.height();
  52. rootInfo->setDesktopGeometry( -1, desktop_geometry );
  53. updateClientArea( true );
  54. checkElectricBorders( true );
  55. }
  56. /*!
  57. Updates the current client areas according to the current clients.
  58. If the area changes or force is true, the new areas are propagated to the world.
  59. The client area is the area that is available for clients (that
  60. which is not taken by windows like panels, the top-of-screen menu
  61. etc).
  62. \sa clientArea()
  63. */
  64. void Workspace::updateClientArea( bool force )
  65. {
  66. TQDesktopWidget *desktopwidget = TDEApplication::desktop();
  67. int nscreens = desktopwidget -> numScreens ();
  68. // kdDebug () << "screens: " << nscreens << endl;
  69. TQRect* new_wareas = new TQRect[ numberOfDesktops() + 1 ];
  70. TQRect** new_sareas = new TQRect*[ numberOfDesktops() + 1];
  71. TQRect* screens = new TQRect [ nscreens ];
  72. TQRect desktopArea = desktopwidget -> geometry ();
  73. for( int iS = 0;
  74. iS < nscreens;
  75. iS ++ )
  76. {
  77. screens [iS] = desktopwidget -> screenGeometry (iS);
  78. }
  79. for( int i = 1;
  80. i <= numberOfDesktops();
  81. ++i )
  82. {
  83. new_wareas[ i ] = desktopArea;
  84. new_sareas[ i ] = new TQRect [ nscreens ];
  85. for( int iS = 0;
  86. iS < nscreens;
  87. iS ++ )
  88. new_sareas[ i ][ iS ] = screens[ iS ];
  89. }
  90. for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
  91. {
  92. if( !(*it)->hasStrut())
  93. continue;
  94. TQRect r = (*it)->adjustedClientArea( desktopArea, desktopArea );
  95. if( (*it)->isOnAllDesktops())
  96. for( int i = 1;
  97. i <= numberOfDesktops();
  98. ++i )
  99. {
  100. new_wareas[ i ] = new_wareas[ i ].intersect( r );
  101. for( int iS = 0;
  102. iS < nscreens;
  103. iS ++ )
  104. new_sareas[ i ][ iS ] =
  105. new_sareas[ i ][ iS ].intersect(
  106. (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
  107. );
  108. }
  109. else
  110. {
  111. new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersect( r );
  112. for( int iS = 0;
  113. iS < nscreens;
  114. iS ++ )
  115. {
  116. // kdDebug () << "adjusting new_sarea: " << screens[ iS ] << endl;
  117. new_sareas[ (*it)->desktop() ][ iS ] =
  118. new_sareas[ (*it)->desktop() ][ iS ].intersect(
  119. (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
  120. );
  121. }
  122. }
  123. }
  124. #if 0
  125. for( int i = 1;
  126. i <= numberOfDesktops();
  127. ++i )
  128. {
  129. for( int iS = 0;
  130. iS < nscreens;
  131. iS ++ )
  132. kdDebug () << "new_sarea: " << new_sareas[ i ][ iS ] << endl;
  133. }
  134. #endif
  135. // TODO topmenu update for screenarea changes?
  136. if( topmenu_space != NULL )
  137. {
  138. TQRect topmenu_area = desktopArea;
  139. topmenu_area.setTop( topMenuHeight());
  140. for( int i = 1;
  141. i <= numberOfDesktops();
  142. ++i )
  143. new_wareas[ i ] = new_wareas[ i ].intersect( topmenu_area );
  144. }
  145. bool changed = force;
  146. if (! screenarea)
  147. changed = true;
  148. for( int i = 1;
  149. !changed && i <= numberOfDesktops();
  150. ++i )
  151. {
  152. if( workarea[ i ] != new_wareas[ i ] )
  153. changed = true;
  154. for( int iS = 0;
  155. iS < nscreens;
  156. iS ++ )
  157. if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
  158. changed = true;
  159. }
  160. if ( changed )
  161. {
  162. delete[] workarea;
  163. workarea = new_wareas;
  164. new_wareas = NULL;
  165. delete[] screenarea;
  166. screenarea = new_sareas;
  167. new_sareas = NULL;
  168. NETRect r;
  169. for( int i = 1; i <= numberOfDesktops(); i++)
  170. {
  171. r.pos.x = workarea[ i ].x();
  172. r.pos.y = workarea[ i ].y();
  173. r.size.width = workarea[ i ].width();
  174. r.size.height = workarea[ i ].height();
  175. rootInfo->setWorkArea( i, r );
  176. }
  177. updateTopMenuGeometry();
  178. for( ClientList::ConstIterator it = clients.begin();
  179. it != clients.end();
  180. ++it)
  181. (*it)->checkWorkspacePosition();
  182. for( ClientList::ConstIterator it = desktops.begin();
  183. it != desktops.end();
  184. ++it)
  185. (*it)->checkWorkspacePosition();
  186. }
  187. delete[] screens;
  188. delete[] new_sareas;
  189. delete[] new_wareas;
  190. }
  191. void Workspace::updateClientArea()
  192. {
  193. updateClientArea( false );
  194. }
  195. /*!
  196. returns the area available for clients. This is the desktop
  197. geometry minus windows on the dock. Placement algorithms should
  198. refer to this rather than geometry().
  199. \sa geometry()
  200. */
  201. TQRect Workspace::clientArea( clientAreaOption opt, int screen, int desktop ) const
  202. {
  203. if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
  204. desktop = currentDesktop();
  205. TQDesktopWidget *desktopwidget = kapp->desktop();
  206. TQRect sarea = screenarea // may be NULL during KWin initialization
  207. ? screenarea[ desktop ][ screen ]
  208. : desktopwidget->screenGeometry( screen );
  209. TQRect warea = workarea[ desktop ].isNull()
  210. ? kapp->desktop()->geometry()
  211. : workarea[ desktop ];
  212. switch (opt)
  213. {
  214. case MaximizeArea:
  215. if (options->xineramaMaximizeEnabled)
  216. if (desktopwidget->numScreens() < 2)
  217. return warea;
  218. else
  219. return sarea;
  220. else
  221. return warea;
  222. case MaximizeFullArea:
  223. if (options->xineramaMaximizeEnabled)
  224. if (desktopwidget->numScreens() < 2)
  225. return desktopwidget->geometry();
  226. else
  227. return desktopwidget->screenGeometry( screen );
  228. else
  229. return desktopwidget->geometry();
  230. case FullScreenArea:
  231. if (options->xineramaFullscreenEnabled)
  232. if (desktopwidget->numScreens() < 2)
  233. return desktopwidget->geometry();
  234. else
  235. return desktopwidget->screenGeometry( screen );
  236. else
  237. return desktopwidget->geometry();
  238. case PlacementArea:
  239. if (options->xineramaPlacementEnabled)
  240. if (desktopwidget->numScreens() < 2)
  241. return warea;
  242. else
  243. return sarea;
  244. else
  245. return warea;
  246. case MovementArea:
  247. if (options->xineramaMovementEnabled)
  248. if (desktopwidget->numScreens() < 2)
  249. return desktopwidget->geometry();
  250. else
  251. return desktopwidget->screenGeometry( screen );
  252. else
  253. return desktopwidget->geometry();
  254. case WorkArea:
  255. return warea;
  256. case FullArea:
  257. return desktopwidget->geometry();
  258. case ScreenArea:
  259. if (desktopwidget->numScreens() < 2)
  260. return desktopwidget->geometry();
  261. else
  262. return desktopwidget->screenGeometry( screen );
  263. }
  264. assert( false );
  265. return TQRect();
  266. }
  267. TQRect Workspace::clientArea( clientAreaOption opt, const TQPoint& p, int desktop ) const
  268. {
  269. TQDesktopWidget *desktopwidget = TDEApplication::desktop();
  270. int screen = desktopwidget->screenNumber( p );
  271. if( screen < 0 )
  272. screen = desktopwidget->primaryScreen();
  273. return clientArea( opt, screen, desktop );
  274. }
  275. TQRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
  276. {
  277. return clientArea( opt, c->geometry().center(), c->desktop());
  278. }
  279. /*!
  280. Client \a c is moved around to position \a pos. This gives the
  281. workspace the opportunity to interveniate and to implement
  282. snap-to-windows functionality.
  283. */
  284. TQPoint Workspace::adjustClientPosition( Client* c, TQPoint pos )
  285. {
  286. //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
  287. //CT adapted for twin on 25Nov1999
  288. //aleXXX 02Nov2000 added second snapping mode
  289. if (options->windowSnapZone || options->borderSnapZone )
  290. {
  291. const bool sOWO=options->snapOnlyWhenOverlapping;
  292. const TQRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
  293. const int xmin = maxRect.left();
  294. const int xmax = maxRect.right()+1; //desk size
  295. const int ymin = maxRect.top();
  296. const int ymax = maxRect.bottom()+1;
  297. const int cx(pos.x());
  298. const int cy(pos.y());
  299. const int cw(c->width());
  300. const int ch(c->height());
  301. const int rx(cx+cw);
  302. const int ry(cy+ch); //these don't change
  303. int nx(cx), ny(cy); //buffers
  304. int deltaX(xmax);
  305. int deltaY(ymax); //minimum distance to other clients
  306. int lx, ly, lrx, lry; //coords and size for the comparison client, l
  307. // border snap
  308. int snap = options->borderSnapZone; //snap trigger
  309. if (snap)
  310. {
  311. if ((sOWO?(cx<xmin):true) && (QABS(xmin-cx)<snap))
  312. {
  313. deltaX = xmin-cx;
  314. nx = xmin;
  315. }
  316. if ((sOWO?(rx>xmax):true) && (QABS(rx-xmax)<snap) && (QABS(xmax-rx) < deltaX))
  317. {
  318. deltaX = rx-xmax;
  319. nx = xmax - cw;
  320. }
  321. if ((sOWO?(cy<ymin):true) && (QABS(ymin-cy)<snap))
  322. {
  323. deltaY = ymin-cy;
  324. ny = ymin;
  325. }
  326. if ((sOWO?(ry>ymax):true) && (QABS(ry-ymax)<snap) && (QABS(ymax-ry) < deltaY))
  327. {
  328. deltaY =ry-ymax;
  329. ny = ymax - ch;
  330. }
  331. }
  332. // windows snap
  333. snap = options->windowSnapZone;
  334. if (snap)
  335. {
  336. TQValueList<Client *>::ConstIterator l;
  337. for (l = clients.begin();l != clients.end();++l )
  338. {
  339. if ((*l)->isOnDesktop(currentDesktop()) &&
  340. !(*l)->isMinimized()
  341. && (*l) != c )
  342. {
  343. lx = (*l)->x();
  344. ly = (*l)->y();
  345. lrx = lx + (*l)->width();
  346. lry = ly + (*l)->height();
  347. if ( (( cy <= lry ) && ( cy >= ly )) ||
  348. (( ry >= ly ) && ( ry <= lry )) ||
  349. (( cy <= ly ) && ( ry >= lry )) )
  350. {
  351. if ((sOWO?(cx<lrx):true) && (QABS(lrx-cx)<snap) && ( QABS(lrx -cx) < deltaX) )
  352. {
  353. deltaX = QABS( lrx - cx );
  354. nx = lrx;
  355. }
  356. if ((sOWO?(rx>lx):true) && (QABS(rx-lx)<snap) && ( QABS( rx - lx )<deltaX) )
  357. {
  358. deltaX = QABS(rx - lx);
  359. nx = lx - cw;
  360. }
  361. }
  362. if ( (( cx <= lrx ) && ( cx >= lx )) ||
  363. (( rx >= lx ) && ( rx <= lrx )) ||
  364. (( cx <= lx ) && ( rx >= lrx )) )
  365. {
  366. if ((sOWO?(cy<lry):true) && (QABS(lry-cy)<snap) && (QABS( lry -cy ) < deltaY))
  367. {
  368. deltaY = QABS( lry - cy );
  369. ny = lry;
  370. }
  371. //if ( (QABS( ry-ly ) < snap) && (QABS( ry - ly ) < deltaY ))
  372. if ((sOWO?(ry>ly):true) && (QABS(ry-ly)<snap) && (QABS( ry - ly ) < deltaY ))
  373. {
  374. deltaY = QABS( ry - ly );
  375. ny = ly - ch;
  376. }
  377. }
  378. }
  379. }
  380. }
  381. pos = TQPoint(nx, ny);
  382. }
  383. return pos;
  384. }
  385. TQRect Workspace::adjustClientSize( Client* c, TQRect moveResizeGeom, int mode )
  386. {
  387. //adapted from adjustClientPosition on 29May2004
  388. //this function is called when resizing a window and will modify
  389. //the new dimensions to snap to other windows/borders if appropriate
  390. if ( options->windowSnapZone || options->borderSnapZone )
  391. {
  392. const bool sOWO=options->snapOnlyWhenOverlapping;
  393. const TQRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
  394. const int xmin = maxRect.left();
  395. const int xmax = maxRect.right(); //desk size
  396. const int ymin = maxRect.top();
  397. const int ymax = maxRect.bottom();
  398. const int cx(moveResizeGeom.left());
  399. const int cy(moveResizeGeom.top());
  400. const int rx(moveResizeGeom.right());
  401. const int ry(moveResizeGeom.bottom());
  402. int newcx(cx), newcy(cy); //buffers
  403. int newrx(rx), newry(ry);
  404. int deltaX(xmax);
  405. int deltaY(ymax); //minimum distance to other clients
  406. int lx, ly, lrx, lry; //coords and size for the comparison client, l
  407. // border snap
  408. int snap = options->borderSnapZone; //snap trigger
  409. if (snap)
  410. {
  411. deltaX = int(snap);
  412. deltaY = int(snap);
  413. #define SNAP_BORDER_TOP \
  414. if ((sOWO?(newcy<ymin):true) && (QABS(ymin-newcy)<deltaY)) \
  415. { \
  416. deltaY = QABS(ymin-newcy); \
  417. newcy = ymin; \
  418. }
  419. #define SNAP_BORDER_BOTTOM \
  420. if ((sOWO?(newry>ymax):true) && (QABS(ymax-newry)<deltaY)) \
  421. { \
  422. deltaY = QABS(ymax-newcy); \
  423. newry = ymax; \
  424. }
  425. #define SNAP_BORDER_LEFT \
  426. if ((sOWO?(newcx<xmin):true) && (QABS(xmin-newcx)<deltaX)) \
  427. { \
  428. deltaX = QABS(xmin-newcx); \
  429. newcx = xmin; \
  430. }
  431. #define SNAP_BORDER_RIGHT \
  432. if ((sOWO?(newrx>xmax):true) && (QABS(xmax-newrx)<deltaX)) \
  433. { \
  434. deltaX = QABS(xmax-newrx); \
  435. newrx = xmax; \
  436. }
  437. switch ( mode )
  438. {
  439. case PositionBottomRight:
  440. SNAP_BORDER_BOTTOM
  441. SNAP_BORDER_RIGHT
  442. break;
  443. case PositionRight:
  444. SNAP_BORDER_RIGHT
  445. break;
  446. case PositionBottom:
  447. SNAP_BORDER_BOTTOM
  448. break;
  449. case PositionTopLeft:
  450. SNAP_BORDER_TOP
  451. SNAP_BORDER_LEFT
  452. break;
  453. case PositionLeft:
  454. SNAP_BORDER_LEFT
  455. break;
  456. case PositionTop:
  457. SNAP_BORDER_TOP
  458. break;
  459. case PositionTopRight:
  460. SNAP_BORDER_TOP
  461. SNAP_BORDER_RIGHT
  462. break;
  463. case PositionBottomLeft:
  464. SNAP_BORDER_BOTTOM
  465. SNAP_BORDER_LEFT
  466. break;
  467. default:
  468. assert( false );
  469. break;
  470. }
  471. }
  472. // windows snap
  473. snap = options->windowSnapZone;
  474. if (snap)
  475. {
  476. deltaX = int(snap);
  477. deltaY = int(snap);
  478. TQValueList<Client *>::ConstIterator l;
  479. for (l = clients.begin();l != clients.end();++l )
  480. {
  481. if ((*l)->isOnDesktop(currentDesktop()) &&
  482. !(*l)->isMinimized()
  483. && (*l) != c )
  484. {
  485. lx = (*l)->x()-1;
  486. ly = (*l)->y()-1;
  487. lrx =(*l)->x() + (*l)->width();
  488. lry =(*l)->y() + (*l)->height();
  489. #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy >= ly )) || \
  490. (( newry >= ly ) && ( newry <= lry )) || \
  491. (( newcy <= ly ) && ( newry >= lry )) )
  492. #define WITHIN_WIDTH ( (( cx <= lrx ) && ( cx >= lx )) || \
  493. (( rx >= lx ) && ( rx <= lrx )) || \
  494. (( cx <= lx ) && ( rx >= lrx )) )
  495. #define SNAP_WINDOW_TOP if ( (sOWO?(newcy<lry):true) \
  496. && WITHIN_WIDTH \
  497. && (QABS( lry - newcy ) < deltaY) ) { \
  498. deltaY = QABS( lry - newcy ); \
  499. newcy=lry; \
  500. }
  501. #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
  502. && WITHIN_WIDTH \
  503. && (QABS( ly - newry ) < deltaY) ) { \
  504. deltaY = QABS( ly - newry ); \
  505. newry=ly; \
  506. }
  507. #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
  508. && WITHIN_HEIGHT \
  509. && (QABS( lrx - newcx ) < deltaX)) { \
  510. deltaX = QABS( lrx - newcx ); \
  511. newcx=lrx; \
  512. }
  513. #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
  514. && WITHIN_HEIGHT \
  515. && (QABS( lx - newrx ) < deltaX)) \
  516. { \
  517. deltaX = QABS( lx - newrx ); \
  518. newrx=lx; \
  519. }
  520. switch ( mode )
  521. {
  522. case PositionBottomRight:
  523. SNAP_WINDOW_BOTTOM
  524. SNAP_WINDOW_RIGHT
  525. break;
  526. case PositionRight:
  527. SNAP_WINDOW_RIGHT
  528. break;
  529. case PositionBottom:
  530. SNAP_WINDOW_BOTTOM
  531. break;
  532. case PositionTopLeft:
  533. SNAP_WINDOW_TOP
  534. SNAP_WINDOW_LEFT
  535. break;
  536. case PositionLeft:
  537. SNAP_WINDOW_LEFT
  538. break;
  539. case PositionTop:
  540. SNAP_WINDOW_TOP
  541. break;
  542. case PositionTopRight:
  543. SNAP_WINDOW_TOP
  544. SNAP_WINDOW_RIGHT
  545. break;
  546. case PositionBottomLeft:
  547. SNAP_WINDOW_BOTTOM
  548. SNAP_WINDOW_LEFT
  549. break;
  550. default:
  551. assert( false );
  552. break;
  553. }
  554. }
  555. }
  556. }
  557. moveResizeGeom = TQRect(TQPoint(newcx, newcy), TQPoint(newrx, newry));
  558. }
  559. return moveResizeGeom;
  560. }
  561. /*!
  562. Marks the client as being moved around by the user.
  563. */
  564. void Workspace::setClientIsMoving( Client *c )
  565. {
  566. Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
  567. // window while still moving the first one.
  568. movingClient = c;
  569. if (movingClient)
  570. ++block_focus;
  571. else
  572. --block_focus;
  573. }
  574. /*!
  575. Cascades all clients on the current desktop
  576. */
  577. void Workspace::cascadeDesktop()
  578. {
  579. // TODO XINERAMA this probably is not right for xinerama
  580. Q_ASSERT( block_stacking_updates == 0 );
  581. ClientList::ConstIterator it(stackingOrder().begin());
  582. initPositioning->reinitCascading( currentDesktop());
  583. TQRect area = clientArea( PlacementArea, TQPoint( 0, 0 ), currentDesktop());
  584. for (; it != stackingOrder().end(); ++it)
  585. {
  586. if((!(*it)->isOnDesktop(currentDesktop())) ||
  587. ((*it)->isMinimized()) ||
  588. ((*it)->isOnAllDesktops()) ||
  589. (!(*it)->isMovable()) )
  590. continue;
  591. initPositioning->placeCascaded(*it, area);
  592. }
  593. }
  594. /*!
  595. Unclutters the current desktop by smart-placing all clients
  596. again.
  597. */
  598. void Workspace::unclutterDesktop()
  599. {
  600. ClientList::Iterator it(clients.fromLast());
  601. for (; it != clients.end(); --it)
  602. {
  603. if((!(*it)->isOnDesktop(currentDesktop())) ||
  604. ((*it)->isMinimized()) ||
  605. ((*it)->isOnAllDesktops()) ||
  606. (!(*it)->isMovable()) )
  607. continue;
  608. initPositioning->placeSmart(*it, TQRect());
  609. }
  610. }
  611. void Workspace::updateTopMenuGeometry( Client* c )
  612. {
  613. if( !managingTopMenus())
  614. return;
  615. if( c != NULL )
  616. {
  617. XEvent ev;
  618. ev.xclient.display = tqt_xdisplay();
  619. ev.xclient.type = ClientMessage;
  620. ev.xclient.window = c->window();
  621. static Atom msg_type_atom = XInternAtom( tqt_xdisplay(), "_KDE_TOPMENU_MINSIZE", False );
  622. ev.xclient.message_type = msg_type_atom;
  623. ev.xclient.format = 32;
  624. ev.xclient.data.l[0] = GET_QT_X_TIME();
  625. ev.xclient.data.l[1] = topmenu_space->width();
  626. ev.xclient.data.l[2] = topmenu_space->height();
  627. ev.xclient.data.l[3] = 0;
  628. ev.xclient.data.l[4] = 0;
  629. XSendEvent( tqt_xdisplay(), c->window(), False, NoEventMask, &ev );
  630. KWin::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
  631. c->checkWorkspacePosition();
  632. return;
  633. }
  634. // c == NULL - update all, including topmenu_space
  635. TQRect area;
  636. area = clientArea( MaximizeFullArea, TQPoint( 0, 0 ), 1 ); // HACK desktop ?
  637. area.setHeight( topMenuHeight());
  638. topmenu_space->setGeometry( area );
  639. for( ClientList::ConstIterator it = topmenus.begin();
  640. it != topmenus.end();
  641. ++it )
  642. updateTopMenuGeometry( *it );
  643. }
  644. //********************************************
  645. // Client
  646. //********************************************
  647. void Client::keepInArea( TQRect area, bool partial )
  648. {
  649. if( partial )
  650. {
  651. // increase the area so that can have only 100 pixels in the area
  652. area.setLeft( QMIN( area.left() - width() + 100, area.left()));
  653. area.setTop( QMIN( area.top() - height() + 100, area.top()));
  654. area.setRight( QMAX( area.right() + width() - 100, area.right()));
  655. area.setBottom( QMAX( area.bottom() + height() - 100, area.bottom()));
  656. }
  657. if ( geometry().right() > area.right() && width() < area.width() )
  658. move( area.right() - width(), y() );
  659. if ( geometry().bottom() > area.bottom() && height() < area.height() )
  660. move( x(), area.bottom() - height() );
  661. if( !area.contains( geometry().topLeft() ))
  662. {
  663. int tx = x();
  664. int ty = y();
  665. if ( tx < area.x() )
  666. tx = area.x();
  667. if ( ty < area.y() )
  668. ty = area.y();
  669. move( tx, ty );
  670. }
  671. }
  672. /*!
  673. Returns \a area with the client's strut taken into account.
  674. Used from Workspace in updateClientArea.
  675. */
  676. // TODO move to Workspace?
  677. TQRect Client::adjustedClientArea( const TQRect &desktopArea, const TQRect& area ) const
  678. {
  679. TQRect r = area;
  680. // topmenu area is reserved in updateClientArea()
  681. if( isTopMenu())
  682. return r;
  683. NETExtendedStrut str = strut();
  684. TQRect stareaL = TQRect(
  685. 0,
  686. str . left_start,
  687. str . left_width,
  688. str . left_end - str . left_start + 1 );
  689. TQRect stareaR = TQRect (
  690. desktopArea . right () - str . right_width + 1,
  691. str . right_start,
  692. str . right_width,
  693. str . right_end - str . right_start + 1 );
  694. TQRect stareaT = TQRect (
  695. str . top_start,
  696. 0,
  697. str . top_end - str . top_start + 1,
  698. str . top_width);
  699. TQRect stareaB = TQRect (
  700. str . bottom_start,
  701. desktopArea . bottom () - str . bottom_width + 1,
  702. str . bottom_end - str . bottom_start + 1,
  703. str . bottom_width);
  704. NETExtendedStrut ext = info->extendedStrut();
  705. if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
  706. && ( str.left_width != 0 || str.right_width != 0 || str.top_width != 0 || str.bottom_width != 0 )) {
  707. // hack, might cause problems... this tries to guess the start/end of a
  708. // non-extended strut; only works on windows that have exact same
  709. // geometry as their strut (ie, if the geometry fits the width
  710. // exactly, we will adjust length of strut to match the geometry as well;
  711. // otherwise we use the full-edge strut)
  712. if (stareaT.top() == geometry().top() && stareaT.bottom() == geometry().bottom()) {
  713. stareaT.setLeft(geometry().left());
  714. stareaT.setRight(geometry().right());
  715. // kdDebug () << "Trimming top-strut to geometry() to: " << stareaT << endl;
  716. }
  717. if (stareaB.top() == geometry().top() && stareaB.bottom() == geometry().bottom()) {
  718. stareaB.setLeft(geometry().left());
  719. stareaB.setRight(geometry().right());
  720. // kdDebug () << "Trimming bottom-strut to geometry(): " << stareaB << endl;
  721. }
  722. if (stareaL.left() == geometry().left() && stareaL.right() == geometry().right()) {
  723. stareaL.setTop(geometry().top());
  724. stareaL.setBottom(geometry().bottom());
  725. // kdDebug () << "Trimming left-strut to geometry(): " << stareaL << endl;
  726. }
  727. if (stareaR.left() == geometry().left() && stareaR.right() == geometry().right()) {
  728. stareaR.setTop(geometry().top());
  729. stareaR.setBottom(geometry().bottom());
  730. // kdDebug () << "Trimming right-strut to geometry(): " << stareaR << endl;
  731. }
  732. }
  733. TQRect screenarea = workspace()->clientArea( ScreenArea, this );
  734. // HACK: workarea handling is not xinerama aware, so if this strut
  735. // reserves place at a xinerama edge that's inside the virtual screen,
  736. // ignore the strut for workspace setting.
  737. if( area == kapp->desktop()->geometry())
  738. {
  739. if( stareaL.left() < screenarea.left())
  740. stareaL = TQRect();
  741. if( stareaR.right() > screenarea.right())
  742. stareaR = TQRect();
  743. if( stareaT.top() < screenarea.top())
  744. stareaT = TQRect();
  745. if( stareaB.bottom() < screenarea.bottom())
  746. stareaB = TQRect();
  747. }
  748. // Handle struts at xinerama edges that are inside the virtual screen.
  749. // They're given in virtual screen coordinates, make them affect only
  750. // their xinerama screen.
  751. stareaL.setLeft( KMAX( stareaL.left(), screenarea.left()));
  752. stareaR.setRight( KMIN( stareaR.right(), screenarea.right()));
  753. stareaT.setTop( KMAX( stareaT.top(), screenarea.top()));
  754. stareaB.setBottom( KMIN( stareaB.bottom(), screenarea.bottom()));
  755. if (stareaL . intersects (area)) {
  756. // kdDebug () << "Moving left of: " << r << " to " << stareaL.right() + 1 << endl;
  757. r . setLeft( stareaL . right() + 1 );
  758. }
  759. if (stareaR . intersects (area)) {
  760. // kdDebug () << "Moving right of: " << r << " to " << stareaR.left() - 1 << endl;
  761. r . setRight( stareaR . left() - 1 );
  762. }
  763. if (stareaT . intersects (area)) {
  764. // kdDebug () << "Moving top of: " << r << " to " << stareaT.bottom() + 1 << endl;
  765. r . setTop( stareaT . bottom() + 1 );
  766. }
  767. if (stareaB . intersects (area)) {
  768. // kdDebug () << "Moving bottom of: " << r << " to " << stareaB.top() - 1 << endl;
  769. r . setBottom( stareaB . top() - 1 );
  770. }
  771. return r;
  772. }
  773. NETExtendedStrut Client::strut() const
  774. {
  775. NETExtendedStrut ext = info->extendedStrut();
  776. NETStrut str = info->strut();
  777. if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
  778. && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 ))
  779. {
  780. // build extended from simple
  781. if( str.left != 0 )
  782. {
  783. ext.left_width = str.left;
  784. ext.left_start = 0;
  785. ext.left_end = XDisplayHeight( tqt_xdisplay(), DefaultScreen( tqt_xdisplay()));
  786. }
  787. if( str.right != 0 )
  788. {
  789. ext.right_width = str.right;
  790. ext.right_start = 0;
  791. ext.right_end = XDisplayHeight( tqt_xdisplay(), DefaultScreen( tqt_xdisplay()));
  792. }
  793. if( str.top != 0 )
  794. {
  795. ext.top_width = str.top;
  796. ext.top_start = 0;
  797. ext.top_end = XDisplayWidth( tqt_xdisplay(), DefaultScreen( tqt_xdisplay()));
  798. }
  799. if( str.bottom != 0 )
  800. {
  801. ext.bottom_width = str.bottom;
  802. ext.bottom_start = 0;
  803. ext.bottom_end = XDisplayWidth( tqt_xdisplay(), DefaultScreen( tqt_xdisplay()));
  804. }
  805. }
  806. return ext;
  807. }
  808. bool Client::hasStrut() const
  809. {
  810. NETExtendedStrut ext = strut();
  811. if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 )
  812. return false;
  813. return true;
  814. }
  815. // updates differences to workarea edges for all directions
  816. void Client::updateWorkareaDiffs()
  817. {
  818. TQRect area = workspace()->clientArea( WorkArea, this );
  819. TQRect geom = geometry();
  820. workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
  821. workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
  822. }
  823. // If the client was inside workarea in the x direction, and if it was close to the left/right
  824. // edge, return the distance from the left/right edge (negative for left, positive for right)
  825. // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
  826. // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
  827. // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
  828. // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
  829. // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
  830. // the y direction is done the same, just the values will be rotated: top->left, bottom->right
  831. int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
  832. {
  833. int left_diff = left - a_left;
  834. int right_diff = a_right - right;
  835. if( left_diff < 0 || right_diff < 0 )
  836. return INT_MIN;
  837. else // fully inside workarea in this direction direction
  838. {
  839. // max distance from edge where it's still considered to be close and is kept at that distance
  840. int max_diff = ( a_right - a_left ) / 10;
  841. if( left_diff < right_diff )
  842. return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
  843. else if( left_diff > right_diff )
  844. return right_diff < max_diff ? right_diff + 1 : INT_MAX;
  845. return INT_MAX; // not close to workarea edge
  846. }
  847. }
  848. void Client::checkWorkspacePosition()
  849. {
  850. if( isDesktop())
  851. {
  852. TQRect area = workspace()->clientArea( FullArea, this );
  853. if( geometry() != area )
  854. setGeometry( area );
  855. return;
  856. }
  857. if( isFullScreen())
  858. {
  859. TQRect area = workspace()->clientArea( FullScreenArea, this );
  860. if( geometry() != area )
  861. setGeometry( area );
  862. return;
  863. }
  864. if( isDock())
  865. return;
  866. if( isTopMenu())
  867. {
  868. if( workspace()->managingTopMenus())
  869. {
  870. TQRect area;
  871. ClientList mainclients = mainClients();
  872. if( mainclients.count() == 1 )
  873. area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
  874. else
  875. area = workspace()->clientArea( MaximizeFullArea, TQPoint( 0, 0 ), desktop());
  876. area.setHeight( workspace()->topMenuHeight());
  877. // kdDebug() << "TOPMENU size adjust: " << area << ":" << this << endl;
  878. setGeometry( area );
  879. }
  880. return;
  881. }
  882. if( maximizeMode() != MaximizeRestore )
  883. // TODO update geom_restore?
  884. changeMaximize( false, false, true ); // adjust size
  885. if( !isShade()) // TODO
  886. {
  887. int old_diff_x = workarea_diff_x;
  888. int old_diff_y = workarea_diff_y;
  889. updateWorkareaDiffs();
  890. // this can be true only if this window was mapped before KWin
  891. // was started - in such case, don't adjust position to workarea,
  892. // because the window already had its position, and if a window
  893. // with a strut altering the workarea would be managed in initialization
  894. // after this one, this window would be moved
  895. if( workspace()->initializing())
  896. return;
  897. TQRect area = workspace()->clientArea( WorkArea, this );
  898. TQRect new_geom = geometry();
  899. TQRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
  900. TQRect tmp_area_x( area.left(), 0, area.width(), 0 );
  901. checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
  902. // the x<->y swapping
  903. TQRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
  904. TQRect tmp_area_y( area.top(), 0, area.height(), 0 );
  905. checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
  906. new_geom = TQRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
  907. TQRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
  908. if( final_geom != new_geom ) // size increments, or size restrictions
  909. { // adjusted size differing matters only for right and bottom edge
  910. if( old_diff_x != INT_MAX && old_diff_x > 0 )
  911. final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
  912. if( old_diff_y != INT_MAX && old_diff_y > 0 )
  913. final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
  914. }
  915. if( final_geom != geometry() )
  916. setGeometry( final_geom );
  917. // updateWorkareaDiffs(); done already by setGeometry()
  918. }
  919. }
  920. // Try to be smart about keeping the clients visible.
  921. // If the client was fully inside the workspace before, try to keep
  922. // it still inside the workarea, possibly moving it or making it smaller if possible,
  923. // and try to keep the distance from the nearest workarea edge.
  924. // On the other hand, it it was partially moved outside of the workspace in some direction,
  925. // don't do anything with that direction if it's still at least partially visible. If it's
  926. // not visible anymore at all, make sure it's visible at least partially
  927. // again (not fully, as that could(?) be potentionally annoying) by
  928. // moving it slightly inside the workarea (those '+ 5').
  929. // Again, this is done for the x direction, y direction will be done by x<->y swapping
  930. void Client::checkDirection( int new_diff, int old_diff, TQRect& rect, const TQRect& area )
  931. {
  932. if( old_diff != INT_MIN ) // was inside workarea
  933. {
  934. if( old_diff == INT_MAX ) // was in workarea, but far from edge
  935. {
  936. if( new_diff == INT_MIN ) // is not anymore fully in workarea
  937. {
  938. rect.setLeft( area.left());
  939. rect.setRight( area.right());
  940. }
  941. return;
  942. }
  943. if( isMovable())
  944. {
  945. if( old_diff < 0 ) // was in left third, keep distance from left edge
  946. rect.moveLeft( area.left() + ( -old_diff - 1 ));
  947. else // old_diff > 0 // was in right third, keep distance from right edge
  948. rect.moveRight( area.right() - ( old_diff - 1 ));
  949. }
  950. else if( isResizable())
  951. {
  952. if( old_diff < 0 )
  953. rect.setLeft( area.left() + ( -old_diff - 1 ) );
  954. else // old_diff > 0
  955. rect.setRight( area.right() - ( old_diff - 1 ));
  956. }
  957. if( rect.width() > area.width() && isResizable())
  958. rect.setWidth( area.width());
  959. if( isMovable())
  960. {
  961. if( rect.left() < area.left())
  962. rect.moveLeft( area.left());
  963. else if( rect.right() > area.right())
  964. rect.moveRight( area.right());
  965. }
  966. }
  967. if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
  968. { // not visible (almost) at all - try to make it at least partially visible
  969. if( isMovable())
  970. {
  971. if( rect.left() < area.left() + 5 )
  972. rect.moveRight( area.left() + 5 );
  973. if( rect.right() > area.right() - 5 )
  974. rect.moveLeft( area.right() - 5 );
  975. }
  976. }
  977. if (!moveResizeMode && options->shadowEnabled(isActive()))
  978. {
  979. // If the user is manually resizing, let Client::leaveMoveResize()
  980. // decide when to redraw the shadow
  981. removeShadow();
  982. drawIntersectingShadows();
  983. if (options->shadowEnabled(isActive()))
  984. drawDelayedShadow();
  985. }
  986. }
  987. /*!
  988. Adjust the frame size \a frame according to he window's size hints.
  989. */
  990. TQSize Client::adjustedSize( const TQSize& frame, Sizemode mode ) const
  991. {
  992. // first, get the window size for the given frame size s
  993. TQSize wsize( frame.width() - ( border_left + border_right ),
  994. frame.height() - ( border_top + border_bottom ));
  995. if( wsize.isEmpty())
  996. wsize = TQSize( 1, 1 );
  997. return sizeForClientSize( wsize, mode, false );
  998. }
  999. // this helper returns proper size even if the window is shaded
  1000. // see also the comment in Client::setGeometry()
  1001. TQSize Client::adjustedSize() const
  1002. {
  1003. return sizeForClientSize( clientSize());
  1004. }
  1005. /*!
  1006. Calculate the appropriate frame size for the given client size \a
  1007. wsize.
  1008. \a wsize is adapted according to the window's size hints (minimum,
  1009. maximum and incremental size changes).
  1010. */
  1011. TQSize Client::sizeForClientSize( const TQSize& wsize, Sizemode mode, bool noframe ) const
  1012. {
  1013. int w = wsize.width();
  1014. int h = wsize.height();
  1015. if( w < 1 || h < 1 )
  1016. {
  1017. kdWarning() << "sizeForClientSize() with empty size!" << endl;
  1018. kdWarning() << kdBacktrace() << endl;
  1019. }
  1020. if (w<1) w = 1;
  1021. if (h<1) h = 1;
  1022. // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
  1023. // even if they're not set in flags - see getWmNormalHints()
  1024. TQSize min_size = minSize();
  1025. TQSize max_size = maxSize();
  1026. if( decoration != NULL )
  1027. {
  1028. TQSize decominsize = decoration->minimumSize();
  1029. TQSize border_size( border_left + border_right, border_top + border_bottom );
  1030. if( border_size.width() > decominsize.width()) // just in case
  1031. decominsize.setWidth( border_size.width());
  1032. if( border_size.height() > decominsize.height())
  1033. decominsize.setHeight( border_size.height());
  1034. if( decominsize.width() > min_size.width())
  1035. min_size.setWidth( decominsize.width());
  1036. if( decominsize.height() > min_size.height())
  1037. min_size.setHeight( decominsize.height());
  1038. }
  1039. w = QMIN( max_size.width(), w );
  1040. h = QMIN( max_size.height(), h );
  1041. w = QMAX( min_size.width(), w );
  1042. h = QMAX( min_size.height(), h );
  1043. int w1 = w;
  1044. int h1 = h;
  1045. int width_inc = xSizeHint.width_inc;
  1046. int height_inc = xSizeHint.height_inc;
  1047. int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
  1048. int baseh_inc = xSizeHint.min_height;
  1049. w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
  1050. h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
  1051. // code for aspect ratios based on code from FVWM
  1052. /*
  1053. * The math looks like this:
  1054. *
  1055. * minAspectX dwidth maxAspectX
  1056. * ---------- <= ------- <= ----------
  1057. * minAspectY dheight maxAspectY
  1058. *
  1059. * If that is multiplied out, then the width and height are
  1060. * invalid in the following situations:
  1061. *
  1062. * minAspectX * dheight > minAspectY * dwidth
  1063. * maxAspectX * dheight < maxAspectY * dwidth
  1064. *
  1065. */
  1066. if( xSizeHint.flags & PAspect )
  1067. {
  1068. double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
  1069. double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
  1070. double max_aspect_w = xSizeHint.max_aspect.x;
  1071. double max_aspect_h = xSizeHint.max_aspect.y;
  1072. // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
  1073. // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
  1074. // and I have no idea how it works, let's hope nobody relies on that.
  1075. w -= xSizeHint.base_width;
  1076. h -= xSizeHint.base_height;
  1077. int max_width = max_size.width() - xSizeHint.base_width;
  1078. int min_width = min_size.width() - xSizeHint.base_width;
  1079. int max_height = max_size.height() - xSizeHint.base_height;
  1080. int min_height = min_size.height() - xSizeHint.base_height;
  1081. #define ASPECT_CHECK_GROW_W \
  1082. if( min_aspect_w * h > min_aspect_h * w ) \
  1083. { \
  1084. int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
  1085. if( w + delta <= max_width ) \
  1086. w += delta; \
  1087. }
  1088. #define ASPECT_CHECK_SHRINK_H_GROW_W \
  1089. if( min_aspect_w * h > min_aspect_h * w ) \
  1090. { \
  1091. int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
  1092. if( h - delta >= min_height ) \
  1093. h -= delta; \
  1094. else \
  1095. { \
  1096. int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
  1097. if( w + delta <= max_width ) \
  1098. w += delta; \
  1099. } \
  1100. }
  1101. #define ASPECT_CHECK_GROW_H \
  1102. if( max_aspect_w * h < max_aspect_h * w ) \
  1103. { \
  1104. int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
  1105. if( h + delta <= max_height ) \
  1106. h += delta; \
  1107. }
  1108. #define ASPECT_CHECK_SHRINK_W_GROW_H \
  1109. if( max_aspect_w * h < max_aspect_h * w ) \
  1110. { \
  1111. int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
  1112. if( w - delta >= min_width ) \
  1113. w -= delta; \
  1114. else \
  1115. { \
  1116. int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
  1117. if( h + delta <= max_height ) \
  1118. h += delta; \
  1119. } \
  1120. }
  1121. switch( mode )
  1122. {
  1123. case SizemodeAny:
  1124. #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
  1125. // so that changing aspect ratio to a different value and back keeps the same size (#87298)
  1126. {
  1127. ASPECT_CHECK_SHRINK_H_GROW_W
  1128. ASPECT_CHECK_SHRINK_W_GROW_H
  1129. ASPECT_CHECK_GROW_H
  1130. ASPECT_CHECK_GROW_W
  1131. break;
  1132. }
  1133. #endif
  1134. case SizemodeFixedW:
  1135. {
  1136. // the checks are order so that attempts to modify height are first
  1137. ASPECT_CHECK_GROW_H
  1138. ASPECT_CHECK_SHRINK_H_GROW_W
  1139. ASPECT_CHECK_SHRINK_W_GROW_H
  1140. ASPECT_CHECK_GROW_W
  1141. break;
  1142. }
  1143. case SizemodeFixedH:
  1144. {
  1145. ASPECT_CHECK_GROW_W
  1146. ASPECT_CHECK_SHRINK_W_GROW_H
  1147. ASPECT_CHECK_SHRINK_H_GROW_W
  1148. ASPECT_CHECK_GROW_H
  1149. break;
  1150. }
  1151. case SizemodeMax:
  1152. {
  1153. // first checks that try to shrink
  1154. ASPECT_CHECK_SHRINK_H_GROW_W
  1155. ASPECT_CHECK_SHRINK_W_GROW_H
  1156. ASPECT_CHECK_GROW_W
  1157. ASPECT_CHECK_GROW_H
  1158. break;
  1159. }
  1160. }
  1161. #undef ASPECT_CHECK_SHRINK_H_GROW_W
  1162. #undef ASPECT_CHECK_SHRINK_W_GROW_H
  1163. #undef ASPECT_CHECK_GROW_W
  1164. #undef ASPECT_CHECK_GROW_H
  1165. w += xSizeHint.base_width;
  1166. h += xSizeHint.base_height;
  1167. }
  1168. if( !rules()->checkStrictGeometry( false ))
  1169. {
  1170. // disobey increments and aspect when maximized
  1171. if( maximizeMode() & MaximizeHorizontal )
  1172. w = w1;
  1173. if( maximizeMode() & MaximizeVertical )
  1174. h = h1;
  1175. }
  1176. if( !noframe )
  1177. {
  1178. w += border_left + border_right;
  1179. h += border_top + border_bottom;
  1180. }
  1181. return rules()->checkSize( TQSize( w, h ));
  1182. }
  1183. /*!
  1184. Gets the client's normal WM hints and reconfigures itself respectively.
  1185. */
  1186. void Client::getWmNormalHints()
  1187. {
  1188. long msize;
  1189. if (XGetWMNormalHints(tqt_xdisplay(), window(), &xSizeHint, &msize) == 0 )
  1190. xSizeHint.flags = 0;
  1191. // set defined values for the fields, even if they're not in flags
  1192. if( ! ( xSizeHint.flags & PMinSize ))
  1193. xSizeHint.min_width = xSizeHint.min_height = 0;
  1194. if( xSizeHint.flags & PBaseSize )
  1195. {
  1196. // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
  1197. // The other way around PMinSize is not a complete fallback for PBaseSize,
  1198. // so that's not handled here.
  1199. if( ! ( xSizeHint.flags & PMinSize ))
  1200. {
  1201. xSizeHint.min_width = xSizeHint.base_width;
  1202. xSizeHint.min_height = xSizeHint.base_height;
  1203. }
  1204. }
  1205. else
  1206. xSizeHint.base_width = xSizeHint.base_height = 0;
  1207. if( ! ( xSizeHint.flags & PMaxSize ))
  1208. xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
  1209. else
  1210. {
  1211. xSizeHint.max_width = QMAX( xSizeHint.max_width, 1 );
  1212. xSizeHint.max_height = QMAX( xSizeHint.max_height, 1 );
  1213. }
  1214. if( xSizeHint.flags & PResizeInc )
  1215. {
  1216. xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 );
  1217. xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 );
  1218. }
  1219. else
  1220. {
  1221. xSizeHint.width_inc = 1;
  1222. xSizeHint.height_inc = 1;
  1223. }
  1224. if( xSizeHint.flags & PAspect )
  1225. { // no dividing by zero
  1226. xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 );
  1227. xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 );
  1228. }
  1229. else
  1230. {
  1231. xSizeHint.min_aspect.x = 1;
  1232. xSizeHint.min_aspect.y = INT_MAX;
  1233. xSizeHint.max_aspect.x = INT_MAX;
  1234. xSizeHint.max_aspect.y = 1;
  1235. }
  1236. if( ! ( xSizeHint.flags & PWinGravity ))
  1237. xSizeHint.win_gravity = NorthWestGravity;
  1238. if( isManaged())
  1239. { // update to match restrictions
  1240. TQSize new_size = adjustedSize();
  1241. if( new_size != size() && !isFullScreen())
  1242. {
  1243. TQRect orig_geometry = geometry();
  1244. resizeWithChecks( new_size );
  1245. if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
  1246. {
  1247. // try to keep the window in its xinerama screen if possible,
  1248. // if that fails at least keep it visible somewhere
  1249. TQRect area = workspace()->clientArea( MovementArea, this );
  1250. if( area.contains( orig_geometry ))
  1251. keepInArea( area );
  1252. area = workspace()->clientArea( WorkArea, this );
  1253. if( area.contains( orig_geometry ))
  1254. keepInArea( area );
  1255. }
  1256. }
  1257. }
  1258. updateAllowedActions(); // affects isResizeable()
  1259. }
  1260. TQSize Client::minSize() const
  1261. {
  1262. return rules()->checkMinSize( TQSize( xSizeHint.min_width, xSizeHint.min_height ));
  1263. }
  1264. TQSize Client::maxSize() const
  1265. {
  1266. return rules()->checkMaxSize( TQSize( xSizeHint.max_width, xSizeHint.max_height ));
  1267. }
  1268. /*!
  1269. Auxiliary function to inform the client about the current window
  1270. configuration.
  1271. */
  1272. void Client::sendSyntheticConfigureNotify()
  1273. {
  1274. XConfigureEvent c;
  1275. c.type = ConfigureNotify;
  1276. c.send_event = True;
  1277. c.event = window();
  1278. c.window = window();
  1279. c.x = x() + clientPos().x();
  1280. c.y = y() + clientPos().y();
  1281. c.width = clientSize().width();
  1282. c.height = clientSize().height();
  1283. c.border_width = 0;
  1284. c.above = None;
  1285. c.override_redirect = 0;
  1286. XSendEvent( tqt_xdisplay(), c.event, TRUE, StructureNotifyMask, (XEvent*)&c );
  1287. }
  1288. const TQPoint Client::calculateGravitation( bool invert, int gravity ) const
  1289. {
  1290. int dx, dy;
  1291. dx = dy = 0;
  1292. if( gravity == 0 ) // default (nonsense) value for the argument
  1293. gravity = xSizeHint.win_gravity;
  1294. // dx, dy specify how the client window moves to make space for the frame
  1295. switch (gravity)
  1296. {
  1297. case NorthWestGravity: // move down right
  1298. default:
  1299. dx = border_left;
  1300. dy = border_top;
  1301. break;
  1302. case NorthGravity: // move right
  1303. dx = 0;
  1304. dy = border_top;
  1305. break;
  1306. case NorthEastGravity: // move down left
  1307. dx = -border_right;
  1308. dy = border_top;
  1309. break;
  1310. case WestGravity: // move right
  1311. dx = border_left;
  1312. dy = 0;
  1313. break;
  1314. case CenterGravity:
  1315. break; // will be handled specially
  1316. case StaticGravity: // don't move
  1317. dx = 0;
  1318. dy = 0;
  1319. break;
  1320. case EastGravity: // move left
  1321. dx = -border_right;
  1322. dy = 0;
  1323. break;
  1324. case SouthWestGravity: // move up right
  1325. dx = border_left ;
  1326. dy = -border_bottom;
  1327. break;
  1328. case SouthGravity: // move up
  1329. dx = 0;
  1330. dy = -border_bottom;
  1331. break;
  1332. case SouthEastGravity: // move up left
  1333. dx = -border_right;
  1334. dy = -border_bottom;
  1335. break;
  1336. }
  1337. if( gravity != CenterGravity )
  1338. { // translate from client movement to frame movement
  1339. dx -= border_left;
  1340. dy -= border_top;
  1341. }
  1342. else
  1343. { // center of the frame will be at the same position client center without frame would be
  1344. dx = - ( border_left + border_right ) / 2;
  1345. dy = - ( border_top + border_bottom ) / 2;
  1346. }
  1347. if( !invert )
  1348. return TQPoint( x() + dx, y() + dy );
  1349. else
  1350. return TQPoint( x() - dx, y() - dy );
  1351. }
  1352. void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
  1353. {
  1354. if( gravity == 0 ) // default (nonsense) value for the argument
  1355. gravity = xSizeHint.win_gravity;
  1356. if( value_mask & ( CWX | CWY ))
  1357. {
  1358. TQPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
  1359. if ( value_mask & CWX )
  1360. new_pos.setX( rx );
  1361. if ( value_mask & CWY )
  1362. new_pos.setY( ry );
  1363. // clever(?) workaround for applications like xv that want to set
  1364. // the location to the current location but miscalculate the
  1365. // frame size due to twin being a double-reparenting window
  1366. // manager
  1367. if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
  1368. && gravity == NorthWestGravity && !from_tool )
  1369. {
  1370. new_pos.setX( x());
  1371. new_pos.setY( y());
  1372. }
  1373. int nw = clientSize().width();
  1374. int nh = clientSize().height();
  1375. if ( value_mask & CWWidth )
  1376. nw = rw;
  1377. if ( value_mask & CWHeight )
  1378. nh = rh;
  1379. TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
  1380. new_pos = rules()->checkPosition( new_pos );
  1381. // TODO what to do with maximized windows?
  1382. if ( maximizeMode() != MaximizeFull
  1383. || ns != size())
  1384. {
  1385. TQRect orig_geometry = geometry();
  1386. GeometryUpdatesPostponer blocker( this );
  1387. move( new_pos );
  1388. plainResize( ns );
  1389. setGeometry( TQRect( calculateGravitation( false, gravity ), size()));
  1390. updateFullScreenHack( TQRect( new_pos, TQSize( nw, nh )));
  1391. TQRect area = workspace()->clientArea( WorkArea, this );
  1392. if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
  1393. && area.contains( orig_geometry ))
  1394. keepInArea( area );
  1395. // this is part of the kicker-xinerama-hack... it should be
  1396. // safe to remove when kicker gets proper ExtendedStrut support;
  1397. // see Workspace::updateClientArea() and
  1398. // Client::adjustedClientArea()
  1399. if (hasStrut ())
  1400. workspace() -> updateClientArea ();
  1401. }
  1402. }
  1403. if ( value_mask & (CWWidth | CWHeight )
  1404. && ! ( value_mask & ( CWX | CWY )) ) // pure resize
  1405. {
  1406. int nw = clientSize().width();
  1407. int nh = clientSize().height();
  1408. if ( value_mask & CWWidth )
  1409. nw = rw;
  1410. if ( value_mask & CWHeight )
  1411. nh = rh;
  1412. TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
  1413. if( ns != size()) // don't restore if some app sets its own size again
  1414. {
  1415. TQRect orig_geometry = geometry();
  1416. GeometryUpdatesPostponer blocker( this );
  1417. int save_gravity = xSizeHint.win_gravity;
  1418. xSizeHint.win_gravity = gravity;
  1419. resizeWithChecks( ns );
  1420. xSizeHint.win_gravity = save_gravity;
  1421. updateFullScreenHack( TQRect( calculateGravitation( true, xSizeHint.win_gravity ), TQSize( nw, nh )));
  1422. if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
  1423. {
  1424. // try to keep the window in its xinerama screen if possible,
  1425. // if that fails at least keep it visible somewhere
  1426. TQRect area = workspace()->clientArea( MovementArea, this );
  1427. if( area.contains( orig_geometry ))
  1428. keepInArea( area );
  1429. area = workspace()->clientArea( WorkArea, this );
  1430. if( area.contains( orig_geometry ))
  1431. keepInArea( area );
  1432. }
  1433. }
  1434. }
  1435. // No need to send synthetic configure notify event here, either it's sent together
  1436. // with geometry change, or there's no need to send it.
  1437. // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
  1438. }
  1439. void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
  1440. {
  1441. if( shade_geometry_change )
  1442. assert( false );
  1443. else if( isShade())
  1444. {
  1445. if( h == border_top + border_bottom )
  1446. {
  1447. kdWarning() << "Shaded geometry passed for size:" << endl;
  1448. kdWarning() << kdBacktrace() << endl;
  1449. }
  1450. }
  1451. int newx = x();
  1452. int newy = y();
  1453. TQRect area = workspace()->clientArea( WorkArea, this );
  1454. // don't allow growing larger than workarea
  1455. if( w > area.width())
  1456. w = area.width();
  1457. if( h > area.height())
  1458. h = area.height();
  1459. TQSize tmp = adjustedSize( TQSize( w, h )); // checks size constraints, including min/max size
  1460. w = tmp.width();
  1461. h = tmp.height();
  1462. switch( xSizeHint.win_gravity )
  1463. {
  1464. case NorthWestGravity: // top left corner doesn't move
  1465. default:
  1466. break;
  1467. case NorthGravity: // middle of top border doesn't move
  1468. newx = ( newx + width() / 2 ) - ( w / 2 );
  1469. break;
  1470. case NorthEastGravity: // top right corner doesn't move
  1471. newx = newx + width() - w;
  1472. break;
  1473. case WestGravity: // middle of left border doesn't move
  1474. newy = ( newy + height() / 2 ) - ( h / 2 );
  1475. break;
  1476. case CenterGravity: // middle point doesn't move
  1477. newx = ( newx + width() / 2 ) - ( w / 2 );
  1478. newy = ( newy + height() / 2 ) - ( h / 2 );
  1479. break;
  1480. case StaticGravity: // top left corner of _client_ window doesn't move
  1481. // since decoration doesn't change, equal to NorthWestGravity
  1482. break;
  1483. case EastGravity: // // middle of right border doesn't move
  1484. newx = newx + width() - w;
  1485. newy = ( newy + height() / 2 ) - ( h / 2 );
  1486. break;
  1487. case SouthWestGravity: // bottom left corner doesn't move
  1488. newy = newy + height() - h;
  1489. break;
  1490. case SouthGravity: // middle of bottom border doesn't move
  1491. newx = ( newx + width() / 2 ) - ( w / 2 );
  1492. newy = newy + height() - h;
  1493. break;
  1494. case SouthEastGravity: // bottom right corner doesn't move
  1495. newx = newx + width() - w;
  1496. newy = newy + height() - h;
  1497. break;
  1498. }
  1499. // if it would be moved outside of workarea, keep it inside,
  1500. // see also Client::computeWorkareaDiff()
  1501. if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
  1502. {
  1503. if( newx < area.left())
  1504. newx = area.left();
  1505. if( newx + w > area.right() + 1 )
  1506. newx = area.right() + 1 - w;
  1507. assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
  1508. }
  1509. if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
  1510. {
  1511. if( newy < area.top())
  1512. newy = area.top();
  1513. if( newy + h > area.bottom() + 1 )
  1514. newy = area.bottom() + 1 - h;
  1515. assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
  1516. }
  1517. setGeometry( newx, newy, w, h, force );
  1518. }
  1519. // _NET_MOVERESIZE_WINDOW
  1520. void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
  1521. {
  1522. int gravity = flags & 0xff;
  1523. int value_mask = 0;
  1524. if( flags & ( 1 << 8 ))
  1525. value_mask |= CWX;
  1526. if( flags & ( 1 << 9 ))
  1527. value_mask |= CWY;
  1528. if( flags & ( 1 << 10 ))
  1529. value_mask |= CWWidth;
  1530. if( flags & ( 1 << 11 ))
  1531. value_mask |= CWHeight;
  1532. configureRequest( value_mask, x, y, width, height, gravity, true );
  1533. }
  1534. /*!
  1535. Returns whether the window is moveable or has a fixed
  1536. position.
  1537. */
  1538. bool Client::isMovable() const
  1539. {
  1540. if( !motif_may_move || isFullScreen())
  1541. return false;
  1542. if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
  1543. return false;
  1544. if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
  1545. return false;
  1546. if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
  1547. return false;
  1548. return true;
  1549. }
  1550. /*!
  1551. Returns whether the window is resizable or has a fixed size.
  1552. */
  1553. bool Client::isResizable() const
  1554. {
  1555. if( !motif_may_resize || isFullScreen())
  1556. return false;
  1557. if( isSpecialWindow() )
  1558. return false;
  1559. if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
  1560. return false;
  1561. if( rules()->checkSize( TQSize()).isValid()) // forced size
  1562. return false;
  1563. TQSize min = minSize();
  1564. TQSize max = maxSize();
  1565. return min.width() < max.width() || min.height() < max.height();
  1566. }
  1567. /*
  1568. Returns whether the window is maximizable or not
  1569. */
  1570. bool Client::isMaximizable() const
  1571. {
  1572. if( isModalSystemNotification())
  1573. return false;
  1574. { // isMovable() and isResizable() may be false for maximized windows
  1575. // with moving/resizing maximized windows disabled
  1576. TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
  1577. if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
  1578. return false;
  1579. }
  1580. if ( maximizeMode() != MaximizeRestore )
  1581. return TRUE;
  1582. TQSize max = maxSize();
  1583. #if 0
  1584. if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
  1585. return false;
  1586. #else
  1587. // apparently there are enough apps which specify some arbitrary value
  1588. // for their maximum size just for the fun of it
  1589. TQSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
  1590. if( max.width() < areasize.width() || max.height() < areasize.height())
  1591. return false;
  1592. #endif
  1593. return true;
  1594. }
  1595. /*!
  1596. Reimplemented to inform the client about the new window position.
  1597. */
  1598. void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
  1599. {
  1600. // this code is also duplicated in Client::plainResize()
  1601. // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
  1602. // simply because there are too many places dealing with geometry. Those places
  1603. // ignore shaded state and use normal geometry, which they usually should get
  1604. // from adjustedSize(). Such geometry comes here, and if the window is shaded,
  1605. // the geometry is used only for client_size, since that one is not used when
  1606. // shading. Then the frame geometry is adjusted for the shaded geometry.
  1607. // This gets more complicated in the case the code does only something like
  1608. // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
  1609. // Such code is wrong and should be changed to handle the case when the window is shaded,
  1610. // for example using Client::clientSize().
  1611. if( shade_geometry_change )
  1612. ; // nothing
  1613. else if( isShade())
  1614. {
  1615. if( h == border_top + border_bottom )
  1616. {
  1617. kdDebug() << "Shaded geometry passed for size:" << endl;
  1618. kdDebug() << kdBacktrace() << endl;
  1619. }
  1620. else
  1621. {
  1622. client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
  1623. h = border_top + border_bottom;
  1624. }
  1625. }
  1626. else
  1627. {
  1628. client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
  1629. }
  1630. if( force == NormalGeometrySet && frame_geometry == TQRect( x, y, w, h ))
  1631. return;
  1632. frame_geometry = TQRect( x, y, w, h );
  1633. updateWorkareaDiffs();
  1634. if( postpone_geometry_updates != 0 )
  1635. {
  1636. pending_geometry_update = true;
  1637. return;
  1638. }
  1639. resizeDecoration( TQSize( w, h ));
  1640. XMoveResizeWindow( tqt_xdisplay(), frameId(), x, y, w, h );
  1641. // resizeDecoration( TQSize( w, h ));
  1642. if( !isShade())
  1643. {
  1644. TQSize cs = clientSize();
  1645. XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
  1646. cs.width(), cs.height());
  1647. XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
  1648. }
  1649. updateShape();
  1650. // SELI TODO won't this be too expensive?
  1651. updateWorkareaDiffs();
  1652. sendSyntheticConfigureNotify();
  1653. updateWindowRules();
  1654. checkMaximizeGeometry();
  1655. workspace()->checkActiveScreen( this );
  1656. }
  1657. void Client::plainResize( int w, int h, ForceGeometry_t force )
  1658. {
  1659. // this code is also duplicated in Client::setGeometry(), and it's also commented there
  1660. if( shade_geometry_change )
  1661. ; // nothing
  1662. else if( isShade())
  1663. {
  1664. if( h == border_top + border_bottom )
  1665. {
  1666. kdDebug() << "Shaded geometry passed for size:" << endl;
  1667. kdDebug() << kdBacktrace() << endl;
  1668. }
  1669. else
  1670. {
  1671. client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
  1672. h = border_top + border_bottom;
  1673. }
  1674. }
  1675. else
  1676. {
  1677. client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
  1678. }
  1679. if( TQSize( w, h ) != rules()->checkSize( TQSize( w, h )))
  1680. {
  1681. kdDebug() << "forced size fail:" << TQSize( w,h ) << ":" << rules()->checkSize( TQSize( w, h )) << endl;
  1682. kdDebug() << kdBacktrace() << endl;
  1683. }
  1684. if( force == NormalGeometrySet && frame_geometry.size() == TQSize( w, h ))
  1685. return;
  1686. frame_geometry.setSize( TQSize( w, h ));
  1687. updateWorkareaDiffs();
  1688. if( postpone_geometry_updates != 0 )
  1689. {
  1690. pending_geometry_update = true;
  1691. return;
  1692. }
  1693. resizeDecoration( TQSize( w, h ));
  1694. XResizeWindow( tqt_xdisplay(), frameId(), w, h );
  1695. // resizeDecoration( TQSize( w, h ));
  1696. if( !isShade())
  1697. {
  1698. TQSize cs = clientSize();
  1699. XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
  1700. cs.width(), cs.height());
  1701. XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
  1702. }
  1703. updateShape();
  1704. updateWorkareaDiffs();
  1705. sendSyntheticConfigureNotify();
  1706. updateWindowRules();
  1707. checkMaximizeGeometry();
  1708. workspace()->checkActiveScreen( this );
  1709. }
  1710. /*!
  1711. Reimplemented to inform the client about the new window position.
  1712. */
  1713. void Client::move( int x, int y, ForceGeometry_t force )
  1714. {
  1715. if( force == NormalGeometrySet && frame_geometry.topLeft() == TQPoint( x, y ))
  1716. return;
  1717. frame_geometry.moveTopLeft( TQPoint( x, y ));
  1718. updateWorkareaDiffs();
  1719. if( postpone_geometry_updates != 0 )
  1720. {
  1721. pending_geometry_update = true;
  1722. return;
  1723. }
  1724. XMoveWindow( tqt_xdisplay(), frameId(), x, y );
  1725. sendSyntheticConfigureNotify();
  1726. updateWindowRules();
  1727. checkMaximizeGeometry();
  1728. workspace()->checkActiveScreen( this );
  1729. }
  1730. void Client::postponeGeometryUpdates( bool postpone )
  1731. {
  1732. if( postpone )
  1733. {
  1734. if( postpone_geometry_updates == 0 )
  1735. pending_geometry_update = false;
  1736. ++postpone_geometry_updates;
  1737. }
  1738. else
  1739. {
  1740. if( --postpone_geometry_updates == 0 )
  1741. {
  1742. if( pending_geometry_update )
  1743. {
  1744. if( isShade())
  1745. setGeometry( TQRect( pos(), adjustedSize()), ForceGeometrySet );
  1746. else
  1747. setGeometry( geometry(), ForceGeometrySet );
  1748. pending_geometry_update = false;
  1749. }
  1750. }
  1751. }
  1752. }
  1753. void Client::maximize( MaximizeMode m )
  1754. {
  1755. setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
  1756. }
  1757. /*!
  1758. Sets the maximization according to \a vertically and \a horizontally
  1759. */
  1760. void Client::setMaximize( bool vertically, bool horizontally )
  1761. { // changeMaximize() flips the state, so change from set->flip
  1762. changeMaximize(
  1763. max_mode & MaximizeVertical ? !vertically : vertically,
  1764. max_mode & MaximizeHorizontal ? !horizontally : horizontally,
  1765. false );
  1766. }
  1767. void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
  1768. {
  1769. if( !isMaximizable())
  1770. return;
  1771. MaximizeMode old_mode = max_mode;
  1772. // 'adjust == true' means to update the size only, e.g. after changing workspace size
  1773. if( !adjust )
  1774. {
  1775. if( vertical )
  1776. max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
  1777. if( horizontal )
  1778. max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
  1779. }
  1780. max_mode = rules()->checkMaximize( max_mode );
  1781. if( !adjust && max_mode == old_mode )
  1782. return;
  1783. GeometryUpdatesPostponer blocker( this );
  1784. // maximing one way and unmaximizing the other way shouldn't happen
  1785. Q_ASSERT( !( vertical && horizontal )
  1786. || ((( max_mode & MaximizeVertical ) != 0 ) == (( max_mode & MaximizeHorizontal ) != 0 )));
  1787. TQRect clientArea = workspace()->clientArea( MaximizeArea, this );
  1788. // save sizes for restoring, if maximalizing
  1789. if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
  1790. {
  1791. geom_restore.setTop( y());
  1792. geom_restore.setHeight( height());
  1793. }
  1794. if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
  1795. {
  1796. geom_restore.setLeft( x());
  1797. geom_restore.setWidth( width());
  1798. }
  1799. if( !adjust )
  1800. {
  1801. if(( vertical && !(old_mode & MaximizeVertical ))
  1802. || ( horizontal && !( old_mode & MaximizeHorizontal )))
  1803. Notify::raise( Notify::Maximize );
  1804. else
  1805. Notify::raise( Notify::UnMaximize );
  1806. }
  1807. if( decoration != NULL ) // decorations may turn off some borders when maximized
  1808. decoration->borders( border_left, border_right, border_top, border_bottom );
  1809. // restore partial maximizations
  1810. if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
  1811. {
  1812. if ( maximizeModeRestore()==MaximizeVertical )
  1813. {
  1814. max_mode = MaximizeVertical;
  1815. maxmode_restore = MaximizeRestore;
  1816. }
  1817. if ( maximizeModeRestore()==MaximizeHorizontal )
  1818. {
  1819. max_mode = MaximizeHorizontal;
  1820. maxmode_restore = MaximizeRestore;
  1821. }
  1822. }
  1823. switch (max_mode)
  1824. {
  1825. case MaximizeVertical:
  1826. {
  1827. if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
  1828. {
  1829. if( geom_restore.width() == 0 )
  1830. { // needs placement
  1831. plainResize( adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH ));
  1832. workspace()->placeSmart( this, clientArea );
  1833. }
  1834. else
  1835. setGeometry( TQRect(TQPoint( geom_restore.x(), clientArea.top()),
  1836. adjustedSize(TQSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
  1837. }
  1838. else
  1839. setGeometry( TQRect(TQPoint(x(), clientArea.top()),
  1840. adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
  1841. info->setState( NET::MaxVert, NET::Max );
  1842. break;
  1843. }
  1844. case MaximizeHorizontal:
  1845. {
  1846. if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
  1847. {
  1848. if( geom_restore.height() == 0 )
  1849. { // needs placement
  1850. plainResize( adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW ));
  1851. workspace()->placeSmart( this, clientArea );
  1852. }
  1853. else
  1854. setGeometry( TQRect( TQPoint(clientArea.left(), geom_restore.y()),
  1855. adjustedSize(TQSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), ForceGeometrySet);
  1856. }
  1857. else
  1858. setGeometry( TQRect( TQPoint(clientArea.left(), y()),
  1859. adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW )), ForceGeometrySet);
  1860. info->setState( NET::MaxHoriz, NET::Max );
  1861. break;
  1862. }
  1863. case MaximizeRestore:
  1864. {
  1865. TQRect restore = geometry();
  1866. // when only partially maximized, geom_restore may not have the other dimension remembered
  1867. if( old_mode & MaximizeVertical )
  1868. {
  1869. restore.setTop( geom_restore.top());
  1870. restore.setBottom( geom_restore.bottom());
  1871. }
  1872. if( old_mode & MaximizeHorizontal )
  1873. {
  1874. restore.setLeft( geom_restore.left());
  1875. restore.setRight( geom_restore.right());
  1876. }
  1877. if( !restore.isValid())
  1878. {
  1879. TQSize s = TQSize( clientArea.width()*2/3, clientArea.height()*2/3 );
  1880. if( geom_restore.width() > 0 )
  1881. s.setWidth( geom_restore.width());
  1882. if( geom_restore.height() > 0 )
  1883. s.setHeight( geom_restore.height());
  1884. plainResize( adjustedSize( s ));
  1885. workspace()->placeSmart( this, clientArea );
  1886. restore = geometry();
  1887. if( geom_restore.width() > 0 )
  1888. restore.moveLeft( geom_restore.x());
  1889. if( geom_restore.height() > 0 )
  1890. restore.moveTop( geom_restore.y());
  1891. }
  1892. setGeometry( restore, ForceGeometrySet );
  1893. info->setState( 0, NET::Max );
  1894. break;
  1895. }
  1896. case MaximizeFull:
  1897. {
  1898. if( !adjust )
  1899. {
  1900. if( old_mode & MaximizeVertical )
  1901. maxmode_restore = MaximizeVertical;
  1902. if( old_mode & MaximizeHorizontal )
  1903. maxmode_restore = MaximizeHorizontal;
  1904. }
  1905. TQSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
  1906. TQRect r = TQRect(clientArea.topLeft(), adjSize);
  1907. setGeometry( r, ForceGeometrySet );
  1908. info->setState( NET::Max, NET::Max );
  1909. break;
  1910. }
  1911. default:
  1912. break;
  1913. }
  1914. updateAllowedActions();
  1915. if( decoration != NULL )
  1916. decoration->maximizeChange();
  1917. updateWindowRules();
  1918. }
  1919. void Client::resetMaximize()
  1920. {
  1921. if( max_mode == MaximizeRestore )
  1922. return;
  1923. max_mode = MaximizeRestore;
  1924. Notify::raise( Notify::UnMaximize );
  1925. info->setState( 0, NET::Max );
  1926. updateAllowedActions();
  1927. if( decoration != NULL )
  1928. decoration->borders( border_left, border_right, border_top, border_bottom );
  1929. if( isShade())
  1930. setGeometry( TQRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
  1931. else
  1932. setGeometry( geometry(), ForceGeometrySet );
  1933. if( decoration != NULL )
  1934. decoration->maximizeChange();
  1935. }
  1936. void Client::checkMaximizeGeometry()
  1937. {
  1938. // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
  1939. // when after the condition is no longer true
  1940. if( isShade())
  1941. return;
  1942. if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
  1943. return;
  1944. // Just in case.
  1945. static int recursion_protection = 0;
  1946. if( recursion_protection > 3 )
  1947. {
  1948. kdWarning( 1212 ) << "Check maximize overflow - you loose!" << endl;
  1949. kdWarning( 1212 ) << kdBacktrace() << endl;
  1950. return;
  1951. }
  1952. ++recursion_protection;
  1953. TQRect max_area = workspace()->clientArea( MaximizeArea, this );
  1954. if( geometry() == max_area )
  1955. {
  1956. if( max_mode != MaximizeFull )
  1957. maximize( MaximizeFull );
  1958. }
  1959. else if( x() == max_area.left() && width() == max_area.width())
  1960. {
  1961. if( max_mode != MaximizeHorizontal )
  1962. maximize( MaximizeHorizontal );
  1963. }
  1964. else if( y() == max_area.top() && height() == max_area.height())
  1965. {
  1966. if( max_mode != MaximizeVertical )
  1967. maximize( MaximizeVertical );
  1968. }
  1969. else if( max_mode != MaximizeRestore )
  1970. {
  1971. resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
  1972. }
  1973. --recursion_protection;
  1974. }
  1975. bool Client::isFullScreenable( bool fullscreen_hack ) const
  1976. {
  1977. if( !rules()->checkFullScreen( true ))
  1978. return false;
  1979. if( fullscreen_hack )
  1980. return isNormalWindow();
  1981. if( rules()->checkStrictGeometry( false ))
  1982. {
  1983. // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
  1984. TQRect fsarea = workspace()->clientArea( FullScreenArea, this );
  1985. if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
  1986. return false;
  1987. }
  1988. // don't check size constrains - some apps request fullscreen despite requesting fixed size
  1989. return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
  1990. }
  1991. bool Client::userCanSetFullScreen() const
  1992. {
  1993. if( fullscreen_mode == FullScreenHack )
  1994. return false;
  1995. if( !isFullScreenable( false ))
  1996. return false;
  1997. // isMaximizable() returns false if fullscreen
  1998. TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
  1999. return isNormalWindow() && isMaximizable();
  2000. }
  2001. void Client::setFullScreen( bool set, bool user )
  2002. {
  2003. if( !isFullScreen() && !set )
  2004. return;
  2005. if( fullscreen_mode == FullScreenHack )
  2006. return;
  2007. if( user && !userCanSetFullScreen())
  2008. return;
  2009. set = rules()->checkFullScreen( set );
  2010. setShade( ShadeNone );
  2011. bool was_fs = isFullScreen();
  2012. if( !was_fs )
  2013. geom_fs_restore = geometry();
  2014. fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
  2015. if( was_fs == isFullScreen())
  2016. return;
  2017. StackingUpdatesBlocker blocker1( workspace());
  2018. GeometryUpdatesPostponer blocker2( this );
  2019. workspace()->updateClientLayer( this ); // active fullscreens get different layer
  2020. info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
  2021. updateDecoration( false, false );
  2022. if( isFullScreen())
  2023. setGeometry( workspace()->clientArea( FullScreenArea, this ));
  2024. else
  2025. {
  2026. if( !geom_fs_restore.isNull())
  2027. setGeometry( TQRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
  2028. // TODO isShaded() ?
  2029. else
  2030. { // does this ever happen?
  2031. setGeometry( workspace()->clientArea( MaximizeArea, this ));
  2032. }
  2033. }
  2034. updateWindowRules();
  2035. }
  2036. int Client::checkFullScreenHack( const TQRect& geom ) const
  2037. {
  2038. // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
  2039. if( noBorder() && !isUserNoBorder() && isFullScreenable( true ))
  2040. {
  2041. if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
  2042. return 2; // full area fullscreen hack
  2043. if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
  2044. return 1; // xinerama-aware fullscreen hack
  2045. }
  2046. return 0;
  2047. }
  2048. void Client::updateFullScreenHack( const TQRect& geom )
  2049. {
  2050. int type = checkFullScreenHack( geom );
  2051. if( fullscreen_mode == FullScreenNone && type != 0 )
  2052. {
  2053. fullscreen_mode = FullScreenHack;
  2054. updateDecoration( false, false );
  2055. TQRect geom;
  2056. if( rules()->checkStrictGeometry( false ))
  2057. {
  2058. geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
  2059. ? workspace()->clientArea( FullArea, geom.center(), desktop())
  2060. : workspace()->clientArea( ScreenArea, geom.center(), desktop());
  2061. }
  2062. else
  2063. geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
  2064. setGeometry( geom );
  2065. }
  2066. else if( fullscreen_mode == FullScreenHack && type == 0 )
  2067. {
  2068. fullscreen_mode = FullScreenNone;
  2069. updateDecoration( false, false );
  2070. // whoever called this must setup correct geometry
  2071. }
  2072. StackingUpdatesBlocker blocker( workspace());
  2073. workspace()->updateClientLayer( this ); // active fullscreens get different layer
  2074. }
  2075. static TQRect* visible_bound = 0;
  2076. static GeometryTip* geometryTip = 0;
  2077. void Client::drawbound( const TQRect& geom )
  2078. {
  2079. assert( visible_bound == NULL );
  2080. visible_bound = new TQRect( geom );
  2081. doDrawbound( *visible_bound, false );
  2082. }
  2083. void Client::clearbound()
  2084. {
  2085. if( visible_bound == NULL )
  2086. return;
  2087. doDrawbound( *visible_bound, true );
  2088. delete visible_bound;
  2089. visible_bound = 0;
  2090. }
  2091. void Client::doDrawbound( const TQRect& geom, bool clear )
  2092. {
  2093. if( decoration != NULL && decoration->drawbound( geom, clear ))
  2094. return; // done by decoration
  2095. TQPainter p ( workspace()->desktopWidget() );
  2096. p.setPen( TQPen( Qt::white, 5 ) );
  2097. p.setRasterOp( TQt::XorROP );
  2098. // the line is 5 pixel thick, so compensate for the extra two pixels
  2099. // on outside (#88657)
  2100. TQRect g = geom;
  2101. if( g.width() > 5 )
  2102. {
  2103. g.setLeft( g.left() + 2 );
  2104. g.setRight( g.right() - 2 );
  2105. }
  2106. if( g.height() > 5 )
  2107. {
  2108. g.setTop( g.top() + 2 );
  2109. g.setBottom( g.bottom() - 2 );
  2110. }
  2111. p.drawRect( g );
  2112. }
  2113. void Client::positionGeometryTip()
  2114. {
  2115. assert( isMove() || isResize());
  2116. // Position and Size display
  2117. if (options->showGeometryTip())
  2118. {
  2119. if( !geometryTip )
  2120. { // save under is not necessary with opaque, and seem to make things slower
  2121. bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
  2122. || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
  2123. geometryTip = new GeometryTip( &xSizeHint, save_under );
  2124. }
  2125. TQRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
  2126. wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
  2127. wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
  2128. if( isShade())
  2129. wgeom.setHeight( 0 );
  2130. geometryTip->setGeometry( wgeom );
  2131. if( !geometryTip->isVisible())
  2132. {
  2133. geometryTip->show();
  2134. geometryTip->raise();
  2135. }
  2136. }
  2137. }
  2138. class EatAllPaintEvents
  2139. : public QObject
  2140. {
  2141. protected:
  2142. virtual bool eventFilter( TQObject* o, TQEvent* e )
  2143. { return e->type() == TQEvent::Paint && TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(geometryTip); }
  2144. };
  2145. static EatAllPaintEvents* eater = 0;
  2146. bool Client::startMoveResize()
  2147. {
  2148. assert( !moveResizeMode );
  2149. assert( TQWidget::keyboardGrabber() == NULL );
  2150. assert( TQWidget::mouseGrabber() == NULL );
  2151. if( TQApplication::activePopupWidget() != NULL )
  2152. return false; // popups have grab
  2153. bool has_grab = false;
  2154. // This reportedly improves smoothness of the moveresize operation,
  2155. // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
  2156. // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
  2157. XSetWindowAttributes attrs;
  2158. TQRect r = workspace()->clientArea( FullArea, this );
  2159. move_resize_grab_window = XCreateWindow( tqt_xdisplay(), workspace()->rootWin(), r.x(), r.y(),
  2160. r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
  2161. XMapRaised( tqt_xdisplay(), move_resize_grab_window );
  2162. if( XGrabPointer( tqt_xdisplay(), move_resize_grab_window, False,
  2163. ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
  2164. GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), GET_QT_X_TIME() ) == Success )
  2165. has_grab = true;
  2166. if( XGrabKeyboard( tqt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, GET_QT_X_TIME() ) == Success )
  2167. has_grab = true;
  2168. if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
  2169. {
  2170. XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
  2171. move_resize_grab_window = None;
  2172. return false;
  2173. }
  2174. if ( maximizeMode() != MaximizeRestore )
  2175. resetMaximize();
  2176. removeShadow();
  2177. moveResizeMode = true;
  2178. workspace()->setClientIsMoving(this);
  2179. initialMoveResizeGeom = moveResizeGeom = geometry();
  2180. checkUnrestrictedMoveResize();
  2181. // rule out non opaque windows from useless translucency settings, maybe resizes?
  2182. if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
  2183. setShadowSize(0);
  2184. if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque){
  2185. savedOpacity_ = opacity_;
  2186. setOpacity(options->translucentMovingWindows, options->movingWindowOpacity);
  2187. }
  2188. if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
  2189. || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
  2190. {
  2191. grabXServer();
  2192. kapp->sendPostedEvents();
  2193. // we have server grab -> nothing should cause paint events
  2194. // unfortunately, that's not completely true, Qt may generate
  2195. // paint events on some widgets due to FocusIn(?)
  2196. // eat them, otherwise XOR painting will be broken (#58054)
  2197. // paint events for the geometrytip need to be allowed, though
  2198. eater = new EatAllPaintEvents;
  2199. // not needed anymore? kapp->installEventFilter( eater );
  2200. }
  2201. Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
  2202. return true;
  2203. }
  2204. void Client::finishMoveResize( bool cancel )
  2205. {
  2206. leaveMoveResize();
  2207. if( cancel )
  2208. setGeometry( initialMoveResizeGeom );
  2209. else
  2210. setGeometry( moveResizeGeom );
  2211. checkMaximizeGeometry();
  2212. // FRAME update();
  2213. Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
  2214. }
  2215. void Client::leaveMoveResize()
  2216. {
  2217. // rule out non opaque windows from useless translucency settings, maybe resizes?
  2218. if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
  2219. setOpacity(true, savedOpacity_);
  2220. if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
  2221. updateShadowSize();
  2222. clearbound();
  2223. if (geometryTip)
  2224. {
  2225. geometryTip->hide();
  2226. delete geometryTip;
  2227. geometryTip = NULL;
  2228. }
  2229. if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
  2230. || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
  2231. ungrabXServer();
  2232. XUngrabKeyboard( tqt_xdisplay(), GET_QT_X_TIME() );
  2233. XUngrabPointer( tqt_xdisplay(), GET_QT_X_TIME() );
  2234. XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
  2235. move_resize_grab_window = None;
  2236. workspace()->setClientIsMoving(0);
  2237. if( move_faked_activity )
  2238. workspace()->unfakeActivity( this );
  2239. move_faked_activity = false;
  2240. moveResizeMode = false;
  2241. delete eater;
  2242. eater = 0;
  2243. if (options->shadowEnabled(isActive()))
  2244. {
  2245. drawIntersectingShadows();
  2246. updateOpacityCache();
  2247. }
  2248. }
  2249. // This function checks if it actually makes sense to perform a restricted move/resize.
  2250. // If e.g. the titlebar is already outside of the workarea, there's no point in performing
  2251. // a restricted move resize, because then e.g. resize would also move the window (#74555).
  2252. // NOTE: Most of it is duplicated from handleMoveResize().
  2253. void Client::checkUnrestrictedMoveResize()
  2254. {
  2255. if( unrestrictedMoveResize )
  2256. return;
  2257. TQRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
  2258. int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
  2259. // restricted move/resize - keep at least part of the titlebar always visible
  2260. // how much must remain visible when moved away in that direction
  2261. left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
  2262. right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
  2263. // width/height change with opaque resizing, use the initial ones
  2264. titlebar_marge = initialMoveResizeGeom.height();
  2265. top_marge = border_bottom;
  2266. bottom_marge = border_top;
  2267. if( isResize())
  2268. {
  2269. if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
  2270. unrestrictedMoveResize = true;
  2271. if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
  2272. unrestrictedMoveResize = true;
  2273. if( moveResizeGeom.right() < desktopArea.left() + left_marge )
  2274. unrestrictedMoveResize = true;
  2275. if( moveResizeGeom.left() > desktopArea.right() - right_marge )
  2276. unrestrictedMoveResize = true;
  2277. if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
  2278. unrestrictedMoveResize = true;
  2279. }
  2280. if( isMove())
  2281. {
  2282. if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
  2283. unrestrictedMoveResize = true;
  2284. // no need to check top_marge, titlebar_marge already handles it
  2285. if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
  2286. unrestrictedMoveResize = true;
  2287. if( moveResizeGeom.right() < desktopArea.left() + left_marge )
  2288. unrestrictedMoveResize = true;
  2289. if( moveResizeGeom.left() > desktopArea.right() - right_marge )
  2290. unrestrictedMoveResize = true;
  2291. }
  2292. }
  2293. void Client::handleMoveResize( int x, int y, int x_root, int y_root )
  2294. {
  2295. if(( mode == PositionCenter && !isMovable())
  2296. || ( mode != PositionCenter && ( isShade() || !isResizable())))
  2297. return;
  2298. if ( !moveResizeMode )
  2299. {
  2300. TQPoint p( TQPoint( x, y ) - moveOffset );
  2301. if (p.manhattanLength() >= 6)
  2302. {
  2303. if( !startMoveResize())
  2304. {
  2305. buttonDown = false;
  2306. setCursor( mode );
  2307. return;
  2308. }
  2309. }
  2310. else
  2311. return;
  2312. }
  2313. // ShadeHover or ShadeActive, ShadeNormal was already avoided above
  2314. if ( mode != PositionCenter && shade_mode != ShadeNone )
  2315. setShade( ShadeNone );
  2316. TQPoint globalPos( x_root, y_root );
  2317. // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
  2318. // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
  2319. TQPoint topleft = globalPos - moveOffset;
  2320. TQPoint bottomright = globalPos + invertedMoveOffset;
  2321. TQRect previousMoveResizeGeom = moveResizeGeom;
  2322. // TODO move whole group when moving its leader or when the leader is not mapped?
  2323. // compute bounds
  2324. // NOTE: This is duped in checkUnrestrictedMoveResize().
  2325. TQRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
  2326. int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
  2327. if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
  2328. left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
  2329. else // restricted move/resize - keep at least part of the titlebar always visible
  2330. {
  2331. // how much must remain visible when moved away in that direction
  2332. left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
  2333. right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
  2334. // width/height change with opaque resizing, use the initial ones
  2335. titlebar_marge = initialMoveResizeGeom.height();
  2336. top_marge = border_bottom;
  2337. bottom_marge = border_top;
  2338. }
  2339. bool update = false;
  2340. if( isResize())
  2341. {
  2342. // first resize (without checking constraints), then snap, then check bounds, then check constraints
  2343. TQRect orig = initialMoveResizeGeom;
  2344. Sizemode sizemode = SizemodeAny;
  2345. switch ( mode )
  2346. {
  2347. case PositionTopLeft:
  2348. moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
  2349. break;
  2350. case PositionBottomRight:
  2351. moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
  2352. break;
  2353. case PositionBottomLeft:
  2354. moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
  2355. break;
  2356. case PositionTopRight:
  2357. moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
  2358. break;
  2359. case PositionTop:
  2360. moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
  2361. sizemode = SizemodeFixedH; // try not to affect height
  2362. break;
  2363. case PositionBottom:
  2364. moveResizeGeom = TQRect( orig.topLeft(), TQPoint( orig.right(), bottomright.y() ) ) ;
  2365. sizemode = SizemodeFixedH;
  2366. break;
  2367. case PositionLeft:
  2368. moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
  2369. sizemode = SizemodeFixedW;
  2370. break;
  2371. case PositionRight:
  2372. moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), orig.bottom() ) ) ;
  2373. sizemode = SizemodeFixedW;
  2374. break;
  2375. case PositionCenter:
  2376. default:
  2377. assert( false );
  2378. break;
  2379. }
  2380. // adjust new size to snap to other windows/borders
  2381. moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
  2382. // NOTE: This is duped in checkUnrestrictedMoveResize().
  2383. if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
  2384. moveResizeGeom.setBottom( desktopArea.top() + top_marge );
  2385. if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
  2386. moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
  2387. if( moveResizeGeom.right() < desktopArea.left() + left_marge )
  2388. moveResizeGeom.setRight( desktopArea.left() + left_marge );
  2389. if( moveResizeGeom.left() > desktopArea.right() - right_marge )
  2390. moveResizeGeom.setLeft(desktopArea.right() - right_marge );
  2391. if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
  2392. moveResizeGeom.setTop( desktopArea.top());
  2393. TQSize size = adjustedSize( moveResizeGeom.size(), sizemode );
  2394. // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
  2395. topleft = TQPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
  2396. bottomright = TQPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
  2397. orig = moveResizeGeom;
  2398. switch ( mode )
  2399. { // these 4 corners ones are copied from above
  2400. case PositionTopLeft:
  2401. moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
  2402. break;
  2403. case PositionBottomRight:
  2404. moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
  2405. break;
  2406. case PositionBottomLeft:
  2407. moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
  2408. break;
  2409. case PositionTopRight:
  2410. moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
  2411. break;
  2412. // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
  2413. // Therefore grow to the right/bottom if needed.
  2414. // TODO it should probably obey gravity rather than always using right/bottom ?
  2415. case PositionTop:
  2416. moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
  2417. break;
  2418. case PositionBottom:
  2419. moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
  2420. break;
  2421. case PositionLeft:
  2422. moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), TQPoint( orig.right(), bottomright.y()));
  2423. break;
  2424. case PositionRight:
  2425. moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
  2426. break;
  2427. case PositionCenter:
  2428. default:
  2429. assert( false );
  2430. break;
  2431. }
  2432. if( moveResizeGeom.size() != previousMoveResizeGeom.size())
  2433. update = true;
  2434. }
  2435. else if( isMove())
  2436. {
  2437. assert( mode == PositionCenter );
  2438. // first move, then snap, then check bounds
  2439. moveResizeGeom.moveTopLeft( topleft );
  2440. moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
  2441. // NOTE: This is duped in checkUnrestrictedMoveResize().
  2442. if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
  2443. moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
  2444. // no need to check top_marge, titlebar_marge already handles it
  2445. if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
  2446. moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
  2447. if( moveResizeGeom.right() < desktopArea.left() + left_marge )
  2448. moveResizeGeom.moveRight( desktopArea.left() + left_marge );
  2449. if( moveResizeGeom.left() > desktopArea.right() - right_marge )
  2450. moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
  2451. if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
  2452. update = true;
  2453. }
  2454. else
  2455. assert( false );
  2456. if( update )
  2457. {
  2458. if( rules()->checkMoveResizeMode
  2459. ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
  2460. {
  2461. setGeometry( moveResizeGeom );
  2462. positionGeometryTip();
  2463. }
  2464. else if( rules()->checkMoveResizeMode
  2465. ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
  2466. {
  2467. clearbound(); // it's necessary to move the geometry tip when there's no outline
  2468. positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
  2469. drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
  2470. } // so the geometry tip will be painted above the outline
  2471. }
  2472. if ( isMove() )
  2473. workspace()->clientMoved(globalPos, GET_QT_X_TIME());
  2474. }
  2475. } // namespace