home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / eText5 / Source / NewXTeXT / XText0.9beta2 / XText.subproj / XText.m < prev    next >
Encoding:
Text File  |  1995-07-28  |  10.9 KB  |  530 lines

  1. #import "XText.h"
  2.  
  3. /*    Some of this code is based on other emacs-like Text classes by
  4.     Julie Zelenski, Lee Boynton, and Glen Diener.
  5.  
  6.     There's some ugly hair in here; the Text object is not very well
  7.     designed to support this kind of stuff.  No doubt this will all be
  8.     fixed by NextStep 9.0 ...
  9. */
  10.  
  11. unsigned char GetPrevious(NXStream *s)
  12. {
  13.      int pos, ch;
  14.      
  15.      pos = NXTell(s);
  16.      if (pos <= 0) return EOF;
  17.      NXSeek(s, --pos, NX_FROMSTART);
  18.      ch = NXGetc(s);
  19.      NXUngetc(s);
  20.      return ch;
  21. }
  22.  
  23. @implementation XText(private)
  24.  
  25. - scrollTo:(const NXPoint *)newOrigin
  26. {
  27.     // superview = ClipView
  28.     // [superview superview] = ScrollView
  29.  
  30.     if([superview respondsTo:@selector(rawScroll:)]){
  31.         [[superview constrainScroll:(NXPoint *)newOrigin] rawScroll:newOrigin];
  32.         if ([[superview superview] respondsTo:@selector(reflectScroll:)]){
  33.             [[superview superview] reflectScroll:superview];// romeo romeo
  34.         }
  35.     }
  36.     return self;
  37. }        
  38. @end            
  39.  
  40. @implementation XText
  41.  
  42. - initFrame:(const NXRect *)frameRect text:(const char *)theText
  43.     alignment:(int)mode
  44. {
  45.     // i don't understand why the compiler whines without the (char *) here
  46.     [super initFrame:frameRect text:(const char *)theText alignment:mode];
  47.     posHint = -1;
  48.     xHintPos = -1;
  49.     return self;
  50. }
  51.  
  52. - goto:(int)pos end:(int)end mode:(int)mode
  53. {
  54.     int start;
  55.     
  56.     switch(mode) {
  57.  
  58.     case 0:        // move
  59.         [self setSel:pos :pos];
  60.         [self scrollSelToVisible];
  61.         posHint = -1;
  62.         break;
  63.  
  64.     case 1:        // delete
  65.     case 2:        // cut
  66.         if (pos != end) {
  67.             start = pos;
  68.             if (start > end)
  69.                 { start = end; end = pos; }
  70.             [self disableAutodisplay];
  71.             [self setSel:start :end];
  72.             if (mode == 1)
  73.                 [self delete:self];
  74.             else
  75.                 [self cut:self];
  76.         }
  77.         posHint = -1;
  78.         break;
  79.  
  80.     case 3:        // select
  81.         start = pos;
  82.         if (start > end)
  83.             { start = end; end = pos; }
  84.         // The Text object can't even extend the selection without flashing,
  85.         // unless we disable autodisplay
  86.         if (sp0.cp != spN.cp) [self disableAutodisplay];
  87.         [self setSel:start :end];
  88.         posHint = pos;
  89.         break;
  90.     }
  91.     xHintPos = -1;
  92.     return self;
  93. }
  94.  
  95. - moveChar:(int)cnt mode:(int)mode
  96. {
  97.     int pos, end;
  98.     int max = [self textLength];
  99.     
  100.     if (sp0.cp == posHint) {
  101.         pos = sp0.cp + cnt;
  102.         end = spN.cp;
  103.     } else {
  104.         pos = spN.cp + cnt;
  105.         end = sp0.cp;
  106.     }
  107.     if (pos < 0)
  108.         pos = 0;
  109.     else if (pos > max)
  110.         pos = max;
  111.     return [self goto:pos end:end mode:mode];
  112. }
  113.  
  114. /* NXBGetc - a text stream macro to get the previous character. */
  115.  
  116. typedef struct {
  117.     id text;
  118.     NXTextBlock *block;
  119. } textInfo;
  120.  
  121. static char getPrevious(NXStream *s)
  122. {
  123.     textInfo *info = (textInfo *) s->info;
  124.     NXTextBlock *block = info->block->prior;
  125.  
  126.     if (!block)
  127.         return EOF;
  128.     s->buf_base = block->text;
  129.     s->buf_ptr = s->buf_base + block->chars;
  130.     s->offset -= block->chars;
  131.     info->block = block;
  132.     return *(--s->buf_ptr);
  133. }
  134.  
  135. #define NXBGetc(s) \
  136.     (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
  137.                                        *(--((s)->buf_ptr)) )
  138.  
  139. - moveWord:(int)cnt mode:(int)mode
  140. {
  141.     NXStream *s = [self stream];
  142.     char c;
  143.     int i, pos, end;
  144.     unsigned char digit_cat = charCategoryTable['0'];
  145.     unsigned char alpha_cat = charCategoryTable['a'];
  146.     unsigned char c_cat;
  147.     BOOL inWord = NO;
  148.  
  149.     if (cnt == 0)
  150.         return self;
  151.     if (sp0.cp == posHint) {
  152.         pos = sp0.cp;
  153.         end = spN.cp;
  154.     } else {
  155.         pos = spN.cp;
  156.         end = sp0.cp;
  157.     }
  158.     NXSeek(s, pos, NX_FROMSTART);
  159.     i = (cnt<0 ? -cnt : cnt);
  160.     while (1) {
  161.         c = (cnt<0 ? NXBGetc(s) : NXGetc(s));
  162.         if (c == EOF) break;
  163.         c_cat = charCategoryTable[c];
  164.         if (c_cat==alpha_cat || c_cat==digit_cat)
  165.             inWord = YES;
  166.         else if (inWord) {
  167.             --i;
  168.             if (i > 0)
  169.                 inWord = NO;
  170.             else
  171.                 break;
  172.         }
  173.     }
  174.     pos = NXTell(s);
  175.     if (c != EOF)
  176.         pos += (cnt<0 ? 1 : -1);
  177.     return [self goto:pos end:end mode:mode];
  178. }
  179.  
  180. /*  line is from an NXSelPt; returns the length of the line.
  181.     too bad this requires grunging in Text's data structures */
  182.  
  183. #define LINE_LENGTH(line) \
  184.     (self->theBreaks->breaks[(line)/sizeof(NXLineDesc)] & 0x3fff)
  185.  
  186. - moveLine:(int)cnt mode:(int)mode
  187. {
  188.     int pos, end, x, dir;
  189.  
  190.     if (sp0.cp == posHint) {
  191.         pos = sp0.cp;
  192.         end = spN.cp;
  193.     } else {
  194.         pos = spN.cp;
  195.         end = sp0.cp;
  196.     }
  197.     if (mode != 0)
  198.         [self disableAutodisplay];
  199.     // collapse and normalize the selection
  200.     [self setSel:pos :pos];
  201.     x = (sp0.cp == xHintPos ? xHint : (sp0.cp - sp0.c1st));
  202.     
  203.     if (cnt < 0) {
  204.         dir = NX_UP;
  205.         cnt = -cnt;
  206.     } else {
  207.         dir = NX_DOWN;
  208.     }
  209.     for (; cnt > 0; --cnt)
  210.         [self moveCaret: dir];
  211.  
  212.     pos = LINE_LENGTH(sp0.line)-1;
  213.     if (x < pos)
  214.         pos = x;
  215.     pos += sp0.c1st;
  216.     [self goto:pos end:end mode:mode];
  217.     xHintPos = pos;
  218.     xHint = x;
  219.     return self;
  220. }
  221.  
  222. - lineBegin:(int)mode
  223. {
  224.     int pos, end;
  225.  
  226.     if (sp0.cp == posHint) {
  227.         pos = sp0.c1st;
  228.         end = spN.cp;
  229.     } else {
  230.         pos = spN.c1st;
  231.         // Text is inconsistent about what line it thinks we're on
  232.         if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
  233.             pos = spN.cp;
  234.         }
  235.         end = sp0.cp;
  236.     }
  237.     return [self goto:pos end:end mode:mode];
  238. }
  239.  
  240. - lineEnd:(int)mode
  241. {
  242.     NXSelPt *sp;
  243.     int pos, end;
  244.  
  245.     if (sp0.cp == posHint) {
  246.         sp = &sp0;
  247.         end = spN.cp;
  248.     } else {
  249.         // need to correct for TBD
  250.         sp = &spN;
  251.         end = sp0.cp;
  252.     }
  253.     pos = sp->c1st + LINE_LENGTH(sp->line) - 1;
  254.     if (pos < sp->cp) {
  255.         // Text is being flakey again; we really want to be on the next line
  256.         // this is pretty gross
  257.         pos = sp->line;
  258.         if (theBreaks->breaks[pos/sizeof(NXLineDesc)] < 0)
  259.             pos += sizeof(NXHeightChange);
  260.         else
  261.             pos += sizeof(NXLineDesc);
  262.         pos = sp->cp + LINE_LENGTH(pos) - 1;
  263.     }
  264.     if ((pos == sp->cp) && (mode != 0))
  265.         ++pos;
  266.     return [self goto:pos end:end mode:mode];
  267. }
  268.  
  269. - docBegin:(int)mode
  270. {
  271.     return [self goto:0
  272.                  end:(sp0.cp == posHint ? spN.cp : sp0.cp)
  273.                  mode:mode];
  274. }
  275.  
  276. - docEnd:(int)mode
  277. {
  278.     return [self goto:[self textLength]
  279.                  end:(sp0.cp == posHint ? spN.cp : sp0.cp)
  280.                  mode:mode];
  281. }
  282.  
  283. - collapseSel:(int)dir
  284. {
  285.     int pos;
  286.  
  287.     if ((dir < 0) || ((dir == 0) && (sp0.cp == posHint)))
  288.         pos = sp0.cp;
  289.     else
  290.         pos = spN.cp;
  291.     return [self goto:pos end:pos mode:0];
  292. }
  293.  
  294. - transChars
  295. {
  296.     int pos = sp0.cp;
  297.     char buf[2], temp;
  298.  
  299.     if (pos == spN.cp) {
  300.         if (pos == (sp0.c1st + LINE_LENGTH(sp0.line) - 1))
  301.             --pos;
  302.         if (pos > 0)
  303.             if ([self getSubstring:buf start:pos-1 length:2] == 2) {
  304.                 temp = buf[1]; buf[1] = buf[0]; buf[0] = temp;
  305.                 [self disableAutodisplay];
  306.                 [self setSel:pos-1 :pos+1];
  307.                 [self replaceSel:buf length:2];
  308.                 return self;
  309.             }
  310.     }
  311.     NXBeep();
  312.     return self;
  313. }
  314.  
  315. - openLine
  316. {
  317.     int pos = sp0.cp;
  318.  
  319.     // don't do anything if there's a non-empty selection
  320.     if (pos == spN.cp) {
  321.         [self replaceSel:"\n"];
  322.         [self setSel:pos :pos];
  323.     } else
  324.         NXBeep();
  325.     return self;
  326. }
  327.  
  328.             
  329. - scroll:(int)pages :(int)lines
  330. {
  331.     NXRect r;
  332.  
  333.     // if our superview isn't a ClipView, we can't scroll
  334.     if ([superview respondsTo:@selector(rawScroll:)]) {
  335.         [superview getBounds:&r];
  336.         r.origin.y += pages*r.size.height + lines*[self lineHeight];
  337.         [self scrollTo:&r.origin];
  338.     } else
  339.         NXBeep();
  340.     return self;
  341. }
  342.  
  343. - scrollIfRO:(int)pages :(int)lines
  344. {
  345.     if (![self isEditable])
  346.         return [self scroll:pages :lines];
  347.     else
  348.         return nil;
  349. }
  350.  
  351. - insertChar:(NXEvent *)event
  352. {
  353.     char c;
  354.  
  355.     c = event->data.key.charCode;
  356.     [self replaceSel:&c length:1];
  357.     return self;
  358. }
  359.  
  360. - insertNextChar
  361. {
  362.     static id action = nil;
  363.  
  364.     if (!action)
  365.         action = [[XTEventMsgAction allocFromZone:[NXApp zone]]
  366.                             initSel:@selector(insertChar:)];
  367.     nextAction = action;
  368.     return self;
  369. }
  370.  
  371. - autoIndent
  372. {
  373.     int pos, end;
  374.     unsigned char buf[2];
  375.  
  376.     // don't do anything if there's a non-empty selection
  377.     if (sp0.cp != spN.cp) {
  378.         NXBeep();
  379.         return self;
  380.     }
  381.     
  382.     if (sp0.cp == posHint) {
  383.         pos = sp0.c1st;
  384.         end = spN.cp;
  385.     } else {
  386.         pos = spN.c1st;
  387.         // Text is inconsistent about what line it thinks we're on
  388.         if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
  389.             pos = spN.cp;
  390.         }
  391.         end = sp0.cp;
  392.     }
  393.     
  394.     [[self hideCaret] disableAutodisplay];  // no need to display yet
  395.     [self replaceSel:"\n" length:1];
  396.  
  397.     while ([self getSubstring:buf start:pos++ length:1] != -1){
  398.         if(buf[0] == ' ' || buf[0] == '\t') [self replaceSel:buf length:1];
  399.         else if(pos == end) break;
  400.         else break;
  401.     }
  402.     
  403.     [[self setAutodisplay:YES] displayIfNeeded];
  404.  
  405.     /* scroll down to the correct line */
  406.     if ([superview respondsTo:@selector(rawScroll:)]) {
  407.         [self scroll:0 :1];
  408.         [self calcLine];
  409.     }
  410.      
  411.     return self;
  412. }
  413.  
  414. - match:(unsigned char *)LR
  415. {
  416.     NXRect oldRect, newRect;
  417.     unsigned char buf[2];
  418.     int count, left_pos, right_pos, utime;        
  419.  
  420.     right_pos = sp0.cp; 
  421.     left_pos = right_pos-1;
  422.     count = 1;
  423.     utime = 300000;
  424.  
  425.     /*    don't do anything if there's a non-empty selection
  426.      *     or not two character */
  427.     
  428.     if (sp0.cp != spN.cp || strlen(LR) != 2) return self;
  429.     
  430.     /* at the beginning of file ? */
  431.     if (left_pos < 0){
  432.         [self replaceSel:&LR[1] length:1];
  433.         return self;        
  434.     }
  435.        
  436.     /* search for the left character */ 
  437.     while([self getSubstring:buf start:left_pos length:1] != -1){
  438.         if(buf[0] == LR[0]) count--;
  439.         else if (buf[0] == LR[1])  count++;
  440.         if(count == 0) break;
  441.         if(left_pos-- == 0) break;
  442.     }
  443.     
  444.     if(count != 0)  {
  445.         [self replaceSel:&LR[1] length:1];
  446.         return self;
  447.     }
  448.  
  449.  
  450.     [self goto:left_pos end:left_pos+1 mode:3];
  451.  
  452.     /* if our superview isn't a ClipView, no scrolling */
  453.     if ([superview respondsTo:@selector(rawScroll:)]) {
  454.         [superview getBounds:&oldRect];
  455.  
  456.         /* scroll to selection */
  457.         [self scrollSelToVisible];
  458.         [superview getBounds:&newRect];
  459.         
  460.         /* add some time for viewing if the text is scrolled */
  461.         if(newRect.origin.y != oldRect.origin.y) utime +=300000;        
  462.     }
  463.  
  464.     [[self window] display];
  465.     
  466.     usleep(utime);
  467.     
  468.     [self goto:right_pos end:right_pos mode:0];    
  469.         
  470.     /* scrollBack */
  471.     if ([superview respondsTo:@selector(rawScroll:)]) {
  472.         [self scrollTo:&oldRect.origin];
  473.     }
  474.         
  475.     [self replaceSel:&LR[1] length:1];
  476.     
  477.     return self;
  478. }
  479.  
  480. - insertKeyCombination:(NXEvent *)event
  481. {
  482.     char code[9];
  483.     int code_len = 0;
  484.     int cc = event->data.key.charCode;
  485.     int f_digit = event->data.key.charCode >> 4;
  486.     int s_digit = event->data.key.charCode & 0xf;
  487.  
  488.     if ((event->flags & NX_ALPHASHIFTMASK) &&
  489.         !(event->flags & NX_SHIFTMASK))     code[code_len++] = 'l';
  490.     if (event->flags & NX_SHIFTMASK)         code[code_len++] = 's';
  491.     if (event->flags & NX_CONTROLMASK)       code[code_len++] = 'c';
  492.     if (event->flags & NX_ALTERNATEMASK)     code[code_len++] = 'a';
  493.     if (event->flags & NX_COMMANDMASK)       code[code_len++] = 'm';
  494.     if (event->flags & NX_NUMERICPADMASK)    code[code_len++] = 'n';
  495.     if (event->flags & NX_HELPMASK)            code[code_len++] = 'h';
  496.  
  497.     if(NXIsPrint(cc) && !NXIsSpace(cc) && !NXIsCntrl(cc)){
  498.         /* should be able to print this character */
  499.         code[code_len++] = 0x27; // '
  500.         code[code_len++] = cc;
  501.     }
  502.     else if(NXIsCntrl(cc) && (event->flags & NX_CONTROLMASK) 
  503.             && !NXIsSpace(cc) && (cc <= 0x1F)){
  504.         /* ordinary control character */
  505.         code[code_len++] = 0x27; // '
  506.         code[code_len++] = (event->flags & NX_SHIFTMASK) ? cc + 0x40: cc+ 0x60;
  507.     }
  508.     else{
  509.         /* cannot print, replace with hex code */
  510.         code[code_len++] = (f_digit < 10) ? '0' + f_digit: 'A'+ f_digit-10;
  511.         code[code_len++] = (s_digit < 10) ? '0'+ s_digit : 'A'+ s_digit-10;
  512.     }
  513.     
  514.     [self replaceSel:code length:code_len];
  515.     return self;
  516. }
  517.  
  518. - insertKeyCombOfNextKey
  519. {
  520.     static id action = nil;
  521.  
  522.     if (!action)
  523.         action = [[XTEventMsgAction alloc]
  524.                         initSel:@selector(insertKeyCombination:)];
  525.     nextAction = action;
  526.     return self;
  527. }
  528.  
  529. @end
  530.