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(sizeof(char)*strlen(str)); 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(sizeof(char)*strlen(str)); 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(sizeof(char)*strlen(str)); 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(sizeof(char)*(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; char *newString = (char *)malloc(strlen(givenString)); 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(sizeof(char)*strlen(str)); 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(sizeof(char)*(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); 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.