// Suppress compiler warning about rcsid being unused, yet prevent assembler
// code for this function from being produced.
inline extern const char *suppressCompilerWarning(void)
{
    static const char *rcsid = "$Id$ cc: "__FILE__" "__DATE__" "__TIME__;

    return rcsid;
}

//
// ------------------- MiscTabCell Class Implementation -----------------------
//
// NSMatrix cell designed to draw itself as a horizontal file folder tab across
// the top of a MiscSwitchView with a "file folder" border.
//
// Written by Art Isbell (derived from UITabActionCell by Bill Edney,
//     Sean Hill, Mark Onyschuk).
// Copyright 1996 by Art Isbell.
// Version 1.0.  All rights reserved.
//
// This notice may not be removed from this source code.
//
// This object is included in the MiscKit by permission from the author
// and its use is governed by the MiscKit license, found in the file
// "License.rtf" in the MiscKit distribution.  Please refer to that file
// for a list of all applicable permissions and restrictions.
//	
// ----------------------------------------------------------------------------
//

// ----------------------------- Header Files ---------------------------------

#import <AppKit/AppKit.h>

#ifndef NOMISC
#import <misckit/MiscTabCell.h>
#import <misckit/drawTab.h>

#else NOMISC

#import "MiscTabCell.h"
#import "drawTab.h"    // pswrap that draws tab ends.
#endif NOMISC

// ---------------- Typedef, Struct, and Union Declarations -------------------

// --------------------- Constant and Enum Definitions ------------------------

// Minimum constant tab end width that results in a nicely-curved tab end.
static const int MiscTabEndWidth = 12;

// Left border gray on color systems (empirically determined)
static const float MiscLeftBorderGray = 21.0 / 25.0;
static NSString *MiscDefaultTabLabel = @"Label";

// ------------------------- Function Declarations ----------------------------

@interface MiscTabCell(Private)
// ---------------------- Private Method Declarations -------------------------

- (void)_finishInitializing;

@end

@implementation MiscTabCell
// ---------------------- Factory Method Definitions --------------------------

// ---------------- Overridden Instance Method Definitions --------------------

- (id)init
{
    return [self initTextCell:MiscDefaultTabLabel];
}

- (id)initTextCell:(NSString *)aString
{
    self = [super initTextCell:aString];

    if (self != nil)
    {
        // Finish the initialization process.
        [self _finishInitializing];
    }
    return self;
}

// Initializes a new instance after being unarchived.
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
    // Finish the initialization process.
    [self _finishInitializing];
    return self;
}

- (BOOL)isOpaque
{
    return YES;
}

// Draws the receiver inside cellFrame in aControlView.  The tab is composed of
// ends drawn by a pswrap and shaped like right triangles with a Bezier curve
// hypotenuse plus a rectangular text cell between the ends.  The tab ends of
// interior tabs extend beyond the ends of the cellFrame to create overlapping
// tabs if the intercell distance is small.  Because the leftmost and rightmost
// tabs cannot draw their left and right ends, respectively, in adjacent cells,
// they are narrower by half the width of an end than interior tabs.
- (void)drawWithFrame:(NSRect)aCellFrame inView:(NSView *)aControlView
{
    BOOL isResizingL = (BOOL)(aControlView == nil ||
                              ![aControlView isKindOfClass:[NSMatrix class]]);
    BOOL isSelectedL = (BOOL)(!isResizingL &&
                              ([(NSMatrix *)aControlView selectedCell] ==
                               self));

    // If self is selected, draw all other cells starting with rightmost, then
    // draw self.
    if (isSelectedL)
    {
        [self redrawCellsInView:(NSMatrix *)aControlView];
    }
    // If self _canDraw or self is resizing in IB, draw self.
    if (_canDraw || isResizingL)
    {
        [self eraseWithFrame:aCellFrame inView:aControlView
                  isSelected:isSelectedL];
        [self drawTabEndsWithFrame:aCellFrame inView:aControlView
                        isSelected:isSelectedL];
        [self drawInteriorWithFrame:aCellFrame inView:aControlView
                         isSelected:(isResizingL || isSelectedL)];
        [self drawUpperBezelWithFrame:aCellFrame inView:aControlView
                           isSelected:isSelectedL];

        // If aControlView is resizing or self isn't selected, draw lower
        // bezel.
        if (!isSelectedL)
        {
            [self drawLowerBezelWithFrame:aCellFrame inView:aControlView
                               isResizing:isResizingL];
        }
        // Prevent redraws.
        _canDraw = NO;
        [aControlView setNeedsDisplay:YES];
    }
}

