1 // Workspace.cc for Openbox
2 // Copyright (c) 2002 - 2002 Ben Jansens (ben@orodu.net)
3 // Copyright (c) 2001 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 // stupid macros needed to access some functions in version 2 of the GNU C
31 # include "../config.h"
32 #endif // HAVE_CONFIG_H
35 #include <X11/Xatom.h>
39 #include "Clientmenu.h"
43 #include "Workspace.h"
44 #include "Windowmenu.h"
49 #endif // HAVE_STDIO_H
53 #endif // HAVE_STDLIB_H
57 #endif // STDC_HEADERS
60 typedef vector
<Rect
> rectList
;
62 Workspace::Workspace(BScreen
&scrn
, int i
) : screen(scrn
) {
64 cascade_x
= cascade_y
= 32;
68 stackingList
= new LinkedList
<OpenboxWindow
>;
69 windowList
= new LinkedList
<OpenboxWindow
>;
70 clientmenu
= new Clientmenu(*this);
72 lastfocus
= (OpenboxWindow
*) 0;
75 char *tmp
= screen
.getNameOfWorkspace(id
);
80 Workspace::~Workspace(void) {
90 const int Workspace::addWindow(OpenboxWindow
*w
, Bool place
) {
93 if (place
) placeWindow(w
);
96 w
->setWindowNumber(windowList
->count());
98 stackingList
->insert(w
, 0);
99 windowList
->insert(w
);
101 clientmenu
->insert((const char **) w
->getTitle());
102 clientmenu
->update();
104 screen
.updateNetizenWindowAdd(w
->getClientWindow(), id
);
108 return w
->getWindowNumber();
112 const int Workspace::removeWindow(OpenboxWindow
*w
) {
115 stackingList
->remove(w
);
117 if (w
->isFocused()) {
118 if (w
->isTransient() && w
->getTransientFor() &&
119 w
->getTransientFor()->isVisible()) {
120 w
->getTransientFor()->setInputFocus();
121 } else if (screen
.isSloppyFocus()) {
122 screen
.getOpenbox().setFocusedWindow((OpenboxWindow
*) 0);
124 OpenboxWindow
*top
= stackingList
->first();
125 if (! top
|| ! top
->setInputFocus()) {
126 screen
.getOpenbox().setFocusedWindow((OpenboxWindow
*) 0);
127 XSetInputFocus(screen
.getOpenbox().getXDisplay(),
128 screen
.getToolbar()->getWindowID(),
129 RevertToParent
, CurrentTime
);
135 lastfocus
= (OpenboxWindow
*) 0;
137 windowList
->remove(w
->getWindowNumber());
138 clientmenu
->remove(w
->getWindowNumber());
139 clientmenu
->update();
141 screen
.updateNetizenWindowDel(w
->getClientWindow());
143 LinkedListIterator
<OpenboxWindow
> it(windowList
);
144 OpenboxWindow
*bw
= it
.current();
145 for (int i
= 0; bw
; it
++, i
++, bw
= it
.current())
146 bw
->setWindowNumber(i
);
148 return windowList
->count();
152 void Workspace::showAll(void) {
153 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
154 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
155 bw
->deiconify(False
, False
);
159 void Workspace::hideAll(void) {
160 LinkedList
<OpenboxWindow
> lst
;
162 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
163 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
166 LinkedListIterator
<OpenboxWindow
> it2(&lst
);
167 for (OpenboxWindow
*bw
= it2
.current(); bw
; it2
++, bw
= it2
.current())
173 void Workspace::removeAll(void) {
174 LinkedListIterator
<OpenboxWindow
> it(windowList
);
175 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
180 void Workspace::raiseWindow(OpenboxWindow
*w
) {
181 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
183 while (bottom
->isTransient() && bottom
->getTransientFor())
184 bottom
= bottom
->getTransientFor();
188 while (win
->hasTransient() && win
->getTransient()) {
189 win
= win
->getTransient();
194 Window
*nstack
= new Window
[i
], *curr
= nstack
;
199 *(curr
++) = win
->getFrameWindow();
200 screen
.updateNetizenWindowRaise(win
->getClientWindow());
202 if (! win
->isIconic()) {
203 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
204 wkspc
->stackingList
->remove(win
);
205 wkspc
->stackingList
->insert(win
, 0);
208 if (! win
->hasTransient() || ! win
->getTransient())
211 win
= win
->getTransient();
214 screen
.raiseWindows(nstack
, i
);
220 void Workspace::lowerWindow(OpenboxWindow
*w
) {
221 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
223 while (bottom
->isTransient() && bottom
->getTransientFor())
224 bottom
= bottom
->getTransientFor();
228 while (win
->hasTransient() && win
->getTransient()) {
229 win
= win
->getTransient();
234 Window
*nstack
= new Window
[i
], *curr
= nstack
;
238 *(curr
++) = win
->getFrameWindow();
239 screen
.updateNetizenWindowLower(win
->getClientWindow());
241 if (! win
->isIconic()) {
242 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
243 wkspc
->stackingList
->remove(win
);
244 wkspc
->stackingList
->insert(win
);
247 if (! win
->getTransientFor())
250 win
= win
->getTransientFor();
253 screen
.getOpenbox().grab();
255 XLowerWindow(screen
.getBaseDisplay().getXDisplay(), *nstack
);
256 XRestackWindows(screen
.getBaseDisplay().getXDisplay(), nstack
, i
);
258 screen
.getOpenbox().ungrab();
264 void Workspace::reconfigure(void) {
265 clientmenu
->reconfigure();
267 LinkedListIterator
<OpenboxWindow
> it(windowList
);
268 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current()) {
269 if (bw
->validateClient())
275 OpenboxWindow
*Workspace::getWindow(int index
) {
276 if ((index
>= 0) && (index
< windowList
->count()))
277 return windowList
->find(index
);
283 const int Workspace::getCount(void) {
284 return windowList
->count();
288 void Workspace::update(void) {
289 clientmenu
->update();
290 screen
.getToolbar()->redrawWindowLabel(True
);
294 Bool
Workspace::isCurrent(void) {
295 return (id
== screen
.getCurrentWorkspaceID());
299 Bool
Workspace::isLastWindow(OpenboxWindow
*w
) {
300 return (w
== windowList
->last());
303 void Workspace::setCurrent(void) {
304 screen
.changeWorkspaceID(id
);
308 void Workspace::setName(char *new_name
) {
313 name
= bstrdup(new_name
);
315 name
= new char[128];
316 sprintf(name
, i18n
->getMessage(WorkspaceSet
, WorkspaceDefaultNameFormat
,
317 "Workspace %d"), id
+ 1);
320 clientmenu
->setLabel(name
);
321 clientmenu
->update();
325 void Workspace::shutdown(void) {
326 while (windowList
->count()) {
327 windowList
->first()->restore();
328 delete windowList
->first();
332 static rectList
calcSpace(const OpenboxWindow
&win
, const rectList
&spaces
) {
334 rectList::const_iterator siter
;
335 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
336 if(win
.getArea().Intersect(*siter
)) {
337 //Check for space to the left of the window
338 if(win
.getXFrame() > siter
->x())
339 result
.push_back(Rect(siter
->x(), siter
->y(),
340 win
.getXFrame() - siter
->x() - 1,
342 //Check for space above the window
343 if(win
.getYFrame() > siter
->y())
344 result
.push_back(Rect(siter
->x(), siter
->y(),
346 win
.getYFrame() - siter
->y() - 1));
347 //Check for space to the right of the window
348 if((win
.getXFrame()+win
.getWidth()) <
349 (siter
->x()+siter
->w()))
350 result
.push_back(Rect(win
.getXFrame() + win
.getWidth() + 1,
352 siter
->x() + siter
->w() -
353 win
.getXFrame() - win
.getWidth() - 1,
355 //Check for space below the window
356 if((win
.getYFrame()+win
.getHeight()) <
357 (siter
->y()+siter
->h()))
358 result
.push_back(Rect(siter
->x(),
359 win
.getYFrame() + win
.getHeight() + 1,
361 siter
->y() + siter
->h()-
362 win
.getYFrame() - win
.getHeight() - 1));
366 result
.push_back(*siter
);
371 //BestFitPlacement finds the smallest free space that fits the window
372 //to be placed. It currentl ignores whether placement is right to left or top
374 Point
*Workspace::bestFitPlacement(const Size
&win_size
, const Rect
&space
)
378 LinkedListIterator
<OpenboxWindow
> it(windowList
);
379 rectList::const_iterator siter
;
380 spaces
.push_back(space
); //initially the entire screen is free
384 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
385 spaces
= calcSpace(*cur
, spaces
);
387 //Find first space that fits the window
389 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
390 if ((siter
->w() >= win_size
.w()) &&
391 (siter
->h() >= win_size
.h()))
396 return new Point(best
->origin());
398 return new Point(200, 0);
401 inline Point
*Workspace::rowSmartPlacement(const Size
&win_size
,
404 int test_x
, test_y
, place_x
= 0, place_y
= 0;
407 ((screen
.getColPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
409 ((screen
.getRowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
410 int delta_x
= 8, delta_y
= 8;
411 LinkedListIterator
<OpenboxWindow
> it(windowList
);
413 test_y
= (screen
.getColPlacementDirection() == BScreen::TopBottom
) ?
414 start_pos
: screen
.getHeight() - win_size
.h() - start_pos
;
417 ((screen
.getColPlacementDirection() == BScreen::BottomTop
) ?
418 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())) {
419 test_x
= (screen
.getRowPlacementDirection() == BScreen::LeftRight
) ?
420 start_pos
: space
.w() - win_size
.w() - start_pos
;
422 ((screen
.getRowPlacementDirection() == BScreen::RightLeft
) ?
423 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
427 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
428 it
++, curr
= it
.current()) {
429 int curr_w
= curr
->getWidth() + (screen
.getBorderWidth() * 4);
431 ((curr
->isShaded()) ? curr
->getTitleHeight() : curr
->getHeight()) +
432 (screen
.getBorderWidth() * 4);
434 if (curr
->getXFrame() < test_x
+ win_size
.w() &&
435 curr
->getXFrame() + curr_w
> test_x
&&
436 curr
->getYFrame() < test_y
+ win_size
.h() &&
437 curr
->getYFrame() + curr_h
> test_y
) {
442 // Removed code for checking toolbar and slit
443 // The space passed in should not include either
452 test_x
+= (change_x
* delta_x
);
455 test_y
+= (change_y
* delta_y
);
457 return new Point(place_x
, place_y
);
460 void Workspace::placeWindow(OpenboxWindow
*win
) {
465 const int win_w
= win
->getWidth() + (screen
.getBorderWidth() * 4),
466 win_h
= win
->getHeight() + (screen
.getBorderWidth() * 4),
468 slit_x
= screen
.getSlit()->getX() - screen
.getBorderWidth(),
469 slit_y
= screen
.getSlit()->getY() - screen
.getBorderWidth(),
470 slit_w
= screen
.getSlit()->getWidth() +
471 (screen
.getBorderWidth() * 4),
472 slit_h
= screen
.getSlit()->getHeight() +
473 (screen
.getBorderWidth() * 4),
475 toolbar_x
= screen
.getToolbar()->getX() - screen
.getBorderWidth(),
476 toolbar_y
= screen
.getToolbar()->getY() - screen
.getBorderWidth(),
477 toolbar_w
= screen
.getToolbar()->getWidth() +
478 (screen
.getBorderWidth() * 4),
479 toolbar_h
= screen
.getToolbar()->getHeight() +
480 (screen
.getBorderWidth() * 4),
483 ((screen
.getColPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
485 ((screen
.getRowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
486 delta_x
= 8, delta_y
= 8;
488 int test_x
, test_y
, place_x
= 0, place_y
= 0;
489 LinkedListIterator
<OpenboxWindow
> it(windowList
);
495 Size
window_size(win_w
, win_h
);
497 switch (screen
.getPlacementPolicy()) {
498 case BScreen::BestFitPlacement
: {
499 Point
*spot
= bestFitPlacement(window_size
, space
);
508 case BScreen::RowSmartPlacement
: {
509 Point
*spot
=rowSmartPlacement(window_size
, space
);
519 case BScreen::ColSmartPlacement
: {
520 test_x
= (screen
.getRowPlacementDirection() == BScreen::LeftRight
) ?
521 start_pos
: screen
.getWidth() - win_w
- start_pos
;
524 ((screen
.getRowPlacementDirection() == BScreen::RightLeft
) ?
525 test_x
> 0 : test_x
+ win_w
< (signed) screen
.getWidth())) {
526 test_y
= (screen
.getColPlacementDirection() == BScreen::TopBottom
) ?
527 start_pos
: screen
.getHeight() - win_h
- start_pos
;
530 ((screen
.getColPlacementDirection() == BScreen::BottomTop
) ?
531 test_y
> 0 : test_y
+ win_h
< (signed) screen
.getHeight())) {
535 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
536 it
++, curr
= it
.current()) {
537 if (curr
->isMaximizedFull()) // fully maximized, ignore it
539 int curr_w
= curr
->getWidth() + (screen
.getBorderWidth() * 4);
541 ((curr
->isShaded()) ? curr
->getTitleHeight() : curr
->getHeight()) +
542 (screen
.getBorderWidth() * 4);
544 if (curr
->getXFrame() < test_x
+ win_w
&&
545 curr
->getXFrame() + curr_w
> test_x
&&
546 curr
->getYFrame() < test_y
+ win_h
&&
547 curr
->getYFrame() + curr_h
> test_y
) {
553 (toolbar_x
< test_x
+ win_w
&&
554 toolbar_x
+ toolbar_w
> test_x
&&
555 toolbar_y
< test_y
+ win_h
&&
556 toolbar_y
+ toolbar_h
> test_y
)
559 (slit_x
< test_x
+ win_w
&&
560 slit_x
+ slit_w
> test_x
&&
561 slit_y
< test_y
+ win_h
&&
562 slit_y
+ slit_h
> test_y
)
574 test_y
+= (change_y
* delta_y
);
577 test_x
+= (change_x
* delta_x
);
585 if (((unsigned) cascade_x
> (screen
.getWidth() / 2)) ||
586 ((unsigned) cascade_y
> (screen
.getHeight() / 2)))
587 cascade_x
= cascade_y
= 32;
592 cascade_x
+= win
->getTitleHeight();
593 cascade_y
+= win
->getTitleHeight();
596 if (place_x
+ win_w
> (signed) screen
.getWidth())
597 place_x
= (((signed) screen
.getWidth()) - win_w
) / 2;
598 if (place_y
+ win_h
> (signed) screen
.getHeight())
599 place_y
= (((signed) screen
.getHeight()) - win_h
) / 2;
601 win
->configure(place_x
, place_y
, win
->getWidth(), win
->getHeight());