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