- (void)drawInteriorWithFrame:(NSRect)aCellFrame inView:(NSView *)aControlView
{
    // isSelected will be true if aControlView is being resized in IB or if
    // self is selected.
    [self drawInteriorWithFrame:aCellFrame inView:aControlView
                     isSelected:(BOOL)(aControlView == nil ||
                                       ![aControlView isKindOfClass:[NSMatrix
                                           class]] ||
                                       [(NSMatrix *)aControlView selectedCell]
                                       == self)];
}

- (void)highlight:(BOOL)aFlag withFrame:(NSRect)aCellFrame
           inView:(NSView *)aView
{
    // Always passing a NO flag to super redraws the key view indication over
    // the cell's top border upon mouse up, so invoke _cmd with super only if
    // aFlag is YES which happens upon mouse down.  Sending
    // highlight:withFrame:inView: with a YES flag from
    // trackMouse:inRect:ofView:untilMouseUp: seems necessary to prevent
    // highlighting. 
    if (aFlag)
    {
        [super highlight:NO withFrame:[self drawingRectForBounds:aCellFrame
                                                          inView:aView]
                  inView:aView];
    }
}

- (NSSize)cellSizeForBounds:(NSRect)aRect
{
    NSSize superSizeL = [super cellSizeForBounds:aRect];

    // Add 1.5 tabEndWidths to width that super returns so that first and last
    // cells will be wide enough to display the text.
    return NSMakeSize((float)((((3 * MiscTabEndWidth) + 1) / 2) +
                              (unsigned)superSizeL.width), superSizeL.height);
}

// Limit the drawing rectangle to the text rectangle.
- (NSRect)drawingRectForBounds:(NSRect)aRect
{
    return [self drawingRectForBounds:(NSRect)aRect inView:[self controlView]];
}

- (NSRect)titleRectForBounds:(NSRect)aRect
{
    return [self drawingRectForBounds:aRect];
}

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)aCellFrame
            ofView:(NSView *)aControlView untilMouseUp:(BOOL)aFlag
{
    [self highlight:YES withFrame:aCellFrame inView:aControlView];
    return [super trackMouse:theEvent inRect:aCellFrame
                      ofView:aControlView untilMouseUp:aFlag];
}

// -------------------- New Instance Method Definitions -----------------------

// Sets the _canDraw ivar that's used to minimize cell drawing.
- (void)setCanDraw:(BOOL)aFlag
{
    _canDraw = aFlag;
}

- (NSColor *)cellGrayWithIsSelected:(BOOL)aFlag
{
    if (aFlag)
    {
        return [NSColor lightGrayColor];
    }
    else
    {
        // If the window default depth limit is 2-bit grayscale or less, return
        // dark gray.
        if ([NSWindow defaultDepthLimit] <=
            NSBestDepth(NSCalibratedWhiteColorSpace, 2, 2, YES, NULL))
        {
            return [NSColor darkGrayColor];
        }
        // The window default depth limit is deeper than 2-bit grayscale, so
        // return gray.
        else
        {
            return [NSColor grayColor];
        }
    }
}

- (NSColor *)textGrayWithIsSelected:(BOOL)aFlag
{
    // If self is enabled, use black text.
    if ([self isEnabled])
    {
        return [NSColor blackColor];
    }
    // Self is disabled, so use grayscale text appropriate to color depth.
    else
    {
        // If this is a 2-bit grayscale system, set appropriate grays.
        if ([NSWindow defaultDepthLimit] <=
            NSBestDepth(NSCalibratedWhiteColorSpace, 2, 2, YES, NULL))
        {
            return [NSColor lightGrayColor];
        }
        // This is not a 2-bit grayscale system, so set appropriate grays.
        else
        {
            return [NSColor darkGrayColor];
        }
    }
}

- (NSColor *)leftBorderGrayWithIsSelected:(BOOL)aFlag
{
    if (aFlag)
    {
        return [NSColor whiteColor];
    }
    else
    {
        return [NSColor colorWithCalibratedWhite:MiscLeftBorderGray alpha:1.0];
    }
}

- (NSColor *)rightBorderGrayWithIsSelected:(BOOL)aFlag
{
    return [NSColor blackColor];
}

- (NSColor *)upperBezelGrayWithIsSelected:(BOOL)aFlag
{
    return [self leftBorderGrayWithIsSelected:aFlag];
}

- (NSColor *)lowerBezelGrayWithIsSelected:(BOOL)aFlag
{
    return [NSColor whiteColor];
}

- (void)redrawCellsInView:(NSMatrix *)aMatrix
{
    NSEnumerator *cellsL = [[aMatrix cells] reverseObjectEnumerator];

    // Draw each cell starting with the rightmost.
    while (YES)
    {
        MiscTabCell *cellL = [cellsL nextObject];

        // Loop termination test
        if (cellL == nil) break;

        // If cell isn't self, set its _canDraw flag and draw it.
        if (cellL != self)
        {
            [cellL setCanDraw:YES];
            [aMatrix drawCell:cellL];
        }
    }
    // Set self's _canDraw flag so it will draw last.
    _canDraw = YES;
}

