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