This is Controller.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 "Controller.h"
#define SHORTESTTIME 1 /* minimal time between two tests on a file */
#define LONGESTTIME 60 /* maximal time - " - */
#define WIN_DELTA_X 25.0 /* coord-difference of new windows */
#define WIN_DELTA_Y 25.0 /* remember: Y comes down! */
#define WIN_START_X 100.0
#define WIN_START_Y 500.0
#define WIN_MAX_X 500.0
#define WIN_MAX_Y 100.0
#define MAXFILELEN MAXNAMLEN
#ifdef DEBUG
#define DEFOWNER "FileSpyDebug"
#else
#define DEFOWNER "FileSpy"
#endif
int wildmat(char *text, char *p); // still uses wildmat, should use GNU-regexp in the near future
@implementation Controller
- appWillInit:sender
{
whileInit = YES;
return self;
}
- appDidInit:sender
{
NXRect matrixRect, scrollRect;
NXSize interCellSpacing = {0.0, 0.0}, cellSize;
const char *fileType[] = {NXFilenamePboardType};
winx = WIN_START_X;
winy = WIN_START_Y;
fileCount = 0; // no files yet
secs = 2.0; // this one should probably be in the defaults database
// now set some defaults
saveFileList = YES;
prefPopup = YES;
prefBeepOnChange = YES;
prefSuperlog = NO;
prefUseFilter = NO;
prefFileMode = RADIO_ENTIRE;
prefColor = NX_COLORBLACK; // defined in <appkit/color.h>
prefTimeStamp = NO; // no timestamps is default
prefSuperCopyFilename = ONCE;
prefSpytype = SPY_CONTENTS; // only spy for contents
superPopup = YES;
superBeepOnChange = YES;
superUseFilter = NO;
useFilter = NO;
filterMode = FILTER_DONTBEEP;
filterIsUpToDate = YES;
displayCurWindow = YES; // only used when loading preferences
becomeKey = NO;
saveState = NO;
maxLineState = NO;
maxLines = 100;
inspectorFrame = (char *)0;
myWindowIsVisible = NO;
nrOpenWindows = 0;
// lastSuperlogString = (char *)0;
tmpList = [[List allocFromZone:[self zone]] init]; // temporary List used by all objects
[SpyTextFieldCell setSharedTmpList:tmpList];
existenceSeen = YES;
mySound = nil; // why do I need this???
swapfilename = NULL;
swapfilesize = -1;
swapfileTooBig = swapfileError = NO;
[[NXApp appListener] setServicesDelegate:self];
[self registerServices:self];
if(system("/bin/stty pass8 pass8out nl > /dev/console") == 127) // proposed by mwa
printf("filespy: couldn't open shell for 'stty pass8 pass8out nl > /dev/console\n");
[[[myScrollView setHorizScrollerRequired: NO] // setup mainwindow
setVertScrollerRequired: YES]
setBorderType: NX_BEZEL];
[myScrollView getFrame:&scrollRect];
[ScrollView getContentSize:&(matrixRect.size)
forFrameSize:&(scrollRect.size)
horizScroller: NO
vertScroller: YES
borderType:NX_BEZEL];
myMatrix = [[MyMatrix alloc] initFrame: &matrixRect
mode: NX_LISTMODE
cellClass: [SpyTextFieldCell class]
numRows: 0
numCols: 1];
[myMatrix setIntercell:&interCellSpacing];
[myMatrix getCellSize:&cellSize];
cellSize.width = NX_WIDTH(&matrixRect);
[myMatrix setCellSize:&cellSize];
[myMatrix sizeToCells];
[myMatrix setAutosizeCells:YES];
[myMatrix setAutoscroll:YES];
[myScrollView setDocView:myMatrix];
[[myMatrix superview] setAutoresizeSubviews:YES];
[myMatrix setAutosizing:NX_WIDTHSIZABLE];
[myMatrix setTarget:self];
[myMatrix setAction:@selector(singleClick:)];
[myMatrix setDoubleAction:@selector(doubleClick:)];
[myScrollView setController:self];
[myScrollView registerForDraggedTypes:fileType count:1];
[[[myExistenceScrollView setHorizScrollerRequired: NO] // setup existencelog
setVertScrollerRequired: YES]
setBorderType: NX_BEZEL];
[myExistenceScrollView getFrame:&scrollRect];
[ScrollView getContentSize:&(matrixRect.size)
forFrameSize:&(scrollRect.size)
horizScroller: NO
vertScroller: YES
borderType:NX_BEZEL];
myExistenceMatrix = [[SimpleMatrix alloc] initFrame: &matrixRect
mode: NX_TRACKMODE
cellClass: [ExistenceTextFieldCell class]
numRows: 0
numCols: 1];
[myExistenceMatrix setIntercell:&interCellSpacing];
[myExistenceMatrix getCellSize:&cellSize];
cellSize.width = NX_WIDTH(&matrixRect);
[myExistenceMatrix setCellSize:&cellSize];
[myExistenceMatrix sizeToCells];
[myExistenceMatrix setAutosizeCells:YES];
[myExistenceMatrix setAutoscroll:YES];
[myExistenceScrollView setDocView:myExistenceMatrix];
[[myExistenceMatrix superview] setAutoresizeSubviews:YES];
[myExistenceMatrix setAutosizing:NX_WIDTHSIZABLE];
[myExistenceMatrix setTarget:self];
[myExistenceScrollView setController:self];
[myExistenceScrollView registerForDraggedTypes:fileType count:1];
finder = [[Finder alloc] init];
[finder setDelegate:self];
[self setupFilter];
[self setupSuperlog];
[self loadPreferences:self];
if(firstFileStore) { // load files dragged on icon before start
unsigned int max = [firstFileStore count], i;
newFileOffset = YES;
displayCurWindow = NO;
for(i = 1; i != max; i++)
[self addFileWithName:[firstFileStore elementAt:i]];
[firstFileStore free];
}
if(fileCount == 0) { // set the main window correctly
[radioMatrix selectCellAt:0:prefFileMode];
[popupSwitch setState:(prefPopup ? 1 : 0)];
[soundButton setState:(prefBeepOnChange ? 1 : 0)];
[filterSwitch setState:(prefUseFilter ? 1 : 0)];
[superlogSwitch setState:(prefSuperlog ? 1 : 0)];
}
[self startSpyIfNeeded];
#ifdef DEBUG
[myWindow setTitle:"Debug"];
#endif
whileInit = NO;
return self;
}
- appWillTerminate:sender
{
if(saveState)
[self savePreferences:self];
[self stopSpy];
return self;
}
- app:sender powerOffIn:(int)ms andSave:(int)aFlag
{
return [self appWillTerminate:sender];
}
- free // hmm.. I'd say there're some more things to free, aren't there?
{
[myWindow free];
return [super free];
}
- (BOOL)appAcceptsAnotherFile:sender
{
return YES;
}
- (int)app:sender openFile:(const char *)filename type:(const char *)aType;
{
BOOL tmpdcw = displayCurWindow; // tmpDisplayCurWindow
displayCurWindow = NO;
if(whileInit) {
if(!firstFileStore)
firstFileStore = [[Storage alloc] initCount:1 elementSize:MAXFILELEN description:"[MAXFILELENc]"];
[firstFileStore addElement:(char *)filename];
} else {
newFileOffset = YES; // place window at a new position (its position is not in the defaults database)
displayCurWindow = NO;
[self addFileWithName:filename];
displayCurWindow = YES;
}
displayCurWindow = tmpdcw;
return YES;
}
- addFileWithName:(const char *)name
{
[myTextField setStringValue:name];
[self addFile:self];
return self;
}
- addFile:sender
{
const char *str;
int rowCount, colCount, button, i = 0;
SpyTextFieldCell *newCell;
BOOL fileExists = NO; // is the file already in the list?
str = [myTextField stringValue];
if(*str == '\000') {
[self chooseFile:self];
return self;
}
[myTextField selectText:self];
[myMatrix getNumRows:&rowCount numCols:&colCount];
while((i < rowCount) && !fileExists) { // only one window per file
if(strcmp([[myMatrix cellAt:i :0] stringValue], str) == 0)
fileExists = TRUE;
else
i++;
}
if(!fileExists) {
BOOL newFile = YES;
struct stat st;
int actRowNr, actColNr, statRet;
[myMatrix getRow:&actRowNr andCol:&actColNr ofCell:[myMatrix selectedCell]];
[myMatrix selectCellAt:-1:-1];
[myMatrix addRow];
newCell = [myMatrix cellAt:rowCount :0];
[newCell setTag:rowCount];
[newCell setStringValue: str];
// now set all the default-values
[newCell setFileMode:prefFileMode];
[newCell setAutoPopUp:prefPopup];
[newCell setBeepOnChange:prefBeepOnChange];
[newCell setMyDelegate:self];
[newCell setLogToSuperlog:prefSuperlog];
[newCell setUseFilter:prefUseFilter];
[newCell setDontCopy:prefDontCopySuperlog];
[newCell setSuperCopyFilename:prefSuperCopyFilename];
[newCell setColor:prefColor];
[newCell setTimeStamp:prefTimeStamp];
[newCell setMaxLines:(maxLineState ? maxLines : 0)];
[newCell setSpytype:prefSpytype];
[newCell setShowTime:showTime];
if(prefFont)
[newCell setSpyFont:prefFont];
if(((statRet = stat(str, &st)) < 0) && !whileInit) {
if(errno == ENOENT) {
displayCurWindow = NO;
button = NXRunAlertPanel("Alert",
"File %s doesn't exist.\nAre you sure you want to spy it?", "Ok", "Cancel", NULL, str);
if(button != NX_ALERTDEFAULT) {
[myMatrix selectCellAt:rowCount :0];
[self deleteFile:self];
newFile = NO;
}
} else if(errno == EACCES) {
displayCurWindow = NO;
button = NXRunAlertPanel("Alert",
"No permission for file %s.\nAre you sure you want to spy it?", "Ok", "Cancel", NULL, str);
if(button != NX_ALERTDEFAULT) {
[myMatrix selectCellAt:rowCount :0];
[self deleteFile:self];
newFile = NO;
}
}
}
if(newFile) {
if(newFileOffset) {
winx = winx + WIN_DELTA_X;
winy = winy - WIN_DELTA_Y;
if((winx > WIN_MAX_X) || (winy < WIN_MAX_Y)) {
winx = WIN_START_X;
winy = WIN_START_Y;
}
[newCell moveWindowTo:winx:winy];
} else {
[[newCell window] setFrameFromString:frameStr];
}
[newCell updateFile];
}
[myMatrix sizeToCells];
[myMatrix selectCellAt:(newFile ? rowCount : actRowNr) :0];
[myMatrix scrollCellToVisible:rowCount :0];
fileCount++;
[self startSpyIfNeeded];
} else { // file is already beeing spyed, so beep and select it
[self nxbeep];
[myMatrix selectCellAt:i :0];
[myMatrix scrollCellToVisible:i :0];
}
[self singleClick:self];
return self;
}
- deleteFile:sender
{
id selectedCellsList;
int i, rowCount, colCount;
[myTextField setStringValue:""];
selectedCellsList = [tmpList empty];
[myMatrix getSelectedCells:selectedCellsList];
[myMatrix getNumRows:&rowCount numCols:&colCount];
for(i=0; i<rowCount; i++) {
if([selectedCellsList indexOf:[myMatrix cellAt:i :0]] != NX_NOT_IN_LIST) {
[[myMatrix cellAt:i :0] delete];
[myMatrix removeRowAt:i-- andFree:YES];
fileCount--;
}
}
[[myMatrix sizeToCells] display];
[myExistenceMatrix sizeToCells];
[myExistenceScrollView display];
if((fileCount == 0) || ([myMatrix selectedCell] == nil)) {
[self setInspectorToView:[inspectorNoView contentView]];
}
[self stopSpyIfNeeded];
return self;
}
- chooseFile:sender
{
id openPanel;
const char *const fileType[1] = {NULL};
const char *const *files;
char tmpfile[MAXFILENAMELENGHT];
int i = 0;
openPanel = [OpenPanel new];
[openPanel setDelegate:self];
[openPanel chooseDirectories:NO];
[openPanel allowMultipleFiles:YES];
if([openPanel runModalForDirectory:[myTextField stringValue] file:NULL types:fileType] == NX_OKTAG) {
if((files = [openPanel filenames]) != (const char *const *)0) {
while(files[i]) {
sprintf(tmpfile, "%s/%s", [openPanel directory], files[i++]);
[self addFileWithName:tmpfile];
}
}
}
[self singleClick:self];
return self;
}
- singleClick:sender
{
PopUpList *popupList = [[superFilenameButton cell] target], *spytypePopuplist = [[spytypeButton cell] target];
Matrix *popupMatrix = [popupList itemList], *spytypeMatrix = [spytypePopuplist itemList];
id selectedCell = [myMatrix selectedCell];
if(selectedCell != nil) { // this does not work correctly because of a bug in NS!
// tempi passati
[myTextField setStringValue:[selectedCell stringValue]];
[myTextField selectText:self];
if(inspectorPanel) { // if the inspectorPanel is open, then set all the values
[radioMatrix selectCellAt:0 :[selectedCell fileMode]];
[soundButton setState:[selectedCell beepOnChange]];
[popupSwitch setState:[selectedCell autoPopUp]];
[superlogSwitch setState:[selectedCell logToSuperlog]];
[filterSwitch setState:[selectedCell useFilter]];
[dontCopySwitch setState:[selectedCell dontCopy]];
[timeSwitch setState:[selectedCell timeStamp]];
[popupMatrix selectCellWithTag:[selectedCell superCopyFilename]];
[superFilenameButton setTitle:
[[popupMatrix findCellWithTag:[selectedCell superCopyFilename]] title]];
[superColorWell setColor:[selectedCell color]];
[fontTextField setFont:[selectedCell spyFont]];
[myFontManager setSelFont:[selectedCell spyFont] isMultiple:NO];
[spytypeMatrix selectCellWithTag:(int)[selectedCell spytype]];
[spytypeButton setTitle:
[[spytypeMatrix findCellWithTag:(int)[selectedCell spytype]] title]];
if([[myMatrix getSelectedCells:[tmpList empty]] count] > 1) { // if multiple selection...
[inspectorFile setStringValue:"Files: "];
[inspectorName setTextGray:NX_DKGRAY];
} else {
[inspectorFile setStringValue:"File: "];
[inspectorName setTextGray:NX_BLACK];
}
[inspectorName setStringValue:[selectedCell stringValue]];
[self setInspectorToView:[inspectorNormalView contentView]];
}
} else { // if no cell is selected
#ifdef DEBUG
// puts("selectedCell == nil"); // for testing if the bug still exists // it doesn't...
#endif
[myTextField setStringValue:""];
[myTextField selectText:self];
[self setInspectorToView:[inspectorNoView contentView]];
}
return self;
}
- doubleClick:sender
{
[[myMatrix selectedCell] displayWindow];
return self;
}
- radioClick:sender // if user selects the radio-button for entire file/only new text
{
List *selectedCellsList = [tmpList empty];
unsigned int i;
int mode = [[radioMatrix selectedCell] tag];
[myMatrix getSelectedCells:selectedCellsList];
for(i = 0; i < [selectedCellsList count]; i++)
[[selectedCellsList objectAt:i] setFileMode:mode];
return self;
}
- autoPopUpClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i;
BOOL state = [popupSwitch state];
[myMatrix getSelectedCells:selectedCellsList];
for(i = 0; i < [selectedCellsList count]; i++)
[[selectedCellsList objectAt:i] setAutoPopUp:state];
return self;
}
- beepOnChangeClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i;
BOOL state = [soundButton state];
[myMatrix getSelectedCells:selectedCellsList];
for(i = 0; i < [selectedCellsList count]; i++)
[[selectedCellsList objectAt:i] setBeepOnChange:state];
return self;
}
- superlogClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
BOOL state = [sender state];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setLogToSuperlog:state];
return self;
}
- useFilterClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
BOOL state = [sender state];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setUseFilter:state];
return self;
}
- copySuperlogClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
BOOL state = [sender state];
selectedCellsList = [tmpList empty];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setDontCopy:state];
return self;
}
- filterModeClick:sender
{
switch([[sender selectedCell] tag]) {
case 0: filterMode = FILTER_DONTCOPY; break;
case 1: filterMode = FILTER_DONTBEEP; break;
#ifdef DEBUG
default: printf("filespy: unknown filtermode %d\n", [sender state]);
#endif
}
return self;
}
// if user changes something in the preferences-window
- prefRadioClick:sender
{
prefFileMode = [[prefRadioMatrix selectedCell] tag];
return self;
}
- prefSuperlogClick:sender
{
prefSuperlog = [sender state];
return self;
}
- prefAutoPopUpClick:sender
{
prefPopup = [sender state];
return self;
}
- prefBeepOnChangeClick:sender
{
prefBeepOnChange = [sender state];
return self;
}
- prefUseFilterClick:sender
{
prefUseFilter = [sender state];
return self;
}
- prefCopySuperlogClick:sender
{
prefDontCopySuperlog = [sender state];
return self;
}
- superAutoPopUpClick:sender
{
superPopup = [sender state];
return self;
}
- superBeepOnChangeClick:sender
{
superBeepOnChange = [sender state];
return self;
}
- superUseFilterClick:sender
{
superUseFilter = [sender state];
return self;
}
- superDupLinesClick:sender
{
// BOOL oldState = noDuplicatedLines;
noDuplicatedLines = [sender state];
#if 0
if((noDuplicatedLines == YES) && (oldState == NO))
lastSuperlogString = (char *)malloc(MAXSTRLEN * sizeof(char));
else if((noDuplicatedLines == NO) && (oldState == YES))
free(lastSuperlogString);
#endif
return self;
}
- saveFileListClick:sender
{
saveFileList = [sender state];
return self;
}
- keyClick:sender
{
becomeKey = [sender state];
return self;
}
- stateClick:sender
{
saveState = [sender state];
return self;
}
- setShowTimeClick:sender
{
unsigned int i, max;
List *selectedCellsList = [myMatrix cellList];
showTime = [sender state];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setShowTime:showTime];
if(existencelogWindow)
[existencelogWindow display];
return self;
}
- (BOOL)showTime
{
return showTime;
}
- startSpy
{
if(!running) {
alarmTimedEntry = DPSAddTimedEntry(secs, &alarmHandler, self, NX_BASETHRESHOLD);
running = YES;
}
return self;
}
- startSpyIfNeeded
{
if(fileCount > 0 || (swapfilename != NULL && swapfilesize >= 0))
[self startSpy];
return self;
}
- stopSpy
{
if(running) {
DPSRemoveTimedEntry(alarmTimedEntry);
NXPing();
running = NO;
}
return self;
}
- stopSpyIfNeeded
{
if(fileCount == 0 && (swapfilename == NULL || swapfilesize < 0))
[self stopSpy];
return self;
}
- checkFiles // update all the files
{
unsigned int i, max;
int row, col;
SpyTextFieldCell *actualCell;
BOOL exMatrixUpdate = NO;
[tmpList empty];
for(i=0; i<fileCount; i++) {
actualCell = [myMatrix cellAt:i :0];
[actualCell updateFile];
if([actualCell changedState])
[actualCell updateText];
if([actualCell askUpdateTime] && showTime)
exMatrixUpdate = YES;
}
max = [tmpList count];
if(max > 0) {
if(existenceSeen) {
existenceSeen = NO;
[myExistenceMatrix clearSelectedCell];
}
for(i=0; i<max; i++) { // select all cell that changed existence in existencelog
[myExistenceMatrix getRow:&row andCol:&col ofCell:[tmpList objectAt:i]];
[myExistenceMatrix setSelectionFrom:row to:row anchor:row lit:YES];
}
[myExistenceMatrix getRow:&row andCol:&col ofCell:[tmpList objectAt:0]]; // get first selected cell
[myExistenceMatrix scrollCellToVisible:row :col]; // and scroll it to visible
}
if((max > 0) || (exMatrixUpdate)) {
[existencelogWindow display];
}
if((swapfilename != NULL) && (swapfilesize >= 0)) {
[self updateSwapfile];
}
return self;
}
- changeUpdatePeriod:sender
{
int newPeriod = [sender intValue];
if(newPeriod > LONGESTTIME)
newPeriod = LONGESTTIME;
if(newPeriod < SHORTESTTIME)
newPeriod = SHORTESTTIME;
[secsTextField setIntValue:newPeriod];
[secsScroller setIntValue:newPeriod];
secs = newPeriod;
[self stopSpy];
[self startSpyIfNeeded];
return self;
}
- showPreferences:sender // shows preferences panel
{
PopUpList *popupList;
Matrix *popupMatrix;
if(!preferencesPanel) {
if(![NXApp loadNibSection:"Preferences.nib" owner:self withNames:NO fromZone:[self zone]]) {
NXLogError("Can't open Preferences.nib!");
}
[self setPrefToView:[prefGenView contentView]];
}
popupList = [[prefSuperFilenameButton cell] target];
popupMatrix = [popupList itemList];
[secsScroller setDoubleValue:secs];
[secsTextField setDoubleValue:secs];
[prefPopupSwitch setState:prefPopup];
[prefSoundButton setState:prefBeepOnChange];
[prefFilterSwitch setState:prefUseFilter];
[prefSuperlogSwitch setState:prefSuperlog];
[superPopupSwitch setState:superPopup];
[superSoundSwitch setState:superBeepOnChange];
[superFilterSwitch setState:superUseFilter];
[superDupSwitch setState:noDuplicatedLines];
[prefRadioMatrix selectCellAt:0:prefFileMode];
[keySwitch setState:becomeKey];
[stateSwitch setState:saveState];
[fileListButton setState:saveFileList];
[prefDontCopySwitch setState:prefDontCopySuperlog];
[showTimeSwitch setState:showTime];
[prefSuperFilenameButton setTitle:
[[popupMatrix findCellWithTag:prefSuperCopyFilename] title]];
[prefColorWell setColor:prefColor];
popupList = [[prefSpytypeButton cell] target];
popupMatrix = [popupList itemList];
[prefSpytypeButton setTitle:
[[popupMatrix findCellWithTag:prefSpytype] title]];
[prefFontTextField setFont:prefFont];
[myFontManager setSelFont:prefFont isMultiple:NO];
[prefTimeSwitch setState:prefTimeStamp];
[[maxLinesScroller setIntValue:maxLines] setEnabled:maxLineState];
[[maxLinesTextField setIntValue:maxLines] setEnabled:maxLineState];
[maxLinesMatrix selectCellWithTag:maxLineState];
if(mySound != nil)
[soundTextField setStringValue:[mySound name]];
if(swapfilename != NULL) {
[prefSwapfileTextField setStringValue:swapfilename];
[prefSwapfileSizeTextField setIntValue:swapfilesize/(1024*1024)];
} else {
[prefSwapfileTextField setStringValue:""];
[prefSwapfileSizeTextField setStringValue:""];
}
[preferencesPanel makeKeyAndOrderFront:sender];
return self;
}
- setPref:sender
{
id newView = nil;
int tag = [[sender selectedCell] tag];
switch(tag) {
case 0: newView = [prefGenView contentView];
break;
case 1: newView = [prefNewView contentView];
break;
case 2: newView = [prefSuperView contentView];
break;
case 3: newView = [prefSwapfileView contentView];
break;
#ifdef DEBUG
default: printf("filespy: unknown tag in setPref:sender\n"); // this shouldn't happen
break;
#endif
}
[self setPrefToView:newView];
[popButton setTitle:[[sender selectedCell] title]];
return self;
}
- setPrefToView:theView
{
NXRect boxRect, viewRect;
[multiView getFrame:&boxRect];
[theView getFrame:&viewRect];
[multiView setContentView:theView];
NX_X(&viewRect) = (NX_WIDTH(&boxRect) - NX_WIDTH(&viewRect)) / 2.0;
NX_Y(&viewRect) = (NX_HEIGHT(&boxRect) - NX_HEIGHT(&viewRect)) / 2.0;
[theView setFrame:&viewRect];
[multiView display];
return self;
}
- savePreferences:sender // saves preferences to defaults-database
{
unsigned int i, oldCount = 0;
char *str;
id cellList, actualCell;
NXDefaultsVector vec = { // contains global defaults
{"filterMode", ""}, // 0
{"superUseFilter", (superUseFilter ? "YES" : "NO")}, // 1
{"prefUseFilter", (prefUseFilter ? "YES" : "NO")}, // 2
{"secs",""}, // 3
{"prefPopup", (prefPopup ? "YES" : "NO")}, // 4
{"prefBeepOnChange", (prefBeepOnChange ? "YES" : "NO")}, // 5
{"prefSuperlog", (prefSuperlog ? "YES" : "NO")}, // 6
{"superPopup", (superPopup ? "YES" : "NO")}, // 7
{"superBeepOnChange", (superBeepOnChange ? "YES" : "NO")}, // 8
{"prefFileMode", ""}, // 9
{"saveFileList", (saveFileList ? "YES" : "NO")}, // 10
{"fileCount",""}, // 11
{"myWindowFrame", ""}, // 12
{"myWindowVisible", [myWindow isVisible] ? "YES" : "NO"}, // 13
{"superWindowFrame", ""}, // 14
{"superWindowVisible", [superWindow isVisible] ? "YES" : "NO"}, // 15
{"becomeKey", becomeKey ? "YES" : "NO"}, // 16
{"saveState", saveState ? "YES" : "NO"}, // 17
{"prefDontCopySuperlog", prefDontCopySuperlog ? "YES" : "NO"}, // 18
{"prefSuperCopyFilename", ""}, // 19
{"prefColor", ""}, // 20
{"prefTimeStamp", prefTimeStamp ? "YES" : "NO"}, // 21
{"prefFont", ""}, // 22
{"maxLines", ""}, // 23
{"inspectorFrame", ""}, // 24
{"superNoDupLines", noDuplicatedLines ? "YES" : "NO"}, // 25
{"prefSpytype", ""}, // 26
{"exWindowFrame", ""}, // 27
{"exWindowVisible",[existencelogWindow isVisible]? "YES":"NO"}, // 28
{"soundfile", ""}, // 29
{"showTime", "NO"}, // 30
{"swapFileName", ""}, // 31
{"swapFileSize", "-1"}, // 32
{NULL, NULL} // <- enter this number in VECLEN
};
#define VECLEN 33
NXDefaultsVector fileVec = { // contains defaults for each file
{"", ""}, // 0
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""}, // 13
{NULL, NULL}, // <- enter this number in FILEVECLEN!
};
#define FILEVECLEN 14
oldCount = atoi(NXGetDefaultValue(DEFOWNER, "fileCount")); // need to remember how many files were in the database
// so that I can remove thouse which now longer belong there
vec[0].value = (char *)alloca(16);
sprintf(vec[0].value, "%d", filterMode);
vec[3].value = (char *)alloca(16);
sprintf(vec[3].value, "%f", secs);
vec[9].value = (char *)alloca(16);
sprintf(vec[9].value, "%d", prefFileMode);
vec[19].value = (char *)alloca(16);
sprintf(vec[19].value, "%d", prefSuperCopyFilename);
vec[20].value = (char *)alloca(32);
sprintf(vec[20].value, "%f,%f,%f", NXRedComponent(prefColor), NXGreenComponent(prefColor), NXBlueComponent(prefColor));
vec[22].value = (char *)alloca(32); // hope this is enough for a fontname, is there some constant somewhere?
sprintf(vec[22].value, "%s %f", [prefFont name], [prefFont pointSize]);
vec[23].value = (char *)alloca(16);
sprintf(vec[23].value, "%d", maxLineState ? maxLines : -maxLines);
vec[26].value = (char *)alloca(16);
sprintf(vec[26].value, "%hu", prefSpytype);
vec[29].value = (char *)alloca(MAXNAMLEN);
sprintf(vec[29].value, [self soundName]);
vec[30].value = (char *)alloca(4);
sprintf(vec[30].value, showTime ? "YES" : "NO");
if(swapfilename != NULL) {
vec[31].value = (char *)alloca(strlen(swapfilename) + 1);
strcpy(vec[31].value, swapfilename);
vec[32].value = (char *)alloca(sizeof(char)*11);
sprintf(vec[32].value, "%ld", swapfilesize);
}
vec[11].value = (char *)alloca(16);
if(saveFileList)
sprintf(vec[11].value, "%d", fileCount);
else
sprintf(vec[11].value, "%d", oldFileCount);
vec[12].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
[myWindow saveFrameToString:vec[12].value];
vec[14].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
[superWindow saveFrameToString:vec[14].value];
vec[24].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
if(inspectorPanel) { // remember the inspector panel's position
[inspectorPanel saveFrameToString:vec[24].value];
} else if(inspectorFrame != (char *)0) {
strcpy(vec[24].value, inspectorFrame);
}
vec[27].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
[existencelogWindow saveFrameToString:vec[27].value];
if(NXWriteDefaults(DEFOWNER, vec) != VECLEN)
NXLogError("error while writing to defaults database");
[self saveFilter:self];
if(saveFileList) { // save defaults for each file
NXColor col;
for(i = 0; i < FILEVECLEN; i++) {
fileVec[i].name = (char *)alloca(32);
fileVec[i].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
}
cellList = [myMatrix cellList];
for(i = 0; i < fileCount; i++) {
actualCell = [cellList objectAt:i];
sprintf(fileVec[0].name, "fileName%d", i);
strcpy(fileVec[0].value, [actualCell stringValue]);
sprintf(fileVec[1].name, "fileFrame%d", i);
[[actualCell window] saveFrameToString:fileVec[1].value];
sprintf(fileVec[2].name, "fileMode%d", i);
sprintf(fileVec[2].value, "%d", [actualCell fileMode]);
sprintf(fileVec[3].name, "filePop%d", i);
strcpy(fileVec[3].value, [actualCell autoPopUp] ? "YES" : "NO");
sprintf(fileVec[4].name, "fileBeep%d", i);
strcpy(fileVec[4].value, [actualCell beepOnChange] ? "YES" : "NO");
sprintf(fileVec[5].name, "fileLog%d", i);
strcpy(fileVec[5].value, [actualCell logToSuperlog] ? "YES" : "NO");
sprintf(fileVec[6].name, "fileFilter%d", i);
strcpy(fileVec[6].value, [actualCell useFilter] ? "YES" : "NO");
sprintf(fileVec[7].name, "fileVisible%d", i);
strcpy(fileVec[7].value, [[actualCell window] isVisible] ? "YES" : "NO");
sprintf(fileVec[8].name, "fileDontCopy%d", i);
strcpy(fileVec[8].value, [actualCell dontCopy] ? "YES" : "NO");
sprintf(fileVec[9].name, "fileSuperCopyName%d", i);
sprintf(fileVec[9].value, "%d", [actualCell superCopyFilename]);
col = [actualCell color];
sprintf(fileVec[10].name, "fileColor%d", i);
sprintf(fileVec[10].value, "%f,%f,%f", NXRedComponent(col), NXGreenComponent(col), NXBlueComponent(col));
sprintf(fileVec[11].name, "fileTimeStamp%d", i);
sprintf(fileVec[11].value, [actualCell timeStamp] ? "YES" : "NO");
sprintf(fileVec[12].name, "fileFont%d", i);
sprintf(fileVec[12].value, "%s %f", [[actualCell spyFont] name], [[actualCell spyFont] pointSize]);
sprintf(fileVec[13].name, "fileSpytype%d", i);
sprintf(fileVec[13].value, "%hu", [actualCell spytype]);
if(NXWriteDefaults(DEFOWNER, fileVec) != FILEVECLEN)
NXLogError("error while writing filevector to defaults database");
}
if(oldCount > fileCount) { // remove old entries which sould no longer be there
str = (char *)alloca(32);
for(i = fileCount; i < oldCount; i++) {
sprintf(str, "fileName%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileFrame%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileMode%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "filePop%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileBeep%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileLog%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileFilter%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileVisible%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileDontCopy%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileSuperCopyName%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileColor%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileTimeStamp%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileFont%d", i);
NXRemoveDefault(DEFOWNER, str);
sprintf(str, "fileSpytype%d", i);
NXRemoveDefault(DEFOWNER, str);
}
}
}
return self;
}
- saveFilter:sender // save filter-patterns to defaults database
{
char *tmp = (char *)alloca(16);
const char *tmpStr;
unsigned int i, maxFilter, oldCount = 0;
maxFilter = [filterStore count];
tmpStr = NXGetDefaultValue(DEFOWNER, "filterStoreCount");
if(tmpStr != (const char *)0)
oldCount = atoi(tmpStr);
sprintf(tmp, "%d", maxFilter);
if(!NXWriteDefault(DEFOWNER, "filterStoreCount", tmp))
NXLogError("error while writing filterStoreCount to defaults database");
for(i = 0; i < maxFilter; i++) { // save all filter strings
sprintf(tmp, "filterLine%d", i);
if(!NXWriteDefault(DEFOWNER, tmp, (char *)[filterStore elementAt:i]))
NXLogError("error while writing filterLine to defaults database");
}
for(i = maxFilter; i < oldCount; i++) { // remove remaining old entries
sprintf(tmp, "filterLine%d", i);
NXRemoveDefault(DEFOWNER, tmp);
}
if([filterWindow isDocEdited])
[filterWindow setDocEdited:NO];
return self;
}
- loadPreferences:sender // load preferences from defaults database
{
NXDefaultsVector vec = { // contains global defaults
{"filterMode", "1"}, // 0
{"superUseFilter", "NO"}, // 1
{"prefUseFilter", "NO"}, // 2
{"secs", "2.000000"}, // 3
{"prefPopup", "YES"}, // 4
{"prefBeepOnChange", "YES"}, // 5
{"prefSuperlog", "NO"}, // 6
{"superPopup", "YES"}, // 7
{"superBeepOnChange", "YES"}, // 8
{"prefFileMode", "0"}, // 9
{"saveFileList", "YES"}, // 10
{"fileCount", "0"}, // 11
{"myWindowFrame", ""}, // 12
{"myWindowVisible", "YES"}, // 13
{"superWindowFrame", ""}, // 14
{"superWindowVisible", "NO"}, // 15
{"becomeKey", "NO"}, // 16
{"saveState", "NO"}, // 17
{"prefDontCopySuperlog", "YES"}, // 18
{"filterStoreCount", "0"}, // 19
{"prefSuperCopyFilename", "0"}, // 20
{"prefColor", "0,0,0"}, // 21
{"prefTimeStamp", "NO"}, // 22
{"prefFont", "Ohlfs 10.000000"}, // 23
{"maxLines", "-100"}, // 24
{"inspectorFrame", ""}, // 25
{"superNoDupLines", "NO"}, // 26
{"prefSpytype", "1"}, // 27
{"exWindowFrame", ""}, // 28
{"exWindowVisible", "NO"}, // 29
{"soundfile", ""}, // 30
{"showTime", "NO"}, // 31
{"swapFileName", ""}, // 32
{"swapFileSize", "-1"}, // 33
{NULL, NULL}
};
NXDefaultsVector fileVec = { // contains defaults for individual files
{"", ""}, // 0
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""}, // 13
{NULL, NULL}, // <- enter this number in NRFILEVEC
};
#define NRFILEVEC 14
unsigned int max, i;
BOOL tmpPopup, tmpBeepOnChange, tmpLogToSuperlog, tmpUseFilter;
unsigned short int tmpSpytype;
int tmpFileMode;
float red, green, blue;
char str[MAXFILELEN], fontStr[48];
const char *tmpStr;
NXRegisterDefaults(DEFOWNER, vec);
[self setSound:NXGetDefaultValue(DEFOWNER, "soundfile")]; // set 'beep'
filterMode = atoi(NXGetDefaultValue(DEFOWNER, "filterMode"));
superUseFilter = (strncmp(NXGetDefaultValue(DEFOWNER, "superUseFilter"), "NO", 2) == 0) ? NO : YES;
tmpUseFilter = (strncmp(NXGetDefaultValue(DEFOWNER, "prefUseFilter"), "NO", 2) == 0) ? NO : YES;
secs = atof(NXGetDefaultValue(DEFOWNER, "secs"));
tmpPopup = (strncmp(NXGetDefaultValue(DEFOWNER, "prefPopup"), "NO", 2) == 0) ? NO : YES;
tmpBeepOnChange = (strncmp(NXGetDefaultValue(DEFOWNER, "prefBeepOnChange"), "NO", 2) == 0) ? NO : YES;
tmpLogToSuperlog = (strncmp(NXGetDefaultValue(DEFOWNER, "prefSuperlog"), "NO", 2) == 0) ? NO : YES;
superPopup = (strncmp(NXGetDefaultValue(DEFOWNER, "superPopup"), "NO", 2) == 0) ? NO : YES;
superBeepOnChange = (strncmp(NXGetDefaultValue(DEFOWNER, "superBeepOnChange"), "NO", 2) == 0) ? NO : YES;
noDuplicatedLines = (strncmp(NXGetDefaultValue(DEFOWNER, "superNoDupLines"), "NO", 2) == 0) ? NO : YES;
#if 0
if(noDuplicatedLines)
lastSuperlogString = (char *)malloc(MAXSTRLEN * sizeof(char));
else
lastSuperlogString = (char *)0;
#endif
tmpFileMode = atoi(NXGetDefaultValue(DEFOWNER, "prefFileMode"));
tmpSpytype = (unsigned short int)atoi(NXGetDefaultValue(DEFOWNER, "prefSpytype"));
saveFileList = (strncmp(NXGetDefaultValue(DEFOWNER, "saveFileList"), "NO", 2) == 0) ? NO : YES;
maxLines = atoi(NXGetDefaultValue(DEFOWNER, "maxLines"));
if(maxLines < 0) {
maxLineState = NO;
maxLines = -maxLines;
} else
maxLineState = YES;
showTime = (strncmp(NXGetDefaultValue(DEFOWNER, "showTime"), "NO", 2) == 0) ? NO : YES;
tmpStr = NXGetDefaultValue(DEFOWNER, "swapFileName");
if(tmpStr != NULL && *tmpStr != '\000') {
swapfilename = (char *)malloc(strlen(tmpStr)+1);
strcpy(swapfilename, tmpStr);
swapfilesize = atoi(NXGetDefaultValue(DEFOWNER, "swapFileSize"));
}
[superWindow setFrameFromString:NXGetDefaultValue(DEFOWNER, "superWindowFrame")];
max = atoi(NXGetDefaultValue(DEFOWNER, "filterStoreCount"));
for(i = 0; i < max; i++) {
const char *ts;
sprintf(str, "filterLine%d", i);
ts = NXGetDefaultValue(DEFOWNER, str);
if(ts != (const char *)0)
[filterStore addElement:(char *)ts];
}
filterIsUpToDate = NO;
for(i = 0; i < NRFILEVEC; i++) {
fileVec[i].name = (char *)alloca(32);
fileVec[i].value = (char *)alloca(NX_MAXFRAMESTRINGLENGTH);
}
max = oldFileCount= atoi(NXGetDefaultValue(DEFOWNER, "fileCount"));
displayCurWindow = NO;
newFileOffset = NO;
for(i = 0; i < max; i++) {
sprintf(fileVec[0].name, "fileName%d", i);
sprintf(fileVec[1].name, "fileFrame%d", i);
sprintf(fileVec[2].name, "fileMode%d", i);
sprintf(fileVec[3].name, "filePop%d", i);
sprintf(fileVec[4].name, "fileBeep%d", i);
sprintf(fileVec[5].name, "fileLog%d", i);
sprintf(fileVec[6].name, "fileDontCopy%d", i);
sprintf(fileVec[7].name, "fileFilter%d", i);
sprintf(fileVec[8].name, "fileVisible%d", i);
sprintf(fileVec[9].name, "fileSuperCopyName%d", i);
sprintf(fileVec[10].name, "fileColor%d", i);
sprintf(fileVec[11].name, "fileTimeStamp%d", i);
sprintf(fileVec[12].name, "fileFont%d", i);
sprintf(fileVec[13].name, "fileSpytype%d", i);
strcpy(fileVec[0].value, "/");
strcpy(fileVec[1].value, "");
strcpy(fileVec[2].value, "0");
strcpy(fileVec[3].value, "YES");
strcpy(fileVec[4].value, "YES");
strcpy(fileVec[5].value, "NO");
strcpy(fileVec[6].value, "YES");
strcpy(fileVec[7].value, "NO");
strcpy(fileVec[8].value, "YES");
strcpy(fileVec[9].value, "0");
strcpy(fileVec[10].value, "0,0,0");
strcpy(fileVec[11].value, "NO");
strcpy(fileVec[12].value, "Ohlfs 10.000000");
strcpy(fileVec[13].value, "1");
NXRegisterDefaults(DEFOWNER, fileVec);
[myTextField setStringValue:NXGetDefaultValue(DEFOWNER, fileVec[0].name)];
strcpy(frameStr, NXGetDefaultValue(DEFOWNER, fileVec[1].name));
prefFileMode = atoi(NXGetDefaultValue(DEFOWNER, fileVec[2].name));
prefPopup = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[3].name), "NO", 2) == 0) ? NO : YES;
prefBeepOnChange = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[4].name), "NO", 2) == 0) ? NO : YES;
prefSuperlog = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[5].name), "NO", 2) == 0) ? NO : YES;
prefDontCopySuperlog = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[6].name), "NO", 2) == 0) ? NO : YES;
prefUseFilter = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[7].name), "NO", 2) == 0) ? NO : YES;
prefSuperCopyFilename = atoi(NXGetDefaultValue(DEFOWNER, fileVec[9].name));
strcpy(str, NXGetDefaultValue(DEFOWNER, fileVec[10].name));
sscanf(str, "%f,%f,%f", &red, &green, &blue);
prefColor = NXConvertRGBToColor(red, green, blue);
prefTimeStamp = (strncmp(NXGetDefaultValue(DEFOWNER, fileVec[11].name), "NO", 2) == 0) ? NO : YES;
strcpy(str, NXGetDefaultValue(DEFOWNER, fileVec[12].name));
sscanf(str, "%s %f", fontStr, &red);
prefFont = [Font newFont:fontStr size:red];
prefSpytype = atoi(NXGetDefaultValue(DEFOWNER, fileVec[13].name));
[self addFile:self];
if(strncmp(NXGetDefaultValue(DEFOWNER, fileVec[8].name), "YES", 3) == 0) {
[[myMatrix selectedCell] displayWindow];
[self addWindow:self];
}
}
displayCurWindow = YES;
newFileOffset = YES;
[myWindow setFrameFromString:NXGetDefaultValue(DEFOWNER, "myWindowFrame")];
if(strncmp(NXGetDefaultValue(DEFOWNER, "myWindowVisible"), "YES", 3) == 0) {
myWindowIsVisible = YES;
[myWindow makeKeyAndOrderFront:self];
} else
myWindowIsVisible = NO;
[existencelogWindow setFrameFromString:NXGetDefaultValue(DEFOWNER, "exWindowFrame")];
if(strncmp(NXGetDefaultValue(DEFOWNER, "exWindowVisible"), "YES", 3) == 0) {
[existencelogWindow makeKeyAndOrderFront:self];
}
prefPopup = tmpPopup;
prefBeepOnChange = tmpBeepOnChange;
prefSuperlog = tmpLogToSuperlog;
prefUseFilter = tmpUseFilter;
prefFileMode = tmpFileMode;
prefDontCopySuperlog = (strncmp(NXGetDefaultValue(DEFOWNER, "prefDontCopySuperlog"), "NO", 2) == 0) ? NO : YES;
prefSuperCopyFilename = atoi(NXGetDefaultValue(DEFOWNER, "prefSuperCopyFilename"));
strcpy(str, NXGetDefaultValue(DEFOWNER, "prefColor"));
sscanf(str, "%f,%f,%f", &red, &green, &blue);
prefColor = NXConvertRGBToColor(red, green, blue);
prefTimeStamp = (strncmp(NXGetDefaultValue(DEFOWNER, "prefTimeStamp"), "NO", 2) == 0) ? NO : YES;
strcpy(str, NXGetDefaultValue(DEFOWNER, "prefFont"));
sscanf(str, "%s %f", fontStr, &red);
prefFont = [Font newFont:fontStr size:red];
prefSpytype = tmpSpytype;
if(strncmp(NXGetDefaultValue(DEFOWNER, "superWindowVisible"), "YES", 3) == 0)
[superWindow makeKeyAndOrderFront:self];
becomeKey = strncmp(NXGetDefaultValue(DEFOWNER, "becomeKey"), "NO", 2) == 0 ? NO : YES;
saveState = strncmp(NXGetDefaultValue(DEFOWNER, "saveState"), "NO", 2) == 0 ? NO : YES;
if(NXGetDefaultValue(DEFOWNER, "inspectorFrame") != (const char *)0) {
inspectorFrame = (char *)malloc(NX_MAXFRAMESTRINGLENGTH*sizeof(char));
strcpy(inspectorFrame, NXGetDefaultValue(DEFOWNER, "inspectorFrame"));
}
[self singleClick:self]; // update the filelist with all buttons
return self;
}
- changeBeepState:sender;
{
BOOL beepOnChange;
unsigned int i, max;
List *selectedCellsList = [tmpList empty];
if([sender state] == 0)
beepOnChange = NO;
else
beepOnChange = YES;
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setBeepOnChange:beepOnChange];
return self;
}
- showSuperlog:sender
{
[superWindow orderFront:self];
return self;
}
- updateSuperlog:sender :(char *)newText :(BOOL)changed :(BOOL)beepThis :(BOOL)popThis
{
long int tmpPos, superLen;
char myText[strlen(newText) + MAXFILELEN], senderName[MAXFILELEN];
if(*newText == '\000') // if there's no new text then forget it
return self;
if(maxLineState)
[self shortSuperText];
if(noDuplicatedLines && (sender != lastSuperlogUser)) { // if user doesn't want the same line from multiple log-files
// logged more than once, then don't append the line to superlog
unsigned int len, len2, oldLineNumber = 0 , newLineNumber = 0, i ,j, k;
NXSelPt start, end;
char *oldText, *pos;
char **oldLines, **newLines;
[superText getSel:&start :&end];
len = end.cp - start.cp;
if(len != 0) {
oldText = (char *)alloca(sizeof(char) * (len + 1));
[superText getSubstring:oldText start:start.cp length:len];
oldText[len] = '\000';
for(pos = oldText; *pos != '\000'; ) { // remove all \r from oldText
if(*pos == '\n') { // count number of lines
oldLineNumber++;
pos++;
} else if(*pos == '\r') // (they are inserted in some logfiles and in some not)
strcpy(pos, pos+1);
else
pos++;
}
for(pos = newText; *pos != '\000'; ) { // same for newtext
if(*pos == '\n') {
newLineNumber++;
pos++;
} else if(*pos == '\r')
strcpy(pos, pos+1);
else
pos++;
}
// get the starting positions of the lines
oldLines = (char **)malloc(oldLineNumber * sizeof(char *));
newLines = (char **)malloc(newLineNumber * sizeof(char *));
oldLineNumber = newLineNumber = 1;
oldLines[0] = oldText;
newLines[0] = newText;
for(pos = oldText; *pos != '\000'; pos++) {
if((pos[0] == '\n') && (pos[1] != '\000'))
oldLines[oldLineNumber++] = pos+1;
}
for(pos = newText; *pos != '\000'; pos++) {
if((pos[0] == '\n') && (pos[1] != '\000'))
newLines[newLineNumber++] = pos+1;
}
// compare all the new lines with the old ones
for(i = 0; i < newLineNumber; i++) { // for each line in the new text
for(len = 0; (newLines[i][len] != '\n') && (newLines[i][len] != '\000'); len++) ;
for(j = 0; j < oldLineNumber; j++) {
for(len2 = 0; (oldLines[j][len2] != '\n') && (oldLines[j][len2] != '\000'); len2++) ;
if((len == len2) && (strncmp(newLines[i], oldLines[j], len) == 0)) {
// in case of a match, remove the new line
for(k = i + 1; k < newLineNumber; k++)
strcpy(newLines[k-1], newLines[k]);
len = newLines[i+1] - newLines[i];
for(k = i+1; k < newLineNumber; k++)
newLines[k] -= len;
newLineNumber--;
i--; // will be increased in the for loop, so adjust here
if(i < newLineNumber)
break;
}
}
}
if(newLineNumber == 0) // if there are no new lines left, we don't have to add any text
return self;
}
}
if(changed || (filterMode == FILTER_DONTBEEP)) {
if(changed) { // if something passed the filter
if(superBeepOnChange && beepThis) // and we prefer beeps in general and in this very special case
[self nxbeep]; // then beep
if(superPopup && popThis) { // if we prefer popups uf the superlogwindow and especially in this case...
[superWindow orderFrontRegardless];
if(becomeKey) {
[NXApp unhide:self];
[superWindow makeKeyWindow];
}
}
}
if(![superWindow isKeyWindow] && ![superWindow isDocEdited])
[superWindow setDocEdited:YES];
myText[0] = '\000';
strcpy(senderName, [sender stringValue]); // sender's filename
superLen = [superText textLength];
[superText setAutodisplay:NO];
[superText setSel:superLen :superLen];
switch([sender superCopyFilename]) {
case ONCE:
if(sender != lastSuperlogUser) {
strcpy(myText, senderName);
strcat(myText, ":\n");
} else
*myText = '\000';
tmpPos = superLen + strlen(myText);
strcat(myText, newText);
[superText replaceSel :myText];
[[superText setSel :superLen :tmpPos] underline:self];
[superText setSel :superLen :superLen + strlen(myText) + 1];
break;
case EACHLINE:{
char tmpText[strlen(newText) + MAXFILELEN], *p, *cp, tmp;
tmpPos = superLen; // remember current length of superText to select new text afterwards
strcpy(myText, senderName);
strcat(myText, ":\t"); // tab better than space (Tremblin), maybe use rulers?
cp = newText;
while(*cp != '\000') { // start each line with myText
strcpy(tmpText, myText);
p = cp;
while((*p != '\000') && (*p != '\n')) // search end of line/string
p++;
tmp = *p;
*p = '\000';
strcat(tmpText, cp);
strcat(tmpText, "\n"); // copy text up to there and append newline
if(tmp == '\n') // go to next line if we haven't found end of string yet
cp = p + 1;
else
cp = p;
superLen = [superText textLength];
[superText setSel:superLen :superLen];
[superText replaceSel :tmpText]; // append this line to superText
}
[superText setSel :tmpPos :[superText textLength]];
break;}
case NONE:
[superText replaceSel :newText];
[superText setSel :superLen :[superText textLength]];
break;
#ifdef DEBUG
default: // that doesn't happen anyway...
printf("unmatched case-value\n");
#endif
}
[superText setSelColor:[sender color]];
if([sender spyFont])
[superText setSelFont:[sender spyFont]];
if([superText needsDisplay])
[superText display];
[superText setAutodisplay:YES];
[[[superText sizeToFit] scrollSelToVisible] display];
if(!lastSuperlogUser && inspectorPanel && [inspectorPanel isVisible] && [superWindow isKeyWindow] && !myWindowIsVisible)
[self setInspectorToView:[inspectorNormalView contentView]];
lastSuperlogUser = sender;
}
return self;
}
- setupSuperlog
{
superFont = [Font newFont:"Ohlfs"
size:10
style:0
matrix:NX_FLIPPEDMATRIX];
[Text setDefaultFont:superFont];
superDoc = [Document new:self];
superWindow = [[superDoc scrollView] window];
superScrollView = [superDoc scrollView];
superText = [[superDoc scrollView] docView];
[superWindow setFreeWhenClosed:NO];
[superWindow setTitle:"Multilog"];
[superWindow setDelegate:self];
[superText setMonoFont:NO];
return self;
}
- setupFilter
{
filterStore = [[Storage alloc] initCount:0 // contains filter-lines
elementSize:128
description:"[128c]"];
return self;
}
- showFilter:sender
{
if(!filterWindow) {
if(![NXApp loadNibSection:"Filter.nib" owner:self withNames:NO fromZone:[self zone]]) {
NXLogError("Can't open Filter.nib!");
return self;
}
[filterText setDelegate:self];
}
[filterModeMatrix selectCellAt:filterMode:0];
if(!filterIsUpToDate) {
unsigned int i, max;
[filterText setSel:0 :[filterText textLength]];
[filterText replaceSel:""];
max = [filterStore count];
for(i = 0; i < max; i++) { // read lines from filterStore and insert them in the text in the filter-panel
char str[128];
int filterTextLen;
filterTextLen = [filterText textLength];
[filterText setSel:filterTextLen :filterTextLen];
strcpy(str, (char *)[filterStore elementAt:i]);
strcat(str, "\n");
[filterText replaceSel:str];
}
filterIsUpToDate = YES;
}
[filterWindow makeKeyAndOrderFront:self];
return self;
}
- textDidGetKeys:sender isEmpty:(BOOL)flag
{
if(sender == filterText) {
if(![filterWindow isDocEdited])
[filterWindow setDocEdited:YES];
}
return self;
}
- applyFilter:sender // copy all the filter-lines from the text-object to the filterStore
{
int max,i, s, l;
char str[128];
[filterStore empty];
max = [filterText lineFromPosition:[filterText textLength]];
for(i = 1; i < max; i++) {
s = [filterText positionFromLine:i];
l = [filterText positionFromLine:i+1] - s;
if(l > 127) {
[[[filterText setSel:s :127] scrollSelToVisible] display];
NXRunAlertPanel("Warning", "Filterline to long, string is truncated to 127 characters.", "Ok", NULL, NULL);
l = 127;
}
[filterText getSubstring:str
start: s
length: l];
str[l-1] = '\000'; // without '\n' please
if((str[0] == '|') && (strchr(str+1, '|') == NULL)) { // illegal syntax for file-sensitive filterstrings
[[[filterText setSel:s :l] scrollSelToVisible] display];
NXRunAlertPanel("Alert", "Filename for file-sensitive filterstring must be between two '|'.", "Ok", NULL, NULL);
} else {
[filterStore addElement:str];
}
}
return self;
}
- (BOOL)filterString:(char *)str andRemove:(BOOL)rm who:sender // returns YES if something passes the filter
{
unsigned int i, max;
char *from, *to, *ptr, ch;
BOOL matched = NO, pass = NO; // pass: TRUE if something passed the filter
char *senderName = (char *)[sender stringValue];
from = to = str;
max = [filterStore count];
while(*from != '\000') {
ptr = from;
while((*ptr != '\n') && (*ptr != '\000'))
ptr++;
ch = *ptr; // in case that this line has no NL at the end
*ptr = '\000';
for(i = 0; i < max; i++) { // go through all filter-lines
char *filtStr = (char *)[filterStore elementAt:i], *tmpStr = filtStr;
if(((filtStr = strchr(filtStr, '|')) != (char *)0) &&
(filtStr[1] != '|')) { // file-sensitive filterstring
*filtStr = '\000';
if(!wildmat(senderName, tmpStr)) {
*filtStr = '|';
break; // if we're doing file-sensitive filtering and this is the wrong file, then skip filterline
}
*filtStr++ = '|'; // restore filterstring and advance to real filterstring (the one after the filename)
} else
filtStr = tmpStr;
if(wildmat(from, filtStr)) {
matched = TRUE;
break;
}
}
if(!matched && rm) { // if we didn't match anything but are removing matches line, we have to copy the whole line
while(*from != '\000')
*to++ = *from++;
*to++ = ch;
from++;
while(*from == '\r')
from++;
} else {
from = (char *)(ptr + 1);
*ptr = ch;
while(*from == '\r') { // remove '\r's from begin of line
ptr = from;
while(ptr[1] != '\000') {
*ptr++ = ptr[1];
}
*ptr = ptr[1];
}
}
if(!matched)
pass = YES;
}
if(rm)
*to = '\000';
return pass;
}
- (int)filterMode
{
return filterMode;
}
- (BOOL)superUsesFilter
{
return superUseFilter;
}
- (BOOL)becomeKey
{
return becomeKey;
}
- unhideApp:sender
{
[NXApp unhide:self];
return self;
}
- clearBuffer:sender
{
id keyWindow = [NXApp keyWindow];
if((keyWindow != nil) && (keyWindow != myWindow) && (keyWindow != superWindow)) {
[[keyWindow delegate] clearBuffer:self];
} else if(keyWindow == myWindow) {
List *selectedCellsList = [tmpList empty];
unsigned int i;
[myMatrix getSelectedCells:selectedCellsList];
for(i = 0; i < [selectedCellsList count]; i++)
[[selectedCellsList objectAt:i] clearBuffer:self];
} else if(keyWindow == superWindow) {
[superText setSel :0 :[superText textLength]];
[superText replaceSel:""];
[[superText sizeToFit] scrollSelToVisible];
if(lastSuperlogUser && inspectorPanel && [inspectorPanel isVisible] && !myWindowIsVisible && [superWindow isKeyWindow]) {
// if there is already something in the superlog AND the inspectorPanel is already loaded AND it is visible
// AND the mainwindow is not visible AND the (now empty) superlogwindow is the keywindow
// THEN the inspectorpanel doesn't know which file it should inspect...
[self setInspectorToView:[inspectorNoView contentView]];
}
lastSuperlogUser = nil;
}
return self;
}
- windowDidBecomeKey:sender
{
if(sender == myWindow) {
myWindowIsVisible = YES;
if([myMatrix selectedCell] != nil) {
[self setInspectorToView:[inspectorNormalView contentView]];
[self singleClick:self];
}
return self;
} else if(sender == superWindow) {
if([superWindow isDocEdited])
[superWindow setDocEdited:NO];
if(!myWindowIsVisible && inspectorPanel && [inspectorPanel isVisible]) {
if(lastSuperlogUser) {
// if the superwindow just became the keywindow and the mainwindow is not visible but the
// inspectorpanel is, then set the inspector to the last file that logged something in the superlog
[self setInspectorToView:[inspectorNormalView contentView]];
[myMatrix selectCell:lastSuperlogUser];
[self singleClick:self];
} else {
// if nothing is in the superlog, then set the inspector to the empty view
[self setInspectorToView:[inspectorNoView contentView]];
}
}
} else if(sender == inspectorPanel) {
if(myWindowIsVisible) {
if([myMatrix cellCount] > 0) {
[myFontManager setSelFont:[[myMatrix selectedCell] spyFont] isMultiple:NO];
[self setInspectorToView:[inspectorNormalView contentView]];
} else {
[self setInspectorToView:[inspectorNoView contentView]];
}
} else if((nrOpenWindows > 0) || ([superWindow isVisible] && lastSuperlogUser)) {
[self setInspectorToView:[inspectorNormalView contentView]];
} else {
[self setInspectorToView:[inspectorNoView contentView]];
}
} else if(sender == preferencesPanel) {
[myFontManager setSelFont:prefFont isMultiple:NO];
} else if(sender == existencelogWindow) {
existenceSeen = YES; // existenceWindow has been seen by the user
} else if(!myWindowIsVisible && inspectorPanel && [inspectorPanel isVisible]
&& ([[sender delegate] class] == [SpyTextFieldCell class])) {
// if the mainwindow is not visible but the inspectorpanel is and the window which just became the
// keywindow is a normal window, then update the inspector the the right file
[myMatrix selectCell:[sender delegate]];
[self singleClick:self];
[self setInspectorToView:[inspectorNormalView contentView]];
}
return self;
}
- windowWillClose:sender
{
Window *keyWindow;
if(sender == myWindow) {
myWindowIsVisible = NO;
keyWindow = [NXApp keyWindow];
if((keyWindow != myWindow) && (keyWindow != nil)
&& (([[keyWindow delegate] class] == [SpyTextFieldCell class]) || (keyWindow == superWindow))) {
[self windowDidBecomeKey:keyWindow];
} else if(nrOpenWindows == 0) {
[self setInspectorToView:[inspectorNoView contentView]];
}
} else if(sender == existencelogWindow) {
existenceSeen = YES; // we assume that the user has noticed eventual changes in the existencelogWindow
} else if([[sender delegate] class] == [SpyTextFieldCell class]) {
if(nrOpenWindows > 0) {
if((--nrOpenWindows == 0) && !myWindowIsVisible && ![superWindow isVisible]) {
// if this was tha last open window (except the panels) then clear the inspectorpanel
[self setInspectorToView:[inspectorNoView contentView]];
}
}
} else if((sender == superWindow) && (nrOpenWindows == 0)) {
[self setInspectorToView:[inspectorNoView contentView]];
}
return self;
}
- showInspectorPanel:sender
{
if(!inspectorPanel) {
if(![NXApp loadNibSection:"Inspector.nib" owner:self withNames:NO fromZone:[self zone]]) {
NXLogError("Can't open Inspector.nib!");
}
if(inspectorFrame != (char *)0) { // if we read a window-position for the inspectorpanel from the defaults database,
// then set the window accordingly
[inspectorPanel setFrameFromString:inspectorFrame];
free(inspectorFrame);
}
if((myWindowIsVisible && ([myMatrix cellCount] > 0) && ([myMatrix selectedCell] != nil))
|| (nrOpenWindows > 0) || ([superWindow isVisible] && lastSuperlogUser)) {
[self setInspectorToView:[inspectorNormalView contentView]];
} else {
[self setInspectorToView:[inspectorNoView contentView]];
}
}
[self singleClick:self];
[inspectorPanel makeKeyAndOrderFront:self];
return self;
}
- setInspectorToView:theView
{
NXRect boxRect, viewRect;
if([inspectorMultiView contentView] != theView) {
[inspectorMultiView getFrame:&boxRect];
[theView getFrame:&viewRect];
[inspectorMultiView setContentView:theView];
NX_X(&viewRect) = (NX_WIDTH(&boxRect) - NX_WIDTH(&viewRect)) / 2.0;
NX_Y(&viewRect) = (NX_HEIGHT(&boxRect) - NX_HEIGHT(&viewRect)) / 2.0;
[theView setFrame:&viewRect];
[inspectorMultiView display];
}
return self;
}
- showFindPanel:sender
{
[finder showFindPanel:self];
return self;
}
- findNext:sender
{
[finder findNext:self];
return self;
}
- findPrevious:sender
{
[finder findPrevious:self];
return self;
}
- enterSelection:sender
{
[finder enterSelection:self];
return self;
}
- jumpToSelection:sender
{
const id mainWindow = [NXApp mainWindow];
if(mainWindow == superWindow)
[superText makeSelectionVisible];
else if(mainWindow == filterWindow)
[filterText makeSelectionVisible];
else if([[mainWindow delegate] class] == [SpyTextFieldCell class])
[[[mainWindow delegate] text] makeSelectionVisible];
return self;
}
- findTexts:(BOOL)all
{
List *textList;
const id mainWindow = [NXApp mainWindow];
textList = [[List alloc] init];
if(all) { // return ALL text objects you have
unsigned int i;
List *cellList;
cellList = [myMatrix cellList];
if(mainWindow == superWindow) // if the superlogWindow is main, search there first
[textList addObject:superText];
else if(mainWindow == filterWindow)
[textList addObject:filterText];
else if(mainWindow == helpWindow)
[textList addObject:helpText];
else if((mainWindow != nil) && ([[mainWindow delegate] class] == [SpyTextFieldCell class]))
[textList addObject:[[mainWindow delegate] text]]; // then search in the mainWindows text
for(i = 0; i < [cellList count]; i++) // add all other texts
[textList addObjectIfAbsent:[[cellList objectAt:i] text]];
[textList addObjectIfAbsent:superText]; // if the superWindow is not opened, add it at the end
[textList addObjectIfAbsent:filterText];
return textList;
} else if(mainWindow == myWindow) { // return only the text objects currently selected in the matrix
[myMatrix getSelectedCells:textList];
return textList;
} else if(mainWindow == superWindow) {
[textList addObject:superText];
return textList;
} else if(mainWindow == filterWindow) {
[textList addObject:filterText];
return textList;
} else if(mainWindow == helpWindow) {
[textList addObject:helpText];
return textList;
} else if((mainWindow != nil) && ([[mainWindow delegate] class] == [SpyTextFieldCell class])) {
[textList addObject:[[mainWindow delegate] text]];
return textList;
} else {
[textList free];
return nil;
}
}
- superFilenameClick:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max, state = [[sender selectedCell] tag];
PopUpList *popupList = [[superFilenameButton cell] target];
Matrix *popupMatrix = [popupList itemList];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setSuperCopyFilename:state];
[popupMatrix selectCellWithTag:state];
[superFilenameButton setTitle:[[popupMatrix findCellWithTag:state] title]];
return self;
}
- prefSuperFilenameClick:sender
{
PopUpList *popupList = [[prefSuperFilenameButton cell] target];
Matrix *popupMatrix = [popupList itemList];
unsigned int state = [[sender selectedCell] tag];
prefSuperCopyFilename = state;
[popupMatrix selectCellWithTag:state];
[prefSuperFilenameButton setTitle:[[popupMatrix findCellWithTag:state] title]];
return self;
}
- timeClick:sender
{
List *selectedCellsList;
unsigned int i, max;
BOOL state = [sender state];
selectedCellsList = [tmpList empty];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[[selectedCellsList objectAt:i] setTimeStamp:state];
return self;
}
- prefTimeClick:sender
{
prefTimeStamp = [sender state];
return self;
}
- spytypeClick:sender
{
unsigned int i, max;
int tag = [[sender selectedCell] tag];
[tmpList empty];
[myMatrix getSelectedCells:tmpList];
max = [tmpList count];
for(i = 0; i < max; i++)
[[tmpList objectAt:i] setSpytype:(unsigned short int)tag];
[myMatrix display];
return self;
}
- prefSpytypeClick:sender
{
prefSpytype = (unsigned short int)[[sender selectedCell] tag];
return self;
}
- showExistenceLog:sender
{
return self;
}
- colorChanged:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
NXColor color = [superColorWell color];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++) {
[[selectedCellsList objectAt:i] setColor:color];
}
[myMatrix display];
return self;
}
- prefColorChanged:sender
{
prefColor = [sender color];
return self;
}
- (NXColor)prefColor
{
return prefColor;
}
- changeFont:sender
{
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
id actCell;
if([NXApp keyWindow] != preferencesPanel) {
[fontTextField setFont:[sender convertFont:[fontTextField font]]];
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++) {
actCell = [selectedCellsList objectAt:i];
[actCell setSpyFont:[sender convertFont:[actCell spyFont]]];
}
} else {
[prefFontTextField setFont:[sender convertFont:[prefFontTextField font]]];
prefFont = [prefFontTextField font];
}
return self;
}
- showFontPanel:sender
{
return [myFontManager orderFrontFontPanel:sender];
}
- setMaxLines:sender
{
int oldMaxLines = maxLines;
if((sender == maxLinesScroller) || (sender == maxLinesTextField)) {
maxLines = [sender intValue];
if(maxLines < 10)
maxLines = 10;
if(maxLines > INT_MAX)
maxLines = INT_MAX;
if(sender == maxLinesScroller) // set the other one to the correct value
[maxLinesTextField setIntValue:maxLines];
else
[maxLinesScroller setIntValue:maxLines];
} else { // so it must be the matrix of ButtonCells
maxLineState = [[sender selectedCell] tag];
[maxLinesScroller setEnabled:maxLineState];
[maxLinesTextField setEnabled:maxLineState];
}
if(oldMaxLines != maxLines) {
List *selectedCellsList = [tmpList empty];
unsigned int i, max;
int state = (maxLineState ? maxLines : 0);
[myMatrix getSelectedCells:selectedCellsList];
max = [selectedCellsList count];
for(i = 0; i < max; i++)
[(SpyTextFieldCell *)[selectedCellsList objectAt:i] setMaxLines:state];
}
return self;
}
- (int)maxLines;
{
return maxLines;
}
- shortSuperText
{
int textLen = [superText textLength];
int textLines = [superText lineFromPosition:textLen];
#ifdef DEBUG
assert(maxLines != 0);
#endif
if(textLines > maxLines)
[[superText setSel:0 :[superText positionFromLine:textLines - maxLines]] replaceSel:""];
return self;
}
- addWindow:sender;
{
if(nrOpenWindows++ == 0) {
if(!myWindowIsVisible && inspectorPanel && [inspectorPanel isVisible]) {
[self setInspectorToView:[inspectorNormalView contentView]];
[myMatrix selectCell:sender];
[self singleClick:self];
}
}
return self;
}
- existenceMatrix
{
return myExistenceMatrix;
}
- showExistencelogRegardless
{
[existencelogWindow orderFrontRegardless];
return self;
}
- window
{
return [NXApp mainWindow];
}
@end
void alarmHandler(DPSTimedEntry te, double timeNow, void *data)
{
[(id)data checkFiles];
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.