/* dvGrid.m
 * Grid additions for Cenon DocView class
 *
 * Copyright (C) 1997-2005 by vhf interservice GmbH
 * Author:   Georg Fleischmann
 *
 * created:  1997-11-05
 * modified: 2005-05-19 (line width set to 0.51)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the vhf Public License as
 * published by vhf interservice GmbH. Among other things, the
 * License requires that the copyright notices and this notice
 * be preserved on all copies.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the vhf Public License for more details.
 *
 * You should have received a copy of the vhf Public License along
 * with this program; see the file LICENSE. If not, write to vhf.
 *
 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
 * eMail: info@vhf.de
 * http://www.vhf.de
 */

#include <AppKit/AppKit.h>
#include "App.h"
#include "DocView.h"
#include "DocWindow.h"
#include "locations.h"
#include "functions.h"


@implementation DocView(Grid)

/* Methods to modify the grid of the GraphicView. */

- (void)drawGrid
{
    if ( [self gridIsEnabled] && numGridRects &&
         ((![self gridIsRelative] && gridSpacing >=4 ) ||
          ([self gridIsRelative] && gridSpacing*[self resolution]*scale>=4)) )
    {   int	i;

        [[NSColor lightGrayColor] set];
        [NSBezierPath setDefaultLineWidth:(VHFIsDrawingToScreen() ? 1.0/scale : 0.1)];
        for ( i=0; i<numGridRects; i++ )
            [NSBezierPath strokeRect:gridList[i]];
    }
}

- (void)setGridSpacing:(float)spacing
{
    if ( gridSpacing != spacing && spacing > 0 && spacing < 256 )
    {
        gridSpacing = spacing;
        gridIsEnabled = YES;
        [self resetGrid];
        [self drawAndDisplay];
        [[self window] flushWindow];
    }
}

- (float)gridSpacing
{
    return gridSpacing;
}

- (void)toggleGrid:sender
{
    if ([sender respondsToSelector:@selector(tag)])
        [self setGridEnabled:[sender tag] ? YES : NO];
}

- (void)setGridEnabled:(BOOL)flag
{
    if (gridIsEnabled != flag)
    {
        gridIsEnabled = flag;
        if (flag)
            [self resetGrid];
        [self drawAndDisplay];
        [[self window] flushWindow];
    }
}

- (BOOL)gridIsEnabled
{
    return gridIsEnabled;
}

- (void)setGridUnit:(int)value
{
    if (gridUnit != value)
    {
        gridUnit = value;
        [self resetGrid];
        [self drawAndDisplay];
        [[self window] flushWindow];
    }
}

- (int)gridUnit
{
    return gridUnit;
}

/* relative unit (mm, inch, pt)
 */
- (BOOL)gridIsRelative;
{
    return (gridUnit > 3) ? NO : YES;
}

//#define GRID (gridIsEnabled ? (gridSpacing ? gridSpacing : 1.0) : 1.0)
#define GRID (gridIsEnabled ? (([self gridIsRelative]) ? gridSpacing*[self resolution] \
                                                       : gridSpacing/scale) : 1.0)
/*#define grid(point) \
	{ (point).x = floor(((point).x / GRID) + 0.5) * GRID; \
	  (point).y = floor(((point).y / GRID) + 0.5) * GRID; }*/

/* grid spacing including scale
 */
- (float)grid
{
    return GRID;
}

/* return closest point on grid
 */
- (NSPoint)grid:(NSPoint)p
{
    if ( gridIsEnabled )
    {   float	grid = GRID;

        p = [self pointRelativeOrigin:p];
	p.x = floor((p.x / grid) + 0.5) * grid;
        p.y = floor((p.y / grid) + 0.5) * grid;
        p = [self pointAbsolute:p];
    }
    return p;
}

/*
 * converts a value from internal unit to the current unit
 */
- (float)resolution
{
    switch ( gridUnit )
    {
        case UNIT_MM:    return 72.0/25.4;
        case UNIT_INCH:  return 72.0;
        case UNIT_POINT: return 1.0;
    }
    return 1.0;
}

/* we maintain a list of rectangles representing the horicontal and vertical lines of the grid.
 * We use Rectangles because they usually draw faster than lines
 */
- (void)resetGrid
{   int		x, y, i, j;
    float	w, h, res = [self resolution], relGrid;
    NSZone	*zone = [self zone];
    NSRect	bounds = [self bounds];
    NSPoint	p, offset;	// start offset for grid
    BOOL	gridIsRelative = [self gridIsRelative];

    if ( (!gridIsRelative && gridSpacing<4) || (gridIsRelative && gridSpacing*res*scale<4) )
        return;

    relGrid = (gridIsRelative) ? gridSpacing*res : gridSpacing/scale;

    x = (int)bounds.size.width  / relGrid;
    y = (int)bounds.size.height / relGrid;
    numGridRects = x + y + 2;
    if (gridList)
	NSZoneFree(zone, gridList);
    gridList = NSZoneMalloc(zone, (numGridRects+4) * sizeof(NSRect));
    w = bounds.size.width;
    h = bounds.size.height;
    j = 0;

    p = [self pointAbsolute:NSZeroPoint];
    offset.x = p.x - floor(p.x / relGrid) * relGrid;
    offset.y = p.y - floor(p.y / relGrid) * relGrid;

    for (i = 0; i <= y; i++, j++)
    {
	gridList[j].origin.x = 0.0;
	gridList[j].origin.y = offset.y + i * relGrid;
        gridList[j].size.width = w;
        gridList[j].size.height = 0.0;
    }
    for (i = 0; i <= x; i++, j++)
    {
	gridList[j].origin.x = offset.x + i * relGrid;
	gridList[j].origin.y = 0.0;
        gridList[j].size.width = 0.0;
        gridList[j].size.height = h;
    }
}

@end
