/*
 * paging.cc
 *
 * Copyright (C) 1995, 1996, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Kenichi Kourai
 * Copyright (C) 1999, 2000, 2001, 2002 Luiz Blanes
 *
 * 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 blwm; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include "main.h"
#include "misc.h"
#include "blwm.h"
#include "blwmrc.h"
#include "paging.h"
#include "pager.h"
#include "mini.h"
#include "event.h"
#include "taskbar.h"
#include "desktop.h"
#include "gnome.h"
#include "util.h"

Paging::Paging(const Point& topLeft, const Dim& size)
: origin(Point(0, 0)), rcVirt(topLeft.x, topLeft.y, size.width, size.height)
{
  if (size.width <= 0) {
    BlwmError("The width of the pager must be positive (%d is an error)",
	      size.width);
    rcVirt.width = 0;
  }
  if (size.height <= 0) {
    BlwmError("The height of the pager must be positive (%d is an error)",
	      size.height);
    rcVirt.width = 0;
  }
  if (topLeft.x > 0 || topLeft.x + size.width - 1 < 0) {
    BlwmError("The pager must include a page (0, 0)");
    rcVirt.x = 0;
  }
  if (topLeft.y > 0 || topLeft.y + size.height - 1 < 0) {
    BlwmError("The pager must include a page (0, 0)");
    rcVirt.y = 0;
  }

  XSetWindowAttributes attributes;
  unsigned long valueMask;
  Cursor cs;
  Rect rcRoot = rootBlwm->GetRect();
  unsigned int fontCursor[] = {
    XC_bottom_side, XC_top_side,
    XC_left_side, XC_right_side,
    XC_bottom_left_corner, XC_bottom_right_corner,
    XC_top_left_corner, XC_top_right_corner
  };


  /*
   * Create transparent, long and slender windows in the 4 edges of screen
   * for paging.
   */
  attributes.override_redirect = True;
  attributes.event_mask = EnterWindowMask | LeaveWindowMask |
    Button1MotionMask;

  valueMask = CWEventMask | CWOverrideRedirect;

    for (int i = 0; i < 8; i++)
      belt[i] = None;
}

Paging::~Paging()
{
    return;
}

/*
 * PagingProc --
 *   Do paging to the direction of win.
 */
Point Paging::PagingProc(Window win, Bool rootFocus)
{
  Point ptDelta(0, 0);
  Rect rcRoot = rootBlwm->GetRect();
  Window junkRoot, junkChild;
  Point ptRoot, ptJunk;
  unsigned int mask;

  if (win == belt[LEFT])
    ptDelta.x = -(PagingMovement * rcRoot.width / 100);
  else if (win == belt[RIGHT])
    ptDelta.x = PagingMovement * rcRoot.width / 100;
  else if (win == belt[TOP])
    ptDelta.y = -(PagingMovement * rcRoot.height / 100);
  else if (win == belt[BOTTOM])
    ptDelta.y = PagingMovement * rcRoot.height / 100;
  else if (win == belt[BOTTOM_LEFT]) {
    ptDelta.x = -(PagingMovement * rcRoot.width / 100);
    ptDelta.y = PagingMovement * rcRoot.height / 100;
  }
  else if (win == belt[BOTTOM_RIGHT]) {
    ptDelta.x = PagingMovement * rcRoot.width / 100;
    ptDelta.y = PagingMovement * rcRoot.height / 100;
  }
  else if (win == belt[TOP_LEFT]) {
    ptDelta.x = -(PagingMovement * rcRoot.width / 100);
    ptDelta.y = -(PagingMovement * rcRoot.height / 100);
  }
  else if (win == belt[TOP_RIGHT]) {
    ptDelta.x = PagingMovement * rcRoot.width / 100;
    ptDelta.y = -(PagingMovement * rcRoot.height / 100);
  }
  else
    return Point(0, 0);

  Point oldOrigin = origin;

  origin.x += ptDelta.x;
  origin.y += ptDelta.y;

  PagingAllWindows(oldOrigin, rootFocus);

  XQueryPointer(display, root, &junkRoot, &junkChild, &ptRoot.x, &ptRoot.y,
		&ptJunk.x, &ptJunk.y, &mask);
  Point pt(ptRoot.x - ptDelta.x, ptRoot.y - ptDelta.y);

    pt.x = rcRoot.width - 1;
    pt.y = rcRoot.height - 1;

  XWarpPointer(display, None, root, 0, 0, 0, 0, pt.x, pt.y);

  desktop.ChangeFocusToCursor();

  return ptDelta;
}

/*
 * PagingProc --
 *   Do paging to the position of pt.
 */
void Paging::PagingProc(const Point& pt, Bool rootFocus)
{
  Point oldOrigin = origin;

  origin = pt;
  PagingAllWindows(oldOrigin, rootFocus);
}

/*
 * PagingAllWindows --
 *   Move all windows according to paging movement.
 */
