X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2FWorkspace.cc;h=11d5187ddf35512a8a9cfbc69fecff71d3f16c91;hb=b016470119788a052ef1de0457e2e8d104e90d81;hp=47ad3827f2ec2ea55053f088308c1f6c301f5549;hpb=67b4df1eff614c79bca956615a514bb5bb211af8;p=chaz%2Fopenbox diff --git a/src/Workspace.cc b/src/Workspace.cc index 47ad3827..11d5187d 100644 --- a/src/Workspace.cc +++ b/src/Workspace.cc @@ -1,6 +1,6 @@ -// Workspace.cc for Openbox -// Copyright (c) 2002 - 2002 Ben Jansens (ben@orodu.net) -// Copyright (c) 2001 Sean 'Shaleh' Perry +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +// Workspace.cc for Blackbox - an X11 Window manager +// Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net) // // Permission is hereby granted, free of charge, to any person obtaining a @@ -21,594 +21,820 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// stupid macros needed to access some functions in version 2 of the GNU C -// library -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - #ifdef HAVE_CONFIG_H # include "../config.h" #endif // HAVE_CONFIG_H +extern "C" { +#include +#include + #ifdef HAVE_STDIO_H # include #endif // HAVE_STDIO_H -#ifdef HAVE_STDLIB_H -# include -#endif // HAVE_STDLIB_H - -#ifdef HAVE_STRING_H +#ifdef HAVE_STRING_H # include #endif // HAVE_STRING_H +} -#include -#include +#include -#include "i18n.h" -#include "openbox.h" -#include "Clientmenu.h" -#include "Screen.h" -#include "Toolbar.h" -#include "Window.h" -#include "Workspace.h" -#include "Windowmenu.h" -#include "Geometry.h" -#include "Util.h" - -#include -#include -typedef std::vector rectList; +#include +#include -Workspace::Workspace(BScreen &scrn, int i) : screen(scrn) { - cascade_x = cascade_y = 0; - _focused = _last = (OpenboxWindow *) 0; - id = i; +using std::string; - clientmenu = new Clientmenu(*this); +#include "i18n.hh" +#include "blackbox.hh" +#include "Clientmenu.hh" +#include "Font.hh" +#include "Netizen.hh" +#include "Screen.hh" +#include "Toolbar.hh" +#include "Util.hh" +#include "Window.hh" +#include "Workspace.hh" +#include "Windowmenu.hh" +#include "XAtom.hh" - name = (char *) 0; - setName(screen.getNameOfWorkspace(id)); -} +Workspace::Workspace(BScreen *scrn, unsigned int i) { + screen = scrn; + xatom = screen->getBlackbox()->getXAtom(); -Workspace::~Workspace(void) { - delete clientmenu; + cascade_x = cascade_y = 0; +#ifdef XINERAMA + cascade_region = 0; +#endif // XINERAMA - if (name) - delete [] name; + id = i; + + clientmenu = new Clientmenu(this); + + lastfocus = (BlackboxWindow *) 0; + + readName(); } -int Workspace::addWindow(OpenboxWindow *w, bool place) { - if (! w) return -1; +void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) { + assert(w != 0); + + if (place) placeWindow(w); - if (place) placeWindow(*w); + stackingList.push_front(w); - w->setWorkspace(id); - w->setWindowNumber(_windows.size()); + if (w->isNormal()) { + if (! sticky) { + w->setWorkspace(id); + w->setWindowNumber(windowList.size()); + } - _zorder.push_front(w); - _windows.push_back(w); + windowList.push_back(w); - clientmenu->insert((const char **) w->getTitle()); - clientmenu->update(); + clientmenu->insert(w->getTitle()); + clientmenu->update(); - screen.updateNetizenWindowAdd(w->getClientWindow(), id); + if (! sticky) + screen->updateNetizenWindowAdd(w->getClientWindow(), id); - raiseWindow(w); + if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() && + w->getTransientFor()->isFocused())) { + if (id != screen->getCurrentWorkspaceID()) { + /* + not on the focused workspace, so the window is not going to get focus + but if the user wants new windows focused, then it should get focus + when this workspace does become focused. + */ + lastfocus = w; + } + } + } - return w->getWindowNumber(); + if (! w->isDesktop()) + raiseWindow(w); + else + lowerWindow(w); } -int Workspace::removeWindow(OpenboxWindow *w) { - if (! w) return -1; +void Workspace::removeWindow(BlackboxWindow *w, bool sticky) { + assert(w != 0); - winVect::iterator winit = std::find(_windows.begin(), _windows.end(), w); + stackingList.remove(w); - if (winit == _windows.end()) { - if (w == _last) - _last = (OpenboxWindow *) 0; - if (w == _focused) - _focused = (OpenboxWindow *) 0; - return _windows.size(); - } - - _zorder.remove(w); - - if (w == _last) - _last = (OpenboxWindow *) 0; - if (w == _focused) { - OpenboxWindow *fw = (OpenboxWindow *) 0; - if (w->isTransient() && w->getTransientFor() && - w->getTransientFor()->isVisible()) - fw = w->getTransientFor(); - else if (screen.sloppyFocus()) // sloppy focus - fw = (OpenboxWindow *) 0; - else if (!_zorder.empty()) // click focus - fw = _zorder.front(); - - if (!(fw != (OpenboxWindow *) 0 && fw->setInputFocus())) - screen.getOpenbox().focusWindow(0); + // pass focus to the next appropriate window + if ((w->isFocused() || w == lastfocus) && + ! screen->getBlackbox()->doShutdown()) { + focusFallback(w); } + + if (! w->isNormal()) return; + + BlackboxWindowList::iterator it, end = windowList.end(); + int i; + for (i = 0, it = windowList.begin(); it != end; ++it, ++i) + if (*it == w) + break; + assert(it != end); - _windows.erase(winit); - clientmenu->remove(w->getWindowNumber()); + windowList.erase(it); + clientmenu->remove(i); clientmenu->update(); - screen.updateNetizenWindowDel(w->getClientWindow()); + if (! sticky) { + screen->updateNetizenWindowDel(w->getClientWindow()); - winVect::iterator it = _windows.begin(); - for (int i=0; it != _windows.end(); ++it, ++i) - (*it)->setWindowNumber(i); + BlackboxWindowList::iterator it = windowList.begin(); + const BlackboxWindowList::iterator end = windowList.end(); + unsigned int i = 0; + for (; it != end; ++it, ++i) + (*it)->setWindowNumber(i); + } - return _windows.size(); + if (i == 0) { + cascade_x = cascade_y = 0; +#ifdef XINERAMA + cascade_region = 0; +#endif // XINERAMA + } } -void Workspace::focusWindow(OpenboxWindow *win) { - if (_focused != (OpenboxWindow *) 0) - clientmenu->setItemSelected(_focused->getWindowNumber(), false); - _focused = win; - // make sure the focused window belongs to this workspace before highlighting - // it in the menu (sticky windows arent in this workspace's menu). - if (_focused != (OpenboxWindow *) 0 && _focused->getWorkspaceNumber() == id) - clientmenu->setItemSelected(_focused->getWindowNumber(), true); - if (win != (OpenboxWindow *) 0) - _last = win; -} +void Workspace::focusFallback(const BlackboxWindow *old_window) { + BlackboxWindow *newfocus = 0; + + if (id == screen->getCurrentWorkspaceID()) { + // The window is on the visible workspace. + // if it's a transient, then try to focus its parent + if (old_window && old_window->isTransient()) { + newfocus = old_window->getTransientFor(); + + if (! newfocus || + newfocus->isIconic() || // do not focus icons + newfocus->getWorkspaceNumber() != id || // or other workspaces + ! newfocus->setInputFocus()) + newfocus = 0; + } + + if (! newfocus) { + BlackboxWindowList::iterator it = stackingList.begin(), + end = stackingList.end(); + for (; it != end; ++it) { + BlackboxWindow *tmp = *it; + if (tmp && tmp->isNormal() && tmp->setInputFocus()) { + // we found our new focus target + newfocus = tmp; + break; + } + } + } -void Workspace::showAll(void) { - winList::iterator it; - for (it = _zorder.begin(); it != _zorder.end(); ++it) - (*it)->deiconify(false, false); + screen->getBlackbox()->setFocusedWindow(newfocus); + } else { + // The window is not on the visible workspace. + + if (old_window && lastfocus == old_window) { + // The window was the last-focus target, so we need to replace it. + BlackboxWindow *win = (BlackboxWindow*) 0; + if (! stackingList.empty()) + win = stackingList.front(); + setLastFocusedWindow(win); + } + } } -void Workspace::hideAll(void) { - winList::reverse_iterator it; - for (it = _zorder.rbegin(); it != _zorder.rend(); ++it) - if (!(*it)->isStuck()) - (*it)->withdraw(); +void Workspace::setFocused(const BlackboxWindow *w, bool focused) { + BlackboxWindowList::iterator it, end = windowList.end(); + int i; + for (i = 0, it = windowList.begin(); it != end; ++it, ++i) + if (*it == w) + break; + // if its == end, then a window thats not in the windowList + // got focused, such as a !isNormal() window. + if (it != end) + clientmenu->setItemSelected(i, focused); } void Workspace::removeAll(void) { - winVect::iterator it; - for (it = _windows.begin(); it != _windows.end(); ++it) - (*it)->iconify(); + while (! windowList.empty()) + windowList.front()->iconify(); +} + + +/* + * returns the number of transients for win, plus the number of transients + * associated with each transient of win + */ +static unsigned int countTransients(const BlackboxWindow * const win) { + BlackboxWindowList transients = win->getTransients(); + if (transients.empty()) return 0; + + unsigned int ret = transients.size(); + BlackboxWindowList::const_iterator it = transients.begin(), + end = transients.end(); + for (; it != end; ++it) + ret += countTransients(*it); + + return ret; +} + + +/* + * puts the transients of win into the stack. windows are stacked above + * the window before it in the stackvector being iterated, meaning + * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above + * stack[1], etc... + */ +void Workspace::raiseTransients(const BlackboxWindow * const win, + StackVector::iterator &stack) { + if (win->getTransients().empty()) return; // nothing to do + + // put win's transients in the stack + BlackboxWindowList::const_iterator it, end = win->getTransients().end(); + for (it = win->getTransients().begin(); it != end; ++it) { + BlackboxWindow *w = *it; + *stack++ = w->getFrameWindow(); + screen->updateNetizenWindowRaise(w->getClientWindow()); + + if (! w->isIconic()) { + Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber()); + wkspc->stackingList.remove(w); + wkspc->stackingList.push_front(w); + } + } + + // put transients of win's transients in the stack + for (it = win->getTransients().begin(); it != end; ++it) + raiseTransients(*it, stack); } -void Workspace::raiseWindow(OpenboxWindow *w) { - OpenboxWindow *win = (OpenboxWindow *) 0, *bottom = w; +void Workspace::lowerTransients(const BlackboxWindow * const win, + StackVector::iterator &stack) { + if (win->getTransients().empty()) return; // nothing to do - while (bottom->isTransient() && bottom->getTransientFor()) - bottom = bottom->getTransientFor(); + // put transients of win's transients in the stack + BlackboxWindowList::const_reverse_iterator it, + end = win->getTransients().rend(); + for (it = win->getTransients().rbegin(); it != end; ++it) + lowerTransients(*it, stack); - int i = 1; - win = bottom; - while (win->hasTransient() && win->getTransient()) { - win = win->getTransient(); + // put win's transients in the stack + for (it = win->getTransients().rbegin(); it != end; ++it) { + BlackboxWindow *w = *it; + *stack++ = w->getFrameWindow(); + screen->updateNetizenWindowLower(w->getClientWindow()); - i++; + if (! w->isIconic()) { + Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber()); + wkspc->stackingList.remove(w); + wkspc->stackingList.push_back(w); + } } +} - Window *nstack = new Window[i], *curr = nstack; - Workspace *wkspc; - win = bottom; - while (true) { - *(curr++) = win->getFrameWindow(); - screen.updateNetizenWindowRaise(win->getClientWindow()); +void Workspace::raiseWindow(BlackboxWindow *w) { + BlackboxWindow *win = w; - if (! win->isIconic()) { - wkspc = screen.getWorkspace(win->getWorkspaceNumber()); - wkspc->_zorder.remove(win); - wkspc->_zorder.push_front(win); - } + if (win->isDesktop()) return; - if (! win->hasTransient() || ! win->getTransient()) - break; + // walk up the transient_for's to the window that is not a transient + while (win->isTransient() && win->getTransientFor()) + win = win->getTransientFor(); + + // get the total window count (win and all transients) + unsigned int i = 1 + countTransients(win); + + // stack the window with all transients above + StackVector stack_vector(i); + StackVector::iterator stack = stack_vector.begin(); - win = win->getTransient(); + *(stack++) = win->getFrameWindow(); + screen->updateNetizenWindowRaise(win->getClientWindow()); + if (! (win->isIconic() || win->isDesktop())) { + Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber()); + wkspc->stackingList.remove(win); + wkspc->stackingList.push_front(win); } - screen.raiseWindows(nstack, i); + raiseTransients(win, stack); - delete [] nstack; + screen->raiseWindows(&stack_vector[0], stack_vector.size()); } -void Workspace::lowerWindow(OpenboxWindow *w) { - OpenboxWindow *win = (OpenboxWindow *) 0, *bottom = w; +void Workspace::lowerWindow(BlackboxWindow *w) { + BlackboxWindow *win = w; - while (bottom->isTransient() && bottom->getTransientFor()) - bottom = bottom->getTransientFor(); + // walk up the transient_for's to the window that is not a transient + while (win->isTransient() && win->getTransientFor()) + win = win->getTransientFor(); + + // get the total window count (win and all transients) + unsigned int i = 1 + countTransients(win); - int i = 1; - win = bottom; - while (win->hasTransient() && win->getTransient()) { - win = win->getTransient(); + // stack the window with all transients above + StackVector stack_vector(i); + StackVector::iterator stack = stack_vector.begin(); - i++; + lowerTransients(win, stack); + + *(stack++) = win->getFrameWindow(); + screen->updateNetizenWindowLower(win->getClientWindow()); + if (! (win->isIconic() || win->isDesktop())) { + Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber()); + wkspc->stackingList.remove(win); + wkspc->stackingList.push_back(win); } - Window *nstack = new Window[i], *curr = nstack; - Workspace *wkspc; + screen->lowerWindows(&stack_vector[0], stack_vector.size()); +} - while (true) { - *(curr++) = win->getFrameWindow(); - screen.updateNetizenWindowLower(win->getClientWindow()); - if (! win->isIconic()) { - wkspc = screen.getWorkspace(win->getWorkspaceNumber()); - wkspc->_zorder.remove(win); - wkspc->_zorder.push_back(win); - } +void Workspace::reconfigure(void) { + clientmenu->reconfigure(); + std::for_each(windowList.begin(), windowList.end(), + std::mem_fun(&BlackboxWindow::reconfigure)); +} - if (! win->getTransientFor()) - break; - win = win->getTransientFor(); +BlackboxWindow *Workspace::getWindow(unsigned int index) { + if (index < windowList.size()) { + BlackboxWindowList::iterator it = windowList.begin(); + while (index-- > 0) // increment to index + ++it; + return *it; } - screen.getOpenbox().grab(); + return 0; +} - XLowerWindow(screen.getBaseDisplay().getXDisplay(), *nstack); - XRestackWindows(screen.getBaseDisplay().getXDisplay(), nstack, i); - screen.getOpenbox().ungrab(); +BlackboxWindow* +Workspace::getNextWindowInList(BlackboxWindow *w) { + BlackboxWindowList::iterator it = std::find(windowList.begin(), + windowList.end(), + w); + assert(it != windowList.end()); // window must be in list + ++it; // next window + if (it == windowList.end()) + return windowList.front(); // if we walked off the end, wrap around - delete [] nstack; + return *it; } -void Workspace::reconfigure(void) { - clientmenu->reconfigure(); +BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) { + BlackboxWindowList::iterator it = std::find(windowList.begin(), + windowList.end(), + w); + assert(it != windowList.end()); // window must be in list + if (it == windowList.begin()) + return windowList.back(); // if we walked of the front, wrap around - winVect::iterator it; - for (it = _windows.begin(); it != _windows.end(); ++it) - if ((*it)->validateClient()) - (*it)->reconfigure(); + return *(--it); } -OpenboxWindow *Workspace::getWindow(int index) { - if ((index >= 0) && (index < (signed)_windows.size())) - return _windows[index]; - else - return (OpenboxWindow *) 0; +BlackboxWindow* Workspace::getTopWindowOnStack(void) const { + assert(! stackingList.empty()); + return stackingList.front(); } -int Workspace::getCount(void) { - return (signed)_windows.size(); +void Workspace::sendWindowList(Netizen &n) { + BlackboxWindowList::iterator it = windowList.begin(), + end = windowList.end(); + for(; it != end; ++it) + n.sendWindowAdd((*it)->getClientWindow(), getID()); } -void Workspace::update(void) { - clientmenu->update(); - screen.getToolbar()->redrawWindowLabel(true); +unsigned int Workspace::getCount(void) const { + return windowList.size(); } -bool Workspace::isCurrent(void) { - return (id == screen.getCurrentWorkspaceID()); +void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const { + BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin(); + const BlackboxWindowList::const_reverse_iterator end = stackingList.rend(); + for (; it != end; ++it) + if ((*it)->isNormal()) + stack_order.push_back(*it); } + +void Workspace::hide(void) { + BlackboxWindow *focused = screen->getBlackbox()->getFocusedWindow(); + if (focused && focused->getScreen() == screen) { + assert(focused->isStuck() || focused->getWorkspaceNumber() == id); -void Workspace::setCurrent(void) { - screen.changeWorkspaceID(id); + lastfocus = focused; + } else { + // if no window had focus, no need to store a last focus + lastfocus = (BlackboxWindow *) 0; + } + + // when we switch workspaces, unfocus whatever was focused + screen->getBlackbox()->setFocusedWindow((BlackboxWindow *) 0); + + // withdraw windows in reverse order to minimize the number of Expose events + + BlackboxWindowList::reverse_iterator it = stackingList.rbegin(); + const BlackboxWindowList::reverse_iterator end = stackingList.rend(); + for (; it != end; ++it) { + BlackboxWindow *bw = *it; + // not normal windows cant focus from mouse enters anyways, so we dont + // need to unmap/remap them on workspace changes + if (! bw->isStuck() || bw->isNormal()) + bw->withdraw(); + } } -void Workspace::setName(const char *new_name) { - if (name) - delete [] name; +void Workspace::show(void) { + BlackboxWindowList::iterator it = stackingList.begin(); + const BlackboxWindowList::iterator end = stackingList.end(); + for (; it != end; ++it) { + BlackboxWindow *bw = *it; + // not normal windows cant focus from mouse enters anyways, so we dont + // need to unmap/remap them on workspace changes + if (! bw->isStuck() || bw->isNormal()) + bw->show(); + } - if (new_name) { - name = bstrdup(new_name); - } else { - name = new char[128]; - sprintf(name, i18n(WorkspaceSet, WorkspaceDefaultNameFormat, - "Workspace %d"), id + 1); + XSync(screen->getBlackbox()->getXDisplay(), False); + + if (screen->doFocusLast()) { + if (! screen->isSloppyFocus() && ! lastfocus && ! stackingList.empty()) + lastfocus = stackingList.front(); + + if (lastfocus) + lastfocus->setInputFocus(); } +} - clientmenu->setLabel(name); - clientmenu->update(); - screen.saveWorkspaceNames(); + +bool Workspace::isCurrent(void) const { + return (id == screen->getCurrentWorkspaceID()); +} + + +bool Workspace::isLastWindow(const BlackboxWindow* const w) const { + return (w == windowList.back()); +} + + +void Workspace::setCurrent(void) { + screen->changeWorkspaceID(id); +} + + +void Workspace::readName(void) { + XAtom::StringVect namesList; + unsigned long numnames = id + 1; + + // attempt to get from the _NET_WM_DESKTOP_NAMES property + if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names, + XAtom::utf8, numnames, namesList) && + namesList.size() > id) { + name = namesList[id]; + + clientmenu->setLabel(name); + clientmenu->update(); + } else { + /* + Use a default name. This doesn't actually change the class. That will + happen after the setName changes the root property, and that change + makes its way back to this function. + */ + string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat, + "Workspace %d"); + assert(tmp.length() < 32); + char default_name[32]; + sprintf(default_name, tmp.c_str(), id + 1); + + setName(default_name); // save this into the _NET_WM_DESKTOP_NAMES property + } } -void Workspace::shutdown(void) { - while (!_windows.empty()) - _windows[0]->restore(); +void Workspace::setName(const string& new_name) { + // set the _NET_WM_DESKTOP_NAMES property with the new name + XAtom::StringVect namesList; + unsigned long numnames = (unsigned) -1; + if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names, + XAtom::utf8, numnames, namesList) && + namesList.size() > id) + namesList[id] = new_name; + else + namesList.push_back(new_name); + + xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names, + XAtom::utf8, namesList); } + +/* + * Calculate free space available for window placement. + */ +typedef std::vector rectList; + static rectList calcSpace(const Rect &win, const rectList &spaces) { + Rect isect, extra; rectList result; - rectList::const_iterator siter; - for(siter=spaces.begin(); siter!=spaces.end(); ++siter) { - if(win.Intersect(*siter)) { - //Check for space to the left of the window - if(win.x() > siter->x()) - result.push_back(Rect(siter->x(), siter->y(), - win.x() - siter->x() - 1, - siter->h())); - //Check for space above the window - if(win.y() > siter->y()) - result.push_back(Rect(siter->x(), siter->y(), - siter->w(), - win.y() - siter->y() - 1)); - //Check for space to the right of the window - if((win.x()+win.w()) < - (siter->x()+siter->w())) - result.push_back(Rect(win.x() + win.w() + 1, - siter->y(), - siter->x() + siter->w() - - win.x() - win.w() - 1, - siter->h())); - //Check for space below the window - if((win.y()+win.h()) < - (siter->y()+siter->h())) - result.push_back(Rect(siter->x(), - win.y() + win.h() + 1, - siter->w(), - siter->y() + siter->h()- - win.y() - win.h() - 1)); + rectList::const_iterator siter, end = spaces.end(); + for (siter = spaces.begin(); siter != end; ++siter) { + const Rect &curr = *siter; + if(! win.intersects(curr)) { + result.push_back(curr); + continue; } - else - result.push_back(*siter); + + /* Use an intersection of win and curr to determine the space around + * curr that we can use. + * + * NOTE: the spaces calculated can overlap. + */ + isect = curr & win; + + // left + extra.setCoords(curr.left(), curr.top(), + isect.left() - 1, curr.bottom()); + if (extra.valid()) result.push_back(extra); + + // top + extra.setCoords(curr.left(), curr.top(), + curr.right(), isect.top() - 1); + if (extra.valid()) result.push_back(extra); + + // right + extra.setCoords(isect.right() + 1, curr.top(), + curr.right(), curr.bottom()); + if (extra.valid()) result.push_back(extra); + + // bottom + extra.setCoords(curr.left(), isect.bottom() + 1, + curr.right(), curr.bottom()); + if (extra.valid()) result.push_back(extra); } return result; } -bool rowRLBT(const Rect &first, const Rect &second){ - if (first.y()+first.h()==second.y()+second.h()) - return first.x()+first.w()>second.x()+second.w(); - return first.y()+first.h()>second.y()+second.h(); + +static bool rowRLBT(const Rect &first, const Rect &second) { + if (first.bottom() == second.bottom()) + return first.right() > second.right(); + return first.bottom() > second.bottom(); } -bool rowRLTB(const Rect &first, const Rect &second){ - if (first.y()==second.y()) - return first.x()+first.w()>second.x()+second.w(); - return first.y() second.right(); + return first.y() < second.y(); } -bool rowLRBT(const Rect &first, const Rect &second){ - if (first.y()+first.h()==second.y()+second.h()) - return first.x()second.y()+second.h(); +static bool rowLRBT(const Rect &first, const Rect &second) { + if (first.bottom() == second.bottom()) + return first.x() < second.x(); + return first.bottom() > second.bottom(); } -bool rowLRTB(const Rect &first, const Rect &second){ - if (first.y()==second.y()) - return first.x()second.y()+second.h(); - return first.x() second.bottom(); + return first.x() < second.x(); } -bool colRLTB(const Rect &first, const Rect &second){ - if (first.x()+first.w()==second.x()+second.w()) - return first.y()second.x()+second.w(); +static bool colRLTB(const Rect &first, const Rect &second) { + if (first.right() == second.right()) + return first.y() < second.y(); + return first.right() > second.right(); } -bool colRLBT(const Rect &first, const Rect &second){ - if (first.x()+first.w()==second.x()+second.w()) - return first.y()+first.h()>second.y()+second.h(); - return first.x()+first.w()>second.x()+second.w(); +static bool colRLBT(const Rect &first, const Rect &second) { + if (first.right() == second.right()) + return first.bottom() > second.bottom(); + return first.right() > second.right(); } -//BestFitPlacement finds the smallest free space that fits the window -//to be placed. It currentl ignores whether placement is right to left or top -//to bottom. -Point *Workspace::bestFitPlacement(const Size &win_size, const Rect &space) { - const Rect *best; +bool Workspace::smartPlacement(Rect& win) { rectList spaces; - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free + + //initially the entire screen is free +#ifdef XINERAMA + if (screen->isXineramaActive() && + screen->getBlackbox()->doXineramaPlacement()) { + RectList availableAreas = screen->allAvailableAreas(); + RectList::iterator it, end = availableAreas.end(); + + for (it = availableAreas.begin(); it != end; ++it) + spaces.push_back(*it); + } else +#endif // XINERAMA + spaces.push_back(screen->availableArea()); //Find Free Spaces - winVect::iterator it; - for (it = _windows.begin(); it != _windows.end(); ++it) - spaces = calcSpace((*it)->area().Inflate(screen.getBorderWidth() * 4), - spaces); - - //Find first space that fits the window - best = NULL; - for (siter=spaces.begin(); siter!=spaces.end(); ++siter) { - if ((siter->w() >= win_size.w()) && (siter->h() >= win_size.h())) { - if (best==NULL) - best = &*siter; - else if(siter->w()*siter->h()h()*best->w()) - best = &*siter; + BlackboxWindowList::const_iterator wit = windowList.begin(), + end = windowList.end(); + Rect tmp; + for (; wit != end; ++wit) { + const BlackboxWindow* const curr = *wit; + + // watch for shaded windows and full-maxed windows + if (curr->isShaded()) { + if (screen->getPlaceIgnoreShaded()) continue; + } else if (curr->isMaximizedFull()) { + if (screen->getPlaceIgnoreMaximized()) continue; } + + tmp.setRect(curr->frameRect().x(), curr->frameRect().y(), + curr->frameRect().width() + screen->getBorderWidth(), + curr->frameRect().height() + screen->getBorderWidth()); + + spaces = calcSpace(tmp, spaces); } - if (best != NULL) { - Point *pt = new Point(best->origin()); - if (screen.colPlacementDirection() != BScreen::TopBottom) - pt->setY(pt->y() + (best->h() - win_size.h())); - if (screen.rowPlacementDirection() != BScreen::LeftRight) - pt->setX(pt->x() + (best->w() - win_size.w())); - return pt; - } else - return NULL; //fall back to cascade + + if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) { + if(screen->getRowPlacementDirection() == BScreen::LeftRight) { + if(screen->getColPlacementDirection() == BScreen::TopBottom) + std::sort(spaces.begin(), spaces.end(), rowLRTB); + else + std::sort(spaces.begin(), spaces.end(), rowLRBT); + } else { + if(screen->getColPlacementDirection() == BScreen::TopBottom) + std::sort(spaces.begin(), spaces.end(), rowRLTB); + else + std::sort(spaces.begin(), spaces.end(), rowRLBT); + } + } else { + if(screen->getColPlacementDirection() == BScreen::TopBottom) { + if(screen->getRowPlacementDirection() == BScreen::LeftRight) + std::sort(spaces.begin(), spaces.end(), colLRTB); + else + std::sort(spaces.begin(), spaces.end(), colRLTB); + } else { + if(screen->getRowPlacementDirection() == BScreen::LeftRight) + std::sort(spaces.begin(), spaces.end(), colLRBT); + else + std::sort(spaces.begin(), spaces.end(), colRLBT); + } + } + + rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end(); + for(; sit != spaces_end; ++sit) { + if (sit->width() >= win.width() && sit->height() >= win.height()) + break; + } + + if (sit == spaces_end) + return False; + + //set new position based on the empty space found + const Rect& where = *sit; + win.setX(where.x()); + win.setY(where.y()); + + // adjust the location() based on left/right and top/bottom placement + if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) { + if (screen->getRowPlacementDirection() == BScreen::RightLeft) + win.setX(where.right() - win.width()); + if (screen->getColPlacementDirection() == BScreen::BottomTop) + win.setY(where.bottom() - win.height()); + } else { + if (screen->getColPlacementDirection() == BScreen::BottomTop) + win.setY(win.y() + where.height() - win.height()); + if (screen->getRowPlacementDirection() == BScreen::RightLeft) + win.setX(win.x() + where.width() - win.width()); + } + return True; } -Point *Workspace::underMousePlacement(const Size &win_size, const Rect &space) { - Point *pt; +bool Workspace::underMousePlacement(Rect &win) { int x, y, rx, ry; Window c, r; unsigned int m; - XQueryPointer(screen.getOpenbox().getXDisplay(), screen.getRootWindow(), + XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(), &r, &c, &rx, &ry, &x, &y, &m); - pt = new Point(rx - win_size.w() / 2, ry - win_size.h() / 2); - if (pt->x() < space.x()) - pt->setX(space.x()); - if (pt->y() < space.y()) - pt->setY(space.y()); - if (pt->x() + win_size.w() > space.x() + space.w()) - pt->setX(space.x() + space.w() - win_size.w()); - if (pt->y() + win_size.h() > space.y() + space.h()) - pt->setY(space.y() + space.h() - win_size.h()); - return pt; -} - -Point *Workspace::rowSmartPlacement(const Size &win_size, const Rect &space) { - const Rect *best; - rectList spaces; + Rect area; +#ifdef XINERAMA + if (screen->isXineramaActive() && + screen->getBlackbox()->doXineramaPlacement()) { + RectList availableAreas = screen->allAvailableAreas(); + RectList::iterator it, end = availableAreas.end(); + + for (it = availableAreas.begin(); it != end; ++it) + if (it->contains(rx, ry)) break; + assert(it != end); // the mouse isn't inside an area? + area = *it; + } else +#endif // XINERAMA + area = screen->availableArea(); + + x = rx - win.width() / 2; + y = ry - win.height() / 2; - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free + if (x < area.x()) + x = area.x(); + if (y < area.y()) + y = area.y(); + if (x + win.width() > area.x() + area.width()) + x = area.x() + area.width() - win.width(); + if (y + win.height() > area.y() + area.height()) + y = area.y() + area.height() - win.height(); - //Find Free Spaces - winVect::iterator it; - for (it = _windows.begin(); it != _windows.end(); ++it) - spaces = calcSpace((*it)->area().Inflate(screen.getBorderWidth() * 4), - spaces); - //Sort spaces by preference - if(screen.rowPlacementDirection() == BScreen::RightLeft) - if(screen.colPlacementDirection() == BScreen::TopBottom) - sort(spaces.begin(),spaces.end(),rowRLTB); - else - sort(spaces.begin(),spaces.end(),rowRLBT); - else - if(screen.colPlacementDirection() == BScreen::TopBottom) - sort(spaces.begin(),spaces.end(),rowLRTB); - else - sort(spaces.begin(),spaces.end(),rowLRBT); - best = NULL; - for (siter=spaces.begin(); siter!=spaces.end(); ++siter) - if ((siter->w() >= win_size.w()) && (siter->h() >= win_size.h())) { - best = &*siter; - break; - } + win.setX(x); + win.setY(y); - if (best != NULL) { - Point *pt = new Point(best->origin()); - if (screen.colPlacementDirection() != BScreen::TopBottom) - pt->setY(best->y() + best->h() - win_size.h()); - if (screen.rowPlacementDirection() != BScreen::LeftRight) - pt->setX(best->x()+best->w()-win_size.w()); - return pt; - } else - return NULL; //fall back to cascade + return True; } -Point *Workspace::colSmartPlacement(const Size &win_size, const Rect &space) { - const Rect *best; - rectList spaces; - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free +bool Workspace::cascadePlacement(Rect &win, const int offset) { + Rect area; + +#ifdef XINERAMA + if (screen->isXineramaActive() && + screen->getBlackbox()->doXineramaPlacement()) { + area = screen->allAvailableAreas()[cascade_region]; + } else +#endif // XINERAMA + area = screen->availableArea(); - //Find Free Spaces - winVect::iterator it; - for (it = _windows.begin(); it != _windows.end(); ++it) - spaces = calcSpace((*it)->area().Inflate(screen.getBorderWidth() * 4), - spaces); - //Sort spaces by user preference - if(screen.colPlacementDirection() == BScreen::TopBottom) - if(screen.rowPlacementDirection() == BScreen::LeftRight) - sort(spaces.begin(),spaces.end(),colLRTB); - else - sort(spaces.begin(),spaces.end(),colRLTB); - else - if(screen.rowPlacementDirection() == BScreen::LeftRight) - sort(spaces.begin(),spaces.end(),colLRBT); - else - sort(spaces.begin(),spaces.end(),colRLBT); - - //Find first space that fits the window - best = NULL; - for (siter=spaces.begin(); siter!=spaces.end(); ++siter) - if ((siter->w() >= win_size.w()) && (siter->h() >= win_size.h())) { - best = &*siter; - break; + if ((static_cast(cascade_x + win.width()) > area.right() + 1) || + (static_cast(cascade_y + win.height()) > area.bottom() + 1)) { + cascade_x = cascade_y = 0; +#ifdef XINERAMA + if (screen->isXineramaActive() && + screen->getBlackbox()->doXineramaPlacement()) { + // go to the next xinerama region, and use its area + if (++cascade_region >= screen->allAvailableAreas().size()) + cascade_region = 0; + area = screen->allAvailableAreas()[cascade_region]; } +#endif // XINERAMA + } - if (best != NULL) { - Point *pt = new Point(best->origin()); - if (screen.colPlacementDirection() != BScreen::TopBottom) - pt->setY(pt->y() + (best->h() - win_size.h())); - if (screen.rowPlacementDirection() != BScreen::LeftRight) - pt->setX(pt->x() + (best->w() - win_size.w())); - return pt; - } else - return NULL; //fall back to cascade -} + if (cascade_x == 0) { + cascade_x = area.x() + offset; + cascade_y = area.y() + offset; + } + win.setPos(cascade_x, cascade_y); -Point *const Workspace::cascadePlacement(const OpenboxWindow &win, - const Rect &space) { - if ((cascade_x + win.area().w() + screen.getBorderWidth() * 2 > - (space.x() + space.w())) || - (cascade_y + win.area().h() + screen.getBorderWidth() * 2 > - (space.y() + space.h()))) - cascade_x = cascade_y = 0; - if (cascade_x < space.x() || cascade_y < space.y()) { - cascade_x = space.x(); - cascade_y = space.y(); - } + cascade_x += offset; + cascade_y += offset; - Point *p = new Point(cascade_x, cascade_y); - cascade_x += win.getTitleHeight(); - cascade_y += win.getTitleHeight(); - return p; + return True; } -void Workspace::placeWindow(OpenboxWindow &win) { - Rect space = screen.availableArea(); - const Size window_size(win.area().w()+screen.getBorderWidth() * 2, - win.area().h()+screen.getBorderWidth() * 2); - Point *place = NULL; +void Workspace::placeWindow(BlackboxWindow *win) { + Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height()); + bool placed = False; - switch (screen.placementPolicy()) { - case BScreen::BestFitPlacement: - place = bestFitPlacement(window_size, space); - break; + switch (screen->getPlacementPolicy()) { case BScreen::RowSmartPlacement: - place = rowSmartPlacement(window_size, space); - break; case BScreen::ColSmartPlacement: - place = colSmartPlacement(window_size, space); + placed = smartPlacement(new_win); break; case BScreen::UnderMousePlacement: case BScreen::ClickMousePlacement: - place = underMousePlacement(window_size, space); - break; + placed = underMousePlacement(new_win); + default: + break; // handled below } // switch - if (place == NULL) - place = cascadePlacement(win, space); + if (placed == False) + cascadePlacement(new_win, (win->getTitleHeight() + + screen->getBorderWidth() * 2)); - ASSERT(place != NULL); - if (place->x() + window_size.w() > (signed) space.x() + space.w()) - place->setX(((signed) space.x() + space.w() - window_size.w()) / 2); - if (place->y() + window_size.h() > (signed) space.y() + space.h()) - place->setY(((signed) space.y() + space.h() - window_size.h()) / 2); + if (new_win.right() > screen->availableArea().right()) + new_win.setX(screen->availableArea().left()); + if (new_win.bottom() > screen->availableArea().bottom()) + new_win.setY(screen->availableArea().top()); - win.configure(place->x(), place->y(), win.area().w(), win.area().h()); - delete place; + win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height()); }