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