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

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.11
This website is maintained by Timothy Pearson.