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