home *** CD-ROM | disk | FTP | other *** search
/ Nebula / nebula.bin / SourceCode / Classes / SampleClasses / ScrollText.m < prev    next >
Text File  |  1992-08-04  |  12KB  |  397 lines

  1. // -------------------------------------------------------------------------------------
  2. // ScrollText.m
  3. // Martin D. Flynn, NeXT Computer, Inc.
  4. // (see ScrollText.h for usage information)
  5. // -------------------------------------------------------------------------------------
  6.  
  7. #import <appkit/appkit.h>
  8. #import <libc.h>
  9. #import <mach/cthreads.h>
  10. #import <stdlib.h>
  11. #import <stdarg.h>
  12. #import <string.h>
  13. #import <pwd.h>
  14. #import <sys/types.h>
  15. #import <sys/wait.h>
  16. #import "ScrollText.h"
  17.  
  18. // -------------------------------------------------------------------------------------
  19. // null text attribute structure
  20. static NXColor          nullColor = { 0 };
  21. static textAttr_t       nullAttr = { (id)nil, 0 };
  22. #define isNullAttr(X)   (!X.fontId && !X.colorMode)
  23.  
  24. // -------------------------------------------------------------------------------------
  25. // main thread handle
  26. #define isMainTHREAD    (mainThread == cthread_self())
  27. static cthread_t        mainThread = (cthread_t)nil;
  28.  
  29. // -------------------------------------------------------------------------------------
  30. // Object thread support
  31. // Support is included to take advantage of the features found in objectThreadPerform.m.
  32. // While including objectThreadPerfrom.m in your application is not required to use
  33. // ScrollText, it is recommended if your app utilizes multiple threads.
  34. @interface Object(ThreadPerform)
  35. - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)wait;
  36. @end
  37.  
  38. // -------------------------------------------------------------------------------------
  39. // private methods
  40. @interface ScrollText(Private)
  41. - _setTextAttrFont:fontId color:(int)mode:(NXColor)color;
  42. - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr;
  43. - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr;
  44. - _appendTextToQueue:(const char*)buffer attr:(textAttr_t)tAttr;
  45. - _appendQueue:sender;
  46. - _delayedAppendQueue;
  47. - (int)_queueLength:(textQueue_t*)queue;
  48. - (int)_maxRunLength:(textQueue_t*)queue;
  49. - _stopCommand;
  50. - (char**)_resetToUser:(const char*)userName;
  51. - (int)_popen:(const char*)cmd cmdPath:(const char*)cmdPath user:(char*)userName;
  52. - (int)_pclose;
  53. - (void)_gotData;
  54. @end
  55.  
  56. // -------------------------------------------------------------------------------------
  57. /* convert color to gray */
  58. static float cvtColor2Gray(NXColor color)
  59. {
  60.     float   gray;
  61.     NXConvertColorToGray(color, &gray);
  62.     return gray;
  63. }
  64.  
  65. // -------------------------------------------------------------------------------------
  66. @implementation ScrollText
  67.  
  68. // -------------------------------------------------------------------------------------
  69. /* initializes the outlet (to be executed by main thread only!) */
  70. + newScrollText:anObject
  71. {
  72.     if (!mainThread) mainThread = cthread_self();
  73.     self             = [super new];
  74.     scrollView       = anObject;
  75.     textView         = [scrollView docView];
  76.     autoLf           = NO;
  77.     queueMutex       = mutex_alloc();
  78.     queueData        = (textQueue_t*)nil;
  79.     queueBack        = (textQueue_t*)nil;
  80.     runAttr          = nullAttr;
  81.     [textView setEditable:NO];
  82.     [textView setMonoFont:NO];
  83.     return self;
  84. }
  85.  
  86. /* return textView id (docView) */
  87. - docView
  88. {
  89.     return textView;
  90. }
  91.  
  92. /* return scroll view */
  93. - scrollView
  94. {
  95.     return scrollView;
  96. }
  97.  
  98. /* set auto linefeed mode */
  99. - setAutoLineFeed:(BOOL)mode
  100. {
  101.     autoLf = mode;
  102.     return self;
  103. }
  104.        
  105. // --------------------------------------------------------------------------------
  106.  
  107. /* set font */
  108. - _setTextAttrFont:fontId color:(int)mode:(NXColor)color
  109. {
  110.     textAttr_t  tAttr = nullAttr;
  111.     
  112.     /* init text attributes */
  113.     tAttr.fontId    = fontId;
  114.     tAttr.colorMode = mode;
  115.     tAttr.color     = color;
  116.     [self _appendTextAndMakeVisible:(char*)nil attr:tAttr];
  117.     
  118.     return self;
  119. }
  120.  
  121. /* set font */
  122. - setTextAttributeFont:fontId
  123. {
  124.     return [self _setTextAttrFont:fontId color:0:nullColor];
  125. }
  126.  
  127. /* set gray */
  128. - setTextAttributeGray:(float)aGray
  129. {
  130.     return [self _setTextAttrFont:(id)nil color:1:NXConvertGrayToColor(aGray)];
  131. }
  132.  
  133. /* set gray */
  134. - setTextAttributeColor:(NXColor)aColor
  135. {
  136.     return [self _setTextAttrFont:(id)nil color:2:aColor];
  137. }
  138.  
  139. // --------------------------------------------------------------------------------
  140.  
  141. /* clear text scroll view area */
  142. - clearScrollText
  143. {
  144.     [textView setEditable:YES];
  145.     [textView setText:""];
  146.     [textView setEditable:NO];
  147.     [scrollView display];
  148.     return self;
  149. }
  150.  
  151. /* delete lines */
  152. - deleteLinesFrom:(int)fLine to:(int)tLine
  153. {
  154.     [textView setEditable:YES];
  155.     [textView setSel:[textView positionFromLine:fLine]:[textView positionFromLine:tLine+1]];
  156.     [textView replaceSel:""];
  157.     [textView setEditable:NO];
  158.     [scrollView display];
  159.     return self;
  160. }
  161.  
  162. /* return current number of lines */
  163. - (int)textLines
  164. {
  165.     return [textView lineFromPosition:[textView textLength]];
  166. }
  167.     
  168. // --------------------------------------------------------------------------------
  169. // append text to scroll view
  170.  
  171. /* append buffer to view: return YES if text was visible */
  172. - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr
  173. {
  174.     int     txtLen;
  175.     NXSelPt startPt, endPt;
  176.     NXRect  rect;
  177.     
  178.     /* check for font/gray change (save state) */
  179.     if (tAttr.fontId) {
  180.         runAttr.fontId = tAttr.fontId;
  181.     }
  182.     if (tAttr.colorMode) {
  183.         runAttr.colorMode = tAttr.colorMode;
  184.         runAttr.color = tAttr.color;
  185.     }
  186.     if (!buffer || !*buffer || (len == 0)) return NO;
  187.     
  188.     /* get ready to print text */
  189.     [textView getVisibleRect:&rect];                      // visible rectangle
  190.     [textView setEditable:YES];
  191.     txtLen = [textView textLength];
  192.     [textView setSel:txtLen :txtLen];
  193.     [textView getSel:&startPt :&endPt];                   // selected coordinates
  194.  
  195.     /* set text run attributes if specified */
  196.     if (!isNullAttr(runAttr)) {
  197.         if (runAttr.fontId) [textView setSelFont:runAttr.fontId];
  198.         if (runAttr.colorMode == 1) [textView setSelGray:cvtColor2Gray(runAttr.color)]; else
  199.         if (runAttr.colorMode == 2) [textView setSelColor:runAttr.color];
  200.         runAttr = nullAttr;
  201.     }
  202.     
  203.     /* print text */
  204.     if (len > 0) [textView replaceSel:buffer length:len];
  205.     else [textView replaceSel:buffer];
  206.     [textView setEditable:NO];
  207.     
  208.     return (rect.origin.y + rect.size.height > endPt.y);  // was visible?
  209. }
  210.  
  211. /* append text to view and scroll to visible */
  212. - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr
  213. {
  214.     BOOL    wasVisible;
  215.   
  216.     /* if not main thread, append buffer to queue */
  217.     if (!isMainTHREAD) return [self _appendTextToQueue:buffer attr:tAttr];
  218.  
  219.     /* print queue contents */
  220.     [self _appendQueue:self];
  221.   
  222.     /* print buffer */
  223.     wasVisible = [self _appendTextToView:buffer len:-1 attr:tAttr];
  224.     if (autoLf && buffer) [self _appendTextToView:"\n" len:-1 attr:nullAttr];
  225.     if (wasVisible) [textView scrollSelToVisible];
  226.   
  227.     return self;
  228. }
  229.     
  230. // --------------------------------------------------------------------------------
  231. // queue up text to print / print text queue
  232.  
  233. /* return queue length */
  234. - (int)_queueLength:(textQueue_t*)queue
  235. {
  236.     int         len;
  237.     textQueue_t *qPtr;
  238.     qPtr = (queue)? queue: queueData;
  239.     for (len = 0; qPtr; qPtr = qPtr->next) {
  240.         if (qPtr->record) len += strlen(qPtr->record) + 1;
  241.     }
  242.     return len;
  243. }
  244.  
  245. /* return queue length */
  246. - (int)_maxRunLength:(textQueue_t*)queue
  247. {
  248.     int         len, maxLen;
  249.     textQueue_t *qPtr;
  250.     qPtr = (queue)? queue: queueData;
  251.     for (maxLen = len = 0; qPtr; qPtr = qPtr->next) {
  252.         if (!isNullAttr(qPtr->attr)) {
  253.             if (len > maxLen) maxLen = len;
  254.             len = 0;
  255.         }
  256.         if (qPtr->record) len += strlen(qPtr->record) + 1;
  257.     }
  258.     return MAX(maxLen, len);
  259. }
  260.  
  261. /* delayed append queue */
  262. - _delayedAppendQueue
  263. {
  264.     if ([self respondsTo:@selector(mainThreadPerform:with:wait:)])
  265.         [self mainThreadPerform:@selector(_appendQueue:) with:self wait:NO];
  266.     return self;
  267. }
  268.  
  269. /* add text string to queue (to be executed by non-main thread only) */
  270. - _appendTextToQueue:(const char*)buffer attr:(textAttr_t)tAttr
  271. {
  272.     textQueue_t *newData;
  273.  
  274.     /* load temporarily permanent buffer */
  275.     newData         = (textQueue_t*)malloc(sizeof(textQueue_t));
  276.     newData->record = buffer? NXCopyStringBuffer(buffer) : (char*)nil;
  277.     newData->attr   = tAttr;
  278.     newData->next   = (textQueue_t*)nil;
  279.   
  280.     /* lock the queue */
  281.     mutex_lock(queueMutex);
  282.   
  283.     /* add it to the queue */
  284.     if (!queueData) { queueData = newData; [self _delayedAppendQueue]; }
  285.     else queueBack->next = newData;
  286.     queueBack = newData;
  287.  
  288.     /* unlock the queue */
  289.     mutex_unlock(queueMutex);
  290.   
  291.     return self;
  292. }
  293.  
  294. /* print text queue contents (executed by main thread only!) */
  295. // needs to be fixed to support fonts/grays
  296. - _appendQueue:sender
  297. {
  298.     int         i;
  299.     char        *text;
  300.     textQueue_t *queue, *next;
  301.   
  302.     /* get pointer to queue list */
  303.     mutex_lock(queueMutex);
  304.     queue = queueData;
  305.     queueData = queueBack = (textQueue_t*)nil;
  306.     mutex_unlock(queueMutex);
  307.     if (!queue) return self;
  308.   
  309.     /* concatenate text */
  310.     text = (char*)malloc([self _maxRunLength:queue] + 1);
  311.     for (i = 0; queue;) {
  312.     
  313.         /* check for attribute change */
  314.         if (!isNullAttr(queue->attr)) { // print contents of 'text'
  315.             if ((i > 0) && [self _appendTextToView:text len:-1 attr:nullAttr])
  316.                 [textView scrollSelToVisible];
  317.             [self _appendTextToView:"" len:0 attr:queue->attr];
  318.             text[i = 0] = 0;
  319.         }
  320.         
  321.         /* append record to string buffer */
  322.         if (queue->record) {
  323.             i += sprintf(&text[i], "%s%s", queue->record, ((autoLf)? "\n": ""));
  324.             free(queue->record);
  325.         }
  326.         
  327.         /* next node */
  328.         next = queue->next;
  329.         free(queue);
  330.         queue = next;
  331.         
  332.     }
  333.   
  334.     /* place remaining text into scroll view */
  335.     if ([self _appendTextToView:text len:-1 attr:nullAttr]) [textView scrollSelToVisible];
  336.     free(text);
  337.     
  338.     return self;
  339. }
  340.  
  341. // --------------------------------------------------------------------------------
  342. // append a formatted text string message into a text view
  343. // input from ktaylor
  344.  
  345. /* append text with variable args into textView */
  346. - (int)textPrintf:(const char*)fmt args:(va_list)args
  347. {
  348.     char        *tempString;
  349.     int         retVal;
  350.     tempString = (char *)malloc(textStringSIZE); *tempString = 0;
  351.     retVal = vsprintf(tempString, fmt, args);
  352.     [self _appendTextAndMakeVisible:tempString attr:nullAttr];
  353.     free(tempString);     
  354.     return retVal;
  355. }
  356.  
  357. /* append text with variable args into textView */
  358. - (int)textPrintf:(const char*)fmt, ...
  359. {
  360.     va_list     args;
  361.     int         retVal;
  362.     va_start(args, fmt);
  363.     retVal = [self textPrintf:fmt args:args];     
  364.     va_end(args);
  365.     return retVal;
  366. }
  367.  
  368. /* append text with fprintf(...) style format */
  369. int textPrintf(id self, const char *fmt, ...)
  370. {
  371.     va_list     args;
  372.     int         retVal;
  373.     va_start(args, fmt);
  374.     retVal = [self textPrintf:fmt args:args]; 
  375.     va_end(args);
  376.     return retVal;
  377. }
  378.  
  379. // --------------------------------------------------------------------------------
  380. // view printing
  381.  
  382. /* prints the textView */
  383. - print:sender
  384. {
  385.     id      oldInfo, pInfo = [PrintInfo new];
  386.     [pInfo setVertCentered:NO];
  387.     oldInfo = [NXApp setPrintInfo:pInfo];
  388.     [textView lockFocus];
  389.     [textView printPSCode:self];
  390.     [textView unlockFocus];
  391.     [NXApp setPrintInfo:oldInfo];
  392.     [pInfo free];
  393.     return self;
  394. }
  395.   
  396. @end
  397.