/*
**  ScrollingTextDisplay, class to implement scrolling resizable text
**  from an input stream. Scrolling is both vertical and horizontal.
*/

#import <appkit/Text.h>
#import <appkit/Font.h>
#import <appkit/Window.h>
#import <appkit/ScrollView.h>
#import <stdio.h>
#import "ScrollDis.h"

#define MAXWIDTH  (1.0e38)
#define MAXHEIGHT (1.0e38)

char *readline (st, buf, size)
NXStream *st;
char buf[];
int size;
{
	char *pt;
	int c;
	
	pt = &buf[0];
	for (c = NXGetc (st); !NXAtEOS (st) && (c != '\n'); c = NXGetc (st)) {
		*pt++ = c;
		}
	if (NXAtEOS (st)) return NULL;
	else {
		*pt++ = c;
		*pt = 0;
		return buf;
		}
}

@implementation ScrollingTextDisplay

+ newFrame:(const NXRect *)frm title:(char *)title
{
  NXRect scrollFrame;
 
  self = [super new]; 
  font = [Font newFont:"Courier" size:12.0];
  text = [Text new];
  [text setOpaque:YES];  
  [text setEditable: NO];
  [text setSelectable: YES];
  [text setNoWrap];
  textFrame.origin.x = textFrame.origin.y = 0.0;
  textFrame.size.width = [font getWidthOf:"X"] * 80.0;
  textFrame.size.height = [text lineHeight] * 24.0;
  [text setFrame:&textFrame];
  scrollFrame.origin.x = frm->origin.x;
  scrollFrame.origin.y = frm->origin.y;
  [ScrollView getFrameSize:&(scrollFrame.size)
  		forContentSize:&(textFrame.size)
		horizScroller:YES
		vertScroller:YES
		borderType:NX_LINE];
  [self setFrame:&scrollFrame];
  [self setFont:font];
  [self setDocView:text];
  [self setBorderType:NX_LINE];  
  [self setVertScrollerRequired:YES];
  [self setHorizScrollerRequired:YES];

  win = [Window newContent:&scrollFrame
	        style:NX_TITLEDSTYLE
		backing:NX_BUFFERED 
                buttonMask:NX_ALLBUTTONS
		defer:YES];

  [win setTitle: title];
  [win setContentView:self];
  [win setBackgroundGray:NX_WHITE];
  [win setFreeWhenClosed:NO];
  [[text superview] setAutoresizeSubviews:YES];

  [[[[text notifyAncestorWhenFrameChanged:YES] 
           setVertResizable:YES] 
           setHorizResizable:YES]
	   setDelegate:self];

  [text setAutosizing:NX_WIDTHSIZABLE];

  {
    NXSize size = {MAXWIDTH, MAXHEIGHT};
    [text setMaxSize:&size];
    [self getContentSize:&size];
    [text setMinSize:&size];  
  }
  return (self);
}  

- show
{
	[win display];
	[win makeKeyAndOrderFront:self];
	return self;
}

- free
{
	[font free];
	[text free];
	/* must remove window's content view (self) first, because
	   freed window first frees its content view, thus
	   recursing into oblivion, re-freeing freed objects. */
	[win setContentView:nil];
	[win free];
	[super free];
	return self;
}

/* readText reads in the contents of the file into the text view.
** Note that some work is required to readjust the size of the text
** view after the text is read in --- After the adjustment, the text view
** lets the scroll view know of the change, and the scroll view recalculates
** and redraws it's scroll bar.
*/
- readText:(NXStream *)st burst:(int)burst 
{
  float w, h, maxwidth;
  int where, strlen(), chars, maxchars;
  char buf[512];
  id docview;
  
  docview = [contentView docView];
  [text setText:""];
  if (burst == YES) {
	[text readText:st];
	[text sizeToFit];
	[text display];
	return self;
	}
  maxchars = 0;
  while (readline (st, buf, 512)) {
  	where = [text textLength];
	[text setSel:where :where];
	[text replaceSel:buf];
	if ((chars = strlen (buf)) > maxchars) {
		maxchars = chars;
		maxwidth = [font getWidthOf: buf];
		[text	getMinWidth: &w
			minHeight: &h
			maxWidth: maxwidth
			maxHeight: MAXHEIGHT];
		[text sizeTo:maxwidth :h];
		}
	{
		NXRect rect1, rect2;
		float diff;
		[docview getFrame:&rect1];
		[contentView getFrame:&rect2];
		diff = rect1.size.height - rect2.size.height;
		if (diff > 0) {
			rect1.origin.y = -diff;
			[docview setFrame:&rect1];
			[self reflectScroll:contentView];
			}
		}
	[text display];
  	}
  [win flushWindow];
  return (self);
}

// To get around Text strangeness. This code assures that on a resize of
// the text is resized and redrawn correctly. Note that the below method
// cannot be in a subclass of Text as renewRuns:text:frame:tag: invokes
// sizeTo::.

- sizeTo:(NXCoord)w :(NXCoord)h 
{
  NXRect frm;
  NXSelPt start, end;

  [super sizeTo:w:h];
  [text getSel:&start :&end];
  [text getFrame:&frm];      
  [text renewRuns:NULL text:NULL frame:&frm tag:0];
  [text setSel:start.cp :end.cp];
  return self;
}

// setFont: sets the text font and also remembers what it has been set to.
// It also has some workarounds to get around a few Text problems.

- setFont:fontID
{
  NXSelPt selBeg, selEnd;
  BOOL insertedText = NO;

  if (fontID) {
    [[self window] disableFlushWindow]; // prevent many flashes
    if ([text textLength] == 0) {
      insertedText = YES;
      [text setText:" "];
    }
    [text getSel:&selBeg :&selEnd];     // get selection
    [text setSel:0 :0];  		// set the selection to nothing
    [text setFont:fontID];		
    [text setSel:selBeg.cp :selEnd.cp]; // restore the original selection
    if (insertedText) [text setText:""];
    [[self window] reenableFlushWindow];// allow window to update
    [[self window] display];		
  } else fprintf (stderr, "setFont: given no font pointer\n");

  return self;
}

@end
