/* Implementation of Box class
 *
 * Copyright (C)  1993, 1994, 1995  The Board of Trustees of  
 * The Leland Stanford Junior University.  All Rights Reserved.
 *
 * Authors: Scott Francis, Paul Kunz, Imran Qureshi, and Libing Wang
 *
 * This file is part of an Objective-C class library for a window system
 *
 * Box.m,v 1.38 1995/06/26 23:02:15 fedor Exp
 */

#include "Box.h"

#include <objc/List.h>
#include "TextFieldCell.h"
#include "graphics.h"
#include "stdmacros.h"
#include <objc/typedstream2.h>

extern char * BoxInstanceName(void);

@interface View(WidgetSet)
- _setOffsets:(NXSize *)offsets;
@end

@interface Box(WidgetSet)
- _init;
- _setBorderType:(int)borderType;
- _managedBy:parent wid:(void *)widget;
@end

@implementation Box

- initFrame:(const NXRect *)frameRect
{
    View               *aView;
    NXRect              rect;

    offsets.width = DEF_OFFSET;
    offsets.height = DEF_OFFSET;
    
    [super initFrame:frameRect];
    instancename = BoxInstanceName();
    /* inherit class from VIEW.  it should be xmBulletinBoardWidgetClass */

	borderRect = *frameRect;
	rect.origin.x = DEF_OFFSET;
	rect.origin.y = DEF_OFFSET;
	rect.size.width = borderRect.size.width - DEF_OFFSET*2;
	rect.size.height = borderRect.size.height - DEF_OFFSET*2;
	aView = [[View alloc] initFrame:&rect];
	[self setContentView:aView];

	titleRect.size.height = 25;
	titleRect.size.width = 50;
	titleRect.origin.x = titleRect.origin.y = 0;
	cell = NULL;
	[self setTitlePosition:NX_ATTOP];
	[self setTitle:"Title"];

	[self setBorderType:NX_RIDGE];
    return self;
}

- awake
{
/* lets instantiate the appropriate title cell widget, but let the title cell
   do the work */
   
    [super awake];
    if (!cell && (bFlags.titlePosition != NX_NOTITLE)) {
    	cell = [[TextFieldCell alloc] init];
    	[cell _setFrame:&titleRect inView:(Control *)self];
	[cell setStringValue:title];
    }
    return self;
}

// - free;

- (int)borderType
{
	return bFlags.borderType;
}

- (int)titlePosition
{
	return bFlags.titlePosition;
}

- setBorderType:(int)borderType
{
    [self _setBorderType:borderType];
    return self;
}

