// 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;
}

//
// ------------------ MiscSwitchView Class Implementation ---------------------
//
// Yet another view that supports a switchable subview.  Features a selection
// of border styles including a border suitable for using the MiscTabCell to
// create a file folder interface.  Supports various alignments of the
// switchable subview within MiscSwitchView's frame.
//
// Written by Art Isbell (inspired by TTools' SwitchView and UHSelectView).
// 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/MiscSwitchView.h>

#else NOMISC

#import "MiscSwitchView.h"
#endif NOMISC

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

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

int MiscNoView = -1;
NSString *MiscViewWillSwitchNotification = @"MiscViewWillSwitch";
NSString *MiscViewDidSwitchNotification = @"MiscViewDidSwitch";
NSString *MiscNextViewNumberKey = @"MiscNextViewNumber";

static const unsigned MiscCurrentVersion = 2;
static const MiscPosition MiscDefaultPosition = MiscCenter;
static const MiscBorderType MiscDefaultBorderType = MiscTab;

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

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

- (void)_positionView;

@end

@implementation MiscSwitchView
// ---------------------- Factory Method Definitions --------------------------

+ (void)initialize
{
    [self setVersion:MiscCurrentVersion];
}

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

- (id)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];

    if (self != nil)
    {
        _position = MiscDefaultPosition;
        _borderType = MiscDefaultBorderType;
        _viewNumber = MiscNoView;
        _useMatrixBehavior = NO;
        _views = [[NSMutableArray allocWithZone:[self zone]] init];
        [self setAutoresizesSubviews:YES];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    if (self != nil)
    {
        _viewNumber = MiscNoView;

        if ([aDecoder versionForClassName:NSStringFromClass([self class])] ==
            MiscCurrentVersion)
        {
            [aDecoder decodeValuesOfObjCTypes:"iiC", &_position, &_borderType,
                &_useMatrixBehavior];
        }
        else
        {
            // If viewNumber were merely unarchived, setViewNumber: wouldn't
            // set the view because it does nothing when its argument is
            // identical to viewNumber which it would be in this case.
            int unarchivedViewNumberL;

            [aDecoder decodeValuesOfObjCTypes:"iii", &_position, &_borderType,
                &unarchivedViewNumberL];
            [self setViewNumber:unarchivedViewNumberL];
        }
        _views = [[aDecoder decodeObject] retain];
        [self setAutoresizesSubviews:YES];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [super encodeWithCoder:aCoder];
    [aCoder encodeValuesOfObjCTypes:"iiC", &_position, &_borderType,
        &_useMatrixBehavior];
    [aCoder encodeObject:_views];
}

- (void)dealloc
{
    if (delegate != nil)
    {
        [[NSNotificationCenter defaultCenter] removeObserver:delegate
            name:nil object:self];
    }
    [_views release];
    [super dealloc];
}

- (void)drawRect:(NSRect)aRect
{
    // Here we only draw peripheral stuff.  The selected view will be
    // displayed as a subview.
    [[NSColor controlColor] set];
    NSRectFill(aRect);

    if (_borderType != MiscNone)
    {
        NSRect boundsL = [self bounds];
        NSRect insideBorderRectL = NSInsetRect(boundsL, 2.0, 2.0);

        // Redraw border only if aRect extends into the border.
        if (!NSContainsRect(insideBorderRectL, aRect))
        {
            switch (_borderType)
            {
                case MiscTab:
                {
                    NSRectEdge sidesL[] = {NSMinXEdge, NSMinYEdge, NSMaxXEdge,
                        NSMaxXEdge, NSMinYEdge};
                    float graysL[] = {NSWhite, NSBlack, NSBlack, NSDarkGray,
                        NSDarkGray};

                    NSDrawTiledRects(boundsL , boundsL , sidesL, graysL, 5);
                    break;
                }
                // If an unsupported borderType is specified, default to
                // MiscNone.
                case MiscNone:
                default:
                    break;

                case MiscLine:
                    [[NSColor blackColor] set];
                    NSFrameRect(boundsL);
                    break;

                case MiscBezel:
                    NSDrawGrayBezel(boundsL, boundsL);
                    break;

                case MiscGroove:
                    NSDrawGroove(boundsL, boundsL);
                    break;

                case MiscButton:
                    NSDrawButton(boundsL, boundsL);
                    break;
            }
        }
    }
}

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

- (void)setBorderType:(MiscBorderType)aType
{
    // MiscNone <= aType is always true because MiscBorderType is an unsigned
    // type and MiscNone == 0.
    if (aType <= MiscTab)
    {
        _borderType = aType;
    }
    else
    {
        _borderType = MiscDefaultBorderType;
    }
    [self setNeedsDisplay:YES];
}

- (MiscBorderType)borderType
{
    return _borderType;
}

- (void)setDelegate:(id)aDelegate
{
    SEL delegateSelectorL;
    NSNotificationCenter *defaultCenterL = [NSNotificationCenter
        defaultCenter];

    if (delegate != nil)
    {
        [defaultCenterL removeObserver:delegate name:nil object:self];
    }
    delegate = aDelegate;
    delegateSelectorL = @selector(viewWillSwitch:);

    if ([delegate respondsToSelector:delegateSelectorL])
    {
        [defaultCenterL addObserver:delegate selector:delegateSelectorL
                               name:MiscViewWillSwitchNotification
                             object:self];
    }
    delegateSelectorL = @selector(viewDidSwitch:);

    if ([delegate respondsToSelector:delegateSelectorL])
    {
        [defaultCenterL addObserver:delegate selector:delegateSelectorL
                               name:MiscViewDidSwitchNotification object:self];
    }
}

- (void)setPosition:(MiscPosition)aPosition
{
    // MiscTopLeft <= aPosition is always true because MiscPosition is an
    // unsigned type and MiscTopLeft == 0.
    if (aPosition <= MiscBottomRight)
    {
        _position = aPosition;
    }
    else
    {
        _position = MiscDefaultPosition;
    }
}

- (MiscPosition)position
{
    return _position;
}

- (void)setUseMatrixBehavior:(BOOL)aFlag
{
    _useMatrixBehavior = aFlag;
}

- (BOOL)useMatrixBehavior
{
    return _useMatrixBehavior;
}

- (void)setViewNumber:(int)aViewNumber
{
    unsigned viewCountL = [_views count];

    if (0 < viewCountL)
    {
        if ((0 <= aViewNumber) && (aViewNumber < viewCountL))
        {
            // If the viewNumber was changed, switch the new view.
            if (aViewNumber != _viewNumber)
            {
                NSView *viewL;
                NSNotificationCenter *notificationCenterL =
                    [NSNotificationCenter defaultCenter];

                [notificationCenterL
                    postNotificationName:MiscViewWillSwitchNotification
                                  object:self userInfo:[NSDictionary
                                      dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithInt:aViewNumber],
                                          MiscNextViewNumberKey,
                                      nil]];

                // This assumes that viewNumber, if not MiscNoView, must be
                // within range of views indices.
                if (_viewNumber != MiscNoView)
                {
                    // Return current view to its original superview.
                    viewL = [_views objectAtIndex:_viewNumber];
                    [viewL removeFromSuperview];
                    [_originalSuperview addSubview:viewL];
                    [viewL setFrame:_originalFrame];
                }
                _viewNumber = aViewNumber;
                viewL = [_views objectAtIndex:_viewNumber];
                _originalSuperview = [viewL superview];
                _originalFrame = [viewL frame];
                [viewL removeFromSuperview];
                [self addSubview:viewL];
                [self _positionView];
                [notificationCenterL
                    postNotificationName:MiscViewDidSwitchNotification
                                  object:self];
            }
        }
        else
        {
            NSLog(@"View number, %d, is outside of range of available view numbers, 0 - %d",
                  aViewNumber, viewCountL - 1);
        }
    }
    else
    {
        NSLog(@"No switchable views exist");
    }
}

