• 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  // Note: see also getWmNormalHints()
1137  int basew_inc = (xSizeHint.flags & PBaseSize) ? xSizeHint.base_width : xSizeHint.min_width;
1138  int baseh_inc = (xSizeHint.flags & PBaseSize) ? xSizeHint.base_height : xSizeHint.min_height;
1139  w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
1140  h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
1141 // code for aspect ratios based on code from FVWM
1142  /*
1143  * The math looks like this:
1144  *
1145  * minAspectX dwidth maxAspectX
1146  * ---------- <= ------- <= ----------
1147  * minAspectY dheight maxAspectY
1148  *
1149  * If that is multiplied out, then the width and height are
1150  * invalid in the following situations:
1151  *
1152  * minAspectX * dheight > minAspectY * dwidth
1153  * maxAspectX * dheight < maxAspectY * dwidth
1154  *
1155  */
1156  if( xSizeHint.flags & PAspect )
1157  {
1158  double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
1159  double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
1160  double max_aspect_w = xSizeHint.max_aspect.x;
1161  double max_aspect_h = xSizeHint.max_aspect.y;
1162  // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
1163  // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
1164  // and I have no idea how it works, let's hope nobody relies on that.
1165  w -= xSizeHint.base_width;
1166  h -= xSizeHint.base_height;
1167  int max_width = max_size.width() - xSizeHint.base_width;
1168  int min_width = min_size.width() - xSizeHint.base_width;
1169  int max_height = max_size.height() - xSizeHint.base_height;
1170  int min_height = min_size.height() - xSizeHint.base_height;
1171 #define ASPECT_CHECK_GROW_W \
1172  if( min_aspect_w * h > min_aspect_h * w ) \
1173  { \
1174  int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1175  if( w + delta <= max_width ) \
1176  w += delta; \
1177  }
1178 #define ASPECT_CHECK_SHRINK_H_GROW_W \
1179  if( min_aspect_w * h > min_aspect_h * w ) \
1180  { \
1181  int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
1182  if( h - delta >= min_height ) \
1183  h -= delta; \
1184  else \
1185  { \
1186  int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1187  if( w + delta <= max_width ) \
1188  w += delta; \
1189  } \
1190  }
1191 #define ASPECT_CHECK_GROW_H \
1192  if( max_aspect_w * h < max_aspect_h * w ) \
1193  { \
1194  int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1195  if( h + delta <= max_height ) \
1196  h += delta; \
1197  }
1198 #define ASPECT_CHECK_SHRINK_W_GROW_H \
1199  if( max_aspect_w * h < max_aspect_h * w ) \
1200  { \
1201  int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
1202  if( w - delta >= min_width ) \
1203  w -= delta; \
1204  else \
1205  { \
1206  int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1207  if( h + delta <= max_height ) \
1208  h += delta; \
1209  } \
1210  }
1211  switch( mode )
1212  {
1213  case SizemodeAny:
1214 #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
1215  // so that changing aspect ratio to a different value and back keeps the same size (#87298)
1216  {
1217  ASPECT_CHECK_SHRINK_H_GROW_W
1218  ASPECT_CHECK_SHRINK_W_GROW_H
1219  ASPECT_CHECK_GROW_H
1220  ASPECT_CHECK_GROW_W
1221  break;
1222  }
1223 #endif
1224  case SizemodeFixedW:
1225  {
1226  // the checks are order so that attempts to modify height are first
1227  ASPECT_CHECK_GROW_H
1228  ASPECT_CHECK_SHRINK_H_GROW_W
1229  ASPECT_CHECK_SHRINK_W_GROW_H
1230  ASPECT_CHECK_GROW_W
1231  break;
1232  }
1233  case SizemodeFixedH:
1234  {
1235  ASPECT_CHECK_GROW_W
1236  ASPECT_CHECK_SHRINK_W_GROW_H
1237  ASPECT_CHECK_SHRINK_H_GROW_W
1238  ASPECT_CHECK_GROW_H
1239  break;
1240  }
1241  case SizemodeMax:
1242  {
1243  // first checks that try to shrink
1244  ASPECT_CHECK_SHRINK_H_GROW_W
1245  ASPECT_CHECK_SHRINK_W_GROW_H
1246  ASPECT_CHECK_GROW_W
1247  ASPECT_CHECK_GROW_H
1248  break;
1249  }
1250  }
1251 #undef ASPECT_CHECK_SHRINK_H_GROW_W
1252 #undef ASPECT_CHECK_SHRINK_W_GROW_H
1253 #undef ASPECT_CHECK_GROW_W
1254 #undef ASPECT_CHECK_GROW_H
1255  w += xSizeHint.base_width;
1256  h += xSizeHint.base_height;
1257  }
1258  if( !rules()->checkStrictGeometry( false ))
1259  {
1260  // disobey increments and aspect when maximized
1261  if( maximizeMode() & MaximizeHorizontal )
1262  w = w1;
1263  if( maximizeMode() & MaximizeVertical )
1264  h = h1;
1265  }
1266 
1267  if( !noframe )
1268  {
1269  w += border_left + border_right;
1270  h += border_top + border_bottom;
1271  }
1272  return rules()->checkSize( TQSize( w, h ));
1273  }
1274 
1278 void Client::getWmNormalHints()
1279  {
1280  long msize;
1281  if (XGetWMNormalHints(tqt_xdisplay(), window(), &xSizeHint, &msize) == 0 )
1282  xSizeHint.flags = 0;
1283  // set defined values for the fields, even if they're not in flags
1284 
1285  if( ! ( xSizeHint.flags & PMinSize ))
1286  xSizeHint.min_width = xSizeHint.min_height = 0;
1287  if( xSizeHint.flags & PBaseSize )
1288  {
1289  // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
1290  // The other way around PMinSize is not a complete fallback for PBaseSize,
1291  // so that's not handled here.
1292  if( ! ( xSizeHint.flags & PMinSize ))
1293  {
1294  xSizeHint.min_width = xSizeHint.base_width;
1295  xSizeHint.min_height = xSizeHint.base_height;
1296  }
1297  }
1298  else
1299  xSizeHint.base_width = xSizeHint.base_height = 0;
1300  if( ! ( xSizeHint.flags & PMaxSize ))
1301  xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
1302  else
1303  {
1304  xSizeHint.max_width = TQMAX( xSizeHint.max_width, 1 );
1305  xSizeHint.max_height = TQMAX( xSizeHint.max_height, 1 );
1306  }
1307  if( xSizeHint.flags & PResizeInc )
1308  {
1309  xSizeHint.width_inc = kMax( xSizeHint.width_inc, 1 );
1310  xSizeHint.height_inc = kMax( xSizeHint.height_inc, 1 );
1311  }
1312  else
1313  {
1314  xSizeHint.width_inc = 1;
1315  xSizeHint.height_inc = 1;
1316  }
1317  if( xSizeHint.flags & PAspect )
1318  { // no dividing by zero
1319  xSizeHint.min_aspect.y = kMax( xSizeHint.min_aspect.y, 1 );
1320  xSizeHint.max_aspect.y = kMax( xSizeHint.max_aspect.y, 1 );
1321  }
1322  else
1323  {
1324  xSizeHint.min_aspect.x = 1;
1325  xSizeHint.min_aspect.y = INT_MAX;
1326  xSizeHint.max_aspect.x = INT_MAX;
1327  xSizeHint.max_aspect.y = 1;
1328  }
1329  if( ! ( xSizeHint.flags & PWinGravity ))
1330  xSizeHint.win_gravity = NorthWestGravity;
1331  if( isManaged())
1332  { // update to match restrictions
1333  TQSize new_size = adjustedSize();
1334  if( new_size != size() && !isFullScreen())
1335  {
1336  TQRect orig_geometry = geometry();
1337  resizeWithChecks( new_size );
1338  if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1339  {
1340  // try to keep the window in its xinerama screen if possible,
1341  // if that fails at least keep it visible somewhere
1342  TQRect area = workspace()->clientArea( MovementArea, this );
1343  if( area.contains( orig_geometry ))
1344  keepInArea( area );
1345  area = workspace()->clientArea( WorkArea, this );
1346  if( area.contains( orig_geometry ))
1347  keepInArea( area );
1348  }
1349  }
1350  }
1351  updateAllowedActions(); // affects isResizeable()
1352  }
1353 
1354 TQSize Client::minSize() const
1355  {
1356  return rules()->checkMinSize( TQSize( xSizeHint.min_width, xSizeHint.min_height ));
1357  }
1358 
1359 TQSize Client::maxSize() const
1360  {
1361  return rules()->checkMaxSize( TQSize( xSizeHint.max_width, xSizeHint.max_height ));
1362  }
1363 
1369 void Client::sendSyntheticConfigureNotify()
1370  {
1371  XConfigureEvent c;
1372  c.type = ConfigureNotify;
1373  c.send_event = True;
1374  c.event = window();
1375  c.window = window();
1376  c.x = x() + clientPos().x();
1377  c.y = y() + clientPos().y();
1378  c.width = clientSize().width();
1379  c.height = clientSize().height();
1380  c.border_width = 0;
1381  c.above = None;
1382  c.override_redirect = 0;
1383  XSendEvent( tqt_xdisplay(), c.event, True, StructureNotifyMask, (XEvent*)&c );
1384  }
1385 
1386 const TQPoint Client::calculateGravitation( bool invert, int gravity ) const
1387  {
1388  int dx, dy;
1389  dx = dy = 0;
1390 
1391  if( gravity == 0 ) // default (nonsense) value for the argument
1392  gravity = xSizeHint.win_gravity;
1393 
1394 // dx, dy specify how the client window moves to make space for the frame
1395  switch (gravity)
1396  {
1397  case NorthWestGravity: // move down right
1398  default:
1399  dx = border_left;
1400  dy = border_top;
1401  break;
1402  case NorthGravity: // move right
1403  dx = 0;
1404  dy = border_top;
1405  break;
1406  case NorthEastGravity: // move down left
1407  dx = -border_right;
1408  dy = border_top;
1409  break;
1410  case WestGravity: // move right
1411  dx = border_left;
1412  dy = 0;
1413  break;
1414  case CenterGravity:
1415  break; // will be handled specially
1416  case StaticGravity: // don't move
1417  dx = 0;
1418  dy = 0;
1419  break;
1420  case EastGravity: // move left
1421  dx = -border_right;
1422  dy = 0;
1423  break;
1424  case SouthWestGravity: // move up right
1425  dx = border_left ;
1426  dy = -border_bottom;
1427  break;
1428  case SouthGravity: // move up
1429  dx = 0;
1430  dy = -border_bottom;
1431  break;
1432  case SouthEastGravity: // move up left
1433  dx = -border_right;
1434  dy = -border_bottom;
1435  break;
1436  }
1437  if( gravity != CenterGravity )
1438  { // translate from client movement to frame movement
1439  dx -= border_left;
1440  dy -= border_top;
1441  }
1442  else
1443  { // center of the frame will be at the same position client center without frame would be
1444  dx = - ( border_left + border_right ) / 2;
1445  dy = - ( border_top + border_bottom ) / 2;
1446  }
1447  if( !invert )
1448  return TQPoint( x() + dx, y() + dy );
1449  else
1450  return TQPoint( x() - dx, y() - dy );
1451  }
1452 
1453 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
1454  {
1455  if( gravity == 0 ) // default (nonsense) value for the argument
1456  gravity = xSizeHint.win_gravity;
1457  if( value_mask & ( CWX | CWY ))
1458  {
1459  TQPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
1460  if ( value_mask & CWX )
1461  new_pos.setX( rx );
1462  if ( value_mask & CWY )
1463  new_pos.setY( ry );
1464 
1465  // clever(?) workaround for applications like xv that want to set
1466  // the location to the current location but miscalculate the
1467  // frame size due to twin being a double-reparenting window
1468  // manager
1469  if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
1470  && gravity == NorthWestGravity && !from_tool )
1471  {
1472  new_pos.setX( x());
1473  new_pos.setY( y());
1474  }
1475 
1476  int nw = clientSize().width();
1477  int nh = clientSize().height();
1478  if ( value_mask & CWWidth )
1479  nw = rw;
1480  if ( value_mask & CWHeight )
1481  nh = rh;
1482  TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
1483  new_pos = rules()->checkPosition( new_pos );
1484 
1485  // TODO what to do with maximized windows?
1486  if ( maximizeMode() != MaximizeFull
1487  || ns != size())
1488  {
1489  TQRect orig_geometry = geometry();
1490  GeometryUpdatesPostponer blocker( this );
1491  move( new_pos );
1492  plainResize( ns );
1493  setGeometry( TQRect( calculateGravitation( false, gravity ), size()));
1494  updateFullScreenHack( TQRect( new_pos, TQSize( nw, nh )));
1495  TQRect area = workspace()->clientArea( WorkArea, this );
1496  if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
1497  && area.contains( orig_geometry ))
1498  keepInArea( area );
1499 
1500  // this is part of the kicker-xinerama-hack... it should be
1501  // safe to remove when kicker gets proper ExtendedStrut support;
1502  // see Workspace::updateClientArea() and
1503  // Client::adjustedClientArea()
1504  if (hasStrut ())
1505  workspace() -> updateClientArea ();
1506  }
1507  }
1508 
1509  if ( value_mask & (CWWidth | CWHeight )
1510  && ! ( value_mask & ( CWX | CWY )) ) // pure resize
1511  {
1512  int nw = clientSize().width();
1513  int nh = clientSize().height();
1514  if ( value_mask & CWWidth )
1515  nw = rw;
1516  if ( value_mask & CWHeight )
1517  nh = rh;
1518  TQSize ns = sizeForClientSize( TQSize( nw, nh ) );
1519 
1520  if( ns != size()) // don't restore if some app sets its own size again
1521  {
1522  TQRect orig_geometry = geometry();
1523  GeometryUpdatesPostponer blocker( this );
1524  int save_gravity = xSizeHint.win_gravity;
1525  xSizeHint.win_gravity = gravity;
1526  resizeWithChecks( ns );
1527  xSizeHint.win_gravity = save_gravity;
1528  updateFullScreenHack( TQRect( calculateGravitation( true, xSizeHint.win_gravity ), TQSize( nw, nh )));
1529  if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1530  {
1531  // try to keep the window in its xinerama screen if possible,
1532  // if that fails at least keep it visible somewhere
1533  TQRect area = workspace()->clientArea( MovementArea, this );
1534  if( area.contains( orig_geometry ))
1535  keepInArea( area );
1536  area = workspace()->clientArea( WorkArea, this );
1537  if( area.contains( orig_geometry ))
1538  keepInArea( area );
1539  }
1540  }
1541  }
1542  // No need to send synthetic configure notify event here, either it's sent together
1543  // with geometry change, or there's no need to send it.
1544  // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
1545  }
1546 
1547 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
1548  {
1549  if( shade_geometry_change )
1550  assert( false );
1551  else if( isShade())
1552  {
1553  if( h == border_top + border_bottom )
1554  {
1555  kdWarning() << "Shaded geometry passed for size:" << endl;
1556  kdWarning() << kdBacktrace() << endl;
1557  }
1558  }
1559  int newx = x();
1560  int newy = y();
1561  TQRect area = workspace()->clientArea( WorkArea, this );
1562  // don't allow growing larger than workarea
1563  if( w > area.width())
1564  w = area.width();
1565  if( h > area.height())
1566  h = area.height();
1567  TQSize tmp = adjustedSize( TQSize( w, h )); // checks size constraints, including min/max size
1568  w = tmp.width();
1569  h = tmp.height();
1570  switch( xSizeHint.win_gravity )
1571  {
1572  case NorthWestGravity: // top left corner doesn't move
1573  default:
1574  break;
1575  case NorthGravity: // middle of top border doesn't move
1576  newx = ( newx + width() / 2 ) - ( w / 2 );
1577  break;
1578  case NorthEastGravity: // top right corner doesn't move
1579  newx = newx + width() - w;
1580  break;
1581  case WestGravity: // middle of left border doesn't move
1582  newy = ( newy + height() / 2 ) - ( h / 2 );
1583  break;
1584  case CenterGravity: // middle point doesn't move
1585  newx = ( newx + width() / 2 ) - ( w / 2 );
1586  newy = ( newy + height() / 2 ) - ( h / 2 );
1587  break;
1588  case StaticGravity: // top left corner of _client_ window doesn't move
1589  // since decoration doesn't change, equal to NorthWestGravity
1590  break;
1591  case EastGravity: // // middle of right border doesn't move
1592  newx = newx + width() - w;
1593  newy = ( newy + height() / 2 ) - ( h / 2 );
1594  break;
1595  case SouthWestGravity: // bottom left corner doesn't move
1596  newy = newy + height() - h;
1597  break;
1598  case SouthGravity: // middle of bottom border doesn't move
1599  newx = ( newx + width() / 2 ) - ( w / 2 );
1600  newy = newy + height() - h;
1601  break;
1602  case SouthEastGravity: // bottom right corner doesn't move
1603  newx = newx + width() - w;
1604  newy = newy + height() - h;
1605  break;
1606  }
1607  // if it would be moved outside of workarea, keep it inside,
1608  // see also Client::computeWorkareaDiff()
1609  if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
1610  {
1611  if( newx < area.left())
1612  newx = area.left();
1613  if( newx + w > area.right() + 1 )
1614  newx = area.right() + 1 - w;
1615  assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
1616  }
1617  if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
1618  {
1619  if( newy < area.top())
1620  newy = area.top();
1621  if( newy + h > area.bottom() + 1 )
1622  newy = area.bottom() + 1 - h;
1623  assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
1624  }
1625  setGeometry( newx, newy, w, h, force );
1626  }
1627 
1628 // _NET_MOVERESIZE_WINDOW
1629 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
1630  {
1631  int gravity = flags & 0xff;
1632  int value_mask = 0;
1633  if( flags & ( 1 << 8 ))
1634  value_mask |= CWX;
1635  if( flags & ( 1 << 9 ))
1636  value_mask |= CWY;
1637  if( flags & ( 1 << 10 ))
1638  value_mask |= CWWidth;
1639  if( flags & ( 1 << 11 ))
1640  value_mask |= CWHeight;
1641  configureRequest( value_mask, x, y, width, height, gravity, true );
1642  }
1643 
1648 bool Client::isMovable() const
1649  {
1650  if( !motif_may_move || isFullScreen())
1651  return false;
1652  if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1653  return false;
1654  if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1655  return false;
1656  if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
1657  return false;
1658  return true;
1659  }
1660 
1664 bool Client::isResizable() const
1665  {
1666  if( !motif_may_resize || isFullScreen())
1667  return false;
1668  if( isSpecialWindow() )
1669  return false;
1670  if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1671  return false;
1672  if( rules()->checkSize( TQSize()).isValid()) // forced size
1673  return false;
1674 
1675  TQSize min = minSize();
1676  TQSize max = maxSize();
1677  return min.width() < max.width() || min.height() < max.height();
1678  }
1679 
1680 /*
1681  Returns whether the window is maximizable or not
1682  */
1683 bool Client::isMaximizable() const
1684  {
1685  if( isModalSystemNotification())
1686  return false;
1687  { // isMovable() and isResizable() may be false for maximized windows
1688  // with moving/resizing maximized windows disabled
1689  TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
1690  if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
1691  return false;
1692  }
1693  if ( maximizeMode() != MaximizeRestore )
1694  return true;
1695  TQSize max = maxSize();
1696 #if 0
1697  if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
1698  return false;
1699 #else
1700  // apparently there are enough apps which specify some arbitrary value
1701  // for their maximum size just for the fun of it
1702  TQSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
1703  if( max.width() < areasize.width() || max.height() < areasize.height())
1704  return false;
1705 #endif
1706  return true;
1707  }
1708 
1709 
1713 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
1714  {
1715  // this code is also duplicated in Client::plainResize()
1716  // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
1717  // simply because there are too many places dealing with geometry. Those places
1718  // ignore shaded state and use normal geometry, which they usually should get
1719  // from adjustedSize(). Such geometry comes here, and if the window is shaded,
1720  // the geometry is used only for client_size, since that one is not used when
1721  // shading. Then the frame geometry is adjusted for the shaded geometry.
1722  // This gets more complicated in the case the code does only something like
1723  // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
1724  // Such code is wrong and should be changed to handle the case when the window is shaded,
1725  // for example using Client::clientSize().
1726  if( shade_geometry_change )
1727  ; // nothing
1728  else if( isShade())
1729  {
1730  if( h == border_top + border_bottom )
1731  {
1732  kdDebug() << "Shaded geometry passed for size:" << endl;
1733  kdDebug() << kdBacktrace() << endl;
1734  }
1735  else
1736  {
1737  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1738  h = border_top + border_bottom;
1739  }
1740  }
1741  else
1742  {
1743  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1744  }
1745  if( force == NormalGeometrySet && frame_geometry == TQRect( x, y, w, h ))
1746  return;
1747  frame_geometry = TQRect( x, y, w, h );
1748  updateWorkareaDiffs();
1749  if( postpone_geometry_updates != 0 )
1750  {
1751  pending_geometry_update = true;
1752  return;
1753  }
1754  resizeDecoration( TQSize( w, h ));
1755  XMoveResizeWindow( tqt_xdisplay(), frameId(), x, y, w, h );
1756 // resizeDecoration( TQSize( w, h ));
1757  if( !isShade())
1758  {
1759  TQSize cs = clientSize();
1760  XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
1761  cs.width(), cs.height());
1762  XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
1763  }
1764  updateShape();
1765  // SELI TODO won't this be too expensive?
1766  updateWorkareaDiffs();
1767  sendSyntheticConfigureNotify();
1768  updateWindowRules();
1769  checkMaximizeGeometry();
1770  workspace()->checkActiveScreen( this );
1771  }
1772 
1773 void Client::plainResize( int w, int h, ForceGeometry_t force )
1774  {
1775  // this code is also duplicated in Client::setGeometry(), and it's also commented there
1776  if( shade_geometry_change )
1777  ; // nothing
1778  else if( isShade())
1779  {
1780  if( h == border_top + border_bottom )
1781  {
1782  kdDebug() << "Shaded geometry passed for size:" << endl;
1783  kdDebug() << kdBacktrace() << endl;
1784  }
1785  else
1786  {
1787  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1788  h = border_top + border_bottom;
1789  }
1790  }
1791  else
1792  {
1793  client_size = TQSize( w - border_left - border_right, h - border_top - border_bottom );
1794  }
1795  if( TQSize( w, h ) != rules()->checkSize( TQSize( w, h )))
1796  {
1797  kdDebug() << "forced size fail:" << TQSize( w,h ) << ":" << rules()->checkSize( TQSize( w, h )) << endl;
1798  kdDebug() << kdBacktrace() << endl;
1799  }
1800  if( force == NormalGeometrySet && frame_geometry.size() == TQSize( w, h ))
1801  return;
1802  frame_geometry.setSize( TQSize( w, h ));
1803  updateWorkareaDiffs();
1804  if( postpone_geometry_updates != 0 )
1805  {
1806  pending_geometry_update = true;
1807  return;
1808  }
1809  resizeDecoration( TQSize( w, h ));
1810  XResizeWindow( tqt_xdisplay(), frameId(), w, h );
1811 // resizeDecoration( TQSize( w, h ));
1812  if( !isShade())
1813  {
1814  TQSize cs = clientSize();
1815  XMoveResizeWindow( tqt_xdisplay(), wrapperId(), clientPos().x(), clientPos().y(),
1816  cs.width(), cs.height());
1817  XMoveResizeWindow( tqt_xdisplay(), window(), 0, 0, cs.width(), cs.height());
1818  }
1819  updateShape();
1820  updateWorkareaDiffs();
1821  sendSyntheticConfigureNotify();
1822  updateWindowRules();
1823  checkMaximizeGeometry();
1824  workspace()->checkActiveScreen( this );
1825  }
1826 
1830 void Client::move( int x, int y, ForceGeometry_t force )
1831  {
1832  if( force == NormalGeometrySet && frame_geometry.topLeft() == TQPoint( x, y ))
1833  return;
1834  frame_geometry.moveTopLeft( TQPoint( x, y ));
1835  updateWorkareaDiffs();
1836  if( postpone_geometry_updates != 0 )
1837  {
1838  pending_geometry_update = true;
1839  return;
1840  }
1841  XMoveWindow( tqt_xdisplay(), frameId(), x, y );
1842  sendSyntheticConfigureNotify();
1843  updateWindowRules();
1844  checkMaximizeGeometry();
1845  workspace()->checkActiveScreen( this );
1846  }
1847 
1848 
1849 void Client::postponeGeometryUpdates( bool postpone )
1850  {
1851  if( postpone )
1852  {
1853  if( postpone_geometry_updates == 0 )
1854  pending_geometry_update = false;
1855  ++postpone_geometry_updates;
1856  }
1857  else
1858  {
1859  if( --postpone_geometry_updates == 0 )
1860  {
1861  if( pending_geometry_update )
1862  {
1863  if( isShade())
1864  setGeometry( TQRect( pos(), adjustedSize()), ForceGeometrySet );
1865  else
1866  setGeometry( geometry(), ForceGeometrySet );
1867  pending_geometry_update = false;
1868  }
1869  }
1870  }
1871  }
1872 
1873 void Client::maximize( MaximizeMode m )
1874  {
1875  setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
1876  }
1877 
1881 void Client::setMaximize( bool vertically, bool horizontally )
1882  { // changeMaximize() flips the state, so change from set->flip
1883  changeMaximize(
1884  max_mode & MaximizeVertical ? !vertically : vertically,
1885  max_mode & MaximizeHorizontal ? !horizontally : horizontally,
1886  false );
1887  }
1888 
1889 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
1890  {
1891  if( !isMaximizable())
1892  return;
1893 
1894  MaximizeMode old_mode = max_mode;
1895  // 'adjust == true' means to update the size only, e.g. after changing workspace size
1896  if( !adjust )
1897  {
1898  if( vertical )
1899  max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
1900  if( horizontal )
1901  max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
1902  }
1903 
1904  max_mode = rules()->checkMaximize( max_mode );
1905  if( !adjust && max_mode == old_mode )
1906  return;
1907 
1908  GeometryUpdatesPostponer blocker( this );
1909 
1910  // maximing one way and unmaximizing the other way shouldn't happen
1911  Q_ASSERT( !( vertical && horizontal )
1912  || ((( max_mode & MaximizeVertical ) != 0 ) == (( max_mode & MaximizeHorizontal ) != 0 )));
1913 
1914  TQRect clientArea = workspace()->clientArea( MaximizeArea, this );
1915 
1916  // save sizes for restoring, if maximalizing
1917  if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
1918  {
1919  geom_restore.setTop( y());
1920  geom_restore.setHeight( height());
1921  }
1922  if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
1923  {
1924  geom_restore.setLeft( x());
1925  geom_restore.setWidth( width());
1926  }
1927 
1928  if( !adjust )
1929  {
1930  if(( vertical && !(old_mode & MaximizeVertical ))
1931  || ( horizontal && !( old_mode & MaximizeHorizontal )))
1932  Notify::raise( Notify::Maximize );
1933  else
1934  Notify::raise( Notify::UnMaximize );
1935  }
1936 
1937  if( decoration != NULL ) // decorations may turn off some borders when maximized
1938  decoration->borders( border_left, border_right, border_top, border_bottom );
1939 
1940  // restore partial maximizations
1941  if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
1942  {
1943  if ( maximizeModeRestore()==MaximizeVertical )
1944  {
1945  max_mode = MaximizeVertical;
1946  maxmode_restore = MaximizeRestore;
1947  }
1948  if ( maximizeModeRestore()==MaximizeHorizontal )
1949  {
1950  max_mode = MaximizeHorizontal;
1951  maxmode_restore = MaximizeRestore;
1952  }
1953  }
1954 
1955  switch (max_mode)
1956  {
1957 
1958  case MaximizeVertical:
1959  {
1960  if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
1961  {
1962  if( geom_restore.width() == 0 )
1963  { // needs placement
1964  plainResize( adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH ));
1965  workspace()->placeSmart( this, clientArea );
1966  }
1967  else
1968  setGeometry( TQRect(TQPoint( geom_restore.x(), clientArea.top()),
1969  adjustedSize(TQSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
1970  }
1971  else
1972  setGeometry( TQRect(TQPoint(x(), clientArea.top()),
1973  adjustedSize(TQSize(width(), clientArea.height()), SizemodeFixedH )), ForceGeometrySet);
1974  info->setState( NET::MaxVert, NET::Max );
1975  break;
1976  }
1977 
1978  case MaximizeHorizontal:
1979  {
1980  if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
1981  {
1982  if( geom_restore.height() == 0 )
1983  { // needs placement
1984  plainResize( adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW ));
1985  workspace()->placeSmart( this, clientArea );
1986  }
1987  else
1988  setGeometry( TQRect( TQPoint(clientArea.left(), geom_restore.y()),
1989  adjustedSize(TQSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), ForceGeometrySet);
1990  }
1991  else
1992  setGeometry( TQRect( TQPoint(clientArea.left(), y()),
1993  adjustedSize(TQSize(clientArea.width(), height()), SizemodeFixedW )), ForceGeometrySet);
1994  info->setState( NET::MaxHoriz, NET::Max );
1995  break;
1996  }
1997 
1998  case MaximizeRestore:
1999  {
2000  TQRect restore = geometry();
2001  // when only partially maximized, geom_restore may not have the other dimension remembered
2002  if( old_mode & MaximizeVertical )
2003  {
2004  restore.setTop( geom_restore.top());
2005  restore.setBottom( geom_restore.bottom());
2006  }
2007  if( old_mode & MaximizeHorizontal )
2008  {
2009  restore.setLeft( geom_restore.left());
2010  restore.setRight( geom_restore.right());
2011  }
2012  if( !restore.isValid())
2013  {
2014  TQSize s = TQSize( clientArea.width()*2/3, clientArea.height()*2/3 );
2015  if( geom_restore.width() > 0 )
2016  s.setWidth( geom_restore.width());
2017  if( geom_restore.height() > 0 )
2018  s.setHeight( geom_restore.height());
2019  plainResize( adjustedSize( s ));
2020  workspace()->placeSmart( this, clientArea );
2021  restore = geometry();
2022  if( geom_restore.width() > 0 )
2023  restore.moveLeft( geom_restore.x());
2024  if( geom_restore.height() > 0 )
2025  restore.moveTop( geom_restore.y());
2026  }
2027  setGeometry( restore, ForceGeometrySet );
2028  info->setState( 0, NET::Max );
2029  break;
2030  }
2031 
2032  case MaximizeFull:
2033  {
2034  if( !adjust )
2035  {
2036  if( old_mode & MaximizeVertical )
2037  maxmode_restore = MaximizeVertical;
2038  if( old_mode & MaximizeHorizontal )
2039  maxmode_restore = MaximizeHorizontal;
2040  }
2041  TQSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
2042  TQRect r = TQRect(clientArea.topLeft(), adjSize);
2043  setGeometry( r, ForceGeometrySet );
2044  info->setState( NET::Max, NET::Max );
2045  break;
2046  }
2047  default:
2048  break;
2049  }
2050 
2051  updateAllowedActions();
2052  if( decoration != NULL )
2053  decoration->maximizeChange();
2054  updateWindowRules();
2055  }
2056 
2057 void Client::resetMaximize()
2058  {
2059  if( max_mode == MaximizeRestore )
2060  return;
2061  max_mode = MaximizeRestore;
2062  Notify::raise( Notify::UnMaximize );
2063  info->setState( 0, NET::Max );
2064  updateAllowedActions();
2065  if( decoration != NULL )
2066  decoration->borders( border_left, border_right, border_top, border_bottom );
2067  if( isShade())
2068  setGeometry( TQRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
2069  else
2070  setGeometry( geometry(), ForceGeometrySet );
2071  if( decoration != NULL )
2072  decoration->maximizeChange();
2073  }
2074 
2075 void Client::checkMaximizeGeometry()
2076  {
2077  // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
2078  // when after the condition is no longer true
2079  if( isShade())
2080  return;
2081  if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2082  return;
2083  // Just in case.
2084  static int recursion_protection = 0;
2085  if( recursion_protection > 3 )
2086  {
2087  kdWarning( 1212 ) << "Check maximize overflow - you loose!" << endl;
2088  kdWarning( 1212 ) << kdBacktrace() << endl;
2089  return;
2090  }
2091  ++recursion_protection;
2092  TQRect max_area = workspace()->clientArea( MaximizeArea, this );
2093  if( geometry() == max_area )
2094  {
2095  if( max_mode != MaximizeFull )
2096  maximize( MaximizeFull );
2097  }
2098  else if( x() == max_area.left() && width() == max_area.width())
2099  {
2100  if( max_mode != MaximizeHorizontal )
2101  maximize( MaximizeHorizontal );
2102  }
2103  else if( y() == max_area.top() && height() == max_area.height())
2104  {
2105  if( max_mode != MaximizeVertical )
2106  maximize( MaximizeVertical );
2107  }
2108  else if( max_mode != MaximizeRestore )
2109  {
2110  resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
2111  }
2112  --recursion_protection;
2113  }
2114 
2115 bool Client::isFullScreenable( bool fullscreen_hack ) const
2116  {
2117  if( !rules()->checkFullScreen( true ))
2118  return false;
2119  if( fullscreen_hack )
2120  return isNormalWindow();
2121  if( rules()->checkStrictGeometry( false ))
2122  {
2123  // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
2124  TQRect fsarea = workspace()->clientArea( FullScreenArea, this );
2125  if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
2126  return false;
2127  }
2128  // don't check size constrains - some apps request fullscreen despite requesting fixed size
2129  return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
2130  }
2131 
2132 bool Client::userCanSetFullScreen() const
2133  {
2134  if( fullscreen_mode == FullScreenHack )
2135  return false;
2136  if( !isFullScreenable( false ))
2137  return false;
2138  // isMaximizable() returns false if fullscreen
2139  TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
2140  return isNormalWindow() && isMaximizable();
2141  }
2142 
2143 void Client::setFullScreen( bool set, bool user )
2144  {
2145  if( !isFullScreen() && !set )
2146  return;
2147  if( fullscreen_mode == FullScreenHack )
2148  return;
2149  if( user && !userCanSetFullScreen())
2150  return;
2151  set = rules()->checkFullScreen( set );
2152  setShade( ShadeNone );
2153  bool was_fs = isFullScreen();
2154  if( !was_fs )
2155  geom_fs_restore = geometry();
2156  fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
2157  if( was_fs == isFullScreen())
2158  return;
2159  StackingUpdatesBlocker blocker1( workspace());
2160  GeometryUpdatesPostponer blocker2( this );
2161  workspace()->updateClientLayer( this ); // active fullscreens get different layer
2162  info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
2163  updateDecoration( false, false );
2164  if( isFullScreen())
2165  setGeometry( workspace()->clientArea( FullScreenArea, this ));
2166  else
2167  {
2168  if( !geom_fs_restore.isNull())
2169  setGeometry( TQRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
2170  // TODO isShaded() ?
2171  else
2172  { // does this ever happen?
2173  setGeometry( workspace()->clientArea( MaximizeArea, this ));
2174  }
2175  }
2176  updateWindowRules();
2177  }
2178 
2179 int Client::checkFullScreenHack( const TQRect& geom ) const
2180  {
2181  // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
2182  if( noBorder() && !isUserNoBorder() && isFullScreenable( true ))
2183  {
2184  if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
2185  return 2; // full area fullscreen hack
2186  if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
2187  return 1; // xinerama-aware fullscreen hack
2188  }
2189  return 0;
2190  }
2191 
2192 void Client::updateFullScreenHack( const TQRect& geom )
2193  {
2194  int type = checkFullScreenHack( geom );
2195  if( fullscreen_mode == FullScreenNone && type != 0 )
2196  {
2197  fullscreen_mode = FullScreenHack;
2198  updateDecoration( false, false );
2199  TQRect geom;
2200  if( rules()->checkStrictGeometry( false ))
2201  {
2202  geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
2203  ? workspace()->clientArea( FullArea, geom.center(), desktop())
2204  : workspace()->clientArea( ScreenArea, geom.center(), desktop());
2205  }
2206  else
2207  geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
2208  setGeometry( geom );
2209  }
2210  else if( fullscreen_mode == FullScreenHack && type == 0 )
2211  {
2212  fullscreen_mode = FullScreenNone;
2213  updateDecoration( false, false );
2214  // whoever called this must setup correct geometry
2215  }
2216  StackingUpdatesBlocker blocker( workspace());
2217  workspace()->updateClientLayer( this ); // active fullscreens get different layer
2218  }
2219 
2220 static TQRect* visible_bound = nullptr;
2221 static GeometryTip* geometryTip = nullptr;
2222 
2223 void Client::drawbound( const TQRect& geom )
2224  {
2225  assert( visible_bound == NULL );
2226  visible_bound = new TQRect( geom );
2227  doDrawbound( *visible_bound, false );
2228  }
2229 
2230 void Client::clearbound()
2231  {
2232  if( visible_bound == NULL )
2233  return;
2234  doDrawbound( *visible_bound, true );
2235  delete visible_bound;
2236  visible_bound = 0;
2237  }
2238 
2239 void Client::doDrawbound( const TQRect& geom, bool clear )
2240  {
2241  if( decoration != NULL && decoration->drawbound( geom, clear ))
2242  return; // done by decoration
2243  TQPainter p ( workspace()->desktopWidget() );
2244  p.setPen( TQPen( TQt::white, 5 ) );
2245  p.setRasterOp( TQt::XorROP );
2246  // the line is 5 pixel thick, so compensate for the extra two pixels
2247  // on outside (#88657)
2248  TQRect g = geom;
2249  if( g.width() > 5 )
2250  {
2251  g.setLeft( g.left() + 2 );
2252  g.setRight( g.right() - 2 );
2253  }
2254  if( g.height() > 5 )
2255  {
2256  g.setTop( g.top() + 2 );
2257  g.setBottom( g.bottom() - 2 );
2258  }
2259  p.drawRect( g );
2260  }
2261 
2262 void Client::positionGeometryTip() {
2263  assert(isMove() || isResize());
2264 
2265  // Position and Size display
2266  if (options->showGeometryTip()) {
2267  if (!geometryTip) {
2268  // save under is not necessary with opaque, and seem to make things slower
2269  bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2270  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
2271  geometryTip = new GeometryTip( &xSizeHint, save_under );
2272  }
2273 
2274  // position of the frame, size of the window itself
2275  TQRect wgeom(isActiveBorderMaximizing() ? activeBorderMaximizeGeometry() : moveResizeGeom);
2276  wgeom.setWidth(wgeom.width() - (width() - clientSize().width()));
2277  wgeom.setHeight(isShade() ? 0 : wgeom.height() - (height() - clientSize().height()));
2278 
2279  geometryTip->setGeometry(wgeom);
2280  if (!geometryTip->isVisible()) {
2281  geometryTip->show();
2282  geometryTip->raise();
2283  }
2284  }
2285 }
2286 
2287 class EatAllPaintEvents
2288  : public TQObject
2289  {
2290  protected:
2291  virtual bool eventFilter( TQObject* o, TQEvent* e )
2292  { return e->type() == TQEvent::Paint && o != geometryTip; }
2293  };
2294 
2295 static EatAllPaintEvents* eater = 0;
2296 
2297 bool Client::startMoveResize()
2298 {
2299  assert( !moveResizeMode );
2300  assert( TQWidget::keyboardGrabber() == NULL );
2301  assert( TQWidget::mouseGrabber() == NULL );
2302  if( TQApplication::activePopupWidget() != NULL )
2303  return false; // popups have grab
2304  bool has_grab = false;
2305  // This reportedly improves smoothness of the moveresize operation,
2306  // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
2307  // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
2308  XSetWindowAttributes attrs;
2309  TQRect r = workspace()->clientArea( FullArea, this );
2310  move_resize_grab_window = XCreateWindow( tqt_xdisplay(), workspace()->rootWin(), r.x(), r.y(),
2311  r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
2312  XMapRaised( tqt_xdisplay(), move_resize_grab_window );
2313  if( XGrabPointer( tqt_xdisplay(), move_resize_grab_window, False,
2314  ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
2315  GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), get_tqt_x_time() ) == Success )
2316  has_grab = true;
2317  if( XGrabKeyboard( tqt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, get_tqt_x_time() ) == Success )
2318  has_grab = true;
2319  if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
2320  {
2321  XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
2322  move_resize_grab_window = None;
2323  return false;
2324  }
2325 
2326  removeShadow();
2327  moveResizeMode = true;
2328  initialMoveResizeGeom = geometry();
2329 
2330  if ( maximizeMode() != MaximizeRestore )
2331  {
2332  if (options->resetMaximizedWindowGeometry() && isMove()) {
2333  /* Original geometry might be smaller than the tiled one, so the
2334  * mouse pointer might appear off-window when untiling.
2335  * Here we center the window horizontally under the mouse pointer.
2336  * This should work with most window decorations.
2337  */
2338  geom_restore.moveLeft(TQCursor::pos().x() - (geom_restore.width() / 2));
2339  moveOffset.setX(TQCursor::pos().x() - geom_restore.x());
2340 
2341  setGeometry(geom_restore);
2342 
2343  maximize(MaximizeRestore);
2344  }
2345  else {
2346  resetMaximize();
2347  }
2348  activeTiled = false;
2349  }
2350 
2351  moveResizeGeom = geometry();
2352  workspace()->setClientIsMoving(this);
2353  checkUnrestrictedMoveResize();
2354 
2355  // rule out non opaque windows from useless translucency settings, maybe resizes?
2356  if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
2357  {
2358  setShadowSize(0);
2359  }
2360 
2361  if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
2362  {
2363  savedOpacity_ = opacity_;
2364  setOpacity(options->translucentMovingWindows ? options->movingWindowOpacity : Opacity::Opaque);
2365  }
2366 
2367  if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2368  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2369  {
2370  grabXServer();
2371  tdeApp->sendPostedEvents();
2372  // we have server grab -> nothing should cause paint events
2373  // unfortunately, that's not completely true, Qt may generate
2374  // paint events on some widgets due to FocusIn(?)
2375  // eat them, otherwise XOR painting will be broken (#58054)
2376  // paint events for the geometrytip need to be allowed, though
2377  // eater = new EatAllPaintEvents;
2378 // not needed anymore? tdeApp->installEventFilter( eater );
2379  }
2380  Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
2381 
2382  if (options->activeBorders() == Options::ActiveSwitchOnMove ||
2383  options->activeBorders() == Options::ActiveTileMaximize ||
2384  options->activeBorders() == Options::ActiveTileOnly)
2385 
2386  {
2387  workspace()->reserveActiveBorderSwitching(true);
2388  }
2389 
2390  return true;
2391 }
2392 
2393 void Client::finishMoveResize( bool cancel )
2394 {
2395  leaveMoveResize();
2396 
2397  if (!isActiveBorderMaximizing()) {
2398  setGeometry(cancel ? initialMoveResizeGeom : moveResizeGeom);
2399  }
2400 
2401  else
2402  {
2403  kdDebug() <<"finishing moveresize in active mode, cancel is " << cancel << endl;
2404  activeMaximizing = false;
2405  activeTiled = true;
2406  geom_restore = initialMoveResizeGeom;
2407  switch (activeMode)
2408  {
2409  case ActiveMaximizeMode: {
2410  if (!cancel) {
2411  bool full = (maximizeMode() == MaximizeFull);
2412  setMaximize(!full, !full);
2413  }
2414  break;
2415  }
2416  default:
2417  setGeometry(cancel ? initialMoveResizeGeom
2418  : activeBorderMaximizeGeometry());
2419  }
2420  }
2421 
2422  checkMaximizeGeometry();
2423 // FRAME update();
2424  Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
2425 }
2426 
2427 void Client::leaveMoveResize()
2428 {
2429  // rule out non opaque windows from useless translucency settings, maybe resizes?
2430  if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque)
2431  setOpacity(savedOpacity_);
2432  if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove))
2433  updateShadowSize();
2434  clearbound();
2435  if (geometryTip)
2436  {
2437  geometryTip->hide();
2438  delete geometryTip;
2439  geometryTip = NULL;
2440  }
2441  if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2442  || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2443  ungrabXServer();
2444  XUngrabKeyboard( tqt_xdisplay(), get_tqt_x_time() );
2445  XUngrabPointer( tqt_xdisplay(), get_tqt_x_time() );
2446  XDestroyWindow( tqt_xdisplay(), move_resize_grab_window );
2447  move_resize_grab_window = None;
2448  workspace()->setClientIsMoving(0);
2449  if( move_faked_activity )
2450  workspace()->unfakeActivity( this );
2451  move_faked_activity = false;
2452  moveResizeMode = false;
2453  delete eater;
2454  eater = 0;
2455  if (options->shadowEnabled(isActive()))
2456  {
2457  drawIntersectingShadows();
2458  updateOpacityCache();
2459  }
2460 
2461  if (options->activeBorders() == Options::ActiveSwitchOnMove ||
2462  options->activeBorders() == Options::ActiveTileMaximize ||
2463  options->activeBorders() == Options::ActiveTileOnly)
2464  {
2465  workspace()->reserveActiveBorderSwitching(false);
2466  }
2467 }
2468 
2469 // This function checks if it actually makes sense to perform a restricted move/resize.
2470 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
2471 // a restricted move resize, because then e.g. resize would also move the window (#74555).
2472 // NOTE: Most of it is duplicated from handleMoveResize().
2473 void Client::checkUnrestrictedMoveResize()
2474  {
2475  if( unrestrictedMoveResize )
2476  return;
2477  TQRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
2478  int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2479  // restricted move/resize - keep at least part of the titlebar always visible
2480  // how much must remain visible when moved away in that direction
2481  left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
2482  right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
2483  // width/height change with opaque resizing, use the initial ones
2484  titlebar_marge = initialMoveResizeGeom.height();
2485  top_marge = border_bottom;
2486  bottom_marge = border_top;
2487  if( isResize())
2488  {
2489  if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2490  unrestrictedMoveResize = true;
2491  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2492  unrestrictedMoveResize = true;
2493  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2494  unrestrictedMoveResize = true;
2495  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2496  unrestrictedMoveResize = true;
2497  if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2498  unrestrictedMoveResize = true;
2499  }
2500  if( isMove())
2501  {
2502  if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2503  unrestrictedMoveResize = true;
2504  // no need to check top_marge, titlebar_marge already handles it
2505  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2506  unrestrictedMoveResize = true;
2507  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2508  unrestrictedMoveResize = true;
2509  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2510  unrestrictedMoveResize = true;
2511  }
2512  }
2513 
2514 void Client::handleMoveResize(int x, int y, int x_root, int y_root) {
2515  if ( (mode == PositionCenter && !isMovable())
2516  || (mode != PositionCenter && (isShade() || !isResizable())) )
2517  return;
2518 
2519  if (!moveResizeMode) {
2520  TQPoint p(TQPoint( x, y ) - moveOffset);
2521  if (p.manhattanLength() >= 6) {
2522  if (!startMoveResize()) {
2523  buttonDown = false;
2524  setCursor( mode );
2525  return;
2526  }
2527  }
2528  else return;
2529  }
2530 
2531  // ShadeHover or ShadeActive, ShadeNormal was already avoided above
2532  if ( mode != PositionCenter && shade_mode != ShadeNone )
2533  setShade( ShadeNone );
2534 
2535  TQPoint globalPos( x_root, y_root );
2536  // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
2537  // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
2538  TQPoint topleft = globalPos - moveOffset;
2539  TQPoint bottomright = globalPos + invertedMoveOffset;
2540  TQRect previousMoveResizeGeom = moveResizeGeom;
2541 
2542  // TODO move whole group when moving its leader or when the leader is not mapped?
2543 
2544  // compute bounds
2545  // NOTE: This is duped in checkUnrestrictedMoveResize().
2546  TQRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
2547  int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2548  if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
2549  left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
2550  else // restricted move/resize - keep at least part of the titlebar always visible
2551  {
2552  // how much must remain visible when moved away in that direction
2553  left_marge = KMIN( 100 + border_right, moveResizeGeom.width());
2554  right_marge = KMIN( 100 + border_left, moveResizeGeom.width());
2555  // width/height change with opaque resizing, use the initial ones
2556  titlebar_marge = initialMoveResizeGeom.height();
2557  top_marge = border_bottom;
2558  bottom_marge = border_top;
2559  }
2560 
2561  bool update = false;
2562  if (isResize())
2563  {
2564  // first resize (without checking constraints), then snap, then check bounds, then check constraints
2565  TQRect orig = initialMoveResizeGeom;
2566  Sizemode sizemode = SizemodeAny;
2567  switch ( mode )
2568  {
2569  case PositionTopLeft:
2570  moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
2571  break;
2572  case PositionBottomRight:
2573  moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
2574  break;
2575  case PositionBottomLeft:
2576  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
2577  break;
2578  case PositionTopRight:
2579  moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2580  break;
2581  case PositionTop:
2582  moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
2583  sizemode = SizemodeFixedH; // try not to affect height
2584  break;
2585  case PositionBottom:
2586  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( orig.right(), bottomright.y() ) ) ;
2587  sizemode = SizemodeFixedH;
2588  break;
2589  case PositionLeft:
2590  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
2591  sizemode = SizemodeFixedW;
2592  break;
2593  case PositionRight:
2594  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), orig.bottom() ) ) ;
2595  sizemode = SizemodeFixedW;
2596  break;
2597  case PositionCenter:
2598  default:
2599  assert( false );
2600  break;
2601  }
2602 
2603  // adjust new size to snap to other windows/borders
2604  moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
2605 
2606  // NOTE: This is duped in checkUnrestrictedMoveResize().
2607  if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2608  moveResizeGeom.setBottom( desktopArea.top() + top_marge );
2609  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2610  moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
2611  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2612  moveResizeGeom.setRight( desktopArea.left() + left_marge );
2613  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2614  moveResizeGeom.setLeft(desktopArea.right() - right_marge );
2615  if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2616  moveResizeGeom.setTop( desktopArea.top());
2617 
2618  TQSize size = adjustedSize( moveResizeGeom.size(), sizemode );
2619  // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
2620  topleft = TQPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
2621  bottomright = TQPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
2622  orig = moveResizeGeom;
2623  switch ( mode )
2624  { // these 4 corners ones are copied from above
2625  case PositionTopLeft:
2626  moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ;
2627  break;
2628  case PositionBottomRight:
2629  moveResizeGeom = TQRect( orig.topLeft(), bottomright ) ;
2630  break;
2631  case PositionBottomLeft:
2632  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.y() ), TQPoint( orig.right(), bottomright.y()) ) ;
2633  break;
2634  case PositionTopRight:
2635  moveResizeGeom = TQRect( TQPoint( orig.x(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2636  break;
2637  // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
2638  // Therefore grow to the right/bottom if needed.
2639  // TODO it should probably obey gravity rather than always using right/bottom ?
2640  case PositionTop:
2641  moveResizeGeom = TQRect( TQPoint( orig.left(), topleft.y() ), TQPoint( bottomright.x(), orig.bottom()) ) ;
2642  break;
2643  case PositionBottom:
2644  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
2645  break;
2646  case PositionLeft:
2647  moveResizeGeom = TQRect( TQPoint( topleft.x(), orig.top() ), TQPoint( orig.right(), bottomright.y()));
2648  break;
2649  case PositionRight:
2650  moveResizeGeom = TQRect( orig.topLeft(), TQPoint( bottomright.x(), bottomright.y() ) ) ;
2651  break;
2652  case PositionCenter:
2653  default:
2654  assert( false );
2655  break;
2656  }
2657  if (moveResizeGeom.size() != previousMoveResizeGeom.size())
2658  update = true;
2659  }
2660  else if (isMove())
2661  {
2662  assert( mode == PositionCenter );
2663  // first move, then snap, then check bounds
2664  moveResizeGeom.moveTopLeft( topleft );
2665  moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
2666  // NOTE: This is duped in checkUnrestrictedMoveResize().
2667  if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2668  moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
2669  // no need to check top_marge, titlebar_marge already handles it
2670  if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2671  moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
2672  if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2673  moveResizeGeom.moveRight( desktopArea.left() + left_marge );
2674  if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2675  moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
2676  if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
2677  update = true;
2678  }
2679  else
2680  assert(false);
2681 
2682  if (update)
2683  {
2684  bool active = isActiveBorderMaximizing();
2685  auto mode = active ? options->tilingMode
2686  : isResize() ? options->resizeMode : options->moveMode;
2687 
2688  if (rules()->checkMoveResizeMode(mode) == Options::Opaque)
2689  {
2690  setGeometry(active ? activeBorderMaximizeGeometry() : moveResizeGeom);
2691  positionGeometryTip();
2692  }
2693  else if (rules()->checkMoveResizeMode(mode) == Options::Transparent)
2694  {
2695  /* It's necessary to move the geometry tip when there's no outline
2696  * shown, otherwise it would cause repaint problems in case
2697  * they overlap; the paint event will come after this,
2698  * so the geometry tip will be painted above the outline
2699  */
2700  clearbound();
2701  positionGeometryTip();
2702  drawbound(active ? activeBorderMaximizeGeometry() : moveResizeGeom);
2703  }
2704  }
2705  if (isMove()) {
2706  workspace()->checkActiveBorder(globalPos, get_tqt_x_time());
2707  }
2708 }
2709 
2710 void Client::setActiveBorderMode( ActiveMaximizingMode mode )
2711 {
2712  activeMode = mode;
2713 }
2714 
2715 ActiveMaximizingMode Client::activeBorderMode() const
2716 {
2717  return activeMode;
2718 }
2719 
2720 void Client::setActiveBorderPos( TQPoint pos )
2721 {
2722  activePos = pos;
2723 }
2724 
2725 TQPoint Client::activeBorderPos() const
2726 {
2727  return activePos;
2728 }
2729 
2730 void Client::setActiveBorder(ActiveBorder border) {
2731  currentActiveBorder = border;
2732 }
2733 
2734 ActiveBorder Client::activeBorder() const {
2735  return currentActiveBorder;
2736 }
2737 
2738 bool Client::isActiveBorderMaximizing() const
2739 {
2740  return activeMaximizing;
2741 }
2742 
2743 void Client::setActiveBorderMaximizing( bool maximizing )
2744 {
2745  activeMaximizing = maximizing;
2746  bool opaque = rules()->checkMoveResizeMode(options->tilingMode) == Options::Opaque;
2747 
2748  if (maximizing || opaque) {
2749  clearbound();
2750  }
2751 
2752  if (maximizing && !opaque) {
2753  drawbound(activeBorderMaximizeGeometry());
2754  }
2755 }
2756 
2757 void Client::cancelActiveBorderMaximizing() {
2758  if (!activeMaximizing) return;
2759  activeMaximizing = false;
2760 
2761  // If we are in transparent mode, we need to clear out the bound we had drawn
2762  if (rules()->checkMoveResizeMode(options->tilingMode) == Options::Transparent) {
2763  clearbound();
2764  }
2765 }
2766 
2767 TQRect Client::activeBorderMaximizeGeometry()
2768 {
2769  TQRect ret;
2770  TQRect max = workspace()->clientArea(MaximizeArea, activePos, workspace()->currentDesktop());
2771  switch (activeBorderMode())
2772  {
2773  case ActiveMaximizeMode:
2774  {
2775  if (maximizeMode() == MaximizeFull)
2776  ret = geometryRestore();
2777  else
2778  ret = max;
2779  break;
2780  }
2781 
2782  case ActiveTilingMode:
2783  {
2784  switch (activeBorder())
2785  {
2786  case ActiveLeft:
2787  {
2788  ret = TQRect( max.x(), max.y(), max.width()/2, max.height() );
2789  break;
2790  }
2791  case ActiveRight:
2792  {
2793  ret = TQRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height() );
2794  break;
2795  }
2796  case ActiveTop:
2797  {
2798  ret = TQRect( max.x(), max.y(), max.width(), max.height()/2 );
2799  break;
2800  }
2801  case ActiveBottom:
2802  {
2803  ret = TQRect( max.x(), max.y() + max.height()/2, max.width(), max.height()/2 );
2804  break;
2805  }
2806  case ActiveTopLeft:
2807  {
2808  ret = TQRect( max.x(), max.y(), max.width()/2, max.height()/2 );
2809  break;
2810  }
2811  case ActiveTopRight:
2812  {
2813  ret = TQRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height()/2 );
2814  break;
2815  }
2816  case ActiveBottomLeft:
2817  {
2818  ret = TQRect( max.x(), max.y() + max.height()/2, max.width()/2, max.height()/2 );
2819  break;
2820  }
2821  case ActiveBottomRight:
2822  {
2823  ret = TQRect( max.x() + max.width()/2, max.y() + max.height()/2, max.width()/2, max.height()/2);
2824  break;
2825  }
2826  }
2827  }
2828  }
2829  return ret;
2830 }
2831 
2832 void Client::tileToBorder(ActiveBorder border) {
2833  if (!isResizable()) return;
2834  activeTiled = true;
2835  if (maximizeMode() == MaximizeRestore)
2836  geom_restore = geometry();
2837  setActiveBorderMode(ActiveTilingMode);
2838  setActiveBorderPos(TQCursor().pos());
2839  setActiveBorder(border);
2840  TQRect geo = activeBorderMaximizeGeometry();
2841  if (geo.isValid() && !geo.isEmpty()) {
2842  setGeometry(geo);
2843  }
2844  workspace()->raiseClient(this);
2845 }
2846 
2847 } // namespace
KWinInternal::Client::desktop
int desktop() const
Definition: client.h:764
KWinInternal::Client::adjustedClientArea
TQRect adjustedClientArea(const TQRect &desktop, const TQRect &area) const
Definition: geometry.cpp:741
KWinInternal::Client::setGeometry
void setGeometry(int x, int y, int w, int h, ForceGeometry_t force=NormalGeometrySet)
Definition: geometry.cpp:1713
KWinInternal::Client::isMovable
bool isMovable() const
Definition: geometry.cpp:1648
KWinInternal::Client::isResizable
bool isResizable() const
Definition: geometry.cpp:1664
KWinInternal::Client::adjustedSize
TQSize adjustedSize(const TQSize &, Sizemode mode=SizemodeAny) const
Definition: geometry.cpp:1071
KWinInternal::Client::setMaximize
void setMaximize(bool vertically, bool horizontally)
Definition: geometry.cpp:1881
KWinInternal::Client::move
void move(int x, int y, ForceGeometry_t force=NormalGeometrySet)
Definition: geometry.cpp:1830

twin

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

twin

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