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