



#include "WINGsP.h"


typedef struct W_ScrollView {
    W_Class widgetClass;
    WMView *view;

    WMView *contentView;
    WMView *viewport;
    
    WMScroller *vScroller;
    WMScroller *hScroller;
    
    short lineScroll;
    short pageScroll;
    
    struct {
	WMReliefType relief:3;
	unsigned int hasVScroller:1;
	unsigned int hasHScroller:1;
	
    } flags;
    
} ScrollView;




static void destroyScrollView(ScrollView *sPtr);

static void paintScrollView(ScrollView *sPtr);
static void handleEvents(XEvent *event, void *data);
static void handleViewportEvents(XEvent *event, void *data);
static void resizeScrollView();


W_ViewDelegate _ScrollViewViewDelegate = {
    NULL,
	NULL,
	resizeScrollView,
	NULL,
	NULL
};


	      
WMScrollView*
WMCreateScrollView(WMWidget *parent)
{
    ScrollView *sPtr;

    sPtr = wmalloc(sizeof(ScrollView));
    memset(sPtr, 0, sizeof(ScrollView));

    sPtr->widgetClass = WC_ScrollView;
    
    sPtr->view = W_CreateView(W_VIEW(parent));
    if (!sPtr->view) {
	wfree(sPtr);
	return NULL;
    }
    sPtr->viewport = W_CreateView(sPtr->view);
    if (!sPtr->view) {
	W_DestroyView(sPtr->view);
	wfree(sPtr);
	return NULL;
    }
    sPtr->view->self = sPtr;

    sPtr->view->delegate = &_ScrollViewViewDelegate;

    sPtr->viewport->flags.mapWhenRealized = 1;

    WMCreateEventHandler(sPtr->view, StructureNotifyMask|ExposureMask,
			 handleEvents, sPtr);
    WMCreateEventHandler(sPtr->viewport, SubstructureNotifyMask, 
			 handleViewportEvents, sPtr);

    sPtr->lineScroll = 4;

    sPtr->pageScroll = 0;

    return sPtr;
}



static void
reorganizeInterior(WMScrollView *sPtr)
{
    int hx, hy, hw;
    int vx, vy, vh;
    int cx, cy, cw, ch;
    
    
    cw = hw = sPtr->view->size.width;
    vh = ch = sPtr->view->size.height;

    if (sPtr->flags.relief == WRSimple) {
	cw -= 2;
	ch -= 2;
	cx = 1;
	cy = 1;	
    } else if (sPtr->flags.relief != WRFlat) {
	cw -= 3;
	ch -= 3;
	cx = 2;
	cy = 2;
    } else {
	cx = 0;
	cy = 0;
    }
    
    if (sPtr->flags.hasHScroller) {
	int h = W_VIEW(sPtr->hScroller)->size.height;

	ch -= h;

	if (sPtr->flags.relief == WRSimple) {
	    hx = 0;
	    hy = sPtr->view->size.height - h;
	} else if (sPtr->flags.relief != WRFlat) {
	    hx = 1;
	    hy = sPtr->view->size.height - h - 1;
	    hw -= 2;
	} else {
	    hx = 0;
	    hy = sPtr->view->size.height - h;
	}
    } else {
	/* make compiler shutup */
	hx = 0;
	hy = 0;
    }
    
    if (sPtr->flags.hasVScroller) {
	int w = W_VIEW(sPtr->vScroller)->size.width;
	cw -= w;
	cx += w;
	hx += w - 1;
	hw -= w - 1;

	if (sPtr->flags.relief == WRSimple) {
	    vx = 0;
	    vy = 0;
	} else if (sPtr->flags.relief != WRFlat) {
	    vx = 1;
	    vy = 1;
	    vh -= 2;
	} else {
	    vx = 0;
	    vy = 0;
	}
    } else {
	/* make compiler shutup */
	vx = 0;
	vy = 0;
    }

    W_ResizeView(sPtr->viewport, cw, ch);
    W_MoveView(sPtr->viewport, cx, cy);
    
    if (sPtr->flags.hasHScroller) {
	WMResizeWidget(sPtr->hScroller, hw, 20);
	WMMoveWidget(sPtr->hScroller, hx, hy);
    }
    if (sPtr->flags.hasVScroller) {
	WMResizeWidget(sPtr->vScroller, 20, vh);
	WMMoveWidget(sPtr->vScroller, vx, vy);
    }
}


