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) 1997 to 2002 Cristian Tibirna <tibirna@kde.org>
7Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2 of the License, or
12(at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>.
21*********************************************************************/
22
23#include "placement.h"
24
25#include <QRect>
26#include <assert.h>
27
28#include <QTextStream>
29
30#ifndef KCMRULES
31#include <kdecoration.h>
32#include "workspace.h"
33#include "client.h"
34#include "options.h"
35#include "rules.h"
36#include "screens.h"
37#endif
38
39namespace KWin
40{
41
42#ifndef KCMRULES
43
44KWIN_SINGLETON_FACTORY(Placement)
45
46Placement::Placement(QObject*)
47{
48 reinitCascading(0);
49}
50
51Placement::~Placement()
52{
53 s_self = NULL;
54}
55
56/*!
57 Places the client \a c according to the workspace's layout policy
58 */
59void Placement::place(Client* c, QRect& area)
60{
61 Policy policy = c->rules()->checkPlacement(Default);
62 if (policy != Default) {
63 place(c, area, policy);
64 return;
65 }
66
67 if (c->isUtility())
68 placeUtility(c, area, options->placement());
69 else if (c->isDialog())
70 placeDialog(c, area, options->placement());
71 else if (c->isSplash())
72 placeOnMainWindow(c, area); // on mainwindow, if any, otherwise centered
73 else
74 place(c, area, options->placement());
75}
76
77void Placement::place(Client* c, QRect& area, Policy policy, Policy nextPlacement)
78{
79 if (policy == Unknown)
80 policy = Default;
81 if (policy == Default)
82 policy = options->placement();
83 if (policy == NoPlacement)
84 return;
85 else if (policy == Random)
86 placeAtRandom(c, area, nextPlacement);
87 else if (policy == Cascade)
88 placeCascaded(c, area, nextPlacement);
89 else if (policy == Centered)
90 placeCentered(c, area, nextPlacement);
91 else if (policy == ZeroCornered)
92 placeZeroCornered(c, area, nextPlacement);
93 else if (policy == UnderMouse)
94 placeUnderMouse(c, area, nextPlacement);
95 else if (policy == OnMainWindow)
96 placeOnMainWindow(c, area, nextPlacement);
97 else if (policy == Maximizing)
98 placeMaximizing(c, area, nextPlacement);
99 else
100 placeSmart(c, area, nextPlacement);
101
102 if (options->borderSnapZone()) {
103 // snap to titlebar / snap to window borders on inner screen edges
104 const QRect geo(c->geometry());
105 QPoint corner = geo.topLeft();
106 const QPoint cp = c->clientPos();
107 const QSize cs = geo.size() - c->clientSize();
108 KDecorationDefines::Position titlePos = c->titlebarPosition();
109
110 const QRect fullRect = workspace()->clientArea(FullArea, c);
111 if (!(c->maximizeMode() & KDecorationDefines::MaximizeHorizontal)) {
112 if (titlePos != KDecorationDefines::PositionRight && geo.right() == fullRect.right())
113 corner.rx() += cs.width() - cp.x();
114 if (titlePos != KDecorationDefines::PositionLeft && geo.x() == fullRect.x())
115 corner.rx() -= cp.x();
116 }
117 if (!(c->maximizeMode() & KDecorationDefines::MaximizeVertical)) {
118 if (titlePos != KDecorationDefines::PositionBottom && geo.bottom() == fullRect.bottom())
119 corner.ry() += cs.height() - cp.y();
120 if (titlePos != KDecorationDefines::PositionTop && geo.y() == fullRect.y())
121 corner.ry() -= cp.y();
122 }
123 c->move(corner);
124 }
125}
126
127/*!
128 Place the client \a c according to a simply "random" placement algorithm.
129 */
130void Placement::placeAtRandom(Client* c, const QRect& area, Policy /*next*/)
131{
132 const int step = 24;
133 static int px = step;
134 static int py = 2 * step;
135 int tx, ty;
136
137 const QRect maxRect = checkArea(c, area);
138
139 if (px < maxRect.x())
140 px = maxRect.x();
141 if (py < maxRect.y())
142 py = maxRect.y();
143
144 px += step;
145 py += 2 * step;
146
147 if (px > maxRect.width() / 2)
148 px = maxRect.x() + step;
149 if (py > maxRect.height() / 2)
150 py = maxRect.y() + step;
151 tx = px;
152 ty = py;
153 if (tx + c->width() > maxRect.right()) {
154 tx = maxRect.right() - c->width();
155 if (tx < 0)
156 tx = 0;
157 px = maxRect.x();
158 }
159 if (ty + c->height() > maxRect.bottom()) {
160 ty = maxRect.bottom() - c->height();
161 if (ty < 0)
162 ty = 0;
163 py = maxRect.y();
164 }
165 c->move(tx, ty);
166}
167
168// TODO: one day, there'll be C++11 ...
169static inline bool isIrrelevant(const Client *client, const Client *regarding, int desktop)
170{
171 if (!client)
172 return true;
173 if (client == regarding)
174 return true;
175 if (!client->isCurrentTab())
176 return true;
177 if (!client->isShown(false))
178 return true;
179 if (!client->isOnDesktop(desktop))
180 return true;
181 if (!client->isOnCurrentActivity())
182 return true;
183 if (client->isDesktop())
184 return true;
185 return false;
186}
187
188/*!
189 Place the client \a c according to a really smart placement algorithm :-)
190*/
191void Placement::placeSmart(Client* c, const QRect& area, Policy /*next*/)
192{
193 /*
194 * SmartPlacement by Cristian Tibirna (tibirna@kde.org)
195 * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
196 * permission) ideas from fvwm, authored by
197 * Anthony Martin (amartin@engr.csulb.edu).
198 * Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
199 * with ideas from xfce.
200 */
201
202 const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types
203 long int overlap, min_overlap = 0;
204 int x_optimal, y_optimal;
205 int possible;
206 int desktop = c->desktop() == 0 || c->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : c->desktop();
207
208 int cxl, cxr, cyt, cyb; //temp coords
209 int xl, xr, yt, yb; //temp coords
210 int basket; //temp holder
211
212 // get the maximum allowed windows space
213 const QRect maxRect = checkArea(c, area);
214 int x = maxRect.left(), y = maxRect.top();
215 x_optimal = x; y_optimal = y;
216
217 //client gabarit
218 int ch = c->height() - 1;
219 int cw = c->width() - 1;
220
221 bool first_pass = true; //CT lame flag. Don't like it. What else would do?
222
223 //loop over possible positions
224 do {
225 //test if enough room in x and y directions
226 if (y + ch > maxRect.bottom() && ch < maxRect.height())
227 overlap = h_wrong; // this throws the algorithm to an exit
228 else if (x + cw > maxRect.right())
229 overlap = w_wrong;
230 else {
231 overlap = none; //initialize
232
233 cxl = x; cxr = x + cw;
234 cyt = y; cyb = y + ch;
235 ToplevelList::ConstIterator l;
236 for (l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
237 Client *client = qobject_cast<Client*>(*l);
238 if (isIrrelevant(client, c, desktop)) {
239 continue;
240 }
241 xl = client->x(); yt = client->y();
242 xr = xl + client->width(); yb = yt + client->height();
243
244 //if windows overlap, calc the overall overlapping
245 if ((cxl < xr) && (cxr > xl) &&
246 (cyt < yb) && (cyb > yt)) {
247 xl = qMax(cxl, xl); xr = qMin(cxr, xr);
248 yt = qMax(cyt, yt); yb = qMin(cyb, yb);
249 if (client->keepAbove())
250 overlap += 16 * (xr - xl) * (yb - yt);
251 else if (client->keepBelow() && !client->isDock()) // ignore KeepBelow windows
252 overlap += 0; // for placement (see Client::belongsToLayer() for Dock)
253 else
254 overlap += (xr - xl) * (yb - yt);
255 }
256 }
257 }
258
259 //CT first time we get no overlap we stop.
260 if (overlap == none) {
261 x_optimal = x;
262 y_optimal = y;
263 break;
264 }
265
266 if (first_pass) {
267 first_pass = false;
268 min_overlap = overlap;
269 }
270 //CT save the best position and the minimum overlap up to now
271 else if (overlap >= none && overlap < min_overlap) {
272 min_overlap = overlap;
273 x_optimal = x;
274 y_optimal = y;
275 }
276
277 // really need to loop? test if there's any overlap
278 if (overlap > none) {
279
280 possible = maxRect.right();
281 if (possible - cw > x) possible -= cw;
282
283 // compare to the position of each client on the same desk
284 ToplevelList::ConstIterator l;
285 for (l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
286 Client *client = qobject_cast<Client*>(*l);
287 if (isIrrelevant(client, c, desktop)) {
288 continue;
289 }
290
291 xl = client->x(); yt = client->y();
292 xr = xl + client->width(); yb = yt + client->height();
293
294 // if not enough room above or under the current tested client
295 // determine the first non-overlapped x position
296 if ((y < yb) && (yt < ch + y)) {
297
298 if ((xr > x) && (possible > xr)) possible = xr;
299
300 basket = xl - cw;
301 if ((basket > x) && (possible > basket)) possible = basket;
302 }
303 }
304 x = possible;
305 }
306
307 // ... else ==> not enough x dimension (overlap was wrong on horizontal)
308 else if (overlap == w_wrong) {
309 x = maxRect.left();
310 possible = maxRect.bottom();
311
312 if (possible - ch > y) possible -= ch;
313
314 //test the position of each window on the desk
315 ToplevelList::ConstIterator l;
316 for (l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd() ; ++l) {
317 Client *client = qobject_cast<Client*>(*l);
318 if (isIrrelevant(client, c, desktop)) {
319 continue;
320 }
321
322 xl = client->x(); yt = client->y();
323 xr = xl + client->width(); yb = yt + client->height();
324
325 // if not enough room to the left or right of the current tested client
326 // determine the first non-overlapped y position
327 if ((yb > y) && (possible > yb)) possible = yb;
328
329 basket = yt - ch;
330 if ((basket > y) && (possible > basket)) possible = basket;
331 }
332 y = possible;
333 }
334 } while ((overlap != none) && (overlap != h_wrong) && (y < maxRect.bottom()));
335
336 if (ch >= maxRect.height())
337 y_optimal = maxRect.top();
338
339 // place the window
340 c->move(x_optimal, y_optimal);
341
342}
343
344void Placement::reinitCascading(int desktop)
345{
346 // desktop == 0 - reinit all
347 if (desktop == 0) {
348 cci.clear();
349 for (uint i = 0; i < VirtualDesktopManager::self()->count(); ++i) {
350 DesktopCascadingInfo inf;
351 inf.pos = QPoint(-1, -1);
352 inf.col = 0;
353 inf.row = 0;
354 cci.append(inf);
355 }
356 } else {
357 cci[desktop - 1].pos = QPoint(-1, -1);
358 cci[desktop - 1].col = cci[desktop - 1].row = 0;
359 }
360}
361
362QPoint Workspace::cascadeOffset(const Client *c) const
363{
364 QRect area = clientArea(PlacementArea, c->geometry().center(), c->desktop());
365 return QPoint(area.width()/48, area.height()/48);
366}
367
368/*!
369 Place windows in a cascading order, remembering positions for each desktop
370*/
371void Placement::placeCascaded(Client* c, QRect& area, Policy nextPlacement)
372{
373 /* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98)
374 */
375 // work coords
376 int xp, yp;
377
378 //CT how do I get from the 'Client' class the size that NW squarish "handle"
379 const QPoint delta = workspace()->cascadeOffset(c);
380
381 const int dn = c->desktop() == 0 || c->isOnAllDesktops() ? (VirtualDesktopManager::self()->current() - 1) : (c->desktop() - 1);
382
383 // get the maximum allowed windows space and desk's origin
384 QRect maxRect = checkArea(c, area);
385
386 // initialize often used vars: width and height of c; we gain speed
387 const int ch = c->height();
388 const int cw = c->width();
389 const int X = maxRect.left();
390 const int Y = maxRect.top();
391 const int H = maxRect.height();
392 const int W = maxRect.width();
393
394 if (nextPlacement == Unknown)
395 nextPlacement = Smart;
396
397 //initialize if needed
398 if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < X || cci[dn].pos.y() < Y) {
399 cci[dn].pos = QPoint(X, Y);
400 cci[dn].col = cci[dn].row = 0;
401 }
402
403
404 xp = cci[dn].pos.x();
405 yp = cci[dn].pos.y();
406
407 //here to touch in case people vote for resize on placement
408 if ((yp + ch) > H) yp = Y;
409
410 if ((xp + cw) > W) {
411 if (!yp) {
412 place(c, area, nextPlacement);
413 return;
414 } else xp = X;
415 }
416
417 //if this isn't the first window
418 if (cci[dn].pos.x() != X && cci[dn].pos.y() != Y) {
419 /* The following statements cause an internal compiler error with
420 * egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine.
421 * 22-Dec-1999 CS
422 *
423 * if (xp != X && yp == Y) xp = delta.x() * (++(cci[dn].col));
424 * if (yp != Y && xp == X) yp = delta.y() * (++(cci[dn].row));
425 */
426 if (xp != X && yp == Y) {
427 ++(cci[dn].col);
428 xp = delta.x() * cci[dn].col;
429 }
430 if (yp != Y && xp == X) {
431 ++(cci[dn].row);
432 yp = delta.y() * cci[dn].row;
433 }
434
435 // last resort: if still doesn't fit, smart place it
436 if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) {
437 place(c, area, nextPlacement);
438 return;
439 }
440 }
441
442 // place the window
443 c->move(QPoint(xp, yp));
444
445 // new position
446 cci[dn].pos = QPoint(xp + delta.x(), yp + delta.y());
447}
448
449/*!
450 Place windows centered, on top of all others
451*/
452void Placement::placeCentered(Client* c, const QRect& area, Policy /*next*/)
453{
454
455 // get the maximum allowed windows space and desk's origin
456 const QRect maxRect = checkArea(c, area);
457
458 const int xp = maxRect.left() + (maxRect.width() - c->width()) / 2;
459 const int yp = maxRect.top() + (maxRect.height() - c->height()) / 2;
460
461 // place the window
462 c->move(QPoint(xp, yp));
463}
464
465/*!
466 Place windows in the (0,0) corner, on top of all others
467*/
468void Placement::placeZeroCornered(Client* c, const QRect& area, Policy /*next*/)
469{
470 // get the maximum allowed windows space and desk's origin
471 c->move(checkArea(c, area).topLeft());
472}
473
474void Placement::placeUtility(Client* c, QRect& area, Policy /*next*/)
475{
476// TODO kwin should try to place utility windows next to their mainwindow,
477// preferably at the right edge, and going down if there are more of them
478// if there's not enough place outside the mainwindow, it should prefer
479// top-right corner
480 // use the default placement for now
481 place(c, area, Default);
482}
483
484
485void Placement::placeDialog(Client* c, QRect& area, Policy nextPlacement)
486{
487 placeOnMainWindow(c, area, nextPlacement);
488}
489
490void Placement::placeUnderMouse(Client* c, QRect& area, Policy /*next*/)
491{
492 area = checkArea(c, area);
493 QRect geom = c->geometry();
494 geom.moveCenter(cursorPos());
495 c->move(geom.topLeft());
496 c->keepInArea(area); // make sure it's kept inside workarea
497}
498
499void Placement::placeOnMainWindow(Client* c, QRect& area, Policy nextPlacement)
500{
501 if (nextPlacement == Unknown)
502 nextPlacement = Centered;
503 if (nextPlacement == Maximizing) // maximize if needed
504 placeMaximizing(c, area, NoPlacement);
505 area = checkArea(c, area);
506 ClientList mainwindows = c->mainClients();
507 Client* place_on = NULL;
508 Client* place_on2 = NULL;
509 int mains_count = 0;
510 for (ClientList::ConstIterator it = mainwindows.constBegin();
511 it != mainwindows.constEnd();
512 ++it) {
513 if (mainwindows.count() > 1 && (*it)->isSpecialWindow())
514 continue; // don't consider toolbars etc when placing
515 ++mains_count;
516 place_on2 = *it;
517 if ((*it)->isOnCurrentDesktop()) {
518 if (place_on == NULL)
519 place_on = *it;
520 else {
521 // two or more on current desktop -> center
522 // That's the default at least. However, with maximizing placement
523 // policy as the default, the dialog should be either maximized or
524 // made as large as its maximum size and then placed centered.
525 // So the nextPlacement argument allows chaining. In this case, nextPlacement
526 // is Maximizing and it will call placeCentered().
527 place(c, area, Centered);
528 return;
529 }
530 }
531 }
532 if (place_on == NULL) {
533 // 'mains_count' is used because it doesn't include ignored mainwindows
534 if (mains_count != 1) {
535 place(c, area, Centered);
536 return;
537 }
538 place_on = place_on2; // use the only window filtered together with 'mains_count'
539 }
540 if (place_on->isDesktop()) {
541 place(c, area, Centered);
542 return;
543 }
544 QRect geom = c->geometry();
545 geom.moveCenter(place_on->geometry().center());
546 c->move(geom.topLeft());
547 // get area again, because the mainwindow may be on different xinerama screen
548 area = checkArea(c, QRect());
549 c->keepInArea(area); // make sure it's kept inside workarea
550}
551
552void Placement::placeMaximizing(Client* c, QRect& area, Policy nextPlacement)
553{
554 if (nextPlacement == Unknown)
555 nextPlacement = Smart;
556 if (c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) {
557 if (workspace()->clientArea(MaximizeArea, c) == area)
558 c->maximize(Client::MaximizeFull);
559 else { // if the geometry doesn't match default maximize area (xinerama case?),
560 // it's probably better to use the given area
561 c->setGeometry(area);
562 }
563 } else {
564 c->resizeWithChecks(c->maxSize().boundedTo(area.size()));
565 place(c, area, nextPlacement);
566 }
567}
568
569void Placement::cascadeDesktop()
570{
571// TODO XINERAMA this probably is not right for xinerama
572 Workspace *ws = Workspace::self();
573 const int desktop = VirtualDesktopManager::self()->current();
574 reinitCascading(desktop);
575 // TODO: make area const once placeFoo methods are fixed to take a const QRect&
576 QRect area = ws->clientArea(PlacementArea, QPoint(0, 0), desktop);
577 foreach (Toplevel *toplevel, ws->stackingOrder()) {
578 Client *client = qobject_cast<Client*>(toplevel);
579 if (!client ||
580 (!client->isOnCurrentDesktop()) ||
581 (client->isMinimized()) ||
582 (client->isOnAllDesktops()) ||
583 (!client->isMovable()))
584 continue;
585 placeCascaded(client, area);
586 }
587}
588
589void Placement::unclutterDesktop()
590{
591 const ClientList &clients = Workspace::self()->clientList();
592 for (int i = clients.size() - 1; i >= 0; i--) {
593 Client *client = clients.at(i);
594 if ((!client->isOnCurrentDesktop()) ||
595 (client->isMinimized()) ||
596 (client->isOnAllDesktops()) ||
597 (!client->isMovable()))
598 continue;
599 placeSmart(client, QRect());
600 }
601}
602
603QRect Placement::checkArea(const Client* c, const QRect& area)
604{
605 if (area.isNull())
606 return workspace()->clientArea(PlacementArea, c->geometry().center(), c->desktop());
607 return area;
608}
609
610#endif
611
612
613Placement::Policy Placement::policyFromString(const QString& policy, bool no_special)
614{
615 if (policy == "NoPlacement")
616 return NoPlacement;
617 else if (policy == "Default" && !no_special)
618 return Default;
619 else if (policy == "Random")
620 return Random;
621 else if (policy == "Cascade")
622 return Cascade;
623 else if (policy == "Centered")
624 return Centered;
625 else if (policy == "ZeroCornered")
626 return ZeroCornered;
627 else if (policy == "UnderMouse")
628 return UnderMouse;
629 else if (policy == "OnMainWindow" && !no_special)
630 return OnMainWindow;
631 else if (policy == "Maximizing")
632 return Maximizing;
633 else
634 return Smart;
635}
636
637const char* Placement::policyToString(Policy policy)
638{
639 const char* const policies[] = {
640 "NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Cascade", "Centered",
641 "ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing"
642 };
643 assert(policy < int(sizeof(policies) / sizeof(policies[ 0 ])));
644 return policies[ policy ];
645}
646
647
648#ifndef KCMRULES
649
650// ********************
651// Workspace
652// ********************
653
654void Client::packTo(int left, int top)
655{
656 const int oldScreen = screen();
657 move(left, top);
658 if (screen() != oldScreen) {
659 workspace()->sendClientToScreen(this, screen()); // checks rule validity
660 if (maximizeMode() != MaximizeRestore)
661 checkWorkspacePosition();
662 }
663}
664
665/*!
666 Moves active window left until in bumps into another window or workarea edge.
667 */
668void Workspace::slotWindowPackLeft()
669{
670 if (active_client && active_client->isMovable())
671 active_client->packTo(packPositionLeft(active_client, active_client->geometry().left(), true),
672 active_client->y());
673}
674
675void Workspace::slotWindowPackRight()
676{
677 if (active_client && active_client->isMovable())
678 active_client->packTo(packPositionRight(active_client, active_client->geometry().right(), true)
679 - active_client->width() + 1, active_client->y());
680}
681
682void Workspace::slotWindowPackUp()
683{
684 if (active_client && active_client->isMovable())
685 active_client->packTo(active_client->x(),
686 packPositionUp(active_client, active_client->geometry().top(), true));
687}
688
689void Workspace::slotWindowPackDown()
690{
691 if (active_client && active_client->isMovable())
692 active_client->packTo(active_client->x(),
693 packPositionDown(active_client, active_client->geometry().bottom(), true) - active_client->height() + 1);
694}
695
696void Workspace::slotWindowGrowHorizontal()
697{
698 if (active_client)
699 active_client->growHorizontal();
700}
701
702void Client::growHorizontal()
703{
704 if (!isResizable() || isShade())
705 return;
706 QRect geom = geometry();
707 geom.setRight(workspace()->packPositionRight(this, geom.right(), true));
708 QSize adjsize = adjustedSize(geom.size(), SizemodeFixedW);
709 if (geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.width_inc > 1) { // take care of size increments
710 int newright = workspace()->packPositionRight(this, geom.right() + xSizeHint.width_inc - 1, true);
711 // check that it hasn't grown outside of the area, due to size increments
712 // TODO this may be wrong?
713 if (workspace()->clientArea(MovementArea,
714 QPoint((x() + newright) / 2, geometry().center().y()), desktop()).right() >= newright)
715 geom.setRight(newright);
716 }
717 geom.setSize(adjustedSize(geom.size(), SizemodeFixedW));
718 setGeometry(geom);
719}
720
721void Workspace::slotWindowShrinkHorizontal()
722{
723 if (active_client)
724 active_client->shrinkHorizontal();
725}
726
727void Client::shrinkHorizontal()
728{
729 if (!isResizable() || isShade())
730 return;
731 QRect geom = geometry();
732 geom.setRight(workspace()->packPositionLeft(this, geom.right(), false));
733 if (geom.width() <= 1)
734 return;
735 geom.setSize(adjustedSize(geom.size(), SizemodeFixedW));
736 if (geom.width() > 20)
737 setGeometry(geom);
738}
739
740void Workspace::slotWindowGrowVertical()
741{
742 if (active_client)
743 active_client->growVertical();
744}
745
746void Client::growVertical()
747{
748 if (!isResizable() || isShade())
749 return;
750 QRect geom = geometry();
751 geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true));
752 QSize adjsize = adjustedSize(geom.size(), SizemodeFixedH);
753 if (geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.height_inc > 1) { // take care of size increments
754 int newbottom = workspace()->packPositionDown(this, geom.bottom() + xSizeHint.height_inc - 1, true);
755 // check that it hasn't grown outside of the area, due to size increments
756 if (workspace()->clientArea(MovementArea,
757 QPoint(geometry().center().x(), (y() + newbottom) / 2), desktop()).bottom() >= newbottom)
758 geom.setBottom(newbottom);
759 }
760 geom.setSize(adjustedSize(geom.size(), SizemodeFixedH));
761 setGeometry(geom);
762}
763
764
765void Workspace::slotWindowShrinkVertical()
766{
767 if (active_client)
768 active_client->shrinkVertical();
769}
770
771void Client::shrinkVertical()
772{
773 if (!isResizable() || isShade())
774 return;
775 QRect geom = geometry();
776 geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false));
777 if (geom.height() <= 1)
778 return;
779 geom.setSize(adjustedSize(geom.size(), SizemodeFixedH));
780 if (geom.height() > 20)
781 setGeometry(geom);
782}
783
784
785void Workspace::slotWindowQuickTileLeft()
786{
787 if (!active_client)
788 return;
789
790 active_client->setQuickTileMode(QuickTileLeft, true);
791}
792
793void Workspace::slotWindowQuickTileRight()
794{
795 if (!active_client)
796 return;
797
798 active_client->setQuickTileMode(QuickTileRight, true);
799}
800
801void Workspace::slotWindowQuickTileTopLeft()
802{
803 if (!active_client) {
804 return;
805 }
806 active_client->setQuickTileMode(QuickTileTop|QuickTileLeft, true);
807}
808
809void Workspace::slotWindowQuickTileTopRight()
810{
811 if (!active_client) {
812 return;
813 }
814 active_client->setQuickTileMode(QuickTileTop|QuickTileRight, true);
815}
816
817void Workspace::slotWindowQuickTileBottomLeft()
818{
819 if (!active_client) {
820 return;
821 }
822 active_client->setQuickTileMode(QuickTileBottom|QuickTileLeft, true);
823}
824
825void Workspace::slotWindowQuickTileBottomRight()
826{
827 if (!active_client) {
828 return;
829 }
830 active_client->setQuickTileMode(QuickTileBottom|QuickTileRight, true);
831}
832
833int Workspace::packPositionLeft(const Client* cl, int oldx, bool left_edge) const
834{
835 int newx = clientArea(MaximizeArea, cl).left();
836 if (oldx <= newx) // try another Xinerama screen
837 newx = clientArea(MaximizeArea,
838 QPoint(cl->geometry().left() - 1, cl->geometry().center().y()), cl->desktop()).left();
839 if (cl->titlebarPosition() != KDecorationDefines::PositionLeft) {
840 QRect geo = cl->geometry();
841 int rgt = newx - cl->clientPos().x();
842 geo.moveRight(rgt);
843 if (screens()->intersecting(geo) < 2)
844 newx = rgt;
845 }
846 if (oldx <= newx)
847 return oldx;
848 for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) {
849 if (isIrrelevant(*it, cl, cl->desktop()))
850 continue;
851 int x = left_edge ? (*it)->geometry().right() + 1 : (*it)->geometry().left() - 1;
852 if (x > newx && x < oldx
853 && !(cl->geometry().top() > (*it)->geometry().bottom() // they overlap in Y direction
854 || cl->geometry().bottom() < (*it)->geometry().top()))
855 newx = x;
856 }
857 return newx;
858}
859
860int Workspace::packPositionRight(const Client* cl, int oldx, bool right_edge) const
861{
862 int newx = clientArea(MaximizeArea, cl).right();
863 if (oldx >= newx) // try another Xinerama screen
864 newx = clientArea(MaximizeArea,
865 QPoint(cl->geometry().right() + 1, cl->geometry().center().y()), cl->desktop()).right();
866 if (cl->titlebarPosition() != KDecorationDefines::PositionRight) {
867 QRect geo = cl->geometry();
868 int rgt = newx + cl->width() - (cl->clientSize().width() + cl->clientPos().x());
869 geo.moveRight(rgt);
870 if (screens()->intersecting(geo) < 2)
871 newx = rgt;
872 }
873 if (oldx >= newx)
874 return oldx;
875 for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) {
876 if (isIrrelevant(*it, cl, cl->desktop()))
877 continue;
878 int x = right_edge ? (*it)->geometry().left() - 1 : (*it)->geometry().right() + 1;
879 if (x < newx && x > oldx
880 && !(cl->geometry().top() > (*it)->geometry().bottom()
881 || cl->geometry().bottom() < (*it)->geometry().top()))
882 newx = x;
883 }
884 return newx;
885}
886
887int Workspace::packPositionUp(const Client* cl, int oldy, bool top_edge) const
888{
889 int newy = clientArea(MaximizeArea, cl).top();
890 if (oldy <= newy) // try another Xinerama screen
891 newy = clientArea(MaximizeArea,
892 QPoint(cl->geometry().center().x(), cl->geometry().top() - 1), cl->desktop()).top();
893 if (cl->titlebarPosition() != KDecorationDefines::PositionTop) {
894 QRect geo = cl->geometry();
895 int top = newy - cl->clientPos().y();
896 geo.moveTop(top);
897 if (screens()->intersecting(geo) < 2)
898 newy = top;
899 }
900 if (oldy <= newy)
901 return oldy;
902 for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) {
903 if (isIrrelevant(*it, cl, cl->desktop()))
904 continue;
905 int y = top_edge ? (*it)->geometry().bottom() + 1 : (*it)->geometry().top() - 1;
906 if (y > newy && y < oldy
907 && !(cl->geometry().left() > (*it)->geometry().right() // they overlap in X direction
908 || cl->geometry().right() < (*it)->geometry().left()))
909 newy = y;
910 }
911 return newy;
912}
913
914int Workspace::packPositionDown(const Client* cl, int oldy, bool bottom_edge) const
915{
916 int newy = clientArea(MaximizeArea, cl).bottom();
917 if (oldy >= newy) // try another Xinerama screen
918 newy = clientArea(MaximizeArea,
919 QPoint(cl->geometry().center().x(), cl->geometry().bottom() + 1), cl->desktop()).bottom();
920 if (cl->titlebarPosition() != KDecorationDefines::PositionBottom) {
921 QRect geo = cl->geometry();
922 int btm = newy + cl->height() - (cl->clientSize().height() + cl->clientPos().y());
923 geo.moveBottom(btm);
924 if (screens()->intersecting(geo) < 2)
925 newy = btm;
926 }
927 if (oldy >= newy)
928 return oldy;
929 for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) {
930 if (isIrrelevant(*it, cl, cl->desktop()))
931 continue;
932 int y = bottom_edge ? (*it)->geometry().top() - 1 : (*it)->geometry().bottom() + 1;
933 if (y < newy && y > oldy
934 && !(cl->geometry().left() > (*it)->geometry().right()
935 || cl->geometry().right() < (*it)->geometry().left()))
936 newy = y;
937 }
938 return newy;
939}
940
941#endif
942
943} // namespace
944