// Copyright 1997-2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OHBasicCell.h"

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import "OHHTMLView.h"
#import "OHHTMLDocument.h"
#import "OHHTMLAnchor.h"
/* compiler needs to know that Anchor inherits from Link */

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniHTML/Cells.subproj/OHBasicCell.m,v 1.31 2000/01/20 00:27:47 kc Exp $")

@implementation OHBasicCell


+ (OHCellAlignment)cellAlignmentForAlignmentString:(NSString *)alignmentString;
{
    alignmentString = [alignmentString lowercaseString];

    if ([alignmentString containsString:@"left"])
        return OHCellAlignMarginalLeft;
    if ([alignmentString containsString:@"right"])
        return OHCellAlignMarginalRight;

    if ([alignmentString isEqualToString:@"texttop"])
        return OHCellAlignVerticalTextTop;
    if ([alignmentString isEqualToString:@"absmiddle"])
        return OHCellAlignVerticalAbsoluteMiddle;
    if ([alignmentString isEqualToString:@"absbottom"])
        return OHCellAlignVerticalAbsoluteBottom;
    
    switch ([alignmentString firstCharacter]) {
        case 'c':
        case 'C':
        case 'm':
        case 'M':
            return OHCellAlignVerticalMiddle;
        case 't':
        case 'T':
            return OHCellAlignVerticalTop;
        default:
        case 'b':
        case 'B':
            return OHCellAlignVerticalBaseline;
    }
}

+ (NSString *)stringForCellAlignment:(OHCellAlignment)anAlignment;
{
    switch (anAlignment) {
        case OHCellAlignVerticalTop:
            return @"top";
        case OHCellAlignVerticalMiddle:
            return @"middle";
        case OHCellAlignVerticalBaseline:
            return @"bottom";
        case OHCellAlignVerticalTextTop:
            return @"texttop";
        case OHCellAlignVerticalAbsoluteMiddle:
            return @"absmiddle";
        case OHCellAlignVerticalAbsoluteBottom:
            return @"absbottom";
        case OHCellAlignMarginalLeft:
            return @"left";
        case OHCellAlignMarginalRight:
            return @"right";
        case OHCellAlignNotMarginal:
            return @"notmarginal";
    }
    return nil;
}

// Init and dealloc

- init
{
    if (![super init])
        return nil;

    alignment = OHCellAlignVerticalBaseline;
    characterPosition = NSNotFound;

    return self;
}

- (void)dealloc;
{
    [font release];
    [super dealloc];
}


//

- (NSRect)cutFromLayoutBounds:(NSRect)bounds;
{
    float newX;

    OBASSERT([self isMarginalCell]);
    // Can't use NSIntersectsRect because NSHeight(bounds) could be 0, which forces NO always.
    if (NSMaxY(cellFrame) <= NSMinY(bounds))
        return bounds;

    switch ([self marginalAlignment]) {
        case OHCellAlignMarginalLeft:
            newX = MAX(NSMinX(bounds), NSMaxX(cellFrame) + insetSize.width);
            bounds.size.width -= newX - NSMinX(bounds);
            bounds.origin.x = newX;
            break;
        case OHCellAlignMarginalRight:
            newX = MIN(NSMaxX(bounds), NSMinX(cellFrame) - insetSize.width);
            bounds.size.width -= NSMaxX(bounds) - newX;
            break;
        default:
            break;
    }
    return bounds;
}

- (OHHTMLView *)containingView;
{
    return [nonretainedHTMLOwner htmlView];
}

    // call from any thread
- (void)relayoutCell;
{
    [[self containingView] relayoutCell:self];
}

    // call from any thread
- (void)redisplayCell;
{
    [[self containingView] redisplayCell:self];
}


// NSTextAttachment pretend methods

- (NSFileWrapper *)fileWrapper;
{
    NSData *epsData;
    NSFileWrapper *fileWrapper;

    epsData = [[nonretainedHTMLOwner htmlView] dataWithEPSInsideRect:[self cellFrame]];
    fileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:epsData];
    [fileWrapper setPreferredFilename:[NSStringFromClass(isa) stringByAppendingString:@".eps"]];
    return [fileWrapper autorelease];
}

- (id <NSTextAttachmentCell>)attachmentCell;
{
    return self;
}



// Sizing and placement

- (void)setLayoutBounds:(NSRect)bounds;
{
    cellFrame.size = [self totalSizeInLayoutBounds:bounds];

    switch (alignment) {
        case OHCellAlignMarginalLeft:
            [self setOrigin:NSMakePoint(NSMinX(bounds), NSMinY(bounds))];
            break;
        case OHCellAlignMarginalRight:
            [self setOrigin:NSMakePoint(NSMaxX(bounds) - NSWidth(cellFrame), NSMinY(bounds))];
            break;
        default:
            break;
    }
}