static void
resizeScrollView(W_ViewDelegate *self, WMView *view)
{
    reorganizeInterior(view->self);
}



void
WMResizeScrollViewContent(WMScrollView *sPtr, unsigned int width, 
			  unsigned int height)
{
    int w, h, x;

    w = width;
    h = height;

    x = 0;
    if (sPtr->flags.relief == WRSimple) {
	w += 2;
	h += 2;
    } else if (sPtr->flags.relief != WRFlat) {
	w += 4;
	h += 4;
	x = 1;
    }
    
    if (sPtr->flags.hasVScroller) {
	w -= W_VIEW(sPtr->hScroller)->size.width;
	WMResizeWidget(sPtr->vScroller, 20, h);
    }    
    if (sPtr->flags.hasHScroller) {
	h -= W_VIEW(sPtr->hScroller)->size.height;
	WMResizeWidget(sPtr->hScroller, w, 20);
	WMMoveWidget(sPtr->hScroller, x, h);
    }

    W_ResizeView(sPtr->view, w, h);

    W_ResizeView(sPtr->viewport, width, height);
}


void
WMSetScrollViewLineScroll(WMScrollView *sPtr, int amount)
{
    assert(amount > 0);
    
    sPtr->lineScroll = amount;
}


void
WMSetScrollViewPageScroll(WMScrollView *sPtr, int amount)
{
    assert(amount >= 0);

    sPtr->pageScroll = amount;
}


static void
doScrolling(WMWidget *self, void *data)
{
    ScrollView *sPtr = (ScrollView*)data;
    float value;
    int pos;
    int vpsize;
    float size;
    
    if (sPtr->hScroller == (WMScroller *)self) {
	pos = -sPtr->contentView->pos.x;
	size = sPtr->contentView->size.width-sPtr->viewport->size.width;
	vpsize = sPtr->viewport->size.width - sPtr->pageScroll;
    } else {
	pos = -sPtr->contentView->pos.y;
	size = sPtr->contentView->size.height-sPtr->viewport->size.height;
	vpsize = sPtr->viewport->size.height - sPtr->pageScroll;
    }
    if (vpsize <= 0)
	vpsize = 1;

    switch (WMGetScrollerHitPart(self)) {
     case WSDecrementLine:
	if (pos > 0) {
	    pos-=sPtr->lineScroll;
	    if (pos < 0)
		pos = 0;
	    value = (float)pos / size;
	    WMSetScrollerParameters(self, value,
				    WMGetScrollerKnobProportion(self));
	}
	break;
     case WSIncrementLine:
	if (pos < size) {
	    pos+=sPtr->lineScroll;
	    if (pos > size)
		pos = size;
	    value = (float)pos / size;
	    WMSetScrollerParameters(self, value,
				    WMGetScrollerKnobProportion(self));
	}
	break;
     case WSKnob:
	value = WMGetScrollerValue(self);
	pos = value*size;
	break;
	
     case WSDecrementPage:
	if (pos > 0) {
	    pos -= vpsize;
	    if (pos < 0)
		pos = 0;
	    value = (float)pos / size;
	    WMSetScrollerParameters(self, value,
				    WMGetScrollerKnobProportion(self));
	}
	break;
	
     case WSIncrementPage:
	if (pos < size) {
	    pos += vpsize;
	    if (pos > size)
		pos = size;
	    value = (float)pos / size;
	    WMSetScrollerParameters(self, value,
				    WMGetScrollerKnobProportion(self));
	}
	break;
	
     case WSNoPart:
     case WSKnobSlot:
	break;
    }
    
    if (sPtr->hScroller == (WMScroller *)self) {
	W_MoveView(sPtr->contentView, -pos, sPtr->contentView->pos.y);
    } else {
	W_MoveView(sPtr->contentView, sPtr->contentView->pos.x, -pos);
    }
}


WMScroller*
WMGetScrollViewHorizontalScroller(WMScrollView *sPtr)
{
    return sPtr->hScroller;
}



WMScroller*
WMGetScrollViewVerticalScroller(WMScrollView *sPtr)
{
    return sPtr->vScroller;
}


