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