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; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.