//
// Workspaces.cc for pekwm
// Copyright (C) 2002-2004 Claes Nasten <pekdo at pekdon dot net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//

#include "../config.h"
#include "Workspaces.hh"

#include "PScreen.hh"
#include "Atoms.hh"
#include "ParseUtil.hh"
#include "Config.hh"
#include "PWinObj.hh"
#include "PDecor.hh"
#include "Frame.hh"
#include "Client.hh" // For isSkip()
#include "Viewport.hh"

#ifdef HAVE_LIMITS
#include <limits>
using std::numeric_limits;
#endif // HAVE_LIMITS

extern "C" {
#include <X11/Xatom.h> // for XA_WINDOW
}

using std::list;
using std::vector;
using std::string;
using std::find;
#ifdef DEBUG
#include <iostream>
using std::cerr;
using std::endl;
#endif // DEBUG

// Workspaces::Workspace

Workspaces::Workspace::Workspace(const std::string &name, uint number,
																 const std::list<PWinObj*> &wo_list) :
_name(name), _number(number),
_viewport(NULL), _wo_list(wo_list), _last_focused(NULL)
{
	_viewport = new Viewport(number, _wo_list);
}

Workspaces::Workspace::~Workspace(void)
{
	delete _viewport;
}

// Workspaces

Workspaces *Workspaces::_instance = NULL;


//! @brief Workpsaces constructor
Workspaces::Workspaces(uint number) :
_active(0), _previous(0)
{
#ifdef DEBUG
	if (_instance != NULL) {
		cerr << __FILE__ << "@" << __LINE__ << ": "
				 << "Workspaces(" << this << ")::Workspaces(" << number << ")" << endl
				 << " *** _instance allready set: " << _instance << endl;
	}
#endif // DEBUG
	_instance = this;

	if (number < 1) {
#ifdef DEBUG
		cerr << __FILE__ << "@" << __LINE__ << ": "
				 << "Workspaces(" << this << ")::Workspaces(" << number << ")"
				 << " *** number < 1" << endl;
#endif // DEBUG
		number = 1;
	}

	// create new workspaces
	for (uint i = 0; i < number; ++i) {
		_workspace_list.push_back(new Workspace("", i, _wo_list));
	}
}

//! @brief Workspaces destructor
Workspaces::~Workspaces(void)
{
	vector<Workspace*>::iterator it(_workspace_list.begin());
	for (; it != _workspace_list.end(); ++it) {
		delete *it;
	}

	_instance = NULL;
}

//! @brief Sets total amount of workspaces to number
void
Workspaces::setSize(uint number)
{
	if (number < 1) {
		number = 1;
	}

	if (number == _workspace_list.size()) {
		return; // no need to change number of workspaces to the current number
	}

	uint before = _workspace_list.size();
	if (_active >= number) {
		_active = number - 1;
	}
        if (_previous >= number) {
          _previous = number - 1;
        }

	// We have more workspaces than we want, lets remove the last ones
	if (before > number) {
		list<PWinObj*>::iterator it(_wo_list.begin());
		for (; it != _wo_list.end(); ++it) {
			if ((*it)->getWorkspace() > (number - 1))
				(*it)->setWorkspace(number - 1);
		}

		for (uint i = before - 1; i >= number; --i) {
			delete _workspace_list[i];
		}

		_workspace_list.resize(number, NULL);

	} else { // We need more workspaces, lets create some
		for (uint i = before; i < number; ++i) {
			_workspace_list.push_back(new Workspace("", i, _wo_list));
		}
	}

	// Tell the rest of the world how many workspaces we have.
	XChangeProperty(PScreen::instance()->getDpy(),
									PScreen::instance()->getRoot(),
									EwmhAtoms::instance()->getAtom(NET_NUMBER_OF_DESKTOPS),
									XA_CARDINAL, 32, PropModeReplace,
									(uchar *) &number, 1);

	// make sure we aren't on an non-existent workspace
	if (number <= _active) {
		setWorkspace(number - 1, true);
	}
}

//! @brief Activates Workspace workspace and sets the right hints
//! @param num Workspace to activate
//! @param focus wheter or not to focus a window after switch
void
Workspaces::setWorkspace(uint num, bool focus)
{
	if ((num == _active) || ( num >= _workspace_list.size())) {
		return;
	}

	PScreen::instance()->grabServer();

	// save the focused window object
	setLastFocused(_active, PWinObj::getFocusedPWinObj());
	PWinObj::setFocusedPWinObj(NULL);

	// switch workspace
	hideAll(_active);
	AtomUtil::setLong(PScreen::instance()->getRoot(),
										EwmhAtoms::instance()->getAtom(NET_CURRENT_DESKTOP),
										num);
	unhideAll(num, focus);

	PScreen::instance()->ungrabServer(true);
}

