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