]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
sync with bb-cvs
[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     // watch for shaded windows and full-maxed windows
619     if (curr->isShaded()) {
620       if (screen->getPlaceIgnoreShaded()) continue;
621     } else if (curr->isMaximizedFull()) {
622       if (screen->getPlaceIgnoreMaximized()) continue;
623     }
624
625     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
626                 curr->frameRect().width() + screen->getBorderWidth(),
627                 curr->frameRect().height() + screen->getBorderWidth());
628
629     spaces = calcSpace(tmp, spaces);
630   }
631
632   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
633     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
634       if(screen->getColPlacementDirection() == BScreen::TopBottom)
635         std::sort(spaces.begin(), spaces.end(), rowLRTB);
636       else
637         std::sort(spaces.begin(), spaces.end(), rowLRBT);
638     } else {
639       if(screen->getColPlacementDirection() == BScreen::TopBottom)
640         std::sort(spaces.begin(), spaces.end(), rowRLTB);
641       else
642         std::sort(spaces.begin(), spaces.end(), rowRLBT);
643     }
644   } else {
645     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
646       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
647         std::sort(spaces.begin(), spaces.end(), colLRTB);
648       else
649         std::sort(spaces.begin(), spaces.end(), colRLTB);
650     } else {
651       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
652         std::sort(spaces.begin(), spaces.end(), colLRBT);
653       else
654         std::sort(spaces.begin(), spaces.end(), colRLBT);
655     }
656   }
657
658   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
659   for(; sit != spaces_end; ++sit) {
660     if (sit->width() >= win.width() && sit->height() >= win.height())
661       break;
662   }
663
664   if (sit == spaces_end)
665     return False;
666
667   //set new position based on the empty space found
668   const Rect& where = *sit;
669   win.setX(where.x());
670   win.setY(where.y());
671
672   // adjust the location() based on left/right and top/bottom placement
673   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
674     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
675       win.setX(where.right() - win.width());
676     if (screen->getColPlacementDirection() == BScreen::BottomTop)
677       win.setY(where.bottom() - win.height());
678   } else {
679     if (screen->getColPlacementDirection() == BScreen::BottomTop)
680       win.setY(win.y() + where.height() - win.height());
681     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
682       win.setX(win.x() + where.width() - win.width());
683   }
684   return True;
685 }
686
687
688 bool Workspace::underMousePlacement(Rect &win) {
689   int x, y, rx, ry;
690   Window c, r;
691   unsigned int m;
692   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
693                 &r, &c, &rx, &ry, &x, &y, &m);
694
695   Rect area;
696 #ifdef    XINERAMA
697   if (screen->isXineramaActive() &&
698       screen->getBlackbox()->doXineramaPlacement()) {
699     RectList availableAreas = screen->allAvailableAreas();
700     RectList::iterator it, end = availableAreas.end();
701
702     for (it = availableAreas.begin(); it != end; ++it)
703       if (it->contains(rx, ry)) break;
704     assert(it != end);  // the mouse isn't inside an area?
705     area = *it;
706   } else
707 #endif // XINERAMA
708     area = screen->availableArea();
709   
710   x = rx - win.width() / 2;
711   y = ry - win.height() / 2;
712
713   if (x < area.x())
714     x = area.x();
715   if (y < area.y())
716     y = area.y();
717   if (x + win.width() > area.x() + area.width())
718     x = area.x() + area.width() - win.width();
719   if (y + win.height() > area.y() + area.height())
720     y = area.y() + area.height() - win.height();
721
722   win.setX(x);
723   win.setY(y);
724
725   return True;
726 }
727
728
729 bool Workspace::cascadePlacement(Rect &win, const int offset) {
730   Rect area;
731   
732 #ifdef    XINERAMA
733   if (screen->isXineramaActive() &&
734       screen->getBlackbox()->doXineramaPlacement()) {
735     area = screen->allAvailableAreas()[cascade_region];
736   } else
737 #endif // XINERAMA
738     area = screen->availableArea();
739
740   if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
741       (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
742     cascade_x = cascade_y = 0;
743 #ifdef    XINERAMA
744     if (screen->isXineramaActive() &&
745         screen->getBlackbox()->doXineramaPlacement()) {
746       // go to the next xinerama region, and use its area
747       if (++cascade_region >= screen->allAvailableAreas().size())
748         cascade_region = 0;
749       area = screen->allAvailableAreas()[cascade_region];
750     }
751 #endif // XINERAMA
752   }
753
754   if (cascade_x == 0) {
755     cascade_x = area.x() + offset;
756     cascade_y = area.y() + offset;
757   }
758
759   win.setPos(cascade_x, cascade_y);
760
761   cascade_x += offset;
762   cascade_y += offset;
763
764   return True;
765 }
766
767
768 void Workspace::placeWindow(BlackboxWindow *win) {
769   Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
770   bool placed = False;
771
772   switch (screen->getPlacementPolicy()) {
773   case BScreen::RowSmartPlacement:
774   case BScreen::ColSmartPlacement:
775     placed = smartPlacement(new_win);
776     break;
777   case BScreen::UnderMousePlacement:
778   case BScreen::ClickMousePlacement:
779     placed = underMousePlacement(new_win);
780   default:
781     break; // handled below
782   } // switch
783
784   if (placed == False)
785     cascadePlacement(new_win, (win->getTitleHeight() +
786                                screen->getBorderWidth() * 2));
787
788   if (new_win.right() > screen->availableArea().right())
789     new_win.setX(screen->availableArea().left());
790   if (new_win.bottom() > screen->availableArea().bottom())
791     new_win.setY(screen->availableArea().top());
792
793   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
794 }