//! @brief
bool
Workspaces::gotoWorkspace(uint direction, bool warp)
{
	uint workspace;
	int dir = 0;
        // Using a bool flag to detect changes due to special workspaces such
        // as PREV
	bool switched = true;

	switch (direction) {
	case WORKSPACE_LEFT:
	case WORKSPACE_PREV:
		dir = 1;

		if (_active > 0) {
			workspace = _active - 1;
		} else if (direction == WORKSPACE_PREV) {
			workspace = _workspace_list.size() - 1;
		} else {
			switched = false;
		}
		break;
	case WORKSPACE_NEXT:
	case WORKSPACE_RIGHT:
		dir = -1;

		if ((_active + 1) < _workspace_list.size()) {
			workspace = _active + 1;
		} else if (direction == WORKSPACE_NEXT) {
			workspace = 0;
		} else {
			switched = false;
		}
		break;
        case WORKSPACE_LAST:
          workspace = _previous;
          if (_active == workspace) {
            switched = false;
          }
          break;
	default:
		if (direction == _active) {
			switched = false;
		} else {
			workspace = direction;
		}
	}

	if (switched) {
		if (warp) {
			warpToWorkspace(workspace, dir);
		} else {
			setWorkspace(workspace, true);
		}
	}

	return switched;
}

//! @brief
bool
Workspaces::warpToWorkspace(uint num, int dir)
{
	if ((num == _active) || ( num >= _workspace_list.size())) {
		return false;
	}

	int x, y, warp;
	PScreen::instance()->getMousePosition(x, y);

	warp = Config::instance()->getScreenEdgeSize();
	warp = (warp > 0) ? (warp * 2) : 2;

	if (dir != 0) {
		if (dir > 0) {
			x = PScreen::instance()->getWidth() - warp;
		} else {
			x = warp;
		}

		// warp pointer
		XWarpPointer(PScreen::instance()->getDpy(), None,
								 PScreen::instance()->getRoot(),
								 0, 0, 0, 0, x, y);
	}

	// set workpsace
	setWorkspace(num, true);

	return true;
}

//! @brief Adds a PWinObj to the stacking list.
//! @param wo PWinObj to insert
//! @param raise Defaults to true, wheter to check for bottom or top.
void
Workspaces::insert(PWinObj* wo, bool raise)
{
	bool inserted = false;

	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end() && !inserted; ++it) {
		if (raise
				? (wo->getLayer() < (*it)->getLayer())
				: (wo->getLayer() <= (*it)->getLayer())) {
			_wo_list.insert(it, wo);

			stackWinUnderWin((*it)->getWindow(), wo->getWindow());

			inserted = true;
		}
	}

	if (!inserted) {
		_wo_list.push_back(wo);
		// we can't do raise here as it'll end up in an recursive loop
		// if we use Workspaces::raise to restack the PWinObj.
		XRaiseWindow(PScreen::instance()->getDpy(), wo->getWindow());
	}
}

//! @brief Removes a PWinObj from the stacking list.
void
Workspaces::remove(PWinObj* wo)
{
	_wo_list.remove(wo);

	// remove from last focused
	vector<Workspace*>::iterator it(_workspace_list.begin());
	for (; it != _workspace_list.end(); ++it) {
		if (wo == (*it)->getLastFocused())
			(*it)->setLastFocused(NULL);
	}
}

//! @brief Hides all non-sticky Frames on the workspace.
void
Workspaces::hideAll(uint workspace)
{
	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end(); ++it) {
		if (((*it)->isSticky() == false) && ((*it)->isHidden() == false) &&
				((*it)->getWorkspace() == workspace)) {
			(*it)->unmapWindow();
		}
	}
}

