/*
 * TScrollBar.cc
 *
 * Turbo Vision - Version 2.0
 *
 * Copyright (c) 1994 by Borland International
 * All Rights Reserved.
 *
 * Modified by Sergio Sigala <sergio@sigala.it>
 */

#define Uses_TKeys
#define Uses_TScrollBar
#define Uses_TRect
#define Uses_TDrawBuffer
#define Uses_TEvent
#define Uses_TGroup
#define Uses_opstream
#define Uses_ipstream
#include <tvision/tv.h>

#include <ctype.h>
#include <string.h>

#define cpScrollBar  "\x04\x05\x05"

TScrollBar::TScrollBar( const TRect& bounds ) :
    TView( bounds ),
    value( 0 ),
    minVal( 0 ),
    maxVal( 0 ),
    pgStep( 1 ),
    arStep( 1 )
{
    if( size.x == 1 )
        {
        growMode = gfGrowLoX | gfGrowHiX | gfGrowHiY;
        memcpy( chars, vChars, sizeof(vChars) );
        }
    else
        {
        growMode = gfGrowLoY | gfGrowHiX | gfGrowHiY;
        memcpy( chars, hChars, sizeof(hChars) );
        }
}

void TScrollBar::draw()
{
    drawPos(getPos());
}

void TScrollBar::drawPos( int pos )
{
    TDrawBuffer b;

    int s = getSize() - 1;
    b.moveChar( 0, chars[0], getColor(2), 1 );
    if( maxVal == minVal )
        b.moveChar( 1, chars[4], getColor(1), s-1 );
    else
        {
        b.moveChar( 1, chars[2], getColor(1), s-1 );
        b.moveChar( pos, chars[3], getColor(3), 1 );
        }

    b.moveChar( s, chars[1], getColor(2), 1 );
    writeBuf( 0, 0, size.x, size.y, b );
}

TPalette& TScrollBar::getPalette() const
{
    static TPalette palette( cpScrollBar, sizeof( cpScrollBar )-1 );
    return palette;
}

int TScrollBar::getPos()
{
    int r = maxVal - minVal;
    if( r == 0 )
        return 1;
    else
        return  int(( ((long(value - minVal) * (getSize() - 3)) + (r >> 1)) / r) + 1);
}

int TScrollBar::getSize()
{
    int s;

    if( size.x == 1 )
        s = size.y;
    else
        s = size.x;

#ifndef __UNPATCHED
    return max( 2, s );
#else
    return max( 3, s );
#endif
}

static TPoint mouse;
static int p, s;
static TRect extent;

int TScrollBar::getPartCode()
{
    int part= - 1;
#ifndef __UNPATCHED
    if( extent.contains(mouse) )
	{
	int mark = (size.x == 1) ? mouse.y : mouse.x;

	// Check for vertical or horizontal size of 2
	if((size.x == 1 && size.y == 2) || (size.x == 2 && size.y == 1))
	{
		// Set 'part' to left or right arrow only
		if(mark < 1) part = sbLeftArrow;
		else if(mark == p) part = sbRightArrow;
	}
	else
	{
		if (mark == p) part= sbIndicator;
		else
		{
			if( mark < 1 ) part = sbLeftArrow;
			else if( mark < p ) part= sbPageLeft;
			else if( mark < s ) part= sbPageRight;
			else part= sbRightArrow;
			if( size.x == 1 ) part += 4;
		}
	}
	}
#else
    if( extent.contains(mouse) )
        {
        int mark = (size.x == 1) ? mouse.y : mouse.x;

        if (mark == p)
            part= sbIndicator;
        else
            {
            if( mark < 1 )
                part = sbLeftArrow;
            else if( mark < p )
                part= sbPageLeft;
            else if( mark < s )
                part= sbPageRight;
            else
                part= sbRightArrow;

            if( size.x == 1 )
                part += 4;
            }
        }
#endif
    return part;
}

