/* -*-c-*- */
/* This module, and the entire GoodStuff program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation
 *
 * Copyright 1993, Robert Nation. No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact. */

/* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#if 0
#define MAX_ICON_NAME_LEN 255
#endif

#include "config.h"

#ifdef HAVE_SYS_BSDTYPES_H
#include <sys/bsdtypes.h> /* Saul */
#endif

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include "libs/ftime.h"

#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include "libs/Module.h"
#include "libs/fvwmlib.h"
#include "libs/Colorset.h"
#include "libs/PictureBase.h"
#include "libs/FScreen.h"
#include "libs/Graphics.h"
#include "libs/Parse.h"
#include "libs/Strings.h"

#include "FvwmScroll.h"

static int Width = 300, Height = 300;
static int target_width, target_height;
static int target_x_offset = 0, target_y_offset = 0;
static int exposed;
int Reduction_H = 2;
int Reduction_V = 2;
int Percent_H = -1;
int Percent_V =-1;

#define BAR_WIDTH 21
#define SCROLL_BAR_WIDTH 9

#define PAD_WIDTH2 3
#define PAD_WIDTH3 5

static Window main_win,holder_win;
static Pixel hilite_pix,shadow_pix;
extern int colorset;
extern char *BackColor;

#define MW_EVENTS   (ExposureMask | StructureNotifyMask| ButtonReleaseMask |\
		     ButtonPressMask | ButtonMotionMask | FocusChangeMask)

Atom wm_del_win;
Atom _XA_WM_PROTOCOLS;
Atom _XA_WM_COLORMAP_WINDOWS;

/*
 *
 * Sizes and creates the window
 *
 */
XSizeHints mysizehints;
void CreateWindow(int x,int y, int w, int h)
{
  XGCValues gcv;
  unsigned long gcm;
  XSetWindowAttributes attributes;
  int wx;
  int wy;

  wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
  _XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);

  mysizehints.flags = PWinGravity| PResizeInc | PBaseSize | PMaxSize | PMinSize
    | USSize | USPosition;
  /* subtract one for the right/bottom border */
  mysizehints.width_inc = 1;
  mysizehints.height_inc = 1;
  mysizehints.base_height = BAR_WIDTH + PAD_WIDTH3;
  mysizehints.base_width = BAR_WIDTH + PAD_WIDTH3;

  if ( Percent_V > -1 )
  {
    Width = (ScreenWidth * Percent_V)/100;
    if ( Width > (w + BAR_WIDTH + PAD_WIDTH3) || Percent_V == 0 )
      Width = w + BAR_WIDTH + PAD_WIDTH3;
  }
  else
  {
    Width = w/Reduction_V + BAR_WIDTH + PAD_WIDTH3;
  }

  if ( Percent_H > -1 )
  {
    Height = (ScreenHeight * Percent_H)/100;
    if ( Height > h || Percent_H == 0 )
      Height = h + BAR_WIDTH + PAD_WIDTH3;
  }
  else
  {
    Height = h/Reduction_H + BAR_WIDTH + PAD_WIDTH3;
  }

  target_width = w;
  target_height = h;
  mysizehints.width = Width;
  mysizehints.height = Height;
  wx = x;
  wy = y;
  mysizehints.max_width = w + BAR_WIDTH + PAD_WIDTH3;
  mysizehints.max_height = h + BAR_WIDTH + PAD_WIDTH3;

  mysizehints.win_gravity = NorthWestGravity;

  attributes.backing_store = NotUseful;
  attributes.background_pixel = (colorset < 0) ? GetColor(BackColor)
    : Colorset[colorset].bg;
  attributes.border_pixel = 0;
  attributes.colormap = Pcmap;
  main_win = XCreateWindow(dpy, Root, wx, wy,
			   mysizehints.width, mysizehints.height, 0, Pdepth,
			   InputOutput, Pvisual, CWBackingStore | CWBackPixel
			   | CWBorderPixel | CWColormap, &attributes);

  hilite_pix = (colorset < 0) ? GetHilite(attributes.background_pixel)
    : Colorset[colorset].hilite;
  shadow_pix = (colorset < 0) ? GetShadow(attributes.background_pixel)
    : Colorset[colorset].shadow;

  XSetWMProtocols(dpy,main_win,&wm_del_win,1);

  /* hack to prevent mapping on wrong screen with StartsOnScreen */
  FScreenMangleScreenIntoUSPosHints(FSCREEN_XYPOS, &mysizehints);
  XSetWMNormalHints(dpy,main_win,&mysizehints);
  XSelectInput(dpy,main_win,MW_EVENTS);
  change_window_name(module->name);

  holder_win = XCreateWindow(dpy, main_win, PAD_WIDTH3, PAD_WIDTH3,
			     mysizehints.width - BAR_WIDTH - PAD_WIDTH3,
			     mysizehints.height - BAR_WIDTH - PAD_WIDTH3,
			     0, Pdepth, InputOutput, Pvisual, CWBackingStore
			     | CWBackPixel | CWBorderPixel | CWColormap,
			     &attributes);
  XMapWindow(dpy,holder_win);


  gcm = GCForeground;
  gcv.foreground = hilite_pix;
  hiliteGC = fvwmlib_XCreateGC(dpy, holder_win, gcm, &gcv);
  gcv.foreground = shadow_pix;
  shadowGC = fvwmlib_XCreateGC(dpy, holder_win, gcm, &gcv);

  if (colorset >= 0)
    SetWindowBackground(dpy, main_win, mysizehints.width, mysizehints.height,
			&Colorset[(colorset)], Pdepth, shadowGC,
			True);

  _XA_WM_COLORMAP_WINDOWS = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", False);
}