//! @brief Unhides all hidden PWinObjs on the workspace.
void
Workspaces::unhideAll(uint workspace, bool focus)
{
	if (workspace >= _workspace_list.size())
		return;
        _previous = _active;
	_active = workspace;
        

	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end(); ++it) {
		if (!(*it)->isMapped() && !(*it)->isIconified() && !(*it)->isHidden()
				&& ((*it)->getWorkspace() == workspace)) {
			(*it)->mapWindow(); // don't restack ontop windows
		}
	}

	// Try to focus last focused window and if that fails we get the top-most
	// Frame if any and give it focus.
	if (focus) {
		PWinObj *wo = _workspace_list[workspace]->getLastFocused();
		if (!wo || !PWinObj::windowObjectExists(wo))
			wo = getTopWO(PWinObj::WO_FRAME);

		if (wo) {
			if (wo->getType() == PWinObj::WO_FRAME) {
				wo = static_cast<Frame*>(wo)->getActiveChild();
			}

			wo->giveInputFocus();
			PWinObj::setFocusedPWinObj(wo);
		}
		
		// If focusing fails, focus the root window.
		if (!PWinObj::getFocusedPWinObj()
				|| !PWinObj::getFocusedPWinObj()->isMapped()) {
			PWinObj::getRootPWinObj()->giveInputFocus();
		}
	}

	_workspace_list[_active]->getViewport()->hintsUpdate();
}

//! @brief Raises a PWinObj and restacks windows.
void
Workspaces::raise(PWinObj* wo)
{
	list<PWinObj*>::iterator it(find(_wo_list.begin(), _wo_list.end(), wo));

	if (it == _wo_list.end()) // no Frame to raise.
		return;
	_wo_list.erase(it);

	insert(wo, true); // reposition and restack
}

//! @brief Lower a PWinObj and restacks windows.
void
Workspaces::lower(PWinObj* wo)
{
	list<PWinObj*>::iterator it(find(_wo_list.begin(), _wo_list.end(), wo));

	if (it == _wo_list.end()) // no Frame to raise.
		return;
	_wo_list.erase(it);

	insert(wo, false); // reposition and restack
}

//! @brief Places the PWinObj above the window win
//! @param wo PWinObj to place.
//! @param win Window to place Frame above.
//! @param restack Restack the X windows, defaults to true.
void
Workspaces::stackAbove(PWinObj *wo, Window win, bool restack)
{
	list<PWinObj*>::iterator old_pos(find(_wo_list.begin(), _wo_list.end(), wo));

	if (old_pos != _wo_list.end()) {
		list<PWinObj*>::iterator it(_wo_list.begin());
		for (; it != _wo_list.end(); ++it) {
			if (win == (*it)->getWindow()) {
				_wo_list.erase(old_pos);
				_wo_list.insert(++it, wo);

				// Before restacking make sure we are the active frame
				// also that there are two different frames
				if (restack)
					stackWinUnderWin((*--it)->getWindow(), wo->getWindow());
				break;
			}
		}
	}
}

//! @brief Places the PWinObj below the window win
//! @param wo PWinObj to place.
//! @param win Window to place Frame under
//! @param restack Restack the X windows, defaults to true
void
Workspaces::stackBelow(PWinObj* wo, Window win, bool restack)
{
	list<PWinObj*>::iterator old_pos(find(_wo_list.begin(), _wo_list.end(), wo));

	if (old_pos != _wo_list.end()) {
		list<PWinObj*>::iterator it(_wo_list.begin());
		for (; it != _wo_list.end(); ++it) {
			if (win == (*it)->getWindow()) {
				_wo_list.erase(old_pos);
				_wo_list.insert(it, wo);

				if (restack)
					stackWinUnderWin((*it)->getWindow(), wo->getWindow());
				break;
			}
		}
	}
}


//! @brief
PWinObj*
Workspaces::getLastFocused(uint workspace)
{
	if (workspace >= _workspace_list.size())
		return NULL;
	return _workspace_list[workspace]->getLastFocused();
}

//! @brief
void
Workspaces::setLastFocused(uint workspace, PWinObj* wo)
{
	if (workspace >= _workspace_list.size())
		return;
	_workspace_list[workspace]->setLastFocused(wo);
}

//! @brief Helper function to stack a window below another
//! @param win_over Window to place win_under under
//! @param win_under Window to place under win_over
void
Workspaces::stackWinUnderWin(Window over, Window under)
{
	if (over == under)
		return;

	Window windows[2] = { over, under };
	XRestackWindows(PScreen::instance()->getDpy(), windows, 2);
}

// MISC METHODS

//! @brief Returns the first focusable PWinObj with the highest stacking
PWinObj*
Workspaces::getTopWO(uint type_mask)
{
	list<PWinObj*>::reverse_iterator r_it = _wo_list.rbegin();
	for (; r_it != _wo_list.rend(); ++r_it) {
		if ((*r_it)->isMapped()
				&& (*r_it)->isFocusable()
				&& ((*r_it)->getType()&type_mask)) {
			return (*r_it);
		}
	}
	return NULL;
}

