home *** CD-ROM | disk | FTP | other *** search
- #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
- {
- // 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
-