/*
 * Copyright (C) 1993 Robert Nation
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/***********************************************************************
 *
 * afterstep pager handling code
 *
 ***********************************************************************/

#include "../configure.h"

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include "afterstep.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "module.h"

extern XEvent Event;

XGCValues Globalgcv;
unsigned long Globalgcm;

void DrawPartitionLines(void);
ASWindow *FindCounterpart(Window target);
Bool pagerOn = True;
Bool EnablePagerRedraw = True;
Bool DoHandlePageing = True;

void RaiseWindow(ASWindow * t)
{
    ASWindow *t2, *tn, *top, *ttop;
    int count, i;
    Window *wins;
    SetTimer(0);

    /* raise the target, at least */
    count = 1;
    Broadcast(M_RAISE_WINDOW, 3, t->w, t->frame, (unsigned long) t, 0, 0, 0, 0);

    if (t->next)
	t->next->prev = t->prev;
    t->prev->next = t->next;

    top = &Scr.ASRoot;
    ttop = NULL;
    for (t2 = Scr.ASRoot.next; t2 != NULL; t2 = tn) {
	tn = t2->next;
	if (t2->flags & ONTOP) {
	    count++;
	    if (top->next != t2) {
		t2->prev->next = t2->next;
		if (t2->next)
		    t2->next->prev = t2->prev;
		t2->next = top->next;
		t2->prev = top;
		top->next->prev = t2;
		top->next = t2;
	    }
	    top = t2;
	}
#ifndef DONT_RAISE_TRANSIENTS
	else if ((t2->flags & TRANSIENT) && (t2->transientfor == t->w)) {
	    count++;
	    Broadcast(M_RAISE_WINDOW, 3, t2->w, t2->frame, (unsigned long) t2,
		      0, 0, 0, 0);
	    if ((t2->flags & ICONIFIED) && (!(t2->flags & SUPPRESSICON))) {
		count++;
	    }
	    if (ttop == NULL)
		ttop = top;
	    if (ttop->next != t2) {
		t2->prev->next = t2->next;
		if (t2->next)
		    t2->next->prev = t2->prev;
		t2->next = ttop->next;
		t2->prev = ttop;
		ttop->next->prev = t2;
		ttop->next = t2;
	    }
	    ttop = t2;
	}
#endif
    }

    if ((t->flags & ICONIFIED) && !(t->flags & SUPPRESSICON))
	count++;

    if (ttop == NULL)
	ttop = top;		/* where to insert a non-ontop window */

    wins = (Window *) safemalloc(count * sizeof(Window));
    i = 0;

    if (t->flags & ONTOP)
	wins[i++] = t->frame;

    /* ONTOP windows on top */
    for (t2 = Scr.ASRoot.next; t2 != top->next; t2 = t2->next) {
	Broadcast(M_RAISE_WINDOW, 3, t2->w, t2->frame, (unsigned long) t2,
		  0, 0, 0, 0);
	wins[i++] = t2->frame;
    }

    /* now raise transients */
#ifndef DONT_RAISE_TRANSIENTS
    for (; t2 != ttop->next; t2 = t2->next) {
	wins[i++] = t2->frame;
	if ((t2->flags & ICONIFIED) && !(t2->flags & SUPPRESSICON)) {
#ifdef NO_ICON_BACKGROUND
	    if (!(t2->flags & NOICON_TITLE))
		wins[i++] = t2->icon_w;
#endif
	    wins[i++] = t2->icon_pixmap_w;
	}
    }
#endif

    if ((t->flags & ICONIFIED) && !(t->flags & SUPPRESSICON)) {
#ifdef NO_ICON_BACKGROUND
	if (!(t->flags & NOICON_TITLE))
	    wins[i++] = t->icon_w;
	wins[i++] = t->icon_pixmap_w;
#endif
    }
    if (t->flags & ONTOP) {
	t->next = Scr.ASRoot.next;
	t->prev = &Scr.ASRoot;
	if (Scr.ASRoot.next)
	    Scr.ASRoot.next->prev = t;
	Scr.ASRoot.next = t;
    } else {
	wins[i++] = t->frame;

	t->prev = ttop;
	t->next = ttop->next;
	if (ttop->next)
	    ttop->next->prev = t;
	ttop->next = t;
    }

    XRaiseWindow(dpy, wins[0]);
    XRestackWindows(dpy, wins, i);
    free(wins);

#ifndef NO_VIRTUAL
    raisePanFrames();
#endif
    UpdateVisibility();
}


void LowerWindow(ASWindow * t)
{
    XLowerWindow(dpy, t->frame);

    SetTimer(0);

    if (t->next) {
	ASWindow *tn = t->next;
	while (tn->next)
	    tn = tn->next;
	t->next->prev = t->prev;
	t->prev->next = t->next;
	tn->next = t;
	t->prev = tn;
	t->next = NULL;
    }
    Broadcast(M_LOWER_WINDOW, 3, t->w, t->frame, (unsigned long) t, 0, 0, 0, 0);

    if ((t->flags & ICONIFIED) && (!(t->flags & SUPPRESSICON))) {
#ifdef NO_ICON_BACKGROUND
	XLowerWindow(dpy, t->icon_w);
#endif
	XLowerWindow(dpy, t->icon_pixmap_w);
    }
    UpdateVisibility();
}


