/* Implementation of Window class
 *
 * Copyright (C)  1993  The Board of Trustees of  
 * The Leland Stanford Junior University.  All Rights Reserved.
 *
 * Authors: Scott Francis, Paul Kunz, Tom Pavel, 
 *	    Imran Qureshi, and Libing Wang (SLAC)
 *          Mike Kienenberger (Alaska)
 *
 * This file is part of an Objective-C class library for a window system
 *
 * Window.m,v 1.120 1995/12/13 22:33:26 fedor Exp
 */


#include "Window.h"

/* Required for implementation: */

#include "Application.h"
#include "Menu.h"
#include "Responder.h"
#include "CustomView.h"
#include <objc/List.h>
#include <stdlib.h>             /* for free() */
#include <objc/hashtable.h>
#ifdef	sgi
#include <malloc.h>	/* although you might need to edit malloc.h */
#endif	/* sgi */

#include "Scroller.h"
#include <objc/typedstream2.h>
#ifdef	RESIZE_DEBUG
#include <Xm/Xm.h>	/* Position, Dimension declarations for debugging */
#endif	/* RESIZE_DEBUG */

extern char *WindowInstanceName( void );

/* private View methods */
@interface View(WidgetSet)
- _flushView;
@end

@implementation Window

/* Factory methods */

+ alloc
{
    self = [super alloc];
    return self;
}


/* Public methods */

- init
{
    NXRect  defaultRect = { {0, 0}, {612, 792} };
            
    return [self initContent:&defaultRect style:0
					backing:0
					buttonMask:0
					defer:YES
					screen:(const NXScreen *)NULL];
}

- initContent:(const NXRect *)contentRect 
{
    return [self initContent:contentRect style:0
					backing:0
					buttonMask:0
					defer:YES
					screen:(const NXScreen *)NULL];
}

- initContent:(const NXRect *)contentRect style:(int)aStyle
					backing:(int)bufferingType
					buttonMask:(int)mask
					defer:(BOOL)flag
{
    return [self initContent:contentRect style:aStyle
					backing:bufferingType
					buttonMask:mask
					defer:flag
					screen:(const NXScreen *)NULL];
}

- initContent:(const NXRect *)contentRect style:(int)aStyle
					backing:(int)bufferingType
					buttonMask:(int)mask
					defer:(BOOL)flag
					screen:(const NXScreen *)screen
{
    View	*aView;
    
    [super init];

   _inResizeFlag = NO;

    wFlags.style = aStyle;
    wFlags.backing = bufferingType;
    wFlags.buttonMask = mask;
    wFlags.visible = NO;
    wFlags.isMainWindow = NO;
    wFlags.isKeyWindow = NO;
    wFlags.isPanel = NO;
    wFlags.hideOnDeactivate = NO;
    wFlags.dontFreeWhenClosed = NO;
    wFlags.oneShot = NO;

    wFlags2.deferred = flag;
    wFlags2.docEdited = NO;
    wFlags2.dynamicDepthLimit = NO;

    instancename = WindowInstanceName();
    if ( !title ) {
        title = NXCopyStringBuffer("Untitled");
    }

    /* Make room for title bar and resize bar */
    NX_X(&frame)      = NX_X(contentRect) - 1;
    NX_Y(&frame)      = NX_X(contentRect) - 9;
    NX_WIDTH(&frame)  = NX_WIDTH(contentRect) + 2;
    NX_HEIGHT(&frame) = NX_HEIGHT(contentRect) + 9 + 23;
    _contentViewFrameRect = *contentRect;
    NX_X(&_contentViewFrameRect) = 1;
    NX_Y(&_contentViewFrameRect) = 9;
    aView = [[View alloc] initFrame:&_contentViewFrameRect];
    [self setContentView:aView];

    [self _initFrame];    
    if (wFlags2.deferred == NO) {
	[self _setWindowAreas];
	[self _realize];
    }

    return self;
}

- setHasMenu:(BOOL)menuFlag hasScroller:(BOOL)scrolFlag
{
	hasMenu = menuFlag;
    	hasScroller = scrolFlag;
	return self;
}

- free
{
    if (title) {
	free(title);
    }
    [NXApp _removeWindow:self];
	[contentView free];
	[menu free];
	if (hasScroller) {
		[verticalScrol free];
		[horizontalScrol free];
	}
    return [super free];
}

