
/*
 * 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 "../../include/afterstep.h"
#include "../../include/menus.h"
#include "../../include/parse.h"
#include "../../include/misc.h"
#include "../../include/screen.h"
#include "../../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;
  MenuRoot *menu;

  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;

  for (menu = Scr.first_menu; menu != NULL; menu = (*menu).next)
    if ((*menu).is_mapped == True)
      count++;

  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)))
	    {
#ifdef NO_ICON_BACKGROUND
	      if (!(t->flags & NOICON_TITLE))
		count++;
#endif
	      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))
    {
#ifdef NO_ICON_BACKGROUND
      if (!(t->flags & NOICON_TITLE))
	count++;
#endif
      count++;
    }

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

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

  /* menus _always_ go on top */
  for (menu = Scr.first_menu; menu != NULL; menu = (*menu).next)
    if ((*menu).is_mapped == True)
      wins[i++] = (*menu).w;

  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;
#endif
      wins[i++] = t->icon_pixmap_w;
    }
  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)
{
  ASWindow *t2, *tn, *last, *list, *onback, *transient;
  int count;
  Window *wins;

  SetTimer (0);

  /* get the target window */
  if (t->next)
    t->next->prev = t->prev;
  t->prev->next = t->next;
  t->prev = t->next = NULL;

  /* get ONBACK and transient windows */
  onback = NULL;
  transient = NULL;
  for (t2 = Scr.ASRoot.next; t2 != NULL; t2 = tn)
    {
      tn = t2->next;
      if (t2->flags & ONBACK)
	{
	  count++;
	  if (t2->next)
	    t2->next->prev = t2->prev;
	  t2->prev->next = t2->next;
	  /* add to the list of ONBACK windows */
	  t2->next = NULL;
	  if (onback != NULL)
	    {
	      ASWindow *ptr;
	      for (ptr = onback; ptr->next != NULL; ptr = ptr->next);
	      t2->prev = ptr;
	      ptr->next = t2;
	    }
	  else
	    {
	      t2->prev = NULL;
	      onback = t2;
	    }
	}
#ifndef DONT_RAISE_TRANSIENTS
      else if ((t2->flags & TRANSIENT) && (t2->transientfor == t->w))
	{
	  if (t2->next)
	    t2->next->prev = t2->prev;
	  t2->prev->next = t2->next;
	  /* add to the list of transient windows */
	  t2->next = NULL;
	  if (transient != NULL)
	    {
	      ASWindow *ptr;
	      for (ptr = transient; ptr->next != NULL; ptr = ptr->next);
	      t2->prev = ptr;
	      ptr->next = t2;
	    }
	  else
	    {
	      t2->prev = NULL;
	      transient = t2;
	    }
	}
#endif
    }

  /* make a list of all of the windows to move */
  list = transient;
  if (list != NULL)
    for (t2 = list; t2->next != NULL; t2 = t2->next);
  if (t2 == NULL)
    list = t2 = t;
  else
    {
      t2->next = t;
      t->prev = t2;
      t2 = t;
    }
  if (onback != NULL)
    {
      if (t->flags & ONBACK)
	{
	  /* other ONBACK windows go before this window */
	  for (t2 = onback; t2->next != NULL; t2 = t2->next);
	  list->prev = t2;
	  t2->next = list;
	  list = onback;
	}
      else
	{
	  /* ONBACK windows go after this window */
	  for (; t2->next != NULL; t2 = t2->next);
	  t2->next = onback;
	  onback->prev = t2;
	}
    }

  /* find the last window */
  for (last = &Scr.ASRoot; last->next != NULL; last = last->next);

  /* add the restacked windows back */
  last->next = list;
  list->prev = last;

  /* count the windows and broadcast the stacking change */
  count = 0;
  for (t2 = list; t2 != NULL; t2 = t2->next)
    {
      if ((t2->flags & ICONIFIED) && !(t2->flags & SUPPRESSICON))
	{
#ifdef NO_ICON_BACKGROUND
	  if (!(t2->flags & NOICON_TITLE))
	    count++;
#endif
	  count++;
	}
      count++;
      Broadcast (M_LOWER_WINDOW, 3, t2->w, t2->frame, (unsigned long) t2,
		 0, 0, 0, 0);
    }

  /* make an array of the windows */
  wins = (Window *) safemalloc ((count + 1) * sizeof (Window));

  count = 0;
  if (last != &Scr.ASRoot)
    wins[count++] = last->frame;

  for (t2 = list; t2 != NULL; t2 = t2->next)
    {
      if ((t2->flags & ICONIFIED) && !(t2->flags & SUPPRESSICON))
	{
#ifdef NO_ICON_BACKGROUND
	  if (!(t2->flags & NOICON_TITLE))
	    wins[count++] = t2->icon_w;
#endif
	  wins[count++] = t2->icon_pixmap_w;
	}
      wins[count++] = t2->frame;
    }

  XRestackWindows (dpy, wins, count);
  free (wins);

  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
}
