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
) {
65 cascade_x
= cascade_y
= 32;
69 stackingList
= new LinkedList
<OpenboxWindow
>;
70 windowList
= new LinkedList
<OpenboxWindow
>;
71 clientmenu
= new Clientmenu(this);
73 lastfocus
= (OpenboxWindow
*) 0;
76 char *tmp
= screen
->getNameOfWorkspace(id
);
81 Workspace::~Workspace(void) {
91 const int Workspace::addWindow(OpenboxWindow
*w
, Bool place
) {
94 if (place
) placeWindow(w
);
97 w
->setWindowNumber(windowList
->count());
99 stackingList
->insert(w
, 0);
100 windowList
->insert(w
);
102 clientmenu
->insert((const char **) w
->getTitle());
103 clientmenu
->update();
105 screen
->updateNetizenWindowAdd(w
->getClientWindow(), id
);
109 return w
->getWindowNumber();
113 const int Workspace::removeWindow(OpenboxWindow
*w
) {
116 stackingList
->remove(w
);
118 if (w
->isFocused()) {
119 if (w
->isTransient() && w
->getTransientFor() &&
120 w
->getTransientFor()->isVisible()) {
121 w
->getTransientFor()->setInputFocus();
122 } else if (screen
->isSloppyFocus()) {
123 screen
->getOpenbox()->setFocusedWindow((OpenboxWindow
*) 0);
125 OpenboxWindow
*top
= stackingList
->first();
126 if (! top
|| ! top
->setInputFocus()) {
127 screen
->getOpenbox()->setFocusedWindow((OpenboxWindow
*) 0);
128 XSetInputFocus(screen
->getOpenbox()->getXDisplay(),
129 screen
->getToolbar()->getWindowID(),
130 RevertToParent
, CurrentTime
);
136 lastfocus
= (OpenboxWindow
*) 0;
138 windowList
->remove(w
->getWindowNumber());
139 clientmenu
->remove(w
->getWindowNumber());
140 clientmenu
->update();
142 screen
->updateNetizenWindowDel(w
->getClientWindow());
144 LinkedListIterator
<OpenboxWindow
> it(windowList
);
145 OpenboxWindow
*bw
= it
.current();
146 for (int i
= 0; bw
; it
++, i
++, bw
= it
.current())
147 bw
->setWindowNumber(i
);
149 return windowList
->count();
153 void Workspace::showAll(void) {
154 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
155 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
156 bw
->deiconify(False
, False
);
160 void Workspace::hideAll(void) {
161 LinkedList
<OpenboxWindow
> lst
;
163 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
164 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
167 LinkedListIterator
<OpenboxWindow
> it2(&lst
);
168 for (OpenboxWindow
*bw
= it2
.current(); bw
; it2
++, bw
= it2
.current())
174 void Workspace::removeAll(void) {
175 LinkedListIterator
<OpenboxWindow
> it(windowList
);
176 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
181 void Workspace::raiseWindow(OpenboxWindow
*w
) {
182 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
184 while (bottom
->isTransient() && bottom
->getTransientFor())
185 bottom
= bottom
->getTransientFor();
189 while (win
->hasTransient() && win
->getTransient()) {
190 win
= win
->getTransient();
195 Window
*nstack
= new Window
[i
], *curr
= nstack
;
200 *(curr
++) = win
->getFrameWindow();
201 screen
->updateNetizenWindowRaise(win
->getClientWindow());
203 if (! win
->isIconic()) {
204 wkspc
= screen
->getWorkspace(win
->getWorkspaceNumber());
205 wkspc
->stackingList
->remove(win
);
206 wkspc
->stackingList
->insert(win
, 0);
209 if (! win
->hasTransient() || ! win
->getTransient())
212 win
= win
->getTransient();
215 screen
->raiseWindows(nstack
, i
);
221 void Workspace::lowerWindow(OpenboxWindow
*w
) {
222 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
224 while (bottom
->isTransient() && bottom
->getTransientFor())
225 bottom
= bottom
->getTransientFor();
229 while (win
->hasTransient() && win
->getTransient()) {
230 win
= win
->getTransient();
235 Window
*nstack
= new Window
[i
], *curr
= nstack
;
239 *(curr
++) = win
->getFrameWindow();
240 screen
->updateNetizenWindowLower(win
->getClientWindow());
242 if (! win
->isIconic()) {
243 wkspc
= screen
->getWorkspace(win
->getWorkspaceNumber());
244 wkspc
->stackingList
->remove(win
);
245 wkspc
->stackingList
->insert(win
);
248 if (! win
->getTransientFor())
251 win
= win
->getTransientFor();
254 screen
->getOpenbox()->grab();
256 XLowerWindow(screen
->getBaseDisplay()->getXDisplay(), *nstack
);
257 XRestackWindows(screen
->getBaseDisplay()->getXDisplay(), nstack
, i
);
259 screen
->getOpenbox()->ungrab();
265 void Workspace::reconfigure(void) {
266 clientmenu
->reconfigure();
268 LinkedListIterator
<OpenboxWindow
> it(windowList
);
269 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current()) {
270 if (bw
->validateClient())
276 OpenboxWindow
*Workspace::getWindow(int index
) {
277 if ((index
>= 0) && (index
< windowList
->count()))
278 return windowList
->find(index
);
284 const int Workspace::getCount(void) {
285 return windowList
->count();
289 void Workspace::update(void) {
290 clientmenu
->update();
291 screen
->getToolbar()->redrawWindowLabel(True
);
295 Bool
Workspace::isCurrent(void) {
296 return (id
== screen
->getCurrentWorkspaceID());
300 Bool
Workspace::isLastWindow(OpenboxWindow
*w
) {
301 return (w
== windowList
->last());
304 void Workspace::setCurrent(void) {
305 screen
->changeWorkspaceID(id
);
309 void Workspace::setName(char *new_name
) {
314 name
= bstrdup(new_name
);
316 name
= new char[128];
317 sprintf(name
, i18n
->getMessage(WorkspaceSet
, WorkspaceDefaultNameFormat
,
318 "Workspace %d"), id
+ 1);
321 clientmenu
->setLabel(name
);
322 clientmenu
->update();
326 void Workspace::shutdown(void) {
327 while (windowList
->count()) {
328 windowList
->first()->restore();
329 delete windowList
->first();
333 static rectList
calcSpace(const OpenboxWindow
&win
, const rectList
&spaces
) {
335 rectList::const_iterator siter
;
336 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
337 if(win
.getArea().Intersect(*siter
)) {
338 //Check for space to the left of the window
339 if(win
.getXFrame() > siter
->x())
340 result
.push_back(Rect(siter
->x(), siter
->y(),
341 win
.getXFrame() - siter
->x() - 1,
343 //Check for space above the window
344 if(win
.getYFrame() > siter
->y())
345 result
.push_back(Rect(siter
->x(), siter
->y(),
347 win
.getYFrame() - siter
->y() - 1));
348 //Check for space to the right of the window
349 if((win
.getXFrame()+win
.getWidth()) <
350 (siter
->x()+siter
->w()))
351 result
.push_back(Rect(win
.getXFrame() + win
.getWidth() + 1,
353 siter
->x() + siter
->w() -
354 win
.getXFrame() - win
.getWidth() - 1,
356 //Check for space below the window
357 if((win
.getYFrame()+win
.getHeight()) <
358 (siter
->y()+siter
->h()))
359 result
.push_back(Rect(siter
->x(),
360 win
.getYFrame() + win
.getHeight() + 1,
362 siter
->y() + siter
->h()-
363 win
.getYFrame() - win
.getHeight() - 1));
367 result
.push_back(*siter
);
372 //BestFitPlacement finds the smallest free space that fits the window
373 //to be placed. It currentl ignores whether placement is right to left or top
375 Point
*Workspace::bestFitPlacement(const Size
&win_size
, const Rect
&space
)
379 LinkedListIterator
<OpenboxWindow
> it(windowList
);
380 rectList::const_iterator siter
;
381 spaces
.push_back(space
); //initially the entire screen is free
385 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
386 spaces
= calcSpace(*cur
, spaces
);
388 //Find first space that fits the window
390 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
391 if ((siter
->w() >= win_size
.w()) &&
392 (siter
->h() >= win_size
.h()))
397 return new Point(best
->origin());
399 return new Point(200, 0);
402 inline Point
*Workspace::rowSmartPlacement(const Size
&win_size
,
405 int test_x
, test_y
, place_x
= 0, place_y
= 0;
408 ((screen
->getColPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
410 ((screen
->getRowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
411 int delta_x
= 8, delta_y
= 8;
412 LinkedListIterator
<OpenboxWindow
> it(windowList
);
414 test_y
= (screen
->getColPlacementDirection() == BScreen::TopBottom
) ?
415 start_pos
: screen
->getHeight() - win_size
.h() - start_pos
;
418 ((screen
->getColPlacementDirection() == BScreen::BottomTop
) ?
419 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())) {
420 test_x
= (screen
->getRowPlacementDirection() == BScreen::LeftRight
) ?
421 start_pos
: space
.w() - win_size
.w() - start_pos
;
423 ((screen
->getRowPlacementDirection() == BScreen::RightLeft
) ?
424 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
428 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
429 it
++, curr
= it
.current()) {
430 int curr_w
= curr
->getWidth() + (screen
->getBorderWidth() * 4);
432 ((curr
->isShaded()) ? curr
->getTitleHeight() : curr
->getHeight()) +
433 (screen
->getBorderWidth() * 4);
435 if (curr
->getXFrame() < test_x
+ win_size
.w() &&
436 curr
->getXFrame() + curr_w
> test_x
&&
437 curr
->getYFrame() < test_y
+ win_size
.h() &&
438 curr
->getYFrame() + curr_h
> test_y
) {
443 // Removed code for checking toolbar and slit
444 // The space passed in should not include either
453 test_x
+= (change_x
* delta_x
);
456 test_y
+= (change_y
* delta_y
);
458 return new Point(place_x
, place_y
);
461 void Workspace::placeWindow(OpenboxWindow
*win
) {
466 const int win_w
= win
->getWidth() + (screen
->getBorderWidth() * 4),
467 win_h
= win
->getHeight() + (screen
->getBorderWidth() * 4),
469 slit_x
= screen
->getSlit()->getX() - screen
->getBorderWidth(),
470 slit_y
= screen
->getSlit()->getY() - screen
->getBorderWidth(),
471 slit_w
= screen
->getSlit()->getWidth() +
472 (screen
->getBorderWidth() * 4),
473 slit_h
= screen
->getSlit()->getHeight() +
474 (screen
->getBorderWidth() * 4),
476 toolbar_x
= screen
->getToolbar()->getX() - screen
->getBorderWidth(),
477 toolbar_y
= screen
->getToolbar()->getY() - screen
->getBorderWidth(),
478 toolbar_w
= screen
->getToolbar()->getWidth() +
479 (screen
->getBorderWidth() * 4),
480 toolbar_h
= screen
->getToolbar()->getHeight() +
481 (screen
->getBorderWidth() * 4),
484 ((screen
->getColPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
486 ((screen
->getRowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
487 delta_x
= 8, delta_y
= 8;
489 int test_x
, test_y
, place_x
= 0, place_y
= 0;
490 LinkedListIterator
<OpenboxWindow
> it(windowList
);
496 Size
window_size(win_w
, win_h
);
498 switch (screen
->getPlacementPolicy()) {
499 case BScreen::BestFitPlacement
: {
500 Point
*spot
= bestFitPlacement(window_size
, space
);
509 case BScreen::RowSmartPlacement
: {
510 Point
*spot
=rowSmartPlacement(window_size
, space
);
520 case BScreen::ColSmartPlacement
: {
521 test_x
= (screen
->getRowPlacementDirection() == BScreen::LeftRight
) ?
522 start_pos
: screen
->getWidth() - win_w
- start_pos
;
525 ((screen
->getRowPlacementDirection() == BScreen::RightLeft
) ?
526 test_x
> 0 : test_x
+ win_w
< (signed) screen
->getWidth())) {
527 test_y
= (screen
->getColPlacementDirection() == BScreen::TopBottom
) ?
528 start_pos
: screen
->getHeight() - win_h
- start_pos
;
531 ((screen
->getColPlacementDirection() == BScreen::BottomTop
) ?
532 test_y
> 0 : test_y
+ win_h
< (signed) screen
->getHeight())) {
536 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
537 it
++, curr
= it
.current()) {
538 if (curr
->isMaximizedFull()) // fully maximized, ignore it
540 int curr_w
= curr
->getWidth() + (screen
->getBorderWidth() * 4);
542 ((curr
->isShaded()) ? curr
->getTitleHeight() : curr
->getHeight()) +
543 (screen
->getBorderWidth() * 4);
545 if (curr
->getXFrame() < test_x
+ win_w
&&
546 curr
->getXFrame() + curr_w
> test_x
&&
547 curr
->getYFrame() < test_y
+ win_h
&&
548 curr
->getYFrame() + curr_h
> test_y
) {
554 (toolbar_x
< test_x
+ win_w
&&
555 toolbar_x
+ toolbar_w
> test_x
&&
556 toolbar_y
< test_y
+ win_h
&&
557 toolbar_y
+ toolbar_h
> test_y
)
560 (slit_x
< test_x
+ win_w
&&
561 slit_x
+ slit_w
> test_x
&&
562 slit_y
< test_y
+ win_h
&&
563 slit_y
+ slit_h
> test_y
)
575 test_y
+= (change_y
* delta_y
);
578 test_x
+= (change_x
* delta_x
);
586 if (((unsigned) cascade_x
> (screen
->getWidth() / 2)) ||
587 ((unsigned) cascade_y
> (screen
->getHeight() / 2)))
588 cascade_x
= cascade_y
= 32;
593 cascade_x
+= win
->getTitleHeight();
594 cascade_y
+= win
->getTitleHeight();
597 if (place_x
+ win_w
> (signed) screen
->getWidth())
598 place_x
= (((signed) screen
->getWidth()) - win_w
) / 2;
599 if (place_y
+ win_h
> (signed) screen
->getHeight())
600 place_y
= (((signed) screen
->getHeight()) - win_h
) / 2;
602 win
->configure(place_x
, place_y
, win
->getWidth(), win
->getHeight());