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"
50 #endif // HAVE_STDIO_H
54 #endif // HAVE_STDLIB_H
58 #endif // STDC_HEADERS
61 typedef vector
<Rect
> rectList
;
63 Workspace::Workspace(BScreen
&scrn
, int i
) : screen(scrn
) {
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
.area().Intersect(*siter
)) {
338 //Check for space to the left of the window
339 if(win
.origin().x() > siter
->x())
340 result
.push_back(Rect(siter
->x(), siter
->y(),
341 win
.origin().x() - siter
->x() - 1,
343 //Check for space above the window
344 if(win
.origin().y() > siter
->y())
345 result
.push_back(Rect(siter
->x(), siter
->y(),
347 win
.origin().y() - siter
->y() - 1));
348 //Check for space to the right of the window
349 if((win
.origin().x()+win
.size().w()) <
350 (siter
->x()+siter
->w()))
351 result
.push_back(Rect(win
.origin().x() + win
.size().w() + 1,
353 siter
->x() + siter
->w() -
354 win
.origin().x() - win
.size().w() - 1,
356 //Check for space below the window
357 if((win
.origin().y()+win
.size().h()) <
358 (siter
->y()+siter
->h()))
359 result
.push_back(Rect(siter
->x(),
360 win
.origin().y() + win
.size().h() + 1,
362 siter
->y() + siter
->h()-
363 win
.origin().y() - win
.size().h() - 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
.size().h() - 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
->size().w() + (screen
.getBorderWidth() * 4);
431 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
433 if (curr
->origin().x() < test_x
+ win_size
.w() &&
434 curr
->origin().x() + curr_w
> test_x
&&
435 curr
->origin().y() < test_y
+ win_size
.h() &&
436 curr
->origin().y() + curr_h
> test_y
) {
441 // Removed code for checking toolbar and slit
442 // The space passed in should not include either
451 test_x
+= (change_x
* delta_x
);
454 test_y
+= (change_y
* delta_y
);
456 return new Point(place_x
, place_y
);
459 void Workspace::placeWindow(OpenboxWindow
*win
) {
464 const int win_w
= win
->size().w() + (screen
.getBorderWidth() * 4),
465 win_h
= win
->size().h() + (screen
.getBorderWidth() * 4),
467 slit_x
= screen
.getSlit()->area().x() - screen
.getBorderWidth(),
468 slit_y
= screen
.getSlit()->area().y() - screen
.getBorderWidth(),
469 slit_w
= screen
.getSlit()->area().w() +
470 (screen
.getBorderWidth() * 4),
471 slit_h
= screen
.getSlit()->area().h() +
472 (screen
.getBorderWidth() * 4),
474 toolbar_x
= screen
.getToolbar()->getX() - screen
.getBorderWidth(),
475 toolbar_y
= screen
.getToolbar()->getY() - screen
.getBorderWidth(),
476 toolbar_w
= screen
.getToolbar()->getWidth() +
477 (screen
.getBorderWidth() * 4),
478 toolbar_h
= screen
.getToolbar()->getHeight() +
479 (screen
.getBorderWidth() * 4),
482 ((screen
.getColPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
484 ((screen
.getRowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
485 delta_x
= 8, delta_y
= 8;
487 int test_x
, test_y
, place_x
= 0, place_y
= 0;
488 LinkedListIterator
<OpenboxWindow
> it(windowList
);
494 Size
window_size(win_w
, win_h
);
496 switch (screen
.getPlacementPolicy()) {
497 case BScreen::BestFitPlacement
: {
498 Point
*spot
= bestFitPlacement(window_size
, space
);
507 case BScreen::RowSmartPlacement
: {
508 Point
*spot
=rowSmartPlacement(window_size
, space
);
518 case BScreen::ColSmartPlacement
: {
519 test_x
= (screen
.getRowPlacementDirection() == BScreen::LeftRight
) ?
520 start_pos
: screen
.size().w() - win_w
- start_pos
;
523 ((screen
.getRowPlacementDirection() == BScreen::RightLeft
) ?
524 test_x
> 0 : test_x
+ win_w
< (signed) screen
.size().w())) {
525 test_y
= (screen
.getColPlacementDirection() == BScreen::TopBottom
) ?
526 start_pos
: screen
.size().h() - win_h
- start_pos
;
529 ((screen
.getColPlacementDirection() == BScreen::BottomTop
) ?
530 test_y
> 0 : test_y
+ win_h
< (signed) screen
.size().h())) {
534 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
535 it
++, curr
= it
.current()) {
536 if (curr
->isMaximizedFull()) // fully maximized, ignore it
538 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
540 ((curr
->isShaded()) ? curr
->getTitleHeight() : curr
->size().h()) +
541 (screen
.getBorderWidth() * 4);
543 if (curr
->origin().x() < test_x
+ win_w
&&
544 curr
->origin().x() + curr_w
> test_x
&&
545 curr
->origin().y() < test_y
+ win_h
&&
546 curr
->origin().y() + curr_h
> test_y
) {
552 (toolbar_x
< test_x
+ win_w
&&
553 toolbar_x
+ toolbar_w
> test_x
&&
554 toolbar_y
< test_y
+ win_h
&&
555 toolbar_y
+ toolbar_h
> test_y
)
558 (slit_x
< test_x
+ win_w
&&
559 slit_x
+ slit_w
> test_x
&&
560 slit_y
< test_y
+ win_h
&&
561 slit_y
+ slit_h
> test_y
)
573 test_y
+= (change_y
* delta_y
);
576 test_x
+= (change_x
* delta_x
);
584 if (((unsigned) cascade_x
> (screen
.size().w() / 2)) ||
585 ((unsigned) cascade_y
> (screen
.size().h() / 2)))
586 cascade_x
= cascade_y
= 32;
591 cascade_x
+= win
->getTitleHeight();
592 cascade_y
+= win
->getTitleHeight();
595 if (place_x
+ win_w
> (signed) screen
.size().w())
596 place_x
= (((signed) screen
.size().w()) - win_w
) / 2;
597 if (place_y
+ win_h
> (signed) screen
.size().h())
598 place_y
= (((signed) screen
.size().h()) - win_h
) / 2;
600 win
->configure(place_x
, place_y
, win
->size().w(), win
->size().h());