//! @brief Updates the Ewmh Client list and Stacking list hint.
void
Workspaces::updateClientStackingList(bool client, bool stacking)
{
	Frame *it_frame;
	list<Window> win_list;

	// Find clients we are going to include in the list
	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end(); ++it) {
		if ((*it)->getType() != PWinObj::WO_FRAME)
			continue;

		it_frame = (Frame*) (*it);
		if (!static_cast<Client*>(it_frame->getActiveChild())->skipTaskbar()) {
			win_list.push_back(it_frame->getActiveChild()->getWindow());
		}
	}

	if (!win_list.size())
		return;

	Window *windows = new Window[win_list.size()];
	copy(win_list.begin(), win_list.end(), windows);

	if (client) {
		AtomUtil::setWindows(PScreen::instance()->getRoot(),
												 EwmhAtoms::instance()->getAtom(NET_CLIENT_LIST),
												 windows, win_list.size());
	}
	if (stacking) {
		AtomUtil::setWindows(PScreen::instance()->getRoot(),
												 EwmhAtoms::instance()->getAtom(NET_CLIENT_LIST_STACKING),
												 windows, win_list.size());
	}

	delete [] windows;
}

// PLACEMENT ROUTINES

//! @brief Tries to place the Wo.
void
Workspaces::placeWo(PWinObj *wo, Window parent)
{
	bool placed = false;

	list<uint>::iterator it(Config::instance()->getPlacementModelBegin());
	for (; (placed != true) &&
				 (it != Config::instance()->getPlacementModelEnd()); ++it) {
		switch (*it) {
		case PLACE_SMART:
			placed = placeSmart(wo);
			break;
		case PLACE_MOUSE_NOT_UNDER:
			placed = placeMouseNotUnder(wo);
			break;
		case PLACE_MOUSE_CENTERED:
			placed = placeMouseCentered(wo);
			break;
		case PLACE_MOUSE_TOP_LEFT:
			placed = placeMouseTopLeft(wo);
			break;
		case PLACE_CENTERED_ON_PARENT:
			placed = placeCenteredOnParent(wo, parent);
			break;
		default:
			// do nothing
			break;
		}
	}
}

//! @brief Tries to find empty space to place the client in
//! @return true if client got placed, else false
//! @todo What should we do about Xinerama as when we don't have it enabled we care about the struts.
bool
Workspaces::placeSmart(PWinObj* wo)
{
	PWinObj *wo_e;
	bool placed = false;

	int step_x = (Config::instance()->getPlacementLtR()) ? 1 : -1;
	int step_y = (Config::instance()->getPlacementTtB()) ? 1 : -1;
	int offset_x = (Config::instance()->getPlacementLtR()) 
		     ? Config::instance()->getPlacementOffsetX()
		     : -Config::instance()->getPlacementOffsetX();
	int offset_y = (Config::instance()->getPlacementTtB()) 
		     ? Config::instance()->getPlacementOffsetY()
		     : -Config::instance()->getPlacementOffsetY();
	int start_x, start_y, test_x = 0, test_y = 0;

  // Wrap these up, to get proper checking of space.
  uint wo_width = wo->getWidth() + Config::instance()->getPlacementOffsetX();
  uint wo_height = wo->getHeight() + Config::instance()->getPlacementOffsetY();

	Geometry head;
	PScreen::instance()->getHeadInfoWithEdge(PScreen::instance()->getCurrHead(),
																					 head);

	start_x = (Config::instance()->getPlacementLtR())
		? (head.x)
		: (head.x + head.width - wo_width);
	start_y = (Config::instance()->getPlacementTtB())
		? (head.y)
		: (head.y + head.height - wo_height);

	if (Config::instance()->getPlacementRow()) { // row placement
		test_y = start_y;
		while (!placed && (Config::instance()->getPlacementTtB()
											 ? ((test_y + wo_height) <= (head.y + head.height))
											 : (test_y >= head.y))) {
			test_x = start_x;
			while (!placed && (Config::instance()->getPlacementLtR()
												 ? ((test_x + wo_width) <= (head.x + head.width))
												 : (test_x >= head.x))) {
				// see if we can place the window here
				if ((wo_e = isEmptySpace(test_x, test_y, wo))) {
					placed = false;
					test_x = Config::instance()->getPlacementLtR()
						? (wo_e->getX() + wo_e->getWidth()) : (wo_e->getX() - wo_width);
				} else {
					placed = true;
					wo->move(test_x + offset_x, test_y + offset_y);
				}
			}
			test_y += step_y;
		}
	} else { // column placement
		test_x = start_x;
		while (!placed && (Config::instance()->getPlacementLtR()
										 ? ((test_x + wo_width) <= (head.x + head.width))
										 : (test_x >= head.x))) {
			test_y = start_y;
			while (!placed && (Config::instance()->getPlacementTtB()
												 ? ((test_y + wo_height) <= (head.y + head.height))
												 : (test_y >= head.y))) {
				// see if we can place the window here
				if ((wo_e = isEmptySpace(test_x, test_y, wo))) {
					placed = false;
					test_y = Config::instance()->getPlacementTtB()
						? (wo_e->getY() + wo_e->getHeight()) : (wo_e->getY() - wo_height);
				} else {
					placed = true;
					wo->move(test_x + offset_x, test_y + offset_y);
				}
			}
			test_x += step_x;
		}
	}

	return placed;

}

