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