/*
 * $Id: desktop.c,v 2.0 90/07/31 10:58:31 dme Exp Locker: dme $
 *
 * Copyright (c) 1990 Dave Edmondson.
 * Copyright (c) 1990 Imperial College of Science, Technoology & Medicine
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the names of Dave Edmondson or Imperial College
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission. Dave Edmondson and
 * Imperial College make no representations about the suitability of this
 */

#include <stdio.h>
#include "twm.h"
#include "screen.h"
#include "add_window.h"
#include "parse.h"
#include "desktop.h"

/*
 * there are too many `+1' and `-2' and `-3's in this.
 */

/*
 * create the virtual desktop display and store the window in the screen structure
 */
void CreateDesktopDisplay()
{
	int width, height;

	if (!Scr->Virtual)
		return;

	width = Scr->VirtualDesktopWidth/Scr->VirtualDesktopDScale;
	height = Scr->VirtualDesktopHeight/Scr->VirtualDesktopDScale;
	
	/* we have some checking for negative (x,y) to do */
	if (Scr->VirtualDesktopDX < 0) {
		Scr->VirtualDesktopDX = Scr->MyDisplayWidth - width -
			(2 * Scr->BorderWidth) + Scr->VirtualDesktopDX;
	}
	if (Scr->VirtualDesktopDY < 0) {
		Scr->VirtualDesktopDY = Scr->MyDisplayHeight - height -
			(2 * Scr->BorderWidth) - Scr->VirtualDesktopDY;
	}

	Scr->VirtualDesktopDisplayOuter =
		XCreateSimpleWindow(dpy, Scr->Root,
				    Scr->VirtualDesktopDX, Scr->VirtualDesktopDY,
				    width, height,
				    1,
				    Scr->Black, Scr->VirtualDesktopDisplayC.back);

	Scr->VirtualDesktopDisplay =
		XCreateSimpleWindow(dpy, Scr->VirtualDesktopDisplayOuter,
				    0, 0,
				    Scr->MaxWindowWidth, Scr->MaxWindowHeight,
				    0,
				    Scr->Black, Scr->VirtualDesktopDisplayC.back);
	

	XSetStandardProperties(dpy, Scr->VirtualDesktopDisplayOuter,
			       "Virtual Desktop", "Virtual Desktop",
			       None, NULL, 0, NULL);
	
	Scr->VirtualDesktopDisplayTwin =
		AddWindow(Scr->VirtualDesktopDisplayOuter, FALSE, NULL);

	SetMapStateProp(Scr->VirtualDesktopDisplayTwin, NormalState);

	/* create the real screen display */
	Scr->VirtualDesktopDScreen = 
		XCreateSimpleWindow(dpy, Scr->VirtualDesktopDisplay,
				    0, 0,
				    SCALE_D(Scr->MyDisplayWidth),
				    SCALE_D(Scr->MyDisplayHeight),
				    2, /* make it distinctive */
				    Scr->VirtualDesktopDisplayC.fore,
				    Scr->VirtualDesktopDisplayC.back);

	/* declare our interest */
	XSelectInput(dpy, Scr->VirtualDesktopDisplay,
		     ButtonPressMask | ButtonReleaseMask);

	/* position the representation */
	DisplayScreenOnDesktop();

	/* map them both */
	XMapWindow(dpy, Scr->VirtualDesktopDScreen);
	XMapWindow(dpy, Scr->VirtualDesktopDisplay);
	XMapWindow(dpy, Scr->VirtualDesktopDisplayOuter);
}

/*
 * add this window to the virtual desktop
 */
void AddToDesktop(tmp_win)
TwmWindow *tmp_win;
{
	int x, y, width, height;

	if (!Scr->Virtual)
		return;

	if (tmp_win->nailed)
		return;

	/* if it already has a vd display window, just move it to the right place
	   and map it, else actually create the window */
	if (!tmp_win->VirtualDesktopDisplayWindow) {
		Pixel background, border;

		if(!GetColorFromList(Scr->VirtualDesktopColorBL, tmp_win->full_name,
				 &tmp_win->class, &background))
			background = Scr->VirtualDesktopDisplayC.back;

		if(!GetColorFromList(Scr->VirtualDesktopColorFL, tmp_win->full_name,
				 &tmp_win->class, &border))
			border = Scr->VirtualDesktopDisplayC.fore;

		/* the position and size don't matter */
		tmp_win->VirtualDesktopDisplayWindow =
			XCreateSimpleWindow(dpy, Scr->VirtualDesktopDisplay,
					    0, 0, 1, 1, 1,
					    border, background);

	} else
		/* unmap whilst re reconfigure it */
		XUnmapWindow(dpy, tmp_win->VirtualDesktopDisplayWindow);

	/* calculate the sizes and position */
	x = SCALE_D(tmp_win->virtual_frame_x);
	y = SCALE_D(tmp_win->virtual_frame_y);
	width = SCALE_D(tmp_win->frame_width);
	height = SCALE_D(tmp_win->frame_height);

#ifdef DEBUG
	fprintf(stderr, "%d*%d+%d+%d\n", x, y, width, height);
#endif /* DEBUG */

	/* move and size it */
	XMoveWindow(dpy, tmp_win->VirtualDesktopDisplayWindow,
		    x, y);
	XResizeWindow(dpy, tmp_win->VirtualDesktopDisplayWindow,
		      width, height);
	XMapWindow(dpy, tmp_win->VirtualDesktopDisplayWindow);
}

