]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
don't try to show windows which are already shown. this also ends up fixing an elusiv...
[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 "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 = 32;
66
67   id = i;
68
69   clientmenu = new Clientmenu(this);
70
71   lastfocus = (BlackboxWindow *) 0;
72
73   readName();
74 }
75
76
77 void Workspace::addWindow(BlackboxWindow *w, bool place) {
78   assert(w != 0);
79
80   if (place) placeWindow(w);
81
82   stackingList.push_front(w);
83     
84   if (w->isNormal()) {
85     w->setWorkspace(id);
86     w->setWindowNumber(windowList.size());
87
88     windowList.push_back(w);
89
90     clientmenu->insert(w->getTitle());
91     clientmenu->update();
92
93     screen->updateNetizenWindowAdd(w->getClientWindow(), id);
94   }
95
96   if (! w->isDesktop())
97     raiseWindow(w);
98   else
99     lowerWindow(w);
100 }
101
102
103 void Workspace::removeWindow(BlackboxWindow *w) {
104   assert(w != 0);
105
106   stackingList.remove(w);
107
108   // pass focus to the next appropriate window
109   if ((w->isFocused() || w == lastfocus) &&
110       ! screen->getBlackbox()->doShutdown()) {
111     focusFallback(w);
112
113     // if the window is sticky, then it needs to be removed on all other
114     // workspaces too!
115     if (w->isStuck()) {
116       for (unsigned int i = 0; i < screen->getWorkspaceCount(); ++i)
117         if (i != id)
118           screen->getWorkspace(i)->focusFallback(w);
119     }
120   }
121
122   if (! w->isNormal()) return;
123
124   windowList.remove(w);
125   clientmenu->remove(w->getWindowNumber());
126   clientmenu->update();
127
128   screen->updateNetizenWindowDel(w->getClientWindow());
129
130   BlackboxWindowList::iterator it = windowList.begin();
131   const BlackboxWindowList::iterator end = windowList.end();
132   unsigned int i = 0;
133   for (; it != end; ++it, ++i)
134     (*it)->setWindowNumber(i);
135
136   if (i == 0)
137     cascade_x = cascade_y = 32;
138 }
139
140
141 void Workspace::focusFallback(const BlackboxWindow *old_window) {
142   BlackboxWindow *newfocus = 0;
143
144   if (id == screen->getCurrentWorkspaceID()) {
145     // The window is on the visible workspace.
146
147     // if it's a transient, then try to focus its parent
148     if (old_window && old_window->isTransient()) {
149       newfocus = old_window->getTransientFor();
150
151       if (! newfocus ||
152           newfocus->isIconic() ||                  // do not focus icons
153           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
154           ! newfocus->setInputFocus())
155         newfocus = 0;
156     }
157
158     if (! newfocus) {
159       BlackboxWindowList::iterator it = stackingList.begin(),
160                                   end = stackingList.end();
161       for (; it != end; ++it) {
162         BlackboxWindow *tmp = *it;
163         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
164           // we found our new focus target
165           newfocus = tmp;
166           break;
167         }
168       }
169     }
170
171     screen->getBlackbox()->setFocusedWindow(newfocus);
172   } else {
173     // The window is not on the visible workspace.
174
175     if (old_window && lastfocus == old_window) {
176       // The window was the last-focus target, so we need to replace it.
177       BlackboxWindow *win = (BlackboxWindow*) 0;
178       if (! stackingList.empty())
179         win = stackingList.front();
180       setLastFocusedWindow(win);
181     }
182   }
183 }
184
185
186 void Workspace::showAll(void) {
187   BlackboxWindowList::iterator it = stackingList.begin();
188   const BlackboxWindowList::iterator end = stackingList.end();
189   for (; it != end; ++it) {
190     BlackboxWindow *bw = *it;
191     if (! bw->isStuck())
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   BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
200   const BlackboxWindowList::reverse_iterator end = stackingList.rend();
201   while (it != end) {
202     BlackboxWindow *bw = *it;
203     ++it; // withdraw removes the current item from the list so we need the next
204           // iterator before that happens
205     if (! bw->isStuck())
206       bw->withdraw();
207   }
208 }
209
210
211 void Workspace::removeAll(void) {
212   while (! windowList.empty())
213     windowList.front()->iconify();
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 int countTransients(const BlackboxWindow * const win) {
222   int ret = win->getTransients().size();
223   if (ret > 0) {
224     BlackboxWindowList::const_iterator it, end = win->getTransients().end();
225     for (it = win->getTransients().begin(); it != end; ++it) {
226       ret += countTransients(*it);
227     }
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().size() == 0) 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     *stack++ = (*it)->getFrameWindow();
247     screen->updateNetizenWindowRaise((*it)->getClientWindow());
248
249     if (! (*it)->isIconic()) {
250       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
251       wkspc->stackingList.remove((*it));
252       wkspc->stackingList.push_front((*it));
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
263 void Workspace::lowerTransients(const BlackboxWindow * const win,
264                                 StackVector::iterator &stack) {
265   if (win->getTransients().size() == 0) return; // nothing to do
266
267   // put transients of win's transients in the stack
268   BlackboxWindowList::const_reverse_iterator it,
269     end = win->getTransients().rend();
270   for (it = win->getTransients().rbegin(); it != end; ++it) {
271     lowerTransients(*it, stack);
272   }
273
274   // put win's transients in the stack
275   for (it = win->getTransients().rbegin(); it != end; ++it) {
276     *stack++ = (*it)->getFrameWindow();
277     screen->updateNetizenWindowLower((*it)->getClientWindow());
278
279     if (! (*it)->isIconic()) {
280       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
281       wkspc->stackingList.remove((*it));
282       wkspc->stackingList.push_back((*it));
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->isDesktop()) {
295     if (! win->getTransientFor()) break;
296     win = win->getTransientFor();
297   }
298
299   // get the total window count (win and all transients)
300   unsigned int i = 1 + countTransients(win);
301
302   // stack the window with all transients above
303   StackVector stack_vector(i);
304   StackVector::iterator stack = stack_vector.begin();
305
306   *(stack++) = win->getFrameWindow();
307   screen->updateNetizenWindowRaise(win->getClientWindow());
308   if (! (win->isIconic() || win->isDesktop())) {
309     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
310     wkspc->stackingList.remove(win);
311     wkspc->stackingList.push_front(win);
312   }
313
314   raiseTransients(win, stack);
315
316   screen->raiseWindows(&stack_vector[0], stack_vector.size());
317 }
318
319
320 void Workspace::lowerWindow(BlackboxWindow *w) {
321   BlackboxWindow *win = w;
322
323   // walk up the transient_for's to the window that is not a transient
324   while (win->isTransient() && ! win->isDesktop()) {
325     if (! win->getTransientFor()) break;
326     win = win->getTransientFor();
327   }
328
329   // get the total window count (win and all transients)
330   unsigned int i = 1 + countTransients(win);
331
332   // stack the window with all transients above
333   StackVector stack_vector(i);
334   StackVector::iterator stack = stack_vector.begin();
335
336   lowerTransients(win, stack);
337
338   *(stack++) = win->getFrameWindow();
339   screen->updateNetizenWindowLower(win->getClientWindow());
340   if (! (win->isIconic() || win->isDesktop())) {
341     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
342     wkspc->stackingList.remove(win);
343     wkspc->stackingList.push_back(win);
344   }
345
346   screen->lowerWindows(&stack_vector[0], stack_vector.size());
347 }
348
349
350 void Workspace::reconfigure(void) {
351   clientmenu->reconfigure();
352   std::for_each(windowList.begin(), windowList.end(),
353                 std::mem_fun(&BlackboxWindow::reconfigure));
354 }
355
356
357 BlackboxWindow *Workspace::getWindow(unsigned int index) {
358   if (index < windowList.size()) {
359     BlackboxWindowList::iterator it = windowList.begin();
360     for(; index > 0; --index, ++it); /* increment to index */
361     return *it;
362   }
363   return 0;
364 }
365
366
367 BlackboxWindow*
368 Workspace::getNextWindowInList(BlackboxWindow *w) {
369   BlackboxWindowList::iterator it = std::find(windowList.begin(),
370                                               windowList.end(),
371                                               w);
372   assert(it != windowList.end());   // window must be in list
373   ++it;                             // next window
374   if (it == windowList.end())
375     return windowList.front();      // if we walked off the end, wrap around
376
377   return *it;
378 }
379
380
381 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
382   BlackboxWindowList::iterator it = std::find(windowList.begin(),
383                                               windowList.end(),
384                                               w);
385   assert(it != windowList.end()); // window must be in list
386   if (it == windowList.begin())
387     return windowList.back();     // if we walked of the front, wrap around
388
389   return *(--it);
390 }
391
392
393 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
394   return stackingList.front();
395 }
396
397
398 void Workspace::sendWindowList(Netizen &n) {
399   BlackboxWindowList::iterator it = windowList.begin(),
400     end = windowList.end();
401   for(; it != end; ++it)
402     n.sendWindowAdd((*it)->getClientWindow(), getID());
403 }
404
405
406 unsigned int Workspace::getCount(void) const {
407   return windowList.size();
408 }
409
410
411 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
412   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
413   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
414   for (; it != end; ++it)
415     if ((*it)->isNormal())
416       stack_order.push_back(*it);
417 }
418   
419
420 bool Workspace::isCurrent(void) const {
421   return (id == screen->getCurrentWorkspaceID());
422 }
423
424
425 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
426   return (w == windowList.back());
427 }
428
429
430 void Workspace::setCurrent(void) {
431   screen->changeWorkspaceID(id);
432 }
433
434
435 void Workspace::readName(void) {
436   XAtom::StringVect namesList;
437   unsigned long numnames = id + 1;
438     
439   // attempt to get from the _NET_WM_DESKTOP_NAMES property
440   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
441                       XAtom::utf8, numnames, namesList) &&
442       namesList.size() > id) {
443     name = namesList[id];
444   
445     clientmenu->setLabel(name);
446     clientmenu->update();
447   } else {
448     /*
449        Use a default name. This doesn't actually change the class. That will
450        happen after the setName changes the root property, and that change
451        makes its way back to this function.
452     */
453     string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
454                      "Workspace %d");
455     assert(tmp.length() < 32);
456     char default_name[32];
457     sprintf(default_name, tmp.c_str(), id + 1);
458     
459     setName(default_name);  // save this into the _NET_WM_DESKTOP_NAMES property
460   }
461 }
462
463
464 void Workspace::setName(const string& new_name) {
465   // set the _NET_WM_DESKTOP_NAMES property with the new name
466   XAtom::StringVect namesList;
467   unsigned long numnames = (unsigned) -1;
468   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
469                       XAtom::utf8, numnames, namesList) &&
470       namesList.size() > id)
471     namesList[id] = new_name;
472   else
473     namesList.push_back(new_name);
474
475   xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
476                   XAtom::utf8, namesList);
477 }
478
479
480 /*
481  * Calculate free space available for window placement.
482  */
483 typedef std::vector<Rect> rectList;
484
485 static rectList calcSpace(const Rect &win, const rectList &spaces) {
486   Rect isect, extra;
487   rectList result;
488   rectList::const_iterator siter, end = spaces.end();
489   for (siter = spaces.begin(); siter != end; ++siter) {
490     const Rect &curr = *siter;
491
492     if(! win.intersects(curr)) {
493       result.push_back(curr);
494       continue;
495     }
496
497     /* Use an intersection of win and curr to determine the space around
498      * curr that we can use.
499      *
500      * NOTE: the spaces calculated can overlap.
501      */
502     isect = curr & win;
503
504     // left
505     extra.setCoords(curr.left(), curr.top(),
506                     isect.left() - 1, curr.bottom());
507     if (extra.valid()) result.push_back(extra);
508
509     // top
510     extra.setCoords(curr.left(), curr.top(),
511                     curr.right(), isect.top() - 1);
512     if (extra.valid()) result.push_back(extra);
513
514     // right
515     extra.setCoords(isect.right() + 1, curr.top(),
516                     curr.right(), curr.bottom());
517     if (extra.valid()) result.push_back(extra);
518
519     // bottom
520     extra.setCoords(curr.left(), isect.bottom() + 1,
521                     curr.right(), curr.bottom());
522     if (extra.valid()) result.push_back(extra);
523   }
524   return result;
525 }
526
527
528 static bool rowRLBT(const Rect &first, const Rect &second) {
529   if (first.bottom() == second.bottom())
530     return first.right() > second.right();
531   return first.bottom() > second.bottom();
532 }
533
534 static bool rowRLTB(const Rect &first, const Rect &second) {
535   if (first.y() == second.y())
536     return first.right() > second.right();
537   return first.y() < second.y();
538 }
539
540 static bool rowLRBT(const Rect &first, const Rect &second) {
541   if (first.bottom() == second.bottom())
542     return first.x() < second.x();
543   return first.bottom() > second.bottom();
544 }
545
546 static bool rowLRTB(const Rect &first, const Rect &second) {
547   if (first.y() == second.y())
548     return first.x() < second.x();
549   return first.y() < second.y();
550 }
551
552 static bool colLRTB(const Rect &first, const Rect &second) {
553   if (first.x() == second.x())
554     return first.y() < second.y();
555   return first.x() < second.x();
556 }
557
558 static bool colLRBT(const Rect &first, const Rect &second) {
559   if (first.x() == second.x())
560     return first.bottom() > second.bottom();
561   return first.x() < second.x();
562 }
563
564 static bool colRLTB(const Rect &first, const Rect &second) {
565   if (first.right() == second.right())
566     return first.y() < second.y();
567   return first.right() > second.right();
568 }
569
570 static bool colRLBT(const Rect &first, const Rect &second) {
571   if (first.right() == second.right())
572     return first.bottom() > second.bottom();
573   return first.right() > second.right();
574 }
575
576
577 bool Workspace::smartPlacement(Rect& win, const Rect& availableArea) {
578   rectList spaces;
579   spaces.push_back(availableArea); //initially the entire screen is free
580
581   //Find Free Spaces
582   BlackboxWindowList::const_iterator wit = windowList.begin(),
583     end = windowList.end();
584   Rect tmp;
585   for (; wit != end; ++wit) {
586     const BlackboxWindow* const curr = *wit;
587
588     if (curr->isShaded() && screen->getPlaceIgnoreShaded()) continue;
589     if (curr->isMaximizedFull() && screen->getPlaceIgnoreMaximized()) continue;
590
591     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
592                 curr->frameRect().width() + screen->getBorderWidth(),
593                 curr->frameRect().height() + screen->getBorderWidth());
594
595     spaces = calcSpace(tmp, spaces);
596   }
597
598   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
599     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
600       if(screen->getColPlacementDirection() == BScreen::TopBottom)
601         std::sort(spaces.begin(), spaces.end(), rowLRTB);
602       else
603         std::sort(spaces.begin(), spaces.end(), rowLRBT);
604     } else {
605       if(screen->getColPlacementDirection() == BScreen::TopBottom)
606         std::sort(spaces.begin(), spaces.end(), rowRLTB);
607       else
608         std::sort(spaces.begin(), spaces.end(), rowRLBT);
609     }
610   } else {
611     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
612       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
613         std::sort(spaces.begin(), spaces.end(), colLRTB);
614       else
615         std::sort(spaces.begin(), spaces.end(), colRLTB);
616     } else {
617       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
618         std::sort(spaces.begin(), spaces.end(), colLRBT);
619       else
620         std::sort(spaces.begin(), spaces.end(), colRLBT);
621     }
622   }
623
624   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
625   for(; sit != spaces_end; ++sit) {
626     if (sit->width() >= win.width() && sit->height() >= win.height())
627       break;
628   }
629
630   if (sit == spaces_end)
631     return False;
632
633   //set new position based on the empty space found
634   const Rect& where = *sit;
635   win.setX(where.x());
636   win.setY(where.y());
637
638   // adjust the location() based on left/right and top/bottom placement
639   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
640     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
641       win.setX(where.right() - win.width());
642     if (screen->getColPlacementDirection() == BScreen::BottomTop)
643       win.setY(where.bottom() - win.height());
644   } else {
645     if (screen->getColPlacementDirection() == BScreen::BottomTop)
646       win.setY(win.y() + where.height() - win.height());
647     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
648       win.setX(win.x() + where.width() - win.width());
649   }
650   return True;
651 }
652
653
654 bool Workspace::underMousePlacement(Rect &win, const Rect &availableArea) {
655   int x, y, rx, ry;
656   Window c, r;
657   unsigned int m;
658   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
659                 &r, &c, &rx, &ry, &x, &y, &m);
660   x = rx - win.width() / 2;
661   y = ry - win.height() / 2;
662
663   if (x < availableArea.x())
664     x = availableArea.x();
665   if (y < availableArea.y())
666     y = availableArea.y();
667   if (x + win.width() > availableArea.x() + availableArea.width())
668     x = availableArea.x() + availableArea.width() - win.width();
669   if (y + win.height() > availableArea.y() + availableArea.height())
670     y = availableArea.y() + availableArea.height() - win.height();
671
672   win.setX(x);
673   win.setY(y);
674
675   return True;
676 }
677
678
679 bool Workspace::cascadePlacement(Rect &win, const Rect &availableArea) {
680   if ((cascade_x > static_cast<signed>(availableArea.width() / 2)) ||
681       (cascade_y > static_cast<signed>(availableArea.height() / 2)))
682     cascade_x = cascade_y = 32;
683
684   if (cascade_x == 32) {
685     cascade_x += availableArea.x();
686     cascade_y += availableArea.y();
687   }
688
689   win.setPos(cascade_x, cascade_y);
690
691   return True;
692 }
693
694
695 void Workspace::placeWindow(BlackboxWindow *win) {
696   Rect availableArea(screen->availableArea()),
697     new_win(availableArea.x(), availableArea.y(),
698             win->frameRect().width(), win->frameRect().height());
699   bool placed = False;
700
701   switch (screen->getPlacementPolicy()) {
702   case BScreen::RowSmartPlacement:
703   case BScreen::ColSmartPlacement:
704     placed = smartPlacement(new_win, availableArea);
705     break;
706   case BScreen::UnderMousePlacement:
707   case BScreen::ClickMousePlacement:
708     placed = underMousePlacement(new_win, availableArea);
709   default:
710     break; // handled below
711   } // switch
712
713   if (placed == False) {
714     cascadePlacement(new_win, availableArea);
715     cascade_x += win->getTitleHeight() + (screen->getBorderWidth() * 2);
716     cascade_y += win->getTitleHeight() + (screen->getBorderWidth() * 2);
717   }
718
719   if (new_win.right() > availableArea.right())
720     new_win.setX(availableArea.left());
721   if (new_win.bottom() > availableArea.bottom())
722     new_win.setY(availableArea.top());
723   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
724 }