rename, remove bullshit. ya
[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 "blackbox.hh"
49 #include "clientmenu.hh"
50 #include "font.hh"
51 #include "netizen.hh"
52 #include "screen.hh"
53 #include "toolbar.hh"
54 #include "util.hh"
55 #include "window.hh"
56 #include "workspace.hh"
57 #include "windowmenu.hh"
58 #include "xatom.hh"
59
60
61 Workspace::Workspace(BScreen *scrn, unsigned int i) {
62   screen = scrn;
63   xatom = screen->getBlackbox()->getXAtom();
64
65   cascade_x = cascade_y = 0;
66 #ifdef    XINERAMA
67   cascade_region = 0;
68 #endif // XINERAMA
69
70   id = i;
71
72   clientmenu = new Clientmenu(this);
73
74   lastfocus = (BlackboxWindow *) 0;
75
76   readName();
77 }
78
79
80 void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
81   assert(w != 0);
82
83   if (place) placeWindow(w);
84
85   stackingList.push_front(w);
86
87   if (! sticky)
88     w->setWorkspace(id);
89   
90   if (! w->isNormal()) {
91     if (! sticky) {
92       // just give it some number, else bad things happen as it is assumed to
93       // not be on a workspace
94       w->setWindowNumber(0);
95     }
96   } else {
97     if (! sticky)
98       w->setWindowNumber(windowList.size());
99
100     windowList.push_back(w);
101
102     clientmenu->insert(w->getTitle());
103     clientmenu->update();
104
105     if (! sticky)
106       screen->updateNetizenWindowAdd(w->getClientWindow(), id);
107
108     if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
109                                  w->getTransientFor()->isFocused())) {
110       if (id != screen->getCurrentWorkspaceID()) {
111         /*
112            not on the focused workspace, so the window is not going to get focus
113            but if the user wants new windows focused, then it should get focus
114            when this workspace does become focused.
115         */
116         lastfocus = w;
117       }
118     }
119   }
120
121   if (! w->isDesktop())
122     raiseWindow(w);
123   else
124     lowerWindow(w);
125 }
126
127
128 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
129   assert(w != 0);
130
131   stackingList.remove(w);
132
133   // pass focus to the next appropriate window
134   if ((w->isFocused() || w == lastfocus) &&
135       ! screen->getBlackbox()->doShutdown()) {
136     focusFallback(w);
137   }
138     
139   if (! w->isNormal()) return;
140
141   BlackboxWindowList::iterator it, end = windowList.end();
142   int i;
143   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
144     if (*it == w)
145       break;
146   assert(it != end);
147   
148   windowList.erase(it);
149   clientmenu->remove(i);
150   clientmenu->update();
151
152   if (! sticky) {
153     screen->updateNetizenWindowDel(w->getClientWindow());
154
155     BlackboxWindowList::iterator it = windowList.begin();
156     const BlackboxWindowList::iterator end = windowList.end();
157     unsigned int i = 0;
158     for (; it != end; ++it, ++i)
159       (*it)->setWindowNumber(i);
160   }
161
162   if (i == 0) {
163     cascade_x = cascade_y = 0;
164 #ifdef    XINERAMA
165     cascade_region = 0;
166 #endif // XINERAMA
167   }
168 }
169
170
171 void Workspace::focusFallback(const BlackboxWindow *old_window) {
172   BlackboxWindow *newfocus = 0;
173
174   if (id == screen->getCurrentWorkspaceID()) {
175     // The window is on the visible workspace.
176
177     // if it's a transient, then try to focus its parent
178     if (old_window && old_window->isTransient()) {
179       newfocus = old_window->getTransientFor();
180
181       if (! newfocus ||
182           newfocus->isIconic() ||                  // do not focus icons
183           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
184           ! newfocus->setInputFocus())
185         newfocus = 0;
186     }
187
188     if (! newfocus) {
189       BlackboxWindowList::iterator it = stackingList.begin(),
190                                   end = stackingList.end();
191       for (; it != end; ++it) {
192         BlackboxWindow *tmp = *it;
193         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
194           // we found our new focus target
195           newfocus = tmp;
196           break;
197         }
198       }
199     }
200
201     screen->getBlackbox()->setFocusedWindow(newfocus);
202   } else {
203     // The window is not on the visible workspace.
204
205     if (old_window && lastfocus == old_window) {
206       // The window was the last-focus target, so we need to replace it.
207       BlackboxWindow *win = (BlackboxWindow*) 0;
208       if (! stackingList.empty())
209         win = stackingList.front();
210       setLastFocusedWindow(win);
211     }
212   }
213 }
214
215
216 void Workspace::setFocused(const BlackboxWindow *w, bool focused) {
217   BlackboxWindowList::iterator it, end = windowList.end();
218   int i;
219   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
220     if (*it == w)
221       break;
222   // if its == end, then a window thats not in the windowList
223   // got focused, such as a !isNormal() window.
224   if (it != end)
225     clientmenu->setItemSelected(i, focused);
226 }
227
228
229 void Workspace::removeAll(void) {
230   while (! windowList.empty())
231     windowList.front()->iconify();
232 }
233
234 void Workspace::showAll(void) {
235   BlackboxWindowList::iterator it = stackingList.begin();
236   const BlackboxWindowList::iterator end = stackingList.end();
237   for (; it != end; ++it) {
238     BlackboxWindow *bw = *it;
239     // sticky windows arent unmapped on a workspace change so we don't have ot
240     // map them, but sometimes on a restart, another app can unmap our sticky
241     // windows, so we map on startup always
242     if (! bw->isStuck() || screen->getBlackbox()->isStartup())
243       bw->show();
244   }
245 }
246
247
248 void Workspace::hideAll(void) {
249   // withdraw in reverse order to minimize the number of Expose events
250
251   BlackboxWindowList lst(stackingList.rbegin(), stackingList.rend());
252
253   BlackboxWindowList::iterator it = lst.begin();
254   const BlackboxWindowList::iterator end = lst.end();
255   for (; it != end; ++it) {
256     BlackboxWindow *bw = *it;
257     // don't hide sticky windows, or they'll end up flickering on a workspace
258     // change
259     if (! bw->isStuck())
260       bw->withdraw();
261   }
262 }
263
264
265
266 /*
267  * returns the number of transients for win, plus the number of transients
268  * associated with each transient of win
269  */
270 static unsigned int countTransients(const BlackboxWindow * const win) {
271   BlackboxWindowList transients = win->getTransients();
272   if (transients.empty()) return 0;
273
274   unsigned int ret = transients.size();
275   BlackboxWindowList::const_iterator it = transients.begin(),
276     end = transients.end();
277   for (; it != end; ++it)
278     ret += countTransients(*it);
279
280   return ret;
281 }
282
283
284 /*
285  * puts the transients of win into the stack. windows are stacked above
286  * the window before it in the stackvector being iterated, meaning
287  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
288  * stack[1], etc...
289  */
290 void Workspace::raiseTransients(const BlackboxWindow * const win,
291                                 StackVector::iterator &stack) {
292   if (win->getTransients().empty()) return; // nothing to do
293
294   // put win's transients in the stack
295   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
296   for (it = win->getTransients().begin(); it != end; ++it) {
297     BlackboxWindow *w = *it;
298     *stack++ = w->getFrameWindow();
299     screen->updateNetizenWindowRaise(w->getClientWindow());
300
301     if (! w->isIconic()) {
302       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
303       wkspc->stackingList.remove(w);
304       wkspc->stackingList.push_front(w);
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 void Workspace::lowerTransients(const BlackboxWindow * const win,
315                                 StackVector::iterator &stack) {
316   if (win->getTransients().empty()) return; // nothing to do
317
318   // put transients of win's transients in the stack
319   BlackboxWindowList::const_reverse_iterator it,
320     end = win->getTransients().rend();
321   for (it = win->getTransients().rbegin(); it != end; ++it)
322     lowerTransients(*it, stack);
323
324   // put win's transients in the stack
325   for (it = win->getTransients().rbegin(); it != end; ++it) {
326     BlackboxWindow *w = *it;
327     *stack++ = w->getFrameWindow();
328     screen->updateNetizenWindowLower(w->getClientWindow());
329
330     if (! w->isIconic()) {
331       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
332       wkspc->stackingList.remove(w);
333       wkspc->stackingList.push_back(w);
334     }
335   }
336 }
337
338
339 void Workspace::raiseWindow(BlackboxWindow *w) {
340   BlackboxWindow *win = w;
341
342   if (win->isDesktop()) return;
343
344   // walk up the transient_for's to the window that is not a transient
345   while (win->isTransient() && win->getTransientFor())
346     win = win->getTransientFor();
347
348   // get the total window count (win and all transients)
349   unsigned int i = 1 + countTransients(win);
350
351   // stack the window with all transients above
352   StackVector stack_vector(i);
353   StackVector::iterator stack = stack_vector.begin();
354
355   *(stack++) = win->getFrameWindow();
356   screen->updateNetizenWindowRaise(win->getClientWindow());
357   if (! (win->isIconic() || win->isDesktop())) {
358     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
359     wkspc->stackingList.remove(win);
360     wkspc->stackingList.push_front(win);
361   }
362
363   raiseTransients(win, stack);
364
365   screen->raiseWindows(&stack_vector[0], stack_vector.size());
366 }
367
368
369 void Workspace::lowerWindow(BlackboxWindow *w) {
370   BlackboxWindow *win = w;
371
372   // walk up the transient_for's to the window that is not a transient
373   while (win->isTransient() && win->getTransientFor())
374     win = win->getTransientFor();
375
376   // get the total window count (win and all transients)
377   unsigned int i = 1 + countTransients(win);
378
379   // stack the window with all transients above
380   StackVector stack_vector(i);
381   StackVector::iterator stack = stack_vector.begin();
382
383   lowerTransients(win, stack);
384
385   *(stack++) = win->getFrameWindow();
386   screen->updateNetizenWindowLower(win->getClientWindow());
387   if (! (win->isIconic() || win->isDesktop())) {
388     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
389     wkspc->stackingList.remove(win);
390     wkspc->stackingList.push_back(win);
391   }
392
393   screen->lowerWindows(&stack_vector[0], stack_vector.size());
394 }
395
396
397 void Workspace::reconfigure(void) {
398   clientmenu->reconfigure();
399   std::for_each(windowList.begin(), windowList.end(),
400                 std::mem_fun(&BlackboxWindow::reconfigure));
401 }
402
403
404 BlackboxWindow *Workspace::getWindow(unsigned int index) {
405   if (index < windowList.size()) {
406     BlackboxWindowList::iterator it = windowList.begin();
407     while (index-- > 0) // increment to index
408       ++it;
409     return *it;
410   }
411
412   return 0;
413 }
414
415
416 BlackboxWindow*
417 Workspace::getNextWindowInList(BlackboxWindow *w) {
418   BlackboxWindowList::iterator it = std::find(windowList.begin(),
419                                               windowList.end(),
420                                               w);
421   assert(it != windowList.end());   // window must be in list
422   ++it;                             // next window
423   if (it == windowList.end())
424     return windowList.front();      // if we walked off the end, wrap around
425
426   return *it;
427 }
428
429
430 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
431   BlackboxWindowList::iterator it = std::find(windowList.begin(),
432                                               windowList.end(),
433                                               w);
434   assert(it != windowList.end()); // window must be in list
435   if (it == windowList.begin())
436     return windowList.back();     // if we walked of the front, wrap around
437
438   return *(--it);
439 }
440
441
442 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
443   assert(! stackingList.empty());
444   return stackingList.front();
445 }
446
447
448 void Workspace::sendWindowList(Netizen &n) {
449   BlackboxWindowList::iterator it = windowList.begin(),
450     end = windowList.end();
451   for(; it != end; ++it)
452     n.sendWindowAdd((*it)->getClientWindow(), getID());
453 }
454
455
456 unsigned int Workspace::getCount(void) const {
457   return windowList.size();
458 }
459
460
461 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
462   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
463   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
464   for (; it != end; ++it)
465     // don't add desktop wnidows, or sticky windows more than once
466     if (! ( (*it)->isDesktop() ||
467             ((*it)->isStuck() && id != screen->getCurrentWorkspaceID())))
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 Workspace::rectList Workspace::calcSpace(const Rect &win,
536                                          const rectList &spaces) const {
537   Rect isect, extra;
538   rectList result;
539   rectList::const_iterator siter, end = spaces.end();
540   for (siter = spaces.begin(); siter != end; ++siter) {
541     const Rect &curr = *siter;
542
543     if(! win.intersects(curr)) {
544       result.push_back(curr);
545       continue;
546     }
547
548     /* Use an intersection of win and curr to determine the space around
549      * curr that we can use.
550      *
551      * NOTE: the spaces calculated can overlap.
552      */
553     isect = curr & win;
554
555     // left
556     extra.setCoords(curr.left(), curr.top(),
557                     isect.left() - screen->getSnapOffset(), curr.bottom());
558     if (extra.valid()) result.push_back(extra);
559
560     // top
561     extra.setCoords(curr.left(), curr.top(),
562                     curr.right(), isect.top() - screen->getSnapOffset());
563     if (extra.valid()) result.push_back(extra);
564
565     // right
566     extra.setCoords(isect.right() + screen->getSnapOffset(), curr.top(),
567                     curr.right(), curr.bottom());
568     if (extra.valid()) result.push_back(extra);
569
570     // bottom
571     extra.setCoords(curr.left(), isect.bottom() + screen->getSnapOffset(),
572                     curr.right(), curr.bottom());
573     if (extra.valid()) result.push_back(extra);
574   }
575   return result;
576 }
577
578
579 static bool rowRLBT(const Rect &first, const Rect &second) {
580   if (first.bottom() == second.bottom())
581     return first.right() > second.right();
582   return first.bottom() > second.bottom();
583 }
584
585 static bool rowRLTB(const Rect &first, const Rect &second) {
586   if (first.y() == second.y())
587     return first.right() > second.right();
588   return first.y() < second.y();
589 }
590
591 static bool rowLRBT(const Rect &first, const Rect &second) {
592   if (first.bottom() == second.bottom())
593     return first.x() < second.x();
594   return first.bottom() > second.bottom();
595 }
596
597 static bool rowLRTB(const Rect &first, const Rect &second) {
598   if (first.y() == second.y())
599     return first.x() < second.x();
600   return first.y() < second.y();
601 }
602
603 static bool colLRTB(const Rect &first, const Rect &second) {
604   if (first.x() == second.x())
605     return first.y() < second.y();
606   return first.x() < second.x();
607 }
608
609 static bool colLRBT(const Rect &first, const Rect &second) {
610   if (first.x() == second.x())
611     return first.bottom() > second.bottom();
612   return first.x() < second.x();
613 }
614
615 static bool colRLTB(const Rect &first, const Rect &second) {
616   if (first.right() == second.right())
617     return first.y() < second.y();
618   return first.right() > second.right();
619 }
620
621 static bool colRLBT(const Rect &first, const Rect &second) {
622   if (first.right() == second.right())
623     return first.bottom() > second.bottom();
624   return first.right() > second.right();
625 }
626
627
628 bool Workspace::smartPlacement(Rect& win) {
629   rectList spaces;
630  
631   //initially the entire screen is free
632 #ifdef    XINERAMA
633   if (screen->isXineramaActive() &&
634       screen->getBlackbox()->doXineramaPlacement()) {
635     RectList availableAreas = screen->allAvailableAreas();
636     RectList::iterator it, end = availableAreas.end();
637
638     for (it = availableAreas.begin(); it != end; ++it) {
639       Rect r = *it;
640       r.setRect(r.x() + screen->getSnapOffset(),
641                 r.y() + screen->getSnapOffset(),
642                 r.width() - screen->getSnapOffset(),
643                 r.height() - screen->getSnapOffset());
644       spaces.push_back(*it);
645     }
646   } else
647 #endif // XINERAMA
648   {
649     Rect r = screen->availableArea();
650     r.setRect(r.x() + screen->getSnapOffset(),
651               r.y() + screen->getSnapOffset(),
652               r.width() - screen->getSnapOffset(),
653               r.height() - screen->getSnapOffset());
654     spaces.push_back(r);
655   }
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 }