1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> |
6 | Copyright (C) 1997 to 2002 Cristian Tibirna <tibirna@kde.org> |
7 | Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> |
8 | |
9 | This program is free software; you can redistribute it and/or modify |
10 | it under the terms of the GNU General Public License as published by |
11 | the Free Software Foundation; either version 2 of the License, or |
12 | (at your option) any later version. |
13 | |
14 | This program is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along 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 | |
39 | namespace KWin |
40 | { |
41 | |
42 | #ifndef KCMRULES |
43 | |
44 | KWIN_SINGLETON_FACTORY(Placement) |
45 | |
46 | Placement::Placement(QObject*) |
47 | { |
48 | reinitCascading(0); |
49 | } |
50 | |
51 | Placement::~Placement() |
52 | { |
53 | s_self = NULL; |
54 | } |
55 | |
56 | /*! |
57 | Places the client \a c according to the workspace's layout policy |
58 | */ |
59 | void 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 | |
77 | void 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 | */ |
130 | void 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 ... |
169 | static 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 | */ |
191 | void 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 | |
344 | void 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 | |
362 | QPoint 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 | */ |
371 | void 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 | */ |
452 | void 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 | */ |
468 | void 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 | |
474 | void 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 | |
485 | void Placement::placeDialog(Client* c, QRect& area, Policy nextPlacement) |
486 | { |
487 | placeOnMainWindow(c, area, nextPlacement); |
488 | } |
489 | |
490 | void 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 | |
499 | void 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 | |
552 | void 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 | |
569 | void 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 | |
589 | void 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 | |
603 | QRect 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 | |
613 | Placement::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 | |
637 | const 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 | |
654 | void 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 | */ |
668 | void 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 | |
675 | void 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 | |
682 | void 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 | |
689 | void 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 | |
696 | void Workspace::slotWindowGrowHorizontal() |
697 | { |
698 | if (active_client) |
699 | active_client->growHorizontal(); |
700 | } |
701 | |
702 | void 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 | |
721 | void Workspace::slotWindowShrinkHorizontal() |
722 | { |
723 | if (active_client) |
724 | active_client->shrinkHorizontal(); |
725 | } |
726 | |
727 | void 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 | |
740 | void Workspace::slotWindowGrowVertical() |
741 | { |
742 | if (active_client) |
743 | active_client->growVertical(); |
744 | } |
745 | |
746 | void 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 | |
765 | void Workspace::slotWindowShrinkVertical() |
766 | { |
767 | if (active_client) |
768 | active_client->shrinkVertical(); |
769 | } |
770 | |
771 | void 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 | |
785 | void Workspace::slotWindowQuickTileLeft() |
786 | { |
787 | if (!active_client) |
788 | return; |
789 | |
790 | active_client->setQuickTileMode(QuickTileLeft, true); |
791 | } |
792 | |
793 | void Workspace::slotWindowQuickTileRight() |
794 | { |
795 | if (!active_client) |
796 | return; |
797 | |
798 | active_client->setQuickTileMode(QuickTileRight, true); |
799 | } |
800 | |
801 | void Workspace::slotWindowQuickTileTopLeft() |
802 | { |
803 | if (!active_client) { |
804 | return; |
805 | } |
806 | active_client->setQuickTileMode(QuickTileTop|QuickTileLeft, true); |
807 | } |
808 | |
809 | void Workspace::slotWindowQuickTileTopRight() |
810 | { |
811 | if (!active_client) { |
812 | return; |
813 | } |
814 | active_client->setQuickTileMode(QuickTileTop|QuickTileRight, true); |
815 | } |
816 | |
817 | void Workspace::slotWindowQuickTileBottomLeft() |
818 | { |
819 | if (!active_client) { |
820 | return; |
821 | } |
822 | active_client->setQuickTileMode(QuickTileBottom|QuickTileLeft, true); |
823 | } |
824 | |
825 | void Workspace::slotWindowQuickTileBottomRight() |
826 | { |
827 | if (!active_client) { |
828 | return; |
829 | } |
830 | active_client->setQuickTileMode(QuickTileBottom|QuickTileRight, true); |
831 | } |
832 | |
833 | int 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 | |
860 | int 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 | |
887 | int 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 | |
914 | int 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 | |