]> icculus.org git repositories - mikachu/openbox.git/blob - src/workspace.cc
compiles now. uses xft2
[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 "blackbox.hh"
49 #include "font.hh"
50 #include "screen.hh"
51 #include "util.hh"
52 #include "window.hh"
53 #include "workspace.hh"
54 #include "xatom.hh"
55
56
57 Workspace::Workspace(BScreen *scrn, unsigned int i) {
58   screen = scrn;
59   xatom = screen->getBlackbox()->getXAtom();
60
61   cascade_x = cascade_y = 0;
62 #ifdef    XINERAMA
63   cascade_region = 0;
64 #endif // XINERAMA
65
66   id = i;
67
68   lastfocus = (BlackboxWindow *) 0;
69
70   readName();
71 }
72
73
74 void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
75   assert(w != 0);
76
77   if (place) placeWindow(w);
78
79   stackingList.push_front(w);
80
81   if (! sticky)
82     w->setWorkspace(id);
83   
84   if (! w->isNormal()) {
85     if (! sticky) {
86       // just give it some number, else bad things happen as it is assumed to
87       // not be on a workspace
88       w->setWindowNumber(0);
89     }
90   } else {
91     if (! sticky)
92       w->setWindowNumber(windowList.size());
93
94     windowList.push_back(w);
95
96     if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
97                                  w->getTransientFor()->isFocused())) {
98       if (id != screen->getCurrentWorkspaceID()) {
99         /*
100            not on the focused workspace, so the window is not going to get focus
101            but if the user wants new windows focused, then it should get focus
102            when this workspace does become focused.
103         */
104         lastfocus = w;
105       }
106     }
107   }
108
109   if (! w->isDesktop())
110     raiseWindow(w);
111   else
112     lowerWindow(w);
113 }
114
115
116 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
117   assert(w != 0);
118
119   stackingList.remove(w);
120
121   // pass focus to the next appropriate window
122   if ((w->isFocused() || w == lastfocus) &&
123       ! screen->getBlackbox()->doShutdown()) {
124     focusFallback(w);
125   }
126     
127   if (! w->isNormal()) return;
128
129   BlackboxWindowList::iterator it, end = windowList.end();
130   int i;
131   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
132     if (*it == w)
133       break;
134   assert(it != end);
135   
136   windowList.erase(it);
137   if (! sticky) {
138     BlackboxWindowList::iterator it = windowList.begin();
139     const BlackboxWindowList::iterator end = windowList.end();
140     unsigned int i = 0;
141     for (; it != end; ++it, ++i)
142       (*it)->setWindowNumber(i);
143   }
144
145   if (i == 0) {
146     cascade_x = cascade_y = 0;
147 #ifdef    XINERAMA
148     cascade_region = 0;
149 #endif // XINERAMA
150   }
151 }
152
153
154 void Workspace::focusFallback(const BlackboxWindow *old_window) {
155   BlackboxWindow *newfocus = 0;
156
157   if (id == screen->getCurrentWorkspaceID()) {
158     // The window is on the visible workspace.
159
160     // if it's a transient, then try to focus its parent
161     if (old_window && old_window->isTransient()) {
162       newfocus = old_window->getTransientFor();
163
164       if (! newfocus ||
165           newfocus->isIconic() ||                  // do not focus icons
166           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
167           ! newfocus->setInputFocus())
168         newfocus = 0;
169     }
170
171     if (! newfocus) {
172       BlackboxWindowList::iterator it = stackingList.begin(),
173                                   end = stackingList.end();
174       for (; it != end; ++it) {
175         BlackboxWindow *tmp = *it;
176         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
177           // we found our new focus target
178           newfocus = tmp;
179           break;
180         }
181       }
182     }
183
184     screen->getBlackbox()->setFocusedWindow(newfocus);
185   } else {
186     // The window is not on the visible workspace.
187
188     if (old_window && lastfocus == old_window) {
189       // The window was the last-focus target, so we need to replace it.
190       BlackboxWindow *win = (BlackboxWindow*) 0;
191       if (! stackingList.empty())
192         win = stackingList.front();
193       setLastFocusedWindow(win);
194     }
195   }
196 }
197
198
199 void Workspace::removeAll(void) {
200   while (! windowList.empty())
201     windowList.front()->iconify();
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     // sticky windows arent unmapped on a workspace change so we don't have ot
210     // map them, but sometimes on a restart, another app can unmap our sticky
211     // windows, so we map on startup always
212     if (! bw->isStuck() || screen->getBlackbox()->isStartup())
213       bw->show();
214   }
215 }
216
217
218 void Workspace::hideAll(void) {
219   // withdraw in reverse order to minimize the number of Expose events
220
221   BlackboxWindowList lst(stackingList.rbegin(), stackingList.rend());
222
223   BlackboxWindowList::iterator it = lst.begin();
224   const BlackboxWindowList::iterator end = lst.end();
225   for (; it != end; ++it) {
226     BlackboxWindow *bw = *it;
227     // don't hide sticky windows, or they'll end up flickering on a workspace
228     // change
229     if (! bw->isStuck())
230       bw->withdraw();
231   }
232 }
233
234
235
236 /*
237  * returns the number of transients for win, plus the number of transients
238  * associated with each transient of win
239  */
240 static unsigned int countTransients(const BlackboxWindow * const win) {
241   BlackboxWindowList transients = win->getTransients();
242   if (transients.empty()) return 0;
243
244   unsigned int ret = transients.size();
245   BlackboxWindowList::const_iterator it = transients.begin(),
246     end = transients.end();
247   for (; it != end; ++it)
248     ret += countTransients(*it);
249
250   return ret;
251 }
252
253
254 /*
255  * puts the transients of win into the stack. windows are stacked above
256  * the window before it in the stackvector being iterated, meaning
257  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
258  * stack[1], etc...
259  */
260 void Workspace::raiseTransients(const BlackboxWindow * const win,
261                                 StackVector::iterator &stack) {
262   if (win->getTransients().empty()) return; // nothing to do
263
264   // put win's transients in the stack
265   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
266   for (it = win->getTransients().begin(); it != end; ++it) {
267     BlackboxWindow *w = *it;
268     *stack++ = w->getFrameWindow();
269
270     if (! w->isIconic()) {
271       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
272       wkspc->stackingList.remove(w);
273       wkspc->stackingList.push_front(w);
274     }
275   }
276
277   // put transients of win's transients in the stack
278   for (it = win->getTransients().begin(); it != end; ++it)
279     raiseTransients(*it, stack);
280 }
281
282
283 void Workspace::lowerTransients(const BlackboxWindow * const win,
284                                 StackVector::iterator &stack) {
285   if (win->getTransients().empty()) return; // nothing to do
286
287   // put transients of win's transients in the stack
288   BlackboxWindowList::const_reverse_iterator it,
289     end = win->getTransients().rend();
290   for (it = win->getTransients().rbegin(); it != end; ++it)
291     lowerTransients(*it, stack);
292
293   // put win's transients in the stack
294   for (it = win->getTransients().rbegin(); it != end; ++it) {
295     BlackboxWindow *w = *it;
296     *stack++ = w->getFrameWindow();
297
298     if (! w->isIconic()) {
299       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
300       wkspc->stackingList.remove(w);
301       wkspc->stackingList.push_back(w);
302     }
303   }
304 }
305
306
307 void Workspace::raiseWindow(BlackboxWindow *w) {
308   BlackboxWindow *win = w;
309
310   if (win->isDesktop()) return;
311
312   // walk up the transient_for's to the window that is not a transient
313   while (win->isTransient() && win->getTransientFor())
314     win = win->getTransientFor();
315
316   // get the total window count (win and all transients)
317   unsigned int i = 1 + countTransients(win);
318
319   // stack the window with all transients above
320   StackVector stack_vector(i);
321   StackVector::iterator stack = stack_vector.begin();
322
323   *(stack++) = win->getFrameWindow();
324   if (! (win->isIconic() || win->isDesktop())) {
325     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
326     wkspc->stackingList.remove(win);
327     wkspc->stackingList.push_front(win);
328   }
329
330   raiseTransients(win, stack);
331
332   screen->raiseWindows(&stack_vector[0], stack_vector.size());
333 }
334
335
336 void Workspace::lowerWindow(BlackboxWindow *w) {
337   BlackboxWindow *win = w;
338
339   // walk up the transient_for's to the window that is not a transient
340   while (win->isTransient() && win->getTransientFor())
341     win = win->getTransientFor();
342
343   // get the total window count (win and all transients)
344   unsigned int i = 1 + countTransients(win);
345
346   // stack the window with all transients above
347   StackVector stack_vector(i);
348   StackVector::iterator stack = stack_vector.begin();
349
350   lowerTransients(win, stack);
351
352   *(stack++) = win->getFrameWindow();
353   if (! (win->isIconic() || win->isDesktop())) {
354     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
355     wkspc->stackingList.remove(win);
356     wkspc->stackingList.push_back(win);
357   }
358
359   screen->lowerWindows(&stack_vector[0], stack_vector.size());
360 }
361
362
363 void Workspace::reconfigure(void) {
364   std::for_each(windowList.begin(), windowList.end(),
365                 std::mem_fun(&BlackboxWindow::reconfigure));
366 }
367
368
369 BlackboxWindow *Workspace::getWindow(unsigned int index) {
370   if (index < windowList.size()) {
371     BlackboxWindowList::iterator it = windowList.begin();
372     while (index-- > 0) // increment to index
373       ++it;
374     return *it;
375   }
376
377   return 0;
378 }
379
380
381 BlackboxWindow*
382 Workspace::getNextWindowInList(BlackboxWindow *w) {
383   BlackboxWindowList::iterator it = std::find(windowList.begin(),
384                                               windowList.end(),
385                                               w);
386   assert(it != windowList.end());   // window must be in list
387   ++it;                             // next window
388   if (it == windowList.end())
389     return windowList.front();      // if we walked off the end, wrap around
390
391   return *it;
392 }
393
394
395 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
396   BlackboxWindowList::iterator it = std::find(windowList.begin(),
397                                               windowList.end(),
398                                               w);
399   assert(it != windowList.end()); // window must be in list
400   if (it == windowList.begin())
401     return windowList.back();     // if we walked of the front, wrap around
402
403   return *(--it);
404 }
405
406
407 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
408   assert(! stackingList.empty());
409   return stackingList.front();
410 }
411
412
413 unsigned int Workspace::getCount(void) const {
414   return windowList.size();
415 }
416
417
418 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
419   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
420   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
421   for (; it != end; ++it)
422     // don't add desktop wnidows, or sticky windows more than once
423     if (! ( (*it)->isDesktop() ||
424             ((*it)->isStuck() && id != screen->getCurrentWorkspaceID())))
425       stack_order.push_back(*it);
426 }
427
428
429 bool Workspace::isCurrent(void) const {
430   return (id == screen->getCurrentWorkspaceID());
431 }
432
433
434 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
435   return (w == windowList.back());
436 }
437
438
439 void Workspace::setCurrent(void) {
440   screen->changeWorkspaceID(id);
441 }
442
443
444 void Workspace::readName(void) {
445   XAtom::StringVect namesList;
446   unsigned long numnames = id + 1;
447     
448   // attempt to get from the _NET_WM_DESKTOP_NAMES property
449   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
450                       XAtom::utf8, numnames, namesList) &&
451       namesList.size() > id) {
452     name = namesList[id];
453   
454   } else {
455     /*
456        Use a default name. This doesn't actually change the class. That will
457        happen after the setName changes the root property, and that change
458        makes its way back to this function.
459     */
460     string tmp = "Workspace %d";
461     assert(tmp.length() < 32);
462     char default_name[32];
463     sprintf(default_name, tmp.c_str(), id + 1);
464     
465     setName(default_name);  // save this into the _NET_WM_DESKTOP_NAMES property
466   }
467 }
468
469
470 void Workspace::setName(const string& new_name) {
471   // set the _NET_WM_DESKTOP_NAMES property with the new name
472   XAtom::StringVect namesList;
473   unsigned long numnames = (unsigned) -1;
474   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
475                       XAtom::utf8, numnames, namesList) &&
476       namesList.size() > id)
477     namesList[id] = new_name;
478   else
479     namesList.push_back(new_name);
480
481   xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
482                   XAtom::utf8, namesList);
483 }
484
485
486 /*
487  * Calculate free space available for window placement.
488  */
489 Workspace::rectList Workspace::calcSpace(const Rect &win,
490                                          const rectList &spaces) const {
491   Rect isect, extra;
492   rectList result;
493   rectList::const_iterator siter, end = spaces.end();
494   for (siter = spaces.begin(); siter != end; ++siter) {
495     const Rect &curr = *siter;
496
497     if(! win.intersects(curr)) {
498       result.push_back(curr);
499       continue;
500     }
501
502     /* Use an intersection of win and curr to determine the space around
503      * curr that we can use.
504      *
505      * NOTE: the spaces calculated can overlap.
506      */
507     isect = curr & win;
508
509     // left
510     extra.setCoords(curr.left(), curr.top(),
511                     isect.left() - screen->getSnapOffset(), curr.bottom());
512     if (extra.valid()) result.push_back(extra);
513
514     // top
515     extra.setCoords(curr.left(), curr.top(),
516                     curr.right(), isect.top() - screen->getSnapOffset());
517     if (extra.valid()) result.push_back(extra);
518
519     // right
520     extra.setCoords(isect.right() + screen->getSnapOffset(), curr.top(),
521                     curr.right(), curr.bottom());
522     if (extra.valid()) result.push_back(extra);
523
524     // bottom
525     extra.setCoords(curr.left(), isect.bottom() + screen->getSnapOffset(),
526                     curr.right(), curr.bottom());
527     if (extra.valid()) result.push_back(extra);
528   }
529   return result;
530 }
531
532
533 static bool rowRLBT(const Rect &first, const Rect &second) {
534   if (first.bottom() == second.bottom())
535     return first.right() > second.right();
536   return first.bottom() > second.bottom();
537 }
538
539 static bool rowRLTB(const Rect &first, const Rect &second) {
540   if (first.y() == second.y())
541     return first.right() > second.right();
542   return first.y() < second.y();
543 }
544
545 static bool rowLRBT(const Rect &first, const Rect &second) {
546   if (first.bottom() == second.bottom())
547     return first.x() < second.x();
548   return first.bottom() > second.bottom();
549 }
550
551 static bool rowLRTB(const Rect &first, const Rect &second) {
552   if (first.y() == second.y())
553     return first.x() < second.x();
554   return first.y() < second.y();
555 }
556
557 static bool colLRTB(const Rect &first, const Rect &second) {
558   if (first.x() == second.x())
559     return first.y() < second.y();
560   return first.x() < second.x();
561 }
562
563 static bool colLRBT(const Rect &first, const Rect &second) {
564   if (first.x() == second.x())
565     return first.bottom() > second.bottom();
566   return first.x() < second.x();
567 }
568
569 static bool colRLTB(const Rect &first, const Rect &second) {
570   if (first.right() == second.right())
571     return first.y() < second.y();
572   return first.right() > second.right();
573 }
574
575 static bool colRLBT(const Rect &first, const Rect &second) {
576   if (first.right() == second.right())
577     return first.bottom() > second.bottom();
578   return first.right() > second.right();
579 }
580
581
582 bool Workspace::smartPlacement(Rect& win) {
583   rectList spaces;
584  
585   //initially the entire screen is free
586 #ifdef    XINERAMA
587   if (screen->isXineramaActive() &&
588       screen->getBlackbox()->doXineramaPlacement()) {
589     RectList availableAreas = screen->allAvailableAreas();
590     RectList::iterator it, end = availableAreas.end();
591
592     for (it = availableAreas.begin(); it != end; ++it) {
593       Rect r = *it;
594       r.setRect(r.x() + screen->getSnapOffset(),
595                 r.y() + screen->getSnapOffset(),
596                 r.width() - screen->getSnapOffset(),
597                 r.height() - screen->getSnapOffset());
598       spaces.push_back(*it);
599     }
600   } else
601 #endif // XINERAMA
602   {
603     Rect r = screen->availableArea();
604     r.setRect(r.x() + screen->getSnapOffset(),
605               r.y() + screen->getSnapOffset(),
606               r.width() - screen->getSnapOffset(),
607               r.height() - screen->getSnapOffset());
608     spaces.push_back(r);
609   }
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 }