/* Implemenation of ScrollView class
 *
 * Copyright (C)  1993, 1994, 1995  The Board of Trustees of  
 * The Leland Stanford Junior University.  All Rights Reserved.
 *
 * Authors: Paul Kunz, Imran Qureshi, and Libing Wang
 *
 * This file is part of an Objective-C class library for a window system
 *
 * ScrollView.m,v 1.40 1995/12/13 22:33:15 fedor Exp
 */


#include "ScrollView.h"

#include "Box.h"
#include "ClipView.h"
#include "Scroller.h"
#include <objc/List.h>

extern char *ScrollViewInstanceName(void);

@interface View(WidgetSet)
- _init;
- _setAsBulletinBoard;
- _managedBy:parent wid:(void*)widget;
@end

@interface ScrollView(WidgetSet)
- _setBorder;
- _addCallback;
@end

@implementation ScrollView:View


/* Private methods not declared in interface file */

- _init
{
    [super _init];
    [self _setAsBulletinBoard];
    instancename = ScrollViewInstanceName();
    [self setLineScroll:10.0];
    return self;
}

/* Factory methods */

+ getFrameSize:(NXSize *)fSize forContentSize:(const NXSize *)cSize 
   				horizScroller:(BOOL)hFlag
 				 vertScroller:(BOOL)vFlag
      				   borderType:(int)aType
{
    if ( vFlag ) {
	fSize->width = cSize->width + NX_SCROLLERWIDTH + CLIPVIEW_MARGIN;
    } else {
	fSize->width = cSize->width;
    }
    if ( hFlag ) {
	fSize->height = cSize->height + NX_SCROLLERWIDTH + CLIPVIEW_MARGIN;
    } else {
	fSize->height = cSize->height;
    }
    if ( aType == NX_BEZEL ) {
        fSize->width  += (2*BEZEL_THICKNESS);
        fSize->height += (2*BEZEL_THICKNESS);
    }
    return self;
}


+ getContentSize:(NXSize *)cSize forFrameSize:(const NXSize *)fSize
                                horizScroller:(BOOL)hFlag 
                                 vertScroller:(BOOL)vFlag 
                                   borderType:(int)aType
{
    if ( vFlag ) {
	cSize->width = fSize->width - NX_SCROLLERWIDTH - CLIPVIEW_MARGIN;
    } else {
	cSize->width = fSize->width;
    }
    if ( hFlag ) {
	cSize->height = fSize->height - NX_SCROLLERWIDTH - CLIPVIEW_MARGIN;
    } else {
	cSize->height = fSize->height;
    }
    if ( aType == NX_BEZEL ) {
        cSize->width  -= (2*BEZEL_THICKNESS);
        cSize->height -= (2*BEZEL_THICKNESS);
    }
    return self;
}

/* Public methods */

- initFrame:(const NXRect *)frameRect
{
    [super initFrame:frameRect];
    if ( !contentView ) {
	contentView = [[ClipView alloc] initFrame:frameRect];
	[self addSubview:contentView];
    }
    if ( lineAmount == 0 ) {
	[self setLineScroll:10.0];
    }
    return self;
}

- getDocVisibleRect:(NXRect *)aRect
{
  [self notImplemented:_cmd];
  return 0;
}

- getContentSize:(NXSize *)contentViewSize
{
  [self notImplemented:_cmd];
  return 0;
}

- resizeSubviews:(const NXSize *)oldSize
{
  [self notImplemented:_cmd];
  return 0;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
  return self;
}

- setDocView:aView
{
    return [contentView setDocView:aView];
}

- docView
{
    return [contentView docView];
}

- setDocCursor:anObj
{
    return [contentView setDocCursor:anObj];
}

- (int)borderType
{
    return borderType;
}

- setBorderType:(int)aType
{
    borderType = aType;
    [self _setBorder];
    [self tile];
    return self;
}

- setBackgroundGray:(float)value
{
    return self;
}

- (float)backgroundGray
{
  [self notImplemented:_cmd];
  return 0;
}

- setBackgroundColor:(NXColor)color
{
  [self notImplemented:_cmd];
  return 0;
}

- (NXColor)backgroundColor
{
  NXColor color;
  [self notImplemented:_cmd];
  return color;
}

- setVertScrollerRequired:(BOOL)flag
{
    vScrollerRequired = flag;
    if ( !flag && vScroller ) {
	[vScroller free];
	vScroller = nil;
    }
    [self tile];
    return self;
}

- setHorizScrollerRequired:(BOOL)flag
{
    hScrollerRequired = flag;
    if ( !flag && hScroller ) {
	[hScroller free];
	hScroller = nil;
    }
    [self tile];	
    return self;
}

- vertScroller
{
    return vScroller;
}

- horizScroller
{
    return hScroller;
}

- setVertScroller:anObject
{
  [self notImplemented:_cmd];
  return 0;
}

- setHorizScroller:anObject
{
  [self notImplemented:_cmd];
  return 0;
}

- setLineScroll:(float)value
{
    lineAmount = value;
    return self;
}

- setPageScroll:(float)value
{
    pageContext = value;
    return self;
}

- setCopyOnScroll:(BOOL)flag
{
  [self notImplemented:_cmd];
  return 0;
}

- setDisplayOnScroll:(BOOL)flag
{
  [self notImplemented:_cmd];
  return 0;
}

- setDynamicScrolling:(BOOL)flag
{
    return self;
    
}

- setFrame:(const NXRect *)frameRect
{
  /* not implmented in OpenStep, but used to re-tile */
    [super setFrame:frameRect];
    [self tile];
    return self;
}