- (NSSize)contentSizeInLayoutBounds:(NSRect)layoutBounds;
{
    NSSize naturalSize, contentSize;

    naturalSize = [self naturalSize];
    contentSize = naturalSize;

    if (requestedSize.width > 0.0) {
        if (flags.requestedWidthIsPercentage)
            contentSize.width = requestedSize.width / 100.0 * NSWidth(layoutBounds);
        else
            contentSize.width = requestedSize.width;
        
        if (requestedSize.height == 0.0 && naturalSize.width > 0.0 && [self shouldScaleContentSizeBasedOnNaturalSize])
            contentSize.height = (contentSize.width / naturalSize.width) * naturalSize.height;
    }

    if (requestedSize.height > 0.0) {
        if (flags.requestedHeightIsPercentage)
            contentSize.height = requestedSize.height / 100.0 * NSHeight(layoutBounds);
        else
            contentSize.height = requestedSize.height;

        if (requestedSize.width == 0.0 && naturalSize.height > 0.0 && [self shouldScaleContentSizeBasedOnNaturalSize])
            contentSize.width = (contentSize.height / naturalSize.height) * naturalSize.width;
    }
    
    contentSize.width = ceil(contentSize.width);
    contentSize.height = ceil(contentSize.height);
    
    return contentSize;
}

- (NSSize)totalSizeInLayoutBounds:(NSRect)layoutBounds;
{
    switch (alignment) {
        default:
            return OHInsetSize([self contentSizeInLayoutBounds:layoutBounds], -insetSize.width, -insetSize.height);
        case OHCellAlignMarginalLeft:
        case OHCellAlignMarginalRight:
            return OHInsetSize([self contentSizeInLayoutBounds:layoutBounds], 0.0, -insetSize.height);
    }
}

- (void)setInsetSize:(NSSize)newInsetSize;
{
    insetSize = newInsetSize;
}

- (void)setInsetSizeWidthString:(NSString *)widthString heightString:(NSString *)heightString defaultWidth:(float)defaultWidth;
{
    NSSize newInsetSize = NSMakeSize(0.0, 0.0);
    
    newInsetSize.width = defaultWidth;
    if (widthString)
	newInsetSize.width = [widthString floatValue];
    if (heightString)
	newInsetSize.height = [heightString floatValue];

    [self setInsetSize:newInsetSize];
}

- (void)setRequestedSize:(NSSize)newRequestedSize requestedWidthIsPercentage:(BOOL)requestedWidthIsPercentage requestedHeightIsPercentage:(BOOL)requestedHeightIsPercentage;
{
    requestedSize = newRequestedSize;
    flags.requestedWidthIsPercentage = requestedWidthIsPercentage;
    flags.requestedHeightIsPercentage = requestedHeightIsPercentage;
}

- (void)setRequestedSizeWidthString:(NSString *)widthString heightString:(NSString *)heightString;
{
    NSSize newRequestedSize;

    newRequestedSize = requestedSize;
    if (widthString)
        newRequestedSize.width = [widthString intValue];
    if (heightString)
        newRequestedSize.height = [heightString intValue];

    [self setRequestedSize:newRequestedSize requestedWidthIsPercentage:(widthString ? [widthString hasSuffix:@"%"] : flags.requestedWidthIsPercentage) requestedHeightIsPercentage:(heightString ? [heightString hasSuffix:@"%"] : flags.requestedHeightIsPercentage)];
}

- (void)setBaselineOffset:(NSPoint)newBaselineOffset;
{
    baselineOffset = newBaselineOffset;
}

    // MAKE SURE TO CALL THIS INSTEAD OF -cellBaselineOffset, as the latter return 0,0 to fool the NSText system.
- (NSPoint)baselineOffset;
{
    return baselineOffset;
}

- (void)setOrigin:(NSPoint)newOrigin;
{
    cellFrame.origin = newOrigin;
}

- (NSSize)naturalSize; // For images, the original image size
{
    return NSMakeSize(0.0, 0.0);
}

- (BOOL)shouldScaleContentSizeBasedOnNaturalSize;
{
    return YES;
}

- (NSSize)insetSize
{
    return insetSize;
}

- (NSRect)contentFrame;
{
    switch (alignment) {
        default:
            return NSInsetRect(cellFrame, insetSize.width, insetSize.height);
        case OHCellAlignMarginalLeft:
        case OHCellAlignMarginalRight:
            return NSInsetRect(cellFrame, 0.0, insetSize.height);
    }

}

- (NSRect)cellFrame;
{
    return cellFrame;
}

//

- (void)setAlignment:(OHCellAlignment)newAlignment;
{
    alignment = newAlignment;
}

- (void)setAlignmentString:(NSString *)alignmentString;
{
    [self setAlignment:[isa cellAlignmentForAlignmentString:alignmentString]];
}

- (BOOL)isMarginalCell;
{
    return alignment == OHCellAlignMarginalLeft || alignment == OHCellAlignMarginalRight;
}