void Paging::PagingAllWindows(const Point& oldOrigin, Bool rootFocus)
{
  MapBelts();

  if (oldOrigin.x == origin.x && oldOrigin.y == origin.y)
    return;

  List<Blwm>::Iterator i(&desktop.GetBlwmList());
  Point newOrigin = origin;

  for (int j = 0; j < PagingSpeed; j++) {
    origin.x = oldOrigin.x + (newOrigin.x - oldOrigin.x) * (j + 1)
      / PagingSpeed;
    origin.y = oldOrigin.y + (newOrigin.y - oldOrigin.y) * (j + 1)
      / PagingSpeed;

    for (Blwm* blWm = i.GetHead(); blWm; blWm = i.GetNext()) {
      Rect rect = blWm->GetRect();
      
      if (blWm->CheckFlags(STICKY)) {
	Point pt(rect.x - oldOrigin.x + origin.x,
		 rect.y - oldOrigin.y + origin.y);

	/*
	 * rc of STICKY window changes on paging.
	 * only last loop
	 */
	if (j == PagingSpeed - 1) {
	  rect.x = pt.x;
	  rect.y = pt.y;
	  blWm->SetRect(rect);
	}

	if (UsePager) {
	  ASSERT(blWm->mini);
	  blWm->mini->MoveMiniature(pager->ConvertToPagerPos(pt));
	}
	continue;
      }
      
      XMoveWindow(display, blWm->GetFrameWin(),
		  rect.x - origin.x, rect.y - origin.y);

      // only last loop
      if (j == PagingSpeed - 1) {
	blWm->SendConfigureEvent();
	if (Intersect(rect, rootBlwm->GetRect()))
	  Gnome::ResetState(blWm, WIN_STATE_HID_WORKSPACE);
	else
	  Gnome::SetState(blWm, WIN_STATE_HID_WORKSPACE);
      }
    }

    XFlush(display);
    usleep(10000);
  }

  if (UsePager) {
    ASSERT(pager);
    pager->DrawVisualPage();
  }

  Gnome::SetActiveDesktop();

  if (!ClickToFocus && NoDesktopFocus && rootFocus) {
    Point ptJunk;
    Window w;

    // set focus root only if pointer is on root.
    XTranslateCoordinates(display, root, root, ptJunk.x, ptJunk.y,
			  &ptJunk.x, &ptJunk.y, &w);
    if (w == root)
      rootBlwm->SetFocus();
  }

  if (rootFocus && UseTaskbar && TaskbarAutoHide)
    taskBar->HideTaskbar();

  if (TaskbarButtonInScr) {
    if (taskBar)
      taskBar->RedrawAllTaskbarButtons();
  }
}

void Paging::MapBelts()
{
  Rect rcRoot = rootBlwm->GetRect();

  // check left-most edge
  if (origin.x > rcVirt.x * rcRoot.width) {
  }
  else {
    origin.x = rcVirt.x * rcRoot.width;
  }

  // check right-most edge
  if (origin.x < (rcVirt.x + rcVirt.width - 1) * rcRoot.width) {
  }
  else {
    origin.x = (rcVirt.x + rcVirt.width - 1) * rcRoot.width;
  }

  // check top-most edge
  if (origin.y > rcVirt.y * rcRoot.height) {
  }
  else {
    origin.y = rcVirt.y * rcRoot.height;
  }

  // check bottom-most side
  if (origin.y < (rcVirt.y + rcVirt.height - 1) * rcRoot.height) {
  }
  else {
    origin.y = (rcVirt.y + rcVirt.height - 1) * rcRoot.height;
  }

}


Paging::BeltPos Paging::GetBeltPos(const Point& pt)
{
  Rect rcRoot = rootBlwm->GetRect();
  Rect rcBelt[] = {
    Rect(0, rcRoot.height - 0, rcRoot.width - 0 * 2, 0),
    Rect(0, 0, rcRoot.width - 0 * 2, 0),
    Rect(0, 0, 0, rcRoot.height - 0 * 2),
    Rect(rcRoot.width - 0, 0, 0, rcRoot.height - 0 * 2),
    Rect(0, rcRoot.height - 0, 0, 0),
    Rect(rcRoot.width - 0, rcRoot.height - 0, 0, 0),
    Rect(0, 0, 0, 0),
    Rect(rcRoot.width - 0, 0, 0, 0)
  };

  for (int i = 0; i < 8; i++) {
    if (InRect(pt, rcBelt[i]))
      return (BeltPos)i;
  }
    
  ASSERT(False);

  return BOTTOM;  // XXX
 }
 
 void Paging::SwitchPageLeft()
 {
   if (Menu::CheckAnyMenusMapped())
     return;
 
   Rect rcRoot = rootBlwm->GetRect();
   Point oldOrigin = origin;
 
   origin.x -= rcRoot.width;
   PagingAllWindows(oldOrigin);
 
   desktop.ChangeFocusToCursor();
 }
 
 void Paging::SwitchPageRight()
 {
   if (Menu::CheckAnyMenusMapped())
     return;
 
   Rect rcRoot = rootBlwm->GetRect();
   Point oldOrigin = origin;
 
   origin.x += rcRoot.width;
   PagingAllWindows(oldOrigin);
 
   desktop.ChangeFocusToCursor();
 }
 
 void Paging::SwitchPageUp()
 {
   if (Menu::CheckAnyMenusMapped())
     return;
 
   Rect rcRoot = rootBlwm->GetRect();
   Point oldOrigin = origin;
 
   origin.y -= rcRoot.height;
   PagingAllWindows(oldOrigin);
 
   desktop.ChangeFocusToCursor();
 }
 
 void Paging::SwitchPageDown()
 {
   if (Menu::CheckAnyMenusMapped())
     return;
 
   Rect rcRoot = rootBlwm->GetRect();
   Point oldOrigin = origin;
 
   origin.y += rcRoot.height;
   PagingAllWindows(oldOrigin);
 
   desktop.ChangeFocusToCursor();
}


syntax highlighted by Code2HTML, v. 0.9.1