]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
move an assert to its proper place
[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     RectList::iterator it, end = availableAreas.end();
596
597     for (it = availableAreas.begin(); it != end; ++it)
598       spaces.push_back(*it);
599   } else
600 #endif // XINERAMA
601     spaces.push_back(screen->availableArea());
602
603   //Find Free Spaces
604   BlackboxWindowList::const_iterator wit = windowList.begin(),
605     end = windowList.end();
606   Rect tmp;
607   for (; wit != end; ++wit) {
608     const BlackboxWindow* const curr = *wit;
609
610     if (curr->isShaded() && screen->getPlaceIgnoreShaded()) continue;
611     if (curr->isMaximizedFull() && screen->getPlaceIgnoreMaximized()) continue;
612
613     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
614                 curr->frameRect().width() + screen->getBorderWidth(),
615                 curr->frameRect().height() + screen->getBorderWidth());
616
617     spaces = calcSpace(tmp, spaces);
618   }
619
620   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
621     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
622       if(screen->getColPlacementDirection() == BScreen::TopBottom)
623         std::sort(spaces.begin(), spaces.end(), rowLRTB);
624       else
625         std::sort(spaces.begin(), spaces.end(), rowLRBT);
626     } else {
627       if(screen->getColPlacementDirection() == BScreen::TopBottom)
628         std::sort(spaces.begin(), spaces.end(), rowRLTB);
629       else
630         std::sort(spaces.begin(), spaces.end(), rowRLBT);
631     }
632   } else {
633     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
634       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
635         std::sort(spaces.begin(), spaces.end(), colLRTB);
636       else
637         std::sort(spaces.begin(), spaces.end(), colRLTB);
638     } else {
639       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
640         std::sort(spaces.begin(), spaces.end(), colLRBT);
641       else
642         std::sort(spaces.begin(), spaces.end(), colRLBT);
643     }
644   }
645
646   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
647   for(; sit != spaces_end; ++sit) {
648     if (sit->width() >= win.width() && sit->height() >= win.height())
649       break;
650   }
651
652   if (sit == spaces_end)
653     return False;
654
655   //set new position based on the empty space found
656   const Rect& where = *sit;
657   win.setX(where.x());
658   win.setY(where.y());
659
660   // adjust the location() based on left/right and top/bottom placement
661   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
662     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
663       win.setX(where.right() - win.width());
664     if (screen->getColPlacementDirection() == BScreen::BottomTop)
665       win.setY(where.bottom() - win.height());
666   } else {
667     if (screen->getColPlacementDirection() == BScreen::BottomTop)
668       win.setY(win.y() + where.height() - win.height());
669     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
670       win.setX(win.x() + where.width() - win.width());
671   }
672   return True;
673 }
674
675
676 bool Workspace::underMousePlacement(Rect &win) {
677   int x, y, rx, ry;
678   Window c, r;
679   unsigned int m;
680   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
681                 &r, &c, &rx, &ry, &x, &y, &m);
682
683   Rect area;
684 #ifdef    XINERAMA
685   if (screen->isXineramaActive() &&
686       screen->getBlackbox()->doXineramaPlacement()) {
687     RectList availableAreas = screen->allAvailableAreas();
688     RectList::iterator it, end = availableAreas.end();
689
690     for (it = availableAreas.begin(); it != end; ++it)
691       if (it->contains(rx, ry)) break;
692     assert(it != end);  // the mouse isn't inside an area?
693     area = *it;
694   } else
695 #endif // XINERAMA
696     area = screen->availableArea();
697   
698   x = rx - win.width() / 2;
699   y = ry - win.height() / 2;
700
701   if (x < area.x())
702     x = area.x();
703   if (y < area.y())
704     y = area.y();
705   if (x + win.width() > area.x() + area.width())
706     x = area.x() + area.width() - win.width();
707   if (y + win.height() > area.y() + area.height())
708     y = area.y() + area.height() - win.height();
709
710   win.setX(x);
711   win.setY(y);
712
713   return True;
714 }
715
716
717 bool Workspace::cascadePlacement(Rect &win) {
718   const Rect &availableArea = screen->availableArea();
719
720   if ((cascade_x > static_cast<signed>(availableArea.width() / 2)) ||
721       (cascade_y > static_cast<signed>(availableArea.height() / 2)))
722     cascade_x = cascade_y = 32;
723
724   if (cascade_x == 32) {
725     cascade_x += availableArea.x();
726     cascade_y += availableArea.y();
727   }
728
729   win.setPos(cascade_x, cascade_y);
730
731   return True;
732 }
733
734
735 void Workspace::placeWindow(BlackboxWindow *win) {
736   Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
737   bool placed = False;
738
739   switch (screen->getPlacementPolicy()) {
740   case BScreen::RowSmartPlacement:
741   case BScreen::ColSmartPlacement:
742     placed = smartPlacement(new_win);
743     break;
744   case BScreen::UnderMousePlacement:
745   case BScreen::ClickMousePlacement:
746     placed = underMousePlacement(new_win);
747   default:
748     break; // handled below
749   } // switch
750
751   if (placed == False) {
752     cascadePlacement(new_win);
753     cascade_x += win->getTitleHeight() + (screen->getBorderWidth() * 2);
754     cascade_y += win->getTitleHeight() + (screen->getBorderWidth() * 2);
755   }
756
757   // make sure the placement was valid
758   assert(screen->availableArea().contains(new_win));
759
760   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
761 }