// Limit the drawing rectangle to the text rectangle whose size is determined
// by the cell's position in aControlView, if it exists.
- (NSRect)drawingRectForBounds:(NSRect)aRect inView:(NSView *)aControlView
{
    NSRect drawingRectL;
    float halfTabEndWidthL = (float)((MiscTabEndWidth + 1) / 2);
    NSSize textCellSizeL = [self cellSizeForBounds:aRect];

    drawingRectL.origin.y = NSMinY(aRect) +
        (float)((unsigned)(NSHeight(aRect) - textCellSizeL.height) / 2);
    drawingRectL.size.height = textCellSizeL.height;

    // If aControlView isn't being resized in IB, return the correct drawing
    // rectangle.
    if ((aControlView != nil) && [aControlView isKindOfClass:[NSMatrix class]])
    {
        NSArray *controlCellsL = [(NSMatrix *)aControlView cells];
        BOOL isFirstCellL = (BOOL)([controlCellsL objectAtIndex:0] == self);

        // If self is first cell, set drawingRect origin tabEndWidth to the
        // right of the cell frame origin.
        if (isFirstCellL)
        {
            drawingRectL.origin.x = NSMinX(aRect) + (float)MiscTabEndWidth;
        }
        // self isn't first cell, so set drawingRect origin halfTabEndWidth to
        // the right of the cell frame origin.
        else
        {
            drawingRectL.origin.x = NSMinX(aRect) + halfTabEndWidthL;
        }
        // If self is first or last cell, set drawingRect width to 1.5
        // tabEndWidth less than that of cell to accommodate tab ends.
        if (isFirstCellL || ([controlCellsL lastObject] == self))
        {
            drawingRectL.size.width = NSWidth(aRect) - (float)MiscTabEndWidth -
                halfTabEndWidthL;
        }
        // self is interior cell, so set drawingRect width to tabEndWidth less
        // than that of cell to accommodate tab ends.
        else
        {
            drawingRectL.size.width = NSWidth(aRect) - (float)MiscTabEndWidth;
        }
    }
    // aControlView is being resized in IB, so return the drawing rectangle of
    // an interior cell.
    else
    {
        drawingRectL.origin.x = NSMinX(aRect) + halfTabEndWidthL;
        drawingRectL.size.width = NSWidth(aRect) - (float)MiscTabEndWidth;
    }
    return drawingRectL;
}

- (void)eraseWithFrame:(NSRect)aCellFrame inView:(NSView *)aControlView
   isSelected:(BOOL)aFlag
{
    NSRect textFrameL = [self drawingRectForBounds:aCellFrame
                                            inView:aControlView];

    // Erase self.
    [[self cellGrayWithIsSelected:aFlag] set];
    NSRectFill(NSMakeRect(NSMinX(textFrameL), NSMinY(aCellFrame),
                          NSWidth(textFrameL), NSHeight(aCellFrame)));
}

- (void)drawTabEndsWithFrame:(NSRect)aCellFrame inView:(NSView *)aControlView
         isSelected:(BOOL)aFlag
{
    float cellGrayL = [[self cellGrayWithIsSelected:aFlag] whiteComponent];
    NSRect textFrameL = [self drawingRectForBounds:aCellFrame
                                           inView:aControlView];

    // Draw tab ends.
    drawLeftTabEndX_y_w_h_borderGray_cellGray((int)NSMinX(textFrameL),
                                              (int)NSMaxY(aCellFrame),
                                              MiscTabEndWidth,
                                              (int)NSHeight(aCellFrame),
                                              [[self
                                               leftBorderGrayWithIsSelected:
                                                  aFlag] whiteComponent],
                                              cellGrayL);
    drawRightTabEndX_y_w_h_borderGray_cellGray((int)NSMinX(textFrameL) +
                                               (int)NSWidth(textFrameL),
                                               (int)NSMaxY(aCellFrame),
                                               MiscTabEndWidth,
                                               (int)NSHeight(aCellFrame),
                                               [[self
                                                rightBorderGrayWithIsSelected:
                                                   aFlag] whiteComponent],
                                               cellGrayL);
}

