This is MiscEmacsText.m in view mode; [Download] [Up]
/**
** EvalText.m
**
** A text subclass which provides emacs key bindings
** and "colored" program text.
**/
#import "MiscEmacsText.h"
#import <stdlib.h>
#import <strings.h>
#import <appkit/appkit.h>
#import <defaults/defaults.h>
#import <libc.h>
#import <ctype.h>
#define ESC (27)
#define CTRL_SPACE (0)
#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_L (12)
#define CTRL_N (14)
#define CTRL_O (15)
#define CTRL_P (16)
#define CTRL_S (19)
#define CTRL_V (22)
#define CTRL_W (23)
#define CTRL_X (24)
#define CTRL_Y (25)
/**
** keyWords[] is a structure used by format: sender to
** identify keyWords in transcript text
**/
static struct
{ char * word ;
int len ;
} keyWords[] =
{ {"int",3},
{"char",4},
{"float",5},
{"double",6},
{"struct",6},
{"union",5},
{"long",4},
{"short",5},
{"unsigned",8},
{"auto",4},
{"extern",6},
{"register",8},
{"typedef",7},
{"static",6},
{"goto",4},
{"return",6},
{"sizeof",6},
{"break",5},
{"continue",8},
{"if",2},
{"else",4},
{"for",3},
{"do",2},
{"while",5},
{"switch",6},
{"case",4},
{"default",7},
{"entry",5}, /* is this a keyword in ansi C ? */
{"const",5},
{"id",2},
{"void",4},
{NULL,0}
} ;
/**
** Fragments of the following procedures were borrowed from
** Lee boynton's LispExample program, then hacked to bits.
**/
static char killedText[10000] ; // buffer for "killed" text...
// since it is statically allocated, this thing could blow up
// if > 10000 chars are written to the kill buffer. If I had
// I'd make this grow as needed....
static BOOL useEmacsBindings = 1 ;
typedef struct {
id text;
NXTextBlock *block;
} textInfo;
BOOL evalGets(NXStream *aStream, char * aBuf)
{ // Procedure to get the next line (or remainder of
// stream) from a stream. The newline is discarded.
// If newline is present, returns YES, else if instead
// the end of stream is found, returns NO (but aBuf may
// still contain data).
int c,i = 0;
c = NXGetc(aStream) ;
do
{ if(c == -1) // end of stream
{ aBuf[i] = '\0' ;
return NO ;
}
else if(c == '\n')
{ aBuf[i] = '\0' ;
return YES ;
}
else
{ aBuf[i++] = (char) c ;
c = NXGetc(aStream) ;
}
} while(1) ;
}
static char getPrevious(NXStream *s)
{ // NXBGetc - a text stream macro to get the previous character.
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];
}
@implementation MiscEmacsText:Text
// macros which write rtf to a stream called "cookedStream"
// for creating fancy text for various code categories
#define PREPROC NXPrintf(cookedStream,"\\f0%s\\cf0%s ",\
fontStuff[0][0],fontStuff[0][2])
#define DIRECTIVE NXPrintf(cookedStream,"\\f1%s\\cf1%s ",\
fontStuff[1][0],fontStuff[1][2])
#define METHODDEF NXPrintf(cookedStream,"\\f2%s\\cf2%s ",\
fontStuff[2][0],fontStuff[2][2])
#define COMMENT NXPrintf(cookedStream,"\\f3%s\\cf3%s ",\
fontStuff[3][0],fontStuff[3][2])
#define STRING NXPrintf(cookedStream,"\\f4%s\\cf4%s ",\
fontStuff[4][0],fontStuff[4][2])
#define KEYWORD NXPrintf(cookedStream,"\\f5%s\\cf5%s ",\
fontStuff[5][0],fontStuff[5][2])
#define NORMAL NXPrintf(cookedStream,"\\f6%s\\cf6%s ",\
fontStuff[6][0],fontStuff[6][2])
#define TEXTDEFAULTKNT 7 /* number of text categories */
- awakeFromNib ;
{ // Do some setup work...I am my own delegate
// [self setDelegate: self] ;
NXAtom textTypes[5];
textTypes[0] = NXRTFPboardType;
textTypes[1] = NXAsciiPboardType;
textTypes[2] = NULL;
[self registerForDraggedTypes:textTypes count:2];
return self;
}
- clearMessage: sender ;
{ // Clear the message in the transcript. Called by keydown
// via a delayed message send.
//
// [window message: ""] ;
return self ;
}
- format: sender ;
{ // Color and set font attributes for text in the
// transcript. If sender responds to
// to the methodList: method, (which must should return a List),
// then allocates and adds an EvalString with the first line
// of each method name to this methodlist.
char textBuf[1024] ;
char fontStuff[TEXTDEFAULTKNT][3][40], defName[12] ;
const char *defaults ;
int i, j, keyWordLen, charNum, braceLevel = 0 ;
BOOL inString = NO, inComment = NO, inSlashSlashComment = NO,
inMethodDef = NO, inPreProc = NO, newline;
NXStream *rawStream, *cookedStream ;
id methodList = nil ;
/*
if([sender respondsTo: @selector(methodList)])
{ methodList = [sender methodList] ;
}
*/
rawStream = [self stream] ;
cookedStream = NXOpenMemory(NULL,0,NX_READWRITE) ;
NXPrintf(cookedStream,"{\\rtf0\\ansi{\\fonttbl") ;
// get the font information from defaults database
for(i = 0 ; i < TEXTDEFAULTKNT ; i++)
{ // Manufacture default name
sprintf(defName,"text%1d",i) ;
defaults = NXGetDefaultValue([NXApp appName],defName) ;
// parse the default value
sscanf(defaults,"%s %s %s %s",
textBuf, fontStuff[i][0], fontStuff[i][1],fontStuff[i][2]) ;
NXPrintf(cookedStream,"\\f%1d\\fnil %s;", i,textBuf) ;
}
NXPrintf(cookedStream,"}\n") ;
NXPrintf(cookedStream,"{\\colortbl") ;
for(i = 0 ; i < TEXTDEFAULTKNT ; i++)
{ NXPrintf(cookedStream,fontStuff[i][1]) ;
NXPrintf(cookedStream,";") ;
}
NXPrintf(cookedStream,"}\n") ;
charNum = 0 ;
NORMAL ;
do
{ NXSeek(rawStream, charNum, NX_FROMSTART) ;
newline = evalGets(rawStream,textBuf) ;
inString = NO ; // ANSI C forbids newline in string
// now examine the string, adding rtf directives as required
for(i = 0 ; textBuf[i] ;)
{ switch(textBuf[i])
{ case '/':
if(inString)
NXPutc(cookedStream,textBuf[i++]) ;
else if(textBuf[i + 1] == '*' || textBuf[i+1] == '/')
{ if(!inComment)
{ COMMENT ;
inComment = YES ;
if(textBuf[i+1] == '/') // behaves specially!
inSlashSlashComment = YES ;
}
NXPutc(cookedStream,textBuf[i++]) ;
}
else
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '*':
if(inComment && textBuf[i+1] == '/' && !inSlashSlashComment)
{ NXPutc(cookedStream,textBuf[i++]) ;
NXPutc(cookedStream,textBuf[i++]) ;
if(inMethodDef)
METHODDEF ;
else
NORMAL ;
inComment = NO ;
}
else
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '"':
if(inComment || inPreProc)
NXPutc(cookedStream,textBuf[i++]) ;
else if(inString)
{ NXPutc(cookedStream,textBuf[i++]) ;
if(textBuf[i - 2] != '\\')
{ inString = NO ;
NORMAL ;
}
}
else
{ if(i == 0 || textBuf[i - 1] != '\\')
{ inString = YES ;
STRING ;
}
NXPutc(cookedStream,textBuf[i++]) ;
}
break ;
case '}' :
if(!inComment && !inString && !inPreProc)
{ if(braceLevel > 0) // don't allow this to go negative
braceLevel-- ;
}
// must be escaped for rtf
NXPutc(cookedStream,'\\') ;
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '\\':
// must be escaped for rtf
NXPutc(cookedStream,'\\') ;
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '{' :
if(!inComment && !inString && !inPreProc)
braceLevel++ ;
if(braceLevel == 1 && inMethodDef)
{ inMethodDef = NO ;
NORMAL ;
}
NXPutc(cookedStream,'\\') ;
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '#':
if(!inComment && i == 0)
{ // a hash in column 0 means a preprocessor line
PREPROC ;
inPreProc = YES ;
}
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '@':
if(!inString && !inComment && !inPreProc)
{ // then this is a compiler directive
DIRECTIVE ;
// if((i == 0) && methodList) // if a compiler directive is in
// column 0, put it into the methodList
//
//
// [methodList addObject: [[EvalString alloc] init: textBuf]] ;
//
//
NXPutc(cookedStream,textBuf[i++]) ;
while(isalnum(textBuf[i]))
NXPutc(cookedStream,textBuf[i++]) ;
NORMAL ;
}
else
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case '-':
case '+':
if(!inComment && !inString && !inPreProc && braceLevel == 0)
{ for(j = 0 ; j < i ; j++)
if(!isspace(textBuf[j]) && textBuf[j] != '\\') break ;
if(j == i)
{ // this is not a truly accurate way of finding the beginning of a
// method def, but it will work "most" of the time...
inMethodDef = YES ;
METHODDEF ;
//
//
// if(methodList)
// [methodList addObject: [[EvalString alloc] init: textBuf]] ;
//
//
//
}
}
if(textBuf[i] == '-') // must be escaped
NXPutc(cookedStream,'\\') ;
NXPutc(cookedStream,textBuf[i++]) ;
break ;
case ' ':
case '\t':
NXPutc(cookedStream,textBuf[i++]) ;
break ;
default:
if(!inComment && !inString && !inMethodDef && !inPreProc &&
(i == 0 || !(isalnum(textBuf[i-1]) || textBuf[i-1] == '_')))
{ // check for a keyword
for(j = 0 ; keyWordLen = keyWords[j].len ; j++)
{ if(!strncmp(&textBuf[i],keyWords[j].word,keyWordLen))
{ int followChar = i + keyWordLen ;
int isKeyWord = !(isalnum(textBuf[followChar])
|| textBuf[followChar]=='_') ;
if(isKeyWord)
KEYWORD ;
NXPrintf(cookedStream,"%s\n",keyWords[j].word) ;
if(isKeyWord)
NORMAL ;
i += keyWordLen ;
break ;
}
}
if(!keyWordLen) // no keyWord match found
NXPutc(cookedStream,textBuf[i++]) ;
}
else
NXPutc(cookedStream,textBuf[i++]) ;
break ;
}
}
// check for a preprocessor continuation line
if(inPreProc && textBuf[i - 1] != '\\')
{ inPreProc = NO ;
NORMAL ;
}
// terminate a slash-slash style comment
if(inSlashSlashComment)
{ inSlashSlashComment = inComment = NO ;
if(inMethodDef)
METHODDEF ;
else
NORMAL ;
}
if(newline) // output a quoted newline
{ NXPutc(cookedStream,'\\') ;
NXPutc(cookedStream,'\n') ;
}
charNum += strlen(textBuf) + 1;
} while(!NXAtEOS(rawStream)) ;
NXPutc(cookedStream,'}') ;
NXSeek(cookedStream, 0, NX_FROMSTART) ;
[self readRichText: cookedStream] ;
NXCloseMemory(cookedStream,NX_FREEBUFFER) ;
return self ;
}
#define MARK_UNSET -1
- keyDown:(NXEvent *)theEvent
{ // provide some emacs-style key bindings. We make
// the assumption that we are subview of a scrollview.
int i ;
static unsigned short val = '\0', lastChar = '\0', nextChar ;
static BOOL firstKey = TRUE, definingMacro = NO,
insertChar, replayingMacro = NO ;
static long mark = MARK_UNSET ;
NXSelPt selStart, selEnd;
static NXStream * macroStream ;
if(!useEmacsBindings)
return [super keyDown: theEvent] ;
if(firstKey) // make sure killedText is empty
{ killedText[0] = '\0' ;
firstKey = FALSE ;
macroStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
}
[self getSel:&selStart :&selEnd];
do
{ lastChar = val ; // val was set in "previous" call of keyDown..
val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
if(definingMacro)
NXPutc(macroStream,theEvent->data.key.charCode) ;
insertChar = YES ;
switch ((unsigned char) val)
{ case ESC: // don't want ESC to cause a beep
insertChar = NO ;
break ;
case '<':
if(lastChar != ESC)
break ;
case 0xa3: // ALT-< or ESC <
{ [self setSel: 0 :0] ;
[self scrollSelToVisible] ;
insertChar = NO ;
}
break ;
case '>':
if(lastChar != ESC)
break ;
case 0xb3: // ALT-> or ESC >
{ i = [self textLength] ; --i ;
[self setSel: i :i] ;
[self scrollSelToVisible] ;
insertChar = NO ;
}
break ;
case '(':
if((lastChar == CTRL_X) && !definingMacro)
{ // start defining macro
// [window message: "Defining kbd macro..."] ;
definingMacro = YES ;
NXSeek(macroStream, 0, NX_FROMSTART) ;
insertChar = NO ;
}
break ;
case ')':
if((lastChar == CTRL_X) && definingMacro)
{ // [window message: "Keyboard macro defined."] ;
// [self perform: @selector(clearMessage:) with: self
// afterDelay: 1500 cancelPrevious: YES] ;
definingMacro = NO ;
NXSeek(macroStream, -2, NX_FROMCURRENT) ; // seek back 2 to remove ctrl_X )
NXPutc(macroStream,'\0') ; // null-terminate the stream
insertChar = NO ;
}
else
{ i = findMatchingOpenParen(self,')','(',selEnd.cp);
[super keyDown:theEvent];
if (i >= 0)
highlightChar(self,i);
insertChar = NO ;
}
break ;
case '}':
i = findMatchingOpenParen(self,'}','{',selEnd.cp);
[super keyDown:theEvent];
if(i >= 0)
highlightChar(self,i);
insertChar = NO ;
break ;
case ']':
i = findMatchingOpenParen(self,']','[',selEnd.cp);
[super keyDown:theEvent];
if (i >= 0)
highlightChar(self,i);
insertChar = NO ;
break ;
case 'e':
if(lastChar == CTRL_X)
{ if(!definingMacro)
{ // replay macro
replayingMacro = YES ;
NXSeek(macroStream,0L,NX_FROMSTART) ;
insertChar = NO ;
}
else
NXSeek(macroStream, -1, NX_FROMCURRENT) ; // seek back 1 to remove ctrl_X
}
break ;
case 'i':
if(lastChar == CTRL_X)
{ if(!definingMacro)
{ // insert a file into the text
int rval ;
id openPanel = [OpenPanel new] ;
[openPanel allowMultipleFiles: NO] ;
rval = [openPanel runModalForTypes: NULL] ;
if(rval == NX_OKTAG)
{ NXStream *fileStream ;
char fName[2048] ;
sprintf(fName,"%s/%s",[openPanel directory],*[openPanel filenames]) ;
if((fileStream = NXMapFile(fName,NX_READONLY)) != NULL)
{ int len,unused ;
char *buf ;
// [window message: "Inserting file..."] ;
NXGetMemoryBuffer(fileStream,&buf,&len,&unused) ;
[self replaceSel: buf length: len] ;
// [window message: ""] ;
}
else
NXRunAlertPanel("Eval","Couldn't insert file: %s",NULL,NULL,NULL,fName) ;
}
}
insertChar = NO ;
}
break ;
case 'k': // CTRL_X k = close window
if(lastChar == CTRL_X)
{ [window performClose: self] ;
insertChar = NO ;
}
break ;
case 's':
if(lastChar == CTRL_X)
{ [window save: self] ;
insertChar = NO ;
}
break ;
case 'v':
if(lastChar != ESC)
break ;
case 0xd6: // ALT-v or ESC v
{ // move up one screenful
NXPoint scrollPoint = {0.0,0.0} ;
NXRect clipRect ;
[self convertPoint: &scrollPoint fromView: [superview superview]] ;
// get the clipview's bounds
[superview getBounds: &clipRect] ;
scrollPoint.y -= clipRect.size.height ; // scroll up
[self scrollPoint: &scrollPoint] ;
insertChar = NO ;
}
break ;
case 'w':
if(lastChar != ESC)
break ;
case 0xc8: // ALT-w or ESC w
{ int subStringLength ;
if(mark == MARK_UNSET)
break ;
[self getSel:&selStart :&selEnd];
if(selStart.cp < mark)
[self getSubstring: killedText
start: selStart.cp length: subStringLength = mark - selStart.cp] ;
else
[self getSubstring: killedText
start: mark length: subStringLength = selStart.cp - mark] ;
killedText[subStringLength] = '\0' ;
insertChar = NO ;
}
break ;
case CTRL_SPACE: // set mark
[self getSel:&selStart :&selEnd];
mark = selStart.cp ;
insertChar = NO ;
break ;
case CTRL_A: // beginning of line
[self getSel:&selStart :&selEnd];
i = [self lineFromPosition:selStart.cp] ;
i = [self positionFromLine: i] ;
[self setSel:i :i];
insertChar = NO ;
break ;
case CTRL_C:
if(lastChar == CTRL_X)
{ [NXApp terminate: self] ;
insertChar = NO ;
}
break ;
case CTRL_E: // end of line
[self getSel:&selStart :&selEnd];
i = [self lineFromPosition: selStart.cp] ;
i = [self positionFromLine: i + 1] ; // beginning of next line
if(i == -1) // then there is no next line...
i = [self textLength] + 1 ;
[self setSel:i -1 :i - 1];
insertChar = NO ;
break ;
case CTRL_B:// backwards one char
[self getSel:&selStart :&selEnd];
if(selStart.cp > 1)
[self setSel:selStart.cp - 1 :selStart.cp -1];
else
NXBeep();
insertChar = NO ;
break ;
case CTRL_F: // CTRL_X CTRL_F = open file, CTRL_F forwards one char
if(lastChar == CTRL_X)
{ [NXApp open: self] ;
}
else
{ [self getSel:&selStart :&selEnd];
if(selEnd.cp < [self textLength])
[self setSel:selEnd.cp+1 :selEnd.cp+1];
else
NXBeep();
}
insertChar = NO ;
break ;
case CTRL_D: // delete char under cursor
[self getSel:&selStart :&selEnd];
[self setSel:selEnd.cp :selEnd.cp+1];
[self replaceSel:""];
[self setSel:selStart.cp :selStart.cp] ;
insertChar = NO ;
break ;
case CTRL_K: // clear to end of line, place killed text in killedText
// if line is empty, then delete the newline (and place at end of killedText)
{ int currentEnd = 0 ;
[self getSel:&selStart :&selEnd];
i = [self lineFromPosition: selStart.cp] ;
i = [self positionFromLine: i + 1] ; // i is index of beginning of next line
if(i == -1) // then there is no next line...
{ i = [self textLength] ;
}
else // there is another line
{ if(i - selStart.cp > 1) // if line is not empty,
i-- ; // don't include the newline.
}
// successive CTRL_K's append to killedText ; any other char
// interrupts the sequence
if(lastChar == CTRL_K)
currentEnd = strlen(killedText) ;
[self getSubstring: &killedText[currentEnd]
start:selStart.cp length:i- selStart.cp] ;
killedText[i - selStart.cp + currentEnd] = '\0' ;
[self setSel:selStart.cp :i];
[self replaceSel:""];
[self setSel:selStart.cp :selStart.cp] ;
insertChar = NO ;
break ;
}
case CTRL_L: // center the current selection
{ // get the selection's location in the scrollview's coordinates
NXPoint loc, scrollPoint = {0.0,0.0} ;
NXRect scrollRect ;
float delta ;
id sView = [[self superview] superview] ;
[self getSel:&selStart :&selEnd];
loc.x = selStart.x ;
loc.y = selStart.y ;
[self convertPoint: &loc toView: sView] ;
// get the scrollview's bounds
[sView getBounds: &scrollRect] ;
// how far is loc.y from middle of scrollview?...reset delta to this value
delta = scrollRect.size.height / 2.0 - loc.y ;
[sView convertPoint: &scrollPoint toView: self] ;
scrollPoint.y -= delta ;
[self scrollPoint: &scrollPoint] ;
insertChar = NO ;
break ;
}
case CTRL_O: // "open" a line...i.e. add blank line before current
[self getSel:&selStart :&selEnd];
[self setSel: selStart.cp :selStart.cp] ;
[self replaceSel: "\n"] ;
[self setSel: selStart.cp :selStart.cp] ;
insertChar = NO ;
break ;
case CTRL_N: // move directly up or down one line
case CTRL_P: // move directly up one line...fake up arrow
{ NXEvent fakeEvent ;
bcopy((char *) theEvent,(char *) &fakeEvent,sizeof(NXEvent)) ;
fakeEvent.data.key.charCode = val == CTRL_N ? 175 :173 ;
fakeEvent.data.key.charSet = 1 ;
fakeEvent.flags = NX_NUMERICPADMASK ;
[super keyDown: &fakeEvent] ;
insertChar = NO ;
}
break ;
case CTRL_S: // ctrl x ctrl s = save
if(lastChar == CTRL_X)
{ [window save: self] ;
insertChar = NO ;
}
else
{
//
//
//
// extern id finderObject ; // declared in Finder.m
//
// [finderObject doFind: self] ;
//
//
}
break ;
case CTRL_V: // move down one screenful
{ NXPoint scrollPoint = {0.0,0.0} ;
NXRect clipRect ;
[self convertPoint: &scrollPoint fromView: [superview superview]] ;
// get the clipview's bounds
[superview getBounds: &clipRect] ;
scrollPoint.y += clipRect.size.height ; // scroll down
[self scrollPoint: &scrollPoint] ;
insertChar = NO ;
break ;
}
case CTRL_W: // delete text from point to mark ; add to killedText
if(mark == MARK_UNSET)
break ;
[self getSel:&selStart :&selEnd];
if(selStart.cp < mark)
{ [self getSubstring: killedText
start: selStart.cp length:mark - selStart.cp] ;
[self setSel: selStart.cp :mark] ;
}
else
{ [self getSubstring: killedText
start: mark length: selStart.cp - mark] ;
[self setSel: mark :selStart.cp] ;
}
[self replaceSel: ""] ;
insertChar = NO ;
break ;
case CTRL_X: // don't want CTRL_X to cause a beep
insertChar = NO ;
break ;
case CTRL_Y: // yank killedText
[self getSel:&selStart :&selEnd];
[self setSel:selStart.cp :selStart.cp] ;
[self replaceSel: killedText] ;
insertChar = NO ;
break ;
default:
break;
}
if(insertChar)
[super keyDown: theEvent] ;
if(replayingMacro)
{ nextChar = NXGetc(macroStream) ;
if(nextChar == '\0')
{ replayingMacro = NO ;
NXPing() ;
}
else
theEvent->data.key.charCode = nextChar ;
}
} while(replayingMacro) ;
return self ;
}
- mouseDown: (NXEvent *) anEvent ;
{ // augment double-click behavior to
// check for bracketed structures and quoted
// items. augment command-click behaviour to
// select between backquotes.
NXSelPt start, end ;
char openers[] = "{([<\"'/`" ;
char closers[] = "})]>\"'/`" ;
char c,d, *place ;
int otherPos ;
NXStream *aStream ;
if(anEvent->data.mouse.click == 1)
[super mouseDown: anEvent] ;
else if(anEvent->data.mouse.click == 2)
{ aStream = [self stream] ;
[self getSel: &start :&end] ;
if(start.cp > 0)
{ NXSeek(aStream,start.cp - 1,NX_FROMSTART) ;
c = NXGetc(aStream) ;
if(place = index(closers,c)) // we have right side of a structure
{ d = openers[(int) place - (int) closers] ;// grab matching char
if( (otherPos = findMatchingOpenParen(self, c, d, start.cp -1))
!= -1)
{ if(c == '`') // put selection "inside" ` ... ` pair
[self setSel: otherPos + 1 :start.cp - 1] ;
else
[self setSel: otherPos :start.cp] ;
return self ;
}
else
[super mouseDown: anEvent] ;
}
else
{ NXSeek(aStream,start.cp,NX_FROMSTART) ;
c = NXGetc(aStream) ;
if(place = index(openers,c)) // we have left side of a structure
{ d = closers[(int) place - (int) openers] ;
if( (otherPos = findMatchingCloseParen(self, c, d, start.cp +1))
!= -1)
{ if(c == '`') // put selection "inside" ` ... ` pair
[self setSel: start.cp +1 :otherPos-1] ;
else
[self setSel: start.cp :otherPos] ;
}
}
else
[super mouseDown: anEvent] ;
}
}
}
else // deal with triple, quadruple, etc. clicks here...
[super mouseDown: anEvent] ;
if((anEvent->data.mouse.click == 1) &&
(anEvent->flags & NX_COMMANDMASK))
{ // command click == search back for backquote (or beginning of text)
// forward for backquote (or end ot text), then select.
int from, to ;
aStream = [self stream] ;
[self getSel: &start :&end] ;
if(start.cp > 0)
{ from = start.cp - 1 ;
NXSeek(aStream,from,NX_FROMSTART) ;
while(from >= 1 && ((c = NXBGetc(aStream)) != '`'))
from-- ;
to = start.cp ;
NXSeek(aStream,to,NX_FROMSTART) ;
while(!NXAtEOS(aStream) && ((c = NXGetc(aStream)) != '`'))
to++ ;
[self setSel: from :to] ;
}
}
return self ;
}
/*
- textDidGetKeys: sender isEmpty: (BOOL) flag
{ [window setDocEdited: YES] ;
return self ;
}
*/
- useEmacsBindings: (BOOL) YESorNO ;
{ useEmacsBindings = YESorNO ;
return self ;
}
@end
@implementation MiscEmacsText(Dropping)
- (BOOL)readDragData:(Pasteboard *)pboard
{
return YES;
}
- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
NXDragOperation sourceMask;
sourceMask = [sender draggingSourceOperationMask];
if (sourceMask & NX_DragOperationCopy)
return NX_DragOperationCopy;
return NX_DragOperationNone;
}
- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
{
return YES;
}
- (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender
{
return YES;
}
- concludeDragOperation:(id <NXDraggingInfo>)sender
{
[self pasteFrom:[sender draggingPasteboard]];
return self;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.