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