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()) &&
395 (siter
->h() >= win_size
.h()))
400 return new Point(best
->origin());
402 return NULL
; //fall back to cascade
405 inline Point
*Workspace::rowSmartPlacement(const Size
&win_size
,
408 int test_x
, test_y
, place_x
= 0, place_y
= 0;
411 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
413 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
414 int delta_x
= 8, delta_y
= 8;
415 LinkedListIterator
<OpenboxWindow
> it(windowList
);
417 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
418 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
421 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
422 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())) {
423 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
424 start_pos
: space
.w() - win_size
.w() - start_pos
;
426 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
427 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
431 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
432 it
++, curr
= it
.current()) {
433 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
434 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
436 if (curr
->origin().x() < test_x
+ win_size
.w() &&
437 curr
->origin().x() + curr_w
> test_x
&&
438 curr
->origin().y() < test_y
+ win_size
.h() &&
439 curr
->origin().y() + curr_h
> test_y
) {
444 // Removed code for checking toolbar and slit
445 // The space passed in should not include either
454 test_x
+= (change_x
* delta_x
);
457 test_y
+= (change_y
* delta_y
);
460 return new Point(place_x
, place_y
);
462 return NULL
; // fall back to cascade
465 inline Point
* Workspace::colSmartPlacement(const Size
&win_size
,
472 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
474 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
475 int delta_x
= 8, delta_y
= 8;
476 LinkedListIterator
<OpenboxWindow
> it(windowList
);
478 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
479 start_pos
: screen
.size().w() - win_size
.w() - start_pos
;
482 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
483 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
484 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
485 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
488 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
489 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())){
494 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
495 it
++, curr
= it
.current()) {
496 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
497 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
499 if (curr
->origin().x() < test_x
+ win_size
.w() &&
500 curr
->origin().x() + curr_w
> test_x
&&
501 curr
->origin().y() < test_y
+ win_size
.h() &&
502 curr
->origin().y() + curr_h
> test_y
) {
507 // Removed code checking for intersection with Toolbar and Slit
508 // The space passed to this method should not include either
511 pt
= new Point(test_x
,test_y
);
516 test_y
+= (change_y
* delta_y
);
519 test_x
+= (change_x
* delta_x
);
527 Point
*const Workspace::cascadePlacement(const OpenboxWindow
*const win
){
528 if (((unsigned) cascade_x
> (screen
.size().w() / 2)) ||
529 ((unsigned) cascade_y
> (screen
.size().h() / 2)))
530 cascade_x
= cascade_y
= 32;
532 cascade_x
+= win
->getTitleHeight();
533 cascade_y
+= win
->getTitleHeight();
535 return new Point(cascade_x
, cascade_y
);
539 void Workspace::placeWindow(OpenboxWindow
*win
) {
542 // the following code is temporary and will be taken care of by Screen in the
543 // future (with the NETWM 'strut')
544 Rect
space(0, 0, screen
.size().w(), screen
.size().h());
547 Slit
*slit
= screen
.getSlit();
548 Toolbar
*toolbar
= screen
.getToolbar();
549 int tbarh
= screen
.hideToolbar() ? 0 :
550 toolbar
->getExposedHeight() + screen
.getBorderWidth() * 2;
552 switch (toolbar
->placement()) {
553 case Toolbar::TopLeft
:
554 case Toolbar::TopCenter
:
555 case Toolbar::TopRight
:
558 case Toolbar::BottomLeft
:
559 case Toolbar::BottomCenter
:
560 case Toolbar::BottomRight
:
564 ASSERT(false); // unhandled placement
566 if ((slit
->direction() == Slit::Horizontal
&&
567 (slit
->placement() == Slit::TopLeft
||
568 slit
->placement() == Slit::TopRight
)) ||
569 slit
->placement() == Slit::TopCenter
) {
572 space
.setY(slit
->area().y());
573 space
.setH(space
.h() - space
.y());
575 space
.setH(space
.h() - tbarh
);
576 space
.setY(space
.y() + slit
->area().h() + screen
.getBorderWidth() * 2);
577 space
.setH(space
.h() - (slit
->area().h() + screen
.getBorderWidth() * 2));
578 } else if ((slit
->direction() == Slit::Vertical
&&
579 (slit
->placement() == Slit::TopRight
||
580 slit
->placement() == Slit::BottomRight
)) ||
581 slit
->placement() == Slit::CenterRight
) {
583 space
.setW(space
.w() - (slit
->area().w() + screen
.getBorderWidth() * 2));
585 space
.setY(space
.y() + tbarh
);
586 space
.setH(space
.h() - tbarh
);
587 } else if ((slit
->direction() == Slit::Horizontal
&&
588 (slit
->placement() == Slit::BottomLeft
||
589 slit
->placement() == Slit::BottomRight
)) ||
590 slit
->placement() == Slit::BottomCenter
) {
592 space
.setH(space
.h() - (screen
.size().h() - slit
->area().y()));
593 } else {// if ((slit->direction() == Slit::Vertical &&
594 // (slit->placement() == Slit::TopLeft ||
595 // slit->placement() == Slit::BottomLeft)) ||
596 // slit->placement() == Slit::CenterLeft)
598 space
.setX(slit
->area().w() + screen
.getBorderWidth() * 2);
599 space
.setW(space
.w() - (slit
->area().w() + screen
.getBorderWidth() * 2));
601 space
.setY(space
.y() + tbarh
);
602 space
.setH(space
.h() - tbarh
);
605 Toolbar
*toolbar
= screen
.getToolbar();
606 int tbarh
= screen
.hideToolbar() ? 0 :
607 toolbar
->getExposedHeight() + screen
.getBorderWidth() * 2;
608 switch (toolbar
->placement()) {
609 case Toolbar::TopLeft
:
610 case Toolbar::TopCenter
:
611 case Toolbar::TopRight
:
612 space
.setY(toolbar
->getExposedHeight());
613 space
.setH(space
.h() - toolbar
->getExposedHeight());
615 case Toolbar::BottomLeft
:
616 case Toolbar::BottomCenter
:
617 case Toolbar::BottomRight
:
618 space
.setH(space
.h() - tbarh
);
621 ASSERT(false); // unhandled placement
625 const int win_w
= win
->size().w() + (screen
.getBorderWidth() * 4),
626 win_h
= win
->size().h() + (screen
.getBorderWidth() * 4),
629 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
631 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
632 delta_x
= 8, delta_y
= 8;
634 LinkedListIterator
<OpenboxWindow
> it(windowList
);
636 Size
window_size(win
->size().w()+screen
.getBorderWidth() * 4,
637 win
->size().h()+screen
.getBorderWidth() * 4);
640 switch (screen
.placementPolicy()) {
641 case BScreen::BestFitPlacement
:
642 place
= bestFitPlacement(window_size
, space
);
644 case BScreen::RowSmartPlacement
:
645 place
= rowSmartPlacement(window_size
, space
);
647 case BScreen::ColSmartPlacement
:
648 place
= colSmartPlacement(window_size
, space
);
653 place
= cascadePlacement(win
);
655 ASSERT(place
!= NULL
);
656 if (place
->x() + win_w
> (signed) screen
.size().w())
657 place
->setX(((signed) screen
.size().w() - win_w
) / 2);
658 if (place
->y() + win_h
> (signed) screen
.size().h())
659 place
->setY(((signed) screen
.size().h() - win_h
) / 2);
661 win
->configure(place
->x(), place
->y(), win
->size().w(), win
->size().h());