X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2FWorkspace.cc;h=67e191107ccb851cf01987cf28fcd77bcb5de1e6;hb=72a2e98738d87b89620bafd15141690aa4be8fab;hp=27afcaded75d4f77df4ad7a67c016dcc7f474862;hpb=6a36505f8aa312ee2ccf0f173bc0fc6659664418;p=chaz%2Fopenbox diff --git a/src/Workspace.cc b/src/Workspace.cc index 27afcade..67e19110 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,651 +21,821 @@ // 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 -#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" - #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 -typedef vector rectList; +#include -Workspace::Workspace(BScreen &scrn, int i) : screen(scrn) { +#include +#include - cascade_x = cascade_y = 32; +using std::string; - id = i; +#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" - stackingList = new LinkedList; - windowList = new LinkedList; - clientmenu = new Clientmenu(*this); - lastfocus = (OpenboxWindow *) 0; +Workspace::Workspace(BScreen *scrn, unsigned int i) { + screen = scrn; + xatom = screen->getBlackbox()->getXAtom(); - name = (char *) 0; - char *tmp = screen.getNameOfWorkspace(id); - setName(tmp); -} + cascade_x = cascade_y = 0; +#ifdef XINERAMA + cascade_region = 0; +#endif // XINERAMA + + id = i; + clientmenu = new Clientmenu(this); -Workspace::~Workspace(void) { - delete stackingList; - delete windowList; - delete clientmenu; + lastfocus = (BlackboxWindow *) 0; - if (name) - delete [] name; + readName(); } -const 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); - w->setWorkspace(id); - w->setWindowNumber(windowList->count()); + stackingList.push_front(w); + + if (! sticky) + w->setWorkspace(id); + + if (! w->isNormal()) { + if (! sticky) { + // just give it some number, else bad things happen as it is assumed to + // not be on a workspace + w->setWindowNumber(0); + } + } else { + if (! sticky) + w->setWindowNumber(windowList.size()); + + windowList.push_back(w); + + clientmenu->insert(w->getTitle()); + clientmenu->update(); + + if (! sticky) + screen->updateNetizenWindowAdd(w->getClientWindow(), id); + + 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; + } + } + } + + if (! w->isDesktop()) + raiseWindow(w); + else + lowerWindow(w); +} + + +void Workspace::removeWindow(BlackboxWindow *w, bool sticky) { + assert(w != 0); - stackingList->insert(w, 0); - windowList->insert(w); + stackingList.remove(w); - clientmenu->insert((const char **) w->getTitle()); + // 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); + + windowList.erase(it); + clientmenu->remove(i); clientmenu->update(); - screen.updateNetizenWindowAdd(w->getClientWindow(), id); + if (! sticky) { + screen->updateNetizenWindowDel(w->getClientWindow()); - raiseWindow(w); + BlackboxWindowList::iterator it = windowList.begin(); + const BlackboxWindowList::iterator end = windowList.end(); + unsigned int i = 0; + for (; it != end; ++it, ++i) + (*it)->setWindowNumber(i); + } - return w->getWindowNumber(); + if (i == 0) { + cascade_x = cascade_y = 0; +#ifdef XINERAMA + cascade_region = 0; +#endif // XINERAMA + } } -const int Workspace::removeWindow(OpenboxWindow *w) { - if (! w) return -1; +void Workspace::focusFallback(const BlackboxWindow *old_window) { + BlackboxWindow *newfocus = 0; - stackingList->remove(w); + if (id == screen->getCurrentWorkspaceID()) { + // The window is on the visible workspace. - if (w->isFocused()) { - if (w->isTransient() && w->getTransientFor() && - w->getTransientFor()->isVisible()) { - w->getTransientFor()->setInputFocus(); - } else if (screen.sloppyFocus()) { - screen.getOpenbox().setFocusedWindow((OpenboxWindow *) 0); - } else { - OpenboxWindow *top = stackingList->first(); - if (! top || ! top->setInputFocus()) { - screen.getOpenbox().setFocusedWindow((OpenboxWindow *) 0); - XSetInputFocus(screen.getOpenbox().getXDisplay(), - screen.getToolbar()->getWindowID(), - RevertToParent, CurrentTime); + // 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; + } } } + + 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); + } } - - if (lastfocus == w) - lastfocus = (OpenboxWindow *) 0; +} - windowList->remove(w->getWindowNumber()); - clientmenu->remove(w->getWindowNumber()); - clientmenu->update(); - screen.updateNetizenWindowDel(w->getClientWindow()); +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); +} - LinkedListIterator it(windowList); - OpenboxWindow *bw = it.current(); - for (int i = 0; bw; it++, i++, bw = it.current()) - bw->setWindowNumber(i); - return windowList->count(); +void Workspace::removeAll(void) { + while (! windowList.empty()) + windowList.front()->iconify(); } - void Workspace::showAll(void) { - LinkedListIterator it(stackingList); - for (OpenboxWindow *bw = it.current(); bw; it++, bw = it.current()) - bw->deiconify(False, False); + BlackboxWindowList::iterator it = stackingList.begin(); + const BlackboxWindowList::iterator end = stackingList.end(); + for (; it != end; ++it) { + BlackboxWindow *bw = *it; + // sticky windows arent unmapped on a workspace change so we don't have ot + // map them, but sometimes on a restart, another app can unmap our sticky + // windows, so we map on startup always + if (! bw->isStuck() || screen->getBlackbox()->isStartup()) + bw->show(); + } } void Workspace::hideAll(void) { - LinkedList lst; + // withdraw in reverse order to minimize the number of Expose events - LinkedListIterator it(stackingList); - for (OpenboxWindow *bw = it.current(); bw; it++, bw = it.current()) - lst.insert(bw, 0); + BlackboxWindowList lst(stackingList.rbegin(), stackingList.rend()); - LinkedListIterator it2(&lst); - for (OpenboxWindow *bw = it2.current(); bw; it2++, bw = it2.current()) + BlackboxWindowList::iterator it = lst.begin(); + const BlackboxWindowList::iterator end = lst.end(); + for (; it != end; ++it) { + BlackboxWindow *bw = *it; + // don't hide sticky windows, or they'll end up flickering on a workspace + // change if (! bw->isStuck()) bw->withdraw(); + } } -void Workspace::removeAll(void) { - LinkedListIterator it(windowList); - for (OpenboxWindow *bw = it.current(); bw; it++, bw = it.current()) - bw->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; -void Workspace::raiseWindow(OpenboxWindow *w) { - OpenboxWindow *win = (OpenboxWindow *) 0, *bottom = w; + unsigned int ret = transients.size(); + BlackboxWindowList::const_iterator it = transients.begin(), + end = transients.end(); + for (; it != end; ++it) + ret += countTransients(*it); - while (bottom->isTransient() && bottom->getTransientFor()) - bottom = bottom->getTransientFor(); + return ret; +} - int i = 1; - win = bottom; - while (win->hasTransient() && win->getTransient()) { - win = win->getTransient(); - i++; +/* + * 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); + } } - Window *nstack = new Window[i], *curr = nstack; - Workspace *wkspc; + // put transients of win's transients in the stack + for (it = win->getTransients().begin(); it != end; ++it) + raiseTransients(*it, stack); +} - win = bottom; - while (True) { - *(curr++) = win->getFrameWindow(); - screen.updateNetizenWindowRaise(win->getClientWindow()); - if (! win->isIconic()) { - wkspc = screen.getWorkspace(win->getWorkspaceNumber()); - wkspc->stackingList->remove(win); - wkspc->stackingList->insert(win, 0); - } +void Workspace::lowerTransients(const BlackboxWindow * const win, + StackVector::iterator &stack) { + if (win->getTransients().empty()) return; // nothing to do - if (! win->hasTransient() || ! win->getTransient()) - break; + // 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); + + // 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()); - win = win->getTransient(); + if (! w->isIconic()) { + Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber()); + wkspc->stackingList.remove(w); + wkspc->stackingList.push_back(w); + } } +} - screen.raiseWindows(nstack, i); - delete [] nstack; -} +void Workspace::raiseWindow(BlackboxWindow *w) { + BlackboxWindow *win = w; + if (win->isDesktop()) return; -void Workspace::lowerWindow(OpenboxWindow *w) { - OpenboxWindow *win = (OpenboxWindow *) 0, *bottom = w; + // walk up the transient_for's to the window that is not a transient + while (win->isTransient() && win->getTransientFor()) + win = win->getTransientFor(); - while (bottom->isTransient() && bottom->getTransientFor()) - bottom = bottom->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++; + *(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); } - Window *nstack = new Window[i], *curr = nstack; - Workspace *wkspc; + raiseTransients(win, stack); - while (True) { - *(curr++) = win->getFrameWindow(); - screen.updateNetizenWindowLower(win->getClientWindow()); + screen->raiseWindows(&stack_vector[0], stack_vector.size()); +} - if (! win->isIconic()) { - wkspc = screen.getWorkspace(win->getWorkspaceNumber()); - wkspc->stackingList->remove(win); - wkspc->stackingList->insert(win); - } - if (! win->getTransientFor()) - break; +void Workspace::lowerWindow(BlackboxWindow *w) { + BlackboxWindow *win = w; + // walk up the transient_for's to the window that is not a transient + while (win->isTransient() && win->getTransientFor()) win = win->getTransientFor(); - } - screen.getOpenbox().grab(); + // get the total window count (win and all transients) + unsigned int i = 1 + countTransients(win); - XLowerWindow(screen.getBaseDisplay().getXDisplay(), *nstack); - XRestackWindows(screen.getBaseDisplay().getXDisplay(), nstack, i); + // stack the window with all transients above + StackVector stack_vector(i); + StackVector::iterator stack = stack_vector.begin(); - screen.getOpenbox().ungrab(); + 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); + } - delete [] nstack; + screen->lowerWindows(&stack_vector[0], stack_vector.size()); } void Workspace::reconfigure(void) { clientmenu->reconfigure(); + std::for_each(windowList.begin(), windowList.end(), + std::mem_fun(&BlackboxWindow::reconfigure)); +} + - LinkedListIterator it(windowList); - for (OpenboxWindow *bw = it.current(); bw; it++, bw = it.current()) { - if (bw->validateClient()) - bw->reconfigure(); +BlackboxWindow *Workspace::getWindow(unsigned int index) { + if (index < windowList.size()) { + BlackboxWindowList::iterator it = windowList.begin(); + while (index-- > 0) // increment to index + ++it; + return *it; } + + return 0; } -OpenboxWindow *Workspace::getWindow(int index) { - if ((index >= 0) && (index < windowList->count())) - return windowList->find(index); - else - return 0; +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 + + return *it; } -const int Workspace::getCount(void) { - return windowList->count(); +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 + + return *(--it); } -void Workspace::update(void) { - clientmenu->update(); - screen.getToolbar()->redrawWindowLabel(True); +BlackboxWindow* Workspace::getTopWindowOnStack(void) const { + assert(! stackingList.empty()); + return stackingList.front(); } -Bool Workspace::isCurrent(void) { - return (id == screen.getCurrentWorkspaceID()); +void Workspace::sendWindowList(Netizen &n) { + BlackboxWindowList::iterator it = windowList.begin(), + end = windowList.end(); + for(; it != end; ++it) + n.sendWindowAdd((*it)->getClientWindow(), getID()); } -Bool Workspace::isLastWindow(OpenboxWindow *w) { - return (w == windowList->last()); +unsigned int Workspace::getCount(void) const { + return windowList.size(); } -void Workspace::setCurrent(void) { - screen.changeWorkspaceID(id); + +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) + // don't add desktop wnidows, or sticky windows more than once + if (! ( (*it)->isDesktop() || + ((*it)->isStuck() && id != screen->getCurrentWorkspaceID()))) + stack_order.push_back(*it); +} + + +bool Workspace::isCurrent(void) const { + return (id == screen->getCurrentWorkspaceID()); +} + + +bool Workspace::isLastWindow(const BlackboxWindow* const w) const { + return (w == windowList.back()); } -void Workspace::setName(char *new_name) { - if (name) - delete [] name; +void Workspace::setCurrent(void) { + screen->changeWorkspaceID(id); +} + - if (new_name) { - name = bstrdup(new_name); +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 { - name = new char[128]; - sprintf(name, i18n->getMessage(WorkspaceSet, WorkspaceDefaultNameFormat, - "Workspace %d"), id + 1); + /* + 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 } - - clientmenu->setLabel(name); - clientmenu->update(); - screen.saveWorkspaceNames(); } -void Workspace::shutdown(void) { - while (windowList->count()) { - windowList->first()->restore(); - delete windowList->first(); - } +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); } -static rectList calcSpace(const Rect &win, const rectList &spaces) { + +/* + * Calculate free space available for window placement. + */ +Workspace::rectList Workspace::calcSpace(const Rect &win, + const rectList &spaces) const { + 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.origin().x() > siter->x()) - result.push_back(Rect(siter->x(), siter->y(), - win.origin().x() - siter->x() - 1, - siter->h())); - //Check for space above the window - if(win.origin().y() > siter->y()) - result.push_back(Rect(siter->x(), siter->y(), - siter->w(), - win.origin().y() - siter->y() - 1)); - //Check for space to the right of the window - if((win.origin().x()+win.size().w()) < - (siter->x()+siter->w())) - result.push_back(Rect(win.origin().x() + win.size().w() + 1, - siter->y(), - siter->x() + siter->w() - - win.origin().x() - win.size().w() - 1, - siter->h())); - //Check for space below the window - if((win.origin().y()+win.size().h()) < - (siter->y()+siter->h())) - result.push_back(Rect(siter->x(), - win.origin().y() + win.size().h() + 1, - siter->w(), - siter->y() + siter->h()- - win.origin().y() - win.size().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() - screen->getSnapOffset(), curr.bottom()); + if (extra.valid()) result.push_back(extra); + + // top + extra.setCoords(curr.left(), curr.top(), + curr.right(), isect.top() - screen->getSnapOffset()); + if (extra.valid()) result.push_back(extra); + + // right + extra.setCoords(isect.right() + screen->getSnapOffset(), curr.top(), + curr.right(), curr.bottom()); + if (extra.valid()) result.push_back(extra); + + // bottom + extra.setCoords(curr.left(), isect.bottom() + screen->getSnapOffset(), + curr.right(), curr.bottom()); + if (extra.valid()) result.push_back(extra); } return result; } -bool incWidth(const Rect &first, const Rect &second) { - return first.x() < second.x(); +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 decWidth(const Rect &first, const Rect &second) { - if (second.x() == first.x()) - return second.w() < first.w(); - return second.x() < first.x(); +static bool rowRLTB(const Rect &first, const Rect &second) { + if (first.y() == second.y()) + return first.right() > second.right(); + return first.y() < second.y(); } +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 incHeight(const Rect &first, const Rect &second) { +static bool rowLRTB(const Rect &first, const Rect &second) { + if (first.y() == second.y()) + return first.x() < second.x(); return first.y() < second.y(); } +static bool colLRTB(const Rect &first, const Rect &second) { + if (first.x() == second.x()) + return first.y() < second.y(); + return first.x() < second.x(); +} -bool decHeight(const Rect &first, const Rect &second) { - if (second.y() == first.y()) - return second.h() < first.h(); - return second.y() < first.y(); +static bool colLRBT(const Rect &first, const Rect &second) { + if (first.x() == second.x()) + return first.bottom() > second.bottom(); + return first.x() < second.x(); } +static bool colRLTB(const Rect &first, const Rect &second) { + if (first.right() == second.right()) + return first.y() < second.y(); + 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; - rectList spaces; - LinkedListIterator it(windowList); - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free - it.reset(); - - //Find Free Spaces - for (OpenboxWindow *cur=it.current(); cur!=NULL; it++, cur=it.current()) - spaces = calcSpace(cur->area().Inflate(screen.getBorderWidth() * 4), - spaces); - - //Sort the spaces by placement choice - if (screen.rowPlacementDirection() == BScreen::TopBottom) - sort(spaces.begin(), spaces.end(), decHeight); - else - sort(spaces.begin(), spaces.end(), incHeight); - if (screen.colPlacementDirection() == BScreen::TopBottom) - stable_sort(spaces.begin(), spaces.end(), incHeight); - else - stable_sort(spaces.begin(), spaces.end(), decHeight); - - //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; - } - } - 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 +static bool colRLBT(const Rect &first, const Rect &second) { + if (first.right() == second.right()) + return first.bottom() > second.bottom(); + return first.right() > second.right(); } -Point *Workspace::rowSmartPlacement(const Size &win_size, const Rect &space) { - const Rect *best; +bool Workspace::smartPlacement(Rect& win) { rectList spaces; - LinkedListIterator it(windowList); - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free - it.reset(); - - //Find Free Spaces - for (OpenboxWindow *cur=it.current(); cur!=NULL; it++, cur=it.current()) - spaces = calcSpace(cur->area().Inflate(screen.getBorderWidth() * 4), - spaces); - - //Sort the spaces by placement choice - if (screen.colPlacementDirection() == BScreen::LeftRight) - sort(spaces.begin(), spaces.end(), incWidth); - else - sort(spaces.begin(), spaces.end(), decWidth); - if (screen.rowPlacementDirection() == BScreen::TopBottom) - stable_sort(spaces.begin(), spaces.end(), incHeight); - else - stable_sort(spaces.begin(), spaces.end(), decHeight); - - //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; + + //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) { + Rect r = *it; + r.setRect(r.x() + screen->getSnapOffset(), + r.y() + screen->getSnapOffset(), + r.width() - screen->getSnapOffset(), + r.height() - screen->getSnapOffset()); + spaces.push_back(*it); } - - 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 -} +#endif // XINERAMA + { + Rect r = screen->availableArea(); + r.setRect(r.x() + screen->getSnapOffset(), + r.y() + screen->getSnapOffset(), + r.width() - screen->getSnapOffset(), + r.height() - screen->getSnapOffset()); + spaces.push_back(r); + } -Point *Workspace::colSmartPlacement(const Size &win_size, const Rect &space) { - const Rect *best; - rectList spaces; - LinkedListIterator it(windowList); - rectList::const_iterator siter; - spaces.push_back(space); //initially the entire screen is free - it.reset(); - //Find Free Spaces - for (OpenboxWindow *cur=it.current(); cur!=NULL; it++, cur=it.current()) - spaces = calcSpace(cur->area().Inflate(screen.getBorderWidth() * 4), - spaces); - - //Sort the spaces by placement choice - if (screen.rowPlacementDirection() == BScreen::LeftRight) - sort(spaces.begin(),spaces.end(),incWidth); - else - sort(spaces.begin(),spaces.end(),decWidth); - - //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; + 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; } - 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 -} + tmp.setRect(curr->frameRect().x(), curr->frameRect().y(), + curr->frameRect().width() + screen->getBorderWidth(), + curr->frameRect().height() + screen->getBorderWidth()); + spaces = calcSpace(tmp, spaces); + } -Point *const Workspace::cascadePlacement(const OpenboxWindow *const win){ - if (((unsigned) cascade_x > (screen.size().w() / 2)) || - ((unsigned) cascade_y > (screen.size().h() / 2))) - cascade_x = cascade_y = 32; + 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; + } - cascade_x += win->getTitleHeight(); - cascade_y += win->getTitleHeight(); + if (sit == spaces_end) + return False; - return new Point(cascade_x, cascade_y); + //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; } -void Workspace::placeWindow(OpenboxWindow *win) { - ASSERT(win != NULL); +bool Workspace::underMousePlacement(Rect &win) { + int x, y, rx, ry; + Window c, r; + unsigned int m; + XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(), + &r, &c, &rx, &ry, &x, &y, &m); + + 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; + + 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(); + + win.setX(x); + win.setY(y); + + return True; +} - // the following code is temporary and will be taken care of by Screen in the - // future (with the NETWM 'strut') - Rect space(0, 0, screen.size().w(), screen.size().h()); -#ifdef SLIT - Slit *slit = screen.getSlit(); - int slit_x = slit->autoHide() ? slit->hiddenOrigin().x() : slit->area().x(), - slit_y = slit->autoHide() ? slit->hiddenOrigin().y() : slit->area().y(); - Toolbar *toolbar = screen.getToolbar(); - int tbarh = screen.hideToolbar() ? 0 : - toolbar->getExposedHeight() + screen.getBorderWidth() * 2; - bool tbartop; - switch (toolbar->placement()) { - case Toolbar::TopLeft: - case Toolbar::TopCenter: - case Toolbar::TopRight: - tbartop = true; - break; - case Toolbar::BottomLeft: - case Toolbar::BottomCenter: - case Toolbar::BottomRight: - tbartop = false; - break; - default: - ASSERT(false); // unhandled placement - } - if ((slit->direction() == Slit::Horizontal && - (slit->placement() == Slit::TopLeft || - slit->placement() == Slit::TopRight)) || - slit->placement() == Slit::TopCenter) { - // exclude top - if (tbartop && slit_y + slit->area().h() < tbarh) { - space.setY(space.y() + tbarh); - space.setH(space.h() - tbarh); - } else { - space.setY(space.y() + (slit_y + slit->area().h() + - screen.getBorderWidth() * 2)); - space.setH(space.h() - (slit_y + slit->area().h() + - screen.getBorderWidth() * 2)); - if (!tbartop) - space.setH(space.h() - tbarh); - } - } else if ((slit->direction() == Slit::Vertical && - (slit->placement() == Slit::TopRight || - slit->placement() == Slit::BottomRight)) || - slit->placement() == Slit::CenterRight) { - // exclude right - space.setW(space.w() - (screen.size().w() - slit_x)); - if (tbartop) - space.setY(space.y() + tbarh); - space.setH(space.h() - tbarh); - } else if ((slit->direction() == Slit::Horizontal && - (slit->placement() == Slit::BottomLeft || - slit->placement() == Slit::BottomRight)) || - slit->placement() == Slit::BottomCenter) { - // exclude bottom - if (!tbartop && (screen.size().h() - slit_y) < tbarh) { - space.setH(space.h() - tbarh); - } else { - space.setH(space.h() - (screen.size().h() - slit_y)); - if (tbartop) { - space.setY(space.y() + tbarh); - space.setH(space.h() - tbarh); - } - } - } else {// if ((slit->direction() == Slit::Vertical && - // (slit->placement() == Slit::TopLeft || - // slit->placement() == Slit::BottomLeft)) || - // slit->placement() == Slit::CenterLeft) - // exclude left - space.setX(slit_x + slit->area().w() + - screen.getBorderWidth() * 2); - space.setW(space.w() - (slit_x + slit->area().w() + - screen.getBorderWidth() * 2)); - if (tbartop) - space.setY(space.y() + tbarh); - space.setH(space.h() - tbarh); +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(); + + 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]; } -#else // !SLIT - Toolbar *toolbar = screen.getToolbar(); - int tbarh = screen.hideToolbar() ? 0 : - toolbar->getExposedHeight() + screen.getBorderWidth() * 2; - switch (toolbar->placement()) { - case Toolbar::TopLeft: - case Toolbar::TopCenter: - case Toolbar::TopRight: - space.setY(toolbar->getExposedHeight()); - space.setH(space.h() - toolbar->getExposedHeight()); - break; - case Toolbar::BottomLeft: - case Toolbar::BottomCenter: - case Toolbar::BottomRight: - space.setH(space.h() - tbarh); - break; - default: - ASSERT(false); // unhandled placement +#endif // XINERAMA } -#endif // SLIT - const Size window_size(win->area().w()+screen.getBorderWidth() * 4, - win->area().h()+screen.getBorderWidth() * 4); - Point *place = NULL; - LinkedListIterator it(windowList); + if (cascade_x == 0) { + cascade_x = area.x() + offset; + cascade_y = area.y() + offset; + } - switch (screen.placementPolicy()) { - case BScreen::BestFitPlacement: - place = bestFitPlacement(window_size, space); - break; + win.setPos(cascade_x, cascade_y); + + cascade_x += offset; + cascade_y += offset; + + return True; +} + + +void Workspace::placeWindow(BlackboxWindow *win) { + Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height()); + bool placed = False; + + 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: + placed = underMousePlacement(new_win); + default: + break; // handled below } // switch - if (place == NULL) - place = cascadePlacement(win); - - ASSERT(place != NULL); - if (place->x() + window_size.w() > (signed) screen.size().w()) - place->setX(((signed) screen.size().w() - window_size.w()) / 2); - if (place->y() + window_size.h() > (signed) screen.size().h()) - place->setY(((signed) screen.size().h() - window_size.h()) / 2); - - win->configure(place->x(), place->y(), win->area().w(), win->area().h()); - delete place; + if (placed == False) + cascadePlacement(new_win, (win->getTitleHeight() + + screen->getBorderWidth() * 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(new_win.x(), new_win.y(), new_win.width(), new_win.height()); }