#include "group.h"

/**
 *
 * Beryl group plugin
 *
 * init.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.
 *
 **/

#define UPDATE_TAB_BAR_INTERVAL 200

/*
 * groupInitDisplay
 *
 */
Bool groupInitDisplay(CompPlugin * p, CompDisplay * d)
{

	GroupDisplay *gd;

	gd = malloc(sizeof(GroupDisplay));
	if (!gd)
		return FALSE;

	gd->screenPrivateIndex = allocateScreenPrivateIndex(d);
	if (gd->screenPrivateIndex < 0) {
		free(gd);
		return FALSE;
	}

	gd->tmpSel.windows = NULL;
	gd->tmpSel.nWins = 0;

	//gd->revGroups = NULL;

	gd->ignoreMode = FALSE;

	groupDisplayInitOptions(gd);

	WRAP(gd, d, handleEvent, groupHandleEvent);

	gd->timeoutHandle = compAddTimeout (UPDATE_TAB_BAR_INTERVAL, groupUpdateTabBars, d);

	d->privates[displayPrivateIndex].ptr = gd;

	return TRUE;
}

/*
 * groupFiniDisplay
 *
 */
void groupFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	GROUP_DISPLAY(d);

	freeScreenPrivateIndex(d, gd->screenPrivateIndex);

	UNWRAP(gd, d, handleEvent);
	
	compRemoveTimeout(gd->timeoutHandle);

	free(gd);
}

/*
 * groupInitScreen
 *
 */
Bool groupInitScreen(CompPlugin * p, CompScreen * s)
{

	GroupScreen *gs;

	GROUP_DISPLAY(s->display);

	gs = malloc(sizeof(GroupScreen));
	if (!gs)
		return FALSE;

	gs->windowPrivateIndex = allocateWindowPrivateIndex(s);
	if (gs->windowPrivateIndex < 0) {
		free(gs);
		return FALSE;
	}

	groupScreenInitOptions(gs);

	gs->wMask=compWindowTypeMaskFromStringList(&gs->opt[GROUP_SCREEN_OPTION_TYPES].value);

	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT_SINGLE].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_GROUPING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_UNGROUPING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_REMOVING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_IGNORE].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_COLOR].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);

	WRAP(gs, s, windowMoveNotify, groupWindowMoveNotify);
	WRAP(gs, s, windowResizeNotify, groupWindowResizeNotify);
	WRAP(gs, s, getOutputExtentsForWindow, groupGetOutputExtentsForWindow);
	WRAP(gs, s, preparePaintScreen, groupPreparePaintScreen);
	WRAP(gs, s, paintScreen, groupPaintScreen);
	WRAP(gs, s, drawWindow, groupDrawWindow);
	WRAP(gs, s, paintWindow, groupPaintWindow);
	WRAP(gs, s, paintTransformedScreen, groupPaintTransformedScreen);
	WRAP(gs, s, donePaintScreen, groupDonePaintScreen);
	WRAP(gs, s, windowGrabNotify, groupWindowGrabNotify);
	WRAP(gs, s, windowUngrabNotify, groupWindowUngrabNotify);
	WRAP(gs,s,damageWindowRect,groupDamageWindowRect);
	WRAP(gs,s,windowStateChangeNotify,groupWindowStateChangeNotify);

	s->privates[gd->screenPrivateIndex].ptr = gs;

	gs->groups = NULL;

	gs->grabIndex = 0;
	gs->grabState = ScreenGrabNone;
	gs->queued = FALSE;
	gs->tabBarVisible = FALSE;

	gs->pendingMoves = NULL;
	gs->pendingGrabs = NULL;
	gs->pendingUngrabs = NULL;
	
	gs->draggedSlot = NULL;
	gs->dragged = FALSE;
	gs->dragHoverTimeoutHandle = 0;
	gs->prevX = 0;
	gs->prevY = 0;

	gs->isRotating = FALSE;

	/* gs->glowType is already initialized in groupScreenInitOptions */
	initTexture (s, &gs->glowTexture);

	RGBAimageToTexture (s, &gs->glowTexture, 
		glowTextureProperties[gs->glowType].textureData,
		glowTextureProperties[gs->glowType].textureSize,
		glowTextureProperties[gs->glowType].textureSize);
	
	return TRUE;
}

/*
 * groupFiniScreen
 *
 */