//! @brief Places the wo in a corner of the screen not under the pointer
bool
Workspaces::placeMouseNotUnder(PWinObj *wo)
{
	Geometry head;
	PScreen::instance()->getHeadInfoWithEdge(PScreen::instance()->getCurrHead(),
																					 head);

	int mouse_x, mouse_y;
	PScreen::instance()->getMousePosition(mouse_x, mouse_y);

	// compensate for head offset
	mouse_x -= head.x;
	mouse_y -= head.y;

	// divide the screen into four rectangles using the pointer as divider
	if ((wo->getWidth() < unsigned(mouse_x)) && (wo->getHeight() < head.height)) {
		wo->move(head.x, head.y);
		return true;
	}

	if ((wo->getWidth() < head.width) && (wo->getHeight() < unsigned(mouse_y))) {
		wo->move(head.x + head.width - wo->getWidth(), head.y);
		return true;
	}

	if ((wo->getWidth() < (head.width - mouse_x)) && (wo->getHeight() < head.height)) {
		wo->move(head.x + head.width - wo->getWidth(), head.y + head.height - wo->getHeight());
		return true;
	}

	if ((wo->getWidth() < head.width) && (wo->getHeight() < (head.height - mouse_y))) {
		wo->move(head.x, head.y + head.height - wo->getHeight());
		return true;
	}

	return false;
}

//! @brief Places the client centered under the mouse
bool
Workspaces::placeMouseCentered(PWinObj *wo)
{
	int mouse_x, mouse_y;
	PScreen::instance()->getMousePosition(mouse_x, mouse_y);

	Geometry gm(mouse_x - (wo->getWidth() / 2), mouse_y - (wo->getHeight() / 2),
							wo->getWidth(), wo->getHeight());

	// make sure it's within the screens border
	placeInsideScreen(gm);

	wo->move(gm.x, gm.y);

	return true;
}

//! @brief Places the client like the menu gets placed
bool
Workspaces::placeMouseTopLeft(PWinObj *wo)
{
	int mouse_x, mouse_y;
	PScreen::instance()->getMousePosition(mouse_x, mouse_y);

	Geometry gm(mouse_x, mouse_y, wo->getWidth(), wo->getHeight());
	placeInsideScreen(gm); // make sure it's within the screens border

	wo->move(gm.x, gm.y);

	return true;
}

//! @brief Places centerd on the window parent
bool
Workspaces::placeCenteredOnParent(PWinObj *wo, Window parent)
{
	if (parent == None) {
		return false;
	}

	PWinObj *wo_s = PWinObj::findPWinObj(parent);
	if (wo_s != NULL) {
		wo->move(wo_s->getX() + wo_s->getWidth() / 2 - wo->getWidth() / 2,
						 wo_s->getY() + wo_s->getHeight() / 2 - wo->getHeight() / 2);
		return true;
	}

	return false;
}

//! @brief Makes sure the window is inside the screen.
void
Workspaces::placeInsideScreen(Geometry &gm)
{
	Geometry head;
	PScreen::instance()->getHeadInfoWithEdge(PScreen::instance()->getCurrHead(),
																					 head);

	if (gm.x < head.x) {
		gm.x = head.x;
	} else if ((gm.x + gm.width) > (head.x + head.width)) {
		gm.x = head.x + head.width - gm.width;
	}

	if (gm.y < head.y) {
		gm.y = head.y;
	} else if ((gm.y + gm.height) > (head.y + head.height)) {
		gm.y = head.y + head.height - gm.height;
	}
}

