
/* Generated by Interface Builder */

static char copyright[] = "Copyright 1990 by The MITRE Corporation.";
/* John D. Ramsdell - June 1990
 *
 * Copyright 1990 by The MITRE Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 1, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* 
 * MAXHISTORYTEXT gives the maximum amount of text allowed in the history 
 * text window.  When the limit is exceeded, the first MAXHISTORYTEXT/2
 * characters are deleted.
*/

#if !defined MAXHISTORYTEXT
#define MAXHISTORYTEXT (1 << 18)
#endif

#import "EvalDelegate.h"
#import "EvalListener.h"
#import "startSlaveProcess.h"
#import <appkit/Application.h>
#import <appkit/Font.h>
#import <defaults/defaults.h>
#import <appkit/appkit.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
  
static NXCharFilterFunc defaultEditorFilter;

/* This makes the inputText view end input when ENTER is typed. */
#define NX_ENTER 3
static unsigned short 
inputEditorFilter(unsigned short theChar, int flags, unsigned short charSet)
{
  if (flags & NX_COMMANDMASK) return 0;
  if (theChar == NX_ENTER) return NX_RETURN;
  return (*defaultEditorFilter) (theChar, flags, charSet);
}   

FILE *processOut;		/* Place to write to the slave process. */

@implementation EvalDelegate
  
void startEvalListener(EvalDelegate *evalDelegate)
{
  EvalListener *evalListener;	/* Start listener for remote messages. */
  evalListener = [EvalListener new];
  [evalListener setDelegate:evalDelegate];
  [evalListener checkInAs:"Twin"];
  [evalListener addPort];
}

Font *getFont(void)
{
  const char *fontname;
  float fontsize;
  
  static NXDefaultsVector twinDefaults = {
    {"SlaveCommand", "t"},
    {"Columns", "80"},
    {"NXFixedPitchFont", "Ohlfs"},
    {"NXFixedPitchFontSize", "10.0" },
    {NULL}
  };
  NXRegisterDefaults("Twin", twinDefaults);

  fontname = NXGetDefaultValue("Twin", "NXFixedPitchFont");
  fontsize = atof(NXGetDefaultValue("Twin", "NXFixedPitchFontSize"));

  return [Font newFont:fontname size:fontsize];
}

- setHistoryScrollView:anObject
{
  NXCoord widthInChars;
  NXRect historyRect, windowRect;
  id window;
  NXCoord width;
  
  historyScrollView = anObject;
  historyText = [historyScrollView docView];

  displayFont = getFont();

  /* Set width of windows. */
  widthInChars = atoi(NXGetDefaultValue("Twin", "Columns"));
  if (widthInChars < 20.0) widthInChars = 20.0;
  width = widthInChars
    * ([displayFont getWidthOf:"  "] - [displayFont getWidthOf:" "]);
  [historyText getFrame:&historyRect];
  window = [historyText window];
  [window getFrame:&windowRect];
  width += NX_WIDTH(&windowRect) - NX_WIDTH(&historyRect);
  [window sizeWindow:width :NX_HEIGHT(&windowRect)];

  [historyText setFont:displayFont];
  startEvalListener(self);

  return self;
}

- setInputScrollView:anObject
{
  inputScrollView = anObject;
  inputText = [inputScrollView docView];
  defaultEditorFilter = [inputText charFilter];
  [inputText setCharFilter:inputEditorFilter];
  [inputText setDelegate:self];
  return self;			/* Font set in appDidInit. */
}

static BOOL stringVisibleMode = TRUE;

- toggleStringVisibleMode:sender
{
  stringVisibleMode = !stringVisibleMode;
  return self;
}

void selectEnd(id text)
{
  int length;
  length = [text textLength];
  [text setSel:length :length];
}

- showString:(char *)string	/* Add text to history view. */
{
  int length;
  length = [historyText textLength];
  if (length >= MAXHISTORYTEXT) {
    [historyText setSel:0 :(MAXHISTORYTEXT>>1)-1];
    [historyText setEditable:YES];
    [historyText delete:self];	/* Delete first half of text. */
    return [self showString:string]; /* Try again. */
  }
  [historyText setSel:length :length];
  [historyText setEditable:YES];
  [historyText replaceSel:string];
  [historyText setEditable:NO];
  if (stringVisibleMode) [historyText scrollSelToVisible];
  selectEnd(inputText);
  return self;
}

- sendString:(char *)string	/* Send string to the slave process. */
{
  if (EOF == fputs(string, processOut)) {
    fprintf(stderr, "\nCannot write.\n");
    [NXApp terminate:self];
  }
  fflush(processOut);
  return self;
}

- showPasteboard:sender		/* Copy pasteboard to history view. */
{
  int length;
  length = [historyText textLength];
  if (length >= MAXHISTORYTEXT) {
    [historyText setSel:0 :(MAXHISTORYTEXT>>1)-1];
    [historyText setEditable:YES];
    [historyText delete:self];	/* Delete first half of text. */
    return [self showPasteboard:sender]; /* Try again. */
  }
  [historyText setSel:length :length];
  [historyText setEditable:YES];
  [historyText paste:self];
  [historyText setEditable:NO];
  selectEnd(inputText);
  return self;
}

- evalPasteboard:sender		/* Copy pasteboard to history view */
{				/* and send text to the slave process. */
  int stringStart, stringLength;
  char *textBuffer;
  stringStart = [historyText textLength];
  [self showPasteboard:self];
  stringLength = [historyText textLength] - stringStart + 1;
  textBuffer = malloc(stringLength);
  [historyText getSubstring:textBuffer start:stringStart length:stringLength];
  [self sendString:textBuffer];
  free (textBuffer);
  return self;
}

- evalSelection:sender		/* Evaluate the selection in the */
{				/* history view. */
  NXSelPt selStart, selEnd; int selLength;
  
  if ([[[historyText window] firstResponder] isDescendantOf:historyText]) {
    /* getSel fails unless historyText is in the responder's view chain. */
    [historyText getSel:&selStart :&selEnd];
    selLength = selEnd.cp - selStart.cp;

    if (selLength > 0) {
      [historyText copy:self];
      return [self evalPasteboard:self];
    }
  }
  NXBeep();			/* Beep on failure. */
  return self;
}

- evalInput:sender		/* Cut text from input view into */
{				/* the pasteboard and perform */
  int length;			/* evalPasteboard. */
  length = [inputText textLength];
  if (length > 0) {
    [inputText setSel:length :length];
    [inputText replaceSel:"\n"]; /* Add a return. */
    [inputText selectAll:self];
    [inputText cut:self];
    return [self evalPasteboard:self];
  }
  [inputText selectAll:self];
  return self;
} 

- sendInterrupt:sender
{
  (void) interruptSlaveProcess();
  return self;
}

/* As a listener delegate. */

- (int)evalString:(char *)string /* Copies string to historyText */
{				/* and then sends string to slave. */
  [self showString:string];	/* Used to respond to a remote message. */
  [self sendString:string];
  return 0;			/* Signal success. */
}

/* As an application delegate. */

- appDidInit:sender
{
  [inputText setFont:displayFont];
  [[inputText window] makeKeyAndOrderFront:self];
  [inputText selectAll:self];
  processOut = startSlaveProcess(self);
  return self;
}
 
- appDidBecomeActive:sender
{
  [[inputText window] makeKeyAndOrderFront:self];
  return self;
}

/* As a Text delegate. */

- textDidEnd:sender endChar:(unsigned short)whyEnd
{
  if (whyEnd == NX_RETURN)
    return [self evalInput:self];
  else
    return self;
}

@end