void groupFiniScreen(CompPlugin * p, CompScreen * s)
{
	GROUP_SCREEN(s);
	GROUP_DISPLAY(s->display);

	if (gs->groups) {
		GroupSelection *group, *next_group;
		for(group = gs->groups; group;)
		{
			if (group->tabBar) {
				GroupTabBarSlot *slot, *next_slot;
				for (slot = group->tabBar->slots; slot;) {

					if (slot->region)
						XDestroyRegion(slot->region);
					if (slot->name)
						free(slot->name);

					next_slot = slot->next;
					free(slot);
					slot = next_slot;
				}

				groupDestroyCairoLayer(group->screen, group->tabBar->textLayer);
				groupDestroyCairoLayer(group->screen, group->tabBar->bgLayer);
				groupDestroyCairoLayer(group->screen, group->tabBar->selectionLayer);

				if (group->inputPrevention)
					XDestroyWindow(s->display->display, group->inputPrevention);

				if (group->tabBar->region)
					XDestroyRegion(group->tabBar->region);

				free(group->tabBar);
			}

			next_group = group->next;
			free(group);
			group = next_group;
		}
	}

	freeWindowPrivateIndex(s, gs->windowPrivateIndex);

	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT_SINGLE].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_GROUPING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_UNGROUPING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_REMOVING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_IGNORE].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_COLOR].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);

	UNWRAP(gs, s, windowMoveNotify);
	UNWRAP(gs, s, windowResizeNotify);
	UNWRAP(gs, s, getOutputExtentsForWindow);
	UNWRAP(gs, s, preparePaintScreen);
	UNWRAP(gs, s, paintScreen);
	UNWRAP(gs, s, drawWindow);
	UNWRAP(gs, s, paintWindow);
	UNWRAP(gs, s, paintTransformedScreen);
	UNWRAP(gs, s, donePaintScreen);
	UNWRAP(gs, s, windowGrabNotify);
	UNWRAP(gs, s, windowUngrabNotify);
	UNWRAP(gs,s,damageWindowRect);
	UNWRAP(gs,s,windowStateChangeNotify);

	finiTexture (s, &gs->glowTexture); 
	free(gs);
}

/*
 * groupInitWindow
 *
 */
Bool groupInitWindow(CompPlugin * p, CompWindow * w)
{
	GroupWindow *gw;
	GROUP_SCREEN(w->screen);

	gw = malloc(sizeof(GroupWindow));
	if (!gw)
		return FALSE;

	gw->group = NULL;
	gw->inSelection = FALSE;

	gw->needsPosSync = FALSE;

	// for tab
	gw->oldWindowState = getWindowState(w->screen->display, w->id);
	gw->animateState = 0;
	gw->ungroup = FALSE;
	gw->slot = NULL;
	gw->tx = gw->ty = 0;
	gw->xVelocity = gw->yVelocity = 0;
	gw->orgPos.x = 0;
	gw->orgPos.y = 0;
	gw->mainTabOffset.x = 0;
	gw->mainTabOffset.y = 0;
	gw->destination.x = 0;
	gw->destination.y = 0;

	if (w->minimized)
		gw->windowState = WindowMinimized;
	else if (w->shaded)
		gw->windowState = WindowShaded;
	else
		gw->windowState = WindowNormal;

	gw->lastState = w->state;

	//gw->offscreen = FALSE;
	w->privates[gs->windowPrivateIndex].ptr = gw;

	gw->glowQuads = NULL;
	groupComputeGlowQuads (w, &gs->glowTexture.matrix);

	return TRUE;
}

/*
 * groupFiniWindow
 *
 */
void groupFiniWindow(CompPlugin * p, CompWindow * w)
{
	GROUP_WINDOW(w);

	/* TODO: this needs more work; positioning is not correct
		 at the moment - but it's way better than leaving the 
		 window offscreen */
	if (gw->group && gw->group->tabBar && !IS_TOP_TAB(w, gw->group))
		moveWindowOnscreen(w);

	if (gw->glowQuads)
		free (gw->glowQuads);

	free(gw);
}

/*
 * groupInit
 *
 */
Bool groupInit(CompPlugin * p)
{
	displayPrivateIndex = allocateDisplayPrivateIndex();
	if (displayPrivateIndex < 0)
		return FALSE;

	return TRUE;
}

/*
 * groupFini
 *
 */
void groupFini(CompPlugin * p)
{
	if (displayPrivateIndex >= 0)
		freeDisplayPrivateIndex(displayPrivateIndex);
}

/*
 * groupDeps array
 *
 */

static CompPluginDep groupDeps[] = {
	{CompPluginRuleAfter, "place"},
};

static GroupDesc groupGroupDescs[] = {
	{"General", N_("General options"), NULL, 0},
	{"Selection", N_("Options for the window selection"), NULL, 0},
	{"Tabbing", N_("Options for window tabbing"), NULL, 0},
	{"Glow", N_("Configuration options for the window glow"), NULL, 0}
};

/*
 * groupVTable
 *
 */
CompPluginVTable groupVTable = {
	"group",
	N_("Group and Tab Windows"),
	N_("With this plugin you can group and tab windows."),
	groupInit,
	groupFini,
	groupInitDisplay,
	groupFiniDisplay,
	groupInitScreen,
	groupFiniScreen,
	groupInitWindow,
	groupFiniWindow,
	groupGetDisplayOptions,
	groupSetDisplayOption,
	groupGetScreenOptions,
	groupSetScreenOption,
	groupDeps,
	sizeof(groupDeps) / sizeof(groupDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins-unsupported",
	"wm",
	groupGroupDescs,
	sizeof(groupGroupDescs) / sizeof(groupGroupDescs[0]),
	True,		/* enabled by default */
};

/*
 * getCompPluginInfo
 *
 */
CompPluginVTable *getCompPluginInfo(void)
{
	return &groupVTable;
}


syntax highlighted by Code2HTML, v. 0.9.1