// ScreenPlacement.cc // Copyright (c) 2006 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. #include "ScreenPlacement.hh" #include "RowSmartPlacement.hh" #include "MinOverlapPlacement.hh" #include "UnderMousePlacement.hh" #include "ColSmartPlacement.hh" #include "CascadePlacement.hh" #include "Screen.hh" #include "Window.hh" #include "FbTk/Menu.hh" #include #include #ifdef HAVE_CSTRING #include #else #include #endif using std::cerr; using std::endl; ScreenPlacement::ScreenPlacement(BScreen &screen): m_row_direction(screen.resourceManager(), LEFTRIGHT, screen.name()+".rowPlacementDirection", screen.altName()+".RowPlacementDirection"), m_col_direction(screen.resourceManager(), TOPBOTTOM, screen.name()+".colPlacementDirection", screen.altName()+".ColPlacementDirection"), m_placement_policy(screen.resourceManager(), ROWMINOVERLAPPLACEMENT, screen.name()+".windowPlacement", screen.altName()+".WindowPlacement"), m_old_policy(ROWSMARTPLACEMENT), m_strategy(0), m_screen(screen) { } bool ScreenPlacement::placeWindow(const FluxboxWindow &win, int head, int &place_x, int &place_y) { // check the resource placement and see if has changed // and if so update the strategy if (m_old_policy != *m_placement_policy || !m_strategy.get()) { m_old_policy = *m_placement_policy; switch (*m_placement_policy) { case ROWSMARTPLACEMENT: m_strategy.reset(new RowSmartPlacement()); break; case COLSMARTPLACEMENT: m_strategy.reset(new ColSmartPlacement()); break; case ROWMINOVERLAPPLACEMENT: case COLMINOVERLAPPLACEMENT: m_strategy.reset(new MinOverlapPlacement()); break; case CASCADEPLACEMENT: m_strategy.reset(new CascadePlacement(win.screen())); break; case UNDERMOUSEPLACEMENT: m_strategy.reset(new UnderMousePlacement()); break; } } // view (screen + head) constraints 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); // start placement, top left corner place_x = head_left; place_y = head_top; bool placed = false; try { placed = m_strategy->placeWindow(win, head, place_x, place_y); } catch (std::bad_cast & cast) { // This should not happen. // If for some reason we change the PlacementStrategy in Screen // from ScreenPlacement to something else then we might get // bad_cast from some placement strategies. cerr<<"Failed to place window: "<placeWindow(win, head, place_x, place_y); } int win_w = win.normalWidth() + win.fbWindow().borderWidth()*2 + win.widthOffset(), win_h = win.normalHeight() + win.fbWindow().borderWidth()*2 + win.heightOffset(); // make sure the window is inside our screen(head) area if (place_x + win_w - win.xOffset() > head_right) place_x = head_left + (head_right - head_left - win_w) / 2 + win.xOffset(); if (place_y + win_h - win.yOffset() > head_bot) place_y = head_top + (head_bot - head_top - win_h) / 2 + win.yOffset(); return true; } void ScreenPlacement::placeAndShowMenu(FbTk::Menu& menu, int x, int y, bool respect_struts) { int head = m_screen.getHead(x, y); menu.setScreen(m_screen.getHeadX(head), m_screen.getHeadY(head), m_screen.getHeadWidth(head), m_screen.getHeadHeight(head)); menu.updateMenu(); // recalculate the size x = x - (menu.width() / 2); if (menu.isTitleVisible()) y = y - (menu.titleWindow().height() / 2); // adjust (x, y) to fit on the screen if (!respect_struts) { int bw = 2 * menu.fbwindow().borderWidth(); std::pair pos = m_screen.clampToHead(head, x, y, menu.width() + bw, menu.height() + bw); x = pos.first; y = pos.second; } else { // do not cover toolbar if no title int top = static_cast(m_screen.maxTop(head)); int bottom = static_cast(m_screen.maxBottom(head)); int left = static_cast(m_screen.maxLeft(head)); int right = static_cast(m_screen.maxRight(head)); if (y < top) y = top; else if (y + static_cast(menu.height()) >= bottom) y = bottom - menu.height() - 1 - menu.fbwindow().borderWidth(); if (x < left) x = left; else if (x + static_cast(menu.width()) >= right) x = right - static_cast(menu.width()) - 1; } menu.move(x, y); menu.show(); menu.grabInputFocus(); } ////////////////////// Placement Resources namespace FbTk { template <> std::string FbTk::Resource::getString() const { switch (*(*this)) { case ScreenPlacement::ROWSMARTPLACEMENT: 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: return "CascadePlacement"; } return "RowSmartPlacement"; } template <> void FbTk::Resource::setFromString(const char *str) { if (strcasecmp("RowSmartPlacement", str) == 0) *(*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) *(*this) = ScreenPlacement::CASCADEPLACEMENT; else setDefaultValue(); } template <> std::string FbTk::Resource::getString() const { switch (*(*this)) { case ScreenPlacement::LEFTRIGHT: return "LeftToRight"; case ScreenPlacement::RIGHTLEFT: return "RightToLeft"; } return "LeftToRight"; } template <> void FbTk::Resource::setFromString(const char *str) { if (strcasecmp("LeftToRight", str) == 0) *(*this) = ScreenPlacement::LEFTRIGHT; else if (strcasecmp("RightToLeft", str) == 0) *(*this) = ScreenPlacement::RIGHTLEFT; else setDefaultValue(); } template <> std::string FbTk::Resource::getString() const { switch (*(*this)) { case ScreenPlacement::TOPBOTTOM: return "TopToBottom"; case ScreenPlacement::BOTTOMTOP: return "BottomToTop"; } return "TopToBottom"; } template <> void FbTk::Resource::setFromString(const char *str) { if (strcasecmp("TopToBottom", str) == 0) *(*this) = ScreenPlacement::TOPBOTTOM; else if (strcasecmp("BottomToTop", str) == 0) *(*this) = ScreenPlacement::BOTTOMTOP; else setDefaultValue(); } } // end namespace FbTk