- setTitle:(const char*)aString
{
    if ( title ) { 
        free(title);
    }
    title = NXCopyStringBuffer(aString);
    [self _setTitle];
    return self;
}

- (const char *)title;
{

    return title;
}

- (int) windowNum;
{
    return windowNum;
}

- setContentView:aView
{
    View	*temp;

    temp = contentView;
    [temp _setWindow:nil];
    if (nil != aView)
    {
	[aView getFrame:&_contentViewFrameRect];
	contentView = aView;
	[aView _setWindow:self];
    }
    else
    {
	_contentViewFrameRect.size.width = -1;
	_contentViewFrameRect.size.height = -1;
	_contentViewFrameRect.origin.x = -1;
	_contentViewFrameRect.origin.y = -1;
	contentView = nil;
    }
    return temp;
}

- setMenu:(id)aMenu
{
    menu = aMenu;
    hasMenu = YES;
    [self _setWindowAreas];
    return self;
}

- contentView
{
    return contentView;
}

- delegate
{
    return delegate;
}

- setDelegate:newDelegate
{
    delegate = newDelegate;
    return self;
}

- becomeMainWindow
{
    wFlags.isMainWindow = YES;
    if ( delegate && [delegate respondsTo:@selector(windowDidBecomeMain:) ] ) {
	[delegate windowDidBecomeMain:self];
    }    
    return self;
}
- resignMainWindow
{
    wFlags.isMainWindow = NO;
    if ( delegate && [delegate respondsTo:@selector(windowDidResignMain:) ] ) {
	[delegate windowDidResignMain:self];
    }
    return self;
}

- orderOut:sender
{
    if (NO == wFlags.visible)  return self;
    
    [self resignMainWindow];
    wFlags.visible = NO;
    [NXApp _removeWindow:self];
    [self _orderOut];
    
    return self;
}

- setMiniwindowIcon:(const char *)anIcon
{
    return self;
}

- orderFront:sender
{
    if (YES == wFlags.visible)  return self;

    [self _setWindowAreas];
    [NXApp _willBecomeMain:self];
    [NXApp _addWindow:self];
    wFlags.visible = YES;
    [self _realize];
    [self _orderFront];
    return self;
}

- orderBack:sender
{
    return self;
}

- makeKeyAndOrderFront:sender
{
    [self makeKeyWindow];
    [self orderFront:self];
    return self;
}

- getFrame:(NXRect *)theRect
{
	*theRect = frame;
	return self;
}

- (int)style
{
	return (int)wFlags.style;
}

- performClose:sender
{
	if (delegate && [delegate respondsTo:@selector(windowWillClose:)])
	{
		if (![delegate perform:@selector(windowWillClose:) with:self])
			return nil;
	}
	return [self close];
}

- setDocEdited:(BOOL)flag
{
	wFlags2.docEdited = YES;
	return self;
}

- (BOOL)isDocEdited
{
	return (BOOL)wFlags2.docEdited;
}

- (BOOL)isVisible
{
    return (BOOL)wFlags.visible;
}

/* Handle a window expose event.  This routine should only get called if the
   Window is NX_NONRETAINED
*/
- windowExposed:(NXEvent *)theEvent
{
    // Here we should figure out which views need re-display and tell them that
    // for now, do nothing...
    if ([delegate respondsTo:@selector(windowDidExpose:)])
	[delegate windowDidExpose:self];
    return self;
}

- (BOOL)isKeyWindow
{
    return (BOOL)wFlags.isKeyWindow;
}

- (BOOL)isMainWindow
{
    return wFlags.isMainWindow;
}

// - (BOOL)canBecomeKeyWindow; 
- (BOOL)canBecomeMainWindow
{
    if ( wFlags.isPanel ) {
        return NO;
    }
    return YES;
}
// - (BOOL)worksWhenModal;
// - convertBaseToScreen:(NXPoint *)aPoint;
// - convertScreenToBase:(NXPoint *)aPoint;

- getFieldEditor:(BOOL)createFlag for:anObject
{
  /* No field editor yet */
    return nil;
}

// - endEditingFor:anObject;
// - placeWindowAndDisplay:(const NXRect *)frameRect;

- placeWindow:(const NXRect *)frameRect
 /*  	There very well may be a subtlety to this function that we are
	missing or didn't notice in NeXT docs...
 */
{
    frame = *frameRect;
    
    return self;
}

