//
// Time-stamp: <95/12/08 22:57:29 stephan>
//
// HRulerView.m
// Project: Rulers
//
// Stephan Wacker
// 93-02-11


#import "HRulerView.h"
#import "HRulerControl.h"
#import "RulerPreferencesControl.h"
#import "math.h"			// ceil()


@implementation HRulerView


- initFrame: (const NXRect *) frameRect
// Initialize yourself after instance has been created.
{
    NXSize	offset;
    
    [super initFrame: frameRect];
    
    // Move origin (from left bottom corner) to the right
    // by 20% of frame width (truncated to an integer).
    offset.width = floor( 0.2 * NX_WIDTH( frameRect ) );
    offset.height = 0.0;
    [self moveOrigin: &offset];

    // Initially, the ``valid'' cached image is a rectangle of zero width
    // at the very left edge of the ruler view.
    validRect.size.height = NX_HEIGHT( frameRect );
    imageOrigin.x = origin.x;
    
    return self;
}



- drawMarks: (const NXRect *) aRect
// Draw the marks in the cachedImage.
// aRect is given in Ruler coordinates.
// Background is drawn by super.
// Drawing is lockFocused in cachedImage.
{
    NXCoord		x;
    int			k;
    char		str[40];
    NXCoord		tick_baselength, tick_length;
    const TickMark	*mark;
    BOOL		displayLabels = [control labelsMode];
    int			marksPosition = [control hMarksMode];
    int			posNumDirection = [control hPosNumMode];
    NXFontMetrics	*metrics = [markFont metrics];	// font dimensions
    float		capHeight = metrics->capHeight * [markFont pointSize];
    
    NXSetColor( [control marksColor] );
    PSsetlinewidth( 0.25 );			// thin lines
    tick_baselength = 18.0;			// main mark length
    [markFont set];


    // Process every set of marks.
    //
    for( mark = [self tickMarkSystem]; mark->distance > 0.0; mark++ )
    {
	tick_length = mark->length * tick_baselength;
	
	// start with
	//  x = k * mark->distance + imageOrigin.x,
	//  x < NX_X(&bounds)
	for(
	  k = (int) ceil( (NX_X(aRect)-imageOrigin.x)/mark->distance ) - 1,
	   x = k * mark->distance + imageOrigin.x + 0.25,
	   k *= mark->value;
	  x < NX_MAXX(aRect) + mark->distance;
	  k += mark->value, x += mark->distance )
	{
	    if( marksPosition & HMARKS_BOTTOM ) {
		PSmoveto( x, NX_Y(&bounds) );
		PSrlineto( 0.0, tick_length );	// a tick mark
	    }
	    if( marksPosition & HMARKS_TOP ) {
		PSmoveto( x, NX_MAXY(&bounds) );
		PSrlineto( 0.0, -tick_length );	// a tick mark
	    }
    
	    if( displayLabels ) {
		if( mark->value > 0 )
		{
		    sprintf( str, "%d",
		      k < 0 ? ((posNumDirection & POSNUM_LEFT ) ? -k : k)
		            : ((posNumDirection & POSNUM_RIGHT) ? k : -k)
		    );
		    PSmoveto( x - 0.5 * ([markFont getWidthOf: str]-1.0),
		      0.5 * (NX_HEIGHT(&bounds) - (capHeight + 0.0)) );
		    PSshow( str );
		}
		else if( mark->value < 0 )
		{
		    // display a fraction like `3/8'
		    // !toDo!
		}
	    }
	} // for k, x
    } // for mark
    
    PSstroke();					// draw all tick marks
    
    return self;
}



- (NXCoord) constrainDistance: (NXCoord) distance
                        frame: (NXRect *) frameRect
// PRIVATE
// Change the frameRect's width by distance.
// Observe minimum and maximum sizes.
// Answer actual width change.
{
    NXSize	minSize, maxSize;
    NXCoord	minDist, maxDist;
    NXCoord	result = distance + off;
    
    [window getMinSize: &minSize];
    [window getMaxSize: &maxSize];
    
    maxDist = maxSize.width - NX_WIDTH( frameRect );
    minDist = minSize.width - NX_WIDTH( frameRect );
    off = 0;
    
    if( result < minDist )
    {
	off = result - minDist;
	result = minDist;
    }
    if( result > maxDist )
    {
	off = result - maxDist;
	result = maxDist;
    }
    
    if( result * distance < 0.0 )	// cancel opposite direction
    {
	off += result;
	result = 0.0;
    }
    
    NX_WIDTH( frameRect ) += result;
    
    return result;
}



- sizeRight: (const NXSize *) delta
// Change the window's width by delta (to the right).
// Positive distance increases width.
{
    NXCoord	distance = delta->width;
    
    if( !(distance = [self constrainDistance: distance frame: &rubberRect]) )
	return nil;				// size is not changed
    
    [self drawRubberband];
    
    return self;
}



- sizeLeft: (const NXSize *) delta
// Change the window's width by delta (to the left).
// Positive distance decreases width.
{
    NXCoord	distance = -delta->width;
    
    if( !(distance = [self constrainDistance: distance frame: &rubberRect]) )
	return nil;				// size is not changed
    
    NX_X( &rubberRect ) -= distance;		// move left edge
    origin.x += distance;			// adjust local origin
    
    [self drawRubberband];
    
    return self;
}


@end
