]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
handle toolbars and utility windows proper. also, no more crashes from kpager (i...
[mikachu/openbox.git] / src / Workspace.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Workspace.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31
32 #ifdef    HAVE_STDIO_H
33 #  include <stdio.h>
34 #endif // HAVE_STDIO_H
35
36 #ifdef HAVE_STRING_H
37 #  include <string.h>
38 #endif // HAVE_STRING_H
39 }
40
41 #include <assert.h>
42
43 #include <functional>
44 #include <string>
45
46 using std::string;
47
48 #include "i18n.hh"
49 #include "blackbox.hh"
50 #include "Clientmenu.hh"
51 #include "Font.hh"
52 #include "Netizen.hh"
53 #include "Screen.hh"
54 #include "Toolbar.hh"
55 #include "Util.hh"
56 #include "Window.hh"
57 #include "Workspace.hh"
58 #include "Windowmenu.hh"
59 #include "XAtom.hh"
60
61
62 Workspace::Workspace(BScreen *scrn, unsigned int i) {
63   screen = scrn;
64   xatom = screen->getBlackbox()->getXAtom();
65
66   cascade_x = cascade_y = 0;
67 #ifdef    XINERAMA
68   cascade_region = 0;
69 #endif // XINERAMA
70
71   id = i;
72
73   clientmenu = new Clientmenu(this);
74
75   lastfocus = (BlackboxWindow *) 0;
76
77   readName();
78 }
79
80
81 void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
82   assert(w != 0);
83
84   if (place) placeWindow(w);
85
86   stackingList.push_front(w);
87
88   if (w->isNormal()) {
89     if (! sticky) {
90       w->setWorkspace(id);
91       w->setWindowNumber(windowList.size());
92     }
93
94     windowList.push_back(w);
95
96     clientmenu->insert(w->getTitle());
97     clientmenu->update();
98
99     if (! sticky)
100       screen->updateNetizenWindowAdd(w->getClientWindow(), id);
101
102     if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
103                                  w->getTransientFor()->isFocused())) {
104       if (id == screen->getCurrentWorkspaceID())
105         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::removeAll(void) {
226   while (! windowList.empty())
227     windowList.front()->iconify();
228 }
229
230
231 /*
232  * returns the number of transients for win, plus the number of transients
233  * associated with each transient of win
234  */
235 static unsigned int countTransients(const BlackboxWindow * const win) {
236   BlackboxWindowList transients = win->getTransients();
237   if (transients.empty()) return 0;
238
239   unsigned int ret = transients.size();
240   BlackboxWindowList::const_iterator it = transients.begin(),
241     end = transients.end();
242   for (; it != end; ++it)
243     ret += countTransients(*it);
244
245   return ret;
246 }
247
248
249 /*
250  * puts the transients of win into the stack. windows are stacked above
251  * the window before it in the stackvector being iterated, meaning
252  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
253  * stack[1], etc...
254  */
255 void Workspace::raiseTransients(const BlackboxWindow * const win,
256                                 StackVector::iterator &stack) {
257   if (win->getTransients().empty()) return; // nothing to do
258
259   // put win's transients in the stack
260   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
261   for (it = win->getTransients().begin(); it != end; ++it) {
262     BlackboxWindow *w = *it;
263     *stack++ = w->getFrameWindow();
264     screen->updateNetizenWindowRaise(w->getClientWindow());
265
266     if (! w->isIconic()) {
267       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
268       wkspc->stackingList.remove(w);
269       wkspc->stackingList.push_front(w);
270     }
271   }
272
273   // put transients of win's transients in the stack
274   for (it = win->getTransients().begin(); it != end; ++it)
275     raiseTransients(*it, stack);
276 }
277
278
279 void Workspace::lowerTransients(const BlackboxWindow * const win,
280                                 StackVector::iterator &stack) {
281   if (win->getTransients().empty()) return; // nothing to do
282
283   // put transients of win's transients in the stack
284   BlackboxWindowList::const_reverse_iterator it,
285     end = win->getTransients().rend();
286   for (it = win->getTransients().rbegin(); it != end; ++it)
287     lowerTransients(*it, stack);
288
289   // put win's transients in the stack
290   for (it = win->getTransients().rbegin(); it != end; ++it) {
291     BlackboxWindow *w = *it;
292     *stack++ = w->getFrameWindow();
293     screen->updateNetizenWindowLower(w->getClientWindow());
294
295     if (! w->isIconic()) {
296       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
297       wkspc->stackingList.remove(w);
298       wkspc->stackingList.push_back(w);
299     }
300   }
301 }
302
303
304 void Workspace::raiseWindow(BlackboxWindow *w) {
305   BlackboxWindow *win = w;
306
307   if (win->isDesktop()) return;
308
309   // walk up the transient_for's to the window that is not a transient
310   while (win->isTransient() && win->getTransientFor())
311     win = win->getTransientFor();
312
313   // get the total window count (win and all transients)
314   unsigned int i = 1 + countTransients(win);
315
316   // stack the window with all transients above
317   StackVector stack_vector(i);
318   StackVector::iterator stack = stack_vector.begin();
319
320   *(stack++) = win->getFrameWindow();
321   screen->updateNetizenWindowRaise(win->getClientWindow());
322   if (! (win->isIconic() || win->isDesktop())) {
323     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
324     wkspc->stackingList.remove(win);
325     wkspc->stackingList.push_front(win);
326   }
327
328   raiseTransients(win, stack);
329
330   screen->raiseWindows(&stack_vector[0], stack_vector.size());
331 }
332
333
334 void Workspace::lowerWindow(BlackboxWindow *w) {
335   BlackboxWindow *win = w;
336
337   // walk up the transient_for's to the window that is not a transient
338   while (win->isTransient() && win->getTransientFor())
339     win = win->getTransientFor();
340
341   // get the total window count (win and all transients)
342   unsigned int i = 1 + countTransients(win);
343
344   // stack the window with all transients above
345   StackVector stack_vector(i);
346   StackVector::iterator stack = stack_vector.begin();
347
348   lowerTransients(win, stack);
349
350   *(stack++) = win->getFrameWindow();
351   screen->updateNetizenWindowLower(win->getClientWindow());
352   if (! (win->isIconic() || win->isDesktop())) {
353     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
354     wkspc->stackingList.remove(win);
355     wkspc->stackingList.push_back(win);
356   }
357
358   screen->lowerWindows(&stack_vector[0], stack_vector.size());
359 }
360
361
362 void Workspace::reconfigure(void) {
363   clientmenu->reconfigure();
364   std::for_each(windowList.begin(), windowList.end(),
365                 std::mem_fun(&BlackboxWindow::reconfigure));
366 }
367
368
369 BlackboxWindow *Workspace::getWindow(unsigned int index) {
370   if (index < windowList.size()) {
371     BlackboxWindowList::iterator it = windowList.begin();
372     while (index-- > 0) // increment to index
373       ++it;
374     return *it;
375   }
376
377   return 0;
378 }
379
380
381 BlackboxWindow*
382 Workspace::getNextWindowInList(BlackboxWindow *w) {
383   BlackboxWindowList::iterator it = std::find(windowList.begin(),
384                                               windowList.end(),
385                                               w);
386   assert(it != windowList.end());   // window must be in list
387   ++it;                             // next window
388   if (it == windowList.end())
389     return windowList.front();      // if we walked off the end, wrap around
390
391   return *it;
392 }
393
394
395 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
396   BlackboxWindowList::iterator it = std::find(windowList.begin(),
397                                               windowList.end(),
398                                               w);
399   assert(it != windowList.end()); // window must be in list
400   if (it == windowList.begin())
401     return windowList.back();     // if we walked of the front, wrap around
402
403   return *(--it);
404 }
405
406
407 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
408   assert(! stackingList.empty());
409   return stackingList.front();
410 }
411
412
413 void Workspace::sendWindowList(Netizen &n) {
414   BlackboxWindowList::iterator it = windowList.begin(),
415     end = windowList.end();
416   for(; it != end; ++it)
417     n.sendWindowAdd((*it)->getClientWindow(), getID());
418 }
419
420
421 unsigned int Workspace::getCount(void) const {
422   return windowList.size();
423 }
424
425
426 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
427   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
428   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
429   for (; it != end; ++it)
430     if ((*it)->isNormal())
431       stack_order.push_back(*it);
432 }
433   
434
435 void Workspace::hide(void) {
436   BlackboxWindow *focused = screen->getBlackbox()->getFocusedWindow();
437   if (focused && focused->getScreen() == screen) {
438     assert(focused->isStuck() || focused->getWorkspaceNumber() == id);
439
440     lastfocus = focused;
441   } else {
442     // if no window had focus, no need to store a last focus
443     lastfocus = (BlackboxWindow *) 0;
444   }
445
446   // when we switch workspaces, unfocus whatever was focused
447   screen->getBlackbox()->setFocusedWindow((BlackboxWindow *) 0);
448
449   // withdraw windows in reverse order to minimize the number of Expose events
450
451   BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
452   const BlackboxWindowList::reverse_iterator end = stackingList.rend();
453   for (; it != end; ++it) {
454     BlackboxWindow *bw = *it;
455     // not normal windows cant focus from mouse enters anyways, so we dont
456     // need to unmap/remap them on workspace changes
457     if (! bw->isStuck() || bw->isNormal())
458       bw->withdraw();
459   }
460 }
461
462
463 void Workspace::show(void) {
464   BlackboxWindowList::iterator it = stackingList.begin();
465   const BlackboxWindowList::iterator end = stackingList.end();
466   for (; it != end; ++it) {
467     BlackboxWindow *bw = *it;
468     // not normal windows cant focus from mouse enters anyways, so we dont
469     // need to unmap/remap them on workspace changes
470     if (! bw->isStuck() || bw->isNormal())
471       bw->show();
472   }
473
474   XSync(screen->getBlackbox()->getXDisplay(), False);
475
476   if (screen->doFocusLast()) {
477     if (! screen->isSloppyFocus() && ! lastfocus && ! stackingList.empty())
478       lastfocus = stackingList.front();
479
480     if (lastfocus)
481       lastfocus->setInputFocus();
482   }
483 }
484
485
486 bool Workspace::isCurrent(void) const {
487   return (id == screen->getCurrentWorkspaceID());
488 }
489
490
491 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
492   return (w == windowList.back());
493 }
494
495
496 void Workspace::setCurrent(void) {
497   screen->changeWorkspaceID(id);
498 }
499
500
501 void Workspace::readName(void) {
502   XAtom::StringVect namesList;
503   unsigned long numnames = id + 1;
504     
505   // attempt to get from the _NET_WM_DESKTOP_NAMES property
506   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
507                       XAtom::utf8, numnames, namesList) &&
508       namesList.size() > id) {
509     name = namesList[id];
510   
511     clientmenu->setLabel(name);
512     clientmenu->update();
513   } else {
514     /*
515        Use a default name. This doesn't actually change the class. That will
516        happen after the setName changes the root property, and that change
517        makes its way back to this function.
518     */
519     string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
520                      "Workspace %d");
521     assert(tmp.length() < 32);
522     char default_name[32];
523     sprintf(default_name, tmp.c_str(), id + 1);
524     
525     setName(default_name);  // save this into the _NET_WM_DESKTOP_NAMES property
526   }
527 }
528
529
530 void Workspace::setName(const string& new_name) {
531   // set the _NET_WM_DESKTOP_NAMES property with the new name
532   XAtom::StringVect namesList;
533   unsigned long numnames = (unsigned) -1;
534   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
535                       XAtom::utf8, numnames, namesList) &&
536       namesList.size() > id)
537     namesList[id] = new_name;
538   else
539     namesList.push_back(new_name);
540
541   xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
542                   XAtom::utf8, namesList);
543 }
544
545
546 /*
547  * Calculate free space available for window placement.
548  */
549 typedef std::vector<Rect> rectList;
550
551 static rectList calcSpace(const Rect &win, const rectList &spaces) {
552   Rect isect, extra;
553   rectList result;
554   rectList::const_iterator siter, end = spaces.end();
555   for (siter = spaces.begin(); siter != end; ++siter) {
556     const Rect &curr = *siter;
557
558     if(! win.intersects(curr)) {
559       result.push_back(curr);
560       continue;
561     }
562
563     /* Use an intersection of win and curr to determine the space around
564      * curr that we can use.
565      *
566      * NOTE: the spaces calculated can overlap.
567      */
568     isect = curr & win;
569
570     // left
571     extra.setCoords(curr.left(), curr.top(),
572                     isect.left() - 1, curr.bottom());
573     if (extra.valid()) result.push_back(extra);
574
575     // top
576     extra.setCoords(curr.left(), curr.top(),
577                     curr.right(), isect.top() - 1);
578     if (extra.valid()) result.push_back(extra);
579
580     // right
581     extra.setCoords(isect.right() + 1, curr.top(),
582                     curr.right(), curr.bottom());
583     if (extra.valid()) result.push_back(extra);
584
585     // bottom
586     extra.setCoords(curr.left(), isect.bottom() + 1,
587                     curr.right(), curr.bottom());
588     if (extra.valid()) result.push_back(extra);
589   }
590   return result;
591 }
592
593
594 static bool rowRLBT(const Rect &first, const Rect &second) {
595   if (first.bottom() == second.bottom())
596     return first.right() > second.right();
597   return first.bottom() > second.bottom();
598 }
599
600 static bool rowRLTB(const Rect &first, const Rect &second) {
601   if (first.y() == second.y())
602     return first.right() > second.right();
603   return first.y() < second.y();
604 }
605
606 static bool rowLRBT(const Rect &first, const Rect &second) {
607   if (first.bottom() == second.bottom())
608     return first.x() < second.x();
609   return first.bottom() > second.bottom();
610 }
611
612 static bool rowLRTB(const Rect &first, const Rect &second) {
613   if (first.y() == second.y())
614     return first.x() < second.x();
615   return first.y() < second.y();
616 }
617
618 static bool colLRTB(const Rect &first, const Rect &second) {
619   if (first.x() == second.x())
620     return first.y() < second.y();
621   return first.x() < second.x();
622 }
623
624 static bool colLRBT(const Rect &first, const Rect &second) {
625   if (first.x() == second.x())
626     return first.bottom() > second.bottom();
627   return first.x() < second.x();
628 }
629
630 static bool colRLTB(const Rect &first, const Rect &second) {
631   if (first.right() == second.right())
632     return first.y() < second.y();
633   return first.right() > second.right();
634 }
635
636 static bool colRLBT(const Rect &first, const Rect &second) {
637   if (first.right() == second.right())
638     return first.bottom() > second.bottom();
639   return first.right() > second.right();
640 }
641
642
643 bool Workspace::smartPlacement(Rect& win) {
644   rectList spaces;
645  
646   //initially the entire screen is free
647 #ifdef    XINERAMA
648   if (screen->isXineramaActive() &&
649       screen->getBlackbox()->doXineramaPlacement()) {
650     RectList availableAreas = screen->allAvailableAreas();
651     RectList::iterator it, end = availableAreas.end();
652
653     for (it = availableAreas.begin(); it != end; ++it)
654       spaces.push_back(*it);
655   } else
656 #endif // XINERAMA
657     spaces.push_back(screen->availableArea());
658
659   //Find Free Spaces
660   BlackboxWindowList::const_iterator wit = windowList.begin(),
661     end = windowList.end();
662   Rect tmp;
663   for (; wit != end; ++wit) {
664     const BlackboxWindow* const curr = *wit;
665
666     // watch for shaded windows and full-maxed windows
667     if (curr->isShaded()) {
668       if (screen->getPlaceIgnoreShaded()) continue;
669     } else if (curr->isMaximizedFull()) {
670       if (screen->getPlaceIgnoreMaximized()) continue;
671     }
672
673     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
674                 curr->frameRect().width() + screen->getBorderWidth(),
675                 curr->frameRect().height() + screen->getBorderWidth());
676
677     spaces = calcSpace(tmp, spaces);
678   }
679
680   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
681     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
682       if(screen->getColPlacementDirection() == BScreen::TopBottom)
683         std::sort(spaces.begin(), spaces.end(), rowLRTB);
684       else
685         std::sort(spaces.begin(), spaces.end(), rowLRBT);
686     } else {
687       if(screen->getColPlacementDirection() == BScreen::TopBottom)
688         std::sort(spaces.begin(), spaces.end(), rowRLTB);
689       else
690         std::sort(spaces.begin(), spaces.end(), rowRLBT);
691     }
692   } else {
693     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
694       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
695         std::sort(spaces.begin(), spaces.end(), colLRTB);
696       else
697         std::sort(spaces.begin(), spaces.end(), colRLTB);
698     } else {
699       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
700         std::sort(spaces.begin(), spaces.end(), colLRBT);
701       else
702         std::sort(spaces.begin(), spaces.end(), colRLBT);
703     }
704   }
705
706   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
707   for(; sit != spaces_end; ++sit) {
708     if (sit->width() >= win.width() && sit->height() >= win.height())
709       break;
710   }
711
712   if (sit == spaces_end)
713     return False;
714
715   //set new position based on the empty space found
716   const Rect& where = *sit;
717   win.setX(where.x());
718   win.setY(where.y());
719
720   // adjust the location() based on left/right and top/bottom placement
721   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
722     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
723       win.setX(where.right() - win.width());
724     if (screen->getColPlacementDirection() == BScreen::BottomTop)
725       win.setY(where.bottom() - win.height());
726   } else {
727     if (screen->getColPlacementDirection() == BScreen::BottomTop)
728       win.setY(win.y() + where.height() - win.height());
729     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
730       win.setX(win.x() + where.width() - win.width());
731   }
732   return True;
733 }
734
735
736 bool Workspace::underMousePlacement(Rect &win) {
737   int x, y, rx, ry;
738   Window c, r;
739   unsigned int m;
740   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
741                 &r, &c, &rx, &ry, &x, &y, &m);
742
743   Rect area;
744 #ifdef    XINERAMA
745   if (screen->isXineramaActive() &&
746       screen->getBlackbox()->doXineramaPlacement()) {
747     RectList availableAreas = screen->allAvailableAreas();
748     RectList::iterator it, end = availableAreas.end();
749
750     for (it = availableAreas.begin(); it != end; ++it)
751       if (it->contains(rx, ry)) break;
752     assert(it != end);  // the mouse isn't inside an area?
753     area = *it;
754   } else
755 #endif // XINERAMA
756     area = screen->availableArea();
757   
758   x = rx - win.width() / 2;
759   y = ry - win.height() / 2;
760
761   if (x < area.x())
762     x = area.x();
763   if (y < area.y())
764     y = area.y();
765   if (x + win.width() > area.x() + area.width())
766     x = area.x() + area.width() - win.width();
767   if (y + win.height() > area.y() + area.height())
768     y = area.y() + area.height() - win.height();
769
770   win.setX(x);
771   win.setY(y);
772
773   return True;
774 }
775
776
777 bool Workspace::cascadePlacement(Rect &win, const int offset) {
778   Rect area;
779   
780 #ifdef    XINERAMA
781   if (screen->isXineramaActive() &&
782       screen->getBlackbox()->doXineramaPlacement()) {
783     area = screen->allAvailableAreas()[cascade_region];
784   } else
785 #endif // XINERAMA
786     area = screen->availableArea();
787
788   if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
789       (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
790     cascade_x = cascade_y = 0;
791 #ifdef    XINERAMA
792     if (screen->isXineramaActive() &&
793         screen->getBlackbox()->doXineramaPlacement()) {
794       // go to the next xinerama region, and use its area
795       if (++cascade_region >= screen->allAvailableAreas().size())
796         cascade_region = 0;
797       area = screen->allAvailableAreas()[cascade_region];
798     }
799 #endif // XINERAMA
800   }
801
802   if (cascade_x == 0) {
803     cascade_x = area.x() + offset;
804     cascade_y = area.y() + offset;
805   }
806
807   win.setPos(cascade_x, cascade_y);
808
809   cascade_x += offset;
810   cascade_y += offset;
811
812   return True;
813 }
814
815
816 void Workspace::placeWindow(BlackboxWindow *win) {
817   Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
818   bool placed = False;
819
820   switch (screen->getPlacementPolicy()) {
821   case BScreen::RowSmartPlacement:
822   case BScreen::ColSmartPlacement:
823     placed = smartPlacement(new_win);
824     break;
825   case BScreen::UnderMousePlacement:
826   case BScreen::ClickMousePlacement:
827     placed = underMousePlacement(new_win);
828   default:
829     break; // handled below
830   } // switch
831
832   if (placed == False)
833     cascadePlacement(new_win, (win->getTitleHeight() +
834                                screen->getBorderWidth() * 2));
835
836   if (new_win.right() > screen->availableArea().right())
837     new_win.setX(screen->availableArea().left());
838   if (new_win.bottom() > screen->availableArea().bottom())
839     new_win.setY(screen->availableArea().top());
840
841   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
842 }