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