//
// Harbour.cc for pekwm
// Copyright (C) 2003-2004 Claes Nasten <pekdon{@}pekdon{.}net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#include "../config.h"
#ifdef HARBOUR
#include "Harbour.hh"
#include "PScreen.hh"
#include "Config.hh"
#include "PWinObj.hh"
#include "DockApp.hh"
#include "Workspaces.hh"
#include "AutoProperties.hh"
#ifdef MENUS
#include "PDecor.hh"
#include "PMenu.hh"
#include "HarbourMenu.hh"
#endif // MENUS
#include <algorithm>
#include <functional>
using std::list;
using std::mem_fun;
using std::find;
#ifdef DEBUG
#include <iostream>
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),
#ifdef MENUS
_harbour_menu(NULL),
#endif // MENUS
_size(0), _strut(NULL),
_last_button_x(0), _last_button_y(0)
{
_strut = new Strut();
_scr->addStrut(_strut);
#ifdef HAVE_XINERAMA
_strut->head = Config::instance()->getHarbourHead();
#endif // HAVE_XINERAMA
#ifdef MENUS
_harbour_menu = new HarbourMenu(_scr, _theme, this);
#endif // MENUS
}
//! @brief Harbour destructor
Harbour::~Harbour(void)
{
removeAllDockApps();
_scr->removeStrut(_strut);
delete _strut;
#ifdef MENUS
if (_harbour_menu != NULL) {
delete _harbour_menu;
}
#endif // MENUS
}
//! @brief Adds a DockApp to the Harbour
//! @todo Add sort option
void
Harbour::addDockApp(DockApp *da)
{
if (da == NULL) {
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() == false) { // make sure it's visible
da->mapWindow();
}
updateHarbourSize();
}
//! @brief Removes a DockApp from the Harbour
void
Harbour::removeDockApp(DockApp *da)
{
if (da == NULL)
return;
list<DockApp*>::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<DockApp*>::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 = NULL;
list<DockApp*>::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 = NULL;
list<DockApp*>::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<DockApp*>::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)
{
#ifdef HAVE_XINERAMA
PScreen::instance()->removeStrut(_strut);
if (Config::instance()->isHarbourOntop()) {
PScreen::instance()->addStrut(_strut);
}
#endif // HAVE_XINERAMA
uint l = Config::instance()->isHarbourOntop() ? LAYER_DOCK : LAYER_DESKTOP;
list<DockApp*>::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)
{
#ifdef HAVE_XINERAMA
_strut->head = Config::instance()->getHarbourHead();
#endif // HAVE_XINERAMA
if (AutoProperties::instance()->isHarbourSort ())
placeDockAppsSorted();
else
{
list<DockApp*>::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<DockApp*>::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 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 == 0)
return;
_last_button_x = ev->x;
_last_button_y = ev->y;
#ifdef MENUS
// 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();
}
}
#endif // MENUS
}
//! @brief Initiates moving of a DockApp based on info from a XMotionEvent.
void
Harbour::handleMotionNotifyEvent(XMotionEvent* ev, DockApp* da)
{
if (da == NULL)
return;
Geometry head;
int x = 0, y = 0;
#ifdef HAVE_XINERAMA
_scr->getHeadInfo(Config::instance()->getHarbourHead(), head);
#else // !HAVE_XINERAMA
_scr->getHeadInfo(0, head);
#endif // HAVE_XINERAMA
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 == NULL)
return;
list<DockApp*>::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;
#ifdef HAVE_XINERAMA
PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head);
#else // !HAVE_XINERAMA
PScreen::instance()->getHeadInfo(0, head);
#endif // HAVE_XINERAMA
getPlaceStartPosition (da, x, y, x_place);
if (right)
{
if (x_place)
x -= da->getWidth ();
else
y -= da->getHeight ();
}
list<DockApp*>::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() < signed(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() < signed(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<DockApp*>::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 ();
}
}
}
}
//! @brief
void
Harbour::placeDockAppInsideScreen(DockApp *da)
{
Geometry head;
#ifdef HAVE_XINERAMA
PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head);
#else // !HAVE_XINEAMA
PScreen::instance()->getHeadInfo(0, head);
#endif // HAVE_XINERAMA
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() > signed(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() > signed(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() > signed(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() > signed(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;
#ifdef HAVE_XINERAMA
PScreen::instance()->getHeadInfo(Config::instance()->getHarbourHead(), head);
#else // !HAVE_XINEAMA
PScreen::instance()->getHeadInfo(0, head);
#endif // HAVE_XINERAMA
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<DockApp*>::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);
}
#endif // HARBOUR
syntax highlighted by Code2HTML, v. 0.9.1