- setTitlePosition:(int)pos
{
    NXRect temp;

    if (cell) {
	[cell _getFrame:&titleRect];
    }
    /* x value is the same for all:  centered horizontally */
    titleRect.origin.x = frame.origin.x + (frame.size.width / 2)
	                                - (titleRect.size.width / 2);
    if (pos == NX_NOTITLE && cell) {
	[cell free];
    } else if (pos != NX_NOTITLE && !cell) {
	cell = [[TextFieldCell alloc] init];
    	[cell _setFrame:&titleRect inView:(Control *)self];
    }
    switch (pos) {
    case (NX_NOTITLE):
	break;
/* original code with Motif y coordinate
 *  case (NX_ABOVETOP):
 *	titleRect.origin.y = frame.origin.y;  // at very top
 *	borderRect.size.height = frame.size.height - titleRect.size.height;
 *	borderRect.origin.y = frame.origin.y + titleRect.size.height;
 *	break;
 *  case (NX_ATTOP):
 *	titleRect.origin.y = frame.origin.y;
 *	borderRect.size.height =frame.size.height-(titleRect.size.height/2);
 *	borderRect.origin.y = frame.origin.y + (titleRect.size.height/2);
 *	break;
 *  case (NX_BELOWTOP):
 *	titleRect.origin.y = frame.origin.y;
 *	borderRect.size.height = frame.size.height;
 *	borderRect.origin.y = frame.origin.y;
 *	break;
 *  case (NX_ABOVEBOTTOM):
 *	titleRect.origin.y = frame.size.height - titleRect.size.height;
 *	borderRect.origin.y = frame.origin.y;
 *	borderRect.size.height = frame.size.height;
 *	break;
 *  case (NX_ATBOTTOM):
 *	titleRect.origin.y = frame.size.height - titleRect.size.height;
 *	borderRect.origin.y = frame.origin.y;
 *	borderRect.size.height = frame.size.height-titleRect.size.height/2;
 * 	break;
 *  case (NX_BELOWBOTTOM):
 *	titleRect.origin.y = frame.size.height - titleRect.size.height;
 *	borderRect.origin.y = frame.origin.y;
 *	borderRect.size.height = frame.size.height - titleRect.size.height;
 *	break;
 */
    case (NX_ABOVETOP):
	titleRect.origin.y = frame.size.height - titleRect.size.height;
	borderRect.size.height = frame.size.height - titleRect.size.height;
	borderRect.origin.y = frame.origin.y;
	break;
    case (NX_ATTOP):
	titleRect.origin.y = frame.size.height - titleRect.size.height/1;
	borderRect.size.height =frame.size.height-(titleRect.size.height/2);
	borderRect.origin.y = frame.origin.y;
	break;
    case (NX_BELOWTOP):
	titleRect.origin.y = frame.origin.y - titleRect.size.height;
	borderRect.size.height = frame.size.height;
	borderRect.origin.y = frame.origin.y;
	break;
    case (NX_ABOVEBOTTOM):
	titleRect.origin.y = frame.origin.y;
	borderRect.origin.y = frame.origin.y;
	borderRect.size.height = frame.size.height;
	break;
    case (NX_ATBOTTOM):
	titleRect.origin.y  = frame.origin.y;
	borderRect.origin.y = frame.origin.y + titleRect.size.height/2;
	borderRect.size.height = frame.size.height - titleRect.size.height/2;
	break;
    case (NX_BELOWBOTTOM):
	titleRect.origin.y = frame.origin.y;
	borderRect.origin.y = frame.origin.y + titleRect.size.height;
	borderRect.size.height = frame.size.height - titleRect.size.height;
	break;
    default:
	return self;
    }
    [cell _setFrame:&titleRect inView:(Control *)self];
    bFlags.titlePosition = pos;
    temp.size.width = borderRect.size.width - offsets.width*2;
    temp.size.height = borderRect.size.height - offsets.height*2;
    [contentView sizeTo:temp.size.width :temp.size.height];
    [self _setBorder:&borderRect];
    return self;
}

- (const char *)title
{
	const char 	*cellTitle;
	
	cellTitle = [cell stringValue];
	return ((cellTitle) ? cellTitle : "Title") ;
}

- setTitle:(const char *)aString
{
	if (!aString) return self;
	[cell setStringValue:aString];	// sets the title
	[self sizeToFit];		// size the box to fit this title
					// and size its subviews, etc
	return self;
}

- cell
{
	return cell;
}

- font
{
	return nil;
}

- setFont:fontObj
{
    return self;
}

- getOffsets:(NXSize *)theSize
{
	theSize->width = offsets.width;
	theSize->height = offsets.height;
	return self;
}