- tile
{
    NXRect  rect;

    if ( vScrollerRequired ) {
	rect.origin.x = 0;
	rect.origin.y = 0;
	rect.size.width = NX_SCROLLERWIDTH;
	rect.size.height = frame.size.height;
	if ( vScroller ) {
	    [vScroller setFrame:&rect];
	} else {
	    vScroller = [[Scroller alloc] initFrame:&rect];
	    [self addSubview:vScroller];
	    [vScroller setTarget:self];
	    [vScroller setAction:@selector(_scrollViaScroller:)];
	}
    }

    if ( hScrollerRequired ) {
	if ( vScrollerRequired ) {
	    rect.origin.x = NX_SCROLLERWIDTH;
	    rect.size.width = frame.size.width - NX_SCROLLERWIDTH;
	} else {
	    rect.origin.x = 0;
	    rect.size.width = frame.size.width;
	}
	rect.origin.y = 0;
	rect.size.height = NX_SCROLLERWIDTH;
	if ( hScroller ) {
	    [hScroller setFrame:&rect];
	} else {
	    hScroller = [[Scroller alloc] initFrame:&rect];
	    [self addSubview:hScroller];
	    [hScroller setTarget:self];
	    [hScroller setAction:@selector(_scrollViaScroller:)];
	}
    }

    rect.origin.x = 0;
    rect.origin.y = 0;
    if ( vScrollerRequired ) {
        rect.origin.x += NX_SCROLLERWIDTH + CLIPVIEW_MARGIN;
    }
    if ( hScrollerRequired ) {
	rect.origin.y += NX_SCROLLERWIDTH + CLIPVIEW_MARGIN;
    }
    if ( borderType == NX_BEZEL ) {
        rect.origin.x += BEZEL_THICKNESS;
	rect.origin.y += BEZEL_THICKNESS;
    }
    [ScrollView getContentSize:&rect.size forFrameSize:&frame.size
	         horizScroller:hScrollerRequired
                  vertScroller:vScrollerRequired
	            borderType:borderType];
    [contentView setFrame:&rect];
    [self reflectScroll:contentView];
    return self;
}

- reflectScroll:cView
{
    NXRect 	docrect;
    NXRect 	viewrect;
    float	pos, percent, d;
    
    [contentView getDocRect:&docrect];
    [contentView getDocVisibleRect:&viewrect];
    if ( hScrollerRequired ) {
	d = docrect.size.width;
	percent  = viewrect.size.width / d;
	d -= viewrect.size.width;
	if ( d > 0 ) {
	    pos = - docrect.origin.x / d;
	} else {
	    pos = 0;
	}
	[hScroller setFloatValue:pos :percent];
    }
    if ( vScrollerRequired ) {
	d = docrect.size.height;
	percent  = viewrect.size.height / d;
	d -= viewrect.size.height;
	if ( d > 0 ) {
	    pos = - docrect.origin.y / d;
	} else {
	    pos = 0;
	}
	[vScroller setFloatValue:pos :percent];
    }
    return self;
}

- write:(NXTypedStream *)stream
{
  return self;
}

- read:(NXTypedStream *)stream
{
    View	*view;
    NXRect	rect;
    int		i;
	
    [super read:stream];
    objc_read_types(stream, "icc", &borderType, 
                                   &vScrollerRequired, &hScrollerRequired);
    i = [subviews count];
    while ( i-- ) {
	view = [subviews objectAt:i];
	if ( [view isKindOf:[ClipView class]] ) {
	    contentView = (ClipView *)view;
	}
	if ( [view isKindOf:[Scroller class]] ) {
	    [view getFrame:&rect];
	    if ( rect.size.width > rect.size.height ) {
	        hScroller = (Scroller *)view;
	    } else {
	        vScroller = (Scroller *)view;
	    }
	}
    }
    return self;
}

- awake
{
    [super awake];
    if ( vScrollerRequired ) {
	[vScroller setTarget:self];
	[vScroller setAction:@selector(_scrollViaScroller:)];
    } else {
	[vScroller removeFromSuperview];
	[subviews removeObject:vScroller];
    } 
    if ( hScrollerRequired ) {
	[hScroller setTarget:self];
	[hScroller setAction:@selector(_scrollViaScroller:)];
    } else {
    	[hScroller removeFromSuperview];
	[subviews removeObject:hScroller];
    }
    [self setBorderType:borderType];
    [self tile];
    return self;
}

- _scrollViaScroller:sender
{
    float	h, v;
    int		hit;
    BOOL	flag;
    
    flag = (sender == vScroller);
    hit = [sender hitPart];
    switch (hit) {
    case NX_KNOB :
        if ( vScrollerRequired ) {
	    v = [vScroller floatValue];
	} else {
	    v = 0;
	}
	if ( hScrollerRequired ) {
	    h = [hScroller floatValue];
	} else {
	    h = 0;
	}
	[contentView _setScrollFloatValue:h :v];
        break;
    case NX_INCLINE :
	[contentView _scrollLine:lineAmount vertical:flag];
	break;
    case NX_INCPAGE :
        [contentView _scrollPage:pageContext vertical:flag up:NO];
        break;
    case NX_DECLINE :
        [contentView _scrollLine:-lineAmount vertical:flag];
	break;
    case NX_DECPAGE :
        [contentView _scrollPage:-pageContext vertical:flag up:YES];
        break;
    case NX_NOPART :
    default:
    	break;
    }
    return self;
}

- _managedBy:parent wid:(void*)widget
{
    [self _setBorder];
    [super _managedBy:parent wid:widget];
    [contentView _setScrollFloatValue:0.0 :0.0];
    return self;
}

+ newFrame:(const NXRect *)frameRect
{
    return [[self alloc] initFrame:frameRect];
}

+ new
{
    return [[self alloc] init];
}


@end