- _resizeWindow:(const NXRect *)frameRect
{
    NXRect oldFrame = frame;
#ifdef	RESIZE_DEBUG
    Position x,y;
    Dimension w,h;
    
    if (YES == _inResizeFlag)
   {
      fprintf(stderr, "skipping window size event (%d, %d), (%d, %d)\n",
              frameRect->origin.x, frameRect->origin.y,
              frameRect->size.width, frameRect->size.height);
    }
#endif	/* RESIZE_DEBUG */
    
    _inResizeFlag = YES;
    
#ifdef	RESIZE_DEBUG
    fprintf(stderr, "\nwindow size was (%d, %d), (%d, %d)\n",
          frame.origin.x, frame.origin.y,
          frame.size.width, frame.size.height);

    XtVaGetValues(widgetid, XmNx, &x, NULL);
    XtVaGetValues(widgetid, XmNy, &y, NULL);
    XtVaGetValues(widgetid, XmNwidth, &w, NULL);
    XtVaGetValues(widgetid, XmNheight, &h, NULL);

    fprintf(stderr, "widget size was (%d, %d), (%d, %d)\n",
          (int)x, (int)y, (int)w, (int)h);
          
    fprintf(stderr, "contentView size was (%d, %d), (%d, %d)\n",
          _contentViewFrameRect.origin.x, _contentViewFrameRect.origin.y,
          _contentViewFrameRect.size.width,
          _contentViewFrameRect.size.height);

    XtVaGetValues([contentView _widget], XmNx, &x, NULL);
    XtVaGetValues([contentView _widget], XmNy, &y, NULL);
    XtVaGetValues([contentView _widget], XmNwidth, &w, NULL);
    XtVaGetValues([contentView _widget], XmNheight, &h, NULL);

    fprintf(stderr, "widget size was (%d, %d), (%d, %d)\n",
          (int)x, (int)y, (int)w, (int)h);
          
    XtVaGetValues(_shell, XmNx, &x, NULL);
    XtVaGetValues(_shell, XmNy, &y, NULL);
    XtVaGetValues(_shell, XmNwidth, &w, NULL);
    XtVaGetValues(_shell, XmNheight, &h, NULL);

    fprintf(stderr, "shell size was (%d, %d), (%d, %d)\n",
          (int)x, (int)y, (int)w, (int)h);
          
    fprintf(stderr, "window size will change to (%d, %d), (%d, %d)\n",
          frameRect->origin.x, frameRect->origin.y,
          frameRect->size.width, frameRect->size.height);
#endif	/* RESIZE_DEBUG */

    frame.size.height = frameRect->size.height;
    frame.size.width = frameRect->size.width;
//    frame.origin.x = frameRect->origin.x;
//    frame.origin.y = frameRect->origin.y;
//    XtVaGetValues(widgetid, XmNwidth, &w, NULL);
//    XtVaGetValues(widgetid, XmNheight, &h, NULL);
//    frame.size.width = w;
//    frame.size.height = h;
//    frame.origin.x = oldFrame.origin.x;
//    frame.origin.y = oldFrame.origin.y;
    [contentView _windowRectChangedFrom:&oldFrame to:&frame];
    
    _inResizeFlag = NO;

    return self;
}

- (void)saveFrameToString:(char *)string
{
    sprintf( string, "%d %d %d %d", 
                     frame.origin.x,   frame.origin.y,
                     frame.size.width, frame.size.height);
    return;
}

- (void)setFrameFromString:(const char *)frameString
{
    sscanf(frameString, "%d%d%d%d", 
    			&(frame.origin.x), &(frame.origin.y),
    			&(frame.size.width), &(frame.size.height));
    return;
}

- read:(TypedStream*)typedStream
{
    int  anInt;
    char aChar;
    
    [super read:typedStream];
    NXReadRect(typedStream,&frame);
    objc_read_string(typedStream,&title);
    objc_read_object(typedStream,&contentView);
    [contentView getFrame:&_contentViewFrameRect];
    
    objc_read_type( typedStream, "i", &anInt );
    wFlags.style = anInt & 0xF;
    objc_read_type( typedStream, "i", &anInt );
    wFlags.backing = anInt & 0x3;
    objc_read_type( typedStream, "i", &anInt );
    wFlags.buttonMask = anInt & 0x7;
    
    objc_read_type( typedStream, "c", &aChar );
    wFlags.visible = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.isMainWindow = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.isKeyWindow = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.isPanel = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.hideOnDeactivate = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.dontFreeWhenClosed = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags.oneShot = (0 != aChar);
    
    objc_read_type( typedStream, "c", &aChar );
    wFlags2.deferred = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags2.docEdited = (0 != aChar);
    objc_read_type( typedStream, "c", &aChar );
    wFlags2.dynamicDepthLimit = (0 != aChar);
    
    return self;
}

