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"
45 #include "Windowmenu.h"
51 #endif // HAVE_STDIO_H
55 #endif // HAVE_STDLIB_H
59 #endif // HAVE_STRING_H
62 typedef vector
<Rect
> rectList
;
64 Workspace::Workspace(BScreen
&scrn
, int i
) : screen(scrn
) {
66 cascade_x
= cascade_y
= 32;
70 stackingList
= new LinkedList
<OpenboxWindow
>;
71 windowList
= new LinkedList
<OpenboxWindow
>;
72 clientmenu
= new Clientmenu(*this);
74 lastfocus
= (OpenboxWindow
*) 0;
77 char *tmp
= screen
.getNameOfWorkspace(id
);
82 Workspace::~Workspace(void) {
92 const int Workspace::addWindow(OpenboxWindow
*w
, Bool place
) {
95 if (place
) placeWindow(w
);
98 w
->setWindowNumber(windowList
->count());
100 stackingList
->insert(w
, 0);
101 windowList
->insert(w
);
103 clientmenu
->insert((const char **) w
->getTitle());
104 clientmenu
->update();
106 screen
.updateNetizenWindowAdd(w
->getClientWindow(), id
);
110 return w
->getWindowNumber();
114 const int Workspace::removeWindow(OpenboxWindow
*w
) {
117 stackingList
->remove(w
);
119 if (w
->isFocused()) {
120 if (w
->isTransient() && w
->getTransientFor() &&
121 w
->getTransientFor()->isVisible()) {
122 w
->getTransientFor()->setInputFocus();
123 } else if (screen
.sloppyFocus()) {
124 screen
.getOpenbox().setFocusedWindow((OpenboxWindow
*) 0);
126 OpenboxWindow
*top
= stackingList
->first();
127 if (! top
|| ! top
->setInputFocus()) {
128 screen
.getOpenbox().setFocusedWindow((OpenboxWindow
*) 0);
129 XSetInputFocus(screen
.getOpenbox().getXDisplay(),
130 screen
.getToolbar()->getWindowID(),
131 RevertToParent
, CurrentTime
);
137 lastfocus
= (OpenboxWindow
*) 0;
139 windowList
->remove(w
->getWindowNumber());
140 clientmenu
->remove(w
->getWindowNumber());
141 clientmenu
->update();
143 screen
.updateNetizenWindowDel(w
->getClientWindow());
145 LinkedListIterator
<OpenboxWindow
> it(windowList
);
146 OpenboxWindow
*bw
= it
.current();
147 for (int i
= 0; bw
; it
++, i
++, bw
= it
.current())
148 bw
->setWindowNumber(i
);
150 return windowList
->count();
154 void Workspace::showAll(void) {
155 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
156 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
157 bw
->deiconify(False
, False
);
161 void Workspace::hideAll(void) {
162 LinkedList
<OpenboxWindow
> lst
;
164 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
165 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
168 LinkedListIterator
<OpenboxWindow
> it2(&lst
);
169 for (OpenboxWindow
*bw
= it2
.current(); bw
; it2
++, bw
= it2
.current())
175 void Workspace::removeAll(void) {
176 LinkedListIterator
<OpenboxWindow
> it(windowList
);
177 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
182 void Workspace::raiseWindow(OpenboxWindow
*w
) {
183 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
185 while (bottom
->isTransient() && bottom
->getTransientFor())
186 bottom
= bottom
->getTransientFor();
190 while (win
->hasTransient() && win
->getTransient()) {
191 win
= win
->getTransient();
196 Window
*nstack
= new Window
[i
], *curr
= nstack
;
201 *(curr
++) = win
->getFrameWindow();
202 screen
.updateNetizenWindowRaise(win
->getClientWindow());
204 if (! win
->isIconic()) {
205 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
206 wkspc
->stackingList
->remove(win
);
207 wkspc
->stackingList
->insert(win
, 0);
210 if (! win
->hasTransient() || ! win
->getTransient())
213 win
= win
->getTransient();
216 screen
.raiseWindows(nstack
, i
);
222 void Workspace::lowerWindow(OpenboxWindow
*w
) {
223 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
225 while (bottom
->isTransient() && bottom
->getTransientFor())
226 bottom
= bottom
->getTransientFor();
230 while (win
->hasTransient() && win
->getTransient()) {
231 win
= win
->getTransient();
236 Window
*nstack
= new Window
[i
], *curr
= nstack
;
240 *(curr
++) = win
->getFrameWindow();
241 screen
.updateNetizenWindowLower(win
->getClientWindow());
243 if (! win
->isIconic()) {
244 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
245 wkspc
->stackingList
->remove(win
);
246 wkspc
->stackingList
->insert(win
);
249 if (! win
->getTransientFor())
252 win
= win
->getTransientFor();
255 screen
.getOpenbox().grab();
257 XLowerWindow(screen
.getBaseDisplay().getXDisplay(), *nstack
);
258 XRestackWindows(screen
.getBaseDisplay().getXDisplay(), nstack
, i
);
260 screen
.getOpenbox().ungrab();
266 void Workspace::reconfigure(void) {
267 clientmenu
->reconfigure();
269 LinkedListIterator
<OpenboxWindow
> it(windowList
);
270 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current()) {
271 if (bw
->validateClient())
277 OpenboxWindow
*Workspace::getWindow(int index
) {
278 if ((index
>= 0) && (index
< windowList
->count()))
279 return windowList
->find(index
);
285 const int Workspace::getCount(void) {
286 return windowList
->count();
290 void Workspace::update(void) {
291 clientmenu
->update();
292 screen
.getToolbar()->redrawWindowLabel(True
);
296 Bool
Workspace::isCurrent(void) {
297 return (id
== screen
.getCurrentWorkspaceID());
301 Bool
Workspace::isLastWindow(OpenboxWindow
*w
) {
302 return (w
== windowList
->last());
305 void Workspace::setCurrent(void) {
306 screen
.changeWorkspaceID(id
);
310 void Workspace::setName(char *new_name
) {
315 name
= bstrdup(new_name
);
317 name
= new char[128];
318 sprintf(name
, i18n
->getMessage(WorkspaceSet
, WorkspaceDefaultNameFormat
,
319 "Workspace %d"), id
+ 1);
322 clientmenu
->setLabel(name
);
323 clientmenu
->update();
324 screen
.saveWorkspaceNames();
328 void Workspace::shutdown(void) {
329 while (windowList
->count()) {
330 windowList
->first()->restore();
331 delete windowList
->first();
335 static rectList
calcSpace(const Rect
&win
, const rectList
&spaces
) {
337 rectList::const_iterator siter
;
338 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
339 if(win
.Intersect(*siter
)) {
340 //Check for space to the left of the window
341 if(win
.origin().x() > siter
->x())
342 result
.push_back(Rect(siter
->x(), siter
->y(),
343 win
.origin().x() - siter
->x() - 1,
345 //Check for space above the window
346 if(win
.origin().y() > siter
->y())
347 result
.push_back(Rect(siter
->x(), siter
->y(),
349 win
.origin().y() - siter
->y() - 1));
350 //Check for space to the right of the window
351 if((win
.origin().x()+win
.size().w()) <
352 (siter
->x()+siter
->w()))
353 result
.push_back(Rect(win
.origin().x() + win
.size().w() + 1,
355 siter
->x() + siter
->w() -
356 win
.origin().x() - win
.size().w() - 1,
358 //Check for space below the window
359 if((win
.origin().y()+win
.size().h()) <
360 (siter
->y()+siter
->h()))
361 result
.push_back(Rect(siter
->x(),
362 win
.origin().y() + win
.size().h() + 1,
364 siter
->y() + siter
->h()-
365 win
.origin().y() - win
.size().h() - 1));
369 result
.push_back(*siter
);
374 //BestFitPlacement finds the smallest free space that fits the window
375 //to be placed. It currentl ignores whether placement is right to left or top
377 Point
*Workspace::bestFitPlacement(const Size
&win_size
, const Rect
&space
)
381 LinkedListIterator
<OpenboxWindow
> it(windowList
);
382 rectList::const_iterator siter
;
383 spaces
.push_back(space
); //initially the entire screen is free
387 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
388 spaces
= calcSpace(cur
->area().Inflate(screen
.getBorderWidth() * 4),
391 //Find first space that fits the window
393 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
394 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
397 else if (win_size
.w() * win_size
.h() - siter
->w() * siter
->h() <
398 best
->w() + best
->h())
403 Point
*pt
= new Point(best
->origin());
404 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
405 pt
->setY(pt
->y() + (best
->h() - win_size
.h()));
406 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
407 pt
->setX(pt
->x() + (best
->w() - win_size
.w()));
410 return NULL
; //fall back to cascade
413 inline Point
*Workspace::rowSmartPlacement(const Size
&win_size
,
416 int test_x
, test_y
, place_x
= 0, place_y
= 0;
419 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
421 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
422 int delta_x
= 8, delta_y
= 8;
423 LinkedListIterator
<OpenboxWindow
> it(windowList
);
425 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
426 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
429 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
430 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())) {
431 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
432 start_pos
: space
.w() - win_size
.w() - start_pos
;
434 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
435 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
439 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
440 it
++, curr
= it
.current()) {
441 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
442 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
444 if (curr
->origin().x() < test_x
+ win_size
.w() &&
445 curr
->origin().x() + curr_w
> test_x
&&
446 curr
->origin().y() < test_y
+ win_size
.h() &&
447 curr
->origin().y() + curr_h
> test_y
) {
452 // Removed code for checking toolbar and slit
453 // The space passed in should not include either
462 test_x
+= (change_x
* delta_x
);
465 test_y
+= (change_y
* delta_y
);
468 return new Point(place_x
, place_y
);
470 return NULL
; // fall back to cascade
473 inline Point
* Workspace::colSmartPlacement(const Size
&win_size
,
480 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
482 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
483 int delta_x
= 8, delta_y
= 8;
484 LinkedListIterator
<OpenboxWindow
> it(windowList
);
486 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
487 start_pos
: screen
.size().w() - win_size
.w() - start_pos
;
490 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
491 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
492 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
493 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
496 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
497 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())){
502 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
503 it
++, curr
= it
.current()) {
504 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
505 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
507 if (curr
->origin().x() < test_x
+ win_size
.w() &&
508 curr
->origin().x() + curr_w
> test_x
&&
509 curr
->origin().y() < test_y
+ win_size
.h() &&
510 curr
->origin().y() + curr_h
> test_y
) {
515 // Removed code checking for intersection with Toolbar and Slit
516 // The space passed to this method should not include either
519 pt
= new Point(test_x
,test_y
);
524 test_y
+= (change_y
* delta_y
);
527 test_x
+= (change_x
* delta_x
);
535 Point
*const Workspace::cascadePlacement(const OpenboxWindow
*const win
){
536 if (((unsigned) cascade_x
> (screen
.size().w() / 2)) ||
537 ((unsigned) cascade_y
> (screen
.size().h() / 2)))
538 cascade_x
= cascade_y
= 32;
540 cascade_x
+= win
->getTitleHeight();
541 cascade_y
+= win
->getTitleHeight();
543 return new Point(cascade_x
, cascade_y
);
547 void Workspace::placeWindow(OpenboxWindow
*win
) {
550 // the following code is temporary and will be taken care of by Screen in the
551 // future (with the NETWM 'strut')
552 Rect
space(0, 0, screen
.size().w(), screen
.size().h());
555 Slit
*slit
= screen
.getSlit();
556 Toolbar
*toolbar
= screen
.getToolbar();
557 int tbarh
= screen
.hideToolbar() ? 0 :
558 toolbar
->getExposedHeight() + screen
.getBorderWidth() * 2;
560 switch (toolbar
->placement()) {
561 case Toolbar::TopLeft
:
562 case Toolbar::TopCenter
:
563 case Toolbar::TopRight
:
566 case Toolbar::BottomLeft
:
567 case Toolbar::BottomCenter
:
568 case Toolbar::BottomRight
:
572 ASSERT(false); // unhandled placement
574 if ((slit
->direction() == Slit::Horizontal
&&
575 (slit
->placement() == Slit::TopLeft
||
576 slit
->placement() == Slit::TopRight
)) ||
577 slit
->placement() == Slit::TopCenter
) {
580 space
.setY(slit
->area().y());
581 space
.setH(space
.h() - space
.y());
583 space
.setH(space
.h() - tbarh
);
584 space
.setY(space
.y() + slit
->area().h() + screen
.getBorderWidth() * 2);
585 space
.setH(space
.h() - (slit
->area().h() + screen
.getBorderWidth() * 2));
586 } else if ((slit
->direction() == Slit::Vertical
&&
587 (slit
->placement() == Slit::TopRight
||
588 slit
->placement() == Slit::BottomRight
)) ||
589 slit
->placement() == Slit::CenterRight
) {
591 space
.setW(space
.w() - (slit
->area().w() + screen
.getBorderWidth() * 2));
593 space
.setY(space
.y() + tbarh
);
594 space
.setH(space
.h() - tbarh
);
595 } else if ((slit
->direction() == Slit::Horizontal
&&
596 (slit
->placement() == Slit::BottomLeft
||
597 slit
->placement() == Slit::BottomRight
)) ||
598 slit
->placement() == Slit::BottomCenter
) {
600 space
.setH(space
.h() - (screen
.size().h() - slit
->area().y()));
601 } else {// if ((slit->direction() == Slit::Vertical &&
602 // (slit->placement() == Slit::TopLeft ||
603 // slit->placement() == Slit::BottomLeft)) ||
604 // slit->placement() == Slit::CenterLeft)
606 space
.setX(slit
->area().w() + screen
.getBorderWidth() * 2);
607 space
.setW(space
.w() - (slit
->area().w() + screen
.getBorderWidth() * 2));
609 space
.setY(space
.y() + tbarh
);
610 space
.setH(space
.h() - tbarh
);
613 Toolbar
*toolbar
= screen
.getToolbar();
614 int tbarh
= screen
.hideToolbar() ? 0 :
615 toolbar
->getExposedHeight() + screen
.getBorderWidth() * 2;
616 switch (toolbar
->placement()) {
617 case Toolbar::TopLeft
:
618 case Toolbar::TopCenter
:
619 case Toolbar::TopRight
:
620 space
.setY(toolbar
->getExposedHeight());
621 space
.setH(space
.h() - toolbar
->getExposedHeight());
623 case Toolbar::BottomLeft
:
624 case Toolbar::BottomCenter
:
625 case Toolbar::BottomRight
:
626 space
.setH(space
.h() - tbarh
);
629 ASSERT(false); // unhandled placement
633 const int win_w
= win
->size().w() + (screen
.getBorderWidth() * 4),
634 win_h
= win
->size().h() + (screen
.getBorderWidth() * 4),
637 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
639 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
640 delta_x
= 8, delta_y
= 8;
642 LinkedListIterator
<OpenboxWindow
> it(windowList
);
644 Size
window_size(win
->size().w()+screen
.getBorderWidth() * 4,
645 win
->size().h()+screen
.getBorderWidth() * 4);
648 switch (screen
.placementPolicy()) {
649 case BScreen::BestFitPlacement
:
650 place
= bestFitPlacement(window_size
, space
);
652 case BScreen::RowSmartPlacement
:
653 place
= rowSmartPlacement(window_size
, space
);
655 case BScreen::ColSmartPlacement
:
656 place
= colSmartPlacement(window_size
, space
);
661 place
= cascadePlacement(win
);
663 ASSERT(place
!= NULL
);
664 if (place
->x() + win_w
> (signed) screen
.size().w())
665 place
->setX(((signed) screen
.size().w() - win_w
) / 2);
666 if (place
->y() + win_h
> (signed) screen
.size().h())
667 place
->setY(((signed) screen
.size().h() - win_h
) / 2);
669 win
->configure(place
->x(), place
->y(), win
->size().w(), win
->size().h());