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