- (OHCellAlignment)verticalAlignment;
{
    if (![self isMarginalCell])
        return alignment;
    return OHCellAlignVerticalBaseline;
}

- (OHCellAlignment)marginalAlignment;
{
    if ([self isMarginalCell])
        return alignment;
    return OHCellAlignNotMarginal;
}

- (OHWidthRange)widthRange;
{
    NSSize minSize, maxSize;

    minSize = [self totalSizeInLayoutBounds:NSZeroRect];
    maxSize = [self totalSizeInLayoutBounds:NSMakeRect(0.0, 0.0, 1e6, 1.0)];
    return OHMakeWidthRange(minSize.width, maxSize.width);
}

//

- (void)drawRect:(NSRect)drawRect inHTMLView:(OHHTMLView *)htmlView;
{
}

//

- (void)setCharacterPosition:(unsigned int)position htmlOwner:(OHHTMLOwner *)anHTMLOwner;
{
    OBPRECONDITION(anHTMLOwner != nil);
    
    characterPosition = position;
    nonretainedHTMLOwner = anHTMLOwner;
}

- (unsigned int)characterPosition;
{
    return characterPosition;
}

- (OHHTMLOwner *)htmlOwner;
{
    return [[nonretainedHTMLOwner retain] autorelease];
}

- (void)nullifyHTMLOwner;
{
    nonretainedHTMLOwner = nil;
}


- (void)setFont:(NSFont *)newFont;
{
    if (font == newFont)
        return;

    [font release];
    font = [newFont retain];
}

- (NSFont *)font;
{
    return font;
}

- (BOOL)trackMouse:(NSEvent *)event inHTMLView:(OHHTMLView *)htmlView;
{
    return NO;
}

- (void)userClicked:(NSEvent *)event inHTMLView:(OHHTMLView *)htmlView;
{
}

- (OHHTMLAnchor *)link;
{
    return nil;
}

- (BOOL)wantsLinkCursor;
{
    return NO;
}

- (OHHTMLLink *)linkAtPoint:(NSPoint)point
{
    return [self link];
}

// NSTextAttachmentCell protocol

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
- (NSTextAttachment *)attachment;
{
    return nil;
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
// OHText uses -baselineOffset instead
- (NSPoint)cellBaselineOffset;
{
    return NSMakePoint(0.0, 0.0);
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
// OHText uses -cellFrame instead
- (NSSize)cellSize;
{
    return NSMakeSize(0.0, 0.0);
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
// OHText uses -drawRect:inHTMLView: instead
- (void)drawWithFrame:(NSRect)rect inView:(NSView *)view;
{
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
- (void)highlight:(BOOL)doHighlight withFrame:(NSRect)rect inView:(NSView *)view;
{
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
- (void)setAttachment:(NSTextAttachment *)anObject;
{
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with NS protocol
// OHText uses -trackMouse:inHTMLView: instead
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)flag;
{
    // Never gets called because -wantsToTrackMouse returns NO.
    return NO;
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with Rhapsody protocol
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView characterIndex:(unsigned)charIndex;
{
}

// NEVER GETS CALLED BY OHText.  Only here for compliance with Rhapsody protocol
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView atCharacterIndex:(unsigned)charIndex untilMouseUp:(BOOL)flag;
{
    return NO;
}

#if 0

// NEVER GETS CALLED BY OHText.  Only here for compliance with Rhapsody protocol
- (NSRect)cellFrameForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(NSRect)lineFrag glyphPosition:(NSPoint)position characterIndex:(unsigned)charIndex;
{
    // WARNING: Do not implement this method naively, it will confuse our text layout system.  In particular, you cannot return 'cellFrame' because our layout engine assumes that the AppKit's layout engine won't be moving things around underneath it.

    // Implementing this method as it is here is what caused the wacky bug where text would disappear (or, really, jump off the screen).

    return cellFrame;
}

#endif

// This gets called by OHText, and the NS protocol
- (BOOL)wantsToTrackMouse;
{
    return NO;
}

@end


@implementation OHBasicCell (Private)

- (NSMutableDictionary *)debugDictionary;
{
    NSMutableDictionary *debugDictionary;

    debugDictionary = [super debugDictionary];
    [debugDictionary setObject:[NSString stringWithFormat:@"%1.0f%@, %1.0f%@", requestedSize.width, flags.requestedWidthIsPercentage ? @"%" : @"", requestedSize.height, flags.requestedHeightIsPercentage ? @"%" : @""] forKey:@"requestedSize"];
    if (characterPosition != NSNotFound)
        [debugDictionary setObject:[NSString stringWithFormat:@"%d", characterPosition] forKey:@"characterPosition"];
    [debugDictionary setObject:[isa stringForCellAlignment:alignment] forKey:@"alignment"];
    [debugDictionary setObject:NSStringFromRect(cellFrame) forKey:@"cellFrame"];
    return debugDictionary;
}

@end