- awake
{
    if (widgetid)
	return self;
    _inResizeFlag = NO;
    [self _initFrame];
    return self;
}

- awakeFromNib
{
   View	*custom;
 /*
  * what's implmented here is that which has to wait until all objects in the
  * heirarchy get a chance to receive their awake method. 
  */
    if ( [contentView isKindOf:[CustomView class]] ) {
        custom = contentView;
        contentView = [(CustomView *)custom nibInstantiate];
	[custom free];
    } else {
	[contentView awakeFromNib];
    }
    
    if (nil != contentView)
    {
	[contentView getFrame:&_contentViewFrameRect];
	[contentView _setWindow:self];
    }
    
    if ( wFlags.visible )
    {
	wFlags.visible = NO;
        [self orderFront:nil];
    }
// Note: _setWindowAreas will create the widgetid for contentView
//	and all subviews.   But this Window might be just a holder
//	for a view that goes into a box or something.   Thus, we
//	should defer no matter what the flag says -- Paul Kunz
//    if (wFlags2.deferred == NO) {
//	[self _setWindowAreas];
//	[self _realize];
//    }
    return self;
}


- makeKeyWindow
{
    return self;
}

- close
{
    [self orderOut:self];
    
    if (!wFlags.dontFreeWhenClosed)  [self free];
	
    return self;
}

- setFreeWhenClosed:(BOOL)flag
{
    wFlags.dontFreeWhenClosed = !flag;
    return self;
}

- makeFirstResponder:aResponder
{
    return self;
}


- setTitleAsFilename:(const char *)aString
{
    return self;
}

- firstResponder
{
	return self;
}

- disableDisplay
{
	displaying = NO;
	return self;
}

- reenableDisplay
{
	displaying = YES;
	return self;
}


- displayIfNeeded
{
 /* Probably don't need this method under X */
    return self;
}

- display
{
    return self;
}

- update
{
    if ( delegate && [delegate respondsTo:@selector(windowDidUpdate:) ] ) {
	[delegate windowDidUpdate:self];
    }
    return self;
}

- (int)setEventMask:(int)newMask
{
    return 0;
}

- (int)addToEventMask:(int)newEvents
{
    return 0;
}

- (int)removeFromEventMask:(int)oldEvents
{
    return 0;
}

- (int)eventMask
{
    return 0;
}

- sizeWindow:(NXCoord)width :(NXCoord)height
/*
	Checks for delegate method first, if delegate returns anything
	other than nil, execute resize code.
	NOTE: there is some code here, but this is not a fully
	functional method.  It doesn't handle subviews at all for
	instance!
 */
{
//	Here we would need to resize subviews of this window accordingly...
//	we may not support this...
    NXSize temp;
    temp.width = width;
    temp.height = height;
    if (delegate && [delegate respondsTo:@selector(windowWillResize:toSize:)] 
    	&& ([delegate perform:@selector(windowWillResize:)
				 with:self with:(id)&temp]))
    {
    
    	frame.size.width = width;
    	frame.size.height = height;
    	return self;
    }
    return self;
}

/* Returns the gstate of our contentView (at least in X-Windows)
*/ 
- (int)gState
{
    int	gstate;
    gstate = [contentView gState];
    if (!gstate) {
	// We really want a gstate for our contentView
	[contentView allocateGState];
	[contentView lockFocus];
	[contentView unlockFocus];
	gstate = [contentView gState];
    }
    return gstate;
}

- setBackingType:(int)bufferingType
{
    // NOT IMPLEMENTED
    return nil;
}

- (int)backingType
{
    return wFlags.backing;
}

- flushWindow
{
    [contentView _flushView];
    return self;
}

- disableFlushWindow
{
    return self;
}
- reenableFlushWindow
{
    return self;
}

- (BOOL)setFrameAutosaveName:(const char *)name
{
 /*
  * This fancy feature we don't need now.   Wasn't even in NeXTSTEP until 3.0 
  */
    return NO;
}
@end