- (int)viewNumber
{
    return _viewNumber;
}

- (NSArray *)views
{
    return _views;
}

- (void)takeViewNumberFrom:(id)aSender
{
    int newViewNumberL;

    if (aSender != nil)
    {
        SEL valueSelectorL = [self valueSelectorForSwitchSource:aSender];

        if (valueSelectorL != (SEL)NULL)
        {
            newViewNumberL = (int)[aSender performSelector:valueSelectorL];
        }
        else
        {
            newViewNumberL = MiscNoView;
        }
    }
    else
    {
        newViewNumberL = MiscNoView;
    }
    [self setViewNumber:newViewNumberL];
}

- (void)takeTagFrom:(id)aSender
{
    [self setViewNumber:[aSender tag]];
}

- (void)takeIntValueFrom:(id)aSender
{
    [self setViewNumber:[aSender intValue]];
}

- (void)takeSelectedRowFrom:(id)aSender
{
    [self setViewNumber:[aSender selectedRow]];
}

- (void)takeSelectedColumnFrom:(id)aSender
{
    [self setViewNumber:[aSender selectedColumn]];
}

- (unsigned)count
{
    return [_views count];
}

- (NSView *)currentView
{
    if (_viewNumber != MiscNoView)
    {
        return [_views objectAtIndex:_viewNumber];
    }
    else
    {
        return nil;
    }
}

- (NSView *)viewAtIndex:(unsigned)anIndex
{
    if (anIndex < [_views count])
    {
        return [_views objectAtIndex:anIndex];
    }
    else
    {
        return nil;
    }
}

- (unsigned)indexOfView:(NSView *)aView
{
    return [_views indexOfObjectIdenticalTo:aView];
}

- (void)addView:(NSView *)aView
{
    if ((aView != nil) &&
        ([_views indexOfObjectIdenticalTo:aView] == NSNotFound))
    {
        [_views addObject:aView];

        if (_viewNumber == MiscNoView)
        {
            _viewNumber = 0;
        }
    }
}

- (void)addView:(NSView *)aView atIndex:(unsigned)anIndex
{
    if ((aView != nil) &&
        ([_views indexOfObjectIdenticalTo:aView] == NSNotFound))
    {
        if (anIndex < [_views count])
        {
            [_views insertObject:aView atIndex:anIndex];
        }
        else
        {
            [_views addObject:aView];
        }
        if (_viewNumber == MiscNoView)
        {
            _viewNumber = 0;
        }
    }
}