void TScrollBar::handleEvent( TEvent& event )
{
    Boolean Tracking;
    int i = 0, clickPart;

    TView::handleEvent(event);
    switch( event.what )
        {
        case evMouseDown:
            message(owner, evBroadcast, cmScrollBarClicked,this); // Clicked()
            mouse = makeLocal( event.mouse.where );
            extent = getExtent();
            extent.grow(1, 1);
            p = getPos();
            s = getSize() - 1;
            clickPart= getPartCode();
            if( clickPart != sbIndicator )
                {
                do  {
                    mouse = makeLocal( event.mouse.where );
                    if( getPartCode() == clickPart )
                        setValue(value + scrollStep(clickPart) );
                    } while( mouseEvent(event, evMouseAuto) );
                }
            else
                {
                do  {
                    mouse = makeLocal( event.mouse.where );
                    Tracking = extent.contains(mouse);
                    if( Tracking )
                        {
                        if( size.x == 1 )
                            i = mouse.y;
                        else
                            i = mouse.x;
                        i = max( i, 1 );
                        i = min( i, s-1 );
                        }
                    else
                        i = getPos();
                    if(i != p )
                        {
                        drawPos(i);
#ifndef __UNPATCHED
			// nothing
#else
                        p = i;
#endif
                        }
                    } while( mouseEvent(event,evMouseMove) );
                if( Tracking && s > 2 )
                    {
                    s -= 2;
                    setValue( int(((long(p - 1) * (maxVal - minVal) + (s >> 1)) / s) + minVal));
                    }
                }
            clearEvent(event);
            break;
        case  evKeyDown:
            if( (state & sfVisible) != 0 )
                {
                clickPart = sbIndicator;
                if( size.y == 1 )
                    switch( ctrlToArrow(event.keyDown.keyCode) )
                        {
                        case kbLeft:
                            clickPart = sbLeftArrow;
                            break;
                        case kbRight:
                            clickPart = sbRightArrow;
                            break;
                        case kbCtrlLeft:
                            clickPart = sbPageLeft;
                            break;
                        case kbCtrlRight:
                            clickPart = sbPageRight;
                            break;
                        case kbHome:
                            i = minVal;
                            break;
                        case kbEnd:
                            i = maxVal;
                            break;
                        default:
                            return;
                        }
                else
                    switch( ctrlToArrow(event.keyDown.keyCode) )
                        {
                        case kbUp:
                            clickPart = sbUpArrow;
                            break;
                        case kbDown:
                            clickPart = sbDownArrow;
                            break;
                        case kbPgUp:
                            clickPart = sbPageUp;
                            break;
                        case kbPgDn:
                            clickPart = sbPageDown;
                            break;
                        case kbCtrlPgUp:
                            i = minVal;
                            break;
                        case kbCtrlPgDn:
                            i = maxVal;
                            break;
                        default:
                            return;
                        }
                message(owner,evBroadcast,cmScrollBarClicked,this); // Clicked
                if( clickPart != sbIndicator )
                    i = value + scrollStep(clickPart);
                setValue(i);
                clearEvent(event);
                }
        }
}

void TScrollBar::scrollDraw()
{
    message(owner, evBroadcast, cmScrollBarChanged,this);
}

int TScrollBar::scrollStep( int part )
{
    int  step;

    if( !(part & 2) )
        step = arStep;
    else
        step = pgStep;
    if( !(part & 1) )
        return -step;
    else
        return step;
}

void TScrollBar::setParams( int aValue,
                            int aMin,
                            int aMax,
                            int aPgStep,
                            int aArStep
                          )
{
 int  sValue;

    aMax = max( aMax, aMin );
    aValue = max( aMin, aValue );
    aValue = min( aMax, aValue );
    sValue = value;
    if( sValue != aValue || minVal != aMin || maxVal != aMax )
        {
        value = aValue;
        minVal = aMin;
        maxVal = aMax;
        drawView();
        if( sValue != aValue )
            scrollDraw();
        }
    pgStep = aPgStep;
    arStep = aArStep;
}

void TScrollBar::setRange( int aMin, int aMax )
{
    setParams( value, aMin, aMax, pgStep, arStep );
}

void TScrollBar::setStep( int aPgStep, int aArStep )
{
    setParams( value, minVal, maxVal, aPgStep, aArStep );
}

void TScrollBar::setValue( int aValue )
{
    setParams( aValue, minVal, maxVal, pgStep, arStep );
}

#if !defined(NO_STREAMABLE)

void TScrollBar::write( opstream& os )
{
    TView::write( os );
    os << value << minVal << maxVal << pgStep << arStep;
    os.writeBytes(chars, sizeof(chars));
}

void *TScrollBar::read( ipstream& is )
{
    TView::read( is );
    is >> value >> minVal >> maxVal >> pgStep >> arStep;
    is.readBytes(chars, sizeof(TScrollChars));
    return this;
}

TStreamable *TScrollBar::build()
{
    return new TScrollBar( streamableInit );
}

TScrollBar::TScrollBar( StreamableInit ) : TView( streamableInit )
{
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1