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 OpenboxWindow
&win
, const rectList
&spaces
) {
337 rectList::const_iterator siter
;
338 for(siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
339 if(win
.area().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
, spaces
);
390 //Find first space that fits the window
392 for (siter
=spaces
.begin(); siter
!=spaces
.end(); ++siter
) {
393 if ((siter
->w() >= win_size
.w()) &&
394 (siter
->h() >= win_size
.h()))
399 return new Point(best
->origin());
401 return NULL
; //fall back to cascade
404 inline Point
*Workspace::rowSmartPlacement(const Size
&win_size
,
407 int test_x
, test_y
, place_x
= 0, place_y
= 0;
410 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
412 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
413 int delta_x
= 8, delta_y
= 8;
414 LinkedListIterator
<OpenboxWindow
> it(windowList
);
416 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
417 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
420 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
421 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())) {
422 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
423 start_pos
: space
.w() - win_size
.w() - start_pos
;
425 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
426 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
430 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
431 it
++, curr
= it
.current()) {
432 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
433 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
435 if (curr
->origin().x() < test_x
+ win_size
.w() &&
436 curr
->origin().x() + curr_w
> test_x
&&
437 curr
->origin().y() < test_y
+ win_size
.h() &&
438 curr
->origin().y() + curr_h
> test_y
) {
443 // Removed code for checking toolbar and slit
444 // The space passed in should not include either
453 test_x
+= (change_x
* delta_x
);
456 test_y
+= (change_y
* delta_y
);
459 return new Point(place_x
, place_y
);
461 return NULL
; // fall back to cascade
464 inline Point
* Workspace::colSmartPlacement(const Size
&win_size
,
471 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1);
473 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1);
474 int delta_x
= 8, delta_y
= 8;
475 LinkedListIterator
<OpenboxWindow
> it(windowList
);
477 test_x
= (screen
.rowPlacementDirection() == BScreen::LeftRight
) ?
478 start_pos
: screen
.size().w() - win_size
.w() - start_pos
;
481 ((screen
.rowPlacementDirection() == BScreen::RightLeft
) ?
482 test_x
> 0 : test_x
+ win_size
.w() < (signed) space
.w())) {
483 test_y
= (screen
.colPlacementDirection() == BScreen::TopBottom
) ?
484 start_pos
: screen
.size().h() - win_size
.h() - start_pos
;
487 ((screen
.colPlacementDirection() == BScreen::BottomTop
) ?
488 test_y
> 0 : test_y
+ win_size
.h() < (signed) space
.h())){
493 for (OpenboxWindow
*curr
= it
.current(); placed
&& curr
;
494 it
++, curr
= it
.current()) {
495 int curr_w
= curr
->size().w() + (screen
.getBorderWidth() * 4);
496 int curr_h
= curr
->size().h() + (screen
.getBorderWidth() * 4);
498 if (curr
->origin().x() < test_x
+ win_size
.w() &&
499 curr
->origin().x() + curr_w
> test_x
&&
500 curr
->origin().y() < test_y
+ win_size
.h() &&
501 curr
->origin().y() + curr_h
> test_y
) {
506 // Removed code checking for intersection with Toolbar and Slit
507 // The space passed to this method should not include either
510 pt
= new Point(test_x
,test_y
);
515 test_y
+= (change_y
* delta_y
);
518 test_x
+= (change_x
* delta_x
);
526 Point
*const Workspace::cascadePlacement(const OpenboxWindow
*const win
){
527 if (((unsigned) cascade_x
> (screen
.size().w() / 2)) ||
528 ((unsigned) cascade_y
> (screen
.size().h() / 2)))
529 cascade_x
= cascade_y
= 32;
531 cascade_x
+= win
->getTitleHeight();
532 cascade_y
+= win
->getTitleHeight();
534 return new Point(cascade_x
, cascade_y
);
538 void Workspace::placeWindow(OpenboxWindow
*win
) {
541 const int win_w
= win
->size().w() + (screen
.getBorderWidth() * 4),
542 win_h
= win
->size().h() + (screen
.getBorderWidth() * 4),
544 slit_x
= screen
.getSlit()->area().x() - screen
.getBorderWidth(),
545 slit_y
= screen
.getSlit()->area().y() - screen
.getBorderWidth(),
546 slit_w
= screen
.getSlit()->area().w() +
547 (screen
.getBorderWidth() * 4),
548 slit_h
= screen
.getSlit()->area().h() +
549 (screen
.getBorderWidth() * 4),
551 toolbar_x
= screen
.getToolbar()->getX() - screen
.getBorderWidth(),
552 toolbar_y
= screen
.getToolbar()->getY() - screen
.getBorderWidth(),
553 toolbar_w
= screen
.getToolbar()->getWidth() +
554 (screen
.getBorderWidth() * 4),
555 toolbar_h
= screen
.getToolbar()->getHeight() +
556 (screen
.getBorderWidth() * 4),
559 ((screen
.colPlacementDirection() == BScreen::TopBottom
) ? 1 : -1),
561 ((screen
.rowPlacementDirection() == BScreen::LeftRight
) ? 1 : -1),
562 delta_x
= 8, delta_y
= 8;
564 LinkedListIterator
<OpenboxWindow
> it(windowList
);
570 Size
window_size(win
->size().w()+screen
.getBorderWidth() * 4,
571 win
->size().h()+screen
.getBorderWidth() * 4);
574 switch (screen
.placementPolicy()) {
575 case BScreen::BestFitPlacement
:
576 place
= bestFitPlacement(window_size
, space
);
578 case BScreen::RowSmartPlacement
:
579 place
= rowSmartPlacement(window_size
, space
);
581 case BScreen::ColSmartPlacement
:
582 place
= colSmartPlacement(window_size
, space
);
587 place
= cascadePlacement(win
);
589 ASSERT(place
!= NULL
);
590 if (place
->x() + win_w
> (signed) screen
.size().w())
591 place
->setX(((signed) screen
.size().w() - win_w
) / 2);
592 if (place
->y() + win_h
> (signed) screen
.size().h())
593 place
->setY(((signed) screen
.size().h() - win_h
) / 2);
595 win
->configure(place
->x(), place
->y(), win
->size().w(), win
->size().h());