/* 
 * WishScroll.c --
 *
 *	Routines for dealing with scrolling.
 *
 * Copyright 1987 Regents of the University of California
 * 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.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /a/newcmds/wish/RCS/wishScroll.c,v 1.3 89/01/11 11:58:35 mlgray Exp Locker: mgbaker $ SPRITE (Berkeley)";
#endif not lint


#include "string.h"
#include "sx.h"
#include "wishInt.h"

/* This is global for now but will go into WishWindow structure... */
extern	int	wishNumGroupsToHide;
extern	Boolean	wishSkipEmptyGroupsP;


/*
 *----------------------------------------------------------------------
 *
 * WishScroll --
 *
 *	Scrollbar call-back routine.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The display may scroll.
 *
 *----------------------------------------------------------------------
 */
void
WishScroll(clientData, distance, units, window)
    ClientData	clientData;
    float	distance;
    int		units;	/*  SX_SCROLL_ABSOLUTE or ..._PAGES */
    Window	window;
{
    float	top, bottom;
    float	newTop, newBottom;
    WishWindow	*aWindow;
    float	displayCover;		
    int		oldFirstElement;
    int		newLastElement;
    int		numDisplayed;
    int		numCouldDisplay;

    aWindow = (WishWindow *) clientData;

#ifdef SCROLL_DEBUG
    fprintf(stderr, "Entered WishScroll:\n");
    fprintf(stderr, "\tdistance is %f\n", distance);
    fprintf(stderr, "\tunits is %s\n", units == SX_SCROLL_PAGES ?
	    "scroll_pages" : "scroll_absolute");
    fprintf(stderr, "\tfirstElement is %d\n",
	    aWindow->firstElement);
    fprintf(stderr, "\tlastElement is %d\n",
	    aWindow->lastElement);
    fprintf(stderr, "\tnumElements is %d\n",
	    aWindow->numElements);
    fprintf(stderr, "\tnumGroups is %d\n",
	    aWindow->numGroups);
#endif SCROLL_DEBUG

    oldFirstElement = aWindow->firstElement;	/* save to compare */
    numCouldDisplay = aWindow->numRows * (aWindow->usedCol + 1);
    numDisplayed = aWindow->lastElement - aWindow->firstElement + 1;

    if (units == SX_SCROLL_PAGES) {
	/*
	 * If we're supposed to scroll forwards, check to see if the end of
	 * the display has already been reached.
	 */
	if (distance > 0 && (numDisplayed < numCouldDisplay ||
		aWindow->lastElement >= aWindow->totalDisplayEntries)) {
	    return;	/* No point -- already hit end of display */
	}
	aWindow->firstElement += distance * numCouldDisplay;
	/* no point in scrolling so that we waste space in last column */
	if (aWindow->firstElement + numCouldDisplay - 1 >
		aWindow->totalDisplayEntries) {
	    aWindow->firstElement = aWindow->totalDisplayEntries -
		    numCouldDisplay + 1;
	}
	/* The lowest numbered element that makes sense is number 1 */
	if (aWindow->firstElement < 1) {
	    aWindow->firstElement = 1;
	}
	if (aWindow->firstElement == oldFirstElement) {
	    return;	/* no change */
	}
	newLastElement = aWindow->firstElement + numCouldDisplay - 1;

#ifdef SCROLL_DEBUG
	fprintf(stderr,
		"WishScroll dealing with page scrolling:\n");
	fprintf(stderr, "\tfirstElement is %d\n",
		aWindow->firstElement);
	fprintf(stderr, "\tnewLastElement is %d\n",
		newLastElement);
	fprintf(stderr, "\tcalling SetPositions and Redraw\n");
#endif SCROLL_DEBUG

	/* scroll */
	WishSetPositions(aWindow);
	/*
	 * WishRedraw will be called from event caused in
	 * WishSetPositions().
	 */

#ifdef SCROLL_DEBUG
	{
	    float	a, b;

	    fprintf(stderr,
		    "End of WishScroll dealing with page scrolling:\n");
	    fprintf(stderr, "\tfirstElement is %d\n",
		    aWindow->firstElement);
	    fprintf(stderr, "\tlastElement is %d\n",
		    aWindow->lastElement);
	    Sx_ScrollbarGetRange(wishDisplay, window, &a, &b);
	    fprintf(stderr, "\ttop is now %f\n", a);
	    fprintf(stderr, "\tbottom is now %f\n", b);
	}
#endif SCROLL_DEBUG

	return;
    }
    if (units != SX_SCROLL_ABSOLUTE) {
	Sx_Panic(wishDisplay,
		"In WishScroll(): Unknown scrolling method requested.");
    }
    /* SX_SCROLL_ABSOLUTE from here on */

    Sx_ScrollbarGetRange(wishDisplay, window, &top, &bottom);

#ifdef SCROLL_DEBUG
    fprintf(stderr, "\tbefore scrolling, top is %f\n", top);
    fprintf(stderr, "\tbefore scrolling, bottom is %f\n", bottom);
#endif SCROLL_DEBUG

    if (aWindow->numElements <= 0) {
	displayCover = 1.0;
    } else {
	/* The portion of the total entries that can be visible. */
	displayCover = ((float) numCouldDisplay) /
		((float) aWindow->totalDisplayEntries);
	if (displayCover > 1.0) {
	    displayCover = 1.0; 	/* can't cover more than all of it */
	}
    }
#ifdef SCROLL_DEBUG
    fprintf(stderr, "\tdisplayCover is %f\n", displayCover);
#endif SCROLL_DEBUG

    newTop = distance - (displayCover / 2.0);
    newBottom = distance + (displayCover / 2.0);
    if (newTop < 0) {
	newTop = 0.0;
	newBottom = displayCover;
    }
    if (newBottom > 1.0) {
	newBottom = 1.0;
	newTop = 1.0 - displayCover;
    }
    if (newTop == top && newBottom == bottom) {
	return;		/* no change */
    }
    /*
     * I set firstElement from newTop unless newBottom happens to be
     * the very end.  If so, I make sure that the last element should
     * be visible to avoid roundoff errors.
     */
    if (newBottom == 1.0) {
	newLastElement = aWindow->totalDisplayEntries;
	aWindow->firstElement = aWindow->totalDisplayEntries -
		numCouldDisplay + 1;
	/*
	 * If there are fewer things to display than there are spaces,
	 * the above would leave firstElement < 0.  The lowest element
	 * we can start displaying is the first element.  (firstElement
	 * is incremented below by one.)
	 */
	if (aWindow->firstElement < 1) {
	    aWindow->firstElement = 1;
	}
    } else {
	aWindow->firstElement =
		(int) (newTop * aWindow->totalDisplayEntries) + 1;
#ifdef NOTDEF
	/* this seems not to be necessary */
	newLastElement = aWindow->firstElement + numCouldDisplay - 1;
#endif NOTDEF
    }
#ifdef SCROLL_DEBUG
    fprintf(stderr, "In WishScroll, after calculations:\n");
    fprintf(stderr, "\tfirstElement is %d\n",
	    aWindow->firstElement);
    fprintf(stderr, "\tnewLastElement is %d\n",
	    newLastElement);
    fprintf(stderr, "\tnewTop is %f\n", newTop);
    fprintf(stderr, "\tnewBottom is %f\n", newBottom);
    fprintf(stderr,
	    "\tcalling WishSetPositions, then WishRedraw\n");
#endif SCROLL_DEBUG
    if (oldFirstElement == aWindow->firstElement) {
	return;		/* no change */
    }

    WishSetPositions(aWindow);

    /* Things are where we want them.  WishRedraw() should be called
     * as a result of an event caused in WishSetPositions.  WishRedraw()
     * will call SetRange again...  Go scroll.
     */
	
#ifdef SCROLL_DEBUG
    {
	float	a, b;

	Sx_ScrollbarGetRange(wishDisplay, window, &a, &b);
	fprintf(stderr, "At end of WishScroll:\n");
	fprintf(stderr, "\tnewTop was supposed to be %f\n", newTop);
	fprintf(stderr, "\tnewBottom was supposed to be %f\n",
		newBottom);
	fprintf(stderr, "\tRedraw called SetRange and:\n");
	fprintf(stderr, "\ttop is really %f\n", a);
	fprintf(stderr, "\tbottom is really %f\n", b);
	fprintf(stderr, "\tfirstElement is %d\n",
		aWindow->firstElement);
	fprintf(stderr, "\tlastElement is %d\n",
		aWindow->lastElement);
	fprintf(stderr, "\tnumElements is %d\n",
		aWindow->numElements);
    }
#endif SCROLL_DEBUG

    return;
}
