home *** CD-ROM | disk | FTP | other *** search
- #import "NuText.h"
- #import <stdlib.h>
- #import "WorkspaceManager.h"
- #import "ClassManager.h"
- #import "NuWraps.h"
- #import "NuString.h"
- #import "MenuManager.h"
- #import "PrefsManager.h"
- #import <strings.h>
- #import <appkit/publicWraps.h>
- #import <appkit/Scroller.h>
- #import <appkit/ScrollView.h>
- #import <appkit/Application.h>
- #import <appkit/drag.h>
- #import <appkit/Pasteboard.h>
- #import <appkit/NXBitmapImageRep.h>
- #import <appkit/NXHelpPanel.h>
- #import <appkit/NXBrowser.h>
- #import <defaults/defaults.h>
- #import "NuGraphicCell.h"
- #import <appkit/Pasteboard.h>
- #import <appkit/tiff.h>
- #import <dpsclient/wraps.h>
- #import <ctype.h>
- #import <libc.h>
- #import <objc/objc-load.h>
-
-
- extern id Nu ;
- extern char bigBuf[] ; // a "big" buffer for general use
-
- #define ESC 27
- #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)
- #define CTRL_V (22)
- //
- // following procedures were "stolen" from
- // Lee boynton's LispExample
- /*
- ** 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;
- }
-
- char *
- nextLine(char *text,char *lineBuf)
- { // fill lineBuf with a copy of the next line in text,
- // not including any terminating newLine. Return NULL if
- // no line found, else return address of first character
- // following line
- char *cursor = text ;
- int i = 0 ;
- if(*cursor == '\0')
- return NULL ;
- while(*cursor && *cursor != '\n')
- { lineBuf[i++] = *cursor ;
- cursor++ ;
- }
- lineBuf[i] = '\0' ;
- if(*cursor == '\n')
- return ++cursor ;
- else
- return cursor ;
- }
-
- 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 NuText:Text
-
- + initialize ;
- { // super will register a bunch of directives
- [super initialize] ;
- // here we "re-register" the NeXTGraphic directive to make
- // NuGraphicCells (a subclass of the undocumented
- // NXGraphicCell) look after the NeXTGraphic RTFD directive.
- [[self class] registerDirective: "NeXTGraphic" forClass:[NuGraphicCell class]] ;
- return self ;
- }
-
- - awake ;
- { // wait until everything has been unarchived, then do setup stuff
- [super awake] ;
- [self perform:@selector(awakeAfterDelay:)
- with:self afterDelay:1 cancelPrevious:YES];
- return self ;
- }
-
- - awakeAfterDelay: sender ;
- { const char *pBoardTypes[] = {NXFilenamePboardType,NULL} ;
- int pBoardTypeCount = 1 ; // elements in types
- if([window isKindOf: [WorkspaceManager class]])
- { [self setEditable: YES] ;
- [self setGraphicsImportEnabled: YES] ;
- // register to accept icons
- [self registerForDraggedTypes:(const char *const *)pBoardTypes
- count:pBoardTypeCount] ;
- }
- else
- { [self setEditable: NO] ;
- }
- return self ;
- }
-
-
- - evalSelection:sender
- { // compile and run the workspace selection
- // Define default name of class:
- #define NUEVAL "NuEval"
- char tmpFile[] = NUEVAL, fullPath[128] ;
- NXSelPt start, end ;
- FILE *fp ;
- int textLen ;
- char *headerBuf, *trueBuf, *textBuf, *cursor ;
- BOOL understandsMessage ;
-
- understandsMessage = [window respondsTo: @selector(message:)] ;
- if(understandsMessage)
- [window message: "Evaluating..."] ;
- // get the selected text
- [self getSel:&start :&end] ;
- textLen = end.cp-start.cp ;
- textBuf = trueBuf = (char *) malloc(textLen+1) ;
- [self getSubstring:textBuf start:start.cp length:textLen] ;
- textBuf[textLen] = '\0' ;
- // now find out if we have any header
- headerBuf = textBuf ;
- while(isspace(*headerBuf))
- headerBuf++ ; // scan past any initial whiteSpace
- if(*headerBuf == '#') // then we have a header
- { char messBuf[256] ;
- cursor = headerBuf ;
- while(cursor = nextLine(cursor,bigBuf))
- { // check for a load command
- if(!strncmp(bigBuf,"#pragma load ",13))
- { if(understandsMessage)
- { sprintf(messBuf,"Preloading %s",&bigBuf[13]) ;
- [window message: messBuf] ;
- }
- [Nu loadIfNeeded: &bigBuf[13]] ;
- }
- else if(!strlen(bigBuf))
- break ;
- }
- if(cursor == NULL)
- { // No body to evaluate free(textBuf) ;
- if(understandsMessage)
- [window message: ""] ;
- return self ;
- }
- textBuf = cursor ;
- cursor-- ;
- *cursor = '\0' ; // chop string into 2 pieces
- }
- else
- cursor = headerBuf = "" ; // no header at all
-
- // now hack together the file...
- sprintf(fullPath,"/tmp/%s.m",tmpFile) ;
- fp = fopen(fullPath,"w+") ;
- fprintf(fp,
- "%s\n"
- "#import \"Glyph.h\"\n"
- "#import \"Nutation.h\"\n"
- "@interface %s: Glyph\n"
- "+(void) doit ;\n"
- "@end\n\n"
- "@implementation %s\n"
- "+ (void) doit\n"
- "{\n"
- "#line 1\n"
- "%s\n"
- "}\n"
- "@end",
- headerBuf, tmpFile, tmpFile, textBuf) ;
- fflush(fp) ;
- fclose(fp) ;
-
- // free up alloc'ed memory
- if(headerBuf[0] != '\0') // string was cut into 2 pieces...
- *cursor = ' ' ; // reassemble
- free(trueBuf) ; // trueBuf always points to beginning of malloc'ed string
-
- // Now compile and load the file
- if(understandsMessage)
- [window message: "Compiling..."] ;
- if([ClassManager compileClass: NUEVAL file: fullPath])
- { // compile succeeded, load it
- if(understandsMessage)
- [window message: "Loading..."] ;
- if([ClassManager load: NUEVAL])
- { // load succeeded, execute it
- if(understandsMessage)
- [window message: "Executing..."] ;
- objc_msgSend(objc_getClass(tmpFile),
- sel_getUid("doit")) ;
- if(understandsMessage)
- [window message: ""] ;
- // now remove the temporary class
- [ClassManager unload: tmpFile] ;
- }
- }
- unlink(tmpFile) ;
- return self ;
- }
-
-
- - insertLink: sender ;
- { [window setDocEdited: YES] ;
- return [self replaceSelWithCell: [[NXLinkCell alloc] initForView: self]] ;
- }
-
- - insertMarker: sender ;
- { [window setDocEdited: YES] ;
- return [self replaceSelWithCell: [[NXMarkerCell alloc] initForView: self]] ;
- }
-
- - keyDown:(NXEvent *)theEvent
- { // provide some emacs-style key bindings
- int i ;
- static unsigned short val = '\0', lastChar = '\0' ;
- NXSelPt selStart, selEnd;
- [self getSel:&selStart :&selEnd];
- lastChar = val ; // val was set in "previous" call of keyDown..
- val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
- switch (val) {
- case ESC: // don't want ESC to cause a beep
- return self ;
- case '<':
- if(lastChar == ESC)
- { [self setSel: 1 :1] ;
- [self scrollSelToVisible] ;
- return self ;
- }
- break ;
- case '>':
- if(lastChar == ESC)
- { i = [self textLength] ; --i ;
- [self setSel: i :i] ;
- [self scrollSelToVisible] ;
- return self ;
- }
- break ;
- case ')':
- i = findMatchingOpenParen(self,')','(',selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- return self;
- case '}':
- i = findMatchingOpenParen(self,'}','{',selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- return self;
- case ']':
- i = findMatchingOpenParen(self,']','[',selEnd.cp);
- [super keyDown:theEvent];
- if (i >= 0)
- highlightChar(self,i);
- return self;
- case CTRL_A: // beginning of line
- i = [self lineFromPosition:selStart.cp] ;
- i = [self positionFromLine: i] ;
- [self setSel:i :i];
- return self;
- case CTRL_E: // end of line
- i = [self lineFromPosition: selStart.cp] ;
- i = [self positionFromLine: i + 1] ; // beginning of next line
- [self setSel:i -1 :i - 1];
- return self;
- case CTRL_B:// backwards one char
- if (selStart.cp > 1)
- [self setSel:selStart.cp - 1 :selStart.cp -1];
- else
- NXBeep();
- return self;
- case CTRL_F: // forwards one char
- if (selEnd.cp < [self textLength])
- [self setSel:selEnd.cp+1 :selEnd.cp+1];
- else
- NXBeep();
- return self;
- case CTRL_D: // delete char under cursor
- [self setSel:selEnd.cp :selEnd.cp+1];
- [self replaceSel:""];
- [self setSel:selStart.cp :selStart.cp] ;
- return self;
- case CTRL_K: // clear to end of line
- i = [self lineFromPosition: selStart.cp] ;
- i = [self positionFromLine: i + 1] ; // beginning of next line
- [self setSel:selStart.cp :--i];
- [self replaceSel:""];
- [self setSel:selStart.cp :selStart.cp] ;
- return self;
- case CTRL_P: // move directly up one line...fake down arrow
- theEvent->data.key.charCode = 173 ;
- theEvent->data.key.charSet = 1 ;
- theEvent->flags = NX_NUMERICPADMASK ;
- break ;
- case CTRL_N: // move directly down one line...fake up arrow
- theEvent->data.key.charCode = 175 ;
- theEvent->data.key.charSet = 1 ;
- theEvent->flags = NX_NUMERICPADMASK ;
- break ;
- case 'v': // move up one screenful
- if(lastChar != ESC)
- break ;
- case CTRL_V: // move down one screenful
- { id scroller ;
- NXRect aRect ;
- NXEvent fakeEvent ;
- fakeEvent = *theEvent ;
- fakeEvent.type = NX_MOUSEDOWN ;
- fakeEvent.data.mouse.reserved = 18 ;
- fakeEvent.data.mouse.eventNum = 9999 ;
- fakeEvent.data.mouse.click = 1 ;
- // fakeEvent.data.mouse.unused = 0 ;
- fakeEvent.flags = NX_ALTERNATEMASK ;
- scroller = [[superview superview] vertScroller] ;
- [scroller calcRect:&aRect forPart:
- val == 'v'? NX_DECLINE :NX_INCLINE ] ;
- [scroller convertPoint: &aRect.origin toView: nil] ;
- fakeEvent.location = aRect.origin ;
- DPSPostEvent(&fakeEvent,1) ;
- fakeEvent.type = NX_MOUSEUP ;
- fakeEvent.time += 10 ; // 10 /68ths of a second
- DPSPostEvent(&fakeEvent,0) ;
- return self ;
- }
- default:
- break;
- }
- return [super keyDown:theEvent];
- }
-
-
- - 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 ;
- [super mouseDown: anEvent] ;
- if(anEvent->data.mouse.click == 2)
- { aStream = [self stream] ;
- [self getSel: &start :&end] ;
- if(start.cp > 0)
- { // NXSeek(aStream,start.cp-1,NX_FROMSTART) ; // worked in 2.0, changed
- NXSeek(aStream,start.cp,NX_FROMSTART) ; // to this for 3.0!
- 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 + 1] ;
- return self ;
- }
- }
- else
- { NXSeek(aStream,start.cp,NX_FROMSTART) ;
- c = NXGetc(aStream) ;
- if(place = index(openers,c)) // we have 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 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 ;
- }
-
- #define NX_NOBUTTONS (0)
- #define NXSetWindowLevel _NXSetWindowLevel
-
- - pasteScreen: sender ;
- { // grab a portion of the screen,
- // save it as a file, then paste
- // it into the rtfd
- id theWin, aWin, theImage ;
- char saveName[32] ;
- if([(theWin = [NXApp mainWindow]) isMemberOf: [WorkspaceManager class]])
- { NXRect aRect ;
- extern int NXSetWindowLevel(int, int);
- // get a rectangle from the user (using a procedure defined
- // in NuWraps.psw-defined procedure)
- grabScreen(&aRect.origin.x,&aRect.origin.y,
- &aRect.size.width, &aRect.size.height) ;
- // now make an invisible window to grab bits
- aWin = [[Window alloc]
- initContent: &aRect
- style:NX_PLAINSTYLE
- backing:NX_NONRETAINED
- buttonMask:NX_NOBUTTONS
- defer:NO] ;
- PSsetautofill(NO,[aWin windowNum]) ;
- NXSetWindowLevel([aWin windowNum],
- NX_MAINMENULEVEL);
- [aWin orderFront: 0] ;
- [[aWin contentView] lockFocus] ;
- theImage = [[NXImage alloc] init] ;
- [theImage setDataRetained:YES] ;
- aRect.origin.x = aRect.origin.y = 0.0 ; // convert screen to aWin
- [theImage useRepresentation:
- [[NXBitmapImageRep alloc] initData: NULL fromRect: &aRect]] ;
- [[aWin contentView] unlockFocus] ;
- [aWin free] ;
- sprintf(saveName,"%s%x.tiff", NUSCREENPASTE, (unsigned) time(NULL));
- [theImage setName: saveName] ;
- [self replaceSelWithCell:[[NXGraphicCell alloc] initIconCell: saveName]] ;
- [window setDocEdited: YES] ;
- [self setRTFD: YES] ;
- return self ;
- }
- else
- return self ; // ignore
- }
-
-
- - showMarkers: sender ;
- { // here I use 2 undocumented Text functions, as gleaned
- // from IB...in order to control visibility or markers
- if([self markersVisible])
- { [self setMarkersVisible: NO] ;
- [sender setStringValue: "Show Markers"] ;
- }
- else
- { [self setMarkersVisible: YES] ;
- [sender setStringValue: "Hide Markers"] ;
- }
- return self ;
- }
-
-
- - (int) tag ;
- { return TEXTVIEWTAG ;
- }
-
- @end