This is XText.m in view mode; [Download] [Up]
#import "XText.h"
/* Some of this code is based on other emacs-like Text classes by
Julie Zelenski, Lee Boynton, and Glen Diener.
There's some ugly hair in here; the Text object is not very well
designed to support this kind of stuff. No doubt this will all be
fixed by NextStep 9.0 ...
*/
unsigned char GetPrevious(NXStream *s)
{
int pos, ch;
pos = NXTell(s);
if (pos <= 0) return EOF;
NXSeek(s, --pos, NX_FROMSTART);
ch = NXGetc(s);
NXUngetc(s);
return ch;
}
@implementation XText(private)
- scrollTo:(const NXPoint *)newOrigin
{
// p. griffin 7/95
// superview = ClipView
// [superview superview] = ScrollView
if([superview respondsTo:@selector(rawScroll:)]){
[[superview constrainScroll:(NXPoint *)newOrigin] rawScroll:newOrigin];
if ([[superview superview] respondsTo:@selector(reflectScroll:)]){
[[superview superview] reflectScroll:superview];// romeo romeo
}
}
return self;
}
@end
@implementation XText
- initFrame:(const NXRect *)frameRect text:(const char *)theText
alignment:(int)mode
{
// i don't understand why the compiler whines without the (char *) here
[super initFrame:frameRect text:(const char *)theText alignment:mode];
posHint = -1;
xHintPos = -1;
return self;
}
- goto:(int)pos end:(int)end mode:(int)mode
{
int start;
switch(mode) {
case 0: // move
[self setSel:pos :pos];
[self scrollSelToVisible];
posHint = -1;
break;
case 1: // delete
case 2: // cut
if (pos != end) {
start = pos;
if (start > end)
{ start = end; end = pos; }
[self disableAutodisplay];
[self setSel:start :end];
if (mode == 1)
[self delete:self];
else
[self cut:self];
}
posHint = -1;
break;
case 3: // select
start = pos;
if (start > end)
{ start = end; end = pos; }
// The Text object can't even extend the selection without flashing,
// unless we disable autodisplay
if (sp0.cp != spN.cp) [self disableAutodisplay];
[self setSel:start :end];
posHint = pos;
break;
}
xHintPos = -1;
return self;
}
- moveChar:(int)cnt mode:(int)mode
{
int pos, end;
int max = [self textLength];
if (sp0.cp == posHint) {
pos = sp0.cp + cnt;
end = spN.cp;
} else {
pos = spN.cp + cnt;
end = sp0.cp;
}
if (pos < 0)
pos = 0;
else if (pos > max)
pos = max;
return [self goto:pos end:end mode:mode];
}
/* 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)) )
- moveWord:(int)cnt mode:(int)mode
{
NXStream *s = [self stream];
char c;
int i, pos, end;
unsigned char digit_cat = charCategoryTable['0'];
unsigned char alpha_cat = charCategoryTable['a'];
unsigned char c_cat;
BOOL inWord = NO;
if (cnt == 0)
return self;
if (sp0.cp == posHint) {
pos = sp0.cp;
end = spN.cp;
} else {
pos = spN.cp;
end = sp0.cp;
}
NXSeek(s, pos, NX_FROMSTART);
i = (cnt<0 ? -cnt : cnt);
while (1) {
c = (cnt<0 ? NXBGetc(s) : NXGetc(s));
if (c == EOF) break;
c_cat = charCategoryTable[c];
if (c_cat==alpha_cat || c_cat==digit_cat)
inWord = YES;
else if (inWord) {
--i;
if (i > 0)
inWord = NO;
else
break;
}
}
pos = NXTell(s);
if (c != EOF)
pos += (cnt<0 ? 1 : -1);
return [self goto:pos end:end mode:mode];
}
/* line is from an NXSelPt; returns the length of the line.
too bad this requires grunging in Text's data structures */
#define LINE_LENGTH(line) \
(self->theBreaks->breaks[(line)/sizeof(NXLineDesc)] & 0x3fff)
- moveLine:(int)cnt mode:(int)mode
{
int pos, end, x, dir;
if (sp0.cp == posHint) {
pos = sp0.cp;
end = spN.cp;
} else {
pos = spN.cp;
end = sp0.cp;
}
if (mode != 0)
[self disableAutodisplay];
// collapse and normalize the selection
[self setSel:pos :pos];
x = (sp0.cp == xHintPos ? xHint : (sp0.cp - sp0.c1st));
if (cnt < 0) {
dir = NX_UP;
cnt = -cnt;
} else {
dir = NX_DOWN;
}
for (; cnt > 0; --cnt)
[self moveCaret: dir];
pos = LINE_LENGTH(sp0.line)-1;
if (x < pos)
pos = x;
pos += sp0.c1st;
[self goto:pos end:end mode:mode];
xHintPos = pos;
xHint = x;
return self;
}
- lineBegin:(int)mode
{
int pos, end;
if (sp0.cp == posHint) {
pos = sp0.c1st;
end = spN.cp;
} else {
pos = spN.c1st;
// Text is inconsistent about what line it thinks we're on
if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
pos = spN.cp;
}
end = sp0.cp;
}
return [self goto:pos end:end mode:mode];
}
- lineEnd:(int)mode
{
NXSelPt *sp;
int pos, end;
if (sp0.cp == posHint) {
sp = &sp0;
end = spN.cp;
} else {
// need to correct for TBD
sp = &spN;
end = sp0.cp;
}
pos = sp->c1st + LINE_LENGTH(sp->line) - 1;
if (pos < sp->cp) {
// Text is being flakey again; we really want to be on the next line
// this is pretty gross
pos = sp->line;
if (theBreaks->breaks[pos/sizeof(NXLineDesc)] < 0)
pos += sizeof(NXHeightChange);
else
pos += sizeof(NXLineDesc);
pos = sp->cp + LINE_LENGTH(pos) - 1;
}
if ((pos == sp->cp) && (mode != 0))
++pos;
return [self goto:pos end:end mode:mode];
}
- docBegin:(int)mode
{
return [self goto:0
end:(sp0.cp == posHint ? spN.cp : sp0.cp)
mode:mode];
}
- docEnd:(int)mode
{
return [self goto:[self textLength]
end:(sp0.cp == posHint ? spN.cp : sp0.cp)
mode:mode];
}
- collapseSel:(int)dir
{
int pos;
if ((dir < 0) || ((dir == 0) && (sp0.cp == posHint)))
pos = sp0.cp;
else
pos = spN.cp;
return [self goto:pos end:pos mode:0];
}
- transChars
{
int pos = sp0.cp;
char buf[2], temp;
if (pos == spN.cp) {
if (pos == (sp0.c1st + LINE_LENGTH(sp0.line) - 1))
--pos;
if (pos > 0)
if ([self getSubstring:buf start:pos-1 length:2] == 2) {
temp = buf[1]; buf[1] = buf[0]; buf[0] = temp;
[self disableAutodisplay];
[self setSel:pos-1 :pos+1];
[self replaceSel:buf length:2];
return self;
}
}
NXBeep();
return self;
}
- openLine
{
int pos = sp0.cp;
// don't do anything if there's a non-empty selection
if (pos == spN.cp) {
[self replaceSel:"\n"];
[self setSel:pos :pos];
} else
NXBeep();
return self;
}
- scroll:(int)pages :(int)lines
{
NXRect r;
// if our superview isn't a ClipView, we can't scroll
if ([superview respondsTo:@selector(rawScroll:)]) {
[superview getBounds:&r];
r.origin.y += pages*r.size.height + lines*[self lineHeight];
[self scrollTo:&r.origin];
} else
NXBeep();
return self;
}
- scrollIfRO:(int)pages :(int)lines
{
if (![self isEditable])
return [self scroll:pages :lines];
else
return nil;
}
- insertChar:(NXEvent *)event
{
char c;
c = event->data.key.charCode;
[self replaceSel:&c length:1];
return self;
}
- insertNextChar
{
static id action = nil;
if (!action)
action = [[XTEventMsgAction allocFromZone:[NXApp zone]]
initSel:@selector(insertChar:)];
nextAction = action;
return self;
}
- autoIndent
{
int pos, end;
unsigned char buf[2];
// don't do anything if there's a non-empty selection
if (sp0.cp != spN.cp) {
NXBeep();
return self;
}
if (sp0.cp == posHint) {
pos = sp0.c1st;
end = spN.cp;
} else {
pos = spN.c1st;
// Text is inconsistent about what line it thinks we're on
if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
pos = spN.cp;
}
end = sp0.cp;
}
[[self hideCaret] disableAutodisplay]; // no need to display yet
[self replaceSel:"\n" length:1];
while ([self getSubstring:buf start:pos++ length:1] != -1){
if(buf[0] == ' ' || buf[0] == '\t') [self replaceSel:buf length:1];
else if(pos == end) break;
else break;
}
[[self setAutodisplay:YES] displayIfNeeded];
/* scroll down to the correct line */
if ([superview respondsTo:@selector(rawScroll:)]) {
[self scroll:0 :1];
[self calcLine];
}
return self;
}
- match:(unsigned char *)LR
{
NXRect oldRect, newRect;
unsigned char buf[2];
int count, left_pos, right_pos, utime;
right_pos = sp0.cp;
left_pos = right_pos-1;
count = 1;
utime = 300000;
/* don't do anything if there's a non-empty selection
* or not two character */
if (sp0.cp != spN.cp || strlen(LR) != 2) return self;
/* at the beginning of file ? */
if (left_pos < 0){
[self replaceSel:&LR[1] length:1];
return self;
}
/* search for the left character */
while([self getSubstring:buf start:left_pos length:1] != -1){
if(buf[0] == LR[0]) count--;
else if (buf[0] == LR[1]) count++;
if(count == 0) break;
if(left_pos-- == 0) break;
}
if(count != 0) {
[self replaceSel:&LR[1] length:1];
return self;
}
[self goto:left_pos end:left_pos+1 mode:3];
/* if our superview isn't a ClipView, no scrolling */
if ([superview respondsTo:@selector(rawScroll:)]) {
[superview getBounds:&oldRect];
/* scroll to selection */
[self scrollSelToVisible];
[superview getBounds:&newRect];
/* add some time for viewing if the text is scrolled */
if(newRect.origin.y != oldRect.origin.y) utime +=300000;
}
[[self window] display];
usleep(utime);
[self goto:right_pos end:right_pos mode:0];
/* scrollBack */
if ([superview respondsTo:@selector(rawScroll:)]) {
[self scrollTo:&oldRect.origin];
}
[self replaceSel:&LR[1] length:1];
return self;
}
- insertKeyCombination:(NXEvent *)event
{
char code[9];
int code_len = 0;
int cc = event->data.key.charCode;
int f_digit = event->data.key.charCode >> 4;
int s_digit = event->data.key.charCode & 0xf;
if ((event->flags & NX_ALPHASHIFTMASK) &&
!(event->flags & NX_SHIFTMASK)) code[code_len++] = 'l';
if (event->flags & NX_SHIFTMASK) code[code_len++] = 's';
if (event->flags & NX_CONTROLMASK) code[code_len++] = 'c';
if (event->flags & NX_ALTERNATEMASK) code[code_len++] = 'a';
if (event->flags & NX_COMMANDMASK) code[code_len++] = 'm';
if (event->flags & NX_NUMERICPADMASK) code[code_len++] = 'n';
if (event->flags & NX_HELPMASK) code[code_len++] = 'h';
if(NXIsPrint(cc) && !NXIsSpace(cc) && !NXIsCntrl(cc)){
/* should be able to print this character */
code[code_len++] = 0x27; // '
code[code_len++] = cc;
}
else if(NXIsCntrl(cc) && (event->flags & NX_CONTROLMASK)
&& !NXIsSpace(cc) && (cc <= 0x1F)){
/* ordinary control character */
code[code_len++] = 0x27; // '
code[code_len++] = (event->flags & NX_SHIFTMASK) ? cc + 0x40: cc+ 0x60;
}
else{
/* cannot print, replace with hex code */
code[code_len++] = (f_digit < 10) ? '0' + f_digit: 'A'+ f_digit-10;
code[code_len++] = (s_digit < 10) ? '0'+ s_digit : 'A'+ s_digit-10;
}
[self replaceSel:code length:code_len];
return self;
}
- insertKeyCombOfNextKey
{
static id action = nil;
if (!action)
action = [[XTEventMsgAction alloc]
initSel:@selector(insertKeyCombination:)];
nextAction = action;
return self;
}
- selectText:sender
{
[self setSel:0:0];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.