/***************************************************************************
 * 
 * Check to see if the pointer is on the edge of the screen, and scroll/page
 * if needed 
 ***************************************************************************/
void HandlePaging(int HorWarpSize, int VertWarpSize, int *xl, int *yt,
		  int *delta_x, int *delta_y, Bool Grab)
{
#ifndef NO_VIRTUAL
    int x, y, total;
#endif

    *delta_x = 0;
    *delta_y = 0;

#ifndef NO_VIRTUAL
    if (DoHandlePageing) {
	if ((Scr.ScrollResistance >= 10000) ||
	    ((HorWarpSize == 0) && (VertWarpSize == 0)))
	    return;

	/* need to move the viewport */
	if ((*xl >= SCROLL_REGION) && (*xl < Scr.MyDisplayWidth - SCROLL_REGION) &&
	    (*yt >= SCROLL_REGION) && (*yt < Scr.MyDisplayHeight - SCROLL_REGION))
	    return;

	total = 0;
	while (total < Scr.ScrollResistance) {
	    sleep_a_little(10000);
	    total += 10;
	    if (XCheckWindowEvent(dpy, Scr.PanFrameTop.win,
				  LeaveWindowMask, &Event)) {
		StashEventTime(&Event);
		return;
	    }
	    if (XCheckWindowEvent(dpy, Scr.PanFrameBottom.win,
				  LeaveWindowMask, &Event)) {
		StashEventTime(&Event);
		return;
	    }
	    if (XCheckWindowEvent(dpy, Scr.PanFrameLeft.win,
				  LeaveWindowMask, &Event)) {
		StashEventTime(&Event);
		return;
	    }
	    if (XCheckWindowEvent(dpy, Scr.PanFrameRight.win,
				  LeaveWindowMask, &Event)) {
		StashEventTime(&Event);
		return;
	    }
	}

	XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
		      &x, &y, &JunkX, &JunkY, &JunkMask);

	/* Turn off the rubberband if its on */
	MoveOutline(Scr.Root, 0, 0, 0, 0);

	/* Move the viewport */
	/* and/or move the cursor back to the approximate correct location */
	/* that is, the same place on the virtual desktop that it */
	/* started at */
	if (x < SCROLL_REGION)
	    *delta_x = -HorWarpSize;
	else if (x >= Scr.MyDisplayWidth - SCROLL_REGION)
	    *delta_x = HorWarpSize;
	else
	    *delta_x = 0;
	if (y < SCROLL_REGION)
	    *delta_y = -VertWarpSize;
	else if (y >= Scr.MyDisplayHeight - SCROLL_REGION)
	    *delta_y = VertWarpSize;
	else
	    *delta_y = 0;

	/* Ouch! lots of bounds checking */
	if (Scr.Vx + *delta_x < 0) {
	    if (!(Scr.flags & EdgeWrapX)) {
		*delta_x = -Scr.Vx;
		*xl = x - *delta_x;
	    } else {
		*delta_x += Scr.VxMax + Scr.MyDisplayWidth;
		*xl = x + *delta_x % Scr.MyDisplayWidth + HorWarpSize;
	    }
	} else if (Scr.Vx + *delta_x > Scr.VxMax) {
	    if (!(Scr.flags & EdgeWrapX)) {
		*delta_x = Scr.VxMax - Scr.Vx;
		*xl = x - *delta_x;
	    } else {
		*delta_x -= Scr.VxMax + Scr.MyDisplayWidth;
		*xl = x + *delta_x % Scr.MyDisplayWidth - HorWarpSize;
	    }
	} else
	    *xl = x - *delta_x;

	if (Scr.Vy + *delta_y < 0) {
	    if (!(Scr.flags & EdgeWrapY)) {
		*delta_y = -Scr.Vy;
		*yt = y - *delta_y;
	    } else {
		*delta_y += Scr.VyMax + Scr.MyDisplayHeight;
		*yt = y + *delta_y % Scr.MyDisplayHeight + VertWarpSize;
	    }
	} else if (Scr.Vy + *delta_y > Scr.VyMax) {
	    if (!(Scr.flags & EdgeWrapY)) {
		*delta_y = Scr.VyMax - Scr.Vy;
		*yt = y - *delta_y;
	    } else {
		*delta_y -= Scr.VyMax + Scr.MyDisplayHeight;
		*yt = y + *delta_y % Scr.MyDisplayHeight - VertWarpSize;
	    }
	} else
	    *yt = y - *delta_y;

	if (*xl <= SCROLL_REGION)
	    *xl = SCROLL_REGION + 1;
	if (*yt <= SCROLL_REGION)
	    *yt = SCROLL_REGION + 1;
	if (*xl >= Scr.MyDisplayWidth - SCROLL_REGION)
	    *xl = Scr.MyDisplayWidth - SCROLL_REGION - 1;
	if (*yt >= Scr.MyDisplayHeight - SCROLL_REGION)
	    *yt = Scr.MyDisplayHeight - SCROLL_REGION - 1;

	if ((*delta_x != 0) || (*delta_y != 0)) {
	    if (Grab)
		XGrabServer(dpy);
	    XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, *xl, *yt);
	    MoveViewport(Scr.Vx + *delta_x, Scr.Vy + *delta_y, False);
	    XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
			  xl, yt, &JunkX, &JunkY, &JunkMask);
	    if (Grab)
		XUngrabServer(dpy);
	}
    }
#endif
}
