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.