• 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 = tdeApp->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  ? tdeApp->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) && (TQABS(xmin-cx)<snap))
341  {
342  deltaX = xmin-cx;
343  nx = xmin;
344  }
345  if ((sOWO?(rx>xmax):true) && (TQABS(rx-xmax)<snap) && (TQABS(xmax-rx) < deltaX))
346  {
347  deltaX = rx-xmax;
348  nx = xmax - cw;
349  }
350 
351  if ((sOWO?(cy<ymin):true) && (TQABS(ymin-cy)<snap))
352  {
353  deltaY = ymin-cy;
354  ny = ymin;
355  }
356  if ((sOWO?(ry>ymax):true) && (TQABS(ry-ymax)<snap) && (TQABS(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) && (TQABS(lrx-cx)<snap) && ( TQABS(lrx -cx) < deltaX) )
384  {
385  deltaX = TQABS( lrx - cx );
386  nx = lrx;
387  }
388  if ((sOWO?(rx>lx):true) && (TQABS(rx-lx)<snap) && ( TQABS( rx - lx )<deltaX) )
389  {
390  deltaX = TQABS(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) && (TQABS(lry-cy)<snap) && (TQABS( lry -cy ) < deltaY))
400  {
401  deltaY = TQABS( lry - cy );
402  ny = lry;
403  }
404  //if ( (TQABS( ry-ly ) < snap) && (TQABS( ry - ly ) < deltaY ))
405  if ((sOWO?(ry>ly):true) && (TQABS(ry-ly)<snap) && (TQABS( ry - ly ) < deltaY ))
406  {
407  deltaY = TQABS( 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) && (TQABS(ymin-newcy)<deltaY)) \
455  { \
456  deltaY = TQABS(ymin-newcy); \
457  newcy = ymin; \
458  }
459 
460 #define SNAP_BORDER_BOTTOM \
461  if ((sOWO?(newry>ymax):true) && (TQABS(ymax-newry)<deltaY)) \
462  { \
463  deltaY = TQABS(ymax-newcy); \
464  newry = ymax; \
465  }
466 
467 #define SNAP_BORDER_LEFT \
468  if ((sOWO?(newcx<xmin):true) && (TQABS(xmin-newcx)<deltaX)) \
469  { \
470  deltaX = TQABS(xmin-newcx); \
471  newcx = xmin; \
472  }
473 
474 #define SNAP_BORDER_RIGHT \
475  if ((sOWO?(newrx>xmax):true) && (TQABS(xmax-newrx)<deltaX)) \
476  { \
477  deltaX = TQABS(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  && (TQABS( lry - newcy ) < deltaY) ) { \
547  deltaY = TQABS( lry - newcy ); \
548  newcy=lry; \
549  }
550 
551 #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
552  && WITHIN_WIDTH \
553  && (TQABS( ly - newry ) < deltaY) ) { \
554  deltaY = TQABS( ly - newry ); \
555  newry=ly; \
556  }
557 
558 #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
559  && WITHIN_HEIGHT \
560  && (TQABS( lrx - newcx ) < deltaX)) { \
561  deltaX = TQABS( lrx - newcx ); \
562  newcx=lrx; \
563  }
564 
565 #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
566  && WITHIN_HEIGHT \
567  && (TQABS( lx - newrx ) < deltaX)) \
568  { \
569  deltaX = TQABS( 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_tqt_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 == tdeApp->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  inhibitConfigureRequests = true;
1455  configureRequestTimer->start(100, true);
1456  if( gravity == 0 ) // default (nonsense) value for the argument
1457  gravity = xSizeHint.win_gravity;
1458  if( value_mask & ( CWX | CWY ))
1459  {
1460  TQPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
1461  if ( value_mask & CWX )
1462  new_pos.setX( rx );
1463  if ( value_mask & CWY )
1464  new_pos.setY( ry );
1465 
1466  // clever(?) workaround for applications like xv that want to set
1467  // the location to the current location but miscalculate the
1468  // frame size due to twin being a double-reparenting window
1469  // manager
1470  if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
1471  && gravity == NorthWestGravity && !from_tool )
1472  {
1473  new_pos.setX( x());
1474  new_pos.setY( y());
1475  }
1476 
1477  int nw = clientSize().width();
1478  int nh = clientSize().height();
1479  if ( value_mask & CWWidth )
1480  nw = rw;
1481  if ( value_mask & CWHeight )
1482  nh = rh;
1483  TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
1484  new_pos = rules()->checkPosition( new_pos );
1485 
1486  // TODO what to do with maximized windows?
1487  if ( maximizeMode() != MaximizeFull
1488  || ns != size())
1489  {
1490  TQRect orig_geometry = geometry();
1491  GeometryUpdatesPostponer blocker( this );
1492  move( new_pos );
1493  plainResize( ns );
1494  setGeometry( TQRect( calculateGravitation( false, gravity ), size()));
1495  updateFullScreenHack( TQRect( new_pos, TQSize( nw, nh )));
1496  TQRect area = workspace()->clientArea( WorkArea, this );
1497  if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
1498  && area.contains( orig_geometry ))
1499  keepInArea( area );
1500 
1501  // this is part of the kicker-xinerama-hack... it should be
1502  // safe to remove when kicker gets proper ExtendedStrut support;
1503  // see Workspace::updateClientArea() and
1504  // Client::adjustedClientArea()
1505  if (hasStrut ())
1506  workspace() -> updateClientArea ();
1507  }
1508  }
1509 
1510  if ( value_mask & (CWWidth | CWHeight )
1511  && ! ( value_mask & ( CWX | CWY )) ) // pure resize
1512  {
1513  int nw = clientSize().width();
1514  int nh = clientSize().height();
1515  if ( value_mask & CWWidth )
1516  nw = rw;
1517  if ( value_mask & CWHeight )
1518  nh = rh;
1519  TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
1520 
1521  if( ns != size()) // don't restore if some app sets its own size again
1522  {
1523  TQRect orig_geometry = geometry();
1524  GeometryUpdatesPostponer blocker( this );
1525  int save_gravity = xSizeHint.win_gravity;
1526  xSizeHint.win_gravity = gravity;
1527  resizeWithChecks( ns );
1528  xSizeHint.win_gravity = save_gravity;
1529  updateFullScreenHack( TQRect( calculateGravitation( true, xSizeHint.win_gravity ), TQSize( nw, nh )));
1530  if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1531  {
1532  // try to keep the window in its xinerama screen if possible,
1533  // if that fails at least keep it visible somewhere
1534  TQRect area = workspace()->clientArea( MovementArea, this );
1535  if( area.contains( orig_geometry ))
1536  keepInArea( area );
1537  area = workspace()->clientArea( WorkArea, this );
1538  if( area.contains( orig_geometry ))
1539  keepInArea( area );
1540  }
1541  }
1542  }
1543  // No need to send synthetic configure notify event here, either it's sent together
1544  // with geometry change, or there's no need to send it.
1545  // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
1546  }
1547 
1548 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
1549  {
1550  if( shade_geometry_change )
1551  assert( false );
1552  else if( isShade())
1553  {
1554  if( h == border_top + border_bottom )
1555  {
1556  kdWarning() << "Shaded geometry passed for size:" << endl;
1557  kdWarning() << kdBacktrace() << endl;
1558  }
1559  }
1560  int newx = x();
1561  int newy = y();
1562  TQRect area = workspace()->clientArea( WorkArea, this );
1563  // don't allow growing larger than workarea
1564  if( w > area.width())
1565  w = area.width();
1566  if( h > area.height())
1567  h = area.height();
1568  TQSize tmp = adjustedSize( TQSize( w, h )); // checks size constraints, including min/max size
1569  w = tmp.width();
1570  h = tmp.height();
1571  switch( xSizeHint.win_gravity )
1572  {
1573  case NorthWestGravity: // top left corner doesn't move
1574  default:
1575  break;
1576  case NorthGravity: // middle of top border doesn't move
1577  newx = ( newx + width() / 2 ) - ( w / 2 );
1578  break;
1579  case NorthEastGravity: // top right corner doesn't move
1580  newx = newx + width() - w;
1581  break;
1582  case WestGravity: // middle of left border doesn't move
1583  newy = ( newy + height() / 2 ) - ( h / 2 );
1584  break;
1585  case CenterGravity: // middle point doesn't move
1586  newx = ( newx + width() / 2 ) - ( w / 2 );
1587  newy = ( newy + height() / 2 ) - ( h / 2 );
1588  break;
1589  case StaticGravity: // top left corner of _client_ window doesn't move
1590  // since decoration doesn't change, equal to NorthWestGravity
1591  break;
1592  case EastGravity: // // middle of right border doesn't move
1593  newx = newx + width() - w;
1594  newy = ( newy + height() / 2 ) - ( h / 2 );
1595  break;
1596  case SouthWestGravity: // bottom left corner doesn't move
1597  newy = newy + height() - h;
1598  break;
1599  case SouthGravity: // middle of bottom border doesn't move
1600  newx = ( newx + width() / 2 ) - ( w / 2 );
1601  newy = newy + height() - h;
1602  break;
1603  case SouthEastGravity: // bottom right corner doesn't move
1604  newx = newx + width() - w;
1605  newy = newy + height() - h;
1606  break;
1607  }
1608  // if it would be moved outside of workarea, keep it inside,
1609  // see also Client::computeWorkareaDiff()
1610  if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
1611  {
1612  if( newx < area.left())
1613  newx = area.left();
1614  if( newx + w > area.right() + 1 )
1615  newx = area.right() + 1 - w;
1616  assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
1617  }
1618  if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
1619  {
1620  if( newy < area.top())
1621  newy = area.top();
1622  if( newy + h > area.bottom() + 1 )
1623  newy = area.bottom() + 1 - h;
1624  assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
1625  }
1626  setGeometry( newx, newy, w, h, force );
1627  }
1628 
1629 // _NET_MOVERESIZE_WINDOW
1630 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
1631  {
1632  int gravity = flags & 0xff;
1633  int value_mask = 0;
1634  if( flags & ( 1 << 8 ))
1635  value_mask |= CWX;
1636  if( flags & ( 1 << 9 ))
1637  value_mask |= CWY;
1638  if( flags & ( 1 << 10 ))
1639  value_mask |= CWWidth;
1640  if( flags & ( 1 << 11 ))
1641  value_mask |= CWHeight;
1642  configureRequest( value_mask, x, y, width, height, gravity, true );
1643  }
1644 
1649 bool Client::isMovable() const
1650  {
1651  if( !motif_may_move || isFullScreen())
1652  return false;
1653  if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1654  return false;
1655  if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1656  return false;
1657  if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
1658  return false;
1659  return true;
1660  }
1661 
1665 bool Client::isResizable() const
1666  {
1667  if( !motif_may_resize || isFullScreen())
1668  return false;
1669  if( isSpecialWindow() )
1670  return false;
1671  if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1672  return false;
1673  if( rules()->checkSize( TQSize()).isValid()) // forced size
1674  return false;
1675 
1676  TQSize min = minSize();
1677  TQSize max = maxSize();
1678  return min.width() < max.width() || min.height() < max.height();
1679  }
1680 
1681 /*
1682  Returns whether the window is maximizable or not
1683  */
1684 bool Client::isMaximizable() const
1685  {
1686  if( isModalSystemNotification())
1687  return false;
1688  { // isMovable() and isResizable() may be false for maximized windows
1689  // with moving/resizing maximized windows disabled
1690  TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
1691  if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
1692  return false;
1693  }
1694  if ( maximizeMode() != MaximizeRestore )
1695  return true;
1696  TQSize max = maxSize();
1697 #if 0
1698  if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
1699  return false;
1700 #else
1701  // apparently there are enough apps which specify some arbitrary value
1702  // for their maximum size just for the fun of it
1703  TQSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
1704  if( max.width() < areasize.width() || max.height() < areasize.height())
1705  return false;
1706 #endif
1707  return true;
1708  }
1709 
1710 
1714 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
1715  {
1716  // this code is also duplicated in Client::plainResize()
1717  // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
1718  // simply because there are too many places dealing with geometry. Those places
1719  // ignore shaded state and use normal geometry, which they usually should get
1720  // from adjustedSize(). Such geometry comes here, and if the window is shaded,
1721  // the geometry is used only for client_size, since that one is not used when
1722  // shading. Then the frame geometry is adjusted for the shaded geometry.
1723  // This gets more complicated in the case the code does only something like
1724  // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
1725  // Such code is wrong and should be changed to handle the case when the window is shaded,
1726  // for example using Client::clientSize().
1727  if( shade_geometry_change )
1728  ; // nothing
1729  else if( isShade())
1730  {
1731  if( h == border_top + border_bottom )
1732  {
1733  kdDebug() << "Shaded geometry passed for size:" << endl;
1734  kdDebug() << kdBacktrace() << endl;
1735  }
1736  else
1737  {
1738  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1739  h = border_top + border_bottom;
1740  }
1741  }
1742  else
1743  {
1744  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1745  }
1746  if( force == NormalGeometrySet && frame_geometry == TQRect( x, y, w, h ))
1747  return;
1748  frame_geometry = TQRect( x, y, w, h );
1749  updateWorkareaDiffs();
1750  if( postpone_geometry_updates != 0 )
1751  {
1752  pending_geometry_update = true;
1753  return;
1754  }
1755  resizeDecoration( TQSize( w, h ));
1756  XMoveResizeWindow( tqt_xdisplay(), frameId(), x, y, w, h );
1757 // resizeDecoration( TQSize( w, h ));
1758  if( !isShade())
1759  {
1760  TQSize cs = clientSize();
1761  XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
1762  cs.width(), cs.height());
1763  XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
1764  }
1765  updateShape();
1766  // SELI TODO won't this be too expensive?
1767  updateWorkareaDiffs();
1768  sendSyntheticConfigureNotify();
1769  updateWindowRules();
1770  checkMaximizeGeometry();
1771  workspace()->checkActiveScreen( this );
1772  }
1773 
1774 void Client::plainResize( int w, int h, ForceGeometry_t force )
1775  {
1776  // this code is also duplicated in Client::setGeometry(), and it's also commented there
1777  if( shade_geometry_change )
1778  ; // nothing
1779  else if( isShade())
1780  {
1781  if( h == border_top + border_bottom )
1782  {
1783  kdDebug() << "Shaded geometry passed for size:" << endl;
1784  kdDebug() << kdBacktrace() << endl;
1785  }
1786  else
1787  {
1788  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1789  h = border_top + border_bottom;
1790  }
1791  }
1792  else
1793  {
1794  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1795  }
1796  if( TQSize( w, h ) != rules()->checkSize( TQSize( w, h )))
1797  {
1798  kdDebug() << "forced size fail:" << TQSize( w,h ) << ":" << rules()->checkSize( TQSize( w, h )) << endl;
1799  kdDebug() << kdBacktrace() << endl;
1800  }
1801  if( force == NormalGeometrySet && frame_geometry.size() == TQSize( w, h ))
1802  return;
1803  frame_geometry.setSize( TQSize( w, h ));
1804  updateWorkareaDiffs();
1805  if( postpone_geometry_updates != 0 )
1806  {
1807  pending_geometry_update = true;
1808  return;
1809  }
1810  resizeDecoration( TQSize( w, h ));
1811  XResizeWindow( tqt_xdisplay(), frameId(), w, h );
1812 // resizeDecoration( TQSize( w, h ));
1813  if( !isShade())
1814  {
1815  TQSize cs = clientSize();
1816  XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
1817  cs.width(), cs.height());
1818  XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
1819  }
1820  updateShape();
1821  updateWorkareaDiffs();
1822  sendSyntheticConfigureNotify();
1823  updateWindowRules();
1824  checkMaximizeGeometry();
1825  workspace()->checkActiveScreen( this );
1826  }
1827 
1831 void Client::move( int x, int y, ForceGeometry_t force )
1832  {
1833  if( force == NormalGeometrySet && frame_geometry.topLeft() == TQPoint( x, y ))
1834  return;
1835  frame_geometry.moveTopLeft( TQPoint( x, y ));
1836  updateWorkareaDiffs();
1837  if( postpone_geometry_updates != 0 )
1838  {
1839  pending_geometry_update = true;
1840  return;
1841  }
1842  XMoveWindow( tqt_xdisplay(), frameId(), x, y );
1843  sendSyntheticConfigureNotify();
1844  updateWindowRules();
1845  checkMaximizeGeometry();
1846  workspace()->checkActiveScreen( this );
1847  }
1848 
1849 
1850 void Client::postponeGeometryUpdates( bool postpone )
1851  {
1852  if( postpone )
1853  {
1854  if( postpone_geometry_updates == 0 )
1855  pending_geometry_update = false;
1856  ++postpone_geometry_updates;
1857  }
1858  else
1859  {
1860  if( --postpone_geometry_updates == 0 )
1861  {
1862  if( pending_geometry_update )
1863  {
1864  if( isShade())
1865  setGeometry( TQRect( pos(), adjustedSize()), ForceGeometrySet );
1866  else
1867  setGeometry( geometry(), ForceGeometrySet );
1868  pending_geometry_update = false;
1869  }
1870  }
1871  }
1872  }
1873 
1874 void Client::maximize( MaximizeMode m )
1875  {
1876  setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
1877  }
1878 
1882 void Client::setMaximize( bool vertically, bool horizontally )
1883  { // changeMaximize() flips the state, so change from set->flip
1884  changeMaximize(
1885  max_mode & MaximizeVertical ? !vertically : vertically,
1886  max_mode & MaximizeHorizontal ? !horizontally : horizontally,
1887  false );
1888  }
1889 
1890 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
1891  {
1892  if( !isMaximizable())
1893  return;
1894 
1895  MaximizeMode old_mode = max_mode;
1896  // 'adjust == true' means to update the size only, e.g. after changing workspace size
1897  if( !adjust )
1898  {
1899  if( vertical )
1900  max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
1901  if( horizontal )
1902  max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
1903  }
1904 
1905  max_mode = rules()->checkMaximize( max_mode );
1906  if( !adjust && max_mode == old_mode )
1907  return;
1908 
1909  GeometryUpdatesPostponer blocker( this );
1910 
1911  // maximing one way and unmaximizing the other way shouldn't happen
1912  Q_ASSERT( !( vertical && horizontal )
1913  || ((( max_mode & MaximizeVertical ) != 0 ) == (( max_mode & MaximizeHorizontal ) != 0 )));
1914 
1915  TQRect clientArea = workspace()->clientArea( MaximizeArea, this );
1916 
1917  // save sizes for restoring, if maximalizing
1918  if( !activeTiled && !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
1919  {
1920  geom_restore.setTop( y());
1921  geom_restore.setHeight( height());
1922  }
1923  if( !activeTiled && !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
1924  {
1925  geom_restore.setLeft( x());
1926  geom_restore.setWidth( width());
1927  }
1928 
1929  if( !adjust )
1930  {
1931  if(( vertical && !(old_mode & MaximizeVertical ))
1932  || ( horizontal && !( old_mode & MaximizeHorizontal )))
1933  Notify::raise( Notify::Maximize );
1934  else
1935  Notify::raise( Notify::UnMaximize );
1936  }
1937 
1938  if( decoration != NULL ) // decorations may turn off some borders when maximized
1939  decoration->borders( border_left, border_right, border_top, border_bottom );
1940 
1941  // restore partial maximizations
1942  if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
1943  {
1944  if ( maximizeModeRestore()==MaximizeVertical )
1945  {
1946  max_mode = MaximizeVertical;
1947  maxmode_restore = MaximizeRestore;
1948  }
1949  if ( maximizeModeRestore()==MaximizeHorizontal )
1950  {
1951  max_mode = MaximizeHorizontal;
1952  maxmode_restore = MaximizeRestore;
1953  }
1954  }
1955 
1956  switch (max_mode)
1957  {
1958 
1959  case MaximizeVertical:
1960  {
1961  if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
1962  {
1963  if( geom_restore.width() == 0 )
1964  { // needs placement
1965  plainResize( adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH ));
1966  workspace()->placeSmart( this, clientArea );
1967  }
1968  else
1969  setGeometry( TQRect(TQPoint( geom_restore.x(), clientArea.top()),
1970  adjustedSize(TQSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
1971  }
1972  else
1973  setGeometry( TQRect(TQPoint(x(), clientArea.top()),
1974  adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
1975  info->setState( NET::MaxVert, NET::Max );
1976  break;
1977  }
1978 
1979  case MaximizeHorizontal:
1980  {
1981  if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
1982  {
1983  if( geom_restore.height() == 0 )
1984  { // needs placement
1985  plainResize( adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW ));
1986  workspace()->placeSmart( this, clientArea );
1987  }
1988  else
1989  setGeometry( TQRect( TQPoint(clientArea.left(), geom_restore.y()),
1990  adjustedSize(TQSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), ForceGeometrySet);
1991  }
1992  else
1993  setGeometry( TQRect( TQPoint(clientArea.left(), y()),
1994  adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW )), ForceGeometrySet);
1995  info->setState( NET::MaxHoriz, NET::Max );
1996  break;
1997  }
1998 
1999  case MaximizeRestore:
2000  {
2001  TQRect restore = geometry();
2002  // when only partially maximized, geom_restore may not have the other dimension remembered
2003  if( old_mode & MaximizeVertical )
2004  {
2005  restore.setTop( geom_restore.top());
2006  restore.setBottom( geom_restore.bottom());
2007  }
2008  if( old_mode & MaximizeHorizontal )
2009  {
2010  restore.setLeft( geom_restore.left());
2011  restore.setRight( geom_restore.right());
2012  }
2013  if( !restore.isValid())
2014  {
2015  TQSize s = TQSize( clientArea.width()*2/3, clientArea.height()*2/3 );
2016  if( geom_restore.width() > 0 )
2017  s.setWidth( geom_restore.width());
2018  if( geom_restore.height() > 0 )
2019  s.setHeight( geom_restore.height());
2020  plainResize( adjustedSize( s ));
2021  workspace()->placeSmart( this, clientArea );
2022  restore = geometry();
2023  if( geom_restore.width() > 0 )
2024  restore.moveLeft( geom_restore.x());
2025  if( geom_restore.height() > 0 )
2026  restore.moveTop( geom_restore.y());
2027  }
2028  setGeometry( restore, ForceGeometrySet );
2029  info->setState( 0, NET::Max );
2030  break;
2031  }
2032 
2033  case MaximizeFull:
2034  {
2035  if( !adjust )
2036  {
2037  if( old_mode & MaximizeVertical )
2038  maxmode_restore = MaximizeVertical;
2039  if( old_mode & MaximizeHorizontal )
2040  maxmode_restore = MaximizeHorizontal;
2041  }
2042  TQSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
2043  TQRect r = TQRect(clientArea.topLeft(), adjSize);
2044  setGeometry( r, ForceGeometrySet );
2045  info->setState( NET::Max, NET::Max );
2046  break;
2047  }
2048  default:
2049  break;
2050  }
2051 
2052  updateAllowedActions();
2053  if( decoration != NULL )
2054  decoration->maximizeChange();
2055  updateWindowRules();
2056  }
2057 
2058 void Client::resetMaximize()
2059  {
2060  if( max_mode == MaximizeRestore )
2061  return;
2062  max_mode = MaximizeRestore;
2063  Notify::raise( Notify::UnMaximize );
2064  info->setState( 0, NET::Max );
2065  updateAllowedActions();
2066  if( decoration != NULL )
2067  decoration->borders( border_left, border_right, border_top, border_bottom );
2068  if( isShade())
2069  setGeometry( TQRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
2070  else
2071  setGeometry( geometry(), ForceGeometrySet );
2072  if( decoration != NULL )
2073  decoration->maximizeChange();
2074  }
2075 
2076 void Client::checkMaximizeGeometry()
2077  {
2078  // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
2079  // when after the condition is no longer true
2080  if( isShade())
2081  return;
2082  if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2083  return;
2084  // Just in case.
2085  static int recursion_protection = 0;
2086  if( recursion_protection > 3 )
2087  {
2088  kdWarning( 1212 ) << "Check maximize overflow - you loose!" << endl;
2089  kdWarning( 1212 ) << kdBacktrace() << endl;
2090  return;
2091  }
2092  ++recursion_protection;
2093  TQRect max_area = workspace()->clientArea( MaximizeArea, this );
2094  if( geometry() == max_area )
2095  {
2096  if( max_mode != MaximizeFull )
2097  maximize( MaximizeFull );
2098  }
2099  else if( x() == max_area.left() && width() == max_area.width())
2100  {
2101  if( max_mode != MaximizeHorizontal )
2102  maximize( MaximizeHorizontal );
2103  }
2104  else if( y() == max_area.top() && height() == max_area.height())
2105  {
2106  if( max_mode != MaximizeVertical )
2107  maximize( MaximizeVertical );
2108  }
2109  else if( max_mode != MaximizeRestore )
2110  {
2111  resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
2112  }
2113  --recursion_protection;
2114  }
2115 
2116 bool Client::isFullScreenable( bool fullscreen_hack ) const
2117  {
2118  if( !rules()->checkFullScreen( true ))
2119  return false;
2120  if( fullscreen_hack )
2121  return isNormalWindow();
2122  if( rules()->checkStrictGeometry( false ))
2123  {
2124  // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
2125  TQRect fsarea = workspace()->clientArea( FullScreenArea, this );
2126  if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
2127  return false;
2128  }
2129  // don't check size constrains - some apps request fullscreen despite requesting fixed size
2130  return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
2131  }
2132 
2133 bool Client::userCanSetFullScreen() const
2134  {
2135  if( fullscreen_mode == FullScreenHack )
2136  return false;
2137  if( !isFullScreenable( false ))
2138  return false;
2139  // isMaximizable() returns false if fullscreen
2140  TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
2141  return isNormalWindow() && isMaximizable();
2142  }
2143 
2144 void Client::setFullScreen( bool set, bool user )
2145  {
2146  if( !isFullScreen() && !set )
2147  return;
2148  if( fullscreen_mode == FullScreenHack )
2149  return;
2150  if( user && !userCanSetFullScreen())
2151  return;
2152  set = rules()->checkFullScreen( set );
2153  setShade( ShadeNone );
2154  bool was_fs = isFullScreen();
2155  if( !was_fs )
2156  geom_fs_restore = geometry();
2157  fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
2158  if( was_fs == isFullScreen())
2159  return;
2160  StackingUpdatesBlocker blocker1( workspace());
2161  GeometryUpdatesPostponer blocker2( this );
2162  workspace()->updateClientLayer( this ); // active fullscreens get different layer
2163  info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
2164  updateDecoration( false, false );
2165  if( isFullScreen())
2166  setGeometry( workspace()->clientArea( FullScreenArea, this ));
2167  else
2168  {
2169  if( !geom_fs_restore.isNull())
2170  setGeometry( TQRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
2171  // TODO isShaded() ?
2172  else
2173  { // does this ever happen?
2174  setGeometry( workspace()->clientArea( MaximizeArea, this ));
2175  }
2176  }
2177  updateWindowRules();
2178  }
2179 
2180 int Client::checkFullScreenHack( const TQRect& geom ) const
2181  {
2182  // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
2183  if( noBorder() && !isUserNoBorder() && isFullScreenable( true ))
2184  {
2185  if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
2186  return 2; // full area fullscreen hack
2187  if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
2188  return 1; // xinerama-aware fullscreen hack
2189  }
2190  return 0;
2191  }
2192 
2193 void Client::updateFullScreenHack( const TQRect& geom )
2194  {
2195  int type = checkFullScreenHack( geom );
2196  if( fullscreen_mode == FullScreenNone && type != 0 )
2197  {
2198  fullscreen_mode = FullScreenHack;
2199  updateDecoration( false, false );
2200  TQRect geom;
2201  if( rules()->checkStrictGeometry( false ))
2202  {
2203  geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
2204  ? workspace()->clientArea( FullArea, geom.center(), desktop())
2205  : workspace()->clientArea( ScreenArea, geom.center(), desktop());
2206  }
2207  else
2208  geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
2209  setGeometry( geom );
2210  }
2211  else if( fullscreen_mode == FullScreenHack && type == 0 )
2212  {
2213  fullscreen_mode = FullScreenNone;
2214  updateDecoration( false, false );
2215  // whoever called this must setup correct geometry
2216  }
2217  StackingUpdatesBlocker blocker( workspace());
2218  workspace()->updateClientLayer( this ); // active fullscreens get different layer
2219  }
2220 
2221 static TQRect* visible_bound = nullptr;
2222 static GeometryTip* geometryTip = nullptr;
2223 
2224 void Client::drawbound( const TQRect& geom )
2225  {
2226  assert( visible_bound == NULL );
2227  visible_bound = new TQRect( geom );
2228  doDrawbound( *visible_bound, false );
2229  }
2230 
2231 void Client::clearbound()
2232  {
2233  if( visible_bound == NULL )
2234  return;
2235  doDrawbound( *visible_bound, true );
2236  delete visible_bound;
2237  visible_bound = 0;
2238  }
2239 
2240 void Client::doDrawbound( const TQRect& geom, bool clear )
2241  {
2242  if( decoration != NULL && decoration->drawbound( geom, clear ))
2243  return; // done by decoration
2244  TQPainter p ( workspace()->desktopWidget() );
2245  p.setPen( TQPen( TQt::white, 5 ) );
2246  p.setRasterOp( TQt::XorROP );
2247  // the line is 5 pixel thick, so compensate for the extra two pixels
2248  // on outside (#88657)
2249  TQRect g = geom;
2250  if( g.width() > 5 )
2251  {
2252  g.setLeft( g.left() + 2 );
2253  g.setRight( g.right() - 2 );
2254  }
2255  if( g.height() > 5 )
2256  {
2257  g.setTop( g.top() + 2 );
2258  g.setBottom( g.bottom() - 2 );
2259  }
2260  p.drawRect( g );
2261  }
2262 
2263 void Client::positionGeometryTip() {
2264  assert(isMove() || isResize());
2265 
2266  // Position and Size display
2267  if (options->showGeometryTip()) {
2268  if (!geometryTip) {
2269  // save under is not necessary with opaque, and seem to make things slower
2270  bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2271  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
2272  geometryTip = new GeometryTip( &xSizeHint, save_under );
2273  }
2274 
2275  // position of the frame, size of the window itself
2276  TQRect wgeom(isActiveBorderMaximizing() ? activeBorderMaximizeGeometry() : moveResizeGeom);
2277  wgeom.setWidth(wgeom.width() - (width() - clientSize().width()));
2278  wgeom.setHeight(isShade() ? 0 : wgeom.height() - (height() - clientSize().height()));
2279 
2280  geometryTip->setGeometry(wgeom);
2281  if (!geometryTip->isVisible()) {
2282  geometryTip->show();
2283  geometryTip->raise();
2284  }
2285  }
2286 }
2287 
2288 class EatAllPaintEvents
2289  : public TQObject
2290  {
2291  protected:
2292  virtual bool eventFilter( TQObject* o, TQEvent* e )
2293  { return e->type() == TQEvent::Paint && o != geometryTip; }
2294  };
2295 
2296 static EatAllPaintEvents* eater = 0;
2297 
2298 bool Client::startMoveResize()
2299 {
2300  assert( !moveResizeMode );
2301  assert( TQWidget::keyboardGrabber() == NULL );
2302  assert( TQWidget::mouseGrabber() == NULL );
2303  if( TQApplication::activePopupWidget() != NULL )
2304  return false; // popups have grab
2305  bool has_grab = false;
2306  // This reportedly improves smoothness of the moveresize operation,
2307  // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
2308  // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
2309  XSetWindowAttributes attrs;
2310  TQRect r = workspace()->clientArea( FullArea, this );
2311  move_resize_grab_window = XCreateWindow( tqt_xdisplay(), workspace()->rootWin(), r.x(), r.y(),
2312  r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
2313  XMapRaised( tqt_xdisplay(), move_resize_grab_window );
2314  if( XGrabPointer( tqt_xdisplay(), move_resize_grab_window, False,
2315  ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
2316  GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), get_tqt_x_time() ) == Success )
2317  has_grab = true;
2318  if( XGrabKeyboard( tqt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, get_tqt_x_time() ) == Success )
2319  has_grab = true;
2320  if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
2321  {
2322  XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
2323  move_resize_grab_window = None;
2324  return false;
2325  }
2326 
2327  removeShadow();
2328  moveResizeMode = true;
2329  initialMoveResizeGeom = geometry();
2330 
2331  if ( maximizeMode() != MaximizeRestore )
2332  {
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  geom_restore.moveLeft(TQCursor::pos().x() - (geom_restore.width() / 2));
2340  moveOffset.setX(TQCursor::pos().x() - geom_restore.x());
2341 
2342  setGeometry(geom_restore);
2343 
2344  maximize(MaximizeRestore);
2345  }
2346  else {
2347  resetMaximize();
2348  }
2349  activeTiled = false;
2350  }
2351 
2352  moveResizeGeom = geometry();
2353  workspace()->setClientIsMoving(this);
2354  checkUnrestrictedMoveResize();
2355 
2356  // rule out non opaque windows from useless translucency settings, maybe resizes?
2357  if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
2358  {
2359  setShadowSize(0);
2360  }
2361 
2362  if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
2363  {
2364  savedOpacity_ = opacity_;
2365  setOpacity(options->translucentMovingWindows, options->movingWindowOpacity);
2366  }
2367 
2368  if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2369  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2370  {
2371  grabXServer();
2372  tdeApp->sendPostedEvents();
2373  // we have server grab -> nothing should cause paint events
2374  // unfortunately, that's not completely true, Qt may generate
2375  // paint events on some widgets due to FocusIn(?)
2376  // eat them, otherwise XOR painting will be broken (#58054)
2377  // paint events for the geometrytip need to be allowed, though
2378  // eater = new EatAllPaintEvents;
2379 // not needed anymore? tdeApp->installEventFilter( eater );
2380  }
2381  Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
2382 
2383  if (options->activeBorders() == Options::ActiveSwitchOnMove ||
2384  options->activeBorders() == Options::ActiveTileMaximize ||
2385  options->activeBorders() == Options::ActiveTileOnly)
2386 
2387  {
2388  workspace()->reserveActiveBorderSwitching(true);
2389  }
2390 
2391  return true;
2392 }
2393 
2394 void Client::finishMoveResize( bool cancel )
2395 {
2396  leaveMoveResize();
2397 
2398  if (!isActiveBorderMaximizing()) {
2399  setGeometry(cancel ? initialMoveResizeGeom : moveResizeGeom);
2400  }
2401 
2402  else
2403  {
2404  kdDebug() <<"finishing moveresize in active mode, cancel is " << cancel << endl;
2405  activeMaximizing = false;
2406  activeTiled = true;
2407  geom_restore = initialMoveResizeGeom;
2408  switch (activeMode)
2409  {
2410  case ActiveMaximizeMode: {
2411  if (!cancel) {
2412  bool full = (maximizeMode() == MaximizeFull);
2413  setMaximize(!full, !full);
2414  }
2415  break;
2416  }
2417  default:
2418  setGeometry(cancel ? initialMoveResizeGeom
2419  : activeBorderMaximizeGeometry());
2420  }
2421  }
2422 
2423  checkMaximizeGeometry();
2424 // FRAME update();
2425  Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
2426 }
2427 
2428 void Client::leaveMoveResize()
2429 {
2430  // rule out non opaque windows from useless translucency settings, maybe resizes?
2431  if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
2432  setOpacity(true, savedOpacity_);
2433  if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
2434  updateShadowSize();
2435  clearbound();
2436  if (geometryTip)
2437  {
2438  geometryTip->hide();
2439  delete geometryTip;
2440  geometryTip = NULL;
2441  }
2442  if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2443  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2444  ungrabXServer();
2445  XUngrabKeyboard( tqt_xdisplay(), get_tqt_x_time() );
2446  XUngrabPointer( tqt_xdisplay(), get_tqt_x_time() );
2447  XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
2448  move_resize_grab_window = None;
2449  workspace()->setClientIsMoving(0);
2450  if( move_faked_activity )
2451  workspace()->unfakeActivity( this );
2452  move_faked_activity = false;
2453  moveResizeMode = false;
2454  delete eater;
2455  eater = 0;
2456  if (options->shadowEnabled(isActive()))
2457  {
2458  drawIntersectingShadows();
2459  updateOpacityCache();
2460  }
2461 
2462  if (options->activeBorders() == Options::ActiveSwitchOnMove ||
2463  options->activeBorders() == Options::ActiveTileMaximize ||
2464  options->activeBorders() == Options::ActiveTileOnly)
2465  {
2466  workspace()->reserveActiveBorderSwitching(false);
2467  }
2468 }
2469 
2470 // This function checks if it actually makes sense to perform a restricted move/resize.
2471 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
2472 // a restricted move resize, because then e.g. resize would also move the window (#74555).
2473 // NOTE: Most of it is duplicated from handleMoveResize().
2474 void Client::checkUnrestrictedMoveResize()
2475  {
2476  if( unrestrictedMoveResize )
2477  return;
2478  TQRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
2479  int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2480  // restricted move/resize - keep at least part of the titlebar always visible
2481  // how much must remain visible when moved away in that direction
2482  left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
2483  right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
2484  // width/height change with opaque resizing, use the initial ones
2485  titlebar_marge = initialMoveResizeGeom.height();
2486  top_marge = border_bottom;
2487  bottom_marge = border_top;
2488  if( isResize())
2489  {
2490  if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2491  unrestrictedMoveResize = true;
2492  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2493  unrestrictedMoveResize = true;
2494  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2495  unrestrictedMoveResize = true;
2496  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2497  unrestrictedMoveResize = true;
2498  if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2499  unrestrictedMoveResize = true;
2500  }
2501  if( isMove())
2502  {
2503  if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2504  unrestrictedMoveResize = true;
2505  // no need to check top_marge, titlebar_marge already handles it
2506  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2507  unrestrictedMoveResize = true;
2508  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2509  unrestrictedMoveResize = true;
2510  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2511  unrestrictedMoveResize = true;
2512  }
2513  }
2514 
2515 void Client::handleMoveResize(int x, int y, int x_root, int y_root) {
2516  if ( (mode == PositionCenter && !isMovable())
2517  || (mode != PositionCenter && (isShade() || !isResizable())) )
2518  return;
2519 
2520  if (!moveResizeMode) {
2521  TQPoint p(TQPoint( x, y ) - moveOffset);
2522  if (p.manhattanLength() >= 6) {
2523  if (!startMoveResize()) {
2524  buttonDown = false;
2525  setCursor( mode );
2526  return;
2527  }
2528  }
2529  else return;
2530  }
2531 
2532  // ShadeHover or ShadeActive, ShadeNormal was already avoided above
2533  if ( mode != PositionCenter && shade_mode != ShadeNone )
2534  setShade( ShadeNone );
2535 
2536  TQPoint globalPos( x_root, y_root );
2537  // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
2538  // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
2539  TQPoint topleft = globalPos - moveOffset;
2540  TQPoint bottomright = globalPos + invertedMoveOffset;
2541  TQRect previousMoveResizeGeom = moveResizeGeom;
2542 
2543  // TODO move whole group when moving its leader or when the leader is not mapped?
2544 
2545  // compute bounds
2546  // NOTE: This is duped in checkUnrestrictedMoveResize().
2547  TQRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
2548  int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2549  if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
2550  left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
2551  else // restricted move/resize - keep at least part of the titlebar always visible
2552  {
2553  // how much must remain visible when moved away in that direction
2554  left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
2555  right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
2556  // width/height change with opaque resizing, use the initial ones
2557  titlebar_marge = initialMoveResizeGeom.height();
2558  top_marge = border_bottom;
2559  bottom_marge = border_top;
2560  }
2561 
2562  bool update = false;
2563  if (isResize())
2564  {
2565  // first resize (without checking constraints), then snap, then check bounds, then check constraints
2566  TQRect orig = initialMoveResizeGeom;
2567  Sizemode sizemode = SizemodeAny;
2568  switch ( mode )
2569  {
2570  case PositionTopLeft:
2571  moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
2572  break;
2573  case PositionBottomRight:
2574  moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
2575  break;
2576  case PositionBottomLeft:
2577  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
2578  break;
2579  case PositionTopRight:
2580  moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2581  break;
2582  case PositionTop:
2583  moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
2584  sizemode = SizemodeFixedH; // try not to affect height
2585  break;
2586  case PositionBottom:
2587  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( orig.right(), bottomright.y() ) ) ;
2588  sizemode = SizemodeFixedH;
2589  break;
2590  case PositionLeft:
2591  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
2592  sizemode = SizemodeFixedW;
2593  break;
2594  case PositionRight:
2595  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), orig.bottom() ) ) ;
2596  sizemode = SizemodeFixedW;
2597  break;
2598  case PositionCenter:
2599  default:
2600  assert( false );
2601  break;
2602  }
2603 
2604  // adjust new size to snap to other windows/borders
2605  moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
2606 
2607  // NOTE: This is duped in checkUnrestrictedMoveResize().
2608  if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2609  moveResizeGeom.setBottom( desktopArea.top() + top_marge );
2610  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2611  moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
2612  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2613  moveResizeGeom.setRight( desktopArea.left() + left_marge );
2614  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2615  moveResizeGeom.setLeft(desktopArea.right() - right_marge );
2616  if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2617  moveResizeGeom.setTop( desktopArea.top());
2618 
2619  TQSize size = adjustedSize( moveResizeGeom.size(), sizemode );
2620  // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
2621  topleft = TQPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
2622  bottomright = TQPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
2623  orig = moveResizeGeom;
2624  switch ( mode )
2625  { // these 4 corners ones are copied from above
2626  case PositionTopLeft:
2627  moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
2628  break;
2629  case PositionBottomRight:
2630  moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
2631  break;
2632  case PositionBottomLeft:
2633  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
2634  break;
2635  case PositionTopRight:
2636  moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2637  break;
2638  // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
2639  // Therefore grow to the right/bottom if needed.
2640  // TODO it should probably obey gravity rather than always using right/bottom ?
2641  case PositionTop:
2642  moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2643  break;
2644  case PositionBottom:
2645  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
2646  break;
2647  case PositionLeft:
2648  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), TQPoint( orig.right(), bottomright.y()));
2649  break;
2650  case PositionRight:
2651  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
2652  break;
2653  case PositionCenter:
2654  default:
2655  assert( false );
2656  break;
2657  }
2658  if (moveResizeGeom.size() != previousMoveResizeGeom.size())
2659  update = true;
2660  }
2661  else if (isMove())
2662  {
2663  assert( mode == PositionCenter );
2664  // first move, then snap, then check bounds
2665  moveResizeGeom.moveTopLeft( topleft );
2666  moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
2667  // NOTE: This is duped in checkUnrestrictedMoveResize().
2668  if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2669  moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
2670  // no need to check top_marge, titlebar_marge already handles it
2671  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2672  moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
2673  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2674  moveResizeGeom.moveRight( desktopArea.left() + left_marge );
2675  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2676  moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
2677  if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
2678  update = true;
2679  }
2680  else
2681  assert(false);
2682 
2683  if (update)
2684  {
2685  bool active = isActiveBorderMaximizing();
2686  auto mode = active ? options->tilingMode
2687  : isResize() ? options->resizeMode : options->moveMode;
2688 
2689  if (rules()->checkMoveResizeMode(mode) == Options::Opaque)
2690  {
2691  setGeometry(active ? activeBorderMaximizeGeometry() : moveResizeGeom);
2692  positionGeometryTip();
2693  }
2694  else if (rules()->checkMoveResizeMode(mode) == Options::Transparent)
2695  {
2696  /* It's necessary to move the geometry tip when there's no outline
2697  * shown, otherwise it would cause repaint problems in case
2698  * they overlap; the paint event will come after this,
2699  * so the geometry tip will be painted above the outline
2700  */
2701  clearbound();
2702  positionGeometryTip();
2703  drawbound(active ? activeBorderMaximizeGeometry() : moveResizeGeom);
2704  }
2705  }
2706  if (isMove()) {
2707  workspace()->checkActiveBorder(globalPos, get_tqt_x_time());
2708  }
2709 }
2710 
2711 void Client::setActiveBorderMode( ActiveMaximizingMode mode )
2712 {
2713  activeMode = mode;
2714 }
2715 
2716 ActiveMaximizingMode Client::activeBorderMode() const
2717 {
2718  return activeMode;
2719 }
2720 
2721 void Client::setActiveBorderPos( TQPoint pos )
2722 {
2723  activePos = pos;
2724 }
2725 
2726 TQPoint Client::activeBorderPos() const
2727 {
2728  return activePos;
2729 }
2730 
2731 void Client::setActiveBorder(ActiveBorder border) {
2732  currentActiveBorder = border;
2733 }
2734 
2735 ActiveBorder Client::activeBorder() const {
2736  return currentActiveBorder;
2737 }
2738 
2739 bool Client::isActiveBorderMaximizing() const
2740 {
2741  return activeMaximizing;
2742 }
2743 
2744 void Client::setActiveBorderMaximizing( bool maximizing )
2745 {
2746  activeMaximizing = maximizing;
2747  bool opaque = rules()->checkMoveResizeMode(options->tilingMode) == Options::Opaque;
2748 
2749  if (maximizing || opaque) {
2750  clearbound();
2751  }
2752 
2753  if (maximizing && !opaque) {
2754  drawbound(activeBorderMaximizeGeometry());
2755  }
2756 }
2757 
2758 void Client::cancelActiveBorderMaximizing() {
2759  if (!activeMaximizing) return;
2760  activeMaximizing = false;
2761 
2762  // If we are in transparent mode, we need to clear out the bound we had drawn
2763  if (rules()->checkMoveResizeMode(options->tilingMode) == Options::Transparent) {
2764  clearbound();
2765  }
2766 }
2767 
2768 TQRect Client::activeBorderMaximizeGeometry()
2769 {
2770  TQRect ret;
2771  TQRect max = workspace()->clientArea(MaximizeArea, activePos, workspace()->currentDesktop());
2772  switch (activeBorderMode())
2773  {
2774  case ActiveMaximizeMode:
2775  {
2776  if (maximizeMode() == MaximizeFull)
2777  ret = geometryRestore();
2778  else
2779  ret = max;
2780  break;
2781  }
2782 
2783  case ActiveTilingMode:
2784  {
2785  switch (activeBorder())
2786  {
2787  case ActiveLeft:
2788  {
2789  ret = TQRect( max.x(), max.y(), max.width()/2, max.height() );
2790  break;
2791  }
2792  case ActiveRight:
2793  {
2794  ret = TQRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height() );
2795  break;
2796  }
2797  case ActiveTop:
2798  {
2799  ret = TQRect( max.x(), max.y(), max.width(), max.height()/2 );
2800  break;
2801  }
2802  case ActiveBottom:
2803  {
2804  ret = TQRect( max.x(), max.y() + max.height()/2, max.width(), max.height()/2 );
2805  break;
2806  }
2807  case ActiveTopLeft:
2808  {
2809  ret = TQRect( max.x(), max.y(), max.width()/2, max.height()/2 );
2810  break;
2811  }
2812  case ActiveTopRight:
2813  {
2814  ret = TQRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height()/2 );
2815  break;
2816  }
2817  case ActiveBottomLeft:
2818  {
2819  ret = TQRect( max.x(), max.y() + max.height()/2, max.width()/2, max.height()/2 );
2820  break;
2821  }
2822  case ActiveBottomRight:
2823  {
2824  ret = TQRect( max.x() + max.width()/2, max.y() + max.height()/2, max.width()/2, max.height()/2);
2825  break;
2826  }
2827  }
2828  }
2829  }
2830  return ret;
2831 }
2832 
2833 void Client::tileToBorder(ActiveBorder border) {
2834  if (!isResizable()) return;
2835  activeTiled = true;
2836  if (maximizeMode() == MaximizeRestore)
2837  geom_restore = geometry();
2838  setActiveBorderMode(ActiveTilingMode);
2839  setActiveBorderPos(TQCursor().pos());
2840  setActiveBorder(border);
2841  TQRect geo = activeBorderMaximizeGeometry();
2842  if (geo.isValid() && !geo.isEmpty()) {
2843  setGeometry(geo);
2844  }
2845  workspace()->raiseClient(this);
2846 }
2847 
2848 } // namespace
KWinInternal::Client::move
void move(int x, int y, ForceGeometry_t force=NormalGeometrySet)
Definition: geometry.cpp:1831
KWinInternal::Client::adjustedSize
TQSize adjustedSize(const TQSize &, Sizemode mode=SizemodeAny) const
Definition: geometry.cpp:1071
KWinInternal::Client::desktop
int desktop() const
Definition: client.h:753
KWinInternal::Client::adjustedClientArea
TQRect adjustedClientArea(const TQRect &desktop, const TQRect &area) const
Definition: geometry.cpp:741
KWinInternal::Client::isResizable
bool isResizable() const
Definition: geometry.cpp:1665
KWinInternal::Client::isMovable
bool isMovable() const
Definition: geometry.cpp:1649
KWinInternal::Client::setGeometry
void setGeometry(int x, int y, int w, int h, ForceGeometry_t force=NormalGeometrySet)
Definition: geometry.cpp:1714
KWinInternal
Definition: activation.cpp:34
KWinInternal::Client::setMaximize
void setMaximize(bool vertically, bool horizontally)
Definition: geometry.cpp:1882

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