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