ede/pekwm/Workspaces.cc

1061 lines
30 KiB
C++
Raw Normal View History

//
// Workspaces.cc for pekwm
// Copyright © 2002-2009 Claes Nasten <me@pekdon.net>
//
// 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 "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 "WindowManager.hh"
#include "WorkspaceIndicator.hh"
#include <iostream>
#include <sstream>
#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;
using std::wostringstream;
using std::wstring;
using std::cerr;
using std::endl;
// Workspaces::Workspace
Workspaces::Workspace::Workspace(const std::wstring &name, uint number)
: _name(name), _number(number), _last_focused(0)
{
}
Workspaces::Workspace::~Workspace(void)
{
}
// Workspaces
Workspaces *Workspaces::_instance = 0;
//! @brief Workspaces constructor
Workspaces::Workspaces(uint number, uint per_row)
: _active(0), _previous(0), _per_row(per_row)
{
#ifdef DEBUG
if (_instance) {
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "Workspaces(" << this << ")::Workspaces(" << number << ")"
<< endl << " *** _instance already 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(getWorkspaceName(i), i));
}
}
//! @brief Workspaces destructor
Workspaces::~Workspaces(void)
{
vector<Workspace*>::iterator it(_workspace_list.begin());
for (; it != _workspace_list.end(); ++it) {
delete *it;
}
_instance = 0;
}
//! @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, 0);
} else { // We need more workspaces, lets create some
for (uint i = before; i < number; ++i) {
_workspace_list.push_back(new Workspace(getWorkspaceName(i), i));
}
}
// Tell the rest of the world how many workspaces we have.
XChangeProperty(PScreen::instance()->getDpy(),
PScreen::instance()->getRoot(),
Atoms::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);
}
}
/**
* Set workspace names.
*/
void
Workspaces::setNames(void)
{
vector<Workspace*>::iterator it(_workspace_list.begin());
for (; it != _workspace_list.end(); ++it) {
(*it)->setName(Config::instance()->getWorkspaceName((*it)->getNumber()));
}
}
//! @brief Activates Workspace workspace and sets the right hints
//! @param num Workspace to activate
//! @param focus whether 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();
PWinObj *wo = PWinObj::getFocusedPWinObj();
// Make sure that sticky windows gets unfocused on workspace change,
// it will be set back after switch is done.
if (wo) {
if (wo->getType() == PWinObj::WO_CLIENT) {
wo->getParent()->setFocused(false);
} else {
wo->setFocused(false);
}
}
// Save the focused window object
setLastFocused(_active, wo);
PWinObj::setFocusedPWinObj(0);
// switch workspace
hideAll(_active);
AtomUtil::setLong(PScreen::instance()->getRoot(),
Atoms::getAtom(NET_CURRENT_DESKTOP),
num);
unhideAll(num, focus);
PScreen::instance()->ungrabServer(true);
// Show workspace indicator if requested
if (Config::instance()->getShowWorkspaceIndicator() > 0) {
WindowManager::instance()->getWorkspaceIndicator()->render();
WindowManager::instance()->getWorkspaceIndicator()->mapWindowRaised();
WindowManager::instance()->getWorkspaceIndicator()->updateHideTimer(Config::instance()->getShowWorkspaceIndicator());
}
}
//! @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;
uint per_row = Config::instance()->getWorkspacesPerRow();
uint cur_row = getRow(), row_min = getRowMin(), row_max = getRowMax();
switch (direction) {
case WORKSPACE_LEFT:
case WORKSPACE_PREV:
dir = 1;
if (_active > row_min) {
workspace = _active - 1;
} else if (direction == WORKSPACE_PREV) {
workspace = row_max;
} else {
switched = false;
}
break;
case WORKSPACE_NEXT:
case WORKSPACE_RIGHT:
dir = 2;
if (_active < row_max) {
workspace = _active + 1;
} else if (direction == WORKSPACE_NEXT) {
workspace = row_min;
} else {
switched = false;
}
break;
case WORKSPACE_PREV_V:
case WORKSPACE_UP:
dir = -1;
if (_active >= per_row) {
workspace = _active - per_row;
} else if (direction == WORKSPACE_PREV_V) {
// Bottom left
workspace = _workspace_list.size() - per_row;
// Add column
workspace += _active - cur_row * per_row;
} else {
switched = false;
}
break;
case WORKSPACE_NEXT_V:
case WORKSPACE_DOWN:
dir = -2;
if ((_active + per_row) < _workspace_list.size()) {
workspace = _active + per_row;
} else if (direction == WORKSPACE_NEXT_V) {
workspace = _active - cur_row * per_row;
} 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;
PScreen::instance()->getMousePosition(x, y);
if (dir != 0) {
switch(dir) {
case 1:
x = PScreen::instance()->getWidth() - std::max(Config::instance()->getScreenEdgeSize(SCREEN_EDGE_LEFT) + 2, 2);
break;
case 2:
x = std::max(Config::instance()->getScreenEdgeSize(SCREEN_EDGE_RIGHT) * 2, 2);
break;
case -1:
y = PScreen::instance()->getHeight() - std::max(Config::instance()->getScreenEdgeSize(SCREEN_EDGE_BOTTOM) + 2, 2);
break;
case -2:
y = std::max(Config::instance()->getScreenEdgeSize(SCREEN_EDGE_TOP) + 2, 2);
break;
}
// warp pointer
XWarpPointer(PScreen::instance()->getDpy(), None,
PScreen::instance()->getRoot(), 0, 0, 0, 0, x, y);
}
// set workpsace
setWorkspace(num, true);
return true;
}
/**
* Adds a PWinObj to the stacking list.
* @param wo PWinObj to insert
* @param raise Whether to insert at the bottom or top of the layer (defaults to true).
*/
void
Workspaces::insert(PWinObj *wo, bool raise)
{
list<PWinObj*>::iterator it(_wo_list.begin()), position(_wo_list.end());
for (; it != _wo_list.end() && position == _wo_list.end(); ++it) {
if (raise) {
// If raising, make sure the inserted wo gets below the first
// window in the next layer.
if ((*it)->getLayer() > wo->getLayer()) {
position = it;
}
} else {
// If lowering, put the window below the first window with the same level.
if (wo->getLayer() <= (*it)->getLayer()) {
position = it;
}
}
}
_wo_list.insert(position, wo);
if (position == _wo_list.end()) {
XRaiseWindow(PScreen::instance()->getDpy(), wo->getWindow());
} else {
stackWinUnderWin((*position)->getWindow(), 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(0);
}
}
}
//! @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()) && ! ((*it)->isHidden()) &&
((*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 && wo->isMapped() && wo->isFocusable()) {
// Render as focused
if (wo->getType() == PWinObj::WO_CLIENT) {
wo->getParent()->setFocused(true);
} else {
wo->setFocused(true);
}
// Get the active child if a frame, to get correct focus behavior
if (wo->getType() == PWinObj::WO_FRAME) {
wo = static_cast<Frame*>(wo)->getActiveChild();
}
// Focus
wo->giveInputFocus();
PWinObj::setFocusedPWinObj(wo);
}
// If focusing fails, focus the root window.
if (! PWinObj::getFocusedPWinObj()
|| ! PWinObj::getFocusedPWinObj()->isMapped()) {
PWinObj::getRootPWinObj()->giveInputFocus();
}
}
}
//! @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(win, 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(wo->getWindow(), win);
}
break;
}
}
}
}
//! @brief
PWinObj*
Workspaces::getLastFocused(uint workspace)
{
if (workspace >= _workspace_list.size()) {
return 0;
}
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);
}
//! @brief Create name for workspace num
wstring
Workspaces::getWorkspaceName(uint num)
{
wostringstream buf;
buf << num + 1;
buf << L": ";
buf << Config::instance()->getWorkspaceName(num);
return buf.str();
}
// 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 0;
}
/**
* Builds a list of all clients in stacking order, clients in the same
* frame come after each other.
*/
Window*
Workspaces::buildClientList(unsigned int &num_windows)
{
Frame *frame;
Client *client, *client_active;
list<Window> windows_list;
list<PWinObj*>::iterator it_f, it_c;
for (it_f = _wo_list.begin(); it_f != _wo_list.end(); ++it_f) {
if ((*it_f)->getType() != PWinObj::WO_FRAME) {
continue;
}
frame = static_cast<Frame*>(*it_f);
client_active = frame->getActiveClient();
if (Config::instance()->isReportAllClients()) {
for (it_c = frame->begin(); it_c != frame->end(); ++it_c) {
client = dynamic_cast<Client*>(*it_c);
if (client
&& ! client->isSkip(SKIP_TASKBAR)
&& client != client_active) {
windows_list.push_back(client->getWindow());
}
}
}
if (client_active && ! client_active->isSkip(SKIP_TASKBAR)) {
windows_list.push_back(client_active->getWindow());
}
}
num_windows = windows_list.size();
Window *windows = new Window[num_windows ? num_windows : 1];
if (num_windows > 0) {
copy(windows_list.begin(), windows_list.end(), windows);
}
return windows;
}
/**
* Updates the Ewmh Client list hint.
*/
void
Workspaces::updateClientList(void)
{
unsigned int num_windows;
Window *windows = buildClientList(num_windows);
AtomUtil::setWindows(PScreen::instance()->getRoot(),
Atoms::getAtom(NET_CLIENT_LIST),
windows, num_windows);
delete [] windows;
}
/**
* Updates the Ewmh Stacking list hint.
*/
void
Workspaces::updateClientStackingList(void)
{
unsigned int num_windows;
Window *windows = buildClientList(num_windows);
AtomUtil::setWindows(PScreen::instance()->getRoot(),
Atoms::getAtom(NET_CLIENT_LIST_STACKING),
windows, num_windows);
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;
}
}
}
/**
* Make sure window is inside screen boundaries.
*/
void
Workspaces::placeWoInsideScreen(PWinObj *wo)
{
Geometry gm_before(wo->getX(), wo->getY(), wo->getWidth(), wo->getHeight());
Geometry gm_after(gm_before);
Strut *strut = 0;
if (wo->getType() == PWinObj::WO_FRAME) {
Client *client = static_cast<Frame*>(wo)->getActiveClient();
if (client) {
strut = client->getStrut();
}
}
placeInsideScreen(gm_after, strut);
if (gm_before != gm_after) {
wo->move(gm_after.x, gm_after.y);
}
}
//! @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) {
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, Strut *strut)
{
// Do not include screen edges when calculating the position if the window
// has a strut as it then is likely to be a panel or the like placed
// along the edge of the screen.
Geometry head;
if (strut) {
PScreen::instance()->getHeadInfo(PScreen::instance()->getCurrHead(), head);
} else {
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) {
return 0;
}
// say that it's placed, now check if we are wrong!
list<PWinObj*>::iterator it(_wo_list.begin());
for (; it != _wo_list.end(); ++it) {
// Skip ourselves, non-mapped and desktop objects. Iconified means
// skip placement.
if (wo == (*it) || ! (*it)->isMapped() || (*it)->isIconified()
|| ((*it)->getLayer() == LAYER_DESKTOP)) {
continue;
}
// Also skip windows tagged as Maximized as they cause us to
// automatically fail.
if ((*it)->getType() == PWinObj::WO_FRAME) {
Client *client = static_cast<Frame*>((*it))->getActiveClient();
if (client &&
(client->isFullscreen()
|| (client->isMaximizedVert() && client->isMaximizedHorz()))) {
continue;
}
}
// 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 0; // 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 = 0;
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())) {
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 0; // 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;
}