]> Dogcows Code - chaz/openbox/blob - src/Workspace.cc
better sticky windows.
[chaz/openbox] / src / Workspace.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Workspace.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef HAVE_CONFIG_H
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31
32 #ifdef HAVE_STDIO_H
33 # include <stdio.h>
34 #endif // HAVE_STDIO_H
35
36 #ifdef HAVE_STRING_H
37 # include <string.h>
38 #endif // HAVE_STRING_H
39 }
40
41 #include <assert.h>
42
43 #include <functional>
44 #include <string>
45
46 using std::string;
47
48 #include "i18n.hh"
49 #include "blackbox.hh"
50 #include "Clientmenu.hh"
51 #include "Font.hh"
52 #include "Netizen.hh"
53 #include "Screen.hh"
54 #include "Toolbar.hh"
55 #include "Util.hh"
56 #include "Window.hh"
57 #include "Workspace.hh"
58 #include "Windowmenu.hh"
59 #include "XAtom.hh"
60
61
62 Workspace::Workspace(BScreen *scrn, unsigned int i) {
63 screen = scrn;
64 xatom = screen->getBlackbox()->getXAtom();
65
66 cascade_x = cascade_y = 0;
67 #ifdef XINERAMA
68 cascade_region = 0;
69 #endif // XINERAMA
70
71 id = i;
72
73 clientmenu = new Clientmenu(this);
74
75 lastfocus = (BlackboxWindow *) 0;
76
77 readName();
78 }
79
80
81 void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
82 assert(w != 0);
83
84 if (place) placeWindow(w);
85
86 stackingList.push_front(w);
87
88 if (w->isNormal()) {
89 if (! sticky) {
90 w->setWorkspace(id);
91 w->setWindowNumber(windowList.size());
92 }
93
94 windowList.push_back(w);
95
96 clientmenu->insert(w->getTitle());
97 clientmenu->update();
98
99 if (! sticky)
100 screen->updateNetizenWindowAdd(w->getClientWindow(), id);
101
102 if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
103 w->getTransientFor()->isFocused())) {
104 if (id == screen->getCurrentWorkspaceID())
105 w->setInputFocus();
106 else {
107 /*
108 not on the focused workspace, so the window is not going to get focus
109 but if the user wants new windows focused, then it should get focus
110 when this workspace does become focused.
111 */
112 lastfocus = w;
113 }
114 }
115 }
116
117 if (! w->isDesktop())
118 raiseWindow(w);
119 else
120 lowerWindow(w);
121 }
122
123
124 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
125 assert(w != 0);
126
127 stackingList.remove(w);
128
129 // pass focus to the next appropriate window
130 if ((w->isFocused() || w == lastfocus) &&
131 ! screen->getBlackbox()->doShutdown()) {
132 focusFallback(w);
133 }
134
135 if (! w->isNormal()) return;
136
137 BlackboxWindowList::iterator it, end = windowList.end();
138 int i;
139 for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
140 if (*it == w)
141 break;
142 assert(it != end);
143
144 windowList.erase(it);
145 clientmenu->remove(i);
146 clientmenu->update();
147
148 if (! sticky) {
149 screen->updateNetizenWindowDel(w->getClientWindow());
150
151 BlackboxWindowList::iterator it = windowList.begin();
152 const BlackboxWindowList::iterator end = windowList.end();
153 unsigned int i = 0;
154 for (; it != end; ++it, ++i)
155 (*it)->setWindowNumber(i);
156 }
157
158 if (i == 0) {
159 cascade_x = cascade_y = 0;
160 #ifdef XINERAMA
161 cascade_region = 0;
162 #endif // XINERAMA
163 }
164 }
165
166
167 void Workspace::focusFallback(const BlackboxWindow *old_window) {
168 BlackboxWindow *newfocus = 0;
169
170 if (id == screen->getCurrentWorkspaceID()) {
171 // The window is on the visible workspace.
172
173 // if it's a transient, then try to focus its parent
174 if (old_window && old_window->isTransient()) {
175 newfocus = old_window->getTransientFor();
176
177 if (! newfocus ||
178 newfocus->isIconic() || // do not focus icons
179 newfocus->getWorkspaceNumber() != id || // or other workspaces
180 ! newfocus->setInputFocus())
181 newfocus = 0;
182 }
183
184 if (! newfocus) {
185 BlackboxWindowList::iterator it = stackingList.begin(),
186 end = stackingList.end();
187 for (; it != end; ++it) {
188 BlackboxWindow *tmp = *it;
189 if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
190 // we found our new focus target
191 newfocus = tmp;
192 break;
193 }
194 }
195 }
196
197 screen->getBlackbox()->setFocusedWindow(newfocus);
198 } else {
199 // The window is not on the visible workspace.
200
201 if (old_window && lastfocus == old_window) {
202 // The window was the last-focus target, so we need to replace it.
203 BlackboxWindow *win = (BlackboxWindow*) 0;
204 if (! stackingList.empty())
205 win = stackingList.front();
206 setLastFocusedWindow(win);
207 }
208 }
209 }
210
211
212 void Workspace::setFocused(const BlackboxWindow *w, bool focused) {
213 BlackboxWindowList::iterator it, end = windowList.end();
214 int i;
215 for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
216 if (*it == w)
217 break;
218 // if its == end, then a window thats not in the windowList
219 // got focused, such as a !isNormal() window.
220 if (it != end)
221 clientmenu->setItemSelected(i, focused);
222 }
223
224
225 void Workspace::showAll(void) {
226 BlackboxWindowList::iterator it = stackingList.begin();
227 const BlackboxWindowList::iterator end = stackingList.end();
228 for (; it != end; ++it) {
229 BlackboxWindow *bw = *it;
230 bw->show();
231 }
232 }
233
234
235 void Workspace::hideAll(void) {
236 // withdraw in reverse order to minimize the number of Expose events
237 BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
238 const BlackboxWindowList::reverse_iterator end = stackingList.rend();
239 while (it != end) {
240 BlackboxWindow *bw = *it;
241 ++it; // withdraw removes the current item from the list so we need the next
242 // iterator before that happens
243 bw->withdraw();
244 }
245 }
246
247
248 void Workspace::removeAll(void) {
249 while (! windowList.empty())
250 windowList.front()->iconify();
251 }
252
253
254 /*
255 * returns the number of transients for win, plus the number of transients
256 * associated with each transient of win
257 */
258 static int countTransients(const BlackboxWindow * const win) {
259 int ret = win->getTransients().size();
260 if (ret > 0) {
261 BlackboxWindowList::const_iterator it, end = win->getTransients().end();
262 for (it = win->getTransients().begin(); it != end; ++it) {
263 ret += countTransients(*it);
264 }
265 }
266 return ret;
267 }
268
269
270 /*
271 * puts the transients of win into the stack. windows are stacked above
272 * the window before it in the stackvector being iterated, meaning
273 * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
274 * stack[1], etc...
275 */
276 void Workspace::raiseTransients(const BlackboxWindow * const win,
277 StackVector::iterator &stack) {
278 if (win->getTransients().size() == 0) return; // nothing to do
279
280 // put win's transients in the stack
281 BlackboxWindowList::const_iterator it, end = win->getTransients().end();
282 for (it = win->getTransients().begin(); it != end; ++it) {
283 *stack++ = (*it)->getFrameWindow();
284 screen->updateNetizenWindowRaise((*it)->getClientWindow());
285
286 if (! (*it)->isIconic()) {
287 Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
288 wkspc->stackingList.remove((*it));
289 wkspc->stackingList.push_front((*it));
290 }
291 }
292
293 // put transients of win's transients in the stack
294 for (it = win->getTransients().begin(); it != end; ++it) {
295 raiseTransients(*it, stack);
296 }
297 }
298
299
300 void Workspace::lowerTransients(const BlackboxWindow * const win,
301 StackVector::iterator &stack) {
302 if (win->getTransients().size() == 0) return; // nothing to do
303
304 // put transients of win's transients in the stack
305 BlackboxWindowList::const_reverse_iterator it,
306 end = win->getTransients().rend();
307 for (it = win->getTransients().rbegin(); it != end; ++it) {
308 lowerTransients(*it, stack);
309 }
310
311 // put win's transients in the stack
312 for (it = win->getTransients().rbegin(); it != end; ++it) {
313 *stack++ = (*it)->getFrameWindow();
314 screen->updateNetizenWindowLower((*it)->getClientWindow());
315
316 if (! (*it)->isIconic()) {
317 Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
318 wkspc->stackingList.remove((*it));
319 wkspc->stackingList.push_back((*it));
320 }
321 }
322 }
323
324
325 void Workspace::raiseWindow(BlackboxWindow *w) {
326 BlackboxWindow *win = w;
327
328 if (win->isDesktop()) return;
329
330 // walk up the transient_for's to the window that is not a transient
331 while (win->isTransient() && ! win->isDesktop()) {
332 if (! win->getTransientFor()) break;
333 win = win->getTransientFor();
334 }
335
336 // get the total window count (win and all transients)
337 unsigned int i = 1 + countTransients(win);
338
339 // stack the window with all transients above
340 StackVector stack_vector(i);
341 StackVector::iterator stack = stack_vector.begin();
342
343 *(stack++) = win->getFrameWindow();
344 screen->updateNetizenWindowRaise(win->getClientWindow());
345 if (! (win->isIconic() || win->isDesktop())) {
346 Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
347 wkspc->stackingList.remove(win);
348 wkspc->stackingList.push_front(win);
349 }
350
351 raiseTransients(win, stack);
352
353 screen->raiseWindows(&stack_vector[0], stack_vector.size());
354 }
355
356
357 void Workspace::lowerWindow(BlackboxWindow *w) {
358 BlackboxWindow *win = w;
359
360 // walk up the transient_for's to the window that is not a transient
361 while (win->isTransient() && ! win->isDesktop()) {
362 if (! win->getTransientFor()) break;
363 win = win->getTransientFor();
364 }
365
366 // get the total window count (win and all transients)
367 unsigned int i = 1 + countTransients(win);
368
369 // stack the window with all transients above
370 StackVector stack_vector(i);
371 StackVector::iterator stack = stack_vector.begin();
372
373 lowerTransients(win, stack);
374
375 *(stack++) = win->getFrameWindow();
376 screen->updateNetizenWindowLower(win->getClientWindow());
377 if (! (win->isIconic() || win->isDesktop())) {
378 Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
379 wkspc->stackingList.remove(win);
380 wkspc->stackingList.push_back(win);
381 }
382
383 screen->lowerWindows(&stack_vector[0], stack_vector.size());
384 }
385
386
387 void Workspace::reconfigure(void) {
388 clientmenu->reconfigure();
389 std::for_each(windowList.begin(), windowList.end(),
390 std::mem_fun(&BlackboxWindow::reconfigure));
391 }
392
393
394 BlackboxWindow *Workspace::getWindow(unsigned int index) {
395 if (index < windowList.size()) {
396 BlackboxWindowList::iterator it = windowList.begin();
397 for(; index > 0; --index, ++it); /* increment to index */
398 return *it;
399 }
400 return 0;
401 }
402
403
404 BlackboxWindow*
405 Workspace::getNextWindowInList(BlackboxWindow *w) {
406 BlackboxWindowList::iterator it = std::find(windowList.begin(),
407 windowList.end(),
408 w);
409 assert(it != windowList.end()); // window must be in list
410 ++it; // next window
411 if (it == windowList.end())
412 return windowList.front(); // if we walked off the end, wrap around
413
414 return *it;
415 }
416
417
418 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
419 BlackboxWindowList::iterator it = std::find(windowList.begin(),
420 windowList.end(),
421 w);
422 assert(it != windowList.end()); // window must be in list
423 if (it == windowList.begin())
424 return windowList.back(); // if we walked of the front, wrap around
425
426 return *(--it);
427 }
428
429
430 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
431 return stackingList.front();
432 }
433
434
435 void Workspace::sendWindowList(Netizen &n) {
436 BlackboxWindowList::iterator it = windowList.begin(),
437 end = windowList.end();
438 for(; it != end; ++it)
439 n.sendWindowAdd((*it)->getClientWindow(), getID());
440 }
441
442
443 unsigned int Workspace::getCount(void) const {
444 return windowList.size();
445 }
446
447
448 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
449 BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
450 const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
451 for (; it != end; ++it)
452 if ((*it)->isNormal())
453 stack_order.push_back(*it);
454 }
455
456
457 bool Workspace::isCurrent(void) const {
458 return (id == screen->getCurrentWorkspaceID());
459 }
460
461
462 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
463 return (w == windowList.back());
464 }
465
466
467 void Workspace::setCurrent(void) {
468 screen->changeWorkspaceID(id);
469 }
470
471
472 void Workspace::readName(void) {
473 XAtom::StringVect namesList;
474 unsigned long numnames = id + 1;
475
476 // attempt to get from the _NET_WM_DESKTOP_NAMES property
477 if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
478 XAtom::utf8, numnames, namesList) &&
479 namesList.size() > id) {
480 name = namesList[id];
481
482 clientmenu->setLabel(name);
483 clientmenu->update();
484 } else {
485 /*
486 Use a default name. This doesn't actually change the class. That will
487 happen after the setName changes the root property, and that change
488 makes its way back to this function.
489 */
490 string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
491 "Workspace %d");
492 assert(tmp.length() < 32);
493 char default_name[32];
494 sprintf(default_name, tmp.c_str(), id + 1);
495
496 setName(default_name); // save this into the _NET_WM_DESKTOP_NAMES property
497 }
498 }
499
500
501 void Workspace::setName(const string& new_name) {
502 // set the _NET_WM_DESKTOP_NAMES property with the new name
503 XAtom::StringVect namesList;
504 unsigned long numnames = (unsigned) -1;
505 if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
506 XAtom::utf8, numnames, namesList) &&
507 namesList.size() > id)
508 namesList[id] = new_name;
509 else
510 namesList.push_back(new_name);
511
512 xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
513 XAtom::utf8, namesList);
514 }
515
516
517 /*
518 * Calculate free space available for window placement.
519 */
520 typedef std::vector<Rect> rectList;
521
522 static rectList calcSpace(const Rect &win, const rectList &spaces) {
523 Rect isect, extra;
524 rectList result;
525 rectList::const_iterator siter, end = spaces.end();
526 for (siter = spaces.begin(); siter != end; ++siter) {
527 const Rect &curr = *siter;
528
529 if(! win.intersects(curr)) {
530 result.push_back(curr);
531 continue;
532 }
533
534 /* Use an intersection of win and curr to determine the space around
535 * curr that we can use.
536 *
537 * NOTE: the spaces calculated can overlap.
538 */
539 isect = curr & win;
540
541 // left
542 extra.setCoords(curr.left(), curr.top(),
543 isect.left() - 1, curr.bottom());
544 if (extra.valid()) result.push_back(extra);
545
546 // top
547 extra.setCoords(curr.left(), curr.top(),
548 curr.right(), isect.top() - 1);
549 if (extra.valid()) result.push_back(extra);
550
551 // right
552 extra.setCoords(isect.right() + 1, curr.top(),
553 curr.right(), curr.bottom());
554 if (extra.valid()) result.push_back(extra);
555
556 // bottom
557 extra.setCoords(curr.left(), isect.bottom() + 1,
558 curr.right(), curr.bottom());
559 if (extra.valid()) result.push_back(extra);
560 }
561 return result;
562 }
563
564
565 static bool rowRLBT(const Rect &first, const Rect &second) {
566 if (first.bottom() == second.bottom())
567 return first.right() > second.right();
568 return first.bottom() > second.bottom();
569 }
570
571 static bool rowRLTB(const Rect &first, const Rect &second) {
572 if (first.y() == second.y())
573 return first.right() > second.right();
574 return first.y() < second.y();
575 }
576
577 static bool rowLRBT(const Rect &first, const Rect &second) {
578 if (first.bottom() == second.bottom())
579 return first.x() < second.x();
580 return first.bottom() > second.bottom();
581 }
582
583 static bool rowLRTB(const Rect &first, const Rect &second) {
584 if (first.y() == second.y())
585 return first.x() < second.x();
586 return first.y() < second.y();
587 }
588
589 static bool colLRTB(const Rect &first, const Rect &second) {
590 if (first.x() == second.x())
591 return first.y() < second.y();
592 return first.x() < second.x();
593 }
594
595 static bool colLRBT(const Rect &first, const Rect &second) {
596 if (first.x() == second.x())
597 return first.bottom() > second.bottom();
598 return first.x() < second.x();
599 }
600
601 static bool colRLTB(const Rect &first, const Rect &second) {
602 if (first.right() == second.right())
603 return first.y() < second.y();
604 return first.right() > second.right();
605 }
606
607 static bool colRLBT(const Rect &first, const Rect &second) {
608 if (first.right() == second.right())
609 return first.bottom() > second.bottom();
610 return first.right() > second.right();
611 }
612
613
614 bool Workspace::smartPlacement(Rect& win) {
615 rectList spaces;
616
617 //initially the entire screen is free
618 #ifdef XINERAMA
619 if (screen->isXineramaActive() &&
620 screen->getBlackbox()->doXineramaPlacement()) {
621 RectList availableAreas = screen->allAvailableAreas();
622 RectList::iterator it, end = availableAreas.end();
623
624 for (it = availableAreas.begin(); it != end; ++it)
625 spaces.push_back(*it);
626 } else
627 #endif // XINERAMA
628 spaces.push_back(screen->availableArea());
629
630 //Find Free Spaces
631 BlackboxWindowList::const_iterator wit = windowList.begin(),
632 end = windowList.end();
633 Rect tmp;
634 for (; wit != end; ++wit) {
635 const BlackboxWindow* const curr = *wit;
636
637 // watch for shaded windows and full-maxed windows
638 if (curr->isShaded()) {
639 if (screen->getPlaceIgnoreShaded()) continue;
640 } else if (curr->isMaximizedFull()) {
641 if (screen->getPlaceIgnoreMaximized()) continue;
642 }
643
644 tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
645 curr->frameRect().width() + screen->getBorderWidth(),
646 curr->frameRect().height() + screen->getBorderWidth());
647
648 spaces = calcSpace(tmp, spaces);
649 }
650
651 if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
652 if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
653 if(screen->getColPlacementDirection() == BScreen::TopBottom)
654 std::sort(spaces.begin(), spaces.end(), rowLRTB);
655 else
656 std::sort(spaces.begin(), spaces.end(), rowLRBT);
657 } else {
658 if(screen->getColPlacementDirection() == BScreen::TopBottom)
659 std::sort(spaces.begin(), spaces.end(), rowRLTB);
660 else
661 std::sort(spaces.begin(), spaces.end(), rowRLBT);
662 }
663 } else {
664 if(screen->getColPlacementDirection() == BScreen::TopBottom) {
665 if(screen->getRowPlacementDirection() == BScreen::LeftRight)
666 std::sort(spaces.begin(), spaces.end(), colLRTB);
667 else
668 std::sort(spaces.begin(), spaces.end(), colRLTB);
669 } else {
670 if(screen->getRowPlacementDirection() == BScreen::LeftRight)
671 std::sort(spaces.begin(), spaces.end(), colLRBT);
672 else
673 std::sort(spaces.begin(), spaces.end(), colRLBT);
674 }
675 }
676
677 rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
678 for(; sit != spaces_end; ++sit) {
679 if (sit->width() >= win.width() && sit->height() >= win.height())
680 break;
681 }
682
683 if (sit == spaces_end)
684 return False;
685
686 //set new position based on the empty space found
687 const Rect& where = *sit;
688 win.setX(where.x());
689 win.setY(where.y());
690
691 // adjust the location() based on left/right and top/bottom placement
692 if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
693 if (screen->getRowPlacementDirection() == BScreen::RightLeft)
694 win.setX(where.right() - win.width());
695 if (screen->getColPlacementDirection() == BScreen::BottomTop)
696 win.setY(where.bottom() - win.height());
697 } else {
698 if (screen->getColPlacementDirection() == BScreen::BottomTop)
699 win.setY(win.y() + where.height() - win.height());
700 if (screen->getRowPlacementDirection() == BScreen::RightLeft)
701 win.setX(win.x() + where.width() - win.width());
702 }
703 return True;
704 }
705
706
707 bool Workspace::underMousePlacement(Rect &win) {
708 int x, y, rx, ry;
709 Window c, r;
710 unsigned int m;
711 XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
712 &r, &c, &rx, &ry, &x, &y, &m);
713
714 Rect area;
715 #ifdef XINERAMA
716 if (screen->isXineramaActive() &&
717 screen->getBlackbox()->doXineramaPlacement()) {
718 RectList availableAreas = screen->allAvailableAreas();
719 RectList::iterator it, end = availableAreas.end();
720
721 for (it = availableAreas.begin(); it != end; ++it)
722 if (it->contains(rx, ry)) break;
723 assert(it != end); // the mouse isn't inside an area?
724 area = *it;
725 } else
726 #endif // XINERAMA
727 area = screen->availableArea();
728
729 x = rx - win.width() / 2;
730 y = ry - win.height() / 2;
731
732 if (x < area.x())
733 x = area.x();
734 if (y < area.y())
735 y = area.y();
736 if (x + win.width() > area.x() + area.width())
737 x = area.x() + area.width() - win.width();
738 if (y + win.height() > area.y() + area.height())
739 y = area.y() + area.height() - win.height();
740
741 win.setX(x);
742 win.setY(y);
743
744 return True;
745 }
746
747
748 bool Workspace::cascadePlacement(Rect &win, const int offset) {
749 Rect area;
750
751 #ifdef XINERAMA
752 if (screen->isXineramaActive() &&
753 screen->getBlackbox()->doXineramaPlacement()) {
754 area = screen->allAvailableAreas()[cascade_region];
755 } else
756 #endif // XINERAMA
757 area = screen->availableArea();
758
759 if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
760 (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
761 cascade_x = cascade_y = 0;
762 #ifdef XINERAMA
763 if (screen->isXineramaActive() &&
764 screen->getBlackbox()->doXineramaPlacement()) {
765 // go to the next xinerama region, and use its area
766 if (++cascade_region >= screen->allAvailableAreas().size())
767 cascade_region = 0;
768 area = screen->allAvailableAreas()[cascade_region];
769 }
770 #endif // XINERAMA
771 }
772
773 if (cascade_x == 0) {
774 cascade_x = area.x() + offset;
775 cascade_y = area.y() + offset;
776 }
777
778 win.setPos(cascade_x, cascade_y);
779
780 cascade_x += offset;
781 cascade_y += offset;
782
783 return True;
784 }
785
786
787 void Workspace::placeWindow(BlackboxWindow *win) {
788 Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
789 bool placed = False;
790
791 switch (screen->getPlacementPolicy()) {
792 case BScreen::RowSmartPlacement:
793 case BScreen::ColSmartPlacement:
794 placed = smartPlacement(new_win);
795 break;
796 case BScreen::UnderMousePlacement:
797 case BScreen::ClickMousePlacement:
798 placed = underMousePlacement(new_win);
799 default:
800 break; // handled below
801 } // switch
802
803 if (placed == False)
804 cascadePlacement(new_win, (win->getTitleHeight() +
805 screen->getBorderWidth() * 2));
806
807 if (new_win.right() > screen->availableArea().right())
808 new_win.setX(screen->availableArea().left());
809 if (new_win.bottom() > screen->availableArea().bottom())
810 new_win.setY(screen->availableArea().top());
811
812 win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
813 }
This page took 0.076472 seconds and 5 git commands to generate.