From d698c43e6387e07348265436c44cf2d2be299a5b Mon Sep 17 00:00:00 2001 From: markt Date: Sun, 13 May 2007 04:17:19 +0000 Subject: added {Row,Col}MinOverlapPlacement strategies --- ChangeLog | 5 ++ src/Makefile.am | 1 + src/MinOverlapPlacement.cc | 182 +++++++++++++++++++++++++++++++++++++++++++++ src/MinOverlapPlacement.hh | 83 +++++++++++++++++++++ src/ScreenPlacement.cc | 13 ++++ src/ScreenPlacement.hh | 6 +- 6 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 src/MinOverlapPlacement.cc create mode 100644 src/MinOverlapPlacement.hh diff --git a/ChangeLog b/ChangeLog index 0dc4dde..8d9f984 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ (Format: Year/Month/Day) Changes for 1.1: +*07/05/13: + * Added new placement policies {Row,Col}MinOverlapPlacement. They behave the + same as {Row,Col}SmartPlacement when the window fits but fall back on + minimizing overlap with other windows instead of CascadePlacement (Mark) + MinOverlapPlacement.cc/hh ScreenPlacement.cc/hh *07/05/12: * Changed interpretation of Horizontal/Vertical maximization of a window that is already maximized (Mark) diff --git a/src/Makefile.am b/src/Makefile.am index df18b97..0f3395e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -131,6 +131,7 @@ fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \ PlacementStrategy.hh \ CascadePlacement.hh CascadePlacement.cc \ ColSmartPlacement.hh ColSmartPlacement.cc \ + MinOverlapPlacement.hh MinOverlapPlacement.cc \ RowSmartPlacement.hh RowSmartPlacement.cc \ ScreenPlacement.hh ScreenPlacement.cc \ UnderMousePlacement.hh UnderMousePlacement.cc \ diff --git a/src/MinOverlapPlacement.cc b/src/MinOverlapPlacement.cc new file mode 100644 index 0000000..121109f --- /dev/null +++ b/src/MinOverlapPlacement.cc @@ -0,0 +1,182 @@ +// MinOverlapPlacement.cc +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR (*it) +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR (*it)WISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR (*it) +// DEALINGS IN THE SOFTWARE. + +// $Id$ + +#include "MinOverlapPlacement.hh" + +#include "Window.hh" +#include "Screen.hh" + +ScreenPlacement::PlacementPolicy MinOverlapPlacement::s_policy = ScreenPlacement::ROWMINOVERLAPPLACEMENT; +ScreenPlacement::RowDirection MinOverlapPlacement::s_row_dir = ScreenPlacement::LEFTRIGHT; +ScreenPlacement::ColumnDirection MinOverlapPlacement::s_col_dir = ScreenPlacement::TOPBOTTOM; + +MinOverlapPlacement::MinOverlapPlacement(ScreenPlacement::PlacementPolicy policy) { + s_policy = policy; +} + +bool MinOverlapPlacement::placeWindow( + const std::list &windowlist, + const FluxboxWindow &win, int &place_x, int &place_y) { + + // view (screen + head) constraints + int head = (signed) win.screen().getCurrHead(); + int head_left = (signed) win.screen().maxLeft(head); + int head_right = (signed) win.screen().maxRight(head); + int head_top = (signed) win.screen().maxTop(head); + int head_bot = (signed) win.screen().maxBottom(head); + + const ScreenPlacement &screen_placement = + dynamic_cast(win.screen().placementStrategy()); + s_row_dir = screen_placement.rowDirection(); + s_col_dir = screen_placement.colDirection(); + + int win_w = win.width() + win.fbWindow().borderWidth()*2 + win.widthOffset(); + int win_h = win.height() + win.fbWindow().borderWidth()*2 + win.heightOffset(); + + // we keep a set of open spaces on the desktop, sorted by size/location + std::set region_set; + + // initialize the set of regions to contain the entire head + region_set.insert(Region(Region::TOPLEFT, head_left, head_top)); + region_set.insert(Region(Region::TOPRIGHT, head_right - win_w, head_top)); + region_set.insert(Region(Region::BOTTOMLEFT, head_left, head_bot - win_h)); + region_set.insert(Region(Region::BOTTOMRIGHT, head_right - win_w, + head_bot - win_h)); + + // go through the list of windows, creating other reasonable placements + // at the end, we'll find the one with minimum overlap + // the size of this set is at most 2(n+2)(n+1) (n = number of windows) + // finding overlaps is therefore O(n^3), but it can probably be improved + std::list::const_reverse_iterator it = windowlist.rbegin(), + it_end = windowlist.rend(); + for (; it != it_end; ++it) { + + // get the dimensions of the window + int bottom = (*it)->y() + (*it)->height() + + 2*(*it)->frame().window().borderWidth(); + int top = (*it)->y(); + int right = (*it)->x() + (*it)->width() + + 2*(*it)->frame().window().borderWidth(); + int left = (*it)->x(); + + // go through the list of regions + // if this window overlaps that region and the new window still fits, + // it will create new regions to test + std::set::iterator reg_it = region_set.begin(); + for (; reg_it != region_set.end(); ++reg_it) { + + switch (reg_it->corner) { + case Region::TOPLEFT: + if (right > reg_it->x && bottom > reg_it->y) { + if (bottom + win_h <= head_bot) + region_set.insert(Region(Region::TOPLEFT, + reg_it->x, bottom)); + if (right + win_w <= head_right) + region_set.insert(Region(Region::TOPLEFT, + right, reg_it->y)); + } + break; + case Region::TOPRIGHT: + if (left < reg_it->x + win_w && bottom > reg_it->y) { + if (bottom + win_h <= head_bot) + region_set.insert(Region(Region::TOPRIGHT, + reg_it->x, bottom)); + if (left - win_w >= head_left) + region_set.insert(Region(Region::TOPRIGHT, + left - win_w, reg_it->y)); + } + break; + case Region::BOTTOMRIGHT: + if (left < reg_it->x + win_w && top < reg_it->y + win_h) { + if (top - win_h >= head_top) + region_set.insert(Region(Region::BOTTOMRIGHT, + reg_it->x, top - win_h)); + if (left - win_w >= head_left) + region_set.insert(Region(Region::BOTTOMRIGHT, + left - win_w, reg_it->y)); + } + break; + case Region::BOTTOMLEFT: + if (right > reg_it->x && top < reg_it->y + win_h) { + if (top - win_h >= head_top) + region_set.insert(Region(Region::BOTTOMLEFT, + reg_it->x, top - win_h)); + if (right + win_w <= head_right) + region_set.insert(Region(Region::BOTTOMLEFT, + right, reg_it->y)); + } + break; + } + + } + } + + // choose the region with minimum overlap + int min_so_far = win_w * win_h * windowlist.size() + 1; + std::set::iterator min_reg = region_set.end(); + + std::set::iterator reg_it = region_set.begin(); + for (; reg_it != region_set.end(); ++reg_it) { + + int overlap = 0; + it = windowlist.rbegin(); + for (; it != windowlist.rend(); ++it) { + + // get the dimensions of the window + int bottom = (*it)->y() + (*it)->height() + + 2*(*it)->frame().window().borderWidth(); + int top = (*it)->y(); + int right = (*it)->x() + (*it)->width() + + 2*(*it)->frame().window().borderWidth(); + int left = (*it)->x(); + + // get the coordinates of the overlap region + int min_right = (right > reg_it->x + win_w) ? + reg_it->x + win_w : right; + int min_bottom = (bottom > reg_it->y + win_h) ? + reg_it->y + win_h : bottom; + int max_left = (left > reg_it->x) ? left : reg_it->x; + int max_top = (top > reg_it->y) ? top : reg_it->y; + + // now compute the overlap and add to running total + if (min_right > max_left && min_bottom > max_top) + overlap += (min_right - max_left) * (min_bottom - max_top); + + } + + // if this placement is better, use it + if (overlap < min_so_far) { + min_reg = reg_it; + min_so_far = overlap; + if (overlap == 0) // can't do better than this + break; + } + + } + + // place window + place_x = min_reg->x; + place_y = min_reg->y; + + return true; +} diff --git a/src/MinOverlapPlacement.hh b/src/MinOverlapPlacement.hh new file mode 100644 index 0000000..20b5c95 --- /dev/null +++ b/src/MinOverlapPlacement.hh @@ -0,0 +1,83 @@ +// MinOverlapPlacement.hh +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// $Id$ + +#ifndef MINOVERLAPPLACEMENT_HH +#define MINOVERLAPPLACEMENT_HH + +#include "ScreenPlacement.hh" + +class MinOverlapPlacement: public PlacementStrategy { +public: + MinOverlapPlacement(ScreenPlacement::PlacementPolicy policy); + + bool placeWindow(const std::list &windowlist, + const FluxboxWindow &win, + int &place_x, int &place_y); + +private: + class Region { + public: + + enum Corner { + TOPLEFT, + TOPRIGHT, + BOTTOMLEFT, + BOTTOMRIGHT + } corner; // indicates the corner of the window that will be placed + + Region(Corner _corner, int _x, int _y): + corner(_corner), x(_x), y(_y) { }; + + // do all STL set implementations use this for sorting? + bool operator <(const Region &o) const { + // for now, I'm assuming RowSmartPlacement, so y is more important + switch (MinOverlapPlacement::s_policy) { + case ScreenPlacement::ROWMINOVERLAPPLACEMENT: + // if we're making rows, y-value is most important + if (y != o.y) + return ((y < o.y) ^ (s_col_dir == ScreenPlacement::BOTTOMTOP)); + if (x != o.x) + return ((x < o.x) ^ (s_row_dir == ScreenPlacement::RIGHTLEFT)); + return (corner < o.corner); + case ScreenPlacement::COLMINOVERLAPPLACEMENT: + // if we're making columns, x-value is most important + if (x != o.x) + return ((x < o.x) ^ (s_row_dir == ScreenPlacement::RIGHTLEFT)); + if (y != o.y) + return ((y < o.y) ^ (s_col_dir == ScreenPlacement::BOTTOMTOP)); + return (corner < o.corner); + default: + return false; + } + } + + // position where the top left corner of the window will be placed + int x, y; + }; + + static ScreenPlacement::PlacementPolicy s_policy; + static ScreenPlacement::RowDirection s_row_dir; + static ScreenPlacement::ColumnDirection s_col_dir; +}; + +#endif // MINOVERLAPPLACEMENT_HH diff --git a/src/ScreenPlacement.cc b/src/ScreenPlacement.cc index 6ba4b6f..fb79d88 100644 --- a/src/ScreenPlacement.cc +++ b/src/ScreenPlacement.cc @@ -25,6 +25,7 @@ #include "RowSmartPlacement.hh" +#include "MinOverlapPlacement.hh" #include "UnderMousePlacement.hh" #include "ColSmartPlacement.hh" #include "CascadePlacement.hh" @@ -68,6 +69,10 @@ bool ScreenPlacement::placeWindow(const std::list &windowlist, case COLSMARTPLACEMENT: m_strategy.reset(new ColSmartPlacement()); break; + case ROWMINOVERLAPPLACEMENT: + case COLMINOVERLAPPLACEMENT: + m_strategy.reset(new MinOverlapPlacement(*m_placement_policy)); + break; case CASCADEPLACEMENT: m_strategy.reset(new CascadePlacement(win.screen())); break; @@ -137,6 +142,10 @@ void FbTk::Resource::setFromString(const char *(*this) = ScreenPlacement::ROWSMARTPLACEMENT; else if (strcasecmp("ColSmartPlacement", str) == 0) *(*this) = ScreenPlacement::COLSMARTPLACEMENT; + else if (strcasecmp("RowMinOverlapPlacement", str) == 0) + *(*this) = ScreenPlacement::ROWMINOVERLAPPLACEMENT; + else if (strcasecmp("ColMinOverlapPlacement", str) == 0) + *(*this) = ScreenPlacement::COLMINOVERLAPPLACEMENT; else if (strcasecmp("UnderMousePlacement", str) == 0) *(*this) = ScreenPlacement::UNDERMOUSEPLACEMENT; else if (strcasecmp("CascadePlacement", str) == 0) @@ -152,6 +161,10 @@ std::string FbTk::Resource::getString() const return "RowSmartPlacement"; case ScreenPlacement::COLSMARTPLACEMENT: return "ColSmartPlacement"; + case ScreenPlacement::ROWMINOVERLAPPLACEMENT: + return "RowMinOverlapPlacement"; + case ScreenPlacement::COLMINOVERLAPPLACEMENT: + return "ColMinOverlapPlacement"; case ScreenPlacement::UNDERMOUSEPLACEMENT: return "UnderMousePlacement"; case ScreenPlacement::CASCADEPLACEMENT: diff --git a/src/ScreenPlacement.hh b/src/ScreenPlacement.hh index 4326c91..79d6c21 100644 --- a/src/ScreenPlacement.hh +++ b/src/ScreenPlacement.hh @@ -43,8 +43,10 @@ class ScreenPlacement: public PlacementStrategy { public: enum PlacementPolicy { ROWSMARTPLACEMENT, - COLSMARTPLACEMENT, - CASCADEPLACEMENT, + COLSMARTPLACEMENT, + COLMINOVERLAPPLACEMENT, + ROWMINOVERLAPPLACEMENT, + CASCADEPLACEMENT, UNDERMOUSEPLACEMENT }; -- cgit v0.11.2