home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // ScrollText.m
- // Martin D. Flynn, NeXT Computer, Inc.
- // (see ScrollText.h for usage information)
- // -------------------------------------------------------------------------------------
-
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <mach/cthreads.h>
- #import <stdlib.h>
- #import <stdarg.h>
- #import <string.h>
- #import <pwd.h>
- #import <sys/types.h>
- #import <sys/wait.h>
- #import "ScrollText.h"
-
- // -------------------------------------------------------------------------------------
- // null text attribute structure
- static NXColor nullColor = { 0 };
- static textAttr_t nullAttr = { (id)nil, 0 };
- #define isNullAttr(X) (!X.fontId && !X.colorMode)
-
- // -------------------------------------------------------------------------------------
- // main thread handle
- #define isMainTHREAD (mainThread == cthread_self())
- static cthread_t mainThread = (cthread_t)nil;
-
- // -------------------------------------------------------------------------------------
- // Object thread support
- // Support is included to take advantage of the features found in objectThreadPerform.m.
- // While including objectThreadPerfrom.m in your application is not required to use
- // ScrollText, it is recommended if your app utilizes multiple threads.
- @interface Object(ThreadPerform)
- - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)wait;
- @end
-
- // -------------------------------------------------------------------------------------
- // private methods
- @interface ScrollText(Private)
- - _setTextAttrFont:fontId color:(int)mode:(NXColor)color;
- - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr;
- - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr;
- - _appendTextToQueue:(const char*)buffer attr:(textAttr_t)tAttr;
- - _appendQueue:sender;
- - _delayedAppendQueue;
- - (int)_queueLength:(textQueue_t*)queue;
- - (int)_maxRunLength:(textQueue_t*)queue;
- - _stopCommand;
- - (char**)_resetToUser:(const char*)userName;
- - (int)_popen:(const char*)cmd cmdPath:(const char*)cmdPath user:(char*)userName;
- - (int)_pclose;
- - (void)_gotData;
- @end
-
- // -------------------------------------------------------------------------------------
- /* convert color to gray */
- static float cvtColor2Gray(NXColor color)
- {
- float gray;
- NXConvertColorToGray(color, &gray);
- return gray;
- }
-
- // -------------------------------------------------------------------------------------
- @implementation ScrollText
-
- // -------------------------------------------------------------------------------------
- /* initializes the outlet (to be executed by main thread only!) */
- + newScrollText:anObject
- {
- if (!mainThread) mainThread = cthread_self();
- self = [super new];
- scrollView = anObject;
- textView = [scrollView docView];
- autoLf = NO;
- queueMutex = mutex_alloc();
- queueData = (textQueue_t*)nil;
- queueBack = (textQueue_t*)nil;
- runAttr = nullAttr;
- [textView setEditable:NO];
- [textView setMonoFont:NO];
- return self;
- }
-
- /* return textView id (docView) */
- - docView
- {
- return textView;
- }
-
- /* return scroll view */
- - scrollView
- {
- return scrollView;
- }
-
- /* set auto linefeed mode */
- - setAutoLineFeed:(BOOL)mode
- {
- autoLf = mode;
- return self;
- }
-
- // --------------------------------------------------------------------------------
-
- /* set font */
- - _setTextAttrFont:fontId color:(int)mode:(NXColor)color
- {
- textAttr_t tAttr = nullAttr;
-
- /* init text attributes */
- tAttr.fontId = fontId;
- tAttr.colorMode = mode;
- tAttr.color = color;
- [self _appendTextAndMakeVisible:(char*)nil attr:tAttr];
-
- return self;
- }
-
- /* set font */
- - setTextAttributeFont:fontId
- {
- return [self _setTextAttrFont:fontId color:0:nullColor];
- }
-
- /* set gray */
- - setTextAttributeGray:(float)aGray
- {
- return [self _setTextAttrFont:(id)nil color:1:NXConvertGrayToColor(aGray)];
- }
-
- /* set gray */
- - setTextAttributeColor:(NXColor)aColor
- {
- return [self _setTextAttrFont:(id)nil color:2:aColor];
- }
-
- // --------------------------------------------------------------------------------
-
- /* clear text scroll view area */
- - clearScrollText
- {
- [textView setEditable:YES];
- [textView setText:""];
- [textView setEditable:NO];
- [scrollView display];
- return self;
- }
-
- /* delete lines */
- - deleteLinesFrom:(int)fLine to:(int)tLine
- {
- [textView setEditable:YES];
- [textView setSel:[textView positionFromLine:fLine]:[textView positionFromLine:tLine+1]];
- [textView replaceSel:""];
- [textView setEditable:NO];
- [scrollView display];
- return self;
- }
-
- /* return current number of lines */
- - (int)textLines
- {
- return [textView lineFromPosition:[textView textLength]];
- }
-
- // --------------------------------------------------------------------------------
- // append text to scroll view
-
- /* append buffer to view: return YES if text was visible */
- - (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr
- {
- int txtLen;
- NXSelPt startPt, endPt;
- NXRect rect;
-
- /* check for font/gray change (save state) */
- if (tAttr.fontId) {
- runAttr.fontId = tAttr.fontId;
- }
- if (tAttr.colorMode) {
- runAttr.colorMode = tAttr.colorMode;
- runAttr.color = tAttr.color;
- }
- if (!buffer || !*buffer || (len == 0)) return NO;
-
- /* get ready to print text */
- [textView getVisibleRect:&rect]; // visible rectangle
- [textView setEditable:YES];
- txtLen = [textView textLength];
- [textView setSel:txtLen :txtLen];
- [textView getSel:&startPt :&endPt]; // selected coordinates
-
- /* set text run attributes if specified */
- if (!isNullAttr(runAttr)) {
- if (runAttr.fontId) [textView setSelFont:runAttr.fontId];
- if (runAttr.colorMode == 1) [textView setSelGray:cvtColor2Gray(runAttr.color)]; else
- if (runAttr.colorMode == 2) [textView setSelColor:runAttr.color];
- runAttr = nullAttr;
- }
-
- /* print text */
- if (len > 0) [textView replaceSel:buffer length:len];
- else [textView replaceSel:buffer];
- [textView setEditable:NO];
-
- return (rect.origin.y + rect.size.height > endPt.y); // was visible?
- }
-
- /* append text to view and scroll to visible */
- - _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr
- {
- BOOL wasVisible;
-
- /* if not main thread, append buffer to queue */
- if (!isMainTHREAD) return [self _appendTextToQueue:buffer attr:tAttr];
-
- /* print queue contents */
- [self _appendQueue:self];
-
- /* print buffer */
- wasVisible = [self _appendTextToView:buffer len:-1 attr:tAttr];
- if (autoLf && buffer) [self _appendTextToView:"\n" len:-1 attr:nullAttr];
- if (wasVisible) [textView scrollSelToVisible];
-
- return self;
- }
-
- // --------------------------------------------------------------------------------
- // queue up text to print / print text queue
-
- /* return queue length */
- - (int)_queueLength:(textQueue_t*)queue
- {
- int len;
- textQueue_t *qPtr;
- qPtr = (queue)? queue: queueData;
- for (len = 0; qPtr; qPtr = qPtr->next) {
- if (qPtr->record) len += strlen(qPtr->record) + 1;
- }
- return len;
- }
-
- /* return queue length */
- - (int)_maxRunLength:(textQueue_t*)queue
- {
- int len, maxLen;
- textQueue_t *qPtr;
- qPtr = (queue)? queue: queueData;
- for (maxLen = len = 0; qPtr; qPtr = qPtr->next) {
- if (!isNullAttr(qPtr->attr)) {
- if (len > maxLen) maxLen = len;
- len = 0;
- }
- if (qPtr->record) len += strlen(qPtr->record) + 1;
- }
- return MAX(maxLen, len);
- }
-
- /* delayed append queue */
- - _delayedAppendQueue
- {
- if ([self respondsTo:@selector(mainThreadPerform:with:wait:)])
- [self mainThreadPerform:@selector(_appendQueue:) with:self wait:NO];
- return self;
- }
-
- /* add text string to queue (to be executed by non-main thread only) */
- - _appendTextToQueue:(const char*)buffer attr:(textAttr_t)tAttr
- {
- textQueue_t *newData;
-
- /* load temporarily permanent buffer */
- newData = (textQueue_t*)malloc(sizeof(textQueue_t));
- newData->record = buffer? NXCopyStringBuffer(buffer) : (char*)nil;
- newData->attr = tAttr;
- newData->next = (textQueue_t*)nil;
-
- /* lock the queue */
- mutex_lock(queueMutex);
-
- /* add it to the queue */
- if (!queueData) { queueData = newData; [self _delayedAppendQueue]; }
- else queueBack->next = newData;
- queueBack = newData;
-
- /* unlock the queue */
- mutex_unlock(queueMutex);
-
- return self;
- }
-
- /* print text queue contents (executed by main thread only!) */
- // needs to be fixed to support fonts/grays
- - _appendQueue:sender
- {
- int i;
- char *text;
- textQueue_t *queue, *next;
-
- /* get pointer to queue list */
- mutex_lock(queueMutex);
- queue = queueData;
- queueData = queueBack = (textQueue_t*)nil;
- mutex_unlock(queueMutex);
- if (!queue) return self;
-
- /* concatenate text */
- text = (char*)malloc([self _maxRunLength:queue] + 1);
- for (i = 0; queue;) {
-
- /* check for attribute change */
- if (!isNullAttr(queue->attr)) { // print contents of 'text'
- if ((i > 0) && [self _appendTextToView:text len:-1 attr:nullAttr])
- [textView scrollSelToVisible];
- [self _appendTextToView:"" len:0 attr:queue->attr];
- text[i = 0] = 0;
- }
-
- /* append record to string buffer */
- if (queue->record) {
- i += sprintf(&text[i], "%s%s", queue->record, ((autoLf)? "\n": ""));
- free(queue->record);
- }
-
- /* next node */
- next = queue->next;
- free(queue);
- queue = next;
-
- }
-
- /* place remaining text into scroll view */
- if ([self _appendTextToView:text len:-1 attr:nullAttr]) [textView scrollSelToVisible];
- free(text);
-
- return self;
- }
-
- // --------------------------------------------------------------------------------
- // append a formatted text string message into a text view
- // input from ktaylor
-
- /* append text with variable args into textView */
- - (int)textPrintf:(const char*)fmt args:(va_list)args
- {
- char *tempString;
- int retVal;
- tempString = (char *)malloc(textStringSIZE); *tempString = 0;
- retVal = vsprintf(tempString, fmt, args);
- [self _appendTextAndMakeVisible:tempString attr:nullAttr];
- free(tempString);
- return retVal;
- }
-
- /* append text with variable args into textView */
- - (int)textPrintf:(const char*)fmt, ...
- {
- va_list args;
- int retVal;
- va_start(args, fmt);
- retVal = [self textPrintf:fmt args:args];
- va_end(args);
- return retVal;
- }
-
- /* append text with fprintf(...) style format */
- int textPrintf(id self, const char *fmt, ...)
- {
- va_list args;
- int retVal;
- va_start(args, fmt);
- retVal = [self textPrintf:fmt args:args];
- va_end(args);
- return retVal;
- }
-
- // --------------------------------------------------------------------------------
- // view printing
-
- /* prints the textView */
- - print:sender
- {
- id oldInfo, pInfo = [PrintInfo new];
- [pInfo setVertCentered:NO];
- oldInfo = [NXApp setPrintInfo:pInfo];
- [textView lockFocus];
- [textView printPSCode:self];
- [textView unlockFocus];
- [NXApp setPrintInfo:oldInfo];
- [pInfo free];
- return self;
- }
-
- @end
-