]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
save the window-to-window snapping right
[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, 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         w->setInputFocus();
106       else {
107         /*
108            not on the focused workspace, so the window is not going to get focus
109            but if the user wants new windows focused, then it should get focus
110            when this workspace does become focused.
111         */
112         lastfocus = w;
113       }
114     }
115   }
116
117   if (! w->isDesktop())
118     raiseWindow(w);
119   else
120     lowerWindow(w);
121 }
122
123
124 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
125   assert(w != 0);
126
127   stackingList.remove(w);
128
129   // pass focus to the next appropriate window
130   if ((w->isFocused() || w == lastfocus) &&
131       ! screen->getBlackbox()->doShutdown()) {
132     focusFallback(w);
133   }
134     
135   if (! w->isNormal()) return;
136
137   BlackboxWindowList::iterator it, end = windowList.end();
138   int i;
139   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
140     if (*it == w)
141       break;
142   assert(it != end);
143   
144   windowList.erase(it);
145   clientmenu->remove(i);
146   clientmenu->update();
147
148   if (! sticky) {
149     screen->updateNetizenWindowDel(w->getClientWindow());
150
151     BlackboxWindowList::iterator it = windowList.begin();
152     const BlackboxWindowList::iterator end = windowList.end();
153     unsigned int i = 0;
154     for (; it != end; ++it, ++i)
155       (*it)->setWindowNumber(i);
156   }
157
158   if (i == 0) {
159     cascade_x = cascade_y = 0;
160 #ifdef    XINERAMA
161     cascade_region = 0;
162 #endif // XINERAMA
163   }
164 }
165
166
167 void Workspace::focusFallback(const BlackboxWindow *old_window) {
168   BlackboxWindow *newfocus = 0;
169
170   if (id == screen->getCurrentWorkspaceID()) {
171     // The window is on the visible workspace.
172
173     // if it's a transient, then try to focus its parent
174     if (old_window && old_window->isTransient()) {
175       newfocus = old_window->getTransientFor();
176
177       if (! newfocus ||
178           newfocus->isIconic() ||                  // do not focus icons
179           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
180           ! newfocus->setInputFocus())
181         newfocus = 0;
182     }
183
184     if (! newfocus) {
185       BlackboxWindowList::iterator it = stackingList.begin(),
186                                   end = stackingList.end();
187       for (; it != end; ++it) {
188         BlackboxWindow *tmp = *it;
189         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
190           // we found our new focus target
191           newfocus = tmp;
192           break;
193         }
194       }
195     }
196
197     screen->getBlackbox()->setFocusedWindow(newfocus);
198   } else {
199     // The window is not on the visible workspace.
200
201     if (old_window && lastfocus == old_window) {
202       // The window was the last-focus target, so we need to replace it.
203       BlackboxWindow *win = (BlackboxWindow*) 0;
204       if (! stackingList.empty())
205         win = stackingList.front();
206       setLastFocusedWindow(win);
207     }
208   }
209 }
210
211
212 void Workspace::setFocused(const BlackboxWindow *w, bool focused) {
213   BlackboxWindowList::iterator it, end = windowList.end();
214   int i;
215   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
216     if (*it == w)
217       break;
218   // if its == end, then a window thats not in the windowList
219   // got focused, such as a !isNormal() window.
220   if (it != end)
221     clientmenu->setItemSelected(i, focused);
222 }
223
224
225 void Workspace::showAll(void) {
226   BlackboxWindowList::iterator it = stackingList.begin();
227   const BlackboxWindowList::iterator end = stackingList.end();
228   for (; it != end; ++it) {
229     BlackboxWindow *bw = *it;
230     // not normal windows cant focus from mouse enters anyways, so we dont
231     // need to unmap/remap them on workspace changes
232     if (! bw->isStuck() || bw->isNormal())
233       bw->show();
234   }
235 }
236
237
238 void Workspace::hideAll(void) {
239   // withdraw in reverse order to minimize the number of Expose events
240   BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
241   const BlackboxWindowList::reverse_iterator end = stackingList.rend();
242   while (it != end) {
243     BlackboxWindow *bw = *it;
244     ++it; // withdraw removes the current item from the list so we need the next
245           // iterator before that happens
246     // not normal windows cant focus from mouse enters anyways, so we dont
247     // need to unmap/remap them on workspace changes
248     if (! bw->isStuck() || bw->isNormal())
249       bw->withdraw();
250   }
251 }
252
253
254 void Workspace::removeAll(void) {
255   while (! windowList.empty())
256     windowList.front()->iconify();
257 }
258
259
260 /*
261  * returns the number of transients for win, plus the number of transients
262  * associated with each transient of win
263  */
264 static int countTransients(const BlackboxWindow * const win) {
265   int ret = win->getTransients().size();
266   if (ret > 0) {
267     BlackboxWindowList::const_iterator it, end = win->getTransients().end();
268     for (it = win->getTransients().begin(); it != end; ++it) {
269       ret += countTransients(*it);
270     }
271   }
272   return ret;
273 }
274
275
276 /*
277  * puts the transients of win into the stack. windows are stacked above
278  * the window before it in the stackvector being iterated, meaning
279  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
280  * stack[1], etc...
281  */
282 void Workspace::raiseTransients(const BlackboxWindow * const win,
283                                 StackVector::iterator &stack) {
284   if (win->getTransients().size() == 0) return; // nothing to do
285
286   // put win's transients in the stack
287   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
288   for (it = win->getTransients().begin(); it != end; ++it) {
289     *stack++ = (*it)->getFrameWindow();
290     screen->updateNetizenWindowRaise((*it)->getClientWindow());
291
292     if (! (*it)->isIconic()) {
293       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
294       wkspc->stackingList.remove((*it));
295       wkspc->stackingList.push_front((*it));
296     }
297   }
298
299   // put transients of win's transients in the stack
300   for (it = win->getTransients().begin(); it != end; ++it) {
301     raiseTransients(*it, stack);
302   }
303 }
304
305
306 void Workspace::lowerTransients(const BlackboxWindow * const win,
307                                 StackVector::iterator &stack) {
308   if (win->getTransients().size() == 0) return; // nothing to do
309
310   // put transients of win's transients in the stack
311   BlackboxWindowList::const_reverse_iterator it,
312     end = win->getTransients().rend();
313   for (it = win->getTransients().rbegin(); it != end; ++it) {
314     lowerTransients(*it, stack);
315   }
316
317   // put win's transients in the stack
318   for (it = win->getTransients().rbegin(); it != end; ++it) {
319     *stack++ = (*it)->getFrameWindow();
320     screen->updateNetizenWindowLower((*it)->getClientWindow());
321
322     if (! (*it)->isIconic()) {
323       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
324       wkspc->stackingList.remove((*it));
325       wkspc->stackingList.push_back((*it));
326     }
327   }
328 }
329
330
331 void Workspace::raiseWindow(BlackboxWindow *w) {
332   BlackboxWindow *win = w;
333
334   if (win->isDesktop()) return;
335
336   // walk up the transient_for's to the window that is not a transient
337   while (win->isTransient() && ! win->isDesktop()) {
338     if (! win->getTransientFor()) break;
339     win = win->getTransientFor();
340   }
341
342   // get the total window count (win and all transients)
343   unsigned int i = 1 + countTransients(win);
344
345   // stack the window with all transients above
346   StackVector stack_vector(i);
347   StackVector::iterator stack = stack_vector.begin();
348
349   *(stack++) = win->getFrameWindow();
350   screen->updateNetizenWindowRaise(win->getClientWindow());
351   if (! (win->isIconic() || win->isDesktop())) {
352     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
353     wkspc->stackingList.remove(win);
354     wkspc->stackingList.push_front(win);
355   }
356
357   raiseTransients(win, stack);
358
359   screen->raiseWindows(&stack_vector[0], stack_vector.size());
360 }
361
362
363 void Workspace::lowerWindow(BlackboxWindow *w) {
364   BlackboxWindow *win = w;
365
366   // walk up the transient_for's to the window that is not a transient
367   while (win->isTransient() && ! win->isDesktop()) {
368     if (! win->getTransientFor()) break;
369     win = win->getTransientFor();
370   }
371
372   // get the total window count (win and all transients)
373   unsigned int i = 1 + countTransients(win);
374
375   // stack the window with all transients above
376   StackVector stack_vector(i);
377   StackVector::iterator stack = stack_vector.begin();
378
379   lowerTransients(win, stack);
380
381   *(stack++) = win->getFrameWindow();
382   screen->updateNetizenWindowLower(win->getClientWindow());
383   if (! (win->isIconic() || win->isDesktop())) {
384     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
385     wkspc->stackingList.remove(win);
386     wkspc->stackingList.push_back(win);
387   }
388
389   screen->lowerWindows(&stack_vector[0], stack_vector.size());
390 }
391
392
393 void Workspace::reconfigure(void) {
394   clientmenu->reconfigure();
395   std::for_each(windowList.begin(), windowList.end(),
396                 std::mem_fun(&BlackboxWindow::reconfigure));
397 }
398
399
400 BlackboxWindow *Workspace::getWindow(unsigned int index) {
401   if (index < windowList.size()) {
402     BlackboxWindowList::iterator it = windowList.begin();
403     for(; index > 0; --index, ++it); /* increment to index */
404     return *it;
405   }
406   return 0;
407 }
408
409
410 BlackboxWindow*
411 Workspace::getNextWindowInList(BlackboxWindow *w) {
412   BlackboxWindowList::iterator it = std::find(windowList.begin(),
413                                               windowList.end(),
414                                               w);
415   assert(it != windowList.end());   // window must be in list
416   ++it;                             // next window
417   if (it == windowList.end())
418     return windowList.front();      // if we walked off the end, wrap around
419
420   return *it;
421 }
422
423
424 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
425   BlackboxWindowList::iterator it = std::find(windowList.begin(),
426                                               windowList.end(),
427                                               w);
428   assert(it != windowList.end()); // window must be in list
429   if (it == windowList.begin())
430     return windowList.back();     // if we walked of the front, wrap around
431
432   return *(--it);
433 }
434
435
436 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
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 }