• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

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

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.8.1.2
This website is maintained by Timothy Pearson.