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
63 typedef std::vector
<Rect
> rectList
;
65 Workspace::Workspace(BScreen
&scrn
, int i
) : screen(scrn
) {
66 cascade_x
= cascade_y
= 0;
67 _focused
= (OpenboxWindow
*) 0;
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().focusWindow((OpenboxWindow
*) 0);
126 OpenboxWindow
*top
= stackingList
->first();
127 if (! top
|| ! top
->setInputFocus()) {
128 screen
.getOpenbox().focusWindow((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::focusWindow(OpenboxWindow
*win
) {
155 if (win
!= (OpenboxWindow
*) 0)
156 clientmenu
->setItemSelected(win
->getWindowNumber(), true);
157 if (_focused
!= (OpenboxWindow
*) 0)
158 clientmenu
->setItemSelected(_focused
->getWindowNumber(), false);
163 void Workspace::showAll(void) {
164 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
165 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
166 bw
->deiconify(False
, False
);
170 void Workspace::hideAll(void) {
171 LinkedList
<OpenboxWindow
> lst
;
173 LinkedListIterator
<OpenboxWindow
> it(stackingList
);
174 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
177 LinkedListIterator
<OpenboxWindow
> it2(&lst
);
178 for (OpenboxWindow
*bw
= it2
.current(); bw
; it2
++, bw
= it2
.current())
184 void Workspace::removeAll(void) {
185 LinkedListIterator
<OpenboxWindow
> it(windowList
);
186 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current())
191 void Workspace::raiseWindow(OpenboxWindow
*w
) {
192 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
194 while (bottom
->isTransient() && bottom
->getTransientFor())
195 bottom
= bottom
->getTransientFor();
199 while (win
->hasTransient() && win
->getTransient()) {
200 win
= win
->getTransient();
205 Window
*nstack
= new Window
[i
], *curr
= nstack
;
210 *(curr
++) = win
->getFrameWindow();
211 screen
.updateNetizenWindowRaise(win
->getClientWindow());
213 if (! win
->isIconic()) {
214 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
215 wkspc
->stackingList
->remove(win
);
216 wkspc
->stackingList
->insert(win
, 0);
219 if (! win
->hasTransient() || ! win
->getTransient())
222 win
= win
->getTransient();
225 screen
.raiseWindows(nstack
, i
);
231 void Workspace::lowerWindow(OpenboxWindow
*w
) {
232 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
234 while (bottom
->isTransient() && bottom
->getTransientFor())
235 bottom
= bottom
->getTransientFor();
239 while (win
->hasTransient() && win
->getTransient()) {
240 win
= win
->getTransient();
245 Window
*nstack
= new Window
[i
], *curr
= nstack
;
249 *(curr
++) = win
->getFrameWindow();
250 screen
.updateNetizenWindowLower(win
->getClientWindow());
252 if (! win
->isIconic()) {
253 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
254 wkspc
->stackingList
->remove(win
);
255 wkspc
->stackingList
->insert(win
);
258 if (! win
->getTransientFor())
261 win
= win
->getTransientFor();
264 screen
.getOpenbox().grab();
266 XLowerWindow(screen
.getBaseDisplay().getXDisplay(), *nstack
);
267 XRestackWindows(screen
.getBaseDisplay().getXDisplay(), nstack
, i
);
269 screen
.getOpenbox().ungrab();
275 void Workspace::reconfigure(void) {
276 clientmenu
->reconfigure();
278 LinkedListIterator
<OpenboxWindow
> it(windowList
);
279 for (OpenboxWindow
*bw
= it
.current(); bw
; it
++, bw
= it
.current()) {
280 if (bw
->validateClient())
286 OpenboxWindow
*Workspace::getWindow(int index
) {
287 if ((index
>= 0) && (index
< windowList
->count()))
288 return windowList
->find(index
);
294 const int Workspace::getCount(void) {
295 return windowList
->count();
299 void Workspace::update(void) {
300 clientmenu
->update();
301 screen
.getToolbar()->redrawWindowLabel(True
);
305 Bool
Workspace::isCurrent(void) {
306 return (id
== screen
.getCurrentWorkspaceID());
310 Bool
Workspace::isLastWindow(OpenboxWindow
*w
) {
311 return (w
== windowList
->last());
314 void Workspace::setCurrent(void) {
315 screen
.changeWorkspaceID(id
);
319 void Workspace::setName(char *new_name
) {
324 name
= bstrdup(new_name
);
326 name
= new char[128];
327 sprintf(name
, i18n
->getMessage(WorkspaceSet
, WorkspaceDefaultNameFormat
,
328 "Workspace %d"), id
+ 1);
331 clientmenu
->setLabel(name
);
332 clientmenu
->update();
333 screen
.saveWorkspaceNames();
337 void Workspace::shutdown(void) {
338 while (windowList
->count()) {
339 windowList
->first()->restore();
340 delete windowList
->first();
344 static rectList
calcSpace(const Rect
&win
, const rectList
&spaces
) {
346 rectList::const_iterator siter
;
347 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
348 if(win
.Intersect(*siter
)) {
349 //Check for space to the left of the window
350 if(win
.x() > siter
->x())
351 result
.push_back(Rect(siter
->x(), siter
->y(),
352 win
.x() - siter
->x() - 1,
354 //Check for space above the window
355 if(win
.y() > siter
->y())
356 result
.push_back(Rect(siter
->x(), siter
->y(),
358 win
.y() - siter
->y() - 1));
359 //Check for space to the right of the window
360 if((win
.x()+win
.w()) <
361 (siter
->x()+siter
->w()))
362 result
.push_back(Rect(win
.x() + win
.w() + 1,
364 siter
->x() + siter
->w() -
365 win
.x() - win
.w() - 1,
367 //Check for space below the window
368 if((win
.y()+win
.h()) <
369 (siter
->y()+siter
->h()))
370 result
.push_back(Rect(siter
->x(),
371 win
.y() + win
.h() + 1,
373 siter
->y() + siter
->h()-
374 win
.y() - win
.h() - 1));
378 result
.push_back(*siter
);
383 bool rowRLBT(const Rect
&first
, const Rect
&second
){
384 if (first
.y()+first
.h()==second
.y()+second
.h())
385 return first
.x()+first
.w()>second
.x()+second
.w();
386 return first
.y()+first
.h()>second
.y()+second
.h();
389 bool rowRLTB(const Rect
&first
, const Rect
&second
){
390 if (first
.y()==second
.y())
391 return first
.x()+first
.w()>second
.x()+second
.w();
392 return first
.y()<second
.y();
395 bool rowLRBT(const Rect
&first
, const Rect
&second
){
396 if (first
.y()+first
.h()==second
.y()+second
.h())
397 return first
.x()<second
.x();
398 return first
.y()+first
.h()>second
.y()+second
.h();
401 bool rowLRTB(const Rect
&first
, const Rect
&second
){
402 if (first
.y()==second
.y())
403 return first
.x()<second
.x();
404 return first
.y()<second
.y();
407 bool colLRTB(const Rect
&first
, const Rect
&second
){
408 if (first
.x()==second
.x())
409 return first
.y()<second
.y();
410 return first
.x()<second
.x();
413 bool colLRBT(const Rect
&first
, const Rect
&second
){
414 if (first
.x()==second
.x())
415 return first
.y()+first
.h()>second
.y()+second
.h();
416 return first
.x()<second
.x();
419 bool colRLTB(const Rect
&first
, const Rect
&second
){
420 if (first
.x()+first
.w()==second
.x()+second
.w())
421 return first
.y()<second
.y();
422 return first
.x()+first
.w()>second
.x()+second
.w();
425 bool colRLBT(const Rect
&first
, const Rect
&second
){
426 if (first
.x()+first
.w()==second
.x()+second
.w())
427 return first
.y()+first
.h()>second
.y()+second
.h();
428 return first
.x()+first
.w()>second
.x()+second
.w();
432 //BestFitPlacement finds the smallest free space that fits the window
433 //to be placed. It currentl ignores whether placement is right to left or top
435 Point
*Workspace::bestFitPlacement(const Size
&win_size
, const Rect
&space
) {
438 LinkedListIterator
<OpenboxWindow
> it(windowList
);
439 rectList::const_iterator siter
;
440 spaces
.push_back(space
); //initially the entire screen is free
444 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
445 spaces
= calcSpace(cur
->area().Inflate(screen
.getBorderWidth() * 4),
448 //Find first space that fits the window
450 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
451 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
454 else if(siter
->w()*siter
->h()<best
->h()*best
->w())
459 Point
*pt
= new Point(best
->origin());
460 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
461 pt
->setY(pt
->y() + (best
->h() - win_size
.h()));
462 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
463 pt
->setX(pt
->x() + (best
->w() - win_size
.w()));
466 return NULL
; //fall back to cascade
469 Point
*Workspace::underMousePlacement(const Size
&win_size
, const Rect
&space
) {
475 XQueryPointer(screen
.getOpenbox().getXDisplay(), screen
.getRootWindow(),
476 &r
, &c
, &rx
, &ry
, &x
, &y
, &m
);
477 pt
= new Point(rx
- win_size
.w() / 2, ry
- win_size
.h() / 2);
479 if (pt
->x() < space
.x())
481 if (pt
->y() < space
.y())
483 if (pt
->x() + win_size
.w() > space
.x() + space
.w())
484 pt
->setX(space
.x() + space
.w() - win_size
.w());
485 if (pt
->y() + win_size
.h() > space
.y() + space
.h())
486 pt
->setY(space
.y() + space
.h() - win_size
.h());
490 Point
*Workspace::rowSmartPlacement(const Size
&win_size
, const Rect
&space
) {
493 LinkedListIterator
<OpenboxWindow
> it(windowList
);
495 rectList::const_iterator siter
;
496 spaces
.push_back(space
); //initially the entire screen is free
500 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
501 spaces
= calcSpace(cur
->area().Inflate(screen
.getBorderWidth() * 4),
503 //Sort spaces by preference
504 if(screen
.rowPlacementDirection() == BScreen::RightLeft
)
505 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
506 sort(spaces
.begin(),spaces
.end(),rowRLTB
);
508 sort(spaces
.begin(),spaces
.end(),rowRLBT
);
510 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
511 sort(spaces
.begin(),spaces
.end(),rowLRTB
);
513 sort(spaces
.begin(),spaces
.end(),rowLRBT
);
515 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
)
516 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
522 Point
*pt
= new Point(best
->origin());
523 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
524 pt
->setY(best
->y() + best
->h() - win_size
.h());
525 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
526 pt
->setX(best
->x()+best
->w()-win_size
.w());
529 return NULL
; //fall back to cascade
532 Point
*Workspace::colSmartPlacement(const Size
&win_size
, const Rect
&space
) {
535 LinkedListIterator
<OpenboxWindow
> it(windowList
);
537 rectList::const_iterator siter
;
538 spaces
.push_back(space
); //initially the entire screen is free
542 for (OpenboxWindow
*cur
=it
.current(); cur
!=NULL
; it
++, cur
=it
.current())
543 spaces
= calcSpace(cur
->area().Inflate(screen
.getBorderWidth() * 4),
545 //Sort spaces by user preference
546 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
547 if(screen
.rowPlacementDirection() == BScreen::LeftRight
)
548 sort(spaces
.begin(),spaces
.end(),colLRTB
);
550 sort(spaces
.begin(),spaces
.end(),colRLTB
);
552 if(screen
.rowPlacementDirection() == BScreen::LeftRight
)
553 sort(spaces
.begin(),spaces
.end(),colLRBT
);
555 sort(spaces
.begin(),spaces
.end(),colRLBT
);
557 //Find first space that fits the window
559 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
)
560 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
566 Point
*pt
= new Point(best
->origin());
567 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
568 pt
->setY(pt
->y() + (best
->h() - win_size
.h()));
569 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
570 pt
->setX(pt
->x() + (best
->w() - win_size
.w()));
573 return NULL
; //fall back to cascade
577 Point
*const Workspace::cascadePlacement(const OpenboxWindow
&win
,
579 if ((cascade_x
+ win
.area().w() + screen
.getBorderWidth() * 2 >
580 (space
.x() + space
.w())) ||
581 (cascade_y
+ win
.area().h() + screen
.getBorderWidth() * 2 >
582 (space
.y() + space
.h())))
583 cascade_x
= cascade_y
= 0;
584 if (cascade_x
< space
.x() || cascade_y
< space
.y()) {
585 cascade_x
= space
.x();
586 cascade_y
= space
.y();
589 Point
*p
= new Point(cascade_x
, cascade_y
);
590 cascade_x
+= win
.getTitleHeight();
591 cascade_y
+= win
.getTitleHeight();
596 void Workspace::placeWindow(OpenboxWindow
&win
) {
597 Rect space
= screen
.availableArea();
598 const Size
window_size(win
.area().w()+screen
.getBorderWidth() * 2,
599 win
.area().h()+screen
.getBorderWidth() * 2);
601 LinkedListIterator
<OpenboxWindow
> it(windowList
);
603 switch (screen
.placementPolicy()) {
604 case BScreen::BestFitPlacement
:
605 place
= bestFitPlacement(window_size
, space
);
607 case BScreen::RowSmartPlacement
:
608 place
= rowSmartPlacement(window_size
, space
);
610 case BScreen::ColSmartPlacement
:
611 place
= colSmartPlacement(window_size
, space
);
613 case BScreen::UnderMousePlacement
:
614 case BScreen::ClickMousePlacement
:
615 place
= underMousePlacement(window_size
, space
);
620 place
= cascadePlacement(win
, space
);
622 ASSERT(place
!= NULL
);
623 if (place
->x() + window_size
.w() > (signed) space
.x() + space
.w())
624 place
->setX(((signed) space
.x() + space
.w() - window_size
.w()) / 2);
625 if (place
->y() + window_size
.h() > (signed) space
.y() + space
.h())
626 place
->setY(((signed) space
.y() + space
.h() - window_size
.h()) / 2);
628 win
.configure(place
->x(), place
->y(), win
.area().w(), win
.area().h());