]> Dogcows Code - chaz/openbox/blob - src/Workspace.cc
reverse the if
[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 "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 std::for_each(stackingList.begin(), stackingList.end(),
188 std::mem_fun(&BlackboxWindow::show));
189 }
190
191
192 void Workspace::hideAll(void) {
193 // withdraw in reverse order to minimize the number of Expose events
194
195 BlackboxWindowList lst(stackingList.rbegin(), stackingList.rend());
196
197 BlackboxWindowList::iterator it = lst.begin();
198 const BlackboxWindowList::iterator end = lst.end();
199 for (; it != end; ++it) {
200 BlackboxWindow *bw = *it;
201 if (! bw->isStuck())
202 bw->withdraw();
203 }
204 }
205
206
207 void Workspace::removeAll(void) {
208 while (! windowList.empty())
209 windowList.front()->iconify();
210 }
211
212
213 /*
214 * returns the number of transients for win, plus the number of transients
215 * associated with each transient of win
216 */
217 static int countTransients(const BlackboxWindow * const win) {
218 int ret = win->getTransients().size();
219 if (ret > 0) {
220 BlackboxWindowList::const_iterator it, end = win->getTransients().end();
221 for (it = win->getTransients().begin(); it != end; ++it) {
222 ret += countTransients(*it);
223 }
224 }
225 return ret;
226 }
227
228
229 /*
230 * puts the transients of win into the stack. windows are stacked above
231 * the window before it in the stackvector being iterated, meaning
232 * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
233 * stack[1], etc...
234 */
235 void Workspace::raiseTransients(const BlackboxWindow * const win,
236 StackVector::iterator &stack) {
237 if (win->getTransients().size() == 0) return; // nothing to do
238
239 // put win's transients in the stack
240 BlackboxWindowList::const_iterator it, end = win->getTransients().end();
241 for (it = win->getTransients().begin(); it != end; ++it) {
242 *stack++ = (*it)->getFrameWindow();
243 screen->updateNetizenWindowRaise((*it)->getClientWindow());
244
245 if (! (*it)->isIconic()) {
246 Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
247 wkspc->stackingList.remove((*it));
248 wkspc->stackingList.push_front((*it));
249 }
250 }
251
252 // put transients of win's transients in the stack
253 for (it = win->getTransients().begin(); it != end; ++it) {
254 raiseTransients(*it, stack);
255 }
256 }
257
258
259 void Workspace::lowerTransients(const BlackboxWindow * const win,
260 StackVector::iterator &stack) {
261 if (win->getTransients().size() == 0) return; // nothing to do
262
263 // put transients of win's transients in the stack
264 BlackboxWindowList::const_reverse_iterator it,
265 end = win->getTransients().rend();
266 for (it = win->getTransients().rbegin(); it != end; ++it) {
267 lowerTransients(*it, stack);
268 }
269
270 // put win's transients in the stack
271 for (it = win->getTransients().rbegin(); it != end; ++it) {
272 *stack++ = (*it)->getFrameWindow();
273 screen->updateNetizenWindowLower((*it)->getClientWindow());
274
275 if (! (*it)->isIconic()) {
276 Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
277 wkspc->stackingList.remove((*it));
278 wkspc->stackingList.push_back((*it));
279 }
280 }
281 }
282
283
284 void Workspace::raiseWindow(BlackboxWindow *w) {
285 BlackboxWindow *win = w;
286
287 if (win->isDesktop()) return;
288
289 // walk up the transient_for's to the window that is not a transient
290 while (win->isTransient() && ! win->isDesktop()) {
291 if (! win->getTransientFor()) break;
292 win = win->getTransientFor();
293 }
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 screen->updateNetizenWindowRaise(win->getClientWindow());
304 if (! (win->isIconic() || win->isDesktop())) {
305 Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
306 wkspc->stackingList.remove(win);
307 wkspc->stackingList.push_front(win);
308 }
309
310 raiseTransients(win, stack);
311
312 screen->raiseWindows(&stack_vector[0], stack_vector.size());
313 }
314
315
316 void Workspace::lowerWindow(BlackboxWindow *w) {
317 BlackboxWindow *win = w;
318
319 // walk up the transient_for's to the window that is not a transient
320 while (win->isTransient() && ! win->isDesktop()) {
321 if (! win->getTransientFor()) break;
322 win = win->getTransientFor();
323 }
324
325 // get the total window count (win and all transients)
326 unsigned int i = 1 + countTransients(win);
327
328 // stack the window with all transients above
329 StackVector stack_vector(i);
330 StackVector::iterator stack = stack_vector.begin();
331
332 lowerTransients(win, stack);
333
334 *(stack++) = win->getFrameWindow();
335 screen->updateNetizenWindowLower(win->getClientWindow());
336 if (! (win->isIconic() || win->isDesktop())) {
337 Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
338 wkspc->stackingList.remove(win);
339 wkspc->stackingList.push_back(win);
340 }
341
342 screen->lowerWindows(&stack_vector[0], stack_vector.size());
343 }
344
345
346 void Workspace::reconfigure(void) {
347 clientmenu->reconfigure();
348 std::for_each(windowList.begin(), windowList.end(),
349 std::mem_fun(&BlackboxWindow::reconfigure));
350 }
351
352
353 BlackboxWindow *Workspace::getWindow(unsigned int index) {
354 if (index < windowList.size()) {
355 BlackboxWindowList::iterator it = windowList.begin();
356 for(; index > 0; --index, ++it); /* increment to index */
357 return *it;
358 }
359 return 0;
360 }
361
362
363 BlackboxWindow*
364 Workspace::getNextWindowInList(BlackboxWindow *w) {
365 BlackboxWindowList::iterator it = std::find(windowList.begin(),
366 windowList.end(),
367 w);
368 assert(it != windowList.end()); // window must be in list
369 ++it; // next window
370 if (it == windowList.end())
371 return windowList.front(); // if we walked off the end, wrap around
372
373 return *it;
374 }
375
376
377 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
378 BlackboxWindowList::iterator it = std::find(windowList.begin(),
379 windowList.end(),
380 w);
381 assert(it != windowList.end()); // window must be in list
382 if (it == windowList.begin())
383 return windowList.back(); // if we walked of the front, wrap around
384
385 return *(--it);
386 }
387
388
389 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
390 return stackingList.front();
391 }
392
393
394 void Workspace::sendWindowList(Netizen &n) {
395 BlackboxWindowList::iterator it = windowList.begin(),
396 end = windowList.end();
397 for(; it != end; ++it)
398 n.sendWindowAdd((*it)->getClientWindow(), getID());
399 }
400
401
402 unsigned int Workspace::getCount(void) const {
403 return windowList.size();
404 }
405
406
407 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
408 BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
409 const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
410 for (; it != end; ++it)
411 if ((*it)->isNormal())
412 stack_order.push_back(*it);
413 }
414
415
416 bool Workspace::isCurrent(void) const {
417 return (id == screen->getCurrentWorkspaceID());
418 }
419
420
421 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
422 return (w == windowList.back());
423 }
424
425
426 void Workspace::setCurrent(void) {
427 screen->changeWorkspaceID(id);
428 }
429
430
431 void Workspace::readName(void) {
432 XAtom::StringVect namesList;
433 unsigned long numnames = id + 1;
434
435 // attempt to get from the _NET_WM_DESKTOP_NAMES property
436 if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
437 XAtom::utf8, numnames, namesList) &&
438 namesList.size() > id) {
439 name = namesList[id];
440
441 clientmenu->setLabel(name);
442 clientmenu->update();
443 } else {
444 /*
445 Use a default name. This doesn't actually change the class. That will
446 happen after the setName changes the root property, and that change
447 makes its way back to this function.
448 */
449 string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
450 "Workspace %d");
451 assert(tmp.length() < 32);
452 char default_name[32];
453 sprintf(default_name, tmp.c_str(), id + 1);
454
455 setName(default_name); // save this into the _NET_WM_DESKTOP_NAMES property
456 }
457 }
458
459
460 void Workspace::setName(const string& new_name) {
461 // set the _NET_WM_DESKTOP_NAMES property with the new name
462 XAtom::StringVect namesList;
463 unsigned long numnames = (unsigned) -1;
464 if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
465 XAtom::utf8, numnames, namesList) &&
466 namesList.size() > id)
467 namesList[id] = new_name;
468 else
469 namesList.push_back(new_name);
470
471 xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
472 XAtom::utf8, namesList);
473 }
474
475
476 /*
477 * Calculate free space available for window placement.
478 */
479 typedef std::vector<Rect> rectList;
480
481 static rectList calcSpace(const Rect &win, const rectList &spaces) {
482 Rect isect, extra;
483 rectList result;
484 rectList::const_iterator siter, end = spaces.end();
485 for (siter = spaces.begin(); siter != end; ++siter) {
486 const Rect &curr = *siter;
487
488 if(! win.intersects(curr)) {
489 result.push_back(curr);
490 continue;
491 }
492
493 /* Use an intersection of win and curr to determine the space around
494 * curr that we can use.
495 *
496 * NOTE: the spaces calculated can overlap.
497 */
498 isect = curr & win;
499
500 // left
501 extra.setCoords(curr.left(), curr.top(),
502 isect.left() - 1, curr.bottom());
503 if (extra.valid()) result.push_back(extra);
504
505 // top
506 extra.setCoords(curr.left(), curr.top(),
507 curr.right(), isect.top() - 1);
508 if (extra.valid()) result.push_back(extra);
509
510 // right
511 extra.setCoords(isect.right() + 1, curr.top(),
512 curr.right(), curr.bottom());
513 if (extra.valid()) result.push_back(extra);
514
515 // bottom
516 extra.setCoords(curr.left(), isect.bottom() + 1,
517 curr.right(), curr.bottom());
518 if (extra.valid()) result.push_back(extra);
519 }
520 return result;
521 }
522
523
524 static bool rowRLBT(const Rect &first, const Rect &second) {
525 if (first.bottom() == second.bottom())
526 return first.right() > second.right();
527 return first.bottom() > second.bottom();
528 }
529
530 static bool rowRLTB(const Rect &first, const Rect &second) {
531 if (first.y() == second.y())
532 return first.right() > second.right();
533 return first.y() < second.y();
534 }
535
536 static bool rowLRBT(const Rect &first, const Rect &second) {
537 if (first.bottom() == second.bottom())
538 return first.x() < second.x();
539 return first.bottom() > second.bottom();
540 }
541
542 static bool rowLRTB(const Rect &first, const Rect &second) {
543 if (first.y() == second.y())
544 return first.x() < second.x();
545 return first.y() < second.y();
546 }
547
548 static bool colLRTB(const Rect &first, const Rect &second) {
549 if (first.x() == second.x())
550 return first.y() < second.y();
551 return first.x() < second.x();
552 }
553
554 static bool colLRBT(const Rect &first, const Rect &second) {
555 if (first.x() == second.x())
556 return first.bottom() > second.bottom();
557 return first.x() < second.x();
558 }
559
560 static bool colRLTB(const Rect &first, const Rect &second) {
561 if (first.right() == second.right())
562 return first.y() < second.y();
563 return first.right() > second.right();
564 }
565
566 static bool colRLBT(const Rect &first, const Rect &second) {
567 if (first.right() == second.right())
568 return first.bottom() > second.bottom();
569 return first.right() > second.right();
570 }
571
572
573 bool Workspace::smartPlacement(Rect& win, const Rect& availableArea) {
574 rectList spaces;
575 spaces.push_back(availableArea); //initially the entire screen is free
576
577 //Find Free Spaces
578 BlackboxWindowList::const_iterator wit = windowList.begin(),
579 end = windowList.end();
580 Rect tmp;
581 for (; wit != end; ++wit) {
582 const BlackboxWindow* const curr = *wit;
583
584 if (curr->isShaded() && screen->getPlaceIgnoreShaded()) continue;
585 if (curr->isMaximizedFull() && screen->getPlaceIgnoreMaximized()) continue;
586
587 tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
588 curr->frameRect().width() + screen->getBorderWidth(),
589 curr->frameRect().height() + screen->getBorderWidth());
590
591 spaces = calcSpace(tmp, spaces);
592 }
593
594 if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
595 if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
596 if(screen->getColPlacementDirection() == BScreen::TopBottom)
597 std::sort(spaces.begin(), spaces.end(), rowLRTB);
598 else
599 std::sort(spaces.begin(), spaces.end(), rowLRBT);
600 } else {
601 if(screen->getColPlacementDirection() == BScreen::TopBottom)
602 std::sort(spaces.begin(), spaces.end(), rowRLTB);
603 else
604 std::sort(spaces.begin(), spaces.end(), rowRLBT);
605 }
606 } else {
607 if(screen->getColPlacementDirection() == BScreen::TopBottom) {
608 if(screen->getRowPlacementDirection() == BScreen::LeftRight)
609 std::sort(spaces.begin(), spaces.end(), colLRTB);
610 else
611 std::sort(spaces.begin(), spaces.end(), colRLTB);
612 } else {
613 if(screen->getRowPlacementDirection() == BScreen::LeftRight)
614 std::sort(spaces.begin(), spaces.end(), colLRBT);
615 else
616 std::sort(spaces.begin(), spaces.end(), colRLBT);
617 }
618 }
619
620 rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
621 for(; sit != spaces_end; ++sit) {
622 if (sit->width() >= win.width() && sit->height() >= win.height())
623 break;
624 }
625
626 if (sit == spaces_end)
627 return False;
628
629 //set new position based on the empty space found
630 const Rect& where = *sit;
631 win.setX(where.x());
632 win.setY(where.y());
633
634 // adjust the location() based on left/right and top/bottom placement
635 if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
636 if (screen->getRowPlacementDirection() == BScreen::RightLeft)
637 win.setX(where.right() - win.width());
638 if (screen->getColPlacementDirection() == BScreen::BottomTop)
639 win.setY(where.bottom() - win.height());
640 } else {
641 if (screen->getColPlacementDirection() == BScreen::BottomTop)
642 win.setY(win.y() + where.height() - win.height());
643 if (screen->getRowPlacementDirection() == BScreen::RightLeft)
644 win.setX(win.x() + where.width() - win.width());
645 }
646 return True;
647 }
648
649
650 bool Workspace::underMousePlacement(Rect &win, const Rect &availableArea) {
651 int x, y, rx, ry;
652 Window c, r;
653 unsigned int m;
654 XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
655 &r, &c, &rx, &ry, &x, &y, &m);
656 x = rx - win.width() / 2;
657 y = ry - win.height() / 2;
658
659 if (x < availableArea.x())
660 x = availableArea.x();
661 if (y < availableArea.y())
662 y = availableArea.y();
663 if (x + win.width() > availableArea.x() + availableArea.width())
664 x = availableArea.x() + availableArea.width() - win.width();
665 if (y + win.height() > availableArea.y() + availableArea.height())
666 y = availableArea.y() + availableArea.height() - win.height();
667
668 win.setX(x);
669 win.setY(y);
670
671 return True;
672 }
673
674
675 bool Workspace::cascadePlacement(Rect &win, const Rect &availableArea) {
676 if ((cascade_x > static_cast<signed>(availableArea.width() / 2)) ||
677 (cascade_y > static_cast<signed>(availableArea.height() / 2)))
678 cascade_x = cascade_y = 32;
679
680 if (cascade_x == 32) {
681 cascade_x += availableArea.x();
682 cascade_y += availableArea.y();
683 }
684
685 win.setPos(cascade_x, cascade_y);
686
687 return True;
688 }
689
690
691 void Workspace::placeWindow(BlackboxWindow *win) {
692 Rect availableArea(screen->availableArea()),
693 new_win(availableArea.x(), availableArea.y(),
694 win->frameRect().width(), win->frameRect().height());
695 bool placed = False;
696
697 switch (screen->getPlacementPolicy()) {
698 case BScreen::RowSmartPlacement:
699 case BScreen::ColSmartPlacement:
700 placed = smartPlacement(new_win, availableArea);
701 break;
702 case BScreen::UnderMousePlacement:
703 case BScreen::ClickMousePlacement:
704 placed = underMousePlacement(new_win, availableArea);
705 default:
706 break; // handled below
707 } // switch
708
709 if (placed == False) {
710 cascadePlacement(new_win, availableArea);
711 cascade_x += win->getTitleHeight() + (screen->getBorderWidth() * 2);
712 cascade_y += win->getTitleHeight() + (screen->getBorderWidth() * 2);
713 }
714
715 if (new_win.right() > availableArea.right())
716 new_win.setX(availableArea.left());
717 if (new_win.bottom() > availableArea.bottom())
718 new_win.setY(availableArea.top());
719 win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
720 }
This page took 0.075112 seconds and 5 git commands to generate.