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