/*
  LoopOnEvents - Process all the X events we get
*/

#define RIGHT 7
#define LEFT 6
#define BOTTOM 5
#define TOP 4
#define QUIT 3
#define VERTICAL 2
#define HORIZONTAL 1
#define NONE 0
int motion = NONE;

void LoopOnEvents(Window target)
{
	Window root;
	int x, y;
	XEvent Event;
	XEvent ev;
	unsigned int tw, th, depth, border_width;
	char *temp;
	char *prop = NULL;
	Atom actual = None;
	int actual_format;
	unsigned long nitems, bytesafter;

	while (FPending(dpy)) {
		FNextEvent(dpy,&Event);
		switch(Event.type)
		{
		case Expose:
			while (FCheckTypedWindowEvent(
				dpy, main_win, Expose, &Event));
			exposed = 1;
			RedrawWindow(target);
			break;

		case ConfigureNotify:
			while (FCheckTypedWindowEvent(
				dpy, main_win, ConfigureNotify, &ev))
			{
				if (!ev.xconfigure.send_event)
					continue;
				Event.xconfigure.send_event = True;
			}
			if (!XGetGeometry(
				dpy, main_win, &root ,&x, &y, &tw, &th,
				&border_width, &depth))
			{
				return;
			}
			if ((tw != Width) || (th != Height))
			{
				if (colorset >= 0)
					SetWindowBackground(
						dpy, main_win, tw, th,
						&Colorset[(colorset)], Pdepth,
						shadowGC, True);
				XResizeWindow(
					dpy, holder_win,
					tw - BAR_WIDTH - PAD_WIDTH3,
					th - BAR_WIDTH - PAD_WIDTH3);
				Width = tw;
				Height = th;
				if (target_y_offset > target_height - Height +
				    BAR_WIDTH)
					target_y_offset =
						target_height - Height +
						BAR_WIDTH;
				if (target_y_offset < 0)
					target_y_offset = 0;
				if (target_x_offset > target_width - Width +
				    BAR_WIDTH)
					target_x_offset =
						target_width - Width + BAR_WIDTH;
				if (target_x_offset < 0)
					target_x_offset = 0;

				XMoveWindow(
					dpy, target, -target_x_offset,
					-target_y_offset);
			}
			else if (Event.xconfigure.send_event &&
				 CSET_IS_TRANSPARENT(colorset))
			{
				SetWindowBackground(
					dpy, main_win, tw, th,
					&Colorset[(colorset)], Pdepth,
					shadowGC, True);
			}
			break;

		case ButtonPress:
			if((Event.xbutton.y > Height-BAR_WIDTH) &&
			   (Event.xbutton.x < SCROLL_BAR_WIDTH+PAD_WIDTH3))
			{
				motion = LEFT;
				exposed = 2;
				RedrawWindow(target);
			}
			else if((Event.xbutton.y > Height - BAR_WIDTH) &&
				(Event.xbutton.x > Width - BAR_WIDTH -
				 SCROLL_BAR_WIDTH-2) &&
				(Event.xbutton.x < Width-BAR_WIDTH))
			{
				motion = RIGHT;
				exposed = 2;
				RedrawWindow(target);
			}
			else if((Event.xbutton.y < SCROLL_BAR_WIDTH+PAD_WIDTH3)
				&& (Event.xbutton.x > Width-BAR_WIDTH))
			{
				motion = TOP;
				exposed = 2;
				RedrawWindow(target);
			}
			else if((Event.xbutton.y > Height - BAR_WIDTH -
				 SCROLL_BAR_WIDTH-2) &&
				(Event.xbutton.y < Height-BAR_WIDTH)&&
				(Event.xbutton.x > Width-BAR_WIDTH))
			{
				motion = BOTTOM;
				exposed = 2;
				RedrawWindow(target);
			}
			else if((Event.xbutton.x > Width - BAR_WIDTH)&&
				(Event.xbutton.y < Height- BAR_WIDTH))
			{
				motion = VERTICAL;
				target_y_offset=
					(Event.xbutton.y - PAD_WIDTH3-
					 SCROLL_BAR_WIDTH)* target_height/
					(Height-BAR_WIDTH-PAD_WIDTH3 -
					 2*SCROLL_BAR_WIDTH);
				if(target_y_offset+Height-BAR_WIDTH -PAD_WIDTH3 >
				   target_height)
					target_y_offset =
						target_height - Height+BAR_WIDTH
						+PAD_WIDTH3;
				if(target_y_offset < 0)
					target_y_offset = 0;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				RedrawWindow(target);
			}
			else if((Event.xbutton.y > Height- BAR_WIDTH ) &&
				(Event.xbutton.x < Width- BAR_WIDTH))
			{
				motion=HORIZONTAL;
				target_x_offset=
					(Event.xbutton.x -PAD_WIDTH3-
					 SCROLL_BAR_WIDTH)* target_width/
					(Width-BAR_WIDTH-PAD_WIDTH3-
					 2*SCROLL_BAR_WIDTH);
				if(target_x_offset < 0)
					target_x_offset = 0;

				if(target_x_offset + Width -BAR_WIDTH-PAD_WIDTH3>
				   target_width)
					target_x_offset =
						target_width - Width + BAR_WIDTH+
						PAD_WIDTH3;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				RedrawWindow(target);
			}
			else if((Event.xbutton.y > Height- BAR_WIDTH ) &&
				(Event.xbutton.x > Width- BAR_WIDTH))
			{
				exposed = 2;
				motion=QUIT;
			}
			RedrawWindow(target);
			break;

		case ButtonRelease:
			if((Event.xbutton.y > Height- BAR_WIDTH ) &&
			   (Event.xbutton.x > Width- BAR_WIDTH)&&
			   (motion==QUIT))
			{
				XUnmapWindow(dpy,main_win);
				XReparentWindow(dpy,target,Root,x,y);
				XSync(dpy,0);
				exit(0);
			}
			if((motion == LEFT)&&
			   (Event.xbutton.y > Height-BAR_WIDTH) &&
			   (Event.xbutton.x < SCROLL_BAR_WIDTH+PAD_WIDTH3))
			{
				target_x_offset -= (Width-BAR_WIDTH-PAD_WIDTH2);
				if(target_x_offset < 0)
					target_x_offset = 0;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				motion = NONE;
				exposed = 2;
			}
			else if((motion == RIGHT)&&
				(Event.xbutton.y > Height-BAR_WIDTH) &&
				(Event.xbutton.x >
				 Width-BAR_WIDTH-SCROLL_BAR_WIDTH-2) &&
				(Event.xbutton.x < Width-BAR_WIDTH))
			{
				target_x_offset += (Width-BAR_WIDTH-PAD_WIDTH2);
				if(target_x_offset+Width-BAR_WIDTH -PAD_WIDTH3 >
				   target_width)
					target_x_offset =
						target_width - Width+BAR_WIDTH+
						PAD_WIDTH3;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				motion = NONE;
				exposed = 2;
			}
			else if((motion == TOP)&&
				(Event.xbutton.y<SCROLL_BAR_WIDTH+PAD_WIDTH3)&&
				(Event.xbutton.x > Width-BAR_WIDTH))
			{
				target_y_offset -= (Height-BAR_WIDTH-PAD_WIDTH2);
				if(target_y_offset < 0)
					target_y_offset = 0;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				motion = NONE;
				exposed = 2;
			}
			else if((motion == BOTTOM)&&
				(Event.xbutton.y >
				 Height-BAR_WIDTH-SCROLL_BAR_WIDTH-2) &&
				(Event.xbutton.y < Height-BAR_WIDTH)&&
				(Event.xbutton.x > Width-BAR_WIDTH))
			{
				target_y_offset += (Height-BAR_WIDTH-PAD_WIDTH2);
				if(target_y_offset+Height-BAR_WIDTH -PAD_WIDTH3 >
				   target_height)
					target_y_offset = target_height -
						Height+BAR_WIDTH+PAD_WIDTH3;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
				motion = NONE;
				exposed = 2;
			}
			if(motion == VERTICAL)
			{
				target_y_offset =
					(Event.xbutton.y-PAD_WIDTH3-
					 SCROLL_BAR_WIDTH)*target_height /
					(Height-BAR_WIDTH-PAD_WIDTH3 -
					 2*SCROLL_BAR_WIDTH);
				if(target_y_offset+Height-BAR_WIDTH -PAD_WIDTH3 >
				   target_height)
					target_y_offset = target_height - Height+
						BAR_WIDTH+PAD_WIDTH3;
				if(target_y_offset < 0)
					target_y_offset = 0;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
			}
			if(motion == HORIZONTAL)
			{
				target_x_offset=
					(Event.xbutton.x -PAD_WIDTH3-
					 SCROLL_BAR_WIDTH) * target_width /
					(Width-BAR_WIDTH-PAD_WIDTH3-
					 2*SCROLL_BAR_WIDTH);
				if(target_x_offset < 0)
					target_x_offset = 0;

				if(target_x_offset + Width-BAR_WIDTH-PAD_WIDTH3>
				   target_width)
					target_x_offset =
						target_width - Width +
						BAR_WIDTH+PAD_WIDTH3;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
			}
			RedrawWindow(target);
			motion = NONE;
			break;

		case MotionNotify:
			if((motion == LEFT)&&
			   ((Event.xmotion.y < Height-BAR_WIDTH) ||
			    (Event.xmotion.x > SCROLL_BAR_WIDTH+PAD_WIDTH3)))
			{
				motion = NONE;
				exposed = 2;
			}
			else if((motion == RIGHT)&&
				((Event.xmotion.y < Height-BAR_WIDTH) ||
				 (Event.xmotion.x <
				  Width-BAR_WIDTH-SCROLL_BAR_WIDTH-2) ||
				 (Event.xmotion.x > Width-BAR_WIDTH)))
			{
				motion = NONE;
				exposed = 2;
			}
			else if((motion == TOP)&&
				((Event.xmotion.y>SCROLL_BAR_WIDTH+PAD_WIDTH3)||
				 (Event.xmotion.x < Width-BAR_WIDTH)))
			{
				motion = NONE;
				exposed = 2;
			}
			else if((motion == BOTTOM)&&
				((Event.xmotion.y <
				  Height-BAR_WIDTH-SCROLL_BAR_WIDTH-2) ||
				 (Event.xmotion.y > Height-BAR_WIDTH)||
				 (Event.xmotion.x < Width-BAR_WIDTH)))
			{
				motion = NONE;
				exposed = 2;
			}
			if(motion == VERTICAL)
			{
				target_y_offset=
					(Event.xmotion.y-PAD_WIDTH3-
					 SCROLL_BAR_WIDTH)*target_height/
					(Height-BAR_WIDTH-PAD_WIDTH3-
					 2*SCROLL_BAR_WIDTH);
				if(target_y_offset+Height-BAR_WIDTH -PAD_WIDTH3 >
				   target_height)
					target_y_offset =
						target_height - Height+BAR_WIDTH+
						PAD_WIDTH3;
				if(target_y_offset < 0)
					target_y_offset = 0;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
			}
			if(motion == HORIZONTAL)
			{
				target_x_offset =
					(Event.xmotion.x -PAD_WIDTH3-
					 SCROLL_BAR_WIDTH)* target_width/
					(Width-BAR_WIDTH-PAD_WIDTH3-
					 2*SCROLL_BAR_WIDTH);
				if(target_x_offset < 0)
					target_x_offset = 0;

				if(target_x_offset + Width-BAR_WIDTH-PAD_WIDTH3>
				   target_width)
					target_x_offset =
						target_width - Width + BAR_WIDTH+
						PAD_WIDTH3;
				XMoveWindow(
					dpy,target,-target_x_offset,
					-target_y_offset);
			}
			if((motion == QUIT)&&
			   ((Event.xbutton.y < Height- BAR_WIDTH )||
			    (Event.xbutton.x < Width- BAR_WIDTH)))
			{
				motion = NONE;
				exposed = 2;
			}
			RedrawWindow(target);
			break;
		case ClientMessage:
			if ((Event.xclient.format==32) &&
			    (Event.xclient.data.l[0]==wm_del_win))
			{
				DeadPipe(1);
			}
			break;
		case PropertyNotify:
			if(Event.xproperty.atom == XA_WM_NAME)
			{
				if(XFetchName(dpy, target, &temp)==0)
					temp = NULL;
				change_window_name(temp);
			}
			else if (Event.xproperty.atom == XA_WM_ICON_NAME)
			{
				if (XGetWindowProperty (
					dpy,
					target, Event.xproperty.atom, 0L,
					(long)MAX_ICON_NAME_LEN, False, XA_STRING,
					&actual,&actual_format, &nitems,
					&bytesafter, (unsigned char **) &prop)
				    == Success && (prop != NULL))
					change_icon_name(prop);
			}
			else if(Event.xproperty.atom == XA_WM_HINTS)
			{
				XWMHints *wmhints;

				wmhints = XGetWMHints(dpy,target);
				XSetWMHints(dpy,main_win, wmhints);
				XFree(wmhints);
			}
			else if(Event.xproperty.atom == XA_WM_NORMAL_HINTS)
			{
				/* don't do Normal Hints. They alter the size of
				 * the window */
			}
			else if (Event.xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
			{
				/* ignore colormap stuff. The FvwmScroll window
				 * may have a different visual to the client and
				 so cannot pass this on */
			}
			break;

		case DestroyNotify:
			DeadPipe(1);
			break;

		case UnmapNotify:
			break;

		case MapNotify:
			XMapWindow(dpy,main_win);
			break;
		case FocusIn:
			XSetInputFocus(dpy,target,RevertToParent,CurrentTime);
			break;
		case ColormapNotify:
			/* FvwmScroll may not have the same visual as the
			 * captured window and so cannot share its colormap
			 {
			 XWindowAttributes xwa;
			 if(XGetWindowAttributes(dpy,target, &xwa) != 0)
			 {
			 XSetWindowColormap(dpy,main_win,xwa.colormap);
			 }
			 }
			*/
			break;
		default:
			break;
		}
	}
}

/*
  ProcessMessage - Process the message coming from Fvwm
*/
void ProcessMessage(unsigned long type,unsigned long *body)
{
	char *tline, *token;

	switch(type)
	{
	case M_CONFIG_INFO:
		tline = (char*)&body[3];
		tline = GetNextToken(tline, &token);
		if (StrEquals(token, "Colorset"))
		{
			/* track all colorset changes and update display
			 * if necessary */
			if (LoadColorset(tline) == colorset)
			{
				XSetForeground(
					dpy, hiliteGC,Colorset[colorset].hilite);
				XSetForeground(
					dpy, shadowGC, Colorset[colorset].shadow);
				SetWindowBackground(
					dpy, main_win, Width, Height,
					&Colorset[colorset], Pdepth, shadowGC,
					True);
			}
		}
		free(token);
		break;
	case MX_PROPERTY_CHANGE:
		if (body[0] == MX_PROPERTY_CHANGE_BACKGROUND && body[2] == 0 &&
		    CSET_IS_TRANSPARENT_PR(colorset))
		{
			SetWindowBackground(
				dpy, main_win, Width, Height,
				&Colorset[colorset],
				Pdepth, shadowGC, True);
		}
		break;
	}
}

/*
  Loop -  Read and redraw until we die, blocking when can't read
*/
void Loop(Window target)
{
	while(1)
	{
		FvwmPacket* packet;
		fd_set fdset;

		FD_ZERO(&fdset);
		FD_SET(fd[1], &fdset);
		FD_SET(x_fd, &fdset);

		LoopOnEvents(target);

		select(fd_width, SELECT_FD_SET_CAST &fdset, NULL, NULL, NULL );

		/* parse any config lines (colorsets) */
		if (FD_ISSET(fd[1], &fdset))
		{
			packet = ReadFvwmPacket(fd[1]);
			if (!packet)
			{
				/* fvwm has gone away or done KillModule */
				XUnmapWindow(dpy,main_win);
				XReparentWindow(dpy,target,Root,0,0);
				XSync(dpy,0);
				exit(0);
			}
			else
			{
				ProcessMessage(packet->type, packet->body);
			}
		}
	}
	return;
}




/*
 *
 * Draw the window
 *
 */
void RedrawWindow(Window target)
{
  static int xv= 0,yv= 0,hv=0,wv=0;
  static int xh=0,yh=0,hh=0,wh=0;
  int x,y,w,h;
  XEvent dummy;

  while (FCheckTypedWindowEvent(dpy, main_win, Expose, &dummy))
    exposed |= 1;

  XSetWindowBorderWidth(dpy,target,0);

  RelieveRectangle(dpy, main_win, PAD_WIDTH3 - 2, PAD_WIDTH3 - 2,
		   Width-BAR_WIDTH - PAD_WIDTH3 + 3,
		   Height-BAR_WIDTH - PAD_WIDTH3 + 3,
		   shadowGC, hiliteGC, 2);

  y = (Height-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH)*
    target_y_offset/target_height
    + PAD_WIDTH2 + 2 + SCROLL_BAR_WIDTH;
  x = Width-SCROLL_BAR_WIDTH- PAD_WIDTH2-2;
  w = SCROLL_BAR_WIDTH;
  h = (Height-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH)*
    (Height-BAR_WIDTH-PAD_WIDTH3)/
    target_height;
  if((y!=yv)||(x != xv)||(w != wv)||(h != hv)||(exposed & 1))
  {
    yv = y;
    xv = x;
    wv = w;
    hv = h;
    XClearArea(dpy,main_win,x,PAD_WIDTH3+SCROLL_BAR_WIDTH,
	       w,Height-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH,False);

    RelieveRectangle(dpy, main_win, x, y, w - 1, h - 1,
		     hiliteGC, shadowGC, 2);
  }
  if(exposed & 1)
    RelieveRectangle(dpy, main_win, x - 2, PAD_WIDTH2, w + 3,
		     Height - BAR_WIDTH - PAD_WIDTH2 + 1,
		     shadowGC, hiliteGC, 2);
  if(exposed)
  {
    if(motion == TOP)
      RedrawTopButton(shadowGC,hiliteGC,x,PAD_WIDTH3);
    else
      RedrawTopButton(hiliteGC,shadowGC,x,PAD_WIDTH3);
    if(motion == BOTTOM)
      RedrawBottomButton(shadowGC,hiliteGC,x,
			 Height-BAR_WIDTH-SCROLL_BAR_WIDTH);
    else
      RedrawBottomButton(hiliteGC,shadowGC,x,
			 Height-BAR_WIDTH-SCROLL_BAR_WIDTH);
  }

  x = (Width-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH)*target_x_offset/
    target_width+PAD_WIDTH2+2 + SCROLL_BAR_WIDTH;
  y = Height-SCROLL_BAR_WIDTH-PAD_WIDTH2-2;
  w = (Width-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH)*
    (Width-BAR_WIDTH-PAD_WIDTH3)/target_width;
  h = SCROLL_BAR_WIDTH;
  if((y!=yh)||(x != xh)||(w != wh)||(h != hh)||(exposed & 1))
  {
    yh = y;
    xh = x;
    wh = w;
    hh = h;
    XClearArea(dpy,main_win,PAD_WIDTH3+SCROLL_BAR_WIDTH,y,
	       Width-BAR_WIDTH-PAD_WIDTH3-2*SCROLL_BAR_WIDTH,h,False);
    RelieveRectangle(dpy, main_win, x, y, w - 1, h - 1,
		     hiliteGC, shadowGC, 2);
  }
  if(exposed& 1)
  {
    RelieveRectangle(dpy, main_win, PAD_WIDTH2, y - 2,
		     Width - BAR_WIDTH - PAD_WIDTH2 + 1, h + 2,
		     shadowGC, hiliteGC, 2);
  }
  if(exposed)
  {
    if(motion == LEFT)
      RedrawLeftButton(shadowGC,hiliteGC,PAD_WIDTH3,y);
    else
      RedrawLeftButton(hiliteGC,shadowGC,PAD_WIDTH3,y);
    if(motion ==RIGHT)
      RedrawRightButton(shadowGC,hiliteGC,
			Width-BAR_WIDTH-SCROLL_BAR_WIDTH,y);
    else
      RedrawRightButton(hiliteGC,shadowGC,
			Width-BAR_WIDTH-SCROLL_BAR_WIDTH,y);
  }

  if(exposed)
  {
    XClearArea(dpy,main_win,Width-BAR_WIDTH+2,
	       Height-BAR_WIDTH+2,BAR_WIDTH-3,BAR_WIDTH-3,False);
    if(motion == QUIT)
      RelieveRectangle(dpy, main_win, Width - SCROLL_BAR_WIDTH - PAD_WIDTH2 - 4,
		       Height - SCROLL_BAR_WIDTH - PAD_WIDTH2 - 4,
		       SCROLL_BAR_WIDTH + 3, SCROLL_BAR_WIDTH + 3,
		       shadowGC, hiliteGC, 2);
    else
      RelieveRectangle(dpy, main_win, Width - SCROLL_BAR_WIDTH - PAD_WIDTH2 - 4,
		       Height - SCROLL_BAR_WIDTH - PAD_WIDTH2 - 4,
		       SCROLL_BAR_WIDTH + 3,SCROLL_BAR_WIDTH + 3,
		       hiliteGC, shadowGC, 2);
  }
  exposed = 0;
}


/*
 *  Change the window name displayed in the title bar.
 */
void change_window_name(char *str)
{
  XTextProperty name;

  if(str == NULL)
    return;

  if (XStringListToTextProperty(&str,1,&name) == 0)
  {
    fprintf(stderr,"%s: cannot allocate window name\n",module->name);
    return;
  }
  XSetWMName(dpy,main_win,&name);
  XFree(name.value);
}


/*
 *  Change the window name displayed in the icon.
 */
void change_icon_name(char *str)
{
  XTextProperty name;

  if(str == NULL)
    return;
  if (XStringListToTextProperty(&str,1,&name) == 0)
  {
    fprintf(stderr,"%s: cannot allocate window name\n",module->name);
    return;
  }
  XSetWMIconName(dpy,main_win,&name);
  XFree(name.value);
}


void GrabWindow(Window target)
{
  char *temp;
  Window Junk,root;
  unsigned int tw,th,border_width,depth;
  int x,y;
  char *prop = NULL;
  Atom actual = None;
  int actual_format;
  unsigned long nitems, bytesafter;

  XUnmapWindow(dpy,target);
  XSync(dpy,0);
  if (!XGetGeometry(dpy,target,&root,&x,&y,
		    (unsigned int *)&tw,(unsigned int *)&th,
		    (unsigned int *)&border_width,
		    (unsigned int *)&depth))
  {
    fprintf(stderr,"%s: cannot get window geometry\n", module->name);
    exit(0);
  }
  XSync(dpy,0);

  XTranslateCoordinates(dpy, target, Root, 0, 0, &x,&y, &Junk);

  CreateWindow(x,y,tw,th);
  XSetWindowBorderWidth(dpy,target,0);
  XReparentWindow(dpy,target, holder_win,0,0);
  XMapWindow(dpy,target);
  XSelectInput(dpy,target, PropertyChangeMask|StructureNotifyMask);
  if(XFetchName(dpy, target, &temp)==0)
    temp = NULL;
  if (XGetWindowProperty (dpy,
			  target, XA_WM_ICON_NAME, 0L,
			  (long)MAX_ICON_NAME_LEN, False, XA_STRING,
			  &actual,&actual_format, &nitems,
			  &bytesafter, (unsigned char **) &prop)
      == Success && (prop != NULL))
  {
    change_icon_name(prop);
    XFree(prop);
  }
  change_window_name(temp);
  {
    XWMHints *wmhints;

    wmhints = XGetWMHints(dpy,target);
    if(wmhints != NULL)
    {
      XSetWMHints(dpy,main_win, wmhints);
      XFree(wmhints);
    }
  }
  /* FvwmScroll may not share the same visual as the target window
   * and so cannot share its colormap  {
   XWindowAttributes xwa;
   if(XGetWindowAttributes(dpy,target, &xwa) != 0)
   {
   XSetWindowColormap(dpy,main_win,xwa.colormap);
   }
   }
  */
  XMapWindow(dpy,main_win);
  RedrawWindow(target);
  XFree(temp);
}





void RedrawLeftButton(GC rgc, GC sgc,int x1,int y1)
{
  XSegment seg[4];
  int i=0;

  seg[i].x1 = x1+1;                     seg[i].y1   = y1+SCROLL_BAR_WIDTH/2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+1;

  seg[i].x1 = x1;                       seg[i].y1   = y1+SCROLL_BAR_WIDTH/2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1;
  XDrawSegments(dpy, main_win, rgc, seg, i);

  i = 0;
  seg[i].x1 = x1+1;                     seg[i].y1   =y1+ SCROLL_BAR_WIDTH/2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 =y1+ SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1;                       seg[i].y1   = y1+SCROLL_BAR_WIDTH/2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH - 2;  seg[i].y1   = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH - 1;  seg[i].y1   = y1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;
  XDrawSegments(dpy,main_win, sgc, seg, i);
}

void RedrawRightButton(GC rgc, GC sgc,int x1,int y1)
{
  XSegment seg[4];
  int i=0;

  seg[i].x1 = x1+1;             seg[i].y1   = y1+1;
  seg[i].x2 = x1+1;             seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1;               seg[i].y1 = y1;
  seg[i].x2 = x1;               seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;

  seg[i].x1 = x1+1;             seg[i].y1 = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH/2;

  seg[i].x1 = x1;               seg[i].y1 = y1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH/2;

  XDrawSegments(dpy, main_win, rgc, seg, i);

  i = 0;
  seg[i].x1 = x1;               seg[i].y1 = y1+SCROLL_BAR_WIDTH - 2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH/2;

  seg[i].x1 = x1;                       seg[i].y1 = y1+SCROLL_BAR_WIDTH - 1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH/2;
  XDrawSegments(dpy,main_win, sgc, seg, i);
}

void RedrawTopButton(GC rgc, GC sgc,int x1,int y1)
{
  XSegment seg[4];
  int i=0;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH/2;    seg[i].y1 = y1+1;
  seg[i].x2 = x1+1;             seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH/2;    seg[i].y1 = y1;
  seg[i].x2 = x1+0;             seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;
  XDrawSegments(dpy, main_win, rgc, seg, i);

  i = 0;
  seg[i].x1 = x1+SCROLL_BAR_WIDTH/2;    seg[i].y1 = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH/2;    seg[i].y1 = y1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;

  seg[i].x1 = x1+1;             seg[i].y1 = y1+SCROLL_BAR_WIDTH - 2;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1+0;             seg[i].y1 = y1+SCROLL_BAR_WIDTH - 1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;
  XDrawSegments(dpy,main_win, sgc, seg, i);
}

void RedrawBottomButton(GC rgc, GC sgc,int x1, int y1)
{
  XSegment seg[4];
  int i=0;

  seg[i].x1 = x1+1;             seg[i].y1 = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH/2;    seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1;               seg[i].y1 = y1+0;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH/2;    seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;

  seg[i].x1 = x1+1;             seg[i].y1 = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 2;  seg[i++].y2 = y1+1;

  seg[i].x1 = x1;               seg[i].y1 = y1+0;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH - 1;  seg[i++].y2 = y1+0;
  XDrawSegments(dpy,main_win, rgc, seg, i);

  i = 0;
  seg[i].x1 = x1+SCROLL_BAR_WIDTH - 2;  seg[i].y1 = y1+1;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH/2;    seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 2;

  seg[i].x1 = x1+SCROLL_BAR_WIDTH - 1;  seg[i].y1 = y1+0;
  seg[i].x2 = x1+SCROLL_BAR_WIDTH/2;    seg[i++].y2 = y1+SCROLL_BAR_WIDTH - 1;
  XDrawSegments(dpy, main_win, sgc, seg, i);
}


syntax highlighted by Code2HTML, v. 0.9.1