This is ServerText.m in view mode; [Download] [Up]
#import "ServerText.h" #import "MiscObjectRecycler.h" #import "../NetTalkThinker.h" #import "ClientText.h" #import "TextDiff.h" #import <appkit/appkit.h> #import <remote/NXProxy.h> #import <remote/NXConnection.h> @implementation ServerText - initFrame:(const NXRect *)frameRect text:(const char *)theText alignment:(int)mode { char path[MAXPATHLEN]; [[NXBundle bundleForClass:[ServerText class]] getPath:path forResource:"Empty.rtfd" ofType:NULL]; [super initFrame:frameRect text:theText alignment:mode]; [self openRTFDFrom:path]; textList = [[List alloc] init]; stayFirstResponder = NO; return self; } - awakeFromNib { // char path[MAXPATHLEN]; NXAtom supportedTypes[11] = {NXFilenamePboardType, NXFileContentsPboardType, NXSoundPboardType, NXPostScriptPboardType, NXTIFFPboardType, NXColorPboardType, NXFontPboardType, NXRulerPboardType, NXRTFPboardType, NXAsciiPboardType, NULL}; [self registerForDraggedTypes:supportedTypes count:10]; /* ***** IF THIS WERE AN XTEXT *** [[NXBundle mainBundle] getPath:path forResource:"emacs" ofType:"keys"]; demoAction = [[XTDispatchAction alloc] initBase:path estream:nil]; [self setInitialAction:demoAction]; fprintf(stderr, "RTFDText: Loaded %s\n", path); */ return self; } - setStayFirstResponder:(BOOL)aFlag { stayFirstResponder = aFlag; return self; } - resignFirstResponder { if (stayFirstResponder) return nil; return [super resignFirstResponder]; } - free { while ([textList count]) { id theObj, connection; theObj = [textList removeObjectAt:0]; connection = [theObj connectionForProxy]; [theObj serverIsDead]; [connection unregisterForInvalidationNotification:self]; } [NXConnection removeObject:self]; [super free]; return self; } - ring:sender { if (strcmp(NXGetDefaultValue(APPNAME, NTD_RINGINGALLOWED), NTD_YES) == 0) [[[[Sound alloc] initFromSoundfile:NXGetDefaultValue(APPNAME, NTD_RINGSOUNDNAME)] play: nil] free]; return self; } - addClientText:(ClientText *)aText { TextDiff *diff; if ([textList indexOf:aText] == NX_NOT_IN_LIST) { [textList addObject:aText]; [[(id)aText connectionForProxy] registerForInvalidationNotification:self]; [(id)aText setProtocolForProxy:@protocol(NetTalkClientText)]; [aText setServerText:self]; diff = [TextDiff newFromRecycler]; [self writeRTFDTo:[diff stream]]; [diff setSelection:0 :0 :1]; [aText applyDifference:diff]; [diff recycle]; } return self; } - removeClientText:(ClientText *)aText; { id theObj, connection; theObj = [textList removeObject:aText]; connection = [theObj connectionForProxy]; [connection unregisterForInvalidationNotification:self]; return self; } - cleanupClientList { int i = 0; while (i < [textList count]) { if ([[(id)[textList objectAt:i] connectionForProxy] isValid]) i++; else [textList removeObjectAt:i]; } return self; } - makeClientsApply:(TextDiff*)diff { id aText; int count; for (count = 0; count < [textList count]; count++) { aText = [textList objectAt:count]; NX_DURING [aText applyDifference:diff]; NX_HANDLER fprintf(stderr, "Error occured in applying difference.\n"); NX_ENDHANDLER } return self; } - gotoLineStart { int pos = sp0.cp; NXStream *text; char c = 'a'; BOOL finished = NO; if (pos == 0) return [self setSel:pos :pos]; text = [self stream]; while (!finished) { if (pos > 0) pos--; else finished = YES; NXSeek(text, pos, NX_FROMSTART); c = NXGetc(text); if (c == '\n') { finished = YES; pos++; } } [self setSel:pos :pos]; return [self scrollSelToVisible]; } - gotoLineEnd { int pos = spN.cp; NXStream *text; char c = 'a'; BOOL finished = NO; if (pos == [self textLength]) return [self setSel:pos :pos]; text = [self stream]; NXSeek(text, pos, NX_FROMSTART); while (!finished) { if (NXAtEOS(text)) finished = YES; else { c = NXGetc(text); pos++; } if (c == '\n') { finished = YES; pos--; } } [self setSel:pos :pos]; return [self scrollSelToVisible]; } - deleteForward { if (sp0.cp == spN.cp) spN.cp++; return [self delete:nil]; } - gotoWordStart { int pos = sp0.cp; NXStream *text; char c = 'a'; BOOL onWord = NO; BOOL finished = NO; if (pos == 0) return [self setSel:pos :pos]; text = [self stream]; while (!finished) { if (pos > 0) pos--; else finished = YES; NXSeek(text, pos, NX_FROMSTART); c = NXGetc(text); if (strchr([self preSelSmartTable], c)) { if (onWord) { finished = YES; pos++; } } else onWord = YES; } [self setSel:pos :pos]; return [self scrollSelToVisible]; } - gotoWordEnd { int pos = spN.cp; NXStream *text; char c = 'a'; BOOL onWord = NO; BOOL finished = NO; if (pos == [self textLength]) return [self setSel:pos :pos]; text = [self stream]; NXSeek(text, pos, NX_FROMSTART); while (!finished) { if (NXAtEOS(text)) finished = YES; else { c = NXGetc(text); pos++; } if (strchr([self postSelSmartTable], c)) { if (onWord) { finished = YES; pos--; } } else onWord = YES; } [self setSel:pos :pos]; return [self scrollSelToVisible]; } /* ------------------------------------- */ #define EXTEND_NONE 0 #define EXTEND_LEFT 1 #define EXTEND_RIGHT 2 #define CTRL(c) ((c)&037) - keyDown:(NXEvent *)theEvent { TextDiff *diff = [TextDiff newFromRecycler]; NXSelPt start, end; int click; static int extendMode = EXTEND_NONE; static NXSelPt ext0, extN; BOOL didExtend = NO; [self getSel:&start:&end]; if (extendMode != EXTEND_NONE) { if ((ext0.cp != start.cp) || (extN.cp != end.cp)) extendMode = EXTEND_NONE; } click = clickCount; switch(theEvent->data.key.charCode) { /* these are emacs bindings that just move the cursor */ /* we don't have to do any diffs then. */ case CTRL('a'): // goto begin of line case CTRL('b'): // move one char back case CTRL('e'): // goto end of line case CTRL('n'): // move directly down one line case CTRL('p'): // move directly up one line case CTRL('v'): // scroll down one screenfull case CTRL(' '): // mark position case 0xa3: // scroll one screenfull up case 0xb3: // scroll one screenfull down [super keyDown:theEvent]; break; case CTRL('d'): [self deleteForward]; break; /* the arrows */ case 172: case 173: case 174: case 175: if (theEvent->flags & NX_ALTERNATEMASK) { // extending the selection -- up/down and shift mode not done yet if (theEvent->data.key.charCode == 172) { if (extendMode == EXTEND_NONE) extendMode = EXTEND_LEFT; switch (extendMode) { case EXTEND_RIGHT: if (spN.cp > sp0.cp) [self setSel:sp0.cp :spN.cp-1]; else NXBeep(); break; default: if (sp0.cp > 0) [self setSel:sp0.cp-1 :spN.cp]; } } if (theEvent->data.key.charCode == 174) { if (extendMode == EXTEND_NONE) extendMode = EXTEND_RIGHT; switch (extendMode) { case EXTEND_LEFT: if (sp0.cp < spN.cp) [self setSel:sp0.cp+1 :spN.cp]; else NXBeep(); break; default: [self setSel:sp0.cp :spN.cp+1]; } } [self getSel:&ext0 :&extN]; didExtend = YES; break; } if (theEvent->flags & NX_CONTROLMASK) { // moving to end of screen -- up/down and shift mode not done yet if (theEvent->data.key.charCode == 174) [self gotoLineEnd]; if (theEvent->data.key.charCode == 172) [self gotoLineStart]; break; } if (theEvent->flags & NX_SHIFTMASK) { // moving word bounds with shift if (theEvent->data.key.charCode == 174) [self gotoWordEnd]; if (theEvent->data.key.charCode == 172) [self gotoWordStart]; break; } // normal cursor movement [super keyDown:theEvent]; break; default: [super keyDown:theEvent]; [diff setSelection:start.cp :end.cp :click]; if (sp0.cp < start.cp) [diff setSelection:sp0.cp :end.cp :click]; else { NXSelPt s = sp0; sp0 = start; [self writeRTFDSelectionTo:[diff stream]]; sp0 = s; } NX_DURING [self makeClientsApply:diff]; NX_HANDLER fprintf(stderr, "Error transmitting difference package\n"); NX_ENDHANDLER } [diff recycle]; if (!didExtend) extendMode = EXTEND_NONE; return self; } - delete:sender { TextDiff *diff = [TextDiff newFromRecycler]; if (spN.cp != sp0.cp) { [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; } [diff recycle]; return [super delete:sender]; } static id pasteboard = nil; static BOOL pasting = NO; - paste:sender { id retval; int start, end, click; if (pasteboard == nil) { pasteboard = [Pasteboard new]; } start = sp0.cp; end = spN.cp; click = clickCount; pasting = YES; retval = [super paste:sender]; pasting = NO; if (retval) { TextDiff *diff = [TextDiff newFromRecycler]; [diff setSelection:start :end :click]; [self setSel:start :sp0.cp]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; [self setSel:spN.cp :spN.cp]; } return retval; } - setSelGray:(float)value { id retval = [super setSelGray:value]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - setSelColor:(NXColor) color { id retval = [super setSelColor:color]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - setSelFont:fontId { id retval = [super setSelFont:fontId]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - setSelFontSize:(float)size { id retval = [super setSelFontSize:size]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - setSelFontFamily:(const char *)fontName { id retval = [super setSelFontFamily:fontName]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - subscript:sender { id retval = [super subscript:sender]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - superscript:sender { id retval = [super superscript:sender]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - underline:sender { id retval = [super underline:sender]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - unscript:sender { id retval = [super unscript:sender]; if (sp0.cp != spN.cp) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - changeFont:sender { id retval; BOOL doApply = (sp0.cp != spN.cp); retval = [super changeFont:sender]; if (doApply) { TextDiff *diff = [TextDiff newFromRecycler]; [self writeRTFDSelectionTo:[diff stream]]; [diff setSelection:sp0.cp :spN.cp :clickCount]; [self makeClientsApply:diff]; [diff recycle]; } return retval; } - replaceSel:(const char *)aString { id retval; TextDiff *diff = [TextDiff newFromRecycler]; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSel:aString]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; return retval; } - replaceSel:(const char *)aString length:(int)length { id retval; TextDiff *diff = [TextDiff newFromRecycler]; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSel:aString length:length]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; return retval; } - replaceSel:(const char *)aString length:(int)length runs:(NXRunArray *)insertRuns { id retval; TextDiff *diff = [TextDiff newFromRecycler]; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSel:aString length:length runs:insertRuns]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; return retval; } - replaceSelWithCell:cell { id retval; int start; TextDiff *diff; if (pasting) return [super replaceSelWithCell:cell]; diff = [TextDiff newFromRecycler]; start = sp0.cp; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSelWithCell:cell]; [self setSel:start :start+1]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; [self setSel:start+1 :start+1]; return retval; } - replaceSelWithRichText:(NXStream *)stream { id retval; int start, end; TextDiff *diff; if (pasting) return [super replaceSelWithRichText:stream]; diff = [TextDiff newFromRecycler]; start = sp0.cp; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSelWithRichText:stream]; end = spN.cp; [self setSel:start :end]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; [self setSel:end :end]; return retval; } - replaceSelWithRTFD:(NXStream *)stream { id retval; TextDiff *diff = [TextDiff newFromRecycler]; [diff setSelection:sp0.cp :spN.cp :clickCount]; retval = [super replaceSelWithRTFD:stream]; [self writeRTFDSelectionTo:[diff stream]]; [self makeClientsApply:diff]; [diff recycle]; return retval; } /********************************************/ /* NXSenderIsInvalid protocol */ /********************************************/ - senderIsInvalid:sender { return [self cleanupClientList]; } /********************************************/ /* behavior on mouse-down */ /********************************************/ - (BOOL)acceptsFirstMouse { return YES; } - mouseDown:(NXEvent *)e { if (![window isKeyWindow]) return self; return [super mouseDown:e]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.