#include "group.h"

/**
 *
 * Beryl group plugin
 *
 * selection.c
 *
 * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
 * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
 *          Roi Cohen       <roico@beryl-project.org>
 *          Danny Baumann   <maniac@beryl-project.org>
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 **/

/*
 * groupWindowInRegion
 *
 */
static Bool
groupWindowInRegion(CompWindow *w, Region src, float precision)
{
	Region buf = XCreateRegion();
	XIntersectRegion(w->region, src, buf);
	
	// buf area
	int i;
	int area = 0;
	BOX *box;
	for(i = 0; i < buf->numRects; i++) {
		box = &buf->rects[i];
		area += (box->x2 - box->x1) * (box->y2 - box->y1); // width * height
	}
	
	XDestroyRegion(buf);

	if (area >= WIN_WIDTH(w) * WIN_HEIGHT(w) * precision) {
		XSubtractRegion(src, w->region, src);
		return TRUE;
	}
	
	return FALSE;
}

/*
 * groupFindGroupInWindows
 *
 */
static Bool groupFindGroupInWindows(GroupSelection *group, CompWindow **windows, int nWins)
{
	int i;
	for (i = 0; i < nWins; i++) {
		CompWindow *cw = windows[i];
		GROUP_WINDOW(cw);
		if (gw->group == group)
			return TRUE;
	}

	return FALSE;
}

/*
 * groupFindWindowsInRegion
 *
 */
CompWindow **groupFindWindowsInRegion(CompScreen * s, Region reg, int *c)
{
	GROUP_SCREEN(s);

	float precision = gs->opt[GROUP_SCREEN_OPTION_PRECISION].value.i / 100.0f;

	CompWindow **ret = NULL;
	int count = 0;
	CompWindow *w;
	for (w = s->reverseWindows; w; w = w->prev) {
		if ((gs->wMask & w->type) && !w->invisible &&
		    groupWindowInRegion(w, reg, precision))
		{
			GROUP_WINDOW(w);
			if (gw->group && groupFindGroupInWindows(gw->group, ret, count))
				continue;

			if (count == 0)	{
				ret = calloc(1, sizeof(CompWindow));
				ret[0] = w;
			} else {
				ret = realloc(ret, sizeof(CompWindow) * (count + 1));
				ret[count] = w;
			}

			count++;
		}
	}
	
	(*c) = count;
	return ret;
}

/*
 * groupDeleteSelectionWindow
 *
 */
void groupDeleteSelectionWindow(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);

	if (gd->tmpSel.nWins > 0 && gd->tmpSel.windows) {
		CompWindow **buf = gd->tmpSel.windows;
		gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - 1, sizeof(CompWindow *));

		int counter = 0;
		int i;
		for (i = 0; i < gd->tmpSel.nWins; i++) {
			if (buf[i]->id == w->id)
				continue;
			gd->tmpSel.windows[counter++] = buf[i];
		}

		gd->tmpSel.nWins = counter;
		free(buf);
	}
}

/*
 * groupAddWindowToSelection
 *
 */
void groupAddWindowToSelection(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);

	gd->tmpSel.windows = (CompWindow **) realloc(gd->tmpSel.windows, sizeof(CompWindow *) * (gd->tmpSel.nWins + 1));

	gd->tmpSel.windows[gd->tmpSel.nWins] = w;
	gd->tmpSel.nWins++;
}

/*
 * groupSelectWindow
 *
 */
void groupSelectWindow(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);
	GROUP_SCREEN(w->screen);
	GROUP_WINDOW(w);

	// select singe window
	if ((gs->wMask & w->type) &&
	    !w->invisible && !gw->inSelection && !gw->group) {

		groupAddWindowToSelection(d, w);

		gw->inSelection = TRUE;
		addWindowDamage(w);
	}
	// unselect single window
	else if ((gs->wMask & w->type) &&
		 !w->invisible && gw->inSelection && !gw->group) {

		groupDeleteSelectionWindow(d, w);
		gw->inSelection = FALSE;
		addWindowDamage(w);
	}
	// select group
	else if ((gs->wMask & w->type) &&
		 !w->invisible && !gw->inSelection && gw->group) {

		int i;
		for (i = 0; i < gw->group->nWins; i++) {
			CompWindow *cw = gw->group->windows[i];
			GROUP_WINDOW(cw);

			groupAddWindowToSelection(d, cw);

			gw->inSelection = TRUE;
			addWindowDamage(cw);
		}
	}
	// Unselect group
	else if ((gs->wMask & w->type) &&
		 !w->invisible && gw->inSelection && gw->group) {

		GroupSelection *group = gw->group;
		CompWindow **buf = gd->tmpSel.windows;
		gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - gw->group->nWins, sizeof(CompWindow *));

		int counter = 0;
		int i;
		for (i = 0; i < gd->tmpSel.nWins; i++) {
			CompWindow *cw = buf[i];
			GROUP_WINDOW(cw);

			if (gw->group == group) {
				gw->inSelection = FALSE;
				addWindowDamage(cw);
				continue;
			}

			gd->tmpSel.windows[counter++] = buf[i];
		}
		gd->tmpSel.nWins = counter;
		free(buf);
	}

}

