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

twin

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

twin

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