This is SpyTextFieldCell.m in view mode; [Download] [Up]
/*
* This sourcecode is part of FileSpy, a logfile observing utility.
* Copyright (C) 1996 Felix Rauch
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Notice that this program may not be possessed, used, copied,
* distributed or modified by people having to do with nuclear
* weapons. See the file CONDITIONS for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* To contact the original author, try:
* e-mail: Felix.Rauch@nice.ch
* Traditional mail: Felix Rauch
* Sempacherstrasse 33
* 8032 Zurich
* Switzerland
*/
#import <appkit/Text.h>
#import <appkit/ScrollView.h>
#import <appkit/graphics.h>
#import "SpyTextFieldCell.h"
#define MAXSIZE 1.0e38 // Maximum size of a text object
#define MAXSLOWDOWN 32 // maximum of file updates to ignore in case of nfs-errors (could be a default-value). Must be power of 2!
@implementation SpyTextFieldCell
static List *tmpList = nil; // list for selected cells in exMatrix
+ (void)setSharedTmpList:newList
{
tmpList = newList;
}
+ sharedTmpList
{
return tmpList;
}
- init
{
[super init];
fileLenght = lastFileLenght = 0;
fileex = oldfileex = NO;
textloaded = NO;
dirloaded = NO;
firstTime = 2;
permissionOK = oldpermissionOK = YES;
maxLines = 0;
zeroFlag = NO;
nrEINVAL = 0;
// init font object
myFont = [Font newFont:"Ohlfs"
size:10
style:0
matrix:NX_FLIPPEDMATRIX];
[Text setDefaultFont:myFont];
myDoc = [Document new:self];
myWindow = [[myDoc scrollView] window];
myScrollView = [myDoc scrollView];
myText = [[myDoc scrollView] docView];
[myWindow setFreeWhenClosed:NO];
color = NX_COLORBLACK;
return self;
}
- delete
{
if(exCell) {
int row, col;
[exMatrix getRow:&row andCol:&col ofCell:exCell];
[exMatrix removeRowAt:row andFree:YES];
}
[myDoc free];
[myWindow setDelegate:nil];
[myWindow setFreeWhenClosed:YES];
[myWindow close];
return self;
}
- free
{
return [super free];
}
- (long int)lastFileLenght
{
return lastFileLenght;
}
- setLastFileLenght:(long int)lenght
{
lastFileLenght = lenght;
return self;
}
- (BOOL)fileExists
{
return fileex;
}
- updateFile
// This is important. It checks the file and looks whether something has changed with it
{
if(nrEINVAL > 0) { // if we had nfs errors recently then wait for next check with
// exponentially increasing time between two checks (proposed by mwa)
unsigned int i = MAXSLOWDOWN;
while((i > 0) && (i >= nrEINVAL)) {
if(nrEINVAL % i == 0)
break;
i /= 2;
}
if(nrEINVAL % i != 0) {
nrEINVAL++;
return self;
}
}
oldfileex = fileex;
oldpermissionOK = permissionOK;
if(firstTime > 0) { // if we're here for the first or second time
// firstTime has the following meanings: 2: first time here
// 1: second time here
// 0: nth time here, no more special cases
struct stat st;
if(stat([self stringValue], &st) < 0) {
switch(errno) {
case ENOENT: // file doesn't exist
case ENOTDIR: // part of path-prefix is not a directory, so file doesn't exist
fileType = FILETYPE_NOTHING;
break;
case EACCES:
fileType = FILETYPE_NOTHING;
if((firstTime == 2) && !(spytype & SPY_EXISTENCE))
printf("filespy: no permission for %s\n", [self stringValue]);
permissionOK = NO;
break;
case EINVAL: // in case of 'nfs-server blafasel not responding still trying'
if(nrEINVAL++ == 0) {
perror("filespy: stat");
}
break;
default:
perror("filespy: stat");
fileex = NO;
}
firstTime = 1; // next time we will be here for the second time
} else { // stat() was ok
fileex = YES; // yes, the file exists
permissionOK = YES; // yes, we can read the file
nrEINVAL = 0; // no more nfs errors
if(st.st_mode & S_IFDIR) { // the file is a deriectory
fileType = FILETYPE_DIR;
lastmtime = (time_t)0; // as we're here for the first or second time, we haven't checked the file before
if((firstTime == 1) && !(spytype & SPY_EXISTENCE)) { // if dir was created and we're not spying for ex.
printf("filespy: created directory %s\n", [self stringValue]); // (if we were spying for existence,
// then this would be noted in the existence-log, so we wouldn't need a printf)
}
} else if(st.st_mode & S_IFREG) { // the file is a regular file
fileType = FILETYPE_FILE;
fileLenght = st.st_size; // remember its current size
if(fileMode == RADIO_ENTIRE) // if we will have to read the whole content,
lastFileLenght = 0; // just pretend that the file grew from 0 length.
else if(fileMode == RADIO_NEW) // otherwise, this is the initial file length
lastFileLenght = fileLenght;
if((firstTime == 1) && (fileLenght == 0) && !(spytype & SPY_EXISTENCE)) {
// if we're here for the second time (i.e. the file didn't exist before) and the new file's length
// is 0 bytes and we're not spying for existence, we have to make the user aware of the new file.
printf("filespy: created empty file %s\n", [self stringValue]);
}
} else {
printf("filespy: not supported filetype in file %s\n", [self stringValue]); // what should I do with other filetypes?
}
firstTime = 0; // now we can handle this file in the regular way
}
} else if(fileType == FILETYPE_FILE) { // this is a regular file
struct stat st;
if(stat([self stringValue], &st) < 0) {
switch (errno) {
case ENOENT: // no such file or directory
case ENOTDIR: // some part of the path changed from directory to file -> file can't exist anymore
[self fileRemoved];
lastFileLenght = fileLenght;
fileLenght = 0;
firstTime = 2; // well, the file doesn't exist anymore, so it's a special case again
if(!permissionOK) { // if we didn't have permission before...
if(!(spytype & SPY_EXISTENCE)) {
printf("filespy: regained permission for not existing file %s\n", [self stringValue]);
// I'm sure whether this message makes much sense...
}
permissionOK = YES; // we have permissions (well, sort of..)
}
break;
case EACCES: // access denied
if(permissionOK) {
if(!(spytype & SPY_EXISTENCE))
printf("filespy: lost permission for file %s\n", [self stringValue]);
permissionOK = NO;
}
break;
case EINVAL: // nfs server not responding
if(nrEINVAL++ == 0) { // do not report nfs-error each time the file is checked
perror("filespy: stat");
}
break;
default:
perror("filespy: stat");
}
} else { // stat() was ok
nrEINVAL = 0; // no more nfs errors
if(!permissionOK) { // if we didn't have permission before
if(!(spytype & SPY_EXISTENCE))
printf("filespy: regained permission for file %s\n", [self stringValue]);
permissionOK = YES;
}
if(!(st.st_mode & S_IFREG)) { // the file is no longer a regular file
[self fileRemoved];
lastFileLenght = fileLenght;
fileLenght = 0;
firstTime = 2;
} else {
lastFileLenght = fileLenght;
fileLenght = st.st_size;
fileex = YES; // yes, the file exists
}
}
} else if(fileType == FILETYPE_DIR) { // this file is a directory
struct stat st;
if(stat([self stringValue], &st) < 0) {
switch(errno) {
case ENOENT:
case ENOTDIR:
[self fileRemoved];
lastmtime = mtime;
firstTime = 2;
if(!permissionOK) {
if(!(spytype & SPY_EXISTENCE))
printf("filespy: regained permission for not existing directory %s\n", [self stringValue]);
permissionOK = YES;
}
break;
case EACCES:
if(permissionOK) {
if(!(spytype & SPY_EXISTENCE))
printf("filespy: lost permission for directory %s\n", [self stringValue]);
permissionOK = NO;
}
break;
case EINVAL: // nfs server not responding
if(nrEINVAL++ == 0) {
perror("filespy: stat");
}
break;
default:
perror("filespy: stat");
}
} else {
nrEINVAL = 0; // no more nfs errors
if(!permissionOK) {
if(!(spytype & SPY_EXISTENCE))
printf("filespy: regained permission for directory %s\n", [self stringValue]);
permissionOK = YES;
}
if(!(st.st_mode & S_IFDIR)) { // this file is no longer a directory
[self fileRemoved];
lastmtime = mtime;
firstTime = 2;
} else {
oldfileex = fileex;
lastmtime = mtime;
mtime = st.st_mtime; // when was the directory last modified?
if(!dirStore) { // if we don't have the directory's contents yet, then read them
DIR *dirp;
struct direct *dp;
// read all filenames and put them in dirStore
dirp = opendir([self stringValue]);
if(dirp != NULL) {
dirStore = [[Storage alloc]
initCount:0 elementSize:MAXFILENAMELENGHT
description:"[MAXFILENAMELENGHT c]"];
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
[dirStore addElement:dp->d_name];
closedir(dirp);
}
}
}
}
}
if((spytype & SPY_EXISTENCE) && fileex) {
time_t myTime = time(NULL);
[exCell changeTime:myTime];
}
return self;
}
- updateText
// This one checks whether it is necessary to read the contents of the file or update the existencewindow
{
if((fileType == FILETYPE_FILE) && (spytype & SPY_CONTENTS))
[self readText];
else if((fileType == FILETYPE_DIR) && (spytype & SPY_CONTENTS))
[self readDir];
if(spytype & SPY_EXISTENCE)
[self updateExistence];
return self;
}
- readText
// read contents of changed regular file
{
if(fileLenght < lastFileLenght) { // if the file shrunk
if((fileLenght > 0) || zeroFlag) { // if there's still something in it or it had 0 length last time we checked...
// (we do not report that a file has shrunken to 0 bytes the first time we notice it. there are
// commands in /usr/adm/daily which produce length-0-files for a very short time (e.g. with 'tail'))
printf("filespy: file %s reduced its size from %ld to %ld bytes\n", [self stringValue], lastFileLenght, fileLenght);
switch(fileMode) {
case RADIO_NEW: lastFileLenght = fileLenght; break;
case RADIO_ENTIRE: lastFileLenght = 0; break;
}
zeroFlag = NO;
} else {
zeroFlag = YES; // first check that this file has length 0 for a longer time
fileLenght = lastFileLenght; // ignore new filelength it until next check
}
[self contentChanged]; // however, something did change
}
if(fileLenght > lastFileLenght) { // if the file grew then get the new text and send it to addText
int diff = fileLenght - lastFileLenght;
char *newString = malloc(diff + 2);
FILE *fp;
if((fp = fopen([self stringValue], "r")) == (FILE *)NULL) {
printf("filespy: couldn't open file %s\n", [self stringValue]);
} else {
if(fseek(fp, lastFileLenght, SEEK_SET) == 0) {
if(fread(newString, sizeof(char), diff, fp) != 0) {
newString[diff] = '\000';
newString[diff+1] = '\000';
[self addText:newString];
} else {
perror("filespy: fread");
}
} else {
perror("filespy: fseek");
}
fclose(fp);
}
free(newString);
} else if(fileLenght == lastFileLenght)
textloaded = YES;
return self;
}
- readDir
// the contents of a directory and remember it
{
DIR *dirp;
struct direct *dp;
unsigned int pos = 0, count = [dirStore count];
node root, actnode, tmpnode; // pointers to nodedescs
if((dirp = opendir([self stringValue])) == NULL) {
if(errno == EACCES)
printf("filespy: no permission to read directory %s\n", [self stringValue]);
else
perror("filespy: opendir");
return self;
}
actnode = root = NULL;
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if((pos >= count) || (strcmp(dp->d_name, [dirStore elementAt:pos]) != 0)) {
unsigned int i = pos;
while(i < count && (strcmp(dp->d_name, (char *)[dirStore elementAt:i]) != 0))
i++;
if(i >= count) { // not found, so it's a new file
// remember all new files in a linked list so that we can print them later
char str[MAXFILENAMELENGHT + 10] = "new file: ";
[dirStore insertElement:dp->d_name at:pos]; // remember new file
count++;
strcat(str, dp->d_name);
strcat(str, "\n");
tmpnode = (node)malloc(sizeof(nodedesc));
if(actnode != NULL) {
actnode->next = tmpnode;
} else {
root = tmpnode;
}
tmpnode->string = (char *)malloc(strlen(str)+1);
strcpy(tmpnode->string, str);
tmpnode->next = NULL;
actnode = tmpnode;
} else { // found, so files between pos and i are deleted
// remember the deleted files so that we can print them later
unsigned int j;
for(j = pos; j < i; j++) {
char str[MAXFILENAMELENGHT + 14] = "removed file: ";
strcat(str, (char *)[dirStore elementAt:j]);
strcat(str, "\n");
tmpnode = (node)malloc(sizeof(nodedesc));
if(actnode != NULL) {
actnode->next = tmpnode;
} else {
root = tmpnode;
}
tmpnode->string = (char *)malloc(strlen(str)+1);
strcpy(tmpnode->string, str);
tmpnode->next = NULL;
actnode = tmpnode;
[dirStore removeElementAt:j];
count--;
}
}
}
pos++;
}
closedir(dirp);
if(pos < count) { // rest of the files in dirStore have been deleted
unsigned int i, max = [dirStore count];
for(i = pos; i < max; i++) {
char str[MAXFILENAMELENGHT + 14] = "removed file: ";
strcat(str, (char *)[dirStore elementAt:pos]);
strcat(str, "\n");
tmpnode = (node)malloc(sizeof(nodedesc));
if(actnode != NULL) {
actnode->next = tmpnode;
} else {
root = tmpnode;
}
tmpnode->string = (char *)malloc(strlen(str)+1);
strcpy(tmpnode->string, str);
tmpnode->next = NULL;
actnode = tmpnode;
[dirStore removeElementAt:pos];
}
}
if(root != NULL) {
// contents of the directory changed (linked list exists), so put all changes together in
// a single string and pass it to addText
unsigned int len = 0;
char *totstr, *actstr;
actnode = root;
while(actnode != NULL) {
len += strlen(actnode->string);
actnode = actnode->next;
}
actstr = totstr = (char *)malloc(len+1);
actnode = root;
while(actnode != NULL) {
strcpy(actstr, actnode->string);
actstr = actstr + strlen(actnode->string);
actnode = actnode->next;
}
[self addText:totstr];
free(totstr);
actnode = root; // remove the linked list
while(actnode != NULL) {
tmpnode = actnode;
actnode = actnode->next;
free(tmpnode->string);
free(tmpnode);
}
}
return self;
}
- addText:(char *)givenString
// output the text givenString depending on the settings and preferences of the user
{
BOOL changed = YES, filterMode, mode = NO;
// Bugreport Nr. 5 and 6 by Karsten Heinze: malloc()ed memory was too small (I forgot the terminating 0-Byte)
char *newString = (char *)malloc(strlen(givenString)+1);
int textlenght;
if(maxLines > 0) // if a maximum number of lines is set, then cut the old parts of the existing text off
[self shortText];
filterMode = [myDelegate filterMode];
strcpy(newString, givenString);
textlenght = [myText textLength];
if(useFilter) { // if the filter is active, the filter the text first
switch(filterMode) {
case FILTER_DONTCOPY: mode = YES; break;
case FILTER_DONTBEEP: mode = NO; break;
}
changed = [myDelegate filterString:newString andRemove:mode who:self];
if(timeStamp && (changed || (filterMode == FILTER_DONTBEEP))) { // insert timestamp in front of each line
[self addTimeStamp:&newString];
}
if(changed || (filterMode == FILTER_DONTBEEP)) {
[self appendText:newString unfilteredString:givenString didChange:changed];
}
} else { // without filter, just add the timeStamp if that feature is active and append the text
if(timeStamp) {
[self addTimeStamp:&newString];
}
[self appendText:newString unfilteredString:givenString didChange:changed];
}
if(fileType == FILETYPE_FILE)
textloaded = YES;
else if(fileType == FILETYPE_DIR)
dirloaded = YES;
free(newString);
return self;
}
- appendText:(char *)string unfilteredString:(char *)ufString didChange:(BOOL)changed
// append string to the text-object in myWindow. changed is YES if something passed the filter (or if the filter is not active)
{
int textlenght;
textlenght = [myText textLength];
[myText setSel :textlenght :textlenght];
[myText replaceSel:string];
[myText setSel :textlenght :[myText textLength]];
[[[myText sizeToFit] scrollSelToVisible] display];
if(!useFilter || changed) {
if(autoPopUp && !logToSuperlog)
[self displayWindow];
if(beepOnChange && !logToSuperlog)
[myDelegate nxbeep];
}
[self contentChanged];
if(logToSuperlog && (fileex || !dontCopySuperlog)) {
if([myDelegate superUsesFilter]) {
[myDelegate updateSuperlog:self :string :changed :beepOnChange :autoPopUp];
} else { // if the superlog doesn't use the filter, then send the unchanged string
// changed is YES here, because nothing could have been filtered out if the multilog doesn't filter
[myDelegate updateSuperlog:self :ufString :YES :beepOnChange :autoPopUp];
}
}
return self;
}
- (BOOL)changedState
// checks whether anything changed with this file
{
int ret = 0;
if((spytype & SPY_CONTENTS) != 0) {
if((fileType == FILETYPE_FILE) && (fileLenght != lastFileLenght)) {
ret++;
} else if((fileType == FILETYPE_DIR) && (mtime != lastmtime)) {
ret++;
}
}
if((spytype & SPY_EXISTENCE) != 0) {
if((fileex != oldfileex) || (permissionOK != oldpermissionOK)) {
ret++;
}
}
return ret > 0;
}
- displayWindow
{
if(![myWindow isVisible]) {
[myWindow orderFrontRegardless];
[myDelegate addWindow:self];
}
if([myDelegate becomeKey]) {
[myWindow makeKeyWindow];
[myDelegate unhideApp:self];
}
return self;
}
- moveWindowTo:(NXCoord)x:(NXCoord)y
{
[myDoc moveWindowTo:x:y];
return self;
}
- setFileMode:(int)mode
{
fileMode = mode;
return self;
}
- (int)fileMode
{
return fileMode;
}
- setAutoPopUp:(BOOL)state
{
autoPopUp = state;
return self;
}
- (BOOL)autoPopUp
{
return autoPopUp;
}
- window
{
return myWindow;
}
- setMyDelegate:sender
{
myDelegate = sender;
exMatrix = [myDelegate existenceMatrix];
return self;
}
- myDelegate
{
return myDelegate;
}
- setBeepOnChange:(BOOL)state
{
beepOnChange = state;
return self;
}
- (BOOL)beepOnChange
{
return beepOnChange;
}
- setLogToSuperlog:(BOOL)state;
{
logToSuperlog = state;
return self;
}
- (BOOL)logToSuperlog
{
return logToSuperlog;
}
- setUseFilter:(BOOL)state
{
useFilter = state;
return self;
}
- (BOOL)useFilter
{
return useFilter;
}
- setDontCopy:(BOOL)state
{
dontCopySuperlog = state;
return self;
}
- (BOOL)dontCopy
{
return dontCopySuperlog;
}
- setTimeStamp:(BOOL)state
{
timeStamp = state;
return self;
}
- (BOOL)timeStamp;
{
return timeStamp;
}
- setSpytype:(unsigned short int)type
{
int rowCount, colCount;
if((type & SPY_EXISTENCE) != (spytype & SPY_EXISTENCE)) { // if the type changed in existence
if(!exCell) { // we haven't spyed for existence yet
// so insert new exCell in existence-matrix
[exMatrix addRow];
[exMatrix getNumRows:&rowCount numCols:&colCount];
exCell = [exMatrix cellAt:rowCount-1 :0];
[exCell setStringValue:[self stringValue]];
[exCell setMyDelegate:self];
[exCell setTime:time(NULL)];
[exMatrix sizeToCells];
[exMatrix scrollCellToVisible:rowCount-1 :0];
[exMatrix display];
} else {
// we were spying for this file, so we aren't any longer from now on -> remove the exCell
[exMatrix getRow:&rowCount andCol:&colCount ofCell:exCell];
[exMatrix removeRowAt:rowCount andFree:YES];
[exMatrix display];
exCell = nil;
}
}
spytype = type;
return self;
}
- (unsigned short int)spytype
{
return spytype;
}
- updateExistence
{
time_t myTime = time(NULL);
List *tmpList = [SpyTextFieldCell sharedTmpList];
if((fileex != oldfileex) || (permissionOK != oldpermissionOK)) { // existence or permission changed
[tmpList addObject:exCell]; // remember cell to update later in controller
if(fileex)
[exCell setTime:myTime];
else
[exCell changeTime:myTime];
if(beepOnChange)
[myDelegate nxbeep];
if(autoPopUp)
[myDelegate showExistencelogRegardless];
}
return self;
}
- setSuperCopyFilename:(unsigned int)state
{
superCopyFilename = state;
return self;
}
- (unsigned int)superCopyFilename;
{
return superCopyFilename;
}
- setStringValue:(const char *)str
{
[myWindow setTitleAsFilename:str];
return [super setStringValue:str];
}
- clearBuffer:sender
{
[myText setSel :0 :[myText textLength]];
[myText replaceSel:""];
[[myText sizeToFit] scrollSelToVisible];
[self contentChanged];
return self;
}
- contentChanged
{
if(![myWindow isKeyWindow] && ![myWindow isDocEdited])
[myWindow setDocEdited:YES];
return self;
}
- fileRemoved
// prepares string that says which file/directories have been removed
{
char str[MAXFILENAMELENGHT + 14] = "removed ";
if(fileType == FILETYPE_FILE) { // this file has been removed
strcat(str, "file: ");
} else if(fileType == FILETYPE_DIR) { // the whole directory has been removed, so output the names of all the files in it
if([dirStore count] > 2) {
unsigned int i, max = [dirStore count];
node root, actnode, tmpnode;
actnode = root = NULL;
for(i = 2; i < max; i++) {
char str[MAXFILENAMELENGHT + 14] = "removed file: ";
strcat(str, (char *)[dirStore elementAt:2]);
strcat(str, "\n");
tmpnode = (node)malloc(sizeof(nodedesc));
if(actnode != NULL) {
actnode->next = tmpnode;
} else {
root = tmpnode;
}
tmpnode->string = (char *)malloc(strlen(str)+1);
strcpy(tmpnode->string, str);
tmpnode->next = NULL;
actnode = tmpnode;
[dirStore removeElementAt:2];
}
if(root != NULL) {
unsigned int len = 0;
char *totstr, *actstr;
actnode = root;
while(actnode != NULL) {
len += strlen(actnode->string);
actnode = actnode->next;
}
actstr = totstr = (char *)malloc(len+1);
actnode = root;
while(actnode != NULL) {
strcpy(actstr, actnode->string);
actstr = actstr + strlen(actnode->string);
actnode = actnode->next;
}
[self addText:totstr];
free(totstr);
actnode = root;
while(actnode != NULL) {
tmpnode = actnode;
actnode = actnode->next;
free(tmpnode->string);
free(tmpnode);
}
}
}
strcat(str, "dir: ");
}
strcat(str, [self stringValue]);
if(!(spytype & SPY_EXISTENCE)) // only print message if we're not spying for existence
puts(str); // otherwise we have to update the existence-log
fileType = FILETYPE_NOTHING;
fileex = NO;
return self;
}
- windowDidBecomeKey:sender
{
if([sender isDocEdited])
[sender setDocEdited:NO];
[myDelegate windowDidBecomeKey:sender];
return self;
}
- windowWillClose:sender
{
[myDelegate windowWillClose:sender];
return self;
}
- text
{
return myText;
}
- (NXColor)color
{
return color;
}
- setColor:(NXColor)newColor
{
color = newColor;
[myText setTextColor:color];
[myText display];
return self;
}
- setSpyFont:newFont
{
myFont = newFont;
[myText setFont:myFont];
[myText scrollSelToVisible];
return self;
}
- addTimeStamp:(char **)string
// add current time in front of each line in string
{
#define TIMELEN 19 // length of timestring
time_t mytime = time((time_t *)NULL);
struct tm *tp = localtime(&mytime);
char tstr[TIMELEN], *tmpText, *p, *cp;
unsigned int n = 0, tlen;
BOOL copied;
strftime(tstr, (size_t)TIMELEN, "{%b %d %H:%M:%S} ", tp);
tlen = strlen(tstr);
p = *string;
while(*p != '\000') {
if(*p++ == '\n')
n++; // count number of lines
}
tmpText = (char *)malloc(strlen(*string) + n*TIMELEN + 1);
tmpText[0] = '\000';
p = *string;
cp = tmpText;
copied = NO;
while(*p != '\000') {
if(!copied) {
strcpy(cp, tstr);
cp += tlen;
copied = YES;
}
*cp++ = *p;
if(*p++ == '\n')
copied = NO;
}
*cp = '\000';
free(*string);
*string = tmpText;
return self;
}
- spyFont
{
return myFont;
}
- setMaxLines:(int)lines
{
maxLines = lines;
return self;
}
- shortText
// remove lines from the beginning of the text so that the text has not more than maxLines lines
{
int textLen = [myText textLength];
int textLines = [myText lineFromPosition:textLen];
if(textLines > maxLines) {
[[myText setSel:0 :[myText positionFromLine:(textLines - maxLines)]] replaceSel:""];
}
return self;
}
- (BOOL)permissionOk
{
return permissionOK;
}
- setShowTime:(BOOL)state
{
// [exCell setTime:time(NULL)];
showTime = state;
return self;
}
- (BOOL)showTime
{
return showTime;
}
- (BOOL)askUpdateTime
{
return (spytype & SPY_EXISTENCE) && fileex; // need to update time in existenceTextFieldCell
}
- drawInside:(const NXRect *)cellFrame inView:controlView // taken from 'ScrollDoodScroll' and modified
{
#define IMAGEMARGIN 4.0
/* every CustomCell needs these two */
static id sharedTextCell = nil;
NXRect rect = *cellFrame;
NXRect boxRect;
NXCoord size;
NXSize ESize;
NXPoint imageOrigin;
/* erase the cell */
PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
NXRectFill(cellFrame);
size = NX_HEIGHT(cellFrame) - 2*IMAGEMARGIN;
if(spytype & SPY_CONTENTS) { // If we're only spying for existence, we don't need a colorbox
NXSetColor(color);
NXSetRect(&boxRect, NX_X(cellFrame) + IMAGEMARGIN, NX_Y(cellFrame) + IMAGEMARGIN,
size, size);
NXRectFill(&boxRect);
} else {
if(!EImage) {
EImage = [NXImage findImageNamed:"E"];
}
[EImage getSize:&ESize];
imageOrigin.x = NX_X(cellFrame) + IMAGEMARGIN;
imageOrigin.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) -
(NX_HEIGHT(cellFrame) - ESize.height) / 2.0;
[EImage composite:NX_SOVER toPoint:&imageOrigin];
}
NX_WIDTH(&rect) -= (size + IMAGEMARGIN * 2.0 - NX_X(&rect));
NX_X(&rect) += size + IMAGEMARGIN * 2.0;
if (!sharedTextCell) {
sharedTextCell = [[Cell alloc] init];
[sharedTextCell setWrap:NO];
}
[sharedTextCell setFont:[self font]];
[sharedTextCell setStringValue:[self stringValue]];
[sharedTextCell drawInside:&rect inView:controlView];
/* all drawing from now on will be in dark gray */
PSsetgray(NX_DKGRAY);
/* draw the two dark gray lines above and below the cell */
if (cFlags1.state || cFlags1.highlighted) {
NXRect rectArray[2];
/*
* draw 1-pixel tall rectangles instead of lines (this is faster than
* PSmoveto(); PSlineto()).
*/
NXSetRect(&(rectArray[0]), NX_X(cellFrame), NX_Y(cellFrame),
NX_WIDTH(cellFrame), 1.0);
NXSetRect(&(rectArray[1]), NX_X(cellFrame), NX_MAXY(cellFrame) - 1.0,
NX_WIDTH(cellFrame), 1.0);
/* using NXRectFillList is faster than separate calls to NXRectFill */
NXRectFillList(rectArray, 2);
}
return self;
}
- highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
{
if (cFlags1.highlighted != flag) {
cFlags1.highlighted = flag;
[self drawInside:cellFrame inView:controlView];
}
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.