/*
 * groupSelectSingle
 *
 */
Bool
groupSelectSingle(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption)
{
	CompWindow *w = (CompWindow *) findWindowAtDisplay(d, d->activeWindow);

	if (w)
		groupSelectWindow(d, w);

	return TRUE;
}

/*
 * groupSelect
 *
 */
Bool
groupSelect(CompDisplay * d, CompAction * action, CompActionState state,
	    CompOption * option, int nOption)
{
	CompWindow *w = (CompWindow *) findWindowAtDisplay(d, d->activeWindow);
	if (!w)
		return FALSE;

	GROUP_SCREEN(w->screen);

	if (gs->grabState == ScreenGrabNone) {
		groupGrabScreen(w->screen, ScreenGrabSelect);

		if (state & CompActionStateInitKey)
			action->state |= CompActionStateTermKey;

		if (state & CompActionStateInitButton)
			action->state |= CompActionStateTermButton;

		gs->x1 = gs->x2 = d->pointerX;
		gs->y1 = gs->y2 = d->pointerY;
	}

	return TRUE;
}

/*
 * groupSelectTerminate
 *
 */
Bool
groupSelectTerminate(CompDisplay * d, CompAction * action,
		     CompActionState state, CompOption * option,
		     int nOption)
{
	CompScreen *s;
	Window xid;

	

	xid = getIntOptionNamed(option, nOption, "root", 0);

	for (s = d->screens; s; s = s->next) {
		if (xid && s->root != xid)
			continue;
		break;
	}

	if (s) {
		GROUP_SCREEN(s);

		if (gs->grabState == ScreenGrabSelect) {
			groupGrabScreen(s, ScreenGrabNone);

			if (gs->x1 != gs->x2 && gs->y1 != gs->y2) {
				Region reg = XCreateRegion();
				XRectangle rect;

				rect.x = MIN(gs->x1, gs->x2) - 2;
				rect.y = MIN(gs->y1, gs->y2) - 2;
				rect.width  = MAX(gs->x1, gs->x2) - MIN(gs->x1, gs->x2) + 4;
				rect.height = MAX(gs->y1, gs->y2) - MIN(gs->y1, gs->y2) + 4;
				XUnionRectWithRegion(&rect, reg, reg);
				
				damageScreenRegion(s, reg);

				int count;
				CompWindow **ws = groupFindWindowsInRegion(s, reg, &count);

				if (ws) {
					// select windows
					int i;
					for (i = 0; i < count; i++) {
						CompWindow *cw = ws[i];

						groupSelectWindow(d, cw);
					}
					if (gs->opt[GROUP_SCREEN_OPTION_AUTO_GROUP].value.b) {
						groupGroupWindows(d, NULL, 0, NULL, 0);
					}
					free(ws);
				}

				XDestroyRegion(reg);
			}
		}

	}

	action->state &=
	    ~(CompActionStateTermKey | CompActionStateTermButton);

	return FALSE;
}

/*
 * groupDamageSelectionRect
 *
 */
void
groupDamageSelectionRect(CompScreen* s, int xRoot, int yRoot)
{
	GROUP_SCREEN(s);
	REGION reg;

	reg.rects = &reg.extents;
	reg.numRects = 1;

    reg.extents.x1 = MIN(gs->x1, gs->x2) - 5;
	reg.extents.y1 = MIN(gs->y1, gs->y2) - 5;
	reg.extents.x2 = MAX(gs->x1, gs->x2) + 5;
	reg.extents.y2 = MAX(gs->y1, gs->y2) + 5;
	damageScreenRegion(s, &reg);

	gs->x2 = xRoot;
	gs->y2 = yRoot;

	reg.extents.x1 = MIN(gs->x1, gs->x2) - 5;
	reg.extents.y1 = MIN(gs->y1, gs->y2) - 5;
	reg.extents.x2 = MAX(gs->x1, gs->x2) + 5;
	reg.extents.y2 = MAX(gs->y1, gs->y2) + 5;
	damageScreenRegion(s, &reg);
}


syntax highlighted by Code2HTML, v. 0.9.1