//! @brief
PWinObj*
Workspaces::isEmptySpace(int x, int y, const PWinObj* wo)
{
	if (wo == NULL) {
		return NULL;
	}

	// say that it's placed, now check if we are wrong!
	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end(); ++it) {
		if (wo == (*it))
			continue; // we don't wanna take ourself into account

		// Make sure clients are visible and _not_ iconified. This maybe doesn't
		// make sense but to "hide" wo's from placement I set _iconified to true
		if ((*it)->isMapped() && !(*it)->isIconified() &&
				((*it)->getLayer() != LAYER_DESKTOP)) {
			// check if we are "intruding" on some other window's place
			if (((*it)->getX() < signed(x + wo->getWidth())) &&
					(signed((*it)->getX() + (*it)->getWidth()) > x) &&
					((*it)->getY() < signed(y + wo->getHeight())) &&
					(signed((*it)->getY() + (*it)->getHeight()) > y)) {
				return (*it);
			}
		}
	}

	return NULL; // we passed the test, no frames in the way
}

//! @brief
//! @param wo PWinObj to originate from when searching
//! @param dir Direction to search
//! @param skip Bitmask for skipping window objects, defaults to 0
PWinObj*
Workspaces::findDirectional(PWinObj *wo, DirectionType dir, uint skip)
{
	// search from the frame not client, if client
	if (wo->getType() == PWinObj::WO_CLIENT) {
		wo = static_cast<Client*>(wo)->getParent();
	}

	PWinObj *found_wo = NULL;

	uint score = 0, score_min;
	int wo_main, wo_sec;
	int diff_main = 0;

#ifdef HAVE_LIMITS
	score_min = numeric_limits<uint>::max();
#else // !HAVE_LIMITS
	score_min = ~0;
#endif // HAVE_LIMITS

	// init wo variables
	if ((dir == DIRECTION_UP) || (dir == DIRECTION_DOWN)) {
		wo_main = wo->getY() + wo->getHeight() / 2;
		wo_sec = wo->getX() + wo->getWidth() / 2;
	} else {
		wo_main = wo->getX() + wo->getWidth() / 2;
		wo_sec = wo->getY() + wo->getHeight() / 2;
	}

	list<PWinObj*>::iterator it(_wo_list.begin());
	for (; it != _wo_list.end(); ++it) {
		if ((wo == (*it)) || ((*it)->isMapped() == false)) {
			continue; // skip ourselves and unmapped wo's
		}
		if (((*it)->getType() != PWinObj::WO_FRAME) ||
				static_cast<Frame*>(*it)->isSkip(skip)) {
			continue; // only include frames and not having skip set
		}

		// check main direction, making sure it's at the right side
		// we check against the middle of the window as it gives a saner feeling
		// than the edges IMHO
		switch (dir) {
		case DIRECTION_UP:
			diff_main = wo_main - ((*it)->getY() + (*it)->getHeight() / 2);
			break;
		case DIRECTION_DOWN:
			diff_main = ((*it)->getY() + (*it)->getHeight() / 2) - wo_main;
			break;
		case DIRECTION_LEFT:
			diff_main = wo_main - ((*it)->getX() + (*it)->getWidth() / 2);
			break;
		case DIRECTION_RIGHT:
			diff_main = ((*it)->getX() + (*it)->getWidth() / 2) - wo_main;
			break;
		default:
			return NULL; // no direction to search
		}

		if (diff_main < 0) {
			continue; // wrong direction
		}

		score = diff_main;

		if ((dir == DIRECTION_UP) || (dir == DIRECTION_DOWN)) {
			if ((wo_sec < (*it)->getX()) || (wo_sec > (*it)->getRX())) {
				score += PScreen::instance()->getHeight() / 2;
			}
			score += abs (static_cast<long> (wo_sec - ((*it)->getX ()
                                       + (*it)->getWidth () / 2)));

		} else {
			if ((wo_sec < (*it)->getY()) || (wo_sec > (*it)->getBY())) {
				score += PScreen::instance()->getWidth() / 2;
			}

			score += abs (static_cast<long> (wo_sec - ((*it)->getY ()
                                                 + (*it)->getHeight () / 2)));
		}

		if (score < score_min) {
			found_wo = *it;
			score_min = score;
		}
	}

	return found_wo;
}



syntax highlighted by Code2HTML, v. 0.9.1