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