/*
 * remove a window from the desktop display
 */
void RemoveFromDesktop(tmp_win)
TwmWindow *tmp_win;
{
	if (!Scr->Virtual)
		return;

	if (tmp_win->VirtualDesktopDisplayWindow)
		XUnmapWindow(dpy, tmp_win->VirtualDesktopDisplayWindow);
}

/*
 * correctly position the real screen representation on the virtual desktop display
 */
void DisplayScreenOnDesktop()
{
	if (!Scr->Virtual)
		return;

	/* the -3 is to account for the 2 pixel border and the 1 pixel
	 * offset added by SCALE_D.... */
	XMoveWindow(dpy, Scr->VirtualDesktopDScreen,
		    SCALE_D(Scr->VirtualDesktopX) - 3,
		    SCALE_D(Scr->VirtualDesktopY) - 3);

	/* I've convinced myself that this is not necessary */
	/* XLowerWindow(dpy, Scr->VirtualDesktopDScreen); */
}

void ResizeDesktopDisplay(w, h)
int w, h;
{
	int x, y, np;

	if (!Scr->Virtual)
		return;

	/* calculate the new vd size */
	Scr->VirtualDesktopWidth = SCALE_U(w);
	Scr->VirtualDesktopHeight = SCALE_U(h);

	x = SCALE_D(Scr->VirtualDesktopX);
	y = SCALE_D(Scr->VirtualDesktopY);

	/* redraw it so that the real screen representation ends up on the display */
	np = SCALE_D(Scr->VirtualDesktopWidth) - SCALE_D(Scr->MyDisplayWidth);
	if (x > np)
		x = np;
	
	np = SCALE_D(Scr->VirtualDesktopHeight) - SCALE_D(Scr->MyDisplayHeight);
	if (y > np)
		y = np;

	/* this is a bit of a fudge factor to account for the borderwidth */
	x -= 2;
	y -= 2;

	SetRealScreen(SCALE_U(x), SCALE_U(y));
	
	/* move the display window */
	XMoveWindow(dpy, Scr->VirtualDesktopDScreen, x - 1, y - 1);
}

/*
 * state during the move
 */
static unsigned int moving_x, moving_y, moving_w, moving_h, moving_bw;
Window moving_window;

/*
 * move a window in the desktop display - possible including the `real' screen
 */
void StartMoveWindowInDesktop(ev)
XMotionEvent ev;
{
	Window nowindow;
	int xoff, yoff;
	unsigned int d;
	
	if (!Scr->Virtual)
		return;

	moving_window = ev.subwindow;

	if (!moving_window)
		moving_window = Scr->VirtualDesktopDScreen;

	XGrabPointer(dpy, Scr->VirtualDesktopDisplayOuter, True,
		     ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
		     GrabModeAsync, GrabModeAsync,
		     Scr->VirtualDesktopDisplay, Scr->NoCursor, CurrentTime);

	XGetGeometry(dpy, moving_window, &nowindow, &xoff, &yoff,
		     &moving_w, &moving_h, &moving_bw, &d);

	moving_x = ev.x;
	moving_y = ev.y;

	DoMoveWindowOnDesktop(ev.x, ev.y);
}

void DoMoveWindowOnDesktop(x, y)
int x, y;
{
	if (!Scr->Virtual)
		return;

	/* check that we are legit */
	if (x < 0)
		x = 0;
	else {
		int np = SCALE_D(Scr->VirtualDesktopWidth) - moving_w;
		if (x > np)
			x = np;
	}
	if (y < 0)
		y = 0;
	else {
		int np = SCALE_D(Scr->VirtualDesktopHeight) - moving_h;
		if (y > np)
			y = np;
	}

	moving_x = x;
	moving_y = y;
	
	/* move the display window */
	XMoveWindow(dpy, moving_window, x - moving_bw, y - moving_bw);
}

