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
36 #endif // HAVE_STDIO_H
40 #endif // HAVE_STDLIB_H
44 #endif // HAVE_STRING_H
47 #include <X11/Xatom.h>
51 #include "Clientmenu.h"
55 #include "Workspace.h"
56 #include "Windowmenu.h"
62 typedef std::vector
<Rect
> rectList
;
64 Workspace::Workspace(BScreen
&scrn
, int i
) : screen(scrn
) {
65 cascade_x
= cascade_y
= 0;
66 _focused
= _last
= (OpenboxWindow
*) 0;
69 clientmenu
= new Clientmenu(*this);
72 setName(screen
.getNameOfWorkspace(id
));
76 Workspace::~Workspace(void) {
84 int Workspace::addWindow(OpenboxWindow
*w
, bool place
) {
87 if (place
) placeWindow(*w
);
90 w
->setWindowNumber(_windows
.size());
92 _zorder
.push_front(w
);
93 _windows
.push_back(w
);
95 clientmenu
->insert((const char **) w
->getTitle());
98 screen
.updateNetizenWindowAdd(w
->getClientWindow(), id
);
102 return w
->getWindowNumber();
106 int Workspace::removeWindow(OpenboxWindow
*w
) {
109 winVect::iterator winit
= std::find(_windows
.begin(), _windows
.end(), w
);
111 if (winit
== _windows
.end()) {
113 _last
= (OpenboxWindow
*) 0;
115 _focused
= (OpenboxWindow
*) 0;
116 return _windows
.size();
122 _last
= (OpenboxWindow
*) 0;
124 OpenboxWindow
*fw
= (OpenboxWindow
*) 0;
125 if (w
->isTransient() && w
->getTransientFor() &&
126 w
->getTransientFor()->isVisible())
127 fw
= w
->getTransientFor();
128 else if (screen
.sloppyFocus()) // sloppy focus
129 fw
= (OpenboxWindow
*) 0;
130 else if (!_zorder
.empty()) // click focus
131 fw
= _zorder
.front();
133 if (!(fw
!= (OpenboxWindow
*) 0 && fw
->setInputFocus()))
134 screen
.getOpenbox().focusWindow(0);
137 _windows
.erase(winit
);
138 clientmenu
->remove(w
->getWindowNumber());
139 clientmenu
->update();
141 screen
.updateNetizenWindowDel(w
->getClientWindow());
143 winVect::iterator it
= _windows
.begin();
144 for (int i
=0; it
!= _windows
.end(); ++it
, ++i
)
145 (*it
)->setWindowNumber(i
);
147 return _windows
.size();
151 void Workspace::focusWindow(OpenboxWindow
*win
) {
152 if (_focused
!= (OpenboxWindow
*) 0)
153 clientmenu
->setItemSelected(_focused
->getWindowNumber(), false);
155 // make sure the focused window belongs to this workspace before highlighting
156 // it in the menu (sticky windows arent in this workspace's menu).
157 if (_focused
!= (OpenboxWindow
*) 0 && _focused
->getWorkspaceNumber() == id
)
158 clientmenu
->setItemSelected(_focused
->getWindowNumber(), true);
159 if (win
!= (OpenboxWindow
*) 0)
164 void Workspace::showAll(void) {
165 winList::iterator it
;
166 for (it
= _zorder
.begin(); it
!= _zorder
.end(); ++it
)
167 (*it
)->deiconify(false, false);
171 void Workspace::hideAll(void) {
172 winList::reverse_iterator it
;
173 for (it
= _zorder
.rbegin(); it
!= _zorder
.rend(); ++it
)
174 if (!(*it
)->isStuck())
179 void Workspace::removeAll(void) {
180 winVect::iterator it
;
181 for (it
= _windows
.begin(); it
!= _windows
.end(); ++it
)
186 void Workspace::raiseWindow(OpenboxWindow
*w
) {
187 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
189 while (bottom
->isTransient() && bottom
->getTransientFor())
190 bottom
= bottom
->getTransientFor();
194 while (win
->hasTransient() && win
->getTransient()) {
195 win
= win
->getTransient();
200 Window
*nstack
= new Window
[i
], *curr
= nstack
;
205 *(curr
++) = win
->getFrameWindow();
206 screen
.updateNetizenWindowRaise(win
->getClientWindow());
208 if (! win
->isIconic()) {
209 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
210 wkspc
->_zorder
.remove(win
);
211 wkspc
->_zorder
.push_front(win
);
214 if (! win
->hasTransient() || ! win
->getTransient())
217 win
= win
->getTransient();
220 screen
.raiseWindows(nstack
, i
);
226 void Workspace::lowerWindow(OpenboxWindow
*w
) {
227 OpenboxWindow
*win
= (OpenboxWindow
*) 0, *bottom
= w
;
229 while (bottom
->isTransient() && bottom
->getTransientFor())
230 bottom
= bottom
->getTransientFor();
234 while (win
->hasTransient() && win
->getTransient()) {
235 win
= win
->getTransient();
240 Window
*nstack
= new Window
[i
], *curr
= nstack
;
244 *(curr
++) = win
->getFrameWindow();
245 screen
.updateNetizenWindowLower(win
->getClientWindow());
247 if (! win
->isIconic()) {
248 wkspc
= screen
.getWorkspace(win
->getWorkspaceNumber());
249 wkspc
->_zorder
.remove(win
);
250 wkspc
->_zorder
.push_back(win
);
253 if (! win
->getTransientFor())
256 win
= win
->getTransientFor();
259 screen
.getOpenbox().grab();
261 XLowerWindow(screen
.getBaseDisplay().getXDisplay(), *nstack
);
262 XRestackWindows(screen
.getBaseDisplay().getXDisplay(), nstack
, i
);
264 screen
.getOpenbox().ungrab();
270 void Workspace::reconfigure(void) {
271 clientmenu
->reconfigure();
273 winVect::iterator it
;
274 for (it
= _windows
.begin(); it
!= _windows
.end(); ++it
)
275 if ((*it
)->validateClient())
276 (*it
)->reconfigure();
280 OpenboxWindow
*Workspace::getWindow(int index
) {
281 if ((index
>= 0) && (index
< (signed)_windows
.size()))
282 return _windows
[index
];
284 return (OpenboxWindow
*) 0;
288 int Workspace::getCount(void) {
289 return (signed)_windows
.size();
293 void Workspace::update(void) {
294 clientmenu
->update();
295 screen
.getToolbar()->redrawWindowLabel(true);
299 bool Workspace::isCurrent(void) {
300 return (id
== screen
.getCurrentWorkspaceID());
304 void Workspace::setCurrent(void) {
305 screen
.changeWorkspaceID(id
);
309 void Workspace::setName(const char *new_name
) {
314 name
= bstrdup(new_name
);
316 name
= new char[128];
317 sprintf(name
, i18n(WorkspaceSet
, WorkspaceDefaultNameFormat
,
318 "Workspace %d"), id
+ 1);
321 clientmenu
->setLabel(name
);
322 clientmenu
->update();
323 screen
.saveWorkspaceNames();
327 void Workspace::shutdown(void) {
328 while (!_windows
.empty())
329 _windows
[0]->restore();
332 static rectList
calcSpace(const Rect
&win
, const rectList
&spaces
) {
334 rectList::const_iterator siter
;
335 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
336 if(win
.Intersect(*siter
)) {
337 //Check for space to the left of the window
338 if(win
.x() > siter
->x())
339 result
.push_back(Rect(siter
->x(), siter
->y(),
340 win
.x() - siter
->x() - 1,
342 //Check for space above the window
343 if(win
.y() > siter
->y())
344 result
.push_back(Rect(siter
->x(), siter
->y(),
346 win
.y() - siter
->y() - 1));
347 //Check for space to the right of the window
348 if((win
.x()+win
.w()) <
349 (siter
->x()+siter
->w()))
350 result
.push_back(Rect(win
.x() + win
.w() + 1,
352 siter
->x() + siter
->w() -
353 win
.x() - win
.w() - 1,
355 //Check for space below the window
356 if((win
.y()+win
.h()) <
357 (siter
->y()+siter
->h()))
358 result
.push_back(Rect(siter
->x(),
359 win
.y() + win
.h() + 1,
361 siter
->y() + siter
->h()-
362 win
.y() - win
.h() - 1));
366 result
.push_back(*siter
);
371 bool rowRLBT(const Rect
&first
, const Rect
&second
){
372 if (first
.y()+first
.h()==second
.y()+second
.h())
373 return first
.x()+first
.w()>second
.x()+second
.w();
374 return first
.y()+first
.h()>second
.y()+second
.h();
377 bool rowRLTB(const Rect
&first
, const Rect
&second
){
378 if (first
.y()==second
.y())
379 return first
.x()+first
.w()>second
.x()+second
.w();
380 return first
.y()<second
.y();
383 bool rowLRBT(const Rect
&first
, const Rect
&second
){
384 if (first
.y()+first
.h()==second
.y()+second
.h())
385 return first
.x()<second
.x();
386 return first
.y()+first
.h()>second
.y()+second
.h();
389 bool rowLRTB(const Rect
&first
, const Rect
&second
){
390 if (first
.y()==second
.y())
391 return first
.x()<second
.x();
392 return first
.y()<second
.y();
395 bool colLRTB(const Rect
&first
, const Rect
&second
){
396 if (first
.x()==second
.x())
397 return first
.y()<second
.y();
398 return first
.x()<second
.x();
401 bool colLRBT(const Rect
&first
, const Rect
&second
){
402 if (first
.x()==second
.x())
403 return first
.y()+first
.h()>second
.y()+second
.h();
404 return first
.x()<second
.x();
407 bool colRLTB(const Rect
&first
, const Rect
&second
){
408 if (first
.x()+first
.w()==second
.x()+second
.w())
409 return first
.y()<second
.y();
410 return first
.x()+first
.w()>second
.x()+second
.w();
413 bool colRLBT(const Rect
&first
, const Rect
&second
){
414 if (first
.x()+first
.w()==second
.x()+second
.w())
415 return first
.y()+first
.h()>second
.y()+second
.h();
416 return first
.x()+first
.w()>second
.x()+second
.w();
420 //BestFitPlacement finds the smallest free space that fits the window
421 //to be placed. It currentl ignores whether placement is right to left or top
423 Point
*Workspace::bestFitPlacement(const Size
&win_size
, const Rect
&space
) {
426 rectList::const_iterator siter
;
427 spaces
.push_back(space
); //initially the entire screen is free
430 winVect::iterator it
;
431 for (it
= _windows
.begin(); it
!= _windows
.end(); ++it
)
432 spaces
= calcSpace((*it
)->area().Inflate(screen
.getBorderWidth() * 4),
435 //Find first space that fits the window
437 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
438 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
441 else if(siter
->w()*siter
->h()<best
->h()*best
->w())
446 Point
*pt
= new Point(best
->origin());
447 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
448 pt
->setY(pt
->y() + (best
->h() - win_size
.h()));
449 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
450 pt
->setX(pt
->x() + (best
->w() - win_size
.w()));
453 return NULL
; //fall back to cascade
456 Point
*Workspace::underMousePlacement(const Size
&win_size
, const Rect
&space
) {
462 XQueryPointer(screen
.getOpenbox().getXDisplay(), screen
.getRootWindow(),
463 &r
, &c
, &rx
, &ry
, &x
, &y
, &m
);
464 pt
= new Point(rx
- win_size
.w() / 2, ry
- win_size
.h() / 2);
466 if (pt
->x() < space
.x())
468 if (pt
->y() < space
.y())
470 if (pt
->x() + win_size
.w() > space
.x() + space
.w())
471 pt
->setX(space
.x() + space
.w() - win_size
.w());
472 if (pt
->y() + win_size
.h() > space
.y() + space
.h())
473 pt
->setY(space
.y() + space
.h() - win_size
.h());
477 Point
*Workspace::rowSmartPlacement(const Size
&win_size
, const Rect
&space
) {
481 rectList::const_iterator siter
;
482 spaces
.push_back(space
); //initially the entire screen is free
485 winVect::iterator it
;
486 for (it
= _windows
.begin(); it
!= _windows
.end(); ++it
)
487 spaces
= calcSpace((*it
)->area().Inflate(screen
.getBorderWidth() * 4),
489 //Sort spaces by preference
490 if(screen
.rowPlacementDirection() == BScreen::RightLeft
)
491 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
492 sort(spaces
.begin(),spaces
.end(),rowRLTB
);
494 sort(spaces
.begin(),spaces
.end(),rowRLBT
);
496 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
497 sort(spaces
.begin(),spaces
.end(),rowLRTB
);
499 sort(spaces
.begin(),spaces
.end(),rowLRBT
);
501 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
)
502 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
508 Point
*pt
= new Point(best
->origin());
509 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
510 pt
->setY(best
->y() + best
->h() - win_size
.h());
511 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
512 pt
->setX(best
->x()+best
->w()-win_size
.w());
515 return NULL
; //fall back to cascade
518 Point
*Workspace::colSmartPlacement(const Size
&win_size
, const Rect
&space
) {
522 rectList::const_iterator siter
;
523 spaces
.push_back(space
); //initially the entire screen is free
526 winVect::iterator it
;
527 for (it
= _windows
.begin(); it
!= _windows
.end(); ++it
)
528 spaces
= calcSpace((*it
)->area().Inflate(screen
.getBorderWidth() * 4),
530 //Sort spaces by user preference
531 if(screen
.colPlacementDirection() == BScreen::TopBottom
)
532 if(screen
.rowPlacementDirection() == BScreen::LeftRight
)
533 sort(spaces
.begin(),spaces
.end(),colLRTB
);
535 sort(spaces
.begin(),spaces
.end(),colRLTB
);
537 if(screen
.rowPlacementDirection() == BScreen::LeftRight
)
538 sort(spaces
.begin(),spaces
.end(),colLRBT
);
540 sort(spaces
.begin(),spaces
.end(),colRLBT
);
542 //Find first space that fits the window
544 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
)
545 if ((siter
->w() >= win_size
.w()) && (siter
->h() >= win_size
.h())) {
551 Point
*pt
= new Point(best
->origin());
552 if (screen
.colPlacementDirection() != BScreen::TopBottom
)
553 pt
->setY(pt
->y() + (best
->h() - win_size
.h()));
554 if (screen
.rowPlacementDirection() != BScreen::LeftRight
)
555 pt
->setX(pt
->x() + (best
->w() - win_size
.w()));
558 return NULL
; //fall back to cascade
562 Point
*const Workspace::cascadePlacement(const OpenboxWindow
&win
,
564 if ((cascade_x
+ win
.area().w() + screen
.getBorderWidth() * 2 >
565 (space
.x() + space
.w())) ||
566 (cascade_y
+ win
.area().h() + screen
.getBorderWidth() * 2 >
567 (space
.y() + space
.h())))
568 cascade_x
= cascade_y
= 0;
569 if (cascade_x
< space
.x() || cascade_y
< space
.y()) {
570 cascade_x
= space
.x();
571 cascade_y
= space
.y();
574 Point
*p
= new Point(cascade_x
, cascade_y
);
575 cascade_x
+= win
.getTitleHeight();
576 cascade_y
+= win
.getTitleHeight();
581 void Workspace::placeWindow(OpenboxWindow
&win
) {
582 Rect space
= screen
.availableArea();
583 const Size
window_size(win
.area().w()+screen
.getBorderWidth() * 2,
584 win
.area().h()+screen
.getBorderWidth() * 2);
587 switch (screen
.placementPolicy()) {
588 case BScreen::BestFitPlacement
:
589 place
= bestFitPlacement(window_size
, space
);
591 case BScreen::RowSmartPlacement
:
592 place
= rowSmartPlacement(window_size
, space
);
594 case BScreen::ColSmartPlacement
:
595 place
= colSmartPlacement(window_size
, space
);
597 case BScreen::UnderMousePlacement
:
598 case BScreen::ClickMousePlacement
:
599 place
= underMousePlacement(window_size
, space
);
604 place
= cascadePlacement(win
, space
);
606 ASSERT(place
!= NULL
);
607 if (place
->x() + window_size
.w() > (signed) space
.x() + space
.w())
608 place
->setX(((signed) space
.x() + space
.w() - window_size
.w()) / 2);
609 if (place
->y() + window_size
.h() > (signed) space
.y() + space
.h())
610 place
->setY(((signed) space
.y() + space
.h() - window_size
.h()) / 2);
612 win
.configure(place
->x(), place
->y(), win
.area().w(), win
.area().h());