- (void)removeView:(NSView *)aView
{
    [_views removeObjectIdenticalTo:aView];

    if ([_views count] == 0)
    {
        _viewNumber = MiscNoView;
    }
}

- (void)removeViewAtIndex:(unsigned)anIndex
{
    unsigned viewCountL = [_views count];

    if (anIndex < viewCountL)
    {
        [_views removeObjectAtIndex:anIndex];

        if (viewCountL == 1)
        {
            _viewNumber = MiscNoView;
        }
    }
}

- (SEL)valueSelectorForSwitchSource:aSender
{
    SEL valueSelectorL;

    if ([aSender isKindOfClass:[NSMatrix class]])
    {
        int rowNumber;
        int columnNumber;

        [aSender getNumberOfRows:&rowNumber columns:&columnNumber];
        if (columnNumber <= rowNumber)
        {
            valueSelectorL = @selector(selectedRow);
        }
        else
        {
            valueSelectorL = @selector(selectedColumn);
        }
    }
    else if ([aSender respondsToSelector:@selector(isEditable)] &&
             [aSender isEditable] &&
             [aSender respondsToSelector:@selector(intValue)])
    {
        valueSelectorL = @selector(intValue);
    }
    else if ([aSender isKindOfClass:[NSButton class]] ||
             [aSender isKindOfClass:[NSButtonCell class]])
    {
        valueSelectorL = @selector(tag);
    }
    else if ([aSender respondsToSelector:@selector(intValue)])
    {
        valueSelectorL = @selector(intValue);
    }
    else if ([aSender respondsToSelector:@selector(tag)])
    {
        valueSelectorL = @selector(tag);
    }
    else
    {
        valueSelectorL = (SEL)NULL;
    }
    return valueSelectorL;
}

- (void)subviewFrameChanged:(NSNotification *)aNotification
{
    if ([self indexOfView:[aNotification object]] == _viewNumber)
    {
        [self _positionView];
    }
}
    
// ----------------- Delegate Instance Method Definitions ---------------------

@end

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

- (void)_positionView
{
    if (_viewNumber != MiscNoView)
    {
        float borderWidthL;
        NSRect boundsRectL;
        NSRect subviewFrameL;
        NSPoint frameOriginL;
        NSView *viewL;

        if (_borderType == MiscNone)
        {
            borderWidthL = 0.0;
        }
        else
        {
            borderWidthL = 2.0;
        }
        viewL = [_views objectAtIndex:_viewNumber];
        boundsRectL = [self bounds];
        subviewFrameL = [viewL frame];

        // Cases ordered in decreasing probability.
        switch (_position)
        {
            // Centering is most common and is the default should an
            // unsupported position be specified.
            case MiscCenter:
            default:
                frameOriginL = NSMakePoint((NSWidth(boundsRectL) -
                                            NSWidth(subviewFrameL)) / 2.0,
                                           (NSHeight(boundsRectL) -
                                            NSHeight(subviewFrameL)) / 2.0);
                break;

            case MiscTopCenter:
                frameOriginL = NSMakePoint((NSWidth(boundsRectL) -
                                            NSWidth(subviewFrameL)) / 2.0,
                                           NSHeight(boundsRectL) -
                                           NSHeight(subviewFrameL) -
                                           borderWidthL);
                break;

            case MiscBottomCenter:
                frameOriginL = NSMakePoint((NSWidth(boundsRectL) -
                                            NSWidth(subviewFrameL)) / 2.0,
                                           borderWidthL);
                break;

            case MiscTopLeft:
                frameOriginL = NSMakePoint(borderWidthL,
                                           NSHeight(boundsRectL) -
                                           NSHeight(subviewFrameL) -
                                           borderWidthL);
                break;

            case MiscTopRight:
                frameOriginL = NSMakePoint(NSWidth(boundsRectL) -
                                           NSWidth(subviewFrameL) -
                                           borderWidthL,
                                           NSHeight(boundsRectL) -
                                           NSHeight(subviewFrameL) -
                                           borderWidthL);
                break;
            case MiscLeft:
                frameOriginL = NSMakePoint(borderWidthL,
                                           (NSHeight(boundsRectL) -
                                            NSHeight(subviewFrameL)) / 2.0);
                break;

            case MiscRight:
                frameOriginL = NSMakePoint(NSWidth(boundsRectL) -
                                           NSWidth(subviewFrameL) -
                                           borderWidthL,
                                           (NSHeight(boundsRectL) -
                                            NSHeight(subviewFrameL)) / 2.0);
                break;

            case MiscBottomLeft:
                frameOriginL = NSMakePoint(borderWidthL, borderWidthL);
                break;

            case MiscBottomRight:
                frameOriginL = NSMakePoint(NSWidth(boundsRectL) -
                                           NSWidth(subviewFrameL) -
                                           borderWidthL,
                                           borderWidthL);
                break;
        }
        [viewL setFrameOrigin:frameOriginL];
        [viewL setNeedsDisplay:YES];
    }
}

@end

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