void
WMSetScrollViewHasHorizontalScroller(WMScrollView *sPtr, Bool flag)
{
    if (flag) {
	if (sPtr->flags.hasHScroller)
	    return;
	sPtr->flags.hasHScroller = 1;
	
	sPtr->hScroller = WMCreateScroller(sPtr);
	WMSetScrollerAction(sPtr->hScroller, doScrolling, sPtr);
	/* make it a horiz. scroller */
	WMResizeWidget(sPtr->hScroller, 2, 1);

	reorganizeInterior(sPtr);

	WMMapWidget(sPtr->hScroller);
    } else {
	if (!sPtr->flags.hasHScroller)
	    return;
	
	WMUnmapWidget(sPtr->hScroller);
	WMDestroyWidget(sPtr->hScroller);
	sPtr->hScroller = NULL;
	sPtr->flags.hasHScroller = 0;
	
	reorganizeInterior(sPtr);
    }
}


void
WMSetScrollViewHasVerticalScroller(WMScrollView *sPtr, Bool flag)
{
    if (flag) {
	if (sPtr->flags.hasVScroller)
	    return;
	sPtr->flags.hasVScroller = 1;
	
	sPtr->vScroller = WMCreateScroller(sPtr);
	WMSetScrollerAction(sPtr->vScroller, doScrolling, sPtr);
	WMSetScrollerArrowsPosition(sPtr->vScroller, WSAMaxEnd);
	/* make it a vert. scroller */
	WMResizeWidget(sPtr->vScroller, 1, 2);

	reorganizeInterior(sPtr);
	
	WMMapWidget(sPtr->vScroller);
    } else {
	if (!sPtr->flags.hasVScroller)
	    return;
	sPtr->flags.hasVScroller = 0;
	
	WMUnmapWidget(sPtr->vScroller);
	WMDestroyWidget(sPtr->vScroller);
	sPtr->vScroller = NULL;

	reorganizeInterior(sPtr);
    }
}


void
WMSetScrollViewContentView(WMScrollView *sPtr, WMView *view)
{
    assert(sPtr->contentView == NULL);
    
    sPtr->contentView = view;

    W_ReparentView(sPtr->contentView, sPtr->viewport, 0, 0);

    if (sPtr->flags.hasHScroller) {
	float prop;
	
	prop = (float)sPtr->viewport->size.width/sPtr->contentView->size.width;
	WMSetScrollerParameters(sPtr->hScroller, 0, prop);
    }
    if (sPtr->flags.hasVScroller) {
	float prop;
	
	prop = (float)sPtr->viewport->size.height/sPtr->contentView->size.height;
	
	WMSetScrollerParameters(sPtr->vScroller, 0, prop);
    }
}


void
WMSetScrollViewRelief(WMScrollView *sPtr, WMReliefType type)
{
    sPtr->flags.relief = type;
    
    if (sPtr->view->flags.mapped)
	paintScrollView(sPtr);
    
    
}


static void
paintScrollView(ScrollView *sPtr)
{
    W_DrawRelief(sPtr->view->screen, sPtr->view->window, 0, 0,
		 sPtr->view->size.width, sPtr->view->size.height,
		 sPtr->flags.relief);
}


static void
updateScrollerProportion(ScrollView *sPtr)
{
    float prop, value;
    
    if (sPtr->flags.hasHScroller) {
	prop = (float)sPtr->viewport->size.width/sPtr->contentView->size.width;
	value = WMGetScrollerValue(sPtr->hScroller);
	
	WMSetScrollerParameters(sPtr->hScroller, value, prop);
    }
    if (sPtr->flags.hasVScroller) {
	prop = (float)sPtr->viewport->size.height/sPtr->contentView->size.height;
	
	value = WMGetScrollerValue(sPtr->vScroller);
	
	WMSetScrollerParameters(sPtr->vScroller, value, prop);
    }
}


static void
handleViewportEvents(XEvent *event, void *data)
{
    ScrollView *sPtr = (ScrollView*)data;

    if (sPtr->contentView 
	&& event->xconfigure.window == sPtr->contentView->window)
	updateScrollerProportion(sPtr);
}


static void
handleEvents(XEvent *event, void *data)
{
    ScrollView *sPtr = (ScrollView*)data;

    CHECK_CLASS(data, WC_ScrollView);

    switch (event->type) {	
     case Expose:
	if (event->xexpose.count!=0)
	    break;
	paintScrollView(sPtr);
	break;
	
     case DestroyNotify:
	destroyScrollView(sPtr);
	break;
	
    }
}


static void
destroyScrollView(ScrollView *sPtr)
{
    
   
    wfree(sPtr);
}

