/* MiscSwapView.m
*
* This subclass of View is able to swap different views into itself. It's
* used to implement any kind of multipage windows. (Inspectors, Prefs...)
*
* For more interface-info see the header file. More in depth information
* can be found here in the source-code.
*
* Notes: There is one Cateogry for this class includes automatic swapping.
*
* Improved by:		Thomas Engel
* First changes:  	24.01.1994 (Copyleft)
* Last modified: 	27.07.1997
* Copyright (C) 1995 Thomas Engel
*/

#import "MiscSwapView.h"
#import "MiscSwapViewItem.h"

// The following are for keeping track of versions when archiving instances
// of this class. If you change the encodeWithCoder: method, make sure to
// bump up the version below and make the appropriate changes to the initWithCoder:
// method so all previously archived instances will be unarchivable.

#define MISC_SV_VERSION 	2
#define MISC_SV_CLASSNAME 	@"MiscSwapView"


@implementation MiscSwapView

+ (void)initialize
{
    // Initialize the current version number which is used when archiving
    // objects. This way we will be able to read all versions if we are
    // careful.

    if( self == [MiscSwapView class] ) [self setVersion: MISC_SV_VERSION];
}

- (id)init
{
    NSRect theFrame = {{0.0, 0.0}, {0.0, 0.0}};
    return [self initWithFrame: theFrame];
}

- (id)initWithFrame:(NSRect)frameRect
{
    // Designated initilizer. We will set allow resizing by default and
    // we will draw in Lightgray if we have to swap 'nil' in.

    self = [super initWithFrame:frameRect];
    if( !self ) return nil;

    // All instances can remain nil .. we just have a few settings to make.

    backgroundColor = [NSColor controlBackgroundColor];
    [self setDrawsBackground:NO];

    // Well the ByObject Category want's some other defaults too.

    itemArray = [NSMutableArray new];	
    [self setTagComparison:YES];

    [self awake];

    return self;
}

- (void)dealloc
{
    // We do not free the contentsitemArray of this list! Well we didin't
    // allocate them..but maybe we should free them...hmmm. I'm not sure yet.

    [contentView release];
    [contentViewsHomeView release];

    [self _unregisterNotificationObserver:delegate];
    [delegate release];
    
    [backgroundColor release];
    [identifier release];

    // Before release our items...we will first remove them... this ensures that they are properly removed
    // from all our notifications.
    
    [self removeAllSwapViewItems];
    [itemArray release];
    [currentSwapItem release];

    [super dealloc];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    int  version;

    [super initWithCoder:aDecoder];

    version = [aDecoder versionForClassName:MISC_SV_CLASSNAME];

    switch (version)
    {
        case 0:
            // <<HACK>> We should never get that one since this was the encoding of the NeXTSTEP version of this class.
            break;

        case 1:
            // We skip this version so that we can start of with encoding #2 since this is
            // mapping a lot better MiscKit 2.0
            break;

        case 2:
            contentView = [[aDecoder decodeObject] retain];				// These usually should be nil !!
            contentViewsHomeView = [[aDecoder decodeObject] retain];	// These usually should be nil !!
            delegate = [[aDecoder decodeObject] retain];
            backgroundColor = [[aDecoder decodeObject] retain];
            identifier = [[aDecoder decodeObject] retain];
            [aDecoder decodeValueOfObjCType:"c" at:&tagComparison];
            [aDecoder decodeValueOfObjCType:"c" at:&backgroundDrawing];
            [aDecoder decodeValueOfObjCType:"i" at:&alignment];

            currentSwapItem = nil;
            itemArray = [NSMutableArray new];

        default:
            break;
    }

    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [super encodeWithCoder:aCoder];
    [aCoder encodeConditionalObject:contentView];
    [aCoder encodeConditionalObject:contentViewsHomeView];
    [aCoder encodeConditionalObject:delegate];
    [aCoder encodeObject:backgroundColor];
    [aCoder encodeConditionalObject:identifier];
    [aCoder encodeValueOfObjCType:"c" at:&tagComparison];
    [aCoder encodeValueOfObjCType:"c" at:&backgroundDrawing];
    [aCoder encodeValueOfObjCType:"i" at:&alignment];
}

- (void)awake
/*"
    This is a general awake method which can be reused by all different awake methods.
"*/
{
    // <<HACK>> This is a really evil and mean hack. I have not had the time to check out the details.
    // But for some strage reasons our autoresize state is NO after we get unarchived from a NIB.
    // This caused the UI's not to resize properly. However...since the default NSView subclass should be
    // autoresizign its subviews by default...I really have no clue what is going wrong where.
    // Ergo: This is a fix...but definitly uncool.

    [self setAutoresizesSubviews:YES];	
}