void EndMoveWindowOnDesktop()
{
	if (!Scr->Virtual)
		return;

	if (moving_window == Scr->VirtualDesktopDScreen)
		/* now move all of the windows */
		SetRealScreen(SCALE_U(moving_x), SCALE_U(moving_y));
	else {
		TwmWindow *Tmp_win;

		/* go find the window - this is dead slow really */
		for (Tmp_win = Scr->TwmRoot.next;
		     Tmp_win != NULL;
		     Tmp_win = Tmp_win->next)
			if (Tmp_win->VirtualDesktopDisplayWindow == moving_window) {

				/* move the window in virtual space */
				Tmp_win->virtual_frame_x = SCALE_U(moving_x);
				Tmp_win->virtual_frame_y = SCALE_U(moving_y);

				/* move it in real space */
				Tmp_win->frame_x = V_TO_R_X(Tmp_win->virtual_frame_x);
				Tmp_win->frame_y = V_TO_R_Y(Tmp_win->virtual_frame_y);

				XMoveWindow(dpy, Tmp_win->frame,
					    Tmp_win->frame_x, Tmp_win->frame_y);

				/* raise the window ? */
				if(!Scr->NoRaiseMove) {
					XRaiseWindow(dpy, Tmp_win->frame);
					XRaiseWindow(dpy, Tmp_win->VirtualDesktopDisplayWindow);
				}

				/* And let the window know about it's move */
				/* Added njw Aug 19, 1990 */
				SendConfigureNotify(Tmp_win, Tmp_win->frame_x,
						    Tmp_win->frame_y);

				moving_window = None;
				return;
			}
	}
	moving_window = None;
}

void SetVirtualDesktop(geom, scale)
char *geom;
int scale;
{

	if (Scr->Virtual) {
		twmrc_error_prefix();
		fprintf(stderr, "VirtualDesktop already defined -- ignored.\n");
		return;
	}

	if (scale < 0) {
		twmrc_error_prefix();
		fprintf(stderr,
			"VirtualDesktop scale must be positive, not %d\n", scale);
		return;
	}
	Scr->VirtualDesktopDScale = scale;

	JunkMask = XParseGeometry (geom, &JunkX, &JunkY, &JunkWidth, &JunkHeight);
	if ((JunkMask & (WidthValue | HeightValue)) != 
	    (WidthValue | HeightValue)) {
	    twmrc_error_prefix();
	    fprintf (stderr, "bad VirtualDesktop \"%s\"\n", geom);
	    return;
	}
	if (JunkWidth <= 0 || JunkHeight <= 0) {
	    twmrc_error_prefix();
	    fprintf (stderr, "VirtualDesktop \"%s\" must be positive\n", geom);
	    return;
	}

	JunkWidth *= Scr->VirtualDesktopDScale;
	JunkHeight *= Scr->VirtualDesktopDScale;

	/* check that the vd is at least as big as the screen */
	if ((JunkWidth < Scr->MyDisplayWidth)
	    || (JunkHeight < Scr->MyDisplayHeight)) 
	{
		twmrc_error_prefix();
		fprintf(stderr,
			"VirtualDesktop must be larger than screen (%dx%d)\n",
			Scr->MyDisplayWidth, Scr->MyDisplayHeight);
		return;
	}

	Scr->VirtualDesktopWidth = JunkWidth;
	Scr->VirtualDesktopHeight = JunkHeight;

	if (JunkMask & XValue)
			Scr->VirtualDesktopDX = JunkX;
	if (JunkMask & YValue)
			Scr->VirtualDesktopDY = JunkY;

	/* all of the values looked reasonable */
	Scr->Virtual = TRUE;
}

void VirtualMoveWindow(t, x, y)
TwmWindow *t;
int x, y;
{
	if (!Scr->Virtual)
		return;

	/* move  window in virtual space */
	t->virtual_frame_x = x;
	t->virtual_frame_y = y;
	
	/* move it in real space */
	t->frame_x = V_TO_R_X(x);
	t->frame_y = V_TO_R_Y(y);
	
	XMoveWindow(dpy, t->frame,
		    t->frame_x, t->frame_y);
	
	/* update the display */
	AddToDesktop(t);

	if (!Scr->NoRaiseMove) {
		XRaiseWindow(dpy, t->frame);
		XRaiseWindow(dpy, t->VirtualDesktopDisplayWindow);
	}
}

/* not currently use.  I can't decide how to deal with this */
#ifdef notdef
/*
 * F_MOVESCREEN function.
 * if the context is C_VIRTUAL then we got a `do this' in the virtual desktop,
 * else we must save cursor position, warp to the vd display, do the move and then
 * warp back.
 */
void MoveScreenAroundDesktop(ev, context)
XEvent *ev;
int context;
{
	
}
#endif /* notdef */
