home *** CD-ROM | disk | FTP | other *** search
- /*
- ** LispText.m
- ** A Text object to drive the Lisp object.
- ** Lee Boynton, NeXT, Inc., 1989
- */
-
-
- #import <stdlib.h>
- #import <string.h>
- #import <sys/time.h>
- #import <appkit/Window.h>
- #import <appkit/publicWraps.h>
- #import "Lisp.h"
- #import "LispText.h"
-
- @implementation LispText
-
- #define DEFAULT_HISTORY 64
-
- #define CLOSE_PAREN (')')
- #define OPEN_PAREN ('(')
- #define NEW_LINE ('\n')
- #define CTRL_A (1)
- #define CTRL_B (2)
- #define CTRL_C (3)
- #define CTRL_D (4)
- #define CTRL_E (5)
- #define CTRL_F (6)
- #define CTRL_K (11)
- #define CTRL_N (14)
- #define CTRL_P (16)
-
-
- unsigned short lispFilter(unsigned short theChar, int flags,
- unsigned short charSet)
- {
- if (flags & NX_COMMANDMASK)
- theChar = 0;
- else if (theChar == NX_DELETE)
- theChar = NX_BACKSPACE;
- else if (theChar == NX_CR || theChar == NEW_LINE)
- theChar = 0;
- else if (theChar == '\t')
- theChar = '\t';
- else if (theChar < ' ')
- theChar = 0;
- return theChar;
- }
-
- static void initLispCategoryTable(id self)
- {
- static unsigned char lispTable[256];
- unsigned char punctuationCategory, normalCategory;
- unsigned char nullCategory, whiteSpaceCategory;
- int i;
-
- bcopy([self charCategoryTable],lispTable,sizeof(lispTable));
- nullCategory = lispTable[0];
- punctuationCategory = lispTable[';'];
- normalCategory = lispTable['a'];
- whiteSpaceCategory = lispTable[' '];
- lispTable['!'] = normalCategory;
- lispTable['&'] = normalCategory;
- lispTable['('] = punctuationCategory;
- lispTable[')'] = punctuationCategory;
- lispTable['*'] = normalCategory;
- lispTable['+'] = normalCategory;
- lispTable['-'] = normalCategory;
- lispTable['.'] = normalCategory;
- lispTable['/'] = normalCategory;
- lispTable[':'] = normalCategory;
- lispTable['<'] = normalCategory;
- lispTable['='] = normalCategory;
- lispTable['>'] = normalCategory;
- lispTable['?'] = normalCategory;
- lispTable['@'] = normalCategory;
- lispTable['['] = punctuationCategory;
- lispTable['\\'] = normalCategory;
- lispTable[']'] = punctuationCategory;
- lispTable['{'] = punctuationCategory;
- lispTable['|'] = normalCategory;
- lispTable['}'] = punctuationCategory;
- for(i=128;i<256;i++) lispTable[i] = nullCategory;
- [self setCharCategoryTable:lispTable];
- }
-
- + newFrame:(NXRect *)frameRect text:(char *)theText alignment:(int)align
- {
- int i;
- self = [super newFrame:frameRect text:theText alignment:align];
- [self setCharFilter:lispFilter];
- [self setOpaque:YES];
- [self setOverstrikeDiacriticals:NO];
- initLispCategoryTable(self);
- inputPosition = 0;
- maxHistory = DEFAULT_HISTORY;
- history = (char **)malloc(maxHistory*sizeof(char*));
- for (i=0; i<maxHistory; i++) history[i] = NULL;
- historyHead = 0;
- connectedToLisp=NO;
- theLisp = [Lisp new];
- [theLisp setDelegate:self];
- return self;
- }
-
- - free
- {
- [theLisp free];
- return [super free];
- }
-
- - lispOutput:(const char *)theString
- {
- int offset;
- NXSelPt selStart, selEnd;
- if (!theString) return self;
- offset = strlen(theString);
- if (!connectedToLisp) {
- int i = [self textLength];
- [self setSel:0 :i];
- [self replaceSel:theString];
- inputPosition = offset;
- [window makeFirstResponder:self];
- connectedToLisp=YES;
- } else {
- [self getSel:&selStart :&selEnd];
- [self setSel:inputPosition :inputPosition];
- [self replaceSel:theString];
- inputPosition += offset;
- [self setSel:selStart.cp+offset :selEnd.cp+offset];
- }
- [self scrollSelToVisible];
- return self;
- }
-
- /*
- ** NXBGetc - a text stream macro to get the previous character.
- */
-
- typedef struct {
- id text;
- NXTextBlock *block;
- } textInfo;
-
- static char getPrevious(NXStream *s)
- {
- textInfo *info = (textInfo *) s->info;
-
- NXTextBlock *block = info->block->prior;
- if (!block) {
- return EOF;
- }
- s->buf_base = block->text;
- s->buf_ptr = s->buf_base + block->chars;
- s->offset -= block->chars;
- info->block = block;
- return *(--s->buf_ptr);
- }
-
- #define NXBGetc(s) \
- (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
- *(--((s)->buf_ptr)) )
-
-
- int findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos)
- {
- int count = 1;
- char ch;
- NXStream *s = [text stream];
- NXSeek(s, curPos, NX_FROMSTART);
- while ((ch = NXBGetc(s)) != EOF) {
- if (ch == thatChar && !(--count))
- return NXTell(s);
- else if (ch == thisChar)
- count++;
- }
- return -1;
- }
-
- int findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos)
- {
- int count = 1;
- char ch;
- NXStream *s = [text stream];
- NXSeek(s, curPos, NX_FROMSTART);
- while ((ch = NXGetc(s)) != EOF) {
- if (ch == thatChar && !(--count))
- return NXTell(s);
- else if (ch == thisChar)
- count++;
- }
- return -1;
- }
-
- void highlightChar(id text, int charPosition)
- {
- NXSelPt selStart, selEnd;
- [text getSel:&selStart :&selEnd];
- [text setSel:charPosition :charPosition+1];
- [[text window] flushWindow];
- NXPing();
- [[text window] disableFlushWindow];
- [text setSel:selStart.cp :selEnd.cp];
- [[text window] reenableFlushWindow];
- }
-
- int searchBackwardsForChar(id text, char theChar, int curPos)
- {
- char ch;
- NXStream *s = [text stream];
- NXSeek(s, curPos, NX_FROMSTART);
-
- while ((ch = NXBGetc(s)) != EOF) {
- if (ch == theChar)
- return NXTell(s);
- }
- return -1;
- }
-
- void insertChar(id self, char c, int pos)
- {
- char buf[2];
- buf[0] = c;
- buf[1] = 0;
- [self setSel:pos :pos];
- [self replaceSel:buf];
- [self scrollSelToVisible];
- }
-
- static void addToHistory(id self, char *buf)
- {
- int i = (self->historyHead++) % self->maxHistory;
- if (self->history[i])
- free(self->history[i]);
- self->history[i] = buf;
- }
-
- static char *getHistory(id self, int index)
- {
- int i = index % self->maxHistory;
- return self->history[i];
- }
-
- - keyDown:(NXEvent *)theEvent
- {
- static int history_index=0;
- int i, lastPos = [self textLength];
- unsigned short val;
- NXSelPt selStart, selEnd;
- if (!connectedToLisp) {
- NXBeep();
- return self;
- }
- [self getSel:&selStart :&selEnd];
- val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
- switch (val) {
- case CLOSE_PAREN:
- if (selStart.cp < inputPosition)
- [self setSel:lastPos :lastPos];
- i = findMatchingOpenParen(self,CLOSE_PAREN,OPEN_PAREN,selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- return self;
- case NX_DELETE:
- case NX_BACKSPACE:
- if (selEnd.cp == selStart.cp) {
- if (lastPos <= inputPosition || selStart.cp <= inputPosition) {
- NXBeep();
- return self;
- }
- } else {
- if (selEnd.cp <= inputPosition ||
- selStart.cp <= inputPosition) {
- NXBeep();
- return self;
- }
- }
- break;
- case NX_CR:
- case NEW_LINE:
- if (selEnd.cp >= inputPosition) {
- char *buf;
- i = lastPos - inputPosition;
- buf = (char *)malloc(i+2);
- if (i)
- [self getSubstring:buf start:inputPosition length:i];
- buf[i] = NEW_LINE;
- buf[i+1] = 0;
- inputPosition = lastPos+1;
- [theLisp inputLisp:buf];
- if (i) {
- buf[i] = 0;
- addToHistory(self,buf);
- history_index = historyHead;
- }
- [self setSel:lastPos :lastPos];
- insertChar(self,NEW_LINE,lastPos);
- return self;
- } else {
- int len = selEnd.cp-selStart.cp;
- if (len) {
- char *buf = (char *)malloc(len+1);
- [self getSubstring:buf start:selStart.cp length:len];
- buf[len] = 0;
- [self setSel:lastPos :lastPos];
- [self replaceSel:buf];
- free(buf);
- [self scrollSelToVisible];
- } else
- [self setSel:lastPos :lastPos];
- return self;
- }
- case CTRL_C:
- [self setSel:lastPos :lastPos];
- [theLisp interruptLisp];
- return self;
- break;
- case CTRL_A:
- [self setSel:inputPosition :inputPosition];
- return self;
- case CTRL_E:
- [self setSel:lastPos :lastPos];
- return self;
- case CTRL_B:
- if (selStart.cp < inputPosition)
- [self setSel:inputPosition :inputPosition];
- else if (selStart.cp != selEnd.cp)
- [self setSel:selStart.cp :selStart.cp];
- else if (selStart.cp > inputPosition)
- [self setSel:selStart.cp-1 :selStart.cp-1];
- else
- NXBeep();
- return self;
- case CTRL_F:
- if (selEnd.cp < inputPosition)
- [self setSel:inputPosition :inputPosition];
- else if (selStart.cp != selEnd.cp)
- [self setSel:selEnd.cp :selEnd.cp];
- else if (selEnd.cp < lastPos)
- [self setSel:selEnd.cp+1 :selEnd.cp+1];
- else
- NXBeep();
- return self;
- case CTRL_D:
- if (selStart.cp >= inputPosition) {
- if (selStart.cp == selEnd.cp)
- if (selEnd.cp >= lastPos) {
- NXBeep();
- return;
- } else
- [self setSel:selEnd.cp :selEnd.cp+1];
- [self replaceSel:""];
- } else
- NXBeep();
- return self;
- case CTRL_K:
- if (selStart.cp >= inputPosition) {
- if (inputPosition != lastPos)
- [self setSel:selStart.cp :lastPos];
- [self replaceSel:""];
- } else
- NXBeep();
- return self;
- case CTRL_P: {
- char *s;
- if (!historyHead ||
- (history_index <= (historyHead-self->maxHistory)))
- return self;
- s = getHistory(self, history_index-1);
- if (!s) return self;
- history_index--;
- [window disableFlushWindow];
- [self setSel:inputPosition :lastPos];
- [self replaceSel:s];
- [self scrollSelToVisible];
- [window reenableFlushWindow];
- [window flushWindow];
- return self; }
- case CTRL_N:
- {
- char *s;
- if (!historyHead || (history_index >= (historyHead)))
- return self;
- s = getHistory(self, history_index);
- if (s)
- s = getHistory(self, ++history_index);
- [window disableFlushWindow];
- [self setSel:inputPosition :lastPos];
- [self replaceSel:s];
- [self scrollSelToVisible];
- [window reenableFlushWindow];
- [window flushWindow];
- return self; }
- default:
- if (selStart.cp < inputPosition)
- [self setSel:lastPos :lastPos];
- break;
- }
- return [super keyDown:theEvent];
- }
-
- - clear:sender
- {
- NXSelPt selStart, selEnd;
- [self getSel:&selStart :&selEnd];
- if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
- return self;
- else
- return [super clear:sender];
- }
-
- - cut:sender
- {
- NXSelPt selStart, selEnd;
- [self getSel:&selStart :&selEnd];
- if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
- return self;
- else
- return [super cut:sender];
- }
-
- - paste:sender
- {
- int size = [self textLength];
- NXSelPt selStart, selEnd;
- [self getSel:&selStart :&selEnd];
- if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
- [self setSel:size :size];
- return [super paste:sender];
- }
-
- - mouseDown: (NXEvent *) theEvent
- {
- BOOL doubleClick;
- doubleClick = (theEvent->data.mouse.click == 2);
- [super mouseDown:theEvent];
- if (theEvent->data.mouse.click == 2) {
- char theChar;
- int i;
- NXSelPt selStart, selEnd;
- [self getSel:&selStart :&selEnd];
- [self getSubstring:&theChar start:selStart.cp length:1];
- if (theChar == OPEN_PAREN) {
- i = findMatchingCloseParen(self,theChar,CLOSE_PAREN,selEnd.cp+1);
- if (i >= 0)
- [self setSel:selStart.cp :i];
- } else if (theChar == CLOSE_PAREN) {
- i = findMatchingOpenParen(self,theChar,OPEN_PAREN,selEnd.cp-1);
- if (i >= 0)
- [self setSel:i :selEnd.cp];
- }
- }
- return self;
- }
-
- - clearAll:sender
- {
- [self setSel:0 :[self textLength]];
- [super clear:self];
- inputPosition = 0;
- return self;
- }
-
- - inputLisp:(const char *)theString
- {
- return [theLisp inputLisp:theString];
- }
-
-
- - interruptLisp
- {
- return [theLisp interruptLisp];
- }
-
-
-
- @end
-
-
-
-
-
-
-