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

twin

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

twin

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