- setOffsets:(NXCoord)horizontal :(NXCoord)vertical
{
	offsets.width = horizontal;
	offsets.height = vertical;
	[borderView _setOffsets:&offsets];
	[self sizeToFit];
	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	NXSize newSize;		// for the contentview
	NXCoord dw, dh;

	dw = frame.size.width - width;
	dh = frame.size.height - height;
	[super sizeTo:width :height];

	// now adjust the borderRect and the box widget...

	borderRect.size.width -= dw;
	borderRect.size.height -= dh;
	[borderView sizeTo:borderRect.size.width :borderRect.size.height];

	// now resize the contentview.

	newSize.width = borderRect.size.width - offsets.width*2;
	newSize.height = borderRect.size.height - offsets.height*2;
	[contentView sizeTo:newSize.width :newSize.height];

	// move the cell to new position according to bFlags.titlePosition

	if (bFlags.titlePosition > NX_BELOWTOP) {
	[cell _moveTo:(titleRect.origin.x-(dw/2)) :(titleRect.origin.y+dh)];
	} else if (bFlags.titlePosition) {
	    [cell _moveTo:(titleRect.origin.x - (dw/2)) :titleRect.origin.y];
	} 
	return self; 
}

- sizeToFit
/* This method tries to do a lot of work...
  	Designed to wrap the box around the contentview and title box.
	If either has become too large, we must resize to be larger.
 */
{
	NXRect cview, tbox;
	
	if ([contentView respondsTo:@selector(sizeToFit)]) {
		[(Box *)contentView sizeToFit];
	}
	// should resize contentview if it can't do itself, but I'm
	// not sure how to proceed for this...
	[contentView getFrame:&cview];
	[cell _getFrame:&tbox];
	
	if ((borderRect.size.width >= cview.size.width + 2*offsets.width) ||
	    (borderRect.size.height >= cview.size.height + 2*offsets.height) ||
	    (borderRect.size.width >= tbox.size.width) ||
	    (NX_MAXY(&frame) >= NX_MAXY(&tbox)))
	{
		return self;
	}
	
	borderRect.origin.x = cview.origin.x - offsets.width;
	borderRect.origin.y = cview.origin.y - offsets.height;
	borderRect.size.width = MAX((NX_MAXX(&cview) + offsets.width),
				    (tbox.size.width));
	borderRect.size.height = (NX_MAXY(&cview) + offsets.height);
	frame.origin.x = borderRect.origin.x;
	frame.size.width = borderRect.size.width;
	switch (bFlags.titlePosition) {
/* old code with Motif y coordinate
 *	case (NX_NOTITLE):
 *	case (NX_BELOWTOP):
 *	    frame.origin.y = titleRect.origin.y;
 *	    frame.size.height = borderRect.size.height;
 *	    break;
 *	case (NX_ABOVETOP):
 *	    frame.origin.y = tbox.origin.y;
 *	    frame.size.height = borderRect.size.height + tbox.size.height;
 *	    break;
 *	case (NX_ATTOP):
 *	    frame.origin.y = tbox.origin.y;
 *	    frame.size.height = borderRect.size.height + tbox.size.height/2;
 *	    break;
 *	case (NX_ABOVEBOTTOM):
 *	    frame.origin.y = borderRect.origin.y;
 *	    frame.size.height = borderRect.size.height;
 *	    break;
 *	case (NX_ATBOTTOM):
 *	    frame.origin.y = borderRect.origin.y;
 *	    frame.size.height = borderRect.size.height + tbox.size.height/2;
 *	    break;
 *	case (NX_BELOWBOTTOM):
 *	    frame.origin.y = borderRect.origin.y;
 *	    frame.size.height = borderRect.size.height+tbox.size.height;
 *	    break;
 *	default:
 *	    fprintf(stderr, "Error:  erroneous title position\n");
 *	    break;
 *	}
 */
	case (NX_NOTITLE):
	case (NX_BELOWTOP):
	    frame.origin.y = borderRect.origin.y;
	    frame.size.height = borderRect.size.height;
	    break;
	case (NX_ABOVETOP):
	    frame.origin.y = borderRect.origin.y;
	    frame.size.height = borderRect.size.height + tbox.size.height;
	    break;
	case (NX_ATTOP):
	    frame.origin.y = borderRect.origin.y;
	    frame.size.height = borderRect.size.height + tbox.size.height/2;
	    break;
	case (NX_ABOVEBOTTOM):
	    frame.origin.y = borderRect.origin.y;
	    frame.size.height = borderRect.size.height;
	    break;
	case (NX_ATBOTTOM):
	    frame.origin.y = borderRect.origin.y - tbox.size.height/2;
	    frame.size.height = borderRect.size.height + tbox.size.height/2;
	    break;
	case (NX_BELOWBOTTOM):
	    frame.origin.y = borderRect.origin.y - tbox.size.height;
	    frame.size.height = borderRect.size.height+tbox.size.height;
	    break;
	default:
	    fprintf(stderr, "Error:  erroneous title position\n");
	    break;
	}
/*	frame.size.height = (NX_MAXY(&frame) > NX_MAXY(&tbox))  
			    ? frame.size.height 
			    : NX_MAXY(&tbox) - frame.origin.y;
 */
	[self _setBorder:&borderRect];
	return self;
}

