// // Harbour.cc for pekwm // Copyright © 2003-2009 Claes Nästen // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include "Harbour.hh" #include "PScreen.hh" #include "Config.hh" #include "PWinObj.hh" #include "DockApp.hh" #include "Workspaces.hh" #include "AutoProperties.hh" #include "PDecor.hh" #include "PMenu.hh" #include "HarbourMenu.hh" #include #include using std::list; using std::mem_fun; using std::find; #ifdef DEBUG #include using std::cerr; using std::endl; #endif // DEBUG //! @brief Harbour constructor Harbour::Harbour(PScreen *s, Theme *t, Workspaces *w) : _scr(s), _theme(t), _workspaces(w), _harbour_menu(0), _hidden(false), _size(0), _strut(0), _last_button_x(0), _last_button_y(0) { _strut = new Strut(); _scr->addStrut(_strut); _strut->head = Config::instance()->getHarbourHead(); _harbour_menu = new HarbourMenu(_scr, _theme, this); } //! @brief Harbour destructor Harbour::~Harbour(void) { removeAllDockApps(); _scr->removeStrut(_strut); delete _strut; delete _harbour_menu; } //! @brief Adds a DockApp to the Harbour //! @todo Add sort option void Harbour::addDockApp(DockApp *da) { if (! da) { return; } // add to the list if (AutoProperties::instance()->isHarbourSort()) { insertDockAppSorted(da); placeDockAppsSorted(); // place in sorted way } else { _da_list.push_back(da); placeDockApp(da); // place it in a empty space } da->setLayer(Config::instance()->isHarbourOntop() ? LAYER_DOCK : LAYER_DESKTOP); _workspaces->insert(da); // add the dockapp to the stacking list if (! da->isMapped()) { // make sure it's visible da->mapWindow(); } #ifdef OPACITY da->setOpacity(Config::instance()->getHarbourOpacity()); #endif // OPACITY updateHarbourSize(); } //! @brief Removes a DockApp from the Harbour void Harbour::removeDockApp(DockApp *da) { if (! da) return; list::iterator it(find(_da_list.begin(), _da_list.end(), da)); if (it != _da_list.end()) { _da_list.remove(da); _workspaces->remove(da); // remove the dockapp to the stacking list delete da; if (AutoProperties::instance()->isHarbourSort()) { placeDockAppsSorted(); } } updateHarbourSize(); } //! @brief Removes all DockApps from the Harbour void Harbour::removeAllDockApps(void) { list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { _workspaces->remove(*it); // remove the dockapp to the stacking list delete (*it); } _da_list.clear(); } //! @brief Tries to find a dockapp which uses the window win DockApp* Harbour::findDockApp(Window win) { DockApp *dockapp = 0; list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { if ((*it)->findDockApp(win)) { dockapp = (*it); break; } } return dockapp; } //! @brief Tries to find a dockapp which has the window win as frame. DockApp* Harbour::findDockAppFromFrame(Window win) { DockApp *dockapp = 0; list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { if ((*it)->findDockAppFromFrame(win)) { dockapp = (*it); break; } } return dockapp; } #ifdef HAVE_XRANDR //! @brief Refetches the root-window size and takes appropriate actions. void Harbour::updateGeometry(void) { list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { placeDockAppInsideScreen(*it); } } #endif // HAVE_XRANDR //! @brief Lowers or Raises all the DockApps in the harbour. void Harbour::restack(void) { PScreen::instance()->removeStrut(_strut); if (Config::instance()->isHarbourOntop() || ! Config::instance()->isHarbourMaximizeOver()) { PScreen::instance()->addStrut(_strut); } uint l = Config::instance()->isHarbourOntop() ? LAYER_DOCK : LAYER_DESKTOP; list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { (*it)->setLayer(l); if (Config::instance()->isHarbourOntop()) { _workspaces->raise(*it); } else { _workspaces->lower(*it); } } } //! @brief Goes through the DockApp list and places the dockapp. void Harbour::rearrange(void) { _strut->head = Config::instance()->getHarbourHead(); if (AutoProperties::instance()->isHarbourSort ()) placeDockAppsSorted(); else { list::iterator it (_da_list.begin ()); for (; it != _da_list.end (); ++it) placeDockApp (*it); } } //! @brief Repaints all dockapps with the new theme void Harbour::loadTheme(void) { for_each(_da_list.begin(), _da_list.end(), mem_fun(&DockApp::loadTheme)); } //! @brief Updates the harbour max size variable. void Harbour::updateHarbourSize(void) { _size = 0; list::iterator it(_da_list.begin()); for (; it != _da_list.end(); ++it) { switch (Config::instance()->getHarbourPlacement()) { case TOP: case BOTTOM: if ((*it)->getHeight() > _size) _size = (*it)->getHeight(); break; case LEFT: case RIGHT: if ((*it)->getWidth() > _size) _size = (*it)->getWidth(); break; default: // Do nothing. break; } } updateStrutSize(); } //! @brief Sets the Hidden state of the harbour //! @param sa StateAction specifying state to set. void Harbour::setStateHidden(StateAction sa) { // Check if there is anything to do if (! ActionUtil::needToggle(sa, _hidden)) { return; } if (_hidden) { // Show if currently hidden. for_each(_da_list.begin(), _da_list.end(), mem_fun(&DockApp::mapWindow)); } else { // Hide if currently visible. for_each(_da_list.begin(), _da_list.end(), mem_fun(&DockApp::unmapWindow)); } _hidden = !_hidden; } //! @brief Updates Harbour strut size. void Harbour::updateStrutSize(void) { _strut->left = _strut->right = _strut->top = _strut->bottom = 0; if (! Config::instance()->isHarbourMaximizeOver()) { switch (Config::instance()->getHarbourPlacement()) { case TOP: _strut->top = _size; break; case BOTTOM: _strut->bottom = _size; break; case LEFT: _strut->left = _size; break; case RIGHT: _strut->right = _size; break; } } _scr->updateStrut(); } //! @brief Handles XButtonEvents made on the DockApp's frames. void Harbour::handleButtonEvent(XButtonEvent* ev, DockApp* da) { if (! da) { return; } _last_button_x = ev->x; _last_button_y = ev->y; // FIXME: Make configurable if (ev->type == ButtonPress) { if (ev->button == BUTTON3) { if (_harbour_menu->isMapped()) { _harbour_menu->unmapWindow(); } else { _harbour_menu->setDockApp(da); _harbour_menu->mapUnderMouse(); } } else if (_harbour_menu->isMapped()) { _harbour_menu->unmapWindow(); } } } //! @brief Initiates moving of a DockApp based on info from a XMotionEvent. void Harbour::handleMotionNotifyEvent(XMotionEvent* ev, DockApp* da) { if (! da) { return; } Geometry head; int x = 0, y = 0; _scr->getHeadInfo(Config::instance()->getHarbourHead(), head); switch(Config::instance()->getHarbourPlacement()) { case TOP: case BOTTOM: x = ev->x_root - _last_button_x; y = da->getY(); if (x < head.x) { x = head.x; } else if ((x + da->getWidth()) > (head.x + head.width)) { x = head.x + head.width - da->getWidth(); } break; case LEFT: case RIGHT: x = da->getX(); y = ev->y_root - _last_button_y; if (y < head.y) { y = head.y; } else if ((y + da->getHeight()) > (head.y + head.height)) { y = head.y + head.height - da->getHeight(); } break; default: // do nothing break; } da->move(x, y); } //! @brief Handles XConfigureRequestEvents. void Harbour::handleConfigureRequestEvent(XConfigureRequestEvent* ev, DockApp* da) { if (! da) { return; } list::iterator it(find(_da_list.begin(), _da_list.end(), da)); if (it != _da_list.end()) { // Thing is that we doesn't listen to border width, position or // stackign so the only thing that we'll alter is size if that's // what we want to configure uint width = (ev->value_mask&CWWidth) ? ev->width : da->getClientWidth(); uint height = (ev->value_mask&CWHeight) ? ev->height : da->getClientHeight(); da->resize(width, height); placeDockAppInsideScreen(da); } } //! @brief Tries to find a empty spot for the DockApp void Harbour::placeDockApp(DockApp *da) { if (! da || ! _da_list.size ()) return; bool right = (Config::instance()->getHarbourOrientation() == BOTTOM_TO_TOP); int test, x = 0, y = 0; bool placed = false, increase = false, x_place = false; Geometry head; PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head); getPlaceStartPosition (da, x, y, x_place); if (right) { if (x_place) { x -= da->getWidth (); } else { y -= da->getHeight (); } } list::iterator it; if (x_place) { x = test = right ? head.x + head.width - da->getWidth() : head.x; while (! placed && (right ? (test >= 0) : ((test + da->getWidth()) < (head.x + head.width)))) { placed = increase = true; it = _da_list.begin(); for (; placed && (it != _da_list.end()); ++it) { if ((*it) == da) { continue; // exclude ourselves } if (((*it)->getX() < static_cast(test + da->getWidth())) && ((*it)->getRX() > test)) { placed = increase = false; test = right ? (*it)->getX() - da->getWidth() : (*it)->getRX(); } } if (placed) { x = test; } else if (increase) { test += right ? -1 : 1; } } } else { // !x_place y = test = right ? head.y + head.height - da->getHeight() : head.y; while (! placed && (right ? (test >= 0) : ((test + da->getHeight()) < (head.y + head.height)))) { placed = increase = true; it = _da_list.begin(); for (; placed && (it != _da_list.end()); ++it) { if ((*it) == da) { continue; // exclude ourselves } if (((*it)->getY() < static_cast(test + da->getHeight())) && ((*it)->getBY() > test)) { placed = increase = false; test = right ? (*it)->getY() - da->getHeight() : (*it)->getBY(); } } if (placed) { y = test; } else if (increase) { test += right ? -1 : 1; } } } da->move (x, y); } //! @brief Inserts DockApp and places all dockapps in sorted order //! @todo Screen boundary checking void Harbour::placeDockAppsSorted(void) { if (! _da_list.size ()) { return; } // place the dockapps int x, y, x_real, y_real; bool inc_x = false; bool right = (Config::instance ()->getHarbourOrientation () == BOTTOM_TO_TOP); getPlaceStartPosition (_da_list.front (), x_real, y_real, inc_x); list::iterator it (_da_list.begin ()); for (; it != _da_list.end (); ++it) { getPlaceStartPosition (*it, x, y, inc_x); if (inc_x) { if (right) { (*it)->move (x_real - (*it)->getWidth (), y); x_real -= -(*it)->getWidth (); } else { (*it)->move (x_real, y); x_real += (*it)->getWidth (); } } else { if (right) { (*it)->move (x, y_real - (*it)->getHeight ()); y_real -= (*it)->getHeight (); } else { (*it)->move (x, y_real); y_real += (*it)->getHeight (); } } } } /** * Make sure dock app is inside screen boundaries and placed on the * right edge, usually called after resizing the dockapp. */ void Harbour::placeDockAppInsideScreen(DockApp *da) { Geometry head; PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head); uint pos = Config::instance()->getHarbourPlacement(); // top or bottom placement if ((pos == TOP) || (pos == BOTTOM)) { // check horizontal position if (da->getX() < head.x) { da->move(head.x, da->getY()); } else if (da->getRX() > static_cast(head.x + head.width)) { da->move(head.x + head.width - da->getWidth(), da->getY()); } // check vertical position if ((pos == TOP) && (da->getY() != head.y)) { da->move(da->getX(), head.y); } else if ((pos == BOTTOM) && (da->getBY() != static_cast(head.y + head.height))) { da->move(da->getX(), head.y + head.height - da->getHeight()); } // left or right placement } else { // check vertical position if (da->getY() < head.y) { da->move(da->getX(), head.y); } else if (da->getBY() > static_cast(head.y + head.height)) { da->move(da->getX(), head.y + head.height - da->getHeight()); } // check horizontal position if ((pos == LEFT) && (da->getX() != head.x)) { da->move(head.x, da->getY()); } else if ((pos == RIGHT) && (da->getRX() != static_cast(head.x + head.width))) { da->move(head.x + head.width - da->getWidth(), da->getY()); } } } //! @brief void Harbour::getPlaceStartPosition(DockApp *da, int &x, int &y, bool &inc_x) { if (! da) { return; } Geometry head; PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head); bool right = (Config::instance()->getHarbourOrientation() == BOTTOM_TO_TOP); switch (Config::instance()->getHarbourPlacement()) { case TOP: inc_x = true; x = right ? head.x + head.width : head.x; y = head.y; break; case BOTTOM: inc_x = true; x = right ? head.x + head.width : head.x; y = head.y + head.height - da->getHeight(); break; case LEFT: x = head.x; y = right ? head.y + head.height : head.y; break; case RIGHT: x = head.x + head.width - da->getWidth(); y = right ? head.y + head.height : head.y; break; } } //! @brief Inserts DockApp in sorted order in the list void Harbour::insertDockAppSorted(DockApp *da) { list::iterator it(_da_list.begin()); // The order of this list doesn't make much sense to me when // it comes to representing it in code, however it's perfectly sane // for representing the order in the config files. // anyway, order goes as follows: 1 2 3 0 0 0 -3 -2 -1 // Middle of the list. if (da->getPosition () == 0) { for (; (it != _da_list.end ()) && ((*it)->getPosition () >= 0); ++it) ; // Beginning of the list. } else if (da->getPosition () > 0) { for (; it != _da_list.end (); ++it) { if (((*it)->getPosition () < 1) || // got to 0 or less (da->getPosition () <= (*it)->getPosition ())) { break; } } // end of the list } else { for (; (it != _da_list.end ()) && ((*it)->getPosition () >= 0); ++it) ; for (; (it != _da_list.end ()) && (da->getPosition () >= (*it)->getPosition ()); ++it) ; } _da_list.insert (it, da); }