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