- (void)drawInteriorWithFrame:(NSRect)aCellFrame inView:(NSView *)aControlView
         isSelected:(BOOL)aFlag
{
    NSRect textFrameL = [self drawingRectForBounds:aCellFrame
                                            inView:aControlView];

    // If aControlView is being resized in IB or self is selected, let super
    // draw cell interior.
    if (aFlag)
    {
        [super drawInteriorWithFrame:textFrameL inView:aControlView];
    }
    // aControlView is being resized in IB or self isn't selected, so draw cell
    // interior because NSCell doesn't support setting text or cell background
    // colors.
    else
    {
        float ascenderL;
        float descenderL;
        float lineHeightL;
        NSFont *cellFontL;
        NSFont *screenFontL;
        NSString *stringValueL;

        // Draw the text by first filling the cell with cellGray.
        [[self cellGrayWithIsSelected:NO] set];
        NSRectFill(textFrameL);
        [[self textGrayWithIsSelected:NO] set];

        // Set the font.
        cellFontL = [self font];
        screenFontL = [cellFontL screenFont];

        if ([[NSDPSContext currentContext] isDrawingToScreen] &&
            (screenFontL != nil))
        {
            [screenFontL set];
        }
        else
        {
            [cellFontL set];
        }
        stringValueL = [self stringValue];

        // Display text centered horizontally in the text cell and vertically,
        // with the bottom of the font descender at the text cell edge.
        NSTextFontInfo(cellFontL, &ascenderL, &descenderL, &lineHeightL);
        PSmoveto(NSMinX(textFrameL) + (float)(((unsigned)NSWidth(textFrameL) -
                                               (unsigned)[cellFontL
                                                  widthOfString:stringValueL] -
                                               1) / 2),
                 NSMaxY(textFrameL) - descenderL);
        PSshow([stringValueL cString]);
    }
}

- (void)drawUpperBezelWithFrame:(NSRect)aCellFrame
                         inView:(NSView *)aControlView
                     isSelected:(BOOL)aFlag
{
    NSRect textFrameL = [self drawingRectForBounds:aCellFrame
                                            inView:aControlView];

    // Draw the upper bezel by using the drawing color that reflects the
    // selected state.
    [[self upperBezelGrayWithIsSelected:aFlag] set];
    NSRectFill(NSMakeRect(NSMinX(textFrameL), NSMinY(aCellFrame),
                          NSWidth(textFrameL), 1.0));
}

- (void)drawLowerBezelWithFrame:(NSRect)aCellFrame
                         inView:(NSView *)aControlView
                     isResizing:(BOOL)aFlag
{
    NSRect lowerBezelFrameL;

    // Set up the lowerBezelFrame that will be used for drawing the bottom
    // bezel.
    lowerBezelFrameL.origin.y = NSMaxY(aCellFrame) - 1.0;
    lowerBezelFrameL.size.height = 1.0;

    if (!aFlag)
    {
        float halfTabEndWidthL = (float)((MiscTabEndWidth + 1) / 2);
        NSSize intercellL = [(NSMatrix *)aControlView intercellSpacing];
        NSArray *controlCellsL = [(NSMatrix *)aControlView cells];

        // If self is the first cell, start the lower bezel at self's right
        // edge and set lowerBezelFrame's width to that of the self plus
        // halfTabEndWidth.
        if ([controlCellsL objectAtIndex:0] == self)
        {
            lowerBezelFrameL.origin.x = NSMinX(aCellFrame);
            lowerBezelFrameL.size.width = NSWidth(aCellFrame) +
                halfTabEndWidthL;
        }
        // self is the last cell so start the lower bezel at the right
        // edge of the cell to the left plus halfTabEndWidth and set
        // lowerBezelFrame's width to that of self plus the intercell less
        // halfTabEndWidth.
        else if ([controlCellsL lastObject] == self)
        {
            lowerBezelFrameL.origin.x = NSMinX(aCellFrame) -
                (intercellL.width - halfTabEndWidthL);
            lowerBezelFrameL.size.width = NSWidth(aCellFrame) +
                (intercellL.width - halfTabEndWidthL);
        }
        // self is an interior cell so start the lower bezel at the
        // right edge of the cell to the left plus halfTabEndWidth and set
        // lowerBezelFrame's width to that of self plus the intercell.
        else
        {
            lowerBezelFrameL.origin.x = NSMinX(aCellFrame) -
                (intercellL.width - halfTabEndWidthL);
            lowerBezelFrameL.size.width = NSWidth(aCellFrame) +
                intercellL.width;
        }
    }
    else
    {
        lowerBezelFrameL.origin.x = NSMinX(aCellFrame);
        lowerBezelFrameL.size.width = NSWidth(aCellFrame);
    }
    // Fill in lowerBezelFrame.
    [[self lowerBezelGrayWithIsSelected:NO] set];
    NSRectFill(lowerBezelFrameL);
}

// ----------------- Delegate Instance Method Definitions ---------------------

@end

@implementation MiscTabCell(Private)
// ---------------------- Private Method Definitions --------------------------

- (void)_finishInitializing
{
    _canDraw = NO;
    [self setShowsFirstResponder:YES];
    [self setAlignment:NSCenterTextAlignment];
}

@end

// ------------------------- Function Definitions -----------------------------
