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