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

twin

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

twin

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