- setFrameFromContentFrame:(const NXRect *)contentFrame
{
	return self;
}

- contentView
{
    return contentView;
}

- setContentView:aView
{
    View	*oldContentView = nil;
    int dh;
    
/*	Well, this should really resize the view before adding it to the
	box, in order to stay with NeXT compliance.  For now scrap that
	until done debugging box.
 
 	Okay, here is the resizing!
 */
    dh = [self heightAdjust];
    [aView sizeTo:(borderRect.size.width - offsets.width*2) 
    		 :(borderRect.size.height) - offsets.height*2 - dh];
    if ( contentView ) {
	oldContentView = [super replaceSubview:contentView with:aView];
	contentView = aView;
    } else {
	oldContentView = nil;
        [self addSubview:aView];
    }
    // we need something to redraw the text label of the box here //
    return oldContentView;
}

- addSubview:aView
{
    [contentView addSubview:aView];
    [self sizeToFit];
    return self;
}

- replaceSubview:oldView with:newView
{
    return [contentView replaceSubview:oldView with:newView];
}

// - drawSelf:(const NXRect *)rects :(int)rectCount
// - write:(NXTypedStream *)stream;

- read:(TypedStream *)ts
{    
    int border,titlePosition;
    
    [super read:ts];

    objc_read_string(ts, &title);
    
    objc_read_type(ts, "i", &border);          // this step is for backward
    bFlags.borderType = border;                // compatibility.
    objc_read_type(ts, "i", &titlePosition);   // one should really write
    bFlags.titlePosition = titlePosition;     // the entire bFlags struct.
    objc_read_type(ts, "i", &(offsets.width));
    objc_read_type(ts, "i", &(offsets.height));
    NXReadRect(ts, &borderRect);
    NXReadRect(ts, &titleRect);

    contentView = [subviews objectAt:0];
    return self;
}

// obsolete methods
// + newFrame:(const NXRect *)frameRect;

/* Methods not in NeXTSTEP */

- (int)heightAdjust
{
    int dh;
    
    switch (bFlags.titlePosition) {
    case (NX_ABOVETOP):
    case (NX_BELOWBOTTOM):
    	dh = titleRect.size.height;
	break;
    case (NX_ATTOP):
    case (NX_ATBOTTOM):
    	dh = titleRect.size.height/2;
	break;
    default:
    	dh = 0;
	break;
    } 
    return dh;
}

/* Sometimes we need to change the border.  This is where you do it.
	Notice that since it does motif work it starts with the underscore
	in the method name.  Don't call sizeToFit:: because this should only
	be called internally, when we should know whether or not we want to
	sizeToFit:: or not.  Notice we use the borderRect to set the bounds
	of the widget, though the logical space of the box is as large as its
	frame Rect (and its physical size is as large as title widget + 
	bulletin board widget)

   Note that this changes the _boxwid widget, because we want its border
   to be thus set, not the border of the outer widget, or container widget.
 */
- _setBorder:(NXRect *)aBorder
{
    [borderView _setBorderRect:aBorder];
    return self;
}

- (void *)_widget 
{
    return [borderView _widget];
}

@end

