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