- (void)awakeFromNib
/*"
    Awake the object after getting instantiated from a NIB.
"*/
{
    // Nothing to do here...we already handle the coder based awaking so that does a proper awake in our case.
}

- (id)awakeAfterUsingCoder:(NSCoder *)aCoder
/*"
    Perform awaking after unarchiving from a stream controlled by aCoder.
"*/
{
    [self awake];
    return self;
}

- (void)setDelegate:(id)anObject
/*"
    Sets our delegate and registers it with all our notifications.
"*/
{
    if( delegate == anObject ) return;

    [self _unregisterNotificationObserver:delegate];
    [self _registerNotificationObserver:anObject];

    // Changing of the guards...

    [delegate release];
    delegate = [anObject retain];

}

- (id)delegate
/*"
    Returns our current delegate.
"*/
{
    return delegate;
}

- (void)setBackgroundColor:(NSColor *)aColor
/*"
    Sets our background color to aColor. The default is NSColor's controlBackgroundColor.
"*/
{
    // <<NOTE>> There was something about releasing colors. ??

    [backgroundColor autorelease];
    backgroundColor = [aColor retain];
}

- (NSColor *)backgroundColor
/*"
    Returns our background color.
"*/
{
    return backgroundColor;
}

- (void)setDrawsBackground:(BOOL)flag
/*"
    Returns our background color. By default this is set to NO.
"*/
{
    backgroundDrawing = flag;
}

- (BOOL)drawsBackground
/*"
    Returns YES if we have to draw our background and NO otherwise.
"*/
{
    return backgroundDrawing;
}

- (BOOL)isOpaque
{
    if( backgroundDrawing &&
        [backgroundColor alphaComponent] == 1.0 )
        return YES;
    
    return NO;
}

- (void)setContentView:(NSView *)aView
/*"
    This method explicitly puts in a new subview and puts the current content view back to were it came from.
"*/
{
    // This is the real method !!
    // Here we process the swapping and resizing.
    // If the new view is different from our current contentView we should
    // put the current back where it came from.

    if( aView != contentView )
    {
        // This is the place to fire off some notifications since we will be changing our contentView
        
        if( contentView && contentViewsHomeView )
        {
            //<<BUG>> this actually needs some different handling if our views do _not_ have
            // a superview !! This currently is a hack that works perfectly if you set up things inside
            // IB and don't create views programatically.
            
            [contentViewsHomeView addSubview:contentView];
            [contentView setFrame:contentViewsHomeRect];

            // We don't need that one...but all these retain release lines make sure that the views stay around
            // In a later version we might go through the code an remove the unnecessary ones...but since I don't
            // want to possible break this class I'll just keep it that way for now.

            [contentViewsHomeView release];
            [contentView release];		

            contentViewsHomeView = nil;
            contentView = nil;
        }

        // If we really have a subview we will take care of it.

        if( aView )
        {
            NSSize	newSize;

            contentView = [aView retain];
            contentViewsHomeView = [[contentView superview] retain];
            contentViewsHomeRect = [contentView frame];

            [self addSubview:contentView];

            // <<HACK>> This needs some additional work since we have not yet considered the alignment
            // settings.
            
            [contentView setFrameOrigin:NSMakePoint(0.0, 0.0)];

            newSize = [self bounds].size;

            if( !([contentView autoresizingMask] & NSViewWidthSizable) )
                newSize.width = contentViewsHomeRect.size.width;

            if( !([contentView autoresizingMask] & NSViewHeightSizable) )
                newSize.height = contentViewsHomeRect.size.height;

            [contentView setFrameSize:newSize];
        }
        [self display];
   }
}

- (NSView *)contentView
{
    return contentView;
}

- (void)drawRect:(NSRect)rect
{
    if( [self drawsBackground] )
    {
        [backgroundColor set];
        NSRectFill( rect );
    }

    // As long as you have a identifier (e.g. button) connected to the identifier outlet, this
    // should select an initial view for you automatically on the first draw.

    // IMHO there should be a better place to drop this !
    
    if( contentView == nil ) [self swapContentView:identifier];
}

@end

/*
 * History: 25.09.94 Added archiving. (read:, write: awake, initialize)
 *					 Also added drawSelf:: so it would select an initial view.
 *
 *			24.02.94 Added Greg's quick composing.
 *
 *			24.01.94 Redesign from my old swapView. Added Buffering.
 *
 *			21.01.94 Added the resizing job the Greg's swapView.
 *
 *
 